From 406f3aae9c981f96f64762343440912e2682c4a3 Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Tue, 5 Apr 2022 22:49:43 +0200 Subject: [PATCH 0001/2268] feat: add sha256 check sums --- .github/workflows/build-and-release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index b97ccaa55..a316d08ac 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -89,6 +89,7 @@ jobs: go build ./cmd/kluctl go test -c ./e2e mv kluctl kluctl-linux-amd64 + sha256sum -z kluctl-linux-amd64 > kluctl-linux-amd64.sha256 mv e2e.test e2e.test-linux-amd64 - name: Build kluctl (darwin) run: | @@ -98,6 +99,7 @@ jobs: go build ./cmd/kluctl go test -c ./e2e mv kluctl kluctl-darwin-amd64 + sha256sum -z kluctl-darwin-amd64 > kluctl-darwin-amd64.sha256 mv e2e.test e2e.test-darwin-amd64 - name: Build kluctl (windows) run: | @@ -107,6 +109,7 @@ jobs: go build ./cmd/kluctl go test -c ./e2e mv kluctl.exe kluctl-windows-amd64.exe + sha256sum -z kluctl-windows-amd64.exe > kluctl-windows-amd64.exe.sha256 mv e2e.test.exe e2e.test-windows-amd64.exe - name: Upload dist artifact uses: actions/upload-artifact@v2 @@ -328,3 +331,4 @@ jobs: dist/kluctl-linux-* dist/kluctl-darwin-* dist/kluctl-windows-* + dist/*.sha256 From 7d7d206d007589040949fcd862c821fa1dc51fc7 Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Tue, 5 Apr 2022 23:24:37 +0200 Subject: [PATCH 0002/2268] fix: add upload-artifact --- .github/workflows/build-and-release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index a316d08ac..5d44fe6d7 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -117,8 +117,11 @@ jobs: name: dist path: | kluctl-linux-amd64 + kluctl-linux-amd64.sha256 kluctl-darwin-amd64 + kluctl-darwin-amd64.sha256 kluctl-windows-amd64.exe + kluctl-windows-amd64.exe.sha256 e2e.test-linux-amd64 e2e.test-darwin-amd64 e2e.test-windows-amd64.exe From aeaa324bb86797e809e1e7111bb6fddbb34eba80 Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Thu, 7 Apr 2022 08:09:37 +0200 Subject: [PATCH 0003/2268] feat: modify to have only one checksum.txt --- .github/workflows/build-and-release.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 5d44fe6d7..5e6c0024d 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -89,7 +89,6 @@ jobs: go build ./cmd/kluctl go test -c ./e2e mv kluctl kluctl-linux-amd64 - sha256sum -z kluctl-linux-amd64 > kluctl-linux-amd64.sha256 mv e2e.test e2e.test-linux-amd64 - name: Build kluctl (darwin) run: | @@ -99,7 +98,6 @@ jobs: go build ./cmd/kluctl go test -c ./e2e mv kluctl kluctl-darwin-amd64 - sha256sum -z kluctl-darwin-amd64 > kluctl-darwin-amd64.sha256 mv e2e.test e2e.test-darwin-amd64 - name: Build kluctl (windows) run: | @@ -109,22 +107,23 @@ jobs: go build ./cmd/kluctl go test -c ./e2e mv kluctl.exe kluctl-windows-amd64.exe - sha256sum -z kluctl-windows-amd64.exe > kluctl-windows-amd64.exe.sha256 mv e2e.test.exe e2e.test-windows-amd64.exe - name: Upload dist artifact uses: actions/upload-artifact@v2 + run: | + sha256sum kluctl-linux-amd64 > checksums.txt + sha256sum kluctl-darwin-amd64 >> checksums.txt + sha256sum kluctl-windows-amd64.exe >> checksums.txt with: name: dist path: | kluctl-linux-amd64 - kluctl-linux-amd64.sha256 kluctl-darwin-amd64 - kluctl-darwin-amd64.sha256 kluctl-windows-amd64.exe - kluctl-windows-amd64.exe.sha256 e2e.test-linux-amd64 e2e.test-darwin-amd64 e2e.test-windows-amd64.exe + checksums.txt docker-host: if: "!startsWith(github.ref, 'refs/tags/')" @@ -334,4 +333,4 @@ jobs: dist/kluctl-linux-* dist/kluctl-darwin-* dist/kluctl-windows-* - dist/*.sha256 + dist/checksums.txt \ No newline at end of file From d0bb15bd017398e422f2657cace6994846bbbdc0 Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Thu, 7 Apr 2022 08:55:50 +0200 Subject: [PATCH 0004/2268] fix: run and uses not allowed, missing line break --- .github/workflows/build-and-release.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 5e6c0024d..63afba934 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -108,12 +108,13 @@ jobs: go test -c ./e2e mv kluctl.exe kluctl-windows-amd64.exe mv e2e.test.exe e2e.test-windows-amd64.exe - - name: Upload dist artifact - uses: actions/upload-artifact@v2 + - name: Build checksums.txt run: | sha256sum kluctl-linux-amd64 > checksums.txt sha256sum kluctl-darwin-amd64 >> checksums.txt sha256sum kluctl-windows-amd64.exe >> checksums.txt + - name: Upload dist artifact + uses: actions/upload-artifact@v2 with: name: dist path: | @@ -333,4 +334,5 @@ jobs: dist/kluctl-linux-* dist/kluctl-darwin-* dist/kluctl-windows-* - dist/checksums.txt \ No newline at end of file + dist/checksums.txt + From 31933fb14efc4ff2fc26d5eaf3f34d0be927398d Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Thu, 7 Apr 2022 09:02:50 +0200 Subject: [PATCH 0005/2268] fix: try to build checksums in the release job --- .github/workflows/build-and-release.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 63afba934..795a2a1b7 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -108,11 +108,6 @@ jobs: go test -c ./e2e mv kluctl.exe kluctl-windows-amd64.exe mv e2e.test.exe e2e.test-windows-amd64.exe - - name: Build checksums.txt - run: | - sha256sum kluctl-linux-amd64 > checksums.txt - sha256sum kluctl-darwin-amd64 >> checksums.txt - sha256sum kluctl-windows-amd64.exe >> checksums.txt - name: Upload dist artifact uses: actions/upload-artifact@v2 with: @@ -124,7 +119,6 @@ jobs: e2e.test-linux-amd64 e2e.test-darwin-amd64 e2e.test-windows-amd64.exe - checksums.txt docker-host: if: "!startsWith(github.ref, 'refs/tags/')" @@ -325,6 +319,11 @@ jobs: uses: actions/checkout@v2 - name: Download artifacts uses: actions/download-artifact@v2 + - name: Build checksums.txt + run: | + sha256sum kluctl-linux-amd64 > checksums.txt + sha256sum kluctl-darwin-amd64 >> checksums.txt + sha256sum kluctl-windows-amd64.exe >> checksums.txt - name: Release uses: softprops/action-gh-release@v1 with: @@ -334,5 +333,5 @@ jobs: dist/kluctl-linux-* dist/kluctl-darwin-* dist/kluctl-windows-* - dist/checksums.txt + checksums.txt From 1021c0745f37e1af975419b5d72107bc21fdf53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aljoscha=20P=C3=B6rtner?= Date: Fri, 15 Apr 2022 16:01:56 +0200 Subject: [PATCH 0006/2268] build: add Makefile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Aljoscha Pörtner --- Makefile | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..7444c30f0 --- /dev/null +++ b/Makefile @@ -0,0 +1,113 @@ +# Based on the work of Thomas Poignant (thomaspoignant) +# https://gist.github.com/thomaspoignant/5b72d579bd5f311904d973652180c705 +GOCMD=go +GOTEST=$(GOCMD) test +GOVET=$(GOCMD) vet +BINARY_NAME=kluctl +TEST_BINARY_NAME=kluctl-e2e +REQUIRED_ENV_VARS=GOOS GOARCH +EXPORT_RESULT?=false + +GREEN := $(shell tput -Txterm setaf 2) +YELLOW := $(shell tput -Txterm setaf 3) +WHITE := $(shell tput -Txterm setaf 7) +CYAN := $(shell tput -Txterm setaf 6) +RESET := $(shell tput -Txterm sgr0) + +.PHONY: all test build vendor check-env check-kubectl check-helm check-kind + +all: help + +## Check: +check-env: ## Checks if required environment variables are set + @test $${GOOS?Please set environment variable GOOS} + @test $${GOARCH?Please set environment variable GOARCH} + +check-kubectl: ## Checks if kubectl is installed + kubectl version --client=true + +check-helm: ## Checks if helm is installed + helm version + +check-kind: ## Checks if kind is installed + kind version + +## Build: +build: vendor python generate build-go ## Run the complete build pipeline + +build-go: check-env ## Build your project and put the output binary in out/bin/ + mkdir -p out/bin + CGO_ENBALED=0 GO111MODULE=on GOARCH=$(GOARCH) GOOS=$(GOOS) $(GOCMD) build -mod vendor -o out/bin/$(BINARY_NAME) ./cmd/kluctl + +clean: ## Remove build related file + rm -fr ./bin + rm -fr ./out + rm -f ./junit-report.xml checkstyle-report.xml ./coverage.xml ./profile.cov yamllint-checkstyle.xml + +vendor: ## Copy of all packages needed to support builds and tests in the vendor directory + $(GOCMD) mod vendor + +python: check-env ## Download python for Jinja2 support + ./hack/download-python.sh $(GOARCH) + +generate: ## Generating Jinja2 support + $(GOCMD) generate ./... + $(GOCMD) generate -tags $(GOOS) ./pkg/python + +## Test: +test: test-unit test-e2e ## Runs the complete test suite + +test-e2e: check-env check-kubectl check-helm check-kind ## Runs the end to end tests + CGO_ENBALED=0 GO111MODULE=on GOARCH=$(GOARCH) GOOS=$(GOOS) $(GOCMD) test -o out/bin/$(TEST_BINARY_NAME) ./e2e + +test-unit: ## Run the unit tests of the project +ifeq ($(EXPORT_RESULT), true) + mkdir -p reports/test-unit + GO111MODULE=off $(GOCMD) get -u github.com/jstemmer/go-junit-report + $(eval OUTPUT_OPTIONS = | tee /dev/tty | go-junit-report -set-exit-code > reports/test-unit/junit-report.xml) +endif + $(GOTEST) -v -race $(shell go list ./... | grep -v /e2e/ | grep -v /vendor/) $(OUTPUT_OPTIONS) + +coverage-unit: ## Run the unit tests of the project and export the coverage + $(GOTEST) -cover -covermode=count -coverprofile=reports/coverage-unit/profile.cov $(shell go list ./... | grep -v /e2e/ | grep -v /vendor/) + $(GOCMD) tool cover -func profile.cov +ifeq ($(EXPORT_RESULT), true) + mkdir -p reports/coverage-unit + GO111MODULE=off $(GOCMD) get -u github.com/AlekSi/gocov-xml + GO111MODULE=off $(GOCMD) get -u github.com/axw/gocov/gocov + gocov convert reports/coverage-unit/profile.cov | gocov-xml > reports/coverage-unit/coverage.xml +endif + +## Lint: +lint: lint-go ## Run all available linters + +lint-go: ## Use golintci-lint on your project +ifeq ($(EXPORT_RESULT), true) + mkdir -p reports/lint-go + $(eval OUTPUT_OPTIONS = $(shell echo "--out-format checkstyle ./... | tee /dev/tty > reports/lint-go/checkstyle-report.xml" )) +else + $(eval OUTPUT_OPTIONS = $(shell echo "")) +endif + docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:latest-alpine golangci-lint run $(OUTPUT_OPTIONS) + + +## Release: +version: ## Write next version into version file + $(GOCMD) install github.com/bvieira/sv4git/v2/cmd/git-sv@v2.7.0 + $(eval KLUCTL_VERSION:=$(shell git sv next-version)) + sed -ibak "s/0.0.0/$(KLUCTL_VERSION)/g" pkg/version/version.go + +changelog: ## Generating changelog + git sv changelog -n 1 > CHANGELOG.md + +## Help: +help: ## Show this help. + @echo '' + @echo 'Usage:' + @echo ' ${YELLOW}make${RESET} ${GREEN}${RESET}' + @echo '' + @echo 'Targets:' + @awk 'BEGIN {FS = ":.*?## "} { \ + if (/^[0-9a-zA-Z_-]+:.*?##.*$$/) {printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n", $$1, $$2} \ + else if (/^## .*$$/) {printf " ${CYAN}%s${RESET}\n", substr($$1,4)} \ + }' $(MAKEFILE_LIST) \ No newline at end of file From 004098af65d97302537e44c3d5b58cf49f5a5388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aljoscha=20P=C3=B6rtner?= Date: Fri, 15 Apr 2022 16:08:27 +0200 Subject: [PATCH 0007/2268] build: add configuration for golangci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Aljoscha Pörtner --- .golangci.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..990178f1b --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,26 @@ +run: + concurrency: 4 + timeout: 5m + issues-exit-code: 2 +output: + format: "colored-line-number" +linters: + disable-all: true + enable: + - deadcode + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - structcheck + - typecheck + - unused + - varcheck + - goconst + - gofmt + - goheader + - goimports + - gomnd + - gosec + - wsl \ No newline at end of file From cf386fa5a1a70c96bcb767e43a9682528c4b7041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aljoscha=20P=C3=B6rtner?= Date: Fri, 15 Apr 2022 16:08:52 +0200 Subject: [PATCH 0008/2268] chore: add /out and /reports to gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Aljoscha Pörtner --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1c81eae77..ac7a47bd5 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,6 @@ /download-python /kluctl /kluctl.exe -/e2e.test* \ No newline at end of file +/e2e.test* +/out +/reports \ No newline at end of file From ef2c32ed56818f50d674208e3c45137f4d4e7640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aljoscha=20P=C3=B6rtner?= Date: Fri, 15 Apr 2022 16:16:50 +0200 Subject: [PATCH 0009/2268] build: add reports and download-python to clean target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Aljoscha Pörtner --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7444c30f0..6be0074e8 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,8 @@ build-go: check-env ## Build your project and put the output binary in out/bin/ clean: ## Remove build related file rm -fr ./bin rm -fr ./out - rm -f ./junit-report.xml checkstyle-report.xml ./coverage.xml ./profile.cov yamllint-checkstyle.xml + rm -fr ./reports + rm -fr ./download-python vendor: ## Copy of all packages needed to support builds and tests in the vendor directory $(GOCMD) mod vendor From ec2f521bd2c71b4c277bd2fc0c8e38bfebeedbd0 Mon Sep 17 00:00:00 2001 From: JJGadgets Date: Sat, 16 Apr 2022 02:48:02 +0800 Subject: [PATCH 0010/2268] fix README typo "CI/CI" should be "CI/CD" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e76e01268..8a798176c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Use kluctl to: * Deploy the same deployment to multiple environments (dev, test, prod, ...), with flexible differences in configuration * Manage multiple target clusters (in multiple clouds or bare-metal if you want) * Manage encrypted secrets for multiple target environments and clusters (based on [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)) -* Integrate it into your CI/CI pipelines and avoid putting too much logic into your shell scripts +* Integrate it into your CI/CD pipelines and avoid putting too much logic into your shell scripts kluctl tries to be as flexible as possible, while keeping it as simple as possible. It reuses established tools (e.g. kustomize and Helm), making it possible to re-use a large set of available third-party deployments. From 1f08f7c96835756935898aefecd6ca3e5adcf249 Mon Sep 17 00:00:00 2001 From: JJGadgets Date: Sat, 16 Apr 2022 02:56:58 +0800 Subject: [PATCH 0011/2268] improve README I feel like having the info about tools compatibility and no cluster-side components should be placed above the list of features, especially given the length of the features list. --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8a798176c..627ac52d1 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,13 @@ kluctl is the missing glue that puts together your (and any third-party) deploym Kubernetes deployment, while making it fully manageable (deploy, diff, prune, delete, ...) via one unified command line interface. +kluctl tries to be as flexible as possible, while remaining as simple as possible. It reuses established +tools (e.g. kustomize and Helm), making it possible to re-use a large set of available third-party deployments. + +kluctl works completely locally, on the same machines that `kubectl` runs on. kluctl does not rely on any operators or other cluster-side components. +As long as the target cluster's kubeconfig is present locally, you are able to execute it from everywhere, including your +CI/CD pipelines or your laptop. + Use kluctl to: * Organize large and complex deployments, consisting of many Helm charts and kustomize deployments * Do the same for small and simple deployments, as the overhead is small @@ -17,13 +24,6 @@ Use kluctl to: * Manage encrypted secrets for multiple target environments and clusters (based on [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)) * Integrate it into your CI/CD pipelines and avoid putting too much logic into your shell scripts -kluctl tries to be as flexible as possible, while keeping it as simple as possible. It reuses established -tools (e.g. kustomize and Helm), making it possible to re-use a large set of available third-party deployments. - -kluctl works completely local. In its simplest form, there is no need for any operators or other server-side components. -As long as the target cluster kubeconfig is present locally, you are able to execute it from everywhere, including your -CI/CD pipelines or your laptop. - ![](https://kluctl.io/asciinema/kluctl.gif) ## Documentation From d26eb9651259ae84a67fb621e187bfe3662116d5 Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Sun, 17 Apr 2022 13:25:17 +0200 Subject: [PATCH 0012/2268] fix: add missing change dir --- .github/workflows/build-and-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 795a2a1b7..41790a262 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -321,6 +321,7 @@ jobs: uses: actions/download-artifact@v2 - name: Build checksums.txt run: | + cd dist sha256sum kluctl-linux-amd64 > checksums.txt sha256sum kluctl-darwin-amd64 >> checksums.txt sha256sum kluctl-windows-amd64.exe >> checksums.txt @@ -333,5 +334,4 @@ jobs: dist/kluctl-linux-* dist/kluctl-darwin-* dist/kluctl-windows-* - checksums.txt - + dist/checksums.txt From 140579f3cbe91c8de3c51ddefcb03fdfe7d66d39 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 19 Apr 2022 15:04:38 +0200 Subject: [PATCH 0013/2268] refactor: Modify LoadKluctlProject to not be callback based anymore --- cmd/kluctl/commands/utils.go | 16 +++++++++++-- pkg/kluctl_project/load.go | 45 +++++++++++++++-------------------- pkg/kluctl_project/project.go | 6 ++--- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 0be2ed76d..0db35e851 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -9,6 +9,7 @@ import ( "github.com/kluctl/kluctl/pkg/k8s" "github.com/kluctl/kluctl/pkg/kluctl_project" "github.com/kluctl/kluctl/pkg/types" + "github.com/kluctl/kluctl/pkg/utils" "github.com/kluctl/kluctl/pkg/utils/uo" "io/ioutil" "os" @@ -24,6 +25,13 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl return err } } + + tmpDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "project-") + if err != nil { + return fmt.Errorf("creating temporary project directory failed: %w", err) + } + defer os.RemoveAll(tmpDir) + j2, err := jinja2.NewJinja2() if err != nil { return err @@ -39,9 +47,13 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl LocalSealedSecrets: projectFlags.LocalSealedSecrets, FromArchive: projectFlags.FromArchive, FromArchiveMetadata: projectFlags.FromArchiveMetadata, - J2: j2, } - return kluctl_project.LoadKluctlProject(loadArgs, cb) + + p, err := kluctl_project.LoadKluctlProject(loadArgs, tmpDir, j2) + if err != nil { + return err + } + return cb(p) } type projectTargetCommandArgs struct { diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index eb187b3ad..238932081 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -5,6 +5,7 @@ import ( "compress/gzip" "fmt" "github.com/kluctl/kluctl/pkg/git" + "github.com/kluctl/kluctl/pkg/jinja2" types2 "github.com/kluctl/kluctl/pkg/types" "github.com/kluctl/kluctl/pkg/utils" "github.com/kluctl/kluctl/pkg/yaml" @@ -158,41 +159,35 @@ func (c *KluctlProjectContext) load(allowGit bool) error { return nil } -func LoadKluctlProject(args LoadKluctlProjectArgs, cb func(ctx *KluctlProjectContext) error) error { - tmpDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "project-") - if err != nil { - return fmt.Errorf("creating temporary project directory failed: %w", err) - } - defer os.RemoveAll(tmpDir) - +func LoadKluctlProject(args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*KluctlProjectContext, error) { if args.FromArchive != "" { if args.ProjectUrl != nil || args.ProjectRef != "" || args.ProjectConfig != "" || args.LocalClusters != "" || args.LocalDeployment != "" || args.LocalSealedSecrets != "" { - return fmt.Errorf("--from-archive can not be combined with any other project related option") + return nil, fmt.Errorf("--from-archive can not be combined with any other project related option") } - project, err := loadKluctlProjectFromArchive(args, tmpDir) + project, err := loadKluctlProjectFromArchive(args, tmpDir, j2) if err != nil { - return err + return nil, err } err = project.load(false) if err != nil { - return err + return nil, err } - return cb(project) + return project, nil } else { - p := NewKluctlProjectContext(args, tmpDir) - err = p.load(true) + p := NewKluctlProjectContext(args, tmpDir, j2) + err := p.load(true) if err != nil { - return err + return nil, err } err = p.loadTargets() if err != nil { - return err + return nil, err } - return cb(p) + return p, nil } } -func loadKluctlProjectFromArchive(args LoadKluctlProjectArgs, tmpDir string) (*KluctlProjectContext, error) { +func loadKluctlProjectFromArchive(args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*KluctlProjectContext, error) { var dir string if utils.IsFile(args.FromArchive) { err := utils.ExtractTarGzFile(args.FromArchive, tmpDir) @@ -217,14 +212,12 @@ func loadKluctlProjectFromArchive(args LoadKluctlProjectArgs, tmpDir string) (*K return nil, err } - p := NewKluctlProjectContext( - LoadKluctlProjectArgs{ - ProjectConfig: yaml.FixPathExt(filepath.Join(dir, ".kluctl.yml")), - LocalClusters: filepath.Join(dir, "clusters"), - LocalDeployment: filepath.Join(dir, "deployment"), - LocalSealedSecrets: filepath.Join(dir, "sealed-secrets"), - J2: args.J2, - }, dir) + p := NewKluctlProjectContext(LoadKluctlProjectArgs{ + ProjectConfig: yaml.FixPathExt(filepath.Join(dir, ".kluctl.yml")), + LocalClusters: filepath.Join(dir, "clusters"), + LocalDeployment: filepath.Join(dir, "deployment"), + LocalSealedSecrets: filepath.Join(dir, "sealed-secrets"), + }, dir, j2) p.involvedRepos = metadata.InvolvedRepos p.DynamicTargets = metadata.Targets return p, nil diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 0a73a2ece..8f44afee7 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -19,8 +19,6 @@ type LoadKluctlProjectArgs struct { LocalSealedSecrets string FromArchive string FromArchiveMetadata string - - J2 *jinja2.Jinja2 } type KluctlProjectContext struct { @@ -43,14 +41,14 @@ type KluctlProjectContext struct { J2 *jinja2.Jinja2 } -func NewKluctlProjectContext(loadArgs LoadKluctlProjectArgs, tmpDir string) *KluctlProjectContext { +func NewKluctlProjectContext(loadArgs LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) *KluctlProjectContext { o := &KluctlProjectContext{ loadArgs: loadArgs, TmpDir: tmpDir, gitAuthProviders: auth2.NewDefaultAuthProviders(), involvedRepos: make(map[string][]types.InvolvedRepo), mirroredRepos: make(map[string]*git.MirroredGitRepo), - J2: loadArgs.J2, + J2: j2, } return o } From a792dfbb27c60931f10568d5b530aed6186c684a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 19 Apr 2022 15:57:32 +0200 Subject: [PATCH 0014/2268] refactor: Split withProjectTargetCommandContext into withProjectTargetCommandContext and NewTargetContext --- cmd/kluctl/args/images.go | 4 +- .../commands/cmd_check_image_updates.go | 2 +- cmd/kluctl/commands/cmd_delete.go | 6 +- cmd/kluctl/commands/cmd_deploy.go | 4 +- cmd/kluctl/commands/cmd_diff.go | 4 +- cmd/kluctl/commands/cmd_downscale.go | 6 +- cmd/kluctl/commands/cmd_poke_images.go | 6 +- cmd/kluctl/commands/cmd_prune.go | 6 +- cmd/kluctl/commands/cmd_render.go | 2 +- cmd/kluctl/commands/cmd_seal.go | 27 ++-- cmd/kluctl/commands/cmd_validate.go | 4 +- cmd/kluctl/commands/utils.go | 126 +++--------------- pkg/kluctl_project/target_context.go | 119 +++++++++++++++++ 13 files changed, 172 insertions(+), 144 deletions(-) create mode 100644 pkg/kluctl_project/target_context.go diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index 6aabbeac2..f53ec2c58 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -13,7 +13,7 @@ type ImageFlags struct { UpdateImages bool `group:"images" short:"u" help:"This causes kluctl to prefer the latest image found in registries, based on the 'latest_image' filters provided to 'images.get_image(...)' calls. Use this flag if you want to update to the latest versions/tags of all images. '-u' takes precedence over '--fixed-image/--fixed-images-file', meaning that the latest images are used even if an older image is given via fixed images."` } -func (args *ImageFlags) LoadFixedImagesFromArgs() (*types.FixedImagesConfig, error) { +func (args *ImageFlags) LoadFixedImagesFromArgs() ([]types.FixedImage, error) { var ret types.FixedImagesConfig if args.FixedImagesFile != "" { @@ -31,7 +31,7 @@ func (args *ImageFlags) LoadFixedImagesFromArgs() (*types.FixedImagesConfig, err ret.Images = append(ret.Images, *e) } - return &ret, nil + return ret.Images, nil } func buildFixedImageEntryFromArg(arg string) (*types.FixedImage, error) { diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index 85ecfce71..74538ce07 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -30,7 +30,7 @@ func (cmd *checkImageUpdatesCmd) Run() error { targetFlags: cmd.TargetFlags, } err := withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - renderedImages = ctx.deploymentCollection.FindRenderedImages() + renderedImages = ctx.targetCtx.DeploymentCollection.FindRenderedImages() return nil }) if err != nil { diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 8374968cf..ae16159b6 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -44,7 +44,7 @@ func (cmd *deleteCmd) Run() error { dryRunArgs: &cmd.DryRunFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - cmd2 := commands.NewDeleteCommand(ctx.deploymentCollection) + cmd2 := commands.NewDeleteCommand(ctx.targetCtx.DeploymentCollection) deleteByLabels, err := deployment.ParseArgs(cmd.DeleteByLabel) if err != nil { @@ -53,11 +53,11 @@ func (cmd *deleteCmd) Run() error { cmd2.OverrideDeleteByLabels = deleteByLabels - objects, err := cmd2.Run(ctx.k) + objects, err := cmd2.Run(ctx.targetCtx.K) if err != nil { return err } - result, err := confirmedDeleteObjects(ctx.k, objects, cmd.DryRun, cmd.Yes) + result, err := confirmedDeleteObjects(ctx.targetCtx.K, objects, cmd.DryRun, cmd.Yes) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index fabd1952b..0f4d22915 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -49,7 +49,7 @@ func (cmd *deployCmd) Run() error { } func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { - cmd2 := commands.NewDeployCommand(ctx.deploymentCollection) + cmd2 := commands.NewDeployCommand(ctx.targetCtx.DeploymentCollection) cmd2.ForceApply = cmd.ForceApply cmd2.ReplaceOnError = cmd.ReplaceOnError cmd2.ForceReplaceOnError = cmd.ForceReplaceOnError @@ -62,7 +62,7 @@ func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { cb = nil } - result, err := cmd2.Run(ctx.k, cb) + result, err := cmd2.Run(ctx.targetCtx.K, cb) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 840617dd9..374926510 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -36,14 +36,14 @@ func (cmd *diffCmd) Run() error { renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - cmd2 := commands.NewDiffCommand(ctx.deploymentCollection) + cmd2 := commands.NewDiffCommand(ctx.targetCtx.DeploymentCollection) cmd2.ForceApply = cmd.ForceApply cmd2.ReplaceOnError = cmd.ReplaceOnError cmd2.ForceReplaceOnError = cmd.ForceReplaceOnError cmd2.IgnoreTags = cmd.IgnoreTags cmd2.IgnoreLabels = cmd.IgnoreLabels cmd2.IgnoreAnnotations = cmd.IgnoreAnnotations - result, err := cmd2.Run(ctx.k) + result, err := cmd2.Run(ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_downscale.go b/cmd/kluctl/commands/cmd_downscale.go index 184533519..d4afda0e6 100644 --- a/cmd/kluctl/commands/cmd_downscale.go +++ b/cmd/kluctl/commands/cmd_downscale.go @@ -37,14 +37,14 @@ func (cmd *downscaleCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { - if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to downscale on context/cluster %s?", ctx.k.Context())) { + if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to downscale on context/cluster %s?", ctx.targetCtx.K.Context())) { return fmt.Errorf("aborted") } } - cmd2 := commands.NewDownscaleCommand(ctx.deploymentCollection) + cmd2 := commands.NewDownscaleCommand(ctx.targetCtx.DeploymentCollection) - result, err := cmd2.Run(ctx.k) + result, err := cmd2.Run(ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 641713e45..0857ab8c0 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -37,14 +37,14 @@ func (cmd *pokeImagesCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { - if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", ctx.k.Context())) { + if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", ctx.targetCtx.K.Context())) { return fmt.Errorf("aborted") } } - cmd2 := commands.NewPokeImagesCommand(ctx.deploymentCollection) + cmd2 := commands.NewPokeImagesCommand(ctx.targetCtx.DeploymentCollection) - result, err := cmd2.Run(ctx.k) + result, err := cmd2.Run(ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 5881089ac..3712b8b3d 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -40,12 +40,12 @@ func (cmd *pruneCmd) Run() error { } func (cmd *pruneCmd) runCmdPrune(ctx *commandCtx) error { - cmd2 := commands.NewPruneCommand(ctx.deploymentCollection) - objects, err := cmd2.Run(ctx.k) + cmd2 := commands.NewPruneCommand(ctx.targetCtx.DeploymentCollection) + objects, err := cmd2.Run(ctx.targetCtx.K) if err != nil { return err } - result, err := confirmedDeleteObjects(ctx.k, objects, cmd.DryRun, cmd.Yes) + result, err := confirmedDeleteObjects(ctx.targetCtx.K, objects, cmd.DryRun, cmd.Yes) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 96c3093e4..abd550b00 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -37,7 +37,7 @@ func (cmd *renderCmd) Run() error { renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - log.Infof("Rendered into %s", ctx.deploymentCollection.RenderDir) + log.Infof("Rendered into %s", ctx.targetCtx.DeploymentCollection.RenderDir) return nil }) } diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 08b89809e..d48510ab2 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -29,7 +29,7 @@ If no '--target' is specified, sealing is performed for all targets.` } func findSecretsEntry(ctx *commandCtx, name string) (*types.SecretSet, error) { - for _, e := range ctx.kluctlProject.Config.SecretsConfig.SecretSets { + for _, e := range ctx.targetCtx.KluctlProject.Config.SecretsConfig.SecretSets { if e.Name == name { return &e, nil } @@ -46,7 +46,7 @@ func loadSecrets(ctx *commandCtx, target *types.Target, secretsLoader *seal.Secr } for _, source := range secretEntry.Sources { var renderedSource types.SecretSource - err = ctx.kluctlProject.J2.RenderStruct(&renderedSource, &source, ctx.deploymentProject.VarsCtx.Vars) + err = ctx.targetCtx.KluctlProject.J2.RenderStruct(&renderedSource, &source, ctx.targetCtx.DeploymentProject.VarsCtx.Vars) if err != nil { return err } @@ -57,25 +57,26 @@ func loadSecrets(ctx *commandCtx, target *types.Target, secretsLoader *seal.Secr secrets.Merge(s) } } - ctx.deploymentProject.MergeSecretsIntoAllChildren(secrets) + ctx.targetCtx.DeploymentProject.MergeSecretsIntoAllChildren(secrets) return nil } -func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.KluctlProjectContext, target *types.Target, secretsLoader *seal.SecretsLoader) error { - log.Infof("Sealing for target %s", target.Name) +func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.KluctlProjectContext, targetName string, secretsLoader *seal.SecretsLoader) error { + log.Infof("Sealing for target %s", targetName) ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, } + ptArgs.targetFlags.Target = targetName // pass forSeal=True so that .sealme files are rendered as well - return withProjectTargetCommandContext(ptArgs, p, target, true, func(ctx *commandCtx) error { - err := loadSecrets(ctx, target, secretsLoader) + return withProjectTargetCommandContext(ptArgs, p, true, func(ctx *commandCtx) error { + err := loadSecrets(ctx, ctx.targetCtx.Target, secretsLoader) if err != nil { return err } - err = ctx.deploymentCollection.RenderDeployments(ctx.k) + err = ctx.targetCtx.DeploymentCollection.RenderDeployments(ctx.targetCtx.K) if err != nil { return err } @@ -91,22 +92,22 @@ func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.KluctlProjectContext, } } if p.Config.SecretsConfig == nil || p.Config.SecretsConfig.SealedSecrets == nil || p.Config.SecretsConfig.SealedSecrets.Bootstrap == nil || *p.Config.SecretsConfig.SealedSecrets.Bootstrap { - err = seal.BootstrapSealedSecrets(ctx.k, sealedSecretsNamespace) + err = seal.BootstrapSealedSecrets(ctx.targetCtx.K, sealedSecretsNamespace) if err != nil { return err } } - clusterConfig, err := p.LoadClusterConfig(target.Cluster) + clusterConfig, err := p.LoadClusterConfig(ctx.targetCtx.Target.Cluster) if err != nil { return err } - sealer, err := seal.NewSealer(ctx.k, sealedSecretsNamespace, sealedSecretsControllerName, clusterConfig.Cluster, cmd.ForceReseal) + sealer, err := seal.NewSealer(ctx.targetCtx.K, sealedSecretsNamespace, sealedSecretsControllerName, clusterConfig.Cluster, cmd.ForceReseal) if err != nil { return err } - cmd2 := commands.NewSealCommand(ctx.deploymentCollection) + cmd2 := commands.NewSealCommand(ctx.targetCtx.DeploymentCollection) err = cmd2.Run(sealer) if err != nil { @@ -153,7 +154,7 @@ func (cmd *sealCmd) Run() error { sealTarget = baseTarget } - err := cmd.runCmdSealForTarget(p, sealTarget, secretsLoader) + err := cmd.runCmdSealForTarget(p, sealTarget.Name, secretsLoader) if err != nil { log.Warningf("Sealing for target %s failed: %v", sealTarget.Name, err) hadError = true diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index f97c00f20..6008a4269 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -37,9 +37,9 @@ func (cmd *validateCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { startTime := time.Now() - cmd2 := commands.NewValidateCommand(ctx.deploymentCollection) + cmd2 := commands.NewValidateCommand(ctx.targetCtx.DeploymentCollection) for true { - result, err := cmd2.Run(ctx.k) + result, err := cmd2.Run(ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 0db35e851..27fa9a832 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -6,14 +6,10 @@ import ( "github.com/kluctl/kluctl/pkg/deployment" git_url "github.com/kluctl/kluctl/pkg/git/git-url" "github.com/kluctl/kluctl/pkg/jinja2" - "github.com/kluctl/kluctl/pkg/k8s" "github.com/kluctl/kluctl/pkg/kluctl_project" - "github.com/kluctl/kluctl/pkg/types" "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" "io/ioutil" "os" - "path/filepath" ) func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl_project.KluctlProjectContext) error) error { @@ -67,62 +63,27 @@ type projectTargetCommandArgs struct { } type commandCtx struct { - kluctlProject *kluctl_project.KluctlProjectContext - target *types.Target - clusterConfig *types.ClusterConfig - k *k8s.K8sCluster - images *deployment.Images - deploymentProject *deployment.DeploymentProject - deploymentCollection *deployment.DeploymentCollection + targetCtx *kluctl_project.TargetContext + images *deployment.Images } func withProjectCommandContext(args projectTargetCommandArgs, cb func(ctx *commandCtx) error) error { return withKluctlProjectFromArgs(args.projectFlags, func(p *kluctl_project.KluctlProjectContext) error { - var target *types.Target - if args.targetFlags.Target != "" { - t, err := p.FindDynamicTarget(args.targetFlags.Target) - if err != nil { - return err - } - target = t.Target - } - return withProjectTargetCommandContext(args, p, target, false, cb) + return withProjectTargetCommandContext(args, p, false, cb) }) } -func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_project.KluctlProjectContext, target *types.Target, forSeal bool, cb func(ctx *commandCtx) error) error { - deploymentDir, err := filepath.Abs(p.DeploymentDir) - if err != nil { - return err - } - - clusterName := args.projectFlags.Cluster - if clusterName == "" { - if target == nil { - return fmt.Errorf("you must specify an existing --cluster when not providing a --target") - } - clusterName = target.Cluster - } - - clusterConfig, err := p.LoadClusterConfig(clusterName) - if err != nil { - return err - } - - k, err := k8s.NewK8sCluster(clusterConfig.Cluster.Context, args.dryRunArgs == nil || args.dryRunArgs.DryRun) +func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_project.KluctlProjectContext, forSeal bool, cb func(ctx *commandCtx) error) error { + images, err := deployment.NewImages(args.imageFlags.UpdateImages) if err != nil { return err } - - varsCtx := jinja2.NewVarsCtx(p.J2) - err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) + fixedImages, err := args.imageFlags.LoadFixedImagesFromArgs() if err != nil { return err } - - images, err := deployment.NewImages(args.imageFlags.UpdateImages) - if err != nil { - return err + for _, fi := range fixedImages { + images.AddFixedImage(fi) } inclusion, err := args.inclusionFlags.ParseInclusionFromArgs() @@ -130,44 +91,10 @@ func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_pr return err } - allArgs := uo.New() - optionArgs, err := deployment.ParseArgs(args.argsFlags.Arg) if err != nil { return err } - if target != nil { - for argName, argValue := range optionArgs { - err = p.CheckDynamicArg(target, argName, argValue) - if err != nil { - return err - } - } - } - allArgs.Merge(deployment.ConvertArgsToVars(optionArgs)) - if target != nil { - if target.Args != nil { - allArgs.Merge(target.Args) - } - if forSeal { - if target.SealingConfig.Args != nil { - allArgs.Merge(target.SealingConfig.Args) - } - } - } - - err = deployment.CheckRequiredDeployArgs(deploymentDir, varsCtx, allArgs) - if err != nil { - return err - } - - varsCtx.UpdateChild("args", allArgs) - - targetVars, err := uo.FromStruct(target) - if err != nil { - return err - } - varsCtx.UpdateChild("target", targetVars) renderOutputDir := args.renderOutputDirFlags.RenderOutputDir if renderOutputDir == "" { @@ -179,44 +106,25 @@ func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_pr renderOutputDir = tmpDir } - d, err := deployment.NewDeploymentProject(k, varsCtx, deploymentDir, p.SealedSecretsDir, nil) + ctx, err := p.NewTargetContext(args.targetFlags.Target, args.projectFlags.Cluster, + args.dryRunArgs == nil || args.dryRunArgs.DryRun, + optionArgs, forSeal, images, inclusion, + renderOutputDir) if err != nil { return err } - c, err := deployment.NewDeploymentCollection(d, images, inclusion, renderOutputDir, forSeal) - if err != nil { - return err - } - - fixedImages, err := args.imageFlags.LoadFixedImagesFromArgs() - if err != nil { - return err - } - if target != nil { - for _, fi := range target.Images { - images.AddFixedImage(fi) - } - } - for _, fi := range fixedImages.Images { - images.AddFixedImage(fi) - } if !forSeal { - err = c.Prepare(k) + err = ctx.DeploymentCollection.Prepare(ctx.K) if err != nil { return err } } - ctx := &commandCtx{ - kluctlProject: p, - target: target, - clusterConfig: clusterConfig, - k: k, - images: images, - deploymentProject: d, - deploymentCollection: c, + cmdCtx := &commandCtx{ + targetCtx: ctx, + images: images, } - return cb(ctx) + return cb(cmdCtx) } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go new file mode 100644 index 000000000..d863fe8c2 --- /dev/null +++ b/pkg/kluctl_project/target_context.go @@ -0,0 +1,119 @@ +package kluctl_project + +import ( + "fmt" + "github.com/kluctl/kluctl/pkg/deployment" + "github.com/kluctl/kluctl/pkg/jinja2" + "github.com/kluctl/kluctl/pkg/k8s" + "github.com/kluctl/kluctl/pkg/types" + "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/pkg/utils/uo" + "path/filepath" +) + +type TargetContext struct { + KluctlProject *KluctlProjectContext + Target *types.Target + ClusterConfig *types.ClusterConfig + K *k8s.K8sCluster + DeploymentProject *deployment.DeploymentProject + DeploymentCollection *deployment.DeploymentCollection +} + +func (p *KluctlProjectContext) NewTargetContext(targetName string, clusterName string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { + deploymentDir, err := filepath.Abs(p.DeploymentDir) + if err != nil { + return nil, err + } + + var target *types.Target + if targetName != "" { + t, err := p.FindDynamicTarget(targetName) + if err != nil { + return nil, err + } + target = t.Target + + for _, fi := range target.Images { + images.AddFixedImage(fi) + } + } + + if clusterName == "" { + if target == nil { + return nil, fmt.Errorf("you must specify an existing --cluster when not providing a --target") + } + clusterName = target.Cluster + } + + clusterConfig, err := p.LoadClusterConfig(clusterName) + if err != nil { + return nil, err + } + + k, err := k8s.NewK8sCluster(clusterConfig.Cluster.Context, dryRun) + if err != nil { + return nil, err + } + + varsCtx := jinja2.NewVarsCtx(p.J2) + err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) + if err != nil { + return nil, err + } + + allArgs := uo.New() + + if target != nil { + for argName, argValue := range args { + err = p.CheckDynamicArg(target, argName, argValue) + if err != nil { + return nil, err + } + } + } + allArgs.Merge(deployment.ConvertArgsToVars(args)) + if target != nil { + if target.Args != nil { + allArgs.Merge(target.Args) + } + if forSeal { + if target.SealingConfig.Args != nil { + allArgs.Merge(target.SealingConfig.Args) + } + } + } + + err = deployment.CheckRequiredDeployArgs(deploymentDir, varsCtx, allArgs) + if err != nil { + return nil, err + } + + varsCtx.UpdateChild("args", allArgs) + + targetVars, err := uo.FromStruct(target) + if err != nil { + return nil, err + } + varsCtx.UpdateChild("target", targetVars) + + d, err := deployment.NewDeploymentProject(k, varsCtx, deploymentDir, p.SealedSecretsDir, nil) + if err != nil { + return nil, err + } + c, err := deployment.NewDeploymentCollection(d, images, inclusion, renderOutputDir, forSeal) + if err != nil { + return nil, err + } + + ctx := &TargetContext{ + KluctlProject: p, + Target: target, + ClusterConfig: clusterConfig, + K: k, + DeploymentProject: d, + DeploymentCollection: c, + } + + return ctx, nil +} From a45494f36c9931da7fc048b9b429235665fdb5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aljoscha=20P=C3=B6rtner?= Date: Tue, 19 Apr 2022 20:51:07 +0200 Subject: [PATCH 0015/2268] refactor: remove envs and download python for each arch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Aljoscha Pörtner --- Makefile | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 6be0074e8..b74eb7b59 100644 --- a/Makefile +++ b/Makefile @@ -14,14 +14,11 @@ WHITE := $(shell tput -Txterm setaf 7) CYAN := $(shell tput -Txterm setaf 6) RESET := $(shell tput -Txterm sgr0) -.PHONY: all test build vendor check-env check-kubectl check-helm check-kind +.PHONY: all test build vendor check-kubectl check-helm check-kind all: help ## Check: -check-env: ## Checks if required environment variables are set - @test $${GOOS?Please set environment variable GOOS} - @test $${GOARCH?Please set environment variable GOARCH} check-kubectl: ## Checks if kubectl is installed kubectl version --client=true @@ -35,9 +32,9 @@ check-kind: ## Checks if kind is installed ## Build: build: vendor python generate build-go ## Run the complete build pipeline -build-go: check-env ## Build your project and put the output binary in out/bin/ +build-go: ## Build your project and put the output binary in out/bin/ mkdir -p out/bin - CGO_ENBALED=0 GO111MODULE=on GOARCH=$(GOARCH) GOOS=$(GOOS) $(GOCMD) build -mod vendor -o out/bin/$(BINARY_NAME) ./cmd/kluctl + CGO_ENBALED=0 GO111MODULE=on $(GOCMD) build -mod vendor -o out/bin/$(BINARY_NAME) ./cmd/kluctl clean: ## Remove build related file rm -fr ./bin @@ -48,18 +45,20 @@ clean: ## Remove build related file vendor: ## Copy of all packages needed to support builds and tests in the vendor directory $(GOCMD) mod vendor -python: check-env ## Download python for Jinja2 support - ./hack/download-python.sh $(GOARCH) +python: ## Download python for Jinja2 support + ./hack/download-python.sh linux + ./hack/download-python.sh windows + ./hack/download-python.sh darwin generate: ## Generating Jinja2 support $(GOCMD) generate ./... - $(GOCMD) generate -tags $(GOOS) ./pkg/python + $(GOCMD) generate ./pkg/python ## Test: test: test-unit test-e2e ## Runs the complete test suite -test-e2e: check-env check-kubectl check-helm check-kind ## Runs the end to end tests - CGO_ENBALED=0 GO111MODULE=on GOARCH=$(GOARCH) GOOS=$(GOOS) $(GOCMD) test -o out/bin/$(TEST_BINARY_NAME) ./e2e +test-e2e: check-kubectl check-helm check-kind ## Runs the end to end tests + CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -o out/bin/$(TEST_BINARY_NAME) ./e2e test-unit: ## Run the unit tests of the project ifeq ($(EXPORT_RESULT), true) From d878db43a5a6efae966367601783e85995b54627 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 19 Apr 2022 22:56:25 +0200 Subject: [PATCH 0016/2268] fix: Fix module to include v2 --- cmd/kluctl/args/images.go | 4 ++-- cmd/kluctl/args/inclusion.go | 2 +- cmd/kluctl/commands/cmd_archive.go | 4 ++-- cmd/kluctl/commands/cmd_check_image_updates.go | 10 +++++----- cmd/kluctl/commands/cmd_delete.go | 16 ++++++++-------- cmd/kluctl/commands/cmd_deploy.go | 8 ++++---- cmd/kluctl/commands/cmd_diff.go | 4 ++-- cmd/kluctl/commands/cmd_downscale.go | 6 +++--- cmd/kluctl/commands/cmd_helm_pull.go | 2 +- cmd/kluctl/commands/cmd_helm_update.go | 4 ++-- cmd/kluctl/commands/cmd_list_images.go | 4 ++-- cmd/kluctl/commands/cmd_list_targets.go | 6 +++--- cmd/kluctl/commands/cmd_poke_images.go | 6 +++--- cmd/kluctl/commands/cmd_prune.go | 4 ++-- cmd/kluctl/commands/cmd_render.go | 4 ++-- cmd/kluctl/commands/cmd_seal.go | 12 ++++++------ cmd/kluctl/commands/cmd_validate.go | 4 ++-- cmd/kluctl/commands/cmd_version.go | 2 +- cmd/kluctl/commands/command_result.go | 8 ++++---- cmd/kluctl/commands/root.go | 10 +++++----- cmd/kluctl/commands/utils.go | 12 ++++++------ cmd/kluctl/main.go | 2 +- e2e/external_projects_test.go | 2 +- e2e/hooks_test.go | 2 +- e2e/kind_cluster.go | 6 +++--- e2e/project.go | 8 ++++---- e2e/utils.go | 4 ++-- e2e/utils_resources.go | 2 +- go.mod | 2 +- pkg/deployment/commands/delete.go | 8 ++++---- pkg/deployment/commands/deploy.go | 8 ++++---- pkg/deployment/commands/diff.go | 8 ++++---- pkg/deployment/commands/downscale.go | 12 ++++++------ pkg/deployment/commands/poke_images.go | 12 ++++++------ pkg/deployment/commands/prune.go | 8 ++++---- pkg/deployment/commands/seal.go | 4 ++-- pkg/deployment/commands/validate.go | 12 ++++++------ pkg/deployment/deployment_collection.go | 8 ++++---- pkg/deployment/deployment_item.go | 10 +++++----- pkg/deployment/deployment_project.go | 12 ++++++------ pkg/deployment/external_args.go | 8 ++++---- pkg/deployment/helm_chart.go | 12 ++++++------ pkg/deployment/images.go | 16 ++++++++-------- pkg/deployment/utils/apply_utils.go | 16 ++++++++-------- pkg/deployment/utils/delete_utils.go | 8 ++++---- pkg/deployment/utils/diff_utils.go | 10 +++++----- pkg/deployment/utils/diff_utils_test.go | 8 ++++---- pkg/deployment/utils/downscale_utils.go | 4 ++-- pkg/deployment/utils/errors_holder.go | 8 ++++---- pkg/deployment/utils/hooks_util.go | 8 ++++---- pkg/deployment/utils/remote_objects_utils.go | 8 ++++---- pkg/diff/diff.go | 6 +++--- pkg/diff/managed_fields.go | 2 +- pkg/diff/normalize.go | 4 ++-- pkg/git/auth/auth_provider.go | 2 +- pkg/git/auth/env_auth_provider.go | 4 ++-- pkg/git/auth/git_credentials_file.go | 4 ++-- pkg/git/auth/ssh_auth_provider.go | 4 ++-- pkg/git/auth/ssh_known_hosts.go | 4 ++-- pkg/git/http-server/http.go | 2 +- pkg/git/http-server/utils.go | 2 +- pkg/git/mirrored_repo.go | 6 +++--- pkg/git/poor_mans_clone.go | 2 +- pkg/git/utils.go | 2 +- pkg/jinja2/jinja2.go | 4 ++-- pkg/jinja2/jinja2_renderer.go | 4 ++-- pkg/jinja2/source.go | 4 ++-- pkg/jinja2/vars.go | 10 +++++----- pkg/k8s/k8s_cluster.go | 8 ++++---- pkg/k8s/utils.go | 2 +- pkg/kluctl_project/git.go | 8 ++++---- pkg/kluctl_project/load.go | 10 +++++----- pkg/kluctl_project/load_targets.go | 12 ++++++------ pkg/kluctl_project/project.go | 10 +++++----- pkg/kluctl_project/target_context.go | 12 ++++++------ pkg/python/embed.go | 4 ++-- pkg/registries/registries.go | 4 ++-- pkg/seal/bootstrap.go | 6 +++--- pkg/seal/fetch_cert.go | 2 +- pkg/seal/sealer.go | 12 ++++++------ pkg/seal/secrets_loader.go | 10 +++++----- pkg/types/cluster_config.go | 6 +++--- pkg/types/command_result.go | 4 ++-- pkg/types/deployment.go | 4 ++-- pkg/types/git_project.go | 4 ++-- pkg/types/kluctl_project.go | 4 ++-- pkg/types/secrets_source.go | 2 +- pkg/types/target_config.go | 4 ++-- pkg/utils/embed_util/extract.go | 2 +- pkg/utils/embed_util/packer/main.go | 2 +- pkg/utils/uo/k8s_fields.go | 2 +- pkg/utils/uo/unstructured.go | 2 +- pkg/utils/uo/uo.go | 2 +- pkg/utils/versions/latest_version.go | 2 +- pkg/utils/versions/latest_version_parse.go | 2 +- pkg/utils/versions/looseversion.go | 2 +- pkg/validation/validation.go | 6 +++--- pkg/yaml/yaml.go | 2 +- 98 files changed, 293 insertions(+), 293 deletions(-) diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index f53ec2c58..bb9de4568 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -2,8 +2,8 @@ package args import ( "fmt" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/yaml" "strings" ) diff --git a/cmd/kluctl/args/inclusion.go b/cmd/kluctl/args/inclusion.go index 85670f009..060a6af97 100644 --- a/cmd/kluctl/args/inclusion.go +++ b/cmd/kluctl/args/inclusion.go @@ -2,7 +2,7 @@ package args import ( "fmt" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils" "os" "path/filepath" "strings" diff --git a/cmd/kluctl/commands/cmd_archive.go b/cmd/kluctl/commands/cmd_archive.go index dbe3bb83c..90977ace4 100644 --- a/cmd/kluctl/commands/cmd_archive.go +++ b/cmd/kluctl/commands/cmd_archive.go @@ -1,8 +1,8 @@ package commands import ( - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" ) type archiveCmd struct { diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index 74538ce07..8d65ee305 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -1,11 +1,11 @@ package commands import ( - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/registries" - "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/versions" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/registries" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/versions" log "github.com/sirupsen/logrus" "os" "regexp" diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index ae16159b6..c078332de 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -2,14 +2,14 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/deployment" - "github.com/kluctl/kluctl/pkg/deployment/commands" - "github.com/kluctl/kluctl/pkg/deployment/utils" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - utils2 "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + utils2 "github.com/kluctl/kluctl/v2/pkg/utils" "os" ) diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 0f4d22915..15dcea2f2 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -2,10 +2,10 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/deployment/commands" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" ) type deployCmd struct { diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 374926510..0f7f70b63 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -2,8 +2,8 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" ) type diffCmd struct { diff --git a/cmd/kluctl/commands/cmd_downscale.go b/cmd/kluctl/commands/cmd_downscale.go index d4afda0e6..e5383e3b2 100644 --- a/cmd/kluctl/commands/cmd_downscale.go +++ b/cmd/kluctl/commands/cmd_downscale.go @@ -2,9 +2,9 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/deployment/commands" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/pkg/utils" ) type downscaleCmd struct { diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index b2dcc01b8..38aec11c5 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -1,7 +1,7 @@ package commands import ( - "github.com/kluctl/kluctl/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/deployment" log "github.com/sirupsen/logrus" "io/fs" "path/filepath" diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index daa48b4c7..f74b41aaa 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -3,8 +3,8 @@ package commands import ( "fmt" "github.com/go-git/go-git/v5" - "github.com/kluctl/kluctl/pkg/deployment" - git2 "github.com/kluctl/kluctl/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/deployment" + git2 "github.com/kluctl/kluctl/v2/pkg/git" log "github.com/sirupsen/logrus" "io/fs" "path/filepath" diff --git a/cmd/kluctl/commands/cmd_list_images.go b/cmd/kluctl/commands/cmd_list_images.go index eeeb58986..80bd70382 100644 --- a/cmd/kluctl/commands/cmd_list_images.go +++ b/cmd/kluctl/commands/cmd_list_images.go @@ -1,8 +1,8 @@ package commands import ( - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/types" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/types" ) type listImagesCmd struct { diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index 93984dbe0..29f9036f3 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -1,9 +1,9 @@ package commands import ( - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/kluctl_project" - "github.com/kluctl/kluctl/pkg/types" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/types" ) type listTargetsCmd struct { diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 0857ab8c0..a6898ea11 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -2,9 +2,9 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/deployment/commands" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/pkg/utils" ) type pokeImagesCmd struct { diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 3712b8b3d..2ef240e88 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -2,8 +2,8 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" ) type pruneCmd struct { diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index abd550b00..a686da313 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -1,8 +1,8 @@ package commands import ( - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" "io/ioutil" ) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index d48510ab2..185c07f44 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -2,12 +2,12 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/deployment/commands" - "github.com/kluctl/kluctl/pkg/kluctl_project" - "github.com/kluctl/kluctl/pkg/seal" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/seal" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" log "github.com/sirupsen/logrus" ) diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index 6008a4269..4a9bf23e4 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -2,8 +2,8 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "os" "time" ) diff --git a/cmd/kluctl/commands/cmd_version.go b/cmd/kluctl/commands/cmd_version.go index 734083a64..de05a2d49 100644 --- a/cmd/kluctl/commands/cmd_version.go +++ b/cmd/kluctl/commands/cmd_version.go @@ -1,7 +1,7 @@ package commands import ( - "github.com/kluctl/kluctl/pkg/version" + "github.com/kluctl/kluctl/v2/pkg/version" "os" ) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index 12dc4f46d..bd2e8cd59 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -3,10 +3,10 @@ package commands import ( "bytes" "fmt" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/yaml" "io" "os" "strings" diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index e1efc9e0e..0b20a914b 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -17,11 +17,11 @@ package commands import ( "github.com/alecthomas/kong" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/utils/versions" - "github.com/kluctl/kluctl/pkg/version" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils/versions" + "github.com/kluctl/kluctl/v2/pkg/version" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "net/http" "os" diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 27fa9a832..bfcbf341e 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -2,12 +2,12 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/cmd/kluctl/args" - "github.com/kluctl/kluctl/pkg/deployment" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" - "github.com/kluctl/kluctl/pkg/jinja2" - "github.com/kluctl/kluctl/pkg/kluctl_project" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/deployment" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/utils" "io/ioutil" "os" ) diff --git a/cmd/kluctl/main.go b/cmd/kluctl/main.go index fbc4cdde9..e95adcccb 100644 --- a/cmd/kluctl/main.go +++ b/cmd/kluctl/main.go @@ -15,7 +15,7 @@ limitations under the License. */ package main -import "github.com/kluctl/kluctl/cmd/kluctl/commands" +import "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" func main() { commands.Execute() diff --git a/e2e/external_projects_test.go b/e2e/external_projects_test.go index 9429aa372..87a972634 100644 --- a/e2e/external_projects_test.go +++ b/e2e/external_projects_test.go @@ -2,7 +2,7 @@ package e2e import ( "fmt" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "testing" "time" ) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index d5cf16b6f..7e6b4557e 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -3,7 +3,7 @@ package e2e import ( "encoding/base64" "fmt" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "testing" ) diff --git a/e2e/kind_cluster.go b/e2e/kind_cluster.go index 9bbfd0e43..f420001a2 100644 --- a/e2e/kind_cluster.go +++ b/e2e/kind_cluster.go @@ -2,9 +2,9 @@ package e2e import ( "fmt" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "k8s.io/client-go/rest" diff --git a/e2e/project.go b/e2e/project.go index bde8aa916..c19bd3b67 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -4,10 +4,10 @@ import ( "context" "fmt" "github.com/go-git/go-git/v5" - http_server "github.com/kluctl/kluctl/pkg/git/http-server" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "io/ioutil" "net" diff --git a/e2e/utils.go b/e2e/utils.go index ad2c8930e..1a1abc672 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -3,8 +3,8 @@ package e2e import ( "bufio" "bytes" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/validation" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/validation" "io" "os/exec" "reflect" diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index aaff14756..255e07476 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -2,7 +2,7 @@ package e2e import ( "bytes" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "text/template" ) diff --git a/go.mod b/go.mod index 30c91dca7..7b7f6a544 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/kluctl/kluctl +module github.com/kluctl/kluctl/v2 go 1.17 diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index aaa60c889..872e8a7ad 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -1,10 +1,10 @@ package commands import ( - "github.com/kluctl/kluctl/pkg/deployment" - utils2 "github.com/kluctl/kluctl/pkg/deployment/utils" - "github.com/kluctl/kluctl/pkg/k8s" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/deployment" + utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" ) type DeleteCommand struct { diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index e90591635..ed762c0e1 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -1,10 +1,10 @@ package commands import ( - "github.com/kluctl/kluctl/pkg/deployment" - utils2 "github.com/kluctl/kluctl/pkg/deployment/utils" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/deployment" + utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" "time" ) diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index 106c98048..c7e35a3d5 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -1,10 +1,10 @@ package commands import ( - "github.com/kluctl/kluctl/pkg/deployment" - "github.com/kluctl/kluctl/pkg/deployment/utils" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" ) type DiffCommand struct { diff --git a/pkg/deployment/commands/downscale.go b/pkg/deployment/commands/downscale.go index 5bede4036..edd25944e 100644 --- a/pkg/deployment/commands/downscale.go +++ b/pkg/deployment/commands/downscale.go @@ -1,12 +1,12 @@ package commands import ( - "github.com/kluctl/kluctl/pkg/deployment" - utils2 "github.com/kluctl/kluctl/pkg/deployment/utils" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/deployment" + utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "sync" ) diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 91ecf397e..9e2fc9173 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -2,12 +2,12 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/pkg/deployment" - utils2 "github.com/kluctl/kluctl/pkg/deployment/utils" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/deployment" + utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "sync" ) diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index c434551df..873dc3a11 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -1,10 +1,10 @@ package commands import ( - "github.com/kluctl/kluctl/pkg/deployment" - utils2 "github.com/kluctl/kluctl/pkg/deployment/utils" - "github.com/kluctl/kluctl/pkg/k8s" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/deployment" + utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" ) type PruneCommand struct { diff --git a/pkg/deployment/commands/seal.go b/pkg/deployment/commands/seal.go index 2d3b2f6df..3a91f8b87 100644 --- a/pkg/deployment/commands/seal.go +++ b/pkg/deployment/commands/seal.go @@ -2,8 +2,8 @@ package commands import ( "fmt" - "github.com/kluctl/kluctl/pkg/deployment" - "github.com/kluctl/kluctl/pkg/seal" + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/seal" "io/fs" "path/filepath" "strings" diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index a067d7832..3a94cc032 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -1,12 +1,12 @@ package commands import ( - "github.com/kluctl/kluctl/pkg/deployment" - utils2 "github.com/kluctl/kluctl/pkg/deployment/utils" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/validation" + "github.com/kluctl/kluctl/v2/pkg/deployment" + utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/validation" ) type ValidateCommand struct { diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index b84c9e11e..887e903bf 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -3,10 +3,10 @@ package deployment import ( "context" "fmt" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" "golang.org/x/sync/semaphore" "path/filepath" diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 4eb16cd43..e723c480e 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -2,11 +2,11 @@ package deployment import ( "fmt" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" "io/fs" "io/ioutil" "os" diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 1b82a17e5..8193ff13d 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -2,12 +2,12 @@ package deployment import ( "fmt" - "github.com/kluctl/kluctl/pkg/jinja2" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "path/filepath" "reflect" diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index 70e203353..b6db19f41 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -2,10 +2,10 @@ package deployment import ( "fmt" - "github.com/kluctl/kluctl/pkg/jinja2" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" "path/filepath" "regexp" "strings" diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 3b0dbaac6..0b26b404c 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -2,12 +2,12 @@ package deployment import ( "fmt" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/utils/versions" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils/versions" + "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" "os" "os/exec" diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 25c04a7c9..654c72025 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -4,14 +4,14 @@ import ( "bytes" "encoding/base64" "fmt" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/registries" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/utils/versions" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/registries" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils/versions" + "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/apimachinery/pkg/api/errors" "strings" "sync" diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index a9aafc2b3..ebbca95c1 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -4,14 +4,14 @@ import ( "context" errors2 "errors" "fmt" - "github.com/kluctl/kluctl/pkg/deployment" - "github.com/kluctl/kluctl/pkg/diff" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/validation" + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/diff" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/validation" log "github.com/sirupsen/logrus" "github.com/vbauerster/mpb/v7" "golang.org/x/sync/semaphore" diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index f83cce908..655f4a1ca 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -2,10 +2,10 @@ package utils import ( "context" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "golang.org/x/sync/semaphore" "k8s.io/apimachinery/pkg/runtime/schema" "strconv" diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index 611523ed4..3690ecdd8 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -1,11 +1,11 @@ package utils import ( - "github.com/kluctl/kluctl/pkg/deployment" - "github.com/kluctl/kluctl/pkg/diff" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/diff" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "sort" "sync" "time" diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index 2e245c5eb..bb494fcb4 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -1,10 +1,10 @@ package utils import ( - "github.com/kluctl/kluctl/pkg/deployment" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" "testing" ) diff --git a/pkg/deployment/utils/downscale_utils.go b/pkg/deployment/utils/downscale_utils.go index 64dd5e87d..aed46dd92 100644 --- a/pkg/deployment/utils/downscale_utils.go +++ b/pkg/deployment/utils/downscale_utils.go @@ -3,8 +3,8 @@ package utils import ( "fmt" jsonpatch "github.com/evanphx/json-patch" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/apimachinery/pkg/runtime/schema" "regexp" "strconv" diff --git a/pkg/deployment/utils/errors_holder.go b/pkg/deployment/utils/errors_holder.go index 5a6ef2ccd..92c184dda 100644 --- a/pkg/deployment/utils/errors_holder.go +++ b/pkg/deployment/utils/errors_holder.go @@ -3,10 +3,10 @@ package utils import ( "errors" "fmt" - k8s2 "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" + k8s2 "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" "sync" ) diff --git a/pkg/deployment/utils/hooks_util.go b/pkg/deployment/utils/hooks_util.go index 23426ab5a..bee3b6811 100644 --- a/pkg/deployment/utils/hooks_util.go +++ b/pkg/deployment/utils/hooks_util.go @@ -2,10 +2,10 @@ package utils import ( "fmt" - "github.com/kluctl/kluctl/pkg/deployment" - "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "sort" "strconv" "strings" diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index 262a5baba..71df19fbe 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -1,10 +1,10 @@ package utils import ( - "github.com/kluctl/kluctl/pkg/k8s" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/k8s" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime/schema" "sync" diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index 1ae783d7a..e490578a5 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -5,9 +5,9 @@ import ( "github.com/hexops/gotextdiff" "github.com/hexops/gotextdiff/myers" "github.com/hexops/gotextdiff/span" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" diff2 "github.com/r3labs/diff/v2" "log" "reflect" diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index 294c3bbb3..5ac3f6261 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -3,7 +3,7 @@ package diff import ( "bytes" "fmt" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" log "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" diff --git a/pkg/diff/normalize.go b/pkg/diff/normalize.go index 53a4b57f8..f2599dc97 100644 --- a/pkg/diff/normalize.go +++ b/pkg/diff/normalize.go @@ -2,8 +2,8 @@ package diff import ( "fmt" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "regexp" "strconv" "strings" diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 99a7ba4e5..6c8872bd6 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -2,7 +2,7 @@ package auth import ( "github.com/go-git/go-git/v5/plumbing/transport" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ) type GitAuthProvider interface { diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index 902b65212..2755ab710 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -4,8 +4,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/plumbing/transport/ssh" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" - "github.com/kluctl/kluctl/pkg/utils" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" "strings" ) diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index e2a497bee..49fbe894e 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -4,8 +4,8 @@ import ( "bufio" "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/http" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" - "github.com/kluctl/kluctl/pkg/utils" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" giturls "github.com/whilp/git-urls" "os" diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index d195fe85c..6da3e0e98 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/go-git/go-git/v5/plumbing/transport" "github.com/kevinburke/ssh_config" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" - "github.com/kluctl/kluctl/pkg/utils" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" sshagent "github.com/xanzy/ssh-agent" "golang.org/x/crypto/ssh" diff --git a/pkg/git/auth/ssh_known_hosts.go b/pkg/git/auth/ssh_known_hosts.go index 8e6891aec..2ad01941d 100644 --- a/pkg/git/auth/ssh_known_hosts.go +++ b/pkg/git/auth/ssh_known_hosts.go @@ -2,8 +2,8 @@ package auth import ( "fmt" - "github.com/kluctl/kluctl/pkg/git/auth/goph" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/git/auth/goph" + "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/crypto/ssh" "net" "os" diff --git a/pkg/git/http-server/http.go b/pkg/git/http-server/http.go index 8bc76c64a..c9da65ee0 100644 --- a/pkg/git/http-server/http.go +++ b/pkg/git/http-server/http.go @@ -4,7 +4,7 @@ package http_server import ( "compress/gzip" "fmt" - process2 "github.com/kluctl/kluctl/pkg/utils/process" + process2 "github.com/kluctl/kluctl/v2/pkg/utils/process" log "github.com/sirupsen/logrus" "io" "net/http" diff --git a/pkg/git/http-server/utils.go b/pkg/git/http-server/utils.go index 5edd5b027..4b1ee02f4 100644 --- a/pkg/git/http-server/utils.go +++ b/pkg/git/http-server/utils.go @@ -2,7 +2,7 @@ package http_server import ( "fmt" - process2 "github.com/kluctl/kluctl/pkg/utils/process" + process2 "github.com/kluctl/kluctl/v2/pkg/utils/process" "io" "log" "net/http" diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 8288c5198..82b822ee2 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -5,9 +5,9 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" - auth2 "github.com/kluctl/kluctl/pkg/git/auth" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" - "github.com/kluctl/kluctl/pkg/utils" + auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" "io/ioutil" "os" diff --git a/pkg/git/poor_mans_clone.go b/pkg/git/poor_mans_clone.go index 53c6d41e4..851b6ae04 100644 --- a/pkg/git/poor_mans_clone.go +++ b/pkg/git/poor_mans_clone.go @@ -5,7 +5,7 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils" "io/ioutil" "os" "path/filepath" diff --git a/pkg/git/utils.go b/pkg/git/utils.go index 11e9f8cf2..d3826b03d 100644 --- a/pkg/git/utils.go +++ b/pkg/git/utils.go @@ -3,7 +3,7 @@ package git import ( "fmt" "github.com/go-git/go-git/v5" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils" "path/filepath" ) diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go index eae023375..431619904 100644 --- a/pkg/jinja2/jinja2.go +++ b/pkg/jinja2/jinja2.go @@ -3,8 +3,8 @@ package jinja2 import ( "fmt" "github.com/gobwas/glob" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "io/fs" "io/ioutil" "os" diff --git a/pkg/jinja2/jinja2_renderer.go b/pkg/jinja2/jinja2_renderer.go index 7db691279..e3299137f 100644 --- a/pkg/jinja2/jinja2_renderer.go +++ b/pkg/jinja2/jinja2_renderer.go @@ -5,8 +5,8 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/kluctl/kluctl/pkg/python" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/python" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "io" "io/ioutil" "os" diff --git a/pkg/jinja2/source.go b/pkg/jinja2/source.go index 7bae0a194..78321257f 100644 --- a/pkg/jinja2/source.go +++ b/pkg/jinja2/source.go @@ -2,8 +2,8 @@ package jinja2 import ( "embed" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/embed_util" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/embed_util" log "github.com/sirupsen/logrus" "path/filepath" ) diff --git a/pkg/jinja2/vars.go b/pkg/jinja2/vars.go index 9d81e61e3..b130cafb3 100644 --- a/pkg/jinja2/vars.go +++ b/pkg/jinja2/vars.go @@ -2,11 +2,11 @@ package jinja2 import ( "fmt" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" ) type VarsCtx struct { diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index bb28c94d7..fec67980f 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -5,10 +5,10 @@ import ( "encoding/json" "fmt" goversion "github.com/hashicorp/go-version" - "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" diff --git a/pkg/k8s/utils.go b/pkg/k8s/utils.go index f70e14fa2..bd0c74dcd 100644 --- a/pkg/k8s/utils.go +++ b/pkg/k8s/utils.go @@ -1,7 +1,7 @@ package k8s import ( - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/apimachinery/pkg/runtime/schema" ) diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index e54e25e95..e7cdcf650 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -2,10 +2,10 @@ package kluctl_project import ( "fmt" - "github.com/kluctl/kluctl/pkg/git" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" - types2 "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/git" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + types2 "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" "os" "path/filepath" diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 238932081..82e3eb165 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -4,11 +4,11 @@ import ( "archive/tar" "compress/gzip" "fmt" - "github.com/kluctl/kluctl/pkg/git" - "github.com/kluctl/kluctl/pkg/jinja2" - types2 "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/jinja2" + types2 "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "io/ioutil" "os" diff --git a/pkg/kluctl_project/load_targets.go b/pkg/kluctl_project/load_targets.go index 5dfdd8f3f..6cfcd76c2 100644 --- a/pkg/kluctl_project/load_targets.go +++ b/pkg/kluctl_project/load_targets.go @@ -2,12 +2,12 @@ package kluctl_project import ( "fmt" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" - "github.com/kluctl/kluctl/pkg/jinja2" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "path/filepath" "reflect" diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 8f44afee7..552240a22 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -2,11 +2,11 @@ package kluctl_project import ( "fmt" - "github.com/kluctl/kluctl/pkg/git" - auth2 "github.com/kluctl/kluctl/pkg/git/auth" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" - "github.com/kluctl/kluctl/pkg/jinja2" - "github.com/kluctl/kluctl/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/git" + auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/types" "regexp" ) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index d863fe8c2..257967bad 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -2,12 +2,12 @@ package kluctl_project import ( "fmt" - "github.com/kluctl/kluctl/pkg/deployment" - "github.com/kluctl/kluctl/pkg/jinja2" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "path/filepath" ) diff --git a/pkg/python/embed.go b/pkg/python/embed.go index c9310de48..b4dd1e637 100644 --- a/pkg/python/embed.go +++ b/pkg/python/embed.go @@ -2,8 +2,8 @@ package python import ( "fmt" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/embed_util" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/embed_util" "log" "path/filepath" "runtime" diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index e886010a5..86ccb63e4 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -10,8 +10,8 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/remote/transport" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" log "github.com/sirupsen/logrus" "io/ioutil" "net/http" diff --git a/pkg/seal/bootstrap.go b/pkg/seal/bootstrap.go index ca1807a16..02865c4e9 100644 --- a/pkg/seal/bootstrap.go +++ b/pkg/seal/bootstrap.go @@ -6,9 +6,9 @@ import ( "encoding/base64" "encoding/pem" "github.com/bitnami-labs/sealed-secrets/pkg/crypto" - "github.com/kluctl/kluctl/pkg/k8s" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/k8s" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" log "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/seal/fetch_cert.go b/pkg/seal/fetch_cert.go index ef341ba4a..4e5e27d5a 100644 --- a/pkg/seal/fetch_cert.go +++ b/pkg/seal/fetch_cert.go @@ -5,7 +5,7 @@ import ( "crypto/rsa" "errors" "fmt" - "github.com/kluctl/kluctl/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/k8s" log "github.com/sirupsen/logrus" "io/ioutil" v12 "k8s.io/api/core/v1" diff --git a/pkg/seal/sealer.go b/pkg/seal/sealer.go index 9c715006a..75f8bb187 100644 --- a/pkg/seal/sealer.go +++ b/pkg/seal/sealer.go @@ -7,12 +7,12 @@ import ( "encoding/hex" "fmt" "github.com/bitnami-labs/sealed-secrets/pkg/crypto" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - k8s2 "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "golang.org/x/crypto/scrypt" "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/seal/secrets_loader.go b/pkg/seal/secrets_loader.go index d8eccb47a..ca4a0ac21 100644 --- a/pkg/seal/secrets_loader.go +++ b/pkg/seal/secrets_loader.go @@ -2,11 +2,11 @@ package seal import ( "fmt" - "github.com/kluctl/kluctl/pkg/kluctl_project" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/aws" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/aws" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "os" "path/filepath" "strings" diff --git a/pkg/types/cluster_config.go b/pkg/types/cluster_config.go index 6b1f33b3b..0c22be6b1 100644 --- a/pkg/types/cluster_config.go +++ b/pkg/types/cluster_config.go @@ -2,9 +2,9 @@ package types import ( "fmt" - "github.com/kluctl/kluctl/pkg/utils" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" "path/filepath" ) diff --git a/pkg/types/command_result.go b/pkg/types/command_result.go index ed3c97f21..5142d73c0 100644 --- a/pkg/types/command_result.go +++ b/pkg/types/command_result.go @@ -1,8 +1,8 @@ package types import ( - "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) type Change struct { diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index 08fc2fa3c..e2069d00f 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -2,8 +2,8 @@ package types import ( "github.com/go-playground/validator/v10" - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" ) type DeploymentItemConfig struct { diff --git a/pkg/types/git_project.go b/pkg/types/git_project.go index db60413da..6d6cbc4a3 100644 --- a/pkg/types/git_project.go +++ b/pkg/types/git_project.go @@ -2,8 +2,8 @@ package types import ( "github.com/go-playground/validator/v10" - git_url "github.com/kluctl/kluctl/pkg/git/git-url" - "github.com/kluctl/kluctl/pkg/yaml" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/yaml" "strings" ) diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 917f1570b..b0ea0257f 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -1,8 +1,8 @@ package types import ( - "github.com/kluctl/kluctl/pkg/utils/uo" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" ) type DynamicArg struct { diff --git a/pkg/types/secrets_source.go b/pkg/types/secrets_source.go index 5490926f0..c29f63615 100644 --- a/pkg/types/secrets_source.go +++ b/pkg/types/secrets_source.go @@ -2,7 +2,7 @@ package types import ( "github.com/go-playground/validator/v10" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) type SecretSourceAwsSecretsManager struct { diff --git a/pkg/types/target_config.go b/pkg/types/target_config.go index 1b439f532..4f27a0cf6 100644 --- a/pkg/types/target_config.go +++ b/pkg/types/target_config.go @@ -1,8 +1,8 @@ package types import ( - "github.com/kluctl/kluctl/pkg/types/k8s" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) type FixedImage struct { diff --git a/pkg/utils/embed_util/extract.go b/pkg/utils/embed_util/extract.go index 21b5deda2..dc38f75f4 100644 --- a/pkg/utils/embed_util/extract.go +++ b/pkg/utils/embed_util/extract.go @@ -3,7 +3,7 @@ package embed_util import ( "fmt" "github.com/gofrs/flock" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils" "io" "io/fs" "io/ioutil" diff --git a/pkg/utils/embed_util/packer/main.go b/pkg/utils/embed_util/packer/main.go index 60bf426cc..f4d4ba5e3 100644 --- a/pkg/utils/embed_util/packer/main.go +++ b/pkg/utils/embed_util/packer/main.go @@ -8,7 +8,7 @@ import ( "encoding/hex" "fmt" "github.com/gobwas/glob" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" "io/fs" "io/ioutil" diff --git a/pkg/utils/uo/k8s_fields.go b/pkg/utils/uo/k8s_fields.go index 68d07dc61..be195b0f1 100644 --- a/pkg/utils/uo/k8s_fields.go +++ b/pkg/utils/uo/k8s_fields.go @@ -1,7 +1,7 @@ package uo import ( - "github.com/kluctl/kluctl/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" log "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/utils/uo/unstructured.go b/pkg/utils/uo/unstructured.go index 1dd8c4adf..118b76df1 100644 --- a/pkg/utils/uo/unstructured.go +++ b/pkg/utils/uo/unstructured.go @@ -2,7 +2,7 @@ package uo import ( "fmt" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) diff --git a/pkg/utils/uo/uo.go b/pkg/utils/uo/uo.go index cae1b10be..ffb513110 100644 --- a/pkg/utils/uo/uo.go +++ b/pkg/utils/uo/uo.go @@ -3,7 +3,7 @@ package uo import ( "fmt" "github.com/jinzhu/copier" - "github.com/kluctl/kluctl/pkg/yaml" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "reflect" diff --git a/pkg/utils/versions/latest_version.go b/pkg/utils/versions/latest_version.go index 654928f6c..7cd524a9c 100644 --- a/pkg/utils/versions/latest_version.go +++ b/pkg/utils/versions/latest_version.go @@ -2,7 +2,7 @@ package versions import ( "fmt" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils" "log" "regexp" "strconv" diff --git a/pkg/utils/versions/latest_version_parse.go b/pkg/utils/versions/latest_version_parse.go index 220bb08f0..6fe06ed23 100644 --- a/pkg/utils/versions/latest_version_parse.go +++ b/pkg/utils/versions/latest_version_parse.go @@ -2,7 +2,7 @@ package versions import ( "fmt" - scanner "github.com/kluctl/kluctl/pkg/utils/python_scanner" + scanner "github.com/kluctl/kluctl/v2/pkg/utils/python_scanner" log "github.com/sirupsen/logrus" "strconv" "strings" diff --git a/pkg/utils/versions/looseversion.go b/pkg/utils/versions/looseversion.go index c0fb8c169..11101ea03 100644 --- a/pkg/utils/versions/looseversion.go +++ b/pkg/utils/versions/looseversion.go @@ -1,7 +1,7 @@ package versions import ( - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils" "regexp" "sort" "strconv" diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index de1e18d4e..52584cedf 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -2,9 +2,9 @@ package validation import ( "fmt" - "github.com/kluctl/kluctl/pkg/k8s" - "github.com/kluctl/kluctl/pkg/types" - "github.com/kluctl/kluctl/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" "regexp" diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index adc37aac6..271c761fe 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -6,7 +6,7 @@ import ( "errors" "fmt" "github.com/goccy/go-yaml" - "github.com/kluctl/kluctl/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils" yaml3 "gopkg.in/yaml.v3" "io" "os" From 6e6f65928562079cd2d8d16acd030654bf2ed7cd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 20 Apr 2022 10:02:41 +0200 Subject: [PATCH 0017/2268] fix: Properly use --output-format for deploy and poke-images --- cmd/kluctl/commands/cmd_deploy.go | 4 ++-- cmd/kluctl/commands/cmd_poke_images.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 15dcea2f2..e234d8b49 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -20,7 +20,7 @@ type deployCmd struct { args.ReplaceOnErrorFlags args.AbortOnErrorFlags args.HookFlags - args.OutputFlags + args.OutputFormatFlags args.RenderOutputDirFlags NoWait bool `group:"misc" help:"Don't wait for objects readiness'"` @@ -66,7 +66,7 @@ func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { if err != nil { return err } - err = outputCommandResult(cmd.Output, result) + err = outputCommandResult(cmd.OutputFormat, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index a6898ea11..33c62ced3 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -15,7 +15,7 @@ type pokeImagesCmd struct { args.InclusionFlags args.YesFlags args.DryRunFlags - args.OutputFlags + args.OutputFormatFlags args.RenderOutputDirFlags } @@ -48,7 +48,7 @@ func (cmd *pokeImagesCmd) Run() error { if err != nil { return err } - err = outputCommandResult(cmd.Output, result) + err = outputCommandResult(cmd.OutputFormat, result) if err != nil { return err } From b2ad31c19c0fc74f7522d56bb5c9d58fd2d99122 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 20 Apr 2022 10:07:57 +0200 Subject: [PATCH 0018/2268] fix: Add missing --render-output-dir flag to delete/list-images and prune --- cmd/kluctl/commands/cmd_delete.go | 14 ++++++++------ cmd/kluctl/commands/cmd_list_images.go | 12 +++++++----- cmd/kluctl/commands/cmd_prune.go | 14 ++++++++------ 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index c078332de..ebb02bfea 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -22,6 +22,7 @@ type deleteCmd struct { args.YesFlags args.DryRunFlags args.OutputFormatFlags + args.RenderOutputDirFlags DeleteByLabel []string `group:"misc" short:"l" help:"Override the labels used to find objects for deletion."` } @@ -36,12 +37,13 @@ take the local target/state into account!` func (cmd *deleteCmd) Run() error { ptArgs := projectTargetCommandArgs{ - projectFlags: cmd.ProjectFlags, - targetFlags: cmd.TargetFlags, - argsFlags: cmd.ArgsFlags, - imageFlags: cmd.ImageFlags, - inclusionFlags: cmd.InclusionFlags, - dryRunArgs: &cmd.DryRunFlags, + projectFlags: cmd.ProjectFlags, + targetFlags: cmd.TargetFlags, + argsFlags: cmd.ArgsFlags, + imageFlags: cmd.ImageFlags, + inclusionFlags: cmd.InclusionFlags, + dryRunArgs: &cmd.DryRunFlags, + renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { cmd2 := commands.NewDeleteCommand(ctx.targetCtx.DeploymentCollection) diff --git a/cmd/kluctl/commands/cmd_list_images.go b/cmd/kluctl/commands/cmd_list_images.go index 80bd70382..cfd9ef56f 100644 --- a/cmd/kluctl/commands/cmd_list_images.go +++ b/cmd/kluctl/commands/cmd_list_images.go @@ -12,6 +12,7 @@ type listImagesCmd struct { args.ImageFlags args.InclusionFlags args.OutputFlags + args.RenderOutputDirFlags Simple bool `group:"misc" help:"Output a simplified version of the images list"` } @@ -25,11 +26,12 @@ as described in for the deploy command.` func (cmd *listImagesCmd) Run() error { ptArgs := projectTargetCommandArgs{ - projectFlags: cmd.ProjectFlags, - targetFlags: cmd.TargetFlags, - argsFlags: cmd.ArgsFlags, - imageFlags: cmd.ImageFlags, - inclusionFlags: cmd.InclusionFlags, + projectFlags: cmd.ProjectFlags, + targetFlags: cmd.TargetFlags, + argsFlags: cmd.ArgsFlags, + imageFlags: cmd.ImageFlags, + inclusionFlags: cmd.InclusionFlags, + renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { result := types.FixedImagesConfig{ diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 2ef240e88..e24578871 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -15,6 +15,7 @@ type pruneCmd struct { args.YesFlags args.DryRunFlags args.OutputFormatFlags + args.RenderOutputDirFlags } func (cmd *pruneCmd) Help() string { @@ -27,12 +28,13 @@ func (cmd *pruneCmd) Help() string { func (cmd *pruneCmd) Run() error { ptArgs := projectTargetCommandArgs{ - projectFlags: cmd.ProjectFlags, - targetFlags: cmd.TargetFlags, - argsFlags: cmd.ArgsFlags, - imageFlags: cmd.ImageFlags, - inclusionFlags: cmd.InclusionFlags, - dryRunArgs: &cmd.DryRunFlags, + projectFlags: cmd.ProjectFlags, + targetFlags: cmd.TargetFlags, + argsFlags: cmd.ArgsFlags, + imageFlags: cmd.ImageFlags, + inclusionFlags: cmd.InclusionFlags, + dryRunArgs: &cmd.DryRunFlags, + renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { return cmd.runCmdPrune(ctx) From ffc0f8295530928701464b753fdda7dd311558e9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 20 Apr 2022 17:20:46 +0200 Subject: [PATCH 0019/2268] chore: Update kluctl logo --- README.md | 2 +- logo/kluctl.png | Bin 12230 -> 0 bytes logo/kluctl.svg | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 logo/kluctl.png create mode 100644 logo/kluctl.svg diff --git a/README.md b/README.md index 627ac52d1..6f71b363c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # kluctl -![kluctl](logo/kluctl.png) +kluctl kluctl is the missing glue that puts together your (and any third-party) deployments into one large declarative Kubernetes deployment, while making it fully manageable (deploy, diff, prune, delete, ...) via one unified command diff --git a/logo/kluctl.png b/logo/kluctl.png deleted file mode 100644 index 8e3ad6e30a2d15febde0b1663c11a341f312a9e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12230 zcmcgygMuyw^t`ww<$`bMEJk>yAiu)rVxnOvDffgzWJns3ru0#SLC(QsRU4Rz80l_=D^E z;ITF#fFGen1b9#6^hnS%*{*Oh(#N7x!WORc)b<=XRbn|@f@&e-N>B(>7VCQQ7 z-020sql;DYrZf}yJ;-C|eQmFljVW)pk)?6jZM#FZ9*d-})E2IfpTg+Hpo963qH_7> zjrVmXuQ}Pkz7o*!=u{XKyd2FBr+;Z@H(Gbo*Ct@@ zplJ-eqHR|tQdM(?i5d=m8ag%}=x(na_%s?9i%n3+LC9CWZtzP^c4^R9O{rF1oZPoS|N)7u@{@|iOyJ>V1t zmc=nCs~;&6uFIV8u7w>h=^bW14VROH;=qeFldIG2^!l%8@WQxw(1<$Xj5G@k=I%N6 zlT9DU;j}Ctw-;qOQRHwOD>i8kxH$ES7IoQb*!v;1)gZJz#^osHFj?cg(CHPOz$sBP z}qk#TM(>bh4c6*o5^%?&^GRK#t|3jPK$4czLkcwAaF0vFV7Jnok%&68n~1YVn(j zzM#SGvA_!Fc-cT8Pa7$Ji;@ucfeA-IV8o?YMA!o>G&o9}=DxiqxrY4=gH}0{D*N%W z7kxp%LzLUL`fpj?*^@34bchbPr^R2+Evze>QYscGd6msX=ZG%hX`2}bw$Q9Wu z`R#odn*qyQv=v61-r_hk>~vmtY@q7?fd={2CDe@;xnZwR`V)x;RbBPeKYQwQFyhfG zGOBfXvFcp-wA8EvPB$mFS?9j?v(WI`sWZ$}>ToCUe5Z>AEG^N+!zee&R78aC^{z{4 zg_ppzu8Z0;cuk@$Z;VID9E0;tF5;)$y$TdE`-2J;TyR3g%qwF8U!IUx2)3suAu_E=wH#73m%+Xy7SXGWQ zP5x(xzuqeQfZYRIC}R5U6%h^l6T*PzifS|{F7`&p(7-vG@|^KMnj(0TB>18?o~t#f zB=az_`Fu$r@T6~0Zd3ie&~w9Thp9SuyM~7f!VUM-HMAR!=c5Fgj#*254*rZ5=!G{m zm7qac^Vc8%n384|>MlOCMUu#3r5E$iyI17$5^L z+j_FwNCX5pE!z#@aWOTrO7Rv=8rSkI~W}O&Pb+ev(H7mTDyVr zC!GoIy8ju;)5_`e6^6E57t0=wIi3xrkOj-^^0InP_Yejl$!;8~DdNua?H|cJ2fwA3 zG8E+$h1n`DJGq3}@62~ZT@#Q!WJ8XZTMeYiT(3Fb?GtD|cge^_gOEYUvj5{f^N?a+ zI}DtN*8WP`Gx+6175K+@zT%6@PBivGWzVvEdRv>7s7mj&)2K<`tEte{K?4utRmHY1 zEQS}<2Y5`P37EZRg9(@k1C3l1ZP$OHJvrG`095!cJ~zBu&&2;=RL=xUIe}Nd%;NDX z8+&N0>0O4PzovB59ITHqd>)U5>|b?jMfIC?Yy7joX`(%6Tl36ht?SBu3tMYEY!+Qsa<4i=mrXkRfPEmR{c{&ZJ(6|I;m z$@HhEz?E$LuH@Nwuf?F{4I@Sv$Iev9!MRwLn>5~j{zwEYik^>n^DScpCIYkoKaO_h z_;TA}Q5NZ$OtvnayA*-PZ7i4D;e%s+ThS}&ftM#M=@bb#gkq0gyL2*Ir8svowkGj7 z{}JB5Se&V!Tx07KhF));0p1Uvn~7F~9`dxrv^4~eBq$Ao;NnEbW3Wf)jUqJyf(O#K zS?^SyW6WohtK~A;{7wXmPbN$Yi}T$=(9v2r8KPs@!CG)QOTh75I19Oda{>n2Y6aBD zZs4b%zh~ie>B%ZYgKz&L&F4>EPMQyK4caDwVXj6C8Hk%xGE3a4{A-f0n~dq9jq`Ax zqMCVlF2xpMwCw3>RM&zGmaJXW(?^4`euqk9 z-aG@573+&wWX^LE>*=LuD3iZKM)98i1Y8k)S^d={uJK(Me=3_26*;aGl z?3y43A+H}rM*N4*eab8`T;hfr%2rOqP8M^VZt!+pJ?mMRXui6@_-u|J1WW33q@xtR zm{@VPF*x?jWBf(pGY>{}BQ$PYIFlvt$~g_Ih)uZ-tF+1(mq~|yO=s&1fRbP~Em8h8D)W`at0nKt-SadLkX2tQDFQNv5yHCycmcN7hHtr9*R_cYv z70`?cs&`U&OSqCd!UX@a6TCIj?9XUG15;?9b=8F{9eyKO8qrddIT+D8xp)BWi3+xT z#m$8VRuMWB7B@+{6)@ysz_tGdS~mBl6?s$IYkRgeG_#n86Ai#GV)6d!W9&w73e>OFHG)zN!KSKo__|G4f^KjlE-|I8QzG*h4zAk}u$b{XiC|ARru3+mWs zWdjU1)?(9jLC9j`c9AumaJ`i+oGP7;T{J z_CJpu1lS(a+g7<|OvK*lG`-&$+}$W##$kxSi+c3>dhY2N8d-8_PVN}Mwsw~C)Z)-& z0}!};G>EP3(RecrBx;4`I`}}MKi~WOi)D90N9HUt+cdHi-!h5V&aU-h5StU89-o8; z<7wS82CLB=v?swv8fZ{m9)z;-L=FII^z*BJX7Lr;EP-vd%Bg)lzyRwyhP~d|UP?3x zI9W>EVWy*E69kw3^N%$t4Ddce??)xJ<3B#K-Xj_VcsZJ!lm74{^Zd)h({G&CsW+~h z!F~|Bd!V6sb+Q=i%=yJHr1!nkJ(Q!bf{fQFht0`d*sVQt+9Sj z9hs5hIqkiY>d9Q*rjeU(0z!3dEj&=Yv?w}D=@c`S-gcpBl5B)!ur@W17XhUd(=a5 z3!Mif6Ep?#b|9<>1K zNmu&bJJHg+-FKpK#TFha8(^@ywz%xg3YP=njoPJr6P@${<)ZLQO~Hy0!Y(Dgp#V!r z`pd9E1UPY@LX*vDAG>`^)62tZj4_k&SHkLJtZIzC=3ZYj_PTvO{J!im$22$5fglFOfDUIpZ6y{pFxE+ z5;410flf?#bYC^4^ODx1+4tH{JV!N&rFbn>3AUOMX`lU7kc$$8zMD*DYo0bQoD^6q z>uDC#ZxPmAyd8nSV0LCnFoLtj!2HC~2nb7%Qp#MCMo(43ANamgO4174Tbw}p# zmwB|z;nUT#NQm@)@xWZ#Um~Ha(f&r~+@!e85w@iS^Zu6zuQoGhzB;>*(XKWly0>2g z9gi4{=D>E^H{n1)TiE2_`TvAQ>d)P~fsTKsqZrBwpE_n`6El&+aHLb~^>h>dY#9va z-0Oc|Y?8bDGuBHgVp^O{0*7nO;&r_q=kG<7Ol^)5e_rRr%jzi)uwY?wxh@u#ixpQ) z$UPlUKnJ?mH|G$t$}xpavPjW;d$jg*R1O2xPOrJgZoz!F`p}6@vGGlN_M018rY?^k z@h6d2#3#H*gX(nW8=0{J3_oEPgV|WE8s9=zwyS_eV{r-c+Rq<3EhrwJ?9n)=^|Zz& zZ!T%ta?V{&cKq2A(g1m&)h)>S?@FuaDRTR1Bi*0FIKW!$^wcOyUK+w+jsofibyjL7 zEb7L_&!%Q5wDrcuCN|_16tv=oi7Ki+-MpmdX>$fR5gu37d24jPr-?V?bDyPS=0h<8We)S#C+oA!nKJ`-9Y|^IC8&Pgh|e| zHTY|0LrC~*ZtZ3>?A&KEdO(M#r4X%vI}&WdveTN&IUggxZ<46qp2h;B5)y*5T9&W| zUbNpP0`I)wtlURkX9YJK3Ncxtjs|q;(mYURF?J0Q8Z@inQUVo0PtT*-E}_4ag5q2n z5wtegOdi>}vUh@wg()Su*3-S1s46R)HiiLPE^c;sDRVgCIWtw3U8te~^0H*d-xG98 z!)ncEMdex=vR4fi6$Q!~z^rS_>kTzT9j9xDEw$qL3_Ui=#%W)i9m3&h%&_rN^MSM` zSy*0fZXRk&FaGy$|Fd-u*m!eu?Kp~d>yAS;0<93BZUuNW3t{`&=#3`rT^9MrF#gOQ z__&Ku`PZkiuFL&NLROuQewcu`6CDWoUqVHYi2IKw%ej>=xAlB)i{{;fp zcl`7DYt6?U0s&aOpa65SyM#RRdGKDyaaVivaATs{afbQa_kEyA+2!Ez&O%o-3(KTE zZ=c31sRcK2ZzUDonnWSeGXAGEjmKsx3CWbcYe5)lReI+szdUa6?3*~F2k|+iE9jg1 zk+N-N97a;hc=3<-vdsQ1n^1%vbnMJ=- zfmt;2fYkb{j<&XTjgcAqq&4p8nSaw|Kt^6(v7-6-SY(frXM<6>m)XzNvx zZj@lTLE$k+q3(ITDn}8`6~*d-WU2uf!`u0v_M%Lqwk@WEYaI)4LqOf&9X8`n&CzvN zNqi9i{zSq{#Vk&>OwN`sDuz{l7a}q5-zO(05k2Kaq3B6W1+%|=dSXVdR*3H>yMtOg zIK2p#-xeLW7G{m;$@ntbdy5@Anbg9z(o0608%Bf+`Ov%KS|7X+Ex>t~pxWpt6qxpGpU0hvV3w3WvNr{O` zUGMJh?(gr0yittnvBo9EzgNC>6lJ9npTSN~jg=!rNs0HaaoUOark${Dr7FS4CmS37 z{*f%XAYB2)=CIApO^0+fT<#CVUI(L~w&Z{d$^n4Lcwsr!HyYQ=1T4g1wF5DJw*B7x zpfAL|EoJbI(Og*a#{6LSuTrFUQDzIe*1eCjh-w7BV7D)U-gK!K_`5=EZl0q}KCZ~4 zRfJl>s5L2mi}lEwmL?<%X?iy~cw{7=Hj0g5keGOPZ*MR7+|QX*cwAEs04J%c(cnr08k4T<(56^03k^1@lVAZji0rXXg5Lp1#*S1gmn#{e&Jb|w- z*1K^UVZr_-Cnu*1g-(&zb?kne?+5Z5)^=+fc-UB_E6Yy&iCr{=xK^6vV`Em; z^;2e+JzEr2_Cu)lH&2{*YMi~ji<2&Jd~kAsz>Dcx*z;}-YRwq^;}(P_?ZWe-v4S+{!X7dr8+uYBr`-fA)XfuS zx|j9hZiL`9?hoR$ZmSoIPu?S~@1CoVU+=N~-dpyxeePN))*vdt>qe~KG_W(ba(!NR zgTfXfjwc^ul6fQ{|LxA<<8x!*QpKw^LL5X++#FvJPesU^{uh0xYYr5*nFrnnZaPJX zdF&htZ(pEG;wvPVo-nGbxFd=VP`;rs0G==KXWQ7Zwoi z4~pic5YgnWJF%o4P<99`Vry$q#8UEj?tKAPQ9feO31xWX9U&zvuB)(bS>_;@d?MqGg6qqO&Y zduzO7=6gOHY#ncJ9Ub#K>Y78pe*NOSG&Ii=|CFzZm6vB$D9Q%29~!njINf(^*&0Lf z(#zY6;HIcHUkx&nv9c=hyRiT1l9N3cnKuKaMNo;#k;Ch^eA~H2z^=m0|IRW8M@jWD zO|CR^>3=Tb628L|Yk;!PR~0Cq>+6kW+O^qMD&j0I!$r(ef0%Z6Lug?I-@75}9?t!h z)p}q3U6bM?ti5{rlpo(q4$et$I-0gC!qU_tAt51#wkkEVfM>~lltmQ4Pf4aYgqz>G z*%4!L*F_Qrv&Cwtc18L%lqKWXSQ@2Lu0Umdg5GN8;zawpN#0UN_qg1LAAP$Wtxo) z)=@iN4aUwB<3x~)i_}zB`^be{L{aB^GH&;OO1Uvgv;%HLsp|_=e2=Bd~l!b=$gux z*&zg}k!p^?OJTTH6vAHKEN}`6MG8I-8U6Gg3zXyYb;e96aItM{=%WZV!;LwT&zJ)n zjJI|mFc?Q#_br$LT8$d4a>L+#Y)#}qB($&h7WtfJ<+sJ|MZsttgpxak5Q=v~61%cS zzhq;HCJ$UAj1Z)n8&L*YKsLu9OQu^zUqRGIlqb3eLAggP3j&c=GUa97cV(S(lKgP25LT~uk zmR9RE7|X~QJO1~YIWhvV@j1@L<*V*&UJj_rR*`Od^)HWTQ+*Ct4HzFcczFxbpVBkz zOY0A$|A;UllJb6D_1IeFczgP|{X-s_%(3fl5r*DAl8{xmq@DeBR z4wQ7F@;V!t*dxZb^Vd2b#{uFV^;+4FmIqzza;PSJ#YD9wmJT6qn?y!NFd-fC284B$rc^Rt~o6%4~oP zN!qlsGN1?F9;8iY{W8yBy1mGC?X4nhoe)F2&4coB(-qqCOG`@&h=hc$orh>l zTH5>Pr4Vm#35k;vc?Sn$V+a12IDljlG($n5f)wuvohT)@8d?E8nHy7|ct60VSiT)Wvht3?(N9T9AMh5JmseJnu=MqH?Hu?`g};1ec#F`l&?U*$o)=R9!Gt^_ z2=7K?q-)JPKPlRSs@+{d!LqU-5`h$9)M8vPi2$wgJMIq?8!lL?Ip2RykgShu>5mUW z?5FC?AG(XEg9{HKu_~$#k0v&P#&|w)(;&Cs$09)JZ22ToJ?S$3qU9BlRt`NKmWQuJ zv;5MMO{FZ$U2}Z3EsgVg>SVDgC?sm}NwjWANSQALLLr8p1t>s56th4V4JR)Tp{MOp zMJTFkr<*X!l!EH%2l#;xa;rU8SF8e^eq7K90$o`g$y5+}Z!meaFFVVx+c0tOq!Uml ztI~DYt3Jkhqv$No`scImPcUo{LCf&#A^dcmEBvX$&IAgo24MaZ4L&P^K z=FObTlED64#ftnp2ZaM%PfN9z0 zdzl#3AHI`H;Kx&p=I2kli{&I4zP!RGqO(7pEa7^7EDsr6gB1uMpc$H%Y7GR`dFZmm zro(LD)zMn9p#da)z@p3m_;a)O2Z>E`3vDwtiugNIg_@B`{NY*khP-^bIvW!NetRl5 zHtr%NR%+%8t)cP{!-r8$5LuQx*EBeHW%dzKmOoO2i9jYph%kG;VFlVTZOPq0Po9_r3=Bg7S6_MBz@%V~d-GK;MxeQCNHBnv9#WyQI*sqzAw?U=J zTwEYDYgDh18nm)0Ahu}aU=Gz?I3Vka^K*AsQ8CYYYVb85+hZfYz|H~x_XG$xGXv?e zN=Dw^=zc?sW58Yu&e$`8&z~dOgpsl~$>{>BTi=s}-1Egh^pi2gA2Z~B8v%{ol^w{8 zzn%ti7{|?{&WP1I>AQ^$;v5o6|C&9K9_C-Pyc`x5N)SAxsgZ7XcKEE`);6e9v2?5t zwSQJy@u(Dmj94vmLp6ApTVm-MIWK)6p^s2nDvc1<*Pq{?o~(^yk8FcU-pryUyGE)_ zq7r`kJ^`NioRWu(9H}L=FZe)u5Sq+VeRY8tp@7p4HNoHNHnp{w-B6 z=SR$RbX;8xldT=TXT#tlxf&>xjZI6PJB9T2UOEnbsQAT=g01{`T4$fFujTAdw$EUF z3{8$hPHpmZY=9D<$ay3rm(g{mw-g8zih?54t1<~-Wnp|8uXe(pw{BjuVv%i_b2h6#{#eRji29E z+d^)4$kUkv54CoLBjvd{K0EMTlzX%jep=&f6Sm^Xgw=!_2IjEq%R;$es5^EIY9iVM3fsx<bnV z8%R6#L7wL16X)jJ+S=OD(5=d(c||AT_nA`qq6zGL)PMv57*vVkmprp?S%@6)ZA!Ud z5>iZ=P8Nv*Ud;QhI-59SMQ>k0zP)HhQ&Z*W7D0n?qcQJIIPp9bd0P}&ZzLnr*eLIR zG2N)5(sH-~Z0;xaOn{+=h8{shWn|J_em?fF`T2&1b^F;!4rV{6aY2pMr{`w$GtNuP zw!*S3PCq1aQohM(#@jRGhSh%hbb8t(n^ER+6%b9}R4>95N2E@hg|wg^+m0uNMJciU zaXs-P@5;=f@9X8}KHS6uW#rPVGHPq<)jwt9H5Ckeidi2c>yD-iij8Y1KT5d%NYPSz z+h8@V-qTH)bF}<@0VShkOsf4jzsO-m7HO7Uid>^pc?@e2OzL0uihif~j>XvE{LoM% zxG6tB4>fz=*%rAlm)GH>lC1mmV>-oG{X42F-qMjG<}XE~hJ8U(^czutoN4LjY-dY& zEFI1$VR>MnYxxwpw)V(ay^*|0IwC^Ml(FLJgXsC&4cQ%s9?rJTpXs3@$uP!*(Soa$ zKzaduI2<@bX~yjn>b23BJ+*SHXr`qD?5>-MiCx3K@#~m&+sbo&{b#AeM87szt+|Lp z>DjPLwO9AXH$0>UNB&Eo_uS+Ku8{^$!_E$NUprZa7J6{DAhX=vlU@rZZRTFxGYpjJUg~d27htNMVdbXNXJym5vr2Z64LcAi1 z6r*vkS%?;6XfU3w7-m;nenb0K7*Mt+AaVCG;s^6SWkYk2I*`|=s%l4A4I3*5xTk8PN{Ot9>BG#kY7ggwQWV5kg(yLYu9kZW*YoXvJ-(wytB{z)(AZW;^SlCit6nJ+EhLao;kG83sRWiSQB5Z!-13lL|LbGTgEEcb_CA5PynmBr^)dQ?`s|09*vy0T(_9}+ZR zbvJ^MFZBM%=sY$K8B0=*qYmcly?^ooxk&1Q zk-1Q(SuhdlA@w)Zm(Wp=sUq;WD-Ce-(6j*TIHw>uOJR1lwMw-9Sp~ndBy3QS0Rp4? zP=bXtc%@hcCDLvk_GOO~ZxnN84W-qv00VZ2-1-Si8X1a${B@6v`iZ5}fB^*!iDbnv zaI&{ICPqp(J`~mlr_Q%nk{n8}ZxPhhF;pmqcoF39yMdtAu=hn(C$HU;@d%rmj0`*v z?$E>pI&MpO!k33OHps&jagHw#bQHOTTlTsTM-yRfS>mC^BnL8rV za6?ZMnhRQgh}YHzx7OSPLVLrsKJQaA{eSsY5gaSK!8Uj+F!M{4`9FEla)Olz8bDOA~)o-9?Um`80KoOQg>Rf2y+Y8J$K z_w?G*1?QK$It9aXjv}a8z^!yV1MQLOKDLkq>y8DFwS%59_{!77b1$HlJ3uBH-~OOb z8+5>dZ*6YiNlL*66kzv~hle0AM&tWDQifSOXTk5to8?lR_x72Q+n`SmZF{%wU1Q#r zCWBbX9L^iX4h}+cT2@e(uRQmtdb+!|dOXJ_SFR6;fLJuRi7*l{bgIM@IlXsRJ428G zq~XABm>j@XbE0le?`^kWQB`2L9GyCCp(uST8^=T3{KLa70`8N6I1Z~9z$+uS(bLm+ z624NJ=&~E^tWR+GGUFTWkiIDC`95mxC!10DeM`>m@$M@cO-*TLP=P|Q;&UP9B7vw@ z?*c7M)YJWKFYoVfm^>p8lJF4;O5L^Ltv!8O1^%)bl_Hdbw&-fJ&(?Z5FjngtbR&Uy z80o_d;>vKIW@K787?a{HlKE}ZaPD_cVaX#(E?q9Lm$vc~c)VCFAY&wDUcp55)}XF5 zf1<9PXcQw~91(+*ZlOng@Z&f*+)a|I87*%K^w2F#|K-MXql}EaVpxZnF!neAB2So# u4J{`0Wcb^b!_Qz+9I6oY|2rFgAt&0kpJc \ No newline at end of file From 520e096056b04b844bc33424aa526f6e7307a1c9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 20 Apr 2022 17:24:51 +0200 Subject: [PATCH 0020/2268] fix: Actually allow directories with --from-archive --- cmd/kluctl/args/project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 2dc50b9ae..9153d27f8 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -8,7 +8,7 @@ type ProjectFlags struct { LocalClusters string `group:"project" help:"Local clusters directory. Overrides the project from .kluctl.yml" type:"existingdir"` LocalDeployment string `group:"project" help:"Local deployment directory. Overrides the project from .kluctl.yml" type:"existingdir"` LocalSealedSecrets string `group:"project" help:"Local sealed-secrets directory. Overrides the project from .kluctl.yml" type:"existingdir"` - FromArchive string `group:"project" help:"Load project (.kluctl.yml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." type:"existingfile"` + FromArchive string `group:"project" help:"Load project (.kluctl.yml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." type:"existing"` FromArchiveMetadata string `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." type:"existingfile"` Cluster string `group:"project" help:"Specify/Override cluster"` } From 239d99624024a22a10f0f6ad6a666728b63abd35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aljoscha=20P=C3=B6rtner?= Date: Thu, 21 Apr 2022 06:47:18 +0200 Subject: [PATCH 0021/2268] build: rely on python download script for detection of arch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Aljoscha Pörtner --- Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b74eb7b59..c17bb50b0 100644 --- a/Makefile +++ b/Makefile @@ -46,9 +46,7 @@ vendor: ## Copy of all packages needed to support builds and tests in the vendor $(GOCMD) mod vendor python: ## Download python for Jinja2 support - ./hack/download-python.sh linux - ./hack/download-python.sh windows - ./hack/download-python.sh darwin + ./hack/download-python.sh generate: ## Generating Jinja2 support $(GOCMD) generate ./... From 49b466bcc14b4f97bb08e427f72541e71e9a3609 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 12:12:52 +0200 Subject: [PATCH 0022/2268] fix: Make metadata.yml sorting stable --- pkg/kluctl_project/git.go | 5 +++++ pkg/kluctl_project/load_targets.go | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index e7cdcf650..4fd28013e 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "reflect" + "sort" "strings" "sync" ) @@ -223,5 +224,9 @@ func (c *KluctlProjectContext) addInvolvedRepo(u git_url.GitUrl, refPattern stri } if !found { c.involvedRepos[repoKey] = append(c.involvedRepos[repoKey], *e) + s := c.involvedRepos[repoKey] + sort.SliceStable(s, func(i, j int) bool { + return s[i].RefPattern < s[j].RefPattern + }) } } diff --git a/pkg/kluctl_project/load_targets.go b/pkg/kluctl_project/load_targets.go index 6cfcd76c2..1d86d5d5e 100644 --- a/pkg/kluctl_project/load_targets.go +++ b/pkg/kluctl_project/load_targets.go @@ -12,6 +12,7 @@ import ( "path/filepath" "reflect" "regexp" + "sort" "strings" "sync" ) @@ -69,6 +70,9 @@ func (c *KluctlProjectContext) loadTargets() error { }) } } + sort.SliceStable(c.DynamicTargets, func(i, j int) bool { + return c.DynamicTargets[i].Target.Name < c.DynamicTargets[j].Target.Name + }) return nil } From 9efae3d847f4fc4c78f4cd13f0ab3b78e8720e1e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 18:14:01 +0200 Subject: [PATCH 0023/2268] build: Move downloading of python into generate stage and via go --- .github/workflows/build-and-release.yml | 8 +- Makefile | 7 +- go.mod | 1 + hack/download-python.sh | 43 -------- pkg/python/download/main.go | 132 ++++++++++++++++++++++++ pkg/python/embed_darwin.go | 8 -- pkg/python/embed_darwin_amd64.go | 9 ++ pkg/python/embed_darwin_arm64.go | 9 ++ pkg/python/embed_linux.go | 8 -- pkg/python/embed_linux_amd64.go | 9 ++ pkg/python/embed_linux_arm64.go | 9 ++ pkg/python/embed_windows.go | 8 -- pkg/python/embed_windows_amd64.go | 9 ++ pkg/utils/embed_util/packer/main.go | 1 + pkg/utils/tar.go | 20 ++-- 15 files changed, 194 insertions(+), 87 deletions(-) delete mode 100755 hack/download-python.sh create mode 100644 pkg/python/download/main.go delete mode 100644 pkg/python/embed_darwin.go create mode 100644 pkg/python/embed_darwin_amd64.go create mode 100644 pkg/python/embed_darwin_arm64.go delete mode 100644 pkg/python/embed_linux.go create mode 100644 pkg/python/embed_linux_amd64.go create mode 100644 pkg/python/embed_linux_arm64.go delete mode 100644 pkg/python/embed_windows.go create mode 100644 pkg/python/embed_windows_amd64.go diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 4cd3c7c64..26e3bcfc7 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -32,11 +32,6 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - name: Download python - run: | - ./hack/download-python.sh linux - ./hack/download-python.sh darwin - ./hack/download-python.sh windows - name: Get Version id: get_version run: | @@ -68,8 +63,7 @@ jobs: - name: Go Generate run: | go generate ./... - go generate -tags darwin ./pkg/python - go generate -tags windows ./pkg/python + go generate -tags linux,darwin,windows,amd64,arm64 ./pkg/python - name: Run unit tests run: | go test ./cmd/... ./pkg/... -v diff --git a/Makefile b/Makefile index c17bb50b0..f73aa2b4c 100644 --- a/Makefile +++ b/Makefile @@ -45,12 +45,9 @@ clean: ## Remove build related file vendor: ## Copy of all packages needed to support builds and tests in the vendor directory $(GOCMD) mod vendor -python: ## Download python for Jinja2 support - ./hack/download-python.sh - -generate: ## Generating Jinja2 support +generate: ## Generating Python and Jinja2 support $(GOCMD) generate ./... - $(GOCMD) generate ./pkg/python + $(GOCMD) generate -tags linux,darwin,windows,amd64,arm64 ./pkg/python ## Test: test: test-unit test-e2e ## Runs the complete test suite diff --git a/go.mod b/go.mod index 7b7f6a544..56cc82387 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/hexops/gotextdiff v1.0.3 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 + github.com/klauspost/compress v1.13.6 github.com/mattn/go-isatty v0.0.14 github.com/mitchellh/go-ps v1.0.0 github.com/ohler55/ojg v1.14.0 diff --git a/hack/download-python.sh b/hack/download-python.sh deleted file mode 100755 index d78a9040a..000000000 --- a/hack/download-python.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -set -e - -DIR=$(cd $(dirname $0) && pwd) -cd $DIR/.. - -if [ "$1" != "" ]; then - os=$1 -else - case "$(uname -s)" in - Linux*) os=linux;; - Darwin*) os=darwin;; - MINGW*) os=windows;; - *) echo "unknown os"; exit 1; - esac -fi - -arch=x86_64 - -case "$os" in - linux*) python_dist=unknown-linux-gnu-pgo-full;; - darwin*) python_dist=apple-darwin-pgo-full;; - windows*) python_dist=pc-windows-msvc-shared-pgo-full;; -esac - -mkdir -p download-python/$os -cd download-python/$os - -PYTHON_STANDALONE_VERSION=20220227 -PYTHON_VERSION=3.10.2 - -if [ ! -d python ]; then - curl -L -o python.tar.zst https://github.com/indygreg/python-build-standalone/releases/download/$PYTHON_STANDALONE_VERSION/cpython-$PYTHON_VERSION+$PYTHON_STANDALONE_VERSION-$arch-$python_dist.tar.zst - tar -xf python.tar.zst -fi - -cd python/install - -for i in test site-packages venv ensurepip idlelib distutils pydoc_data asyncio email tkinter lib2to3 xml multiprocessing unittest; do - rm -rf lib/python3.*/$i - rm -rf Lib/$i -done diff --git a/pkg/python/download/main.go b/pkg/python/download/main.go new file mode 100644 index 000000000..0987a2015 --- /dev/null +++ b/pkg/python/download/main.go @@ -0,0 +1,132 @@ +package main + +import ( + "fmt" + "github.com/klauspost/compress/zstd" + "github.com/kluctl/kluctl/v2/pkg/utils" + log "github.com/sirupsen/logrus" + "io/ioutil" + "net/http" + "os" + "path/filepath" +) + +const ( + pythonVersionBase = "3.10" + pythonVersionFull = "3.10.3" + pythonStandaloneVersion = "20220318" +) + +var pythonDists = map[string]string{ + "linux": "unknown-linux-gnu-lto-full", + "darwin": "apple-darwin-lto-full", + "windows": "pc-windows-msvc-shared-pgo-full", +} + +var archMapping = map[string]string{ + "amd64": "x86_64", + "386": "i686", + "arm64": "aarch64", +} + +var removeLibs = []string{ + "site-packages", + "venv", + "ensurepip", + "idlelib", + "distutils", + "pydoc_data", + "asyncio", + "email", + "tkinter", + "lib2to3", + "xml", + "multiprocessing", + "unittest", +} + +func main() { + osName := os.Args[1] + arch := os.Args[2] + extractPath := os.Args[3] + + dist, ok := pythonDists[osName] + if !ok { + log.Panicf("no dist for %s", osName) + } + + downloadPath := download(osName, arch, dist) + + os.RemoveAll(extractPath) + decompress(downloadPath, extractPath) + + for _, lib := range removeLibs { + _ = os.RemoveAll(filepath.Join(extractPath, "python", "install", "lib", fmt.Sprintf("python%s", pythonVersionBase), lib)) + _ = os.RemoveAll(filepath.Join(extractPath, "python", "install", "Lib", lib)) + } +} + +func download(osName, arch, dist string) string { + pythonArch, ok := archMapping[arch] + if !ok { + log.Errorf("arch %s not supported", arch) + os.Exit(1) + } + fname := fmt.Sprintf("cpython-%s+%s-%s-%s.tar.zst", pythonVersionFull, pythonStandaloneVersion, pythonArch, dist) + downloadPath := filepath.Join(os.TempDir(), fname) + downloadUrl := fmt.Sprintf("https://github.com/indygreg/python-build-standalone/releases/download/%s/%s", pythonStandaloneVersion, fname) + + if _, err := os.Stat(downloadPath); err == nil { + log.Infof("skipping download of %s", downloadUrl) + return downloadPath + } + + log.Infof("downloading %s", downloadUrl) + + r, err := http.Get(downloadUrl) + if err != nil { + log.Errorf("download failed: %v", err) + os.Exit(1) + } + if r.StatusCode == http.StatusNotFound { + log.Errorf("404 not found") + os.Exit(1) + } + defer r.Body.Close() + + fileData, err := ioutil.ReadAll(r.Body) + + err = ioutil.WriteFile(downloadPath, fileData, 0o640) + if err != nil { + log.Errorf("writing file failed: %v", err) + os.Remove(downloadPath) + os.Exit(1) + } + + return downloadPath +} + +func decompress(archivePath string, targetPath string) string { + f, err := os.Open(archivePath) + if err != nil { + log.Errorf("opening file failed: %v", err) + os.Exit(1) + } + defer f.Close() + + z, err := zstd.NewReader(f) + if err != nil { + log.Errorf("decompression failed: %v", err) + os.Exit(1) + } + defer z.Close() + + log.Infof("decompressing %s", archivePath) + err = utils.ExtractTarStream(z, targetPath) + if err != nil { + log.Errorf("decompression failed: %v", err) + os.Exit(1) + } + + return targetPath +} \ No newline at end of file diff --git a/pkg/python/embed_darwin.go b/pkg/python/embed_darwin.go deleted file mode 100644 index ee5252620..000000000 --- a/pkg/python/embed_darwin.go +++ /dev/null @@ -1,8 +0,0 @@ -package python - -import "embed" - -//go:generate go run ../utils/embed_util/packer python-darwin.tar.gz ../../download-python/darwin/python/install bin lib/*.dylib lib/python3.* - -//go:embed python-darwin.tar.gz python-darwin.tar.gz.files -var pythonLib embed.FS diff --git a/pkg/python/embed_darwin_amd64.go b/pkg/python/embed_darwin_amd64.go new file mode 100644 index 000000000..4fb6ecf9a --- /dev/null +++ b/pkg/python/embed_darwin_amd64.go @@ -0,0 +1,9 @@ +package python + +import "embed" + +//go:generate go run ./download darwin amd64 ../../download-python/darwin/amd64 +//go:generate go run ../utils/embed_util/packer python-darwin-amd64.tar.gz ../../download-python/darwin/amd64/python/install bin lib/*.dylib lib/python3.* + +//go:embed python-darwin-amd64.tar.gz python-darwin-amd64.tar.gz.files +var pythonLib embed.FS diff --git a/pkg/python/embed_darwin_arm64.go b/pkg/python/embed_darwin_arm64.go new file mode 100644 index 000000000..18aa21233 --- /dev/null +++ b/pkg/python/embed_darwin_arm64.go @@ -0,0 +1,9 @@ +package python + +import "embed" + +//go:generate go run ./download darwin arm64 ../../download-python/darwin/arm64 +//go:generate go run ../utils/embed_util/packer python-darwin-arm64.tar.gz ../../download-python/darwin/arm64/python/install bin lib/*.dylib lib/python3.* + +//go:embed python-darwin-arm64.tar.gz python-darwin-arm64.tar.gz.files +var pythonLib embed.FS diff --git a/pkg/python/embed_linux.go b/pkg/python/embed_linux.go deleted file mode 100644 index db8d55096..000000000 --- a/pkg/python/embed_linux.go +++ /dev/null @@ -1,8 +0,0 @@ -package python - -import "embed" - -//go:generate go run ../utils/embed_util/packer python-linux.tar.gz ../../download-python/linux/python/install bin lib/*.so* lib/python3.* - -//go:embed python-linux.tar.gz python-linux.tar.gz.files -var pythonLib embed.FS diff --git a/pkg/python/embed_linux_amd64.go b/pkg/python/embed_linux_amd64.go new file mode 100644 index 000000000..5961a767d --- /dev/null +++ b/pkg/python/embed_linux_amd64.go @@ -0,0 +1,9 @@ +package python + +import "embed" + +//go:generate go run ./download linux amd64 ../../download-python/linux/amd64 +//go:generate go run ../utils/embed_util/packer python-linux-amd64.tar.gz ../../download-python/linux/amd64/python/install bin lib/*.so* lib/python3.* + +//go:embed python-linux-amd64.tar.gz python-linux-amd64.tar.gz.files +var pythonLib embed.FS diff --git a/pkg/python/embed_linux_arm64.go b/pkg/python/embed_linux_arm64.go new file mode 100644 index 000000000..40be4e8b0 --- /dev/null +++ b/pkg/python/embed_linux_arm64.go @@ -0,0 +1,9 @@ +package python + +import "embed" + +//go:generate go run ./download linux arm64 ../../download-python/linux/arm64 +//go:generate go run ../utils/embed_util/packer python-linux-arm64.tar.gz ../../download-python/linux/arm64/python/install bin lib/*.so* lib/python3.* + +//go:embed python-linux-arm64.tar.gz python-linux-arm64.tar.gz.files +var pythonLib embed.FS diff --git a/pkg/python/embed_windows.go b/pkg/python/embed_windows.go deleted file mode 100644 index 09a5af595..000000000 --- a/pkg/python/embed_windows.go +++ /dev/null @@ -1,8 +0,0 @@ -package python - -import "embed" - -//go:generate go run ../utils/embed_util/packer python-windows.tar.gz ../../download-python/windows/python/install Lib DLLs *.dll *.exe - -//go:embed python-windows.tar.gz python-windows.tar.gz.files -var pythonLib embed.FS diff --git a/pkg/python/embed_windows_amd64.go b/pkg/python/embed_windows_amd64.go new file mode 100644 index 000000000..dfa2a12a3 --- /dev/null +++ b/pkg/python/embed_windows_amd64.go @@ -0,0 +1,9 @@ +package python + +import "embed" + +//go:generate go run ./download windows amd64 ../../download-python/windows/amd64 +//go:generate go run ../utils/embed_util/packer python-windows-amd64.tar.gz ../../download-python/windows/amd64/python/install Lib DLLs *.dll *.exe + +//go:embed python-windows-amd64.tar.gz python-windows-amd64.tar.gz.files +var pythonLib embed.FS diff --git a/pkg/utils/embed_util/packer/main.go b/pkg/utils/embed_util/packer/main.go index f4d4ba5e3..39cb9e2cf 100644 --- a/pkg/utils/embed_util/packer/main.go +++ b/pkg/utils/embed_util/packer/main.go @@ -23,6 +23,7 @@ func main() { dir := os.Args[2] patterns := os.Args[3:] + log.Infof("writing tar %s", out) fileList, tgz := writeTar(dir, patterns) hash := sha256.Sum256(tgz) diff --git a/pkg/utils/tar.go b/pkg/utils/tar.go index 7fa7761b8..0cd6807ed 100644 --- a/pkg/utils/tar.go +++ b/pkg/utils/tar.go @@ -33,7 +33,11 @@ func ExtractTarGzStream(r io.Reader, targetPath string) error { } defer gz.Close() - tarReader := tar.NewReader(gz) + return ExtractTarStream(gz, targetPath) +} + +func ExtractTarStream(r io.Reader, targetPath string) error { + tarReader := tar.NewReader(r) for true { header, err := tarReader.Next() if err == io.EOF { @@ -41,7 +45,7 @@ func ExtractTarGzStream(r io.Reader, targetPath string) error { } if err != nil { - return fmt.Errorf("ExtractTarGz: Next() failed: %w", err) + return fmt.Errorf("ExtractTarStream: Next() failed: %w", err) } header.Name = strings.ReplaceAll(header.Name, "/", string(os.PathSeparator)) @@ -55,28 +59,28 @@ func ExtractTarGzStream(r io.Reader, targetPath string) error { switch header.Typeflag { case tar.TypeDir: if err := os.MkdirAll(p, 0755); err != nil { - return fmt.Errorf("ExtractTarGz: Mkdir() failed: %w", err) + return fmt.Errorf("ExtractTarStream: Mkdir() failed: %w", err) } case tar.TypeReg: outFile, err := os.Create(p) if err != nil { - return fmt.Errorf("ExtractTarGz: Create() failed: %w", err) + return fmt.Errorf("ExtractTarStream: Create() failed: %w", err) } _, err = io.Copy(outFile, tarReader) _ = outFile.Close() if err != nil { - return fmt.Errorf("ExtractTarGz: Copy() failed: %w", err) + return fmt.Errorf("ExtractTarStream: Copy() failed: %w", err) } err = os.Chmod(p, header.FileInfo().Mode()) if err != nil { - return fmt.Errorf("ExtractTarGz: Chmod() failed: %w", err) + return fmt.Errorf("ExtractTarStream: Chmod() failed: %w", err) } case tar.TypeSymlink: if err := os.Symlink(header.Linkname, p); err != nil { - return fmt.Errorf("ExtractTarGz: Symlink() failed: %w", err) + return fmt.Errorf("ExtractTarStream: Symlink() failed: %w", err) } default: - return fmt.Errorf("ExtractTarGz: uknown type %v in %v", header.Typeflag, header.Name) + return fmt.Errorf("ExtractTarStream: uknown type %v in %v", header.Typeflag, header.Name) } } return nil From cd56bbfdfe514475133a0c5e4411917fcb1ad2cf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 18:25:41 +0200 Subject: [PATCH 0024/2268] fix: Fix extracting of python --- pkg/python/embed.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/python/embed.go b/pkg/python/embed.go index b4dd1e637..e2654a685 100644 --- a/pkg/python/embed.go +++ b/pkg/python/embed.go @@ -16,7 +16,7 @@ func init() { } func decompressPython() string { - tarName := fmt.Sprintf("python-%s.tar.gz", runtime.GOOS) + tarName := fmt.Sprintf("python-%s-%s.tar.gz", runtime.GOOS, runtime.GOARCH) tgz, err := pythonLib.Open(tarName) if err != nil { log.Panic(err) From fc0a8c9cec75ceaf4486bb65098303235e394585 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 21:50:22 +0200 Subject: [PATCH 0025/2268] build: Use goreleaser for releases --- .github/workflows/release.yml | 34 ++++++++++ .../{build-and-release.yml => tests.yml} | 26 ------- .gitignore | 3 +- .goreleaser.yaml | 67 +++++++++++++++++++ Dockerfile | 3 + 5 files changed, 106 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/release.yml rename .github/workflows/{build-and-release.yml => tests.yml} (92%) create mode 100644 .goreleaser.yaml create mode 100644 Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..da7b6fba8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,34 @@ +name: goreleaser + +on: + push: + # run only against tags + tags: + - 'v*' + +permissions: + contents: write + packages: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Fetch all tags + run: git fetch --force --tags + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.17.9 + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + distribution: goreleaser + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/tests.yml similarity index 92% rename from .github/workflows/build-and-release.yml rename to .github/workflows/tests.yml index 26e3bcfc7..7b23d3510 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/tests.yml @@ -295,29 +295,3 @@ jobs: export KLUCTL_EXE=./dist/kluctl-${{ matrix.binary-suffix }}$TOOLS_EXE ./dist/e2e.test-${{ matrix.binary-suffix }}$TOOLS_EXE -test.v - release: - runs-on: ubuntu-latest - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') - needs: - - build - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Download artifacts - uses: actions/download-artifact@v2 - - name: Build checksums.txt - run: | - cd dist - sha256sum kluctl-linux-amd64 > checksums.txt - sha256sum kluctl-darwin-amd64 >> checksums.txt - sha256sum kluctl-windows-amd64.exe >> checksums.txt - - name: Release - uses: softprops/action-gh-release@v1 - with: - draft: true - body_path: changelog/CHANGELOG.md - files: | - dist/kluctl-linux-* - dist/kluctl-darwin-* - dist/kluctl-windows-* - dist/checksums.txt diff --git a/.gitignore b/.gitignore index ac7a47bd5..6cebf73ac 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ /kluctl.exe /e2e.test* /out -/reports \ No newline at end of file +/reports +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 000000000..2fde674b2 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,67 @@ +before: + hooks: + - go mod tidy + - go generate ./... + - go generate -tags linux,darwin,windows,amd64,arm64 ./pkg/python +builds: + - id: kluctl-unix + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + - id: kluctl-windows + env: + - CGO_ENABLED=0 + goos: + - windows + goarch: + - amd64 +archives: + - replacements: + darwin: Darwin + linux: Linux + windows: Windows + 386: i386 + amd64: x86_64 +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ incpatch .Version }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' + - '^chore:' + +release: + draft: true + prerelease: auto + name_template: "{{.ProjectName}}-v{{.Version}}" + +dockers: + - id: linux-amd64 + goos: linux + goarch: amd64 + build_flag_templates: + - "--platform=linux/amd64" + image_templates: + - "ghcr.io/kluctl/kluctl:{{ .Version }}-amd64" + - id: linux-arm64 + goos: linux + goarch: arm64 + build_flag_templates: + - "--platform=linux/arm64" + image_templates: + - "ghcr.io/kluctl/kluctl:{{ .Version }}-arm64" + +docker_manifests: + - name_template: ghcr.io/kluctl/kluctl:{{ .Version }} + image_templates: + - "ghcr.io/kluctl/kluctl:{{ .Version }}-amd64" + - "ghcr.io/kluctl/kluctl:{{ .Version }}-arm64" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..8e44c42ff --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine +COPY kluctl / +ENTRYPOINT ["/kluctl"] From 424c9334447ea1bd39a10ac4ed0a7f6424afe875 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 21:51:29 +0200 Subject: [PATCH 0026/2268] build: Move main.go to root dir --- .github/workflows/tests.yml | 6 +++--- cmd/kluctl/main.go => main.go | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename cmd/kluctl/main.go => main.go (100%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7b23d3510..076c4276c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -72,7 +72,7 @@ jobs: export CGO_ENABLED=0 export GOARCH=amd64 export GOOS=linux - go build ./cmd/kluctl + go build go test -c ./e2e mv kluctl kluctl-linux-amd64 mv e2e.test e2e.test-linux-amd64 @@ -81,7 +81,7 @@ jobs: export CGO_ENABLED=0 export GOARCH=amd64 export GOOS=darwin - go build ./cmd/kluctl + go build go test -c ./e2e mv kluctl kluctl-darwin-amd64 mv e2e.test e2e.test-darwin-amd64 @@ -90,7 +90,7 @@ jobs: export CGO_ENABLED=0 export GOARCH=amd64 export GOOS=windows - go build ./cmd/kluctl + go build go test -c ./e2e mv kluctl.exe kluctl-windows-amd64.exe mv e2e.test.exe e2e.test-windows-amd64.exe diff --git a/cmd/kluctl/main.go b/main.go similarity index 100% rename from cmd/kluctl/main.go rename to main.go From 912ee942c02ee1ef83e8f0be4e2e297134b53615 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 22:17:21 +0200 Subject: [PATCH 0027/2268] build: Use version provided by goreleaser --- .github/workflows/tests.yml | 27 --------------------------- cmd/kluctl/commands/cmd_version.go | 2 +- cmd/kluctl/commands/root.go | 4 ++-- main.go | 8 +++++++- pkg/version/version.go | 10 +++++++++- 5 files changed, 19 insertions(+), 32 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 076c4276c..c22bbadb3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,8 +10,6 @@ on: jobs: build: runs-on: ubuntu-latest - outputs: - version: ${{ steps.get_version.outputs.version }} steps: - name: Checkout uses: actions/checkout@v2 @@ -32,31 +30,6 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - name: Get Version - id: get_version - run: | - go install github.com/bvieira/sv4git/v2/cmd/git-sv@v2.7.0 - KLUCTL_VERSION="$(git sv next-version)" - if [[ "${{ github.ref }}" != refs/tags/* ]]; then - KLUCTL_VERSION="$KLUCTL_VERSION-dev-$(git rev-parse --short HEAD)" - fi - echo "KLUCTL_VERSION=$KLUCTL_VERSION" - echo "KLUCTL_VERSION=$KLUCTL_VERSION" >> $GITHUB_ENV - echo "::set-output name=version::$VERSION" - - name: Generate Changelog - id: gen_changelog - run: | - git sv changelog -n 1 > CHANGELOG.md - - name: Upload Changelog - uses: actions/upload-artifact@v2 - with: - name: changelog - path: | - CHANGELOG.md - - name: Write Version to version.go - run: | - sed -ibak "s/0.0.0/$KLUCTL_VERSION/g" pkg/version/version.go - cat pkg/version/version.go - name: Go Mod Vendor run: | go mod vendor diff --git a/cmd/kluctl/commands/cmd_version.go b/cmd/kluctl/commands/cmd_version.go index de05a2d49..98da1b398 100644 --- a/cmd/kluctl/commands/cmd_version.go +++ b/cmd/kluctl/commands/cmd_version.go @@ -9,6 +9,6 @@ type versionCmd struct { } func (cmd *versionCmd) Run() error { - _, err := os.Stdout.WriteString(version.Version + "\n") + _, err := os.Stdout.WriteString(version.GetVersion() + "\n") return err } diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 0b20a914b..4027e612c 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -81,7 +81,7 @@ func (c *cli) checkNewVersion() { if c.NoUpdateCheck { return } - if version.Version == "0.0.0" { + if version.GetVersion() == "(devel)" { return } @@ -119,7 +119,7 @@ func (c *cli) checkNewVersion() { latestVersionStr = latestVersionStr[1:] } latestVersion := versions.LooseVersion(latestVersionStr) - localVersion := versions.LooseVersion(version.Version) + localVersion := versions.LooseVersion(version.GetVersion()) if localVersion.Less(latestVersion, true) { log.Warningf("You are using an outdated version (%v) of kluctl. You should update soon to version %v", localVersion, latestVersion) } diff --git a/main.go b/main.go index e95adcccb..fb256f4c4 100644 --- a/main.go +++ b/main.go @@ -15,8 +15,14 @@ limitations under the License. */ package main -import "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" +import ( + "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" + version2 "github.com/kluctl/kluctl/v2/pkg/version" +) + +var version = "0.0.0" func main() { + version2.SetVersion(version) commands.Execute() } diff --git a/pkg/version/version.go b/pkg/version/version.go index 415100dcc..f948678a6 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,3 +1,11 @@ package version -var Version = "0.0.0" +var version = "0.0.0" + +func SetVersion(v string) { + version = v +} + +func GetVersion() string { + return version +} From b80c289402cf635f592aac48afe2336b4fa40ac0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 22:18:07 +0200 Subject: [PATCH 0028/2268] build: Fix "make build" to not depend on non-existing target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f73aa2b4c..dc4f6521c 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ check-kind: ## Checks if kind is installed kind version ## Build: -build: vendor python generate build-go ## Run the complete build pipeline +build: vendor generate build-go ## Run the complete build pipeline build-go: ## Build your project and put the output binary in out/bin/ mkdir -p out/bin From 1792931295772a4ce3f95f1980f3402a605fdcde Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 22:18:25 +0200 Subject: [PATCH 0029/2268] build: Install python in release job --- .github/workflows/release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index da7b6fba8..681f86b49 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,10 @@ jobs: uses: actions/setup-go@v2 with: go-version: 1.17.9 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.10.2 - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: From 76297c2d4a631d97536bd686228d3cc190e0bd03 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 22:18:41 +0200 Subject: [PATCH 0030/2268] build: Rename dist artifact to binaries --- .github/workflows/tests.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c22bbadb3..d0d67de68 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,11 +1,9 @@ -name: build +name: tests on: push: branches: - '*' - tags: - - 'v*' jobs: build: @@ -67,10 +65,10 @@ jobs: go test -c ./e2e mv kluctl.exe kluctl-windows-amd64.exe mv e2e.test.exe e2e.test-windows-amd64.exe - - name: Upload dist artifact + - name: Upload binaries uses: actions/upload-artifact@v2 with: - name: dist + name: binaries path: | kluctl-linux-amd64 kluctl-darwin-amd64 @@ -259,12 +257,11 @@ jobs: echo "KIND_KUBECONFIG=$(pwd)/kind-kubeconfig" >> $GITHUB_ENV ./hack/start-kind-cluster.sh "$KIND_CLUSTER_NAME" "$DOCKER_IP" "$PORT" - - name: Download dist artifacts + - name: Download artifacts uses: actions/download-artifact@v2 - name: Run e2e tests shell: bash run: | - chmod +x ./dist/* - export KLUCTL_EXE=./dist/kluctl-${{ matrix.binary-suffix }}$TOOLS_EXE - ./dist/e2e.test-${{ matrix.binary-suffix }}$TOOLS_EXE -test.v - + chmod +x ./binaries/* + export KLUCTL_EXE=./binaries/kluctl-${{ matrix.binary-suffix }}$TOOLS_EXE + ./binaries/e2e.test-${{ matrix.binary-suffix }}$TOOLS_EXE -test.v From 6dac10ce6fc82cb3a6bc63c550baf89b65c0c5f6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 23:03:44 +0200 Subject: [PATCH 0031/2268] build: Skip extraction to speed up generate --- pkg/python/download/main.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/python/download/main.go b/pkg/python/download/main.go index 0987a2015..b61e93840 100644 --- a/pkg/python/download/main.go +++ b/pkg/python/download/main.go @@ -56,14 +56,23 @@ func main() { } downloadPath := download(osName, arch, dist) + archiveBytes, _ := ioutil.ReadFile(downloadPath) + hash := utils.Sha256Bytes(archiveBytes) - os.RemoveAll(extractPath) - decompress(downloadPath, extractPath) + if utils.Exists(filepath.Join(extractPath, hash)) { + log.Infof("skipping extract") + return + } + + _ = os.RemoveAll(extractPath) + extract(downloadPath, extractPath) for _, lib := range removeLibs { _ = os.RemoveAll(filepath.Join(extractPath, "python", "install", "lib", fmt.Sprintf("python%s", pythonVersionBase), lib)) _ = os.RemoveAll(filepath.Join(extractPath, "python", "install", "Lib", lib)) } + + _ = utils.Touch(filepath.Join(extractPath, hash)) } func download(osName, arch, dist string) string { @@ -106,7 +115,7 @@ func download(osName, arch, dist string) string { return downloadPath } -func decompress(archivePath string, targetPath string) string { +func extract(archivePath string, targetPath string) string { f, err := os.Open(archivePath) if err != nil { log.Errorf("opening file failed: %v", err) From 888ccd053ddb97f4d727abf208a9252a21b6aaf0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 23:04:09 +0200 Subject: [PATCH 0032/2268] build: No need to duplicate builds (we can use ignore) --- .goreleaser.yaml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 2fde674b2..94593898d 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -4,22 +4,19 @@ before: - go generate ./... - go generate -tags linux,darwin,windows,amd64,arm64 ./pkg/python builds: - - id: kluctl-unix + - id: kluctl env: - CGO_ENABLED=0 goos: - linux - darwin - goarch: - - amd64 - - arm64 - - id: kluctl-windows - env: - - CGO_ENABLED=0 - goos: - windows goarch: - amd64 + - arm64 + ignore: + - goos: windows + goarch: arm64 archives: - replacements: darwin: Darwin From 3c27643a0c621476bc4ac5340ed550ccc049f4f6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 23:08:56 +0200 Subject: [PATCH 0033/2268] build: No need to generate for arm64 for tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d0d67de68..e0d2548be 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,7 +34,7 @@ jobs: - name: Go Generate run: | go generate ./... - go generate -tags linux,darwin,windows,amd64,arm64 ./pkg/python + go generate -tags linux,darwin,windows,amd64 ./pkg/python - name: Run unit tests run: | go test ./cmd/... ./pkg/... -v From 0afaa38a450068c748794b4967d781bace5b9d86 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 23:13:34 +0200 Subject: [PATCH 0034/2268] build: Fix make build --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dc4f6521c..386304320 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ build: vendor generate build-go ## Run the complete build pipeline build-go: ## Build your project and put the output binary in out/bin/ mkdir -p out/bin - CGO_ENBALED=0 GO111MODULE=on $(GOCMD) build -mod vendor -o out/bin/$(BINARY_NAME) ./cmd/kluctl + CGO_ENBALED=0 GO111MODULE=on $(GOCMD) build -mod vendor -o out/bin/$(BINARY_NAME) clean: ## Remove build related file rm -fr ./bin From b54f008c15d5805205f5ccc535323384c14035bc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 21 Apr 2022 23:58:22 +0200 Subject: [PATCH 0035/2268] build: Add homebrew release --- .github/workflows/release.yml | 1 + .goreleaser.yaml | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 681f86b49..d58ed932d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,3 +36,4 @@ jobs: args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 94593898d..31e60d0e1 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -62,3 +62,17 @@ docker_manifests: image_templates: - "ghcr.io/kluctl/kluctl:{{ .Version }}-amd64" - "ghcr.io/kluctl/kluctl:{{ .Version }}-arm64" + +brews: + - name: kluctl + tap: + owner: kluctl + name: homebrew-tap + token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" + folder: Formula + homepage: "https://kluctl.io/" + description: "kluctl" + install: | + bin.install "kluctl" + test: | + system "#{bin}/kluctl version" From ad816f20e47569f58789616ab193f8f06b68563e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 00:04:19 +0200 Subject: [PATCH 0036/2268] build: Immediately release instead of creating a draft --- .goreleaser.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 31e60d0e1..14d80e4f5 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -37,7 +37,6 @@ changelog: - '^chore:' release: - draft: true prerelease: auto name_template: "{{.ProjectName}}-v{{.Version}}" From 27e8bb305998e377c51e7fe93b1b1c83e71e9070 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 00:20:37 +0200 Subject: [PATCH 0037/2268] build: Add go cache to goreleaser workflow --- .github/workflows/release.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d58ed932d..a5a72b116 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,6 +28,14 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.10.2 + - uses: actions/cache@v2 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: From ad14a743c72408c9869b599819bee75d30665997 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 00:22:36 +0200 Subject: [PATCH 0038/2268] build: Use own cache key for goreleaser --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a5a72b116..3bff60fef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,9 +33,9 @@ jobs: path: | ~/go/pkg/mod ~/.cache/go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-goreleaser-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-go- + ${{ runner.os }}-goreleaser- - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: From 803a6fd8e607e593f95eb6695eaa785e88ad251d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 00:31:51 +0200 Subject: [PATCH 0039/2268] build: Add opencontainers labels to docker images --- .goreleaser.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 14d80e4f5..5bba04646 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -45,6 +45,13 @@ dockers: goos: linux goarch: amd64 build_flag_templates: + - "--pull" + - "--build-arg=ARCH=linux/amd64" + - "--label=org.opencontainers.image.created={{ .Date }}" + - "--label=org.opencontainers.image.name={{ .ProjectName }}" + - "--label=org.opencontainers.image.revision={{ .FullCommit }}" + - "--label=org.opencontainers.image.version={{ .Version }}" + - "--label=org.opencontainers.image.source={{ .GitURL }}" - "--platform=linux/amd64" image_templates: - "ghcr.io/kluctl/kluctl:{{ .Version }}-amd64" @@ -52,6 +59,13 @@ dockers: goos: linux goarch: arm64 build_flag_templates: + - "--pull" + - "--build-arg=ARCH=linux/arm64" + - "--label=org.opencontainers.image.created={{ .Date }}" + - "--label=org.opencontainers.image.name={{ .ProjectName }}" + - "--label=org.opencontainers.image.revision={{ .FullCommit }}" + - "--label=org.opencontainers.image.version={{ .Version }}" + - "--label=org.opencontainers.image.source={{ .GitURL }}" - "--platform=linux/arm64" image_templates: - "ghcr.io/kluctl/kluctl:{{ .Version }}-arm64" From 0cd6851b5ff377a0abfa6ef122927d2aa513ce73 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 00:33:58 +0200 Subject: [PATCH 0040/2268] build: Setup docker and login to ghcr --- .github/workflows/release.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3bff60fef..a304b20d6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,6 +28,17 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.10.2 + - name: Setup QEMU + uses: docker/setup-qemu-action@v1 + - name: Setup Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: fluxcdbot + password: ${{ secrets.GHCR_TOKEN }} - uses: actions/cache@v2 with: path: | From 9316fe4370a3bd7e5b5fc326d984fdf7d19b8d29 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 00:36:03 +0200 Subject: [PATCH 0041/2268] build: Use correct username for ghcr login --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a304b20d6..1f423ea0d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,7 @@ jobs: uses: docker/login-action@v1 with: registry: ghcr.io - username: fluxcdbot + username: kluctlbot password: ${{ secrets.GHCR_TOKEN }} - uses: actions/cache@v2 with: From 8e6cb2651eb4eaad751d6ee33ae189d47bd98e92 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 00:59:19 +0200 Subject: [PATCH 0042/2268] build: Fix HOMEBREW_TAP_GITHUB_TOKEN --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f423ea0d..0f2953895 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,4 +55,4 @@ jobs: args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} + HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} From dc218a424e2f4da3833e04acb56b9dc4a1453677 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 09:34:22 +0200 Subject: [PATCH 0043/2268] build: Use tag instead of version in release names/tags --- .goreleaser.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 5bba04646..851a78bfc 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -38,7 +38,7 @@ changelog: release: prerelease: auto - name_template: "{{.ProjectName}}-v{{.Version}}" + name_template: "{{.ProjectName}}-{{.Tag}}" dockers: - id: linux-amd64 @@ -54,7 +54,7 @@ dockers: - "--label=org.opencontainers.image.source={{ .GitURL }}" - "--platform=linux/amd64" image_templates: - - "ghcr.io/kluctl/kluctl:{{ .Version }}-amd64" + - "ghcr.io/kluctl/kluctl:{{ .Tag }}-amd64" - id: linux-arm64 goos: linux goarch: arm64 @@ -68,13 +68,13 @@ dockers: - "--label=org.opencontainers.image.source={{ .GitURL }}" - "--platform=linux/arm64" image_templates: - - "ghcr.io/kluctl/kluctl:{{ .Version }}-arm64" + - "ghcr.io/kluctl/kluctl:{{ .Tag }}-arm64" docker_manifests: - - name_template: ghcr.io/kluctl/kluctl:{{ .Version }} + - name_template: ghcr.io/kluctl/kluctl:{{ .Tag }} image_templates: - - "ghcr.io/kluctl/kluctl:{{ .Version }}-amd64" - - "ghcr.io/kluctl/kluctl:{{ .Version }}-arm64" + - "ghcr.io/kluctl/kluctl:{{ .Tag }}-amd64" + - "ghcr.io/kluctl/kluctl:{{ .Tag }}-arm64" brews: - name: kluctl From d3ebe24ac3faa8968af03f1e30d74f3fea8d7fb4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 09:39:23 +0200 Subject: [PATCH 0044/2268] fix: Use $TMPDIR/kluctl-workdir as base for temporary files This avoids clashes in case someone has put a file into /tmp/ with the name kluctl, which is not that unlikely to happen. --- pkg/utils/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index aa5433d9e..ae338d920 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -14,7 +14,7 @@ import ( var createTmpBaseDirOnce sync.Once func GetTmpBaseDir() string { - dir := filepath.Join(os.TempDir(), "kluctl") + dir := filepath.Join(os.TempDir(), "kluctl-workdir") createTmpBaseDirOnce.Do(func() { err := os.MkdirAll(dir, 0o700) if err != nil { From c7847ae653352b373f18f7c51e666a288a9eee1f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 10:04:13 +0200 Subject: [PATCH 0045/2268] build: Add SBOM to releases --- .github/workflows/release.yml | 2 ++ .goreleaser.yaml | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f2953895..b91fdad0a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,8 @@ jobs: - name: Setup Docker Buildx id: buildx uses: docker/setup-buildx-action@v1 + - name: Setup Syft + uses: anchore/sbom-action/download-syft@v0 - name: Login to GitHub Container Registry uses: docker/login-action@v1 with: diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 851a78bfc..3fd73cc23 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -24,6 +24,11 @@ archives: windows: Windows 386: i386 amd64: x86_64 +sboms: + - id: source + artifacts: source + documents: + - "{{ .ProjectName }}_v{{ .Version }}_sbom.spdx.json" checksum: name_template: 'checksums.txt' snapshot: From cb3bed6b7414ad8cf53ddd28f29c15aab743c071 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 10:05:31 +0200 Subject: [PATCH 0046/2268] build: Fix versions in release artifacts and stop replacing os/arch --- .goreleaser.yaml | 52 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 3fd73cc23..6fb8940d7 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -4,26 +4,44 @@ before: - go generate ./... - go generate -tags linux,darwin,windows,amd64,arm64 ./pkg/python builds: - - id: kluctl - env: - - CGO_ENABLED=0 + - <<: &build_defaults + binary: kluctl + env: + - CGO_ENABLED=0 + id: linux goos: - linux + goarch: + - amd64 + - arm64 + goarm: + - 7 + - <<: *build_defaults + id: darwin + goos: - darwin - - windows goarch: - amd64 - arm64 - ignore: - - goos: windows - goarch: arm64 + - <<: *build_defaults + id: windows + goos: + - windows + goarch: + - amd64 archives: - - replacements: - darwin: Darwin - linux: Linux - windows: Windows - 386: i386 - amd64: x86_64 + - name_template: "{{ .Binary }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}" + id: nix + builds: [linux, darwin] + format: tar.gz + files: + - none* + - name_template: "{{ .Binary }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}" + id: windows + builds: [windows] + format: zip + files: + - none* sboms: - id: source artifacts: source @@ -59,7 +77,7 @@ dockers: - "--label=org.opencontainers.image.source={{ .GitURL }}" - "--platform=linux/amd64" image_templates: - - "ghcr.io/kluctl/kluctl:{{ .Tag }}-amd64" + - "ghcr.io/kluctl/kluctl:v{{ .Version }}-amd64" - id: linux-arm64 goos: linux goarch: arm64 @@ -73,13 +91,13 @@ dockers: - "--label=org.opencontainers.image.source={{ .GitURL }}" - "--platform=linux/arm64" image_templates: - - "ghcr.io/kluctl/kluctl:{{ .Tag }}-arm64" + - "ghcr.io/kluctl/kluctl:v{{ .Version }}-arm64" docker_manifests: - name_template: ghcr.io/kluctl/kluctl:{{ .Tag }} image_templates: - - "ghcr.io/kluctl/kluctl:{{ .Tag }}-amd64" - - "ghcr.io/kluctl/kluctl:{{ .Tag }}-arm64" + - "ghcr.io/kluctl/kluctl:v{{ .Version }}-amd64" + - "ghcr.io/kluctl/kluctl:v{{ .Version }}-arm64" brews: - name: kluctl From cf56e5a8afaadbd83c121439a6276bde9863eb16 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 10:05:41 +0200 Subject: [PATCH 0047/2268] build: Add source to releases --- .goreleaser.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 6fb8940d7..c29d0d594 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -42,6 +42,9 @@ archives: format: zip files: - none* +source: + enabled: true + name_template: '{{ .ProjectName }}_v{{ .Version }}_source_code' sboms: - id: source artifacts: source From e0157fcb8548b66d5e03f8640ee9c912fbc13104 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 10:51:35 +0200 Subject: [PATCH 0048/2268] build: Put binaries below ./bin when using make build --- .gitignore | 2 +- Makefile | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 6cebf73ac..3158e3eb0 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,6 @@ /kluctl /kluctl.exe /e2e.test* -/out +/bin /reports dist/ diff --git a/Makefile b/Makefile index 386304320..6372eb9d5 100644 --- a/Makefile +++ b/Makefile @@ -32,9 +32,9 @@ check-kind: ## Checks if kind is installed ## Build: build: vendor generate build-go ## Run the complete build pipeline -build-go: ## Build your project and put the output binary in out/bin/ - mkdir -p out/bin - CGO_ENBALED=0 GO111MODULE=on $(GOCMD) build -mod vendor -o out/bin/$(BINARY_NAME) +build-go: ## Build your project and put the output binary in ./bin/ + mkdir -p ./bin + CGO_ENBALED=0 GO111MODULE=on $(GOCMD) build -mod vendor -o ./bin/$(BINARY_NAME) clean: ## Remove build related file rm -fr ./bin @@ -53,7 +53,7 @@ generate: ## Generating Python and Jinja2 support test: test-unit test-e2e ## Runs the complete test suite test-e2e: check-kubectl check-helm check-kind ## Runs the end to end tests - CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -o out/bin/$(TEST_BINARY_NAME) ./e2e + CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -o ./bin/$(TEST_BINARY_NAME) ./e2e test-unit: ## Run the unit tests of the project ifeq ($(EXPORT_RESULT), true) From 144310b777a36f033cc3d0f4a601e5f74d9cf9bd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 10:53:46 +0200 Subject: [PATCH 0049/2268] build: Change checksums.txt to {{ .ProjectName }}_v{{ .Version }}_checksums.txt --- .goreleaser.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index c29d0d594..e526c43db 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -51,7 +51,7 @@ sboms: documents: - "{{ .ProjectName }}_v{{ .Version }}_sbom.spdx.json" checksum: - name_template: 'checksums.txt' + name_template: '{{ .ProjectName }}_v{{ .Version }}_checksums.txt' snapshot: name_template: "{{ incpatch .Version }}-next" changelog: From c0d0fe7e9ff4e4a097d2cc09cfcc4320c46ad549 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 11:06:25 +0200 Subject: [PATCH 0050/2268] docs: Add install script --- install/README.md | 41 ++++++++++ install/kluctl.sh | 193 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 install/README.md create mode 100755 install/kluctl.sh diff --git a/install/README.md b/install/README.md new file mode 100644 index 000000000..cf80366eb --- /dev/null +++ b/install/README.md @@ -0,0 +1,41 @@ +# kluctl Installation + +Binaries for macOS and Linux AMD64 are available for download on the +[release page](https://github.com/kluctl/kluctl/releases). + +To install the latest release run: + +```bash +curl -s https://raw.githubusercontent.com/kluctl/kluctl/main/install/kluctl.sh | sudo bash +``` + +The install script does the following: +* attempts to detect your OS +* downloads and unpacks the release tar file in a temporary directory +* copies the kluctl binary to `/usr/local/bin` +* removes the temporary directory + +## Alternative installation methods + +See https://kluctl.io/getting-started/installation for alternative installation methods. + +## Build from source + +Clone the repository: + +```bash +git clone https://github.com/kluctl/kluctl +cd kluctl +``` + +Build the `kluctl` binary (requires go >= 1.17 and python >= 3.10): + +```bash +make build +``` + +Run the binary: + +```bash +./bin/kluctl -h +``` diff --git a/install/kluctl.sh b/install/kluctl.sh new file mode 100755 index 000000000..b8a34066b --- /dev/null +++ b/install/kluctl.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +set -e + +DEFAULT_BIN_DIR="/usr/local/bin" +BIN_DIR=${1:-"${DEFAULT_BIN_DIR}"} +GITHUB_REPO="kluctl/kluctl" + +# Helper functions for logs +info() { + echo '[INFO] ' "$@" +} + +warn() { + echo '[WARN] ' "$@" >&2 +} + +fatal() { + echo '[ERROR] ' "$@" >&2 + exit 1 +} + +# Set os, fatal if operating system not supported +setup_verify_os() { + if [[ -z "${OS}" ]]; then + OS=$(uname) + fi + case ${OS} in + Darwin) + OS=darwin + ;; + Linux) + OS=linux + ;; + *) + fatal "Unsupported operating system ${OS}" + esac +} + +# Set arch, fatal if architecture not supported +setup_verify_arch() { + if [[ -z "${ARCH}" ]]; then + ARCH=$(uname -m) + fi + case ${ARCH} in + arm64|aarch64|armv8l) + ARCH=arm64 + ;; + amd64) + ARCH=amd64 + ;; + x86_64) + ARCH=amd64 + ;; + *) + fatal "Unsupported architecture ${ARCH}" + esac +} + +# Verify existence of downloader executable +verify_downloader() { + # Return failure if it doesn't exist or is no executable + [[ -x "$(which "$1")" ]] || return 1 + + # Set verified executable as our downloader program and return success + DOWNLOADER=$1 + return 0 +} + +# Create tempory directory and cleanup when done +setup_tmp() { + TMP_DIR=$(mktemp -d -t kluctl-install.XXXXXXXXXX) + TMP_METADATA="${TMP_DIR}/kluctl.json" + TMP_HASH="${TMP_DIR}/kluctl.hash" + TMP_BIN="${TMP_DIR}/kluctl.tar.gz" + cleanup() { + local code=$? + set +e + trap - EXIT + rm -rf "${TMP_DIR}" + exit ${code} + } + trap cleanup INT EXIT +} + +# Find version from Github metadata +get_release_version() { + if [[ -n "${kluctl_VERSION}" ]]; then + SUFFIX_URL="tags/v${kluctl_VERSION}" + else + SUFFIX_URL="latest" + fi + + METADATA_URL="https://api.github.com/repos/${GITHUB_REPO}/releases/${SUFFIX_URL}" + + info "Downloading metadata ${METADATA_URL}" + download "${TMP_METADATA}" "${METADATA_URL}" + + VERSION_KLUCTL=$(grep '"tag_name":' "${TMP_METADATA}" | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-) + if [[ -n "${VERSION_KLUCTL}" ]]; then + info "Using ${VERSION_KLUCTL} as release" + else + fatal "Unable to determine release version" + fi +} + +# Download from file from URL +download() { + [[ $# -eq 2 ]] || fatal 'download needs exactly 2 arguments' + + case $DOWNLOADER in + curl) + curl -o "$1" -sfL "$2" + ;; + wget) + wget -qO "$1" "$2" + ;; + *) + fatal "Incorrect executable '${DOWNLOADER}'" + ;; + esac + + # Abort if download command failed + [[ $? -eq 0 ]] || fatal 'Download failed' +} + +# Download hash from Github URL +download_hash() { + HASH_URL="https://github.com/${GITHUB_REPO}/releases/download/v${VERSION_KLUCTL}/kluctl_v${VERSION_KLUCTL}_checksums.txt" + + info "Downloading hash ${HASH_URL}" + download "${TMP_HASH}" "${HASH_URL}" + HASH_EXPECTED=$(grep " kluctl_v${VERSION_KLUCTL}_${OS}_${ARCH}.tar.gz$" "${TMP_HASH}") + HASH_EXPECTED=${HASH_EXPECTED%%[[:blank:]]*} +} + +# Download binary from Github URL +download_binary() { + BIN_URL="https://github.com/${GITHUB_REPO}/releases/download/v${VERSION_KLUCTL}/kluctl_v${VERSION_KLUCTL}_${OS}_${ARCH}.tar.gz" + info "Downloading binary ${BIN_URL}" + download "${TMP_BIN}" "${BIN_URL}" +} + +compute_sha256sum() { + cmd=$(which sha256sum shasum | head -n 1) + case $(basename "$cmd") in + sha256sum) + sha256sum "$1" | cut -f 1 -d ' ' + ;; + shasum) + shasum -a 256 "$1" | cut -f 1 -d ' ' + ;; + *) + fatal "Can not find sha256sum or shasum to compute checksum" + ;; + esac +} + +# Verify downloaded binary hash +verify_binary() { + info "Verifying binary download" + HASH_BIN=$(compute_sha256sum "${TMP_BIN}") + HASH_BIN=${HASH_BIN%%[[:blank:]]*} + if [[ "${HASH_EXPECTED}" != "${HASH_BIN}" ]]; then + fatal "Download sha256 does not match ${HASH_EXPECTED}, got ${HASH_BIN}" + fi +} + +# Setup permissions and move binary +setup_binary() { + chmod 755 "${TMP_BIN}" + info "Installing kluctl to ${BIN_DIR}/kluctl" + tar -xzof "${TMP_BIN}" -C "${TMP_DIR}" + + local CMD_MOVE="mv -f \"${TMP_DIR}/kluctl\" \"${BIN_DIR}\"" + if [[ -w "${BIN_DIR}" ]]; then + eval "${CMD_MOVE}" + else + eval "sudo ${CMD_MOVE}" + fi +} + +# Run the install process +{ + setup_verify_os + setup_verify_arch + verify_downloader curl || verify_downloader wget || fatal 'Can not find curl or wget for downloading files' + setup_tmp + get_release_version + download_hash + download_binary + verify_binary + setup_binary +} \ No newline at end of file From 1539b364c05fcd1ef106f947919fe6c19393ae5a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 11:30:59 +0200 Subject: [PATCH 0051/2268] build: Add helm to docker image --- .goreleaser.yaml | 4 ++-- Dockerfile | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index e526c43db..7ec26ccc2 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -72,7 +72,7 @@ dockers: goarch: amd64 build_flag_templates: - "--pull" - - "--build-arg=ARCH=linux/amd64" + - "--build-arg=ARCH=linux-amd64" - "--label=org.opencontainers.image.created={{ .Date }}" - "--label=org.opencontainers.image.name={{ .ProjectName }}" - "--label=org.opencontainers.image.revision={{ .FullCommit }}" @@ -86,7 +86,7 @@ dockers: goarch: arm64 build_flag_templates: - "--pull" - - "--build-arg=ARCH=linux/arm64" + - "--build-arg=ARCH=linux-arm64" - "--label=org.opencontainers.image.created={{ .Date }}" - "--label=org.opencontainers.image.name={{ .ProjectName }}" - "--label=org.opencontainers.image.revision={{ .FullCommit }}" diff --git a/Dockerfile b/Dockerfile index 8e44c42ff..991b24a50 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,15 @@ +FROM alpine:3.15 as builder + +RUN apk add --no-cache ca-certificates curl + +ARG ARCH=linux-amd64 + +ENV HELM_VERSION=v3.8.2 +RUN wget -O helm.tar.gz https://get.helm.sh/helm-$HELM_VERSION-$ARCH.tar.gz && \ + tar xzf helm.tar.gz && \ + mv $ARCH/helm / + FROM alpine -COPY kluctl / -ENTRYPOINT ["/kluctl"] +COPY --from=builder /helm /usr/bin +COPY kluctl /usr/bin/ +ENTRYPOINT ["/usr/bin/kluctl"] From 32d92d7c887c60ae1bc2e54edfa01b67f12fa26a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 12:22:56 +0200 Subject: [PATCH 0052/2268] docs: No need to use sudo when running installer script It does this internally when needed. --- install/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/README.md b/install/README.md index cf80366eb..422e42d38 100644 --- a/install/README.md +++ b/install/README.md @@ -6,7 +6,7 @@ Binaries for macOS and Linux AMD64 are available for download on the To install the latest release run: ```bash -curl -s https://raw.githubusercontent.com/kluctl/kluctl/main/install/kluctl.sh | sudo bash +curl -s https://raw.githubusercontent.com/kluctl/kluctl/main/install/kluctl.sh | bash ``` The install script does the following: From f1e4f643c98b3c11dc75884480a26dd8f0763729 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Apr 2022 16:54:11 +0200 Subject: [PATCH 0053/2268] Update README.md with text from https://kluctl.io/docs --- README.md | 58 ++++++++++++++++++++++++++++++++--------------- install/README.md | 2 +- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 6f71b363c..099728ed9 100644 --- a/README.md +++ b/README.md @@ -2,30 +2,52 @@ kluctl -kluctl is the missing glue that puts together your (and any third-party) deployments into one large declarative + + +Kluctl is the missing glue that puts together your (and any third-party) deployments into one large declarative Kubernetes deployment, while making it fully manageable (deploy, diff, prune, delete, ...) via one unified command line interface. -kluctl tries to be as flexible as possible, while remaining as simple as possible. It reuses established -tools (e.g. kustomize and Helm), making it possible to re-use a large set of available third-party deployments. +Kluctl tries to be as flexible as possible, while remaining as simple as possible. It reuses established +tools (e.g. Kustomize and Helm), making it possible to re-use a large set of available third-party deployments. -kluctl works completely locally, on the same machines that `kubectl` runs on. kluctl does not rely on any operators or other cluster-side components. -As long as the target cluster's kubeconfig is present locally, you are able to execute it from everywhere, including your -CI/CD pipelines or your laptop. +Kluctl is centered around "targets", which can be a cluster or a specific environment (e.g. test, dev, prod, ...) on one +or multiple clusters. Targets can be deployed, diffed, pruned, deleted, and so on. The idea is to have the same set of +operations for every target, no matter how simple or complex the deployment and/or target is. -Use kluctl to: -* Organize large and complex deployments, consisting of many Helm charts and kustomize deployments -* Do the same for small and simple deployments, as the overhead is small -* Always know what the state of your deployments is by being able to run diffs on the whole deployment -* Always know what you actually changed after performing a deployment -* Keep your clusters clean by issuing regular prune calls -* Deploy the same deployment to multiple environments (dev, test, prod, ...), with flexible differences in configuration -* Manage multiple target clusters (in multiple clouds or bare-metal if you want) -* Manage encrypted secrets for multiple target environments and clusters (based on [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)) -* Integrate it into your CI/CD pipelines and avoid putting too much logic into your shell scripts +Kluctl does not depend on external operators/controllers and allows to use the same deployment wherever you want, +as long as access to the kluctl project and clusters is available. This means, that you can use it from your +local machine, from your CI/CD pipelines or any automation platform/system that allows to call custom tools. -![](https://kluctl.io/asciinema/kluctl.gif) +Flux support is in development and will come soon. + +## Installation + +See [installation](./install). ## Documentation -Documentation can be found here: https://kluctl.io +Documentation can be found here: https://kluctl.io/docs + +## Kluctl in Short + + + +| | | +| --- | --- | +| 💪 Kluctl handles all your deployments | You can manage all your deployments with Kluctl, including infrastructure related and your applications. | +| 🪶 Complex or simple, all the same | You can manage complex and simple deployments with Kluctl. Simple deployments are lightweight while complex deployment are easily manageable. | +| 🤖 Native git support | Kluctl has native Git support integrated, meaning that it can easily deploy remote Kluctl projects or externalize parts (e.g. cluster configs) of your Kluctl project. | +| 🪐 Multiple environments | Deploy the same deployment to multiple environments (dev, test, prod, ...), with flexible differences in configuration. | +| 🌌 Multiple clusters | Manage multiple target clusters (in multiple clouds or bare-metal if you want). | +| 🔩 Configuration and Templating | Kluctl allows to use templating in nearly all places, making it easy to have dynamic configuration. | +| ⎈ Helm and Kustomize | The Helm and Kustomize integrations allow you to reuse plenty of third-party charts and kustomizations. | +| 🔍 See what's different | Always know what the state of your deployments is by being able to run diffs on the whole deployment. | +| 🔎 See what happened | Always know what you actually changed after performing a deployment. | +| 💥 Know what went wrong | Kluctl will show you what part of your deployment failed and why. | +| 👐 Live and let live | Kluctl tries to not interfere with any other tools or operators. This is possible due to it's use of server-side-apply. | +| 🧹 Keep it clean | Keep your clusters clean by issuing regular prune calls. | +| 🔐 Encrypted Secrets | Manage encrypted secrets for multiple target environments and clusters. | + +## Demo +![](https://kluctl.io/asciinema/kluctl.gif) diff --git a/install/README.md b/install/README.md index 422e42d38..09f38f6e3 100644 --- a/install/README.md +++ b/install/README.md @@ -17,7 +17,7 @@ The install script does the following: ## Alternative installation methods -See https://kluctl.io/getting-started/installation for alternative installation methods. +See https://kluctl.io/docs/installation for alternative installation methods. ## Build from source From f8823bc0abda98dfb25d1171137613a368fc80de Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Sun, 24 Apr 2022 00:33:05 +0200 Subject: [PATCH 0054/2268] fix: runtime error on seal command when missing SealedSecret (nil) --- pkg/deployment/commands/seal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/deployment/commands/seal.go b/pkg/deployment/commands/seal.go index 3a91f8b87..cc255a91c 100644 --- a/pkg/deployment/commands/seal.go +++ b/pkg/deployment/commands/seal.go @@ -20,7 +20,7 @@ func NewSealCommand(c *deployment.DeploymentCollection) *SealCommand { } func (cmd *SealCommand) Run(sealer *seal.Sealer) error { - if cmd.c.Project.Config.SealedSecrets.OutputPattern == nil { + if cmd.c.Project.Config.SealedSecrets == nil || cmd.c.Project.Config.SealedSecrets.OutputPattern == nil { return fmt.Errorf("sealedSecrets.outputPattern is not defined") } From 6a723d1e5fe0ab667065124bf1fcdaf1f743466a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 26 Apr 2022 11:36:21 +0200 Subject: [PATCH 0055/2268] fix: Allow to save metadata externally for all commands --- cmd/kluctl/args/project.go | 1 + cmd/kluctl/commands/cmd_archive.go | 5 ++--- cmd/kluctl/commands/utils.go | 12 ++++++++++++ pkg/kluctl_project/load.go | 30 +++++++++++++++--------------- pkg/types/metadata.go | 2 +- 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 9153d27f8..8b6f0d583 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -10,6 +10,7 @@ type ProjectFlags struct { LocalSealedSecrets string `group:"project" help:"Local sealed-secrets directory. Overrides the project from .kluctl.yml" type:"existingdir"` FromArchive string `group:"project" help:"Load project (.kluctl.yml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." type:"existing"` FromArchiveMetadata string `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." type:"existingfile"` + OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file." type:"file"` Cluster string `group:"project" help:"Specify/Override cluster"` } diff --git a/cmd/kluctl/commands/cmd_archive.go b/cmd/kluctl/commands/cmd_archive.go index 90977ace4..bcbeb9f42 100644 --- a/cmd/kluctl/commands/cmd_archive.go +++ b/cmd/kluctl/commands/cmd_archive.go @@ -8,8 +8,7 @@ import ( type archiveCmd struct { args.ProjectFlags - OutputArchive string `group:"misc" help:"Path to .tgz to write project to." type:"path"` - OutputMetadata string `group:"misc" help:"Path to .yml to write metadata to. If not specified, metadata is written into the archive."` + OutputArchive string `group:"misc" help:"Path to .tgz to write project to." type:"path"` } func (cmd *archiveCmd) Help() string { @@ -18,6 +17,6 @@ func (cmd *archiveCmd) Help() string { func (cmd *archiveCmd) Run() error { return withKluctlProjectFromArgs(cmd.ProjectFlags, func(p *kluctl_project.KluctlProjectContext) error { - return p.CreateTGZArchive(cmd.OutputArchive, cmd.OutputMetadata) + return p.CreateTGZArchive(cmd.OutputArchive, cmd.ProjectFlags.OutputMetadata == "") }) } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index bfcbf341e..829ab5bbb 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -8,6 +8,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" "os" ) @@ -49,6 +50,17 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl if err != nil { return err } + if projectFlags.OutputMetadata != "" { + md := p.GetMetadata() + b, err := yaml.WriteYamlBytes(md) + if err != nil { + return err + } + err = ioutil.WriteFile(projectFlags.OutputMetadata, b, 0o640) + if err != nil { + return err + } + } return cb(p) } diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 82e3eb165..48dffe726 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -206,7 +206,7 @@ func loadKluctlProjectFromArchive(args LoadKluctlProjectArgs, tmpDir string, j2 metdataPath = yaml.FixPathExt(filepath.Join(dir, "metadata.yml")) } - var metadata types2.ArchiveMetadata + var metadata types2.ProjectMetadata err := yaml.ReadYamlFile(metdataPath, &metadata) if err != nil { return nil, err @@ -223,7 +223,15 @@ func loadKluctlProjectFromArchive(args LoadKluctlProjectArgs, tmpDir string, j2 return p, nil } -func (c *KluctlProjectContext) CreateTGZArchive(archivePath string, metadataPath string) error { +func (c *KluctlProjectContext) GetMetadata() *types2.ProjectMetadata { + md := &types2.ProjectMetadata{ + InvolvedRepos: c.involvedRepos, + Targets: c.DynamicTargets, + } + return md +} + +func (c *KluctlProjectContext) CreateTGZArchive(archivePath string, embedMetadata bool) error { f, err := os.Create(archivePath) if err != nil { return err @@ -248,23 +256,15 @@ func (c *KluctlProjectContext) CreateTGZArchive(archivePath string, metadataPath return h, nil } - md := types2.ArchiveMetadata{ - InvolvedRepos: c.involvedRepos, - Targets: c.DynamicTargets, - } - mdStr, err := yaml.WriteYamlBytes(md) - if err != nil { - return err - } - - if metadataPath != "" { - err = ioutil.WriteFile(metadataPath, mdStr, 0o666) + if embedMetadata { + md := c.GetMetadata() + mdStr, err := yaml.WriteYamlBytes(md) if err != nil { return err } - } else { + err = tw.WriteHeader(&tar.Header{ - Name: "metadata.yml", + Name: "metadata.yaml", Size: int64(len(mdStr)), Mode: 0o666 | tar.TypeReg, }) diff --git a/pkg/types/metadata.go b/pkg/types/metadata.go index f17fcde5f..638f4bca9 100644 --- a/pkg/types/metadata.go +++ b/pkg/types/metadata.go @@ -5,7 +5,7 @@ type InvolvedRepo struct { Refs map[string]string `yaml:"refs"` } -type ArchiveMetadata struct { +type ProjectMetadata struct { InvolvedRepos map[string][]InvolvedRepo `yaml:"involvedRepos"` Targets []*DynamicTarget `yaml:"targets"` } From e29239fbabba3753e1adc0e693c216a7476fe5aa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 26 Apr 2022 16:19:35 +0200 Subject: [PATCH 0056/2268] fix: Use $TMPDIR/kluctl-workdir as tmp base dir for jinja2 renderer --- pkg/jinja2/python_src/jinja2_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/jinja2/python_src/jinja2_cache.py b/pkg/jinja2/python_src/jinja2_cache.py index 669c0a2dc..8d37ec2d4 100644 --- a/pkg/jinja2/python_src/jinja2_cache.py +++ b/pkg/jinja2/python_src/jinja2_cache.py @@ -11,7 +11,7 @@ from jinja2.bccache import Bucket def get_tmp_base_dir(): - dir = os.path.join(tempfile.gettempdir(), "kluctl") + dir = os.path.join(tempfile.gettempdir(), "kluctl-workdir") os.makedirs(dir, exist_ok=True) return dir From 7f31f3d156b3e8cb4aa824851350d30cf8cf0c07 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 26 Apr 2022 11:44:28 +0200 Subject: [PATCH 0057/2268] build: Put generate logic into go program instead of fully relying on go:generate This is needed to allow other applications depend on kluctl. These would still have to run the generate programs, but at least it's possible then. --- pkg/jinja2/embed/python_src.dummy | 0 pkg/jinja2/generate/main.go | 56 ++++++++ pkg/jinja2/jinja2.go | 3 - pkg/jinja2/python_src/dummy.go | 1 + pkg/jinja2/python_src/pip-wheel.sh | 14 -- pkg/jinja2/source.go | 8 +- pkg/jinja2/tools.go | 9 ++ pkg/python/embed.go | 4 +- pkg/python/embed/python-darwin-amd64.dummy | 0 pkg/python/embed/python-darwin-arm64.dummy | 0 pkg/python/embed/python-linux-amd64.dummy | 0 pkg/python/embed/python-linux-arm64.dummy | 0 pkg/python/embed/python-windows-amd64.dummy | 0 pkg/python/embed_darwin_amd64.go | 5 +- pkg/python/embed_darwin_arm64.go | 5 +- pkg/python/embed_linux_amd64.go | 5 +- pkg/python/embed_linux_arm64.go | 5 +- pkg/python/embed_windows_amd64.go | 5 +- pkg/python/{download => generate}/main.go | 70 ++++++++-- pkg/python/tools.go | 8 ++ pkg/utils/embed_util/extract.go | 42 ++++-- pkg/utils/embed_util/packer/main.go | 113 --------------- pkg/utils/embed_util/packer/packer.go | 146 ++++++++++++++++++++ 23 files changed, 324 insertions(+), 175 deletions(-) create mode 100644 pkg/jinja2/embed/python_src.dummy create mode 100644 pkg/jinja2/generate/main.go create mode 100644 pkg/jinja2/python_src/dummy.go delete mode 100755 pkg/jinja2/python_src/pip-wheel.sh create mode 100644 pkg/jinja2/tools.go create mode 100644 pkg/python/embed/python-darwin-amd64.dummy create mode 100644 pkg/python/embed/python-darwin-arm64.dummy create mode 100644 pkg/python/embed/python-linux-amd64.dummy create mode 100644 pkg/python/embed/python-linux-arm64.dummy create mode 100644 pkg/python/embed/python-windows-amd64.dummy rename pkg/python/{download => generate}/main.go (64%) create mode 100644 pkg/python/tools.go delete mode 100644 pkg/utils/embed_util/packer/main.go create mode 100644 pkg/utils/embed_util/packer/packer.go diff --git a/pkg/jinja2/embed/python_src.dummy b/pkg/jinja2/embed/python_src.dummy new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/jinja2/generate/main.go b/pkg/jinja2/generate/main.go new file mode 100644 index 000000000..a767884b3 --- /dev/null +++ b/pkg/jinja2/generate/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "github.com/kluctl/kluctl/v2/pkg/utils/embed_util/packer" + log "github.com/sirupsen/logrus" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func main() { + pipWheel() +} + +func pipWheel() { + _ = os.RemoveAll("python_src/wheel") + _ = os.MkdirAll("python_src/wheel", 0o700) + + cmd := exec.Command("pip3", "wheel", "-r", "../requirements.txt") + cmd.Dir = "python_src/wheel" + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + log.Panic(err) + } + + wheels, err := os.ReadDir("python_src/wheel") + if err != nil { + log.Panic(err) + } + for _, w := range wheels { + if !strings.HasSuffix(w.Name(), ".whl") { + continue + } + + cmd = exec.Command("unzip", w.Name()) + cmd.Dir = "python_src/wheel" + + err = cmd.Run() + if err != nil { + log.Panic(err) + } + + err = os.Remove(filepath.Join("python_src/wheel", w.Name())) + if err != nil { + log.Panic(err) + } + } + + err = packer.Pack("embed/python_src.tar.gz", "python_src", "*") + if err != nil { + log.Panic(err) + } +} diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go index 431619904..58f115727 100644 --- a/pkg/jinja2/jinja2.go +++ b/pkg/jinja2/jinja2.go @@ -13,9 +13,6 @@ import ( "sync" ) -//go:generate bash ./python_src/pip-wheel.sh -//go:generate go run ../utils/embed_util/packer python_src.tar.gz python_src * - var paralellism = 4 type Jinja2 struct { diff --git a/pkg/jinja2/python_src/dummy.go b/pkg/jinja2/python_src/dummy.go new file mode 100644 index 000000000..60d276519 --- /dev/null +++ b/pkg/jinja2/python_src/dummy.go @@ -0,0 +1 @@ +package python_src diff --git a/pkg/jinja2/python_src/pip-wheel.sh b/pkg/jinja2/python_src/pip-wheel.sh deleted file mode 100755 index 4dfe7d70a..000000000 --- a/pkg/jinja2/python_src/pip-wheel.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -DIR=$(cd $(dirname $0) && pwd) - -rm -rf $DIR/wheel -mkdir -p $DIR/wheel -cd $DIR/wheel - -pip3 wheel -r ../requirements.txt - -for f in *.whl; do - unzip $f - rm $f -done diff --git a/pkg/jinja2/source.go b/pkg/jinja2/source.go index 78321257f..f07623b74 100644 --- a/pkg/jinja2/source.go +++ b/pkg/jinja2/source.go @@ -8,7 +8,9 @@ import ( "path/filepath" ) -//go:embed python_src.tar.gz python_src.tar.gz.files +//go:generate go run ./generate + +//go:embed embed/python_src.* var pythonSrc embed.FS var pythonSrcExtracted string @@ -21,13 +23,13 @@ func init() { } func extractSource() (string, error) { - tgz, err := pythonSrc.Open("python_src.tar.gz") + tgz, err := pythonSrc.Open("embed/python_src.tar.gz") if err != nil { return "", err } defer tgz.Close() - fileList, err := pythonSrc.Open("python_src.tar.gz.files") + fileList, err := pythonSrc.Open("embed/python_src.tar.gz.files") if err != nil { return "", err } diff --git a/pkg/jinja2/tools.go b/pkg/jinja2/tools.go new file mode 100644 index 000000000..839cd9103 --- /dev/null +++ b/pkg/jinja2/tools.go @@ -0,0 +1,9 @@ +//go:build tools +// +build tools + +package tools + +import ( + _ "github.com/kluctl/kluctl/v2/pkg/jinja2/generate" + _ "github.com/kluctl/kluctl/v2/pkg/jinja2/python_src" +) diff --git a/pkg/python/embed.go b/pkg/python/embed.go index e2654a685..4a67eee0d 100644 --- a/pkg/python/embed.go +++ b/pkg/python/embed.go @@ -9,6 +9,8 @@ import ( "runtime" ) +//go:generate go run ./generate + var embeddedPythonPath string func init() { @@ -16,7 +18,7 @@ func init() { } func decompressPython() string { - tarName := fmt.Sprintf("python-%s-%s.tar.gz", runtime.GOOS, runtime.GOARCH) + tarName := fmt.Sprintf("embed/python-%s-%s.tar.gz", runtime.GOOS, runtime.GOARCH) tgz, err := pythonLib.Open(tarName) if err != nil { log.Panic(err) diff --git a/pkg/python/embed/python-darwin-amd64.dummy b/pkg/python/embed/python-darwin-amd64.dummy new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/python/embed/python-darwin-arm64.dummy b/pkg/python/embed/python-darwin-arm64.dummy new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/python/embed/python-linux-amd64.dummy b/pkg/python/embed/python-linux-amd64.dummy new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/python/embed/python-linux-arm64.dummy b/pkg/python/embed/python-linux-arm64.dummy new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/python/embed/python-windows-amd64.dummy b/pkg/python/embed/python-windows-amd64.dummy new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/python/embed_darwin_amd64.go b/pkg/python/embed_darwin_amd64.go index 4fb6ecf9a..110c5af57 100644 --- a/pkg/python/embed_darwin_amd64.go +++ b/pkg/python/embed_darwin_amd64.go @@ -2,8 +2,5 @@ package python import "embed" -//go:generate go run ./download darwin amd64 ../../download-python/darwin/amd64 -//go:generate go run ../utils/embed_util/packer python-darwin-amd64.tar.gz ../../download-python/darwin/amd64/python/install bin lib/*.dylib lib/python3.* - -//go:embed python-darwin-amd64.tar.gz python-darwin-amd64.tar.gz.files +//go:embed embed/python-darwin-amd64.* var pythonLib embed.FS diff --git a/pkg/python/embed_darwin_arm64.go b/pkg/python/embed_darwin_arm64.go index 18aa21233..f14b56a5f 100644 --- a/pkg/python/embed_darwin_arm64.go +++ b/pkg/python/embed_darwin_arm64.go @@ -2,8 +2,5 @@ package python import "embed" -//go:generate go run ./download darwin arm64 ../../download-python/darwin/arm64 -//go:generate go run ../utils/embed_util/packer python-darwin-arm64.tar.gz ../../download-python/darwin/arm64/python/install bin lib/*.dylib lib/python3.* - -//go:embed python-darwin-arm64.tar.gz python-darwin-arm64.tar.gz.files +//go:embed embed/python-darwin-arm64.* var pythonLib embed.FS diff --git a/pkg/python/embed_linux_amd64.go b/pkg/python/embed_linux_amd64.go index 5961a767d..99838aee9 100644 --- a/pkg/python/embed_linux_amd64.go +++ b/pkg/python/embed_linux_amd64.go @@ -2,8 +2,5 @@ package python import "embed" -//go:generate go run ./download linux amd64 ../../download-python/linux/amd64 -//go:generate go run ../utils/embed_util/packer python-linux-amd64.tar.gz ../../download-python/linux/amd64/python/install bin lib/*.so* lib/python3.* - -//go:embed python-linux-amd64.tar.gz python-linux-amd64.tar.gz.files +//go:embed embed/python-linux-amd64.* var pythonLib embed.FS diff --git a/pkg/python/embed_linux_arm64.go b/pkg/python/embed_linux_arm64.go index 40be4e8b0..158c5087e 100644 --- a/pkg/python/embed_linux_arm64.go +++ b/pkg/python/embed_linux_arm64.go @@ -2,8 +2,5 @@ package python import "embed" -//go:generate go run ./download linux arm64 ../../download-python/linux/arm64 -//go:generate go run ../utils/embed_util/packer python-linux-arm64.tar.gz ../../download-python/linux/arm64/python/install bin lib/*.so* lib/python3.* - -//go:embed python-linux-arm64.tar.gz python-linux-arm64.tar.gz.files +//go:embed embed/python-linux-arm64.* var pythonLib embed.FS diff --git a/pkg/python/embed_windows_amd64.go b/pkg/python/embed_windows_amd64.go index dfa2a12a3..65e5e2c9c 100644 --- a/pkg/python/embed_windows_amd64.go +++ b/pkg/python/embed_windows_amd64.go @@ -2,8 +2,5 @@ package python import "embed" -//go:generate go run ./download windows amd64 ../../download-python/windows/amd64 -//go:generate go run ../utils/embed_util/packer python-windows-amd64.tar.gz ../../download-python/windows/amd64/python/install Lib DLLs *.dll *.exe - -//go:embed python-windows-amd64.tar.gz python-windows-amd64.tar.gz.files +//go:embed embed/python-windows-amd64.* var pythonLib embed.FS diff --git a/pkg/python/download/main.go b/pkg/python/generate/main.go similarity index 64% rename from pkg/python/download/main.go rename to pkg/python/generate/main.go index b61e93840..6c2ef6085 100644 --- a/pkg/python/download/main.go +++ b/pkg/python/generate/main.go @@ -4,11 +4,13 @@ import ( "fmt" "github.com/klauspost/compress/zstd" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/embed_util/packer" log "github.com/sirupsen/logrus" "io/ioutil" "net/http" "os" "path/filepath" + "sync" ) const ( @@ -18,14 +20,14 @@ const ( ) var pythonDists = map[string]string{ - "linux": "unknown-linux-gnu-lto-full", - "darwin": "apple-darwin-lto-full", + "linux": "unknown-linux-gnu-lto-full", + "darwin": "apple-darwin-lto-full", "windows": "pc-windows-msvc-shared-pgo-full", } var archMapping = map[string]string{ "amd64": "x86_64", - "386": "i686", + "386": "i686", "arm64": "aarch64", } @@ -45,11 +47,49 @@ var removeLibs = []string{ "unittest", } +var downloadLock sync.Mutex + func main() { - osName := os.Args[1] - arch := os.Args[2] - extractPath := os.Args[3] + var wg sync.WaitGroup + + nixPatterns := []string{ + "bin", + "lib/*.so*", + "lib/*.dylib", + "lib/python3.*", + } + winPatterns := []string{ + "Lib", + "DLLs", + "*.dll", + "*.exe", + } + type job struct { + os string + arch string + packSubDir string + out string + patterns []string + } + jobs := []job{ + {"linux", "amd64", "python/install", "embed/python-linux-amd64.tar.gz", nixPatterns}, + {"linux", "arm64", "python/install", "embed/python-linux-arm64.tar.gz", nixPatterns}, + {"darwin", "amd64", "python/install", "embed/python-darwin-amd64.tar.gz", nixPatterns}, + {"darwin", "arm64", "python/install", "embed/python-darwin-arm64.tar.gz", nixPatterns}, + {"windows", "amd64", "python/install", "embed/python-windows-amd64.tar.gz", winPatterns}, + } + for _, j := range jobs { + j := j + wg.Add(1) + go func() { + downloadAndPack(j.os, j.arch, j.packSubDir, j.out, j.patterns) + wg.Done() + }() + } + wg.Wait() +} +func downloadAndPack(osName string, arch string, packSubdir string, out string, patterns []string) { dist, ok := pythonDists[osName] if !ok { log.Panicf("no dist for %s", osName) @@ -59,8 +99,10 @@ func main() { archiveBytes, _ := ioutil.ReadFile(downloadPath) hash := utils.Sha256Bytes(archiveBytes) + extractPath := downloadPath + ".extracted" if utils.Exists(filepath.Join(extractPath, hash)) { - log.Infof("skipping extract") + log.Infof("skipping extract of %s", extractPath) + pack(out, filepath.Join(extractPath, packSubdir), patterns) return } @@ -73,9 +115,21 @@ func main() { } _ = utils.Touch(filepath.Join(extractPath, hash)) + + pack(out, filepath.Join(extractPath, packSubdir), patterns) +} + +func pack(out string, dir string, patterns []string) { + err := packer.Pack(out, dir, patterns...) + if err != nil { + log.Panic(err) + } } func download(osName, arch, dist string) string { + downloadLock.Lock() + defer downloadLock.Unlock() + pythonArch, ok := archMapping[arch] if !ok { log.Errorf("arch %s not supported", arch) @@ -138,4 +192,4 @@ func extract(archivePath string, targetPath string) string { } return targetPath -} \ No newline at end of file +} diff --git a/pkg/python/tools.go b/pkg/python/tools.go new file mode 100644 index 000000000..48d6d8a70 --- /dev/null +++ b/pkg/python/tools.go @@ -0,0 +1,8 @@ +//go:build tools +// +build tools + +package tools + +import ( + _ "github.com/kluctl/kluctl/v2/pkg/python/generate" +) diff --git a/pkg/utils/embed_util/extract.go b/pkg/utils/embed_util/extract.go index dc38f75f4..020f28596 100644 --- a/pkg/utils/embed_util/extract.go +++ b/pkg/utils/embed_util/extract.go @@ -60,9 +60,10 @@ func ExtractTarToTmp(r io.Reader, fileListR io.Reader, targetPrefix string) (str } func checkExtractNeeded(targetPath string, fileListStr string) (bool, string, error) { - fileList := strings.Split(fileListStr, "\n") - expectedHash := fileList[0] - fileList = fileList[1:] + expectedHash, tarFilesMap, err := ReadFileList(fileListStr) + if err != nil { + return false, "", err + } if !utils.Exists(targetPath) { return true, expectedHash, nil @@ -77,6 +78,24 @@ func checkExtractNeeded(targetPath string, fileListStr string) (bool, string, er return true, expectedHash, nil } + existingFiles, err := BuildFileList(targetPath) + if err != nil { + return false, "", err + } + + for fname, size := range tarFilesMap { + if s, ok := existingFiles[fname]; !ok || s != size { + return true, expectedHash, nil + } + } + return false, expectedHash, nil +} + +func ReadFileList(fileListStr string) (string, map[string]int64, error) { + fileList := strings.Split(fileListStr, "\n") + expectedHash := fileList[0] + fileList = fileList[1:] + tarFilesMap := make(map[string]int64) for _, l := range fileList { s := strings.SplitN(l, ":", 2) @@ -84,13 +103,16 @@ func checkExtractNeeded(targetPath string, fileListStr string) (bool, string, er sh := strings.SplitN(strings.TrimSpace(s[1]), " ", 2) size, err := strconv.ParseInt(strings.TrimSpace(sh[0]), 10, 64) if err != nil { - return false, expectedHash, err + return expectedHash, tarFilesMap, err } tarFilesMap[fname] = size } + return "", tarFilesMap, nil +} +func BuildFileList(targetPath string) (map[string]int64, error) { existingFiles := make(map[string]int64) - err = filepath.Walk(targetPath, func(path string, info fs.FileInfo, err error) error { + err := filepath.Walk(targetPath, func(path string, info fs.FileInfo, err error) error { if !info.Mode().IsRegular() && info.Mode().Type() != fs.ModeSymlink && info.Mode().Type() != fs.ModeDir { return nil } @@ -107,13 +129,7 @@ func checkExtractNeeded(targetPath string, fileListStr string) (bool, string, er return nil }) if err != nil { - return false, "", err + return nil, err } - - for fname, size := range tarFilesMap { - if s, ok := existingFiles[fname]; !ok || s != size { - return true, expectedHash, nil - } - } - return false, expectedHash, nil + return existingFiles, nil } diff --git a/pkg/utils/embed_util/packer/main.go b/pkg/utils/embed_util/packer/main.go deleted file mode 100644 index 39cb9e2cf..000000000 --- a/pkg/utils/embed_util/packer/main.go +++ /dev/null @@ -1,113 +0,0 @@ -package main - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "crypto/sha256" - "encoding/hex" - "fmt" - "github.com/gobwas/glob" - "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" - "io/fs" - "io/ioutil" - "os" - "path/filepath" - "sort" - "strings" -) - -func main() { - out := os.Args[1] - dir := os.Args[2] - patterns := os.Args[3:] - - log.Infof("writing tar %s", out) - fileList, tgz := writeTar(dir, patterns) - - hash := sha256.Sum256(tgz) - err := ioutil.WriteFile(out, tgz, 0o600) - if err != nil { - log.Panic(err) - } - - fileListStr := strings.Join(fileList, "\n") - fileListStr = hex.EncodeToString(hash[:]) + "\n" + fileListStr - err = ioutil.WriteFile(out+".files", []byte(fileListStr), 0o600) - if err != nil { - log.Panic(err) - } -} - -func writeTar(dir string, patterns []string) ([]string, []byte) { - b := bytes.NewBuffer(nil) - gz := gzip.NewWriter(b) - t := tar.NewWriter(gz) - - var globs []glob.Glob - for _, p := range patterns { - globs = append(globs, glob.MustCompile(p, '/')) - } - - var rootNames []string - err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { - rel, err := filepath.Rel(dir, path) - if err != nil { - return err - } - match := false - for _, p := range globs { - if p.Match(rel) { - match = true - break - } - } - if match { - rootNames = append(rootNames, rel) - } - return nil - }) - sort.Strings(rootNames) - - excludes := []glob.Glob{ - glob.MustCompile("__pycache__"), - glob.MustCompile("**/__pycache__"), - glob.MustCompile("**.a"), - glob.MustCompile("**.pdb"), - } - - var fileList []string - - for _, d := range rootNames { - err := utils.AddToTar(t, filepath.Join(dir, d), d, func(h *tar.Header, size int64) (*tar.Header, error) { - for _, e := range excludes { - if e.Match(h.Name) { - return nil, nil - } - } - - hashStr, err := utils.HashTarEntry(dir, h.Name) - if err != nil { - return nil, err - } - - fileList = append(fileList, fmt.Sprintf("%s: %d %s", h.Name, size, hashStr)) - return h, nil - }) - if err != nil { - log.Panic(err) - } - } - - err = t.Close() - if err != nil { - log.Panic(err) - } - err = gz.Close() - if err != nil { - log.Panic(err) - } - - return fileList, b.Bytes() -} diff --git a/pkg/utils/embed_util/packer/packer.go b/pkg/utils/embed_util/packer/packer.go new file mode 100644 index 000000000..38846957a --- /dev/null +++ b/pkg/utils/embed_util/packer/packer.go @@ -0,0 +1,146 @@ +package packer + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "crypto/sha256" + "encoding/hex" + "fmt" + "github.com/gobwas/glob" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/embed_util" + log "github.com/sirupsen/logrus" + "io/fs" + "io/ioutil" + "path/filepath" + "reflect" + "strings" +) + +func Pack(out string, dir string, patterns ...string) error { + fileList, err := findFiles(dir, patterns) + if err != nil { + return err + } + + if utils.Exists(out) && utils.Exists(out+".files") { + existingFileListStr, err := ioutil.ReadFile(out + ".files") + if err == nil { + _, existingFileList, err := embed_util.ReadFileList(string(existingFileListStr)) + if err == nil { + if reflect.DeepEqual(existingFileList, fileList) { + log.Infof("Skipping packing of %s", out) + return nil + } + } + } + } + + log.Infof("writing tar %s with %d files", out, len(fileList)) + tgz, err := writeTar(dir, fileList) + if err != nil { + return err + } + + hash := sha256.Sum256(tgz) + err = ioutil.WriteFile(out, tgz, 0o600) + if err != nil { + return err + } + + var fileList2 []string + for f, l := range fileList { + fileList2 = append(fileList2, fmt.Sprintf("%s: %d", f, l)) + } + + fileListStr := strings.Join(fileList2, "\n") + fileListStr = hex.EncodeToString(hash[:]) + "\n" + fileListStr + err = ioutil.WriteFile(out+".files", []byte(fileListStr), 0o600) + return err +} + +func findFiles(dir string, patterns []string) (map[string]int64, error) { + var globs []glob.Glob + for _, p := range patterns { + globs = append(globs, glob.MustCompile(p, '/')) + } + + var rootNames []string + err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + rel, err := filepath.Rel(dir, path) + if err != nil { + return err + } + match := false + for _, p := range globs { + if p.Match(rel) { + match = true + break + } + } + if match { + rootNames = append(rootNames, rel) + } + return nil + }) + if err != nil { + return nil, err + } + + excludes := []glob.Glob{ + glob.MustCompile("__pycache__"), + glob.MustCompile("**/__pycache__"), + glob.MustCompile("**.a"), + glob.MustCompile("**.pdb"), + } + + fileList := make(map[string]int64) + for _, d := range rootNames { + err = filepath.Walk(filepath.Join(dir, d), func(path string, info fs.FileInfo, err error) error { + if !info.Mode().IsRegular() && info.Mode().Type() != fs.ModeSymlink { + return nil + } + + relPath, err := filepath.Rel(dir, path) + if err != nil { + return err + } + for _, e := range excludes { + if e.Match(relPath) { + return nil + } + } + fileList[relPath] = info.Size() + return nil + }) + if err != nil { + return nil, err + } + } + return fileList, err +} + +func writeTar(dir string, fileList map[string]int64) ([]byte, error) { + b := bytes.NewBuffer(nil) + gz := gzip.NewWriter(b) + t := tar.NewWriter(gz) + + for f, _ := range fileList { + err := utils.AddToTar(t, filepath.Join(dir, f), f, nil) + if err != nil { + return nil, err + } + } + + err := t.Close() + if err != nil { + return nil, err + } + err = gz.Close() + if err != nil { + return nil, err + } + + return b.Bytes(), nil +} From ce1d53acdd3b532ac9775219fd4c538abf170d71 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 26 Apr 2022 16:24:39 +0200 Subject: [PATCH 0058/2268] fix: Check for version 0.0.0 in checkNewVersion --- cmd/kluctl/commands/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 4027e612c..9cfa4bf32 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -81,7 +81,7 @@ func (c *cli) checkNewVersion() { if c.NoUpdateCheck { return } - if version.GetVersion() == "(devel)" { + if version.GetVersion() == "0.0.0" { return } From ed6af42e74b6f511d5cb513982020e857a14c4e7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 26 Apr 2022 16:34:48 +0200 Subject: [PATCH 0059/2268] build: No need to run go generate with other tags --- .github/workflows/tests.yml | 1 - .goreleaser.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e0d2548be..f1e9db9ee 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,7 +34,6 @@ jobs: - name: Go Generate run: | go generate ./... - go generate -tags linux,darwin,windows,amd64 ./pkg/python - name: Run unit tests run: | go test ./cmd/... ./pkg/... -v diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 7ec26ccc2..8967d7158 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -2,7 +2,6 @@ before: hooks: - go mod tidy - go generate ./... - - go generate -tags linux,darwin,windows,amd64,arm64 ./pkg/python builds: - <<: &build_defaults binary: kluctl From f39ad505f91f1da4f282c10cd23c31e204a55aaa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 26 Apr 2022 18:08:35 +0200 Subject: [PATCH 0060/2268] build: Use utils.GetTmpBaseDir() for generation/downloads --- pkg/python/generate/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/python/generate/main.go b/pkg/python/generate/main.go index 6c2ef6085..f3ffc0816 100644 --- a/pkg/python/generate/main.go +++ b/pkg/python/generate/main.go @@ -136,7 +136,7 @@ func download(osName, arch, dist string) string { os.Exit(1) } fname := fmt.Sprintf("cpython-%s+%s-%s-%s.tar.zst", pythonVersionFull, pythonStandaloneVersion, pythonArch, dist) - downloadPath := filepath.Join(os.TempDir(), fname) + downloadPath := filepath.Join(utils.GetTmpBaseDir(), fname) downloadUrl := fmt.Sprintf("https://github.com/indygreg/python-build-standalone/releases/download/%s/%s", pythonStandaloneVersion, fname) if _, err := os.Stat(downloadPath); err == nil { From 2377430e02941cf565efcf3bed0fc0ced8149719 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 26 Apr 2022 18:08:59 +0200 Subject: [PATCH 0061/2268] fix: Fix embed_util false positive extracts --- pkg/utils/embed_util/extract.go | 2 +- pkg/utils/embed_util/packer/packer.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/utils/embed_util/extract.go b/pkg/utils/embed_util/extract.go index 020f28596..16ed318ba 100644 --- a/pkg/utils/embed_util/extract.go +++ b/pkg/utils/embed_util/extract.go @@ -107,7 +107,7 @@ func ReadFileList(fileListStr string) (string, map[string]int64, error) { } tarFilesMap[fname] = size } - return "", tarFilesMap, nil + return expectedHash, tarFilesMap, nil } func BuildFileList(targetPath string) (map[string]int64, error) { diff --git a/pkg/utils/embed_util/packer/packer.go b/pkg/utils/embed_util/packer/packer.go index 38846957a..9714cedda 100644 --- a/pkg/utils/embed_util/packer/packer.go +++ b/pkg/utils/embed_util/packer/packer.go @@ -93,6 +93,7 @@ func findFiles(dir string, patterns []string) (map[string]int64, error) { glob.MustCompile("**/__pycache__"), glob.MustCompile("**.a"), glob.MustCompile("**.pdb"), + glob.MustCompile("**.pyc"), } fileList := make(map[string]int64) From 6933396b9e84b37a33927af82be919bb336f550f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 10:57:59 +0200 Subject: [PATCH 0062/2268] build: Upgrade to go 1.18.1 --- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 2 +- go.mod | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b91fdad0a..11588e3a3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17.9 + go-version: 1.18.1 - name: Set up Python uses: actions/setup-python@v2 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f1e9db9ee..932808d7f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-go@v2 with: - go-version: '1.17.9' + go-version: '1.18.1' - name: Set up Python uses: actions/setup-python@v2 with: diff --git a/go.mod b/go.mod index 56cc82387..0ff894db8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/kluctl/kluctl/v2 -go 1.17 +go 1.18 require ( github.com/alecthomas/kong v0.5.0 From 528baa96ae4552ef02670e0302fce61030898ae4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 10:58:31 +0200 Subject: [PATCH 0063/2268] build: Upgrade vendor packages --- go.mod | 26 +++---- go.sum | 225 +++++++++++++-------------------------------------------- 2 files changed, 62 insertions(+), 189 deletions(-) diff --git a/go.mod b/go.mod index 0ff894db8..73da526f4 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.18 require ( github.com/alecthomas/kong v0.5.0 - github.com/aws/aws-sdk-go v1.43.39 - github.com/bitnami-labs/sealed-secrets v0.17.4 + github.com/aws/aws-sdk-go v1.44.1 + github.com/bitnami-labs/sealed-secrets v0.17.5 github.com/docker/distribution v2.8.1+incompatible github.com/evanphx/json-patch v5.6.0+incompatible github.com/gammazero/workerpool v1.1.2 @@ -20,7 +20,7 @@ require ( github.com/hexops/gotextdiff v1.0.3 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/klauspost/compress v1.13.6 + github.com/klauspost/compress v1.15.2 github.com/mattn/go-isatty v0.0.14 github.com/mitchellh/go-ps v1.0.0 github.com/ohler55/ojg v1.14.0 @@ -33,12 +33,12 @@ require ( github.com/xanzy/ssh-agent v0.3.1 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad + golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b - k8s.io/api v0.24.0-beta.0 - k8s.io/apimachinery v0.24.0-beta.0 - k8s.io/client-go v0.24.0-beta.0 + k8s.io/api v0.25.0-alpha.0 + k8s.io/apimachinery v0.25.0-alpha.0 + k8s.io/client-go v0.25.0-alpha.0 sigs.k8s.io/kind v0.12.0 sigs.k8s.io/kustomize/api v0.11.4 sigs.k8s.io/kustomize/kyaml v0.13.6 @@ -46,9 +46,9 @@ require ( ) require ( - cloud.google.com/go/compute v1.5.0 // indirect + cloud.google.com/go/compute v1.6.1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.25 // indirect + github.com/Azure/go-autorest/autorest v0.11.27 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect @@ -65,7 +65,7 @@ require ( github.com/docker/docker v20.10.14+incompatible // indirect github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/emicklei/go-restful v2.15.0+incompatible // indirect - github.com/emirpasic/gods v1.17.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/gammazero/deque v0.1.1 // indirect @@ -81,7 +81,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic v0.6.8 // indirect + github.com/google/gnostic v0.6.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect @@ -102,7 +102,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5 // indirect - github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect @@ -112,7 +112,7 @@ require ( github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect - golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect diff --git a/go.sum b/go.sum index 2acb29ed5..fe612bf11 100644 --- a/go.sum +++ b/go.sum @@ -28,7 +28,6 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= 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= @@ -38,8 +37,10 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1 h1:2sMmt8prCn7DPaG4Pmh0N3Inmc8cT8ae5k1M6VJ9Wqc= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -61,8 +62,8 @@ github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.25 h1:yp+V8DGur2aIUE87ebP8twPLz6k68jtJTlg61mEoByA= -github.com/Azure/go-autorest/autorest v0.11.25/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= +github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= +github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= @@ -85,7 +86,6 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= @@ -116,16 +116,12 @@ github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5/go.mod h1:z4/ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/kong v0.5.0 h1:u8Kdw+eeml93qtMZ04iei0CFYve/WPcA5IFh+9wSskE= github.com/alecthomas/kong v0.5.0/go.mod h1:uzxf/HUh0tj43x1AyJROl3JT7SgsZ5m+icOv1csRhc0= github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48= @@ -134,15 +130,12 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy 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/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -151,24 +144,18 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.43.39 h1:5W8pton/8OuS5hpbAkzfr7e+meAAFkK7LsUehB39L3I= -github.com/aws/aws-sdk-go v1.43.39/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aws/aws-sdk-go v1.44.1 h1:w34ZmPT6K4NTd7Yap1P7SLXPTii0ABmBz3KEh4KJdKc= +github.com/aws/aws-sdk-go v1.44.1/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bitnami-labs/flagenv v0.0.0-20190607135054-a87af7a1d6fc/go.mod h1:OeW4NPgFPO7+t8q1Vn2Yv+rkO+4kEQzlDskwm7C7PXs= -github.com/bitnami-labs/pflagenv v0.0.0-20190702160147-b4d9f048d98f/go.mod h1:Lw3ejf6HTt4DqBIAXlkOIvFjnpj8Zq+zD/UtH29ILFA= -github.com/bitnami-labs/sealed-secrets v0.17.4 h1:DFZCyR8ntNKINRaneKPBrnEXnejWz48fE8htx83w0yQ= -github.com/bitnami-labs/sealed-secrets v0.17.4/go.mod h1:xL3ifFPr9lIS6QoCfqj8shGgLriGjQke5o/X4NFUZfE= +github.com/bitnami-labs/sealed-secrets v0.17.5 h1:v5ENZRSrgog3GnFr8fWfVtrUTPlZlNlbsjaro9mn6YY= +github.com/bitnami-labs/sealed-secrets v0.17.5/go.mod h1:ZgGUqKixr/SRpsG8LVXnuneyZG7DOX/+eCRvXi8SVjo= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -180,8 +167,6 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -200,7 +185,6 @@ github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/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= @@ -213,7 +197,6 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= @@ -358,19 +341,14 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.15.0+incompatible h1:8KpYO/Xl/ZudZs5RNOEhWMBY4hmzlZhhRd9cu+jrZP4= github.com/emicklei/go-restful v2.15.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/emirpasic/gods v1.17.0 h1:qOswSAaPBdCkBIAXmvwe0V9mv4UtNQHN3zxRHmw3sKQ= -github.com/emirpasic/gods v1.17.0/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 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= @@ -398,8 +376,6 @@ github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -435,10 +411,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 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/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-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= @@ -453,7 +427,6 @@ github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL9 github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= 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/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -471,7 +444,6 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig= github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= @@ -484,11 +456,9 @@ github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -533,16 +503,14 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 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/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/gnostic v0.6.8 h1:bT56GPYBWh1tvBuBEd94qcS3+60b+y0HQur0ITkGuCk= -github.com/google/gnostic v0.6.8/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +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= @@ -592,15 +560,13 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -615,10 +581,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -640,7 +604,6 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -661,7 +624,6 @@ github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpT github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= 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= @@ -673,7 +635,6 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= @@ -683,7 +644,6 @@ github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -691,10 +651,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -704,7 +662,6 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -714,8 +671,9 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.2 h1:3WH+AG7s2+T8o3nrM/8u2rdqUEcQhmga7smjrT41nAw= +github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -734,10 +692,7 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -790,7 +745,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/mkmik/multierror v0.3.0/go.mod h1:wjBYXRpDhh+8mIp+iLBOq0kZ3Y4ICTncojwvP8LUYLQ= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= @@ -813,44 +767,31 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m 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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/ohler55/ojg v1.14.0 h1:DyHomsCwofNswmKj7BLMdx51xnKbXxgIo1rVWCaBcNk= github.com/ohler55/ojg v1.14.0/go.mod h1:3+GH+0PggMKocQtbZCrFifal3yRpHiBT4QUkxFJI6e8= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -877,35 +818,22 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +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/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/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-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -914,34 +842,25 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -954,8 +873,6 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -969,7 +886,6 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= @@ -990,7 +906,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -1009,7 +924,6 @@ github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v0.0.0-20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -1020,9 +934,6 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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= @@ -1042,7 +953,6 @@ github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/throttled/throttled v2.2.2+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= @@ -1085,7 +995,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= @@ -1093,7 +1002,6 @@ github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -1102,8 +1010,6 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3 go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1117,14 +1023,10 @@ go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd h1:Uo/x0Ir5vQJ+683GXB9Ug+4fcj go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1152,9 +1054,7 @@ golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 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= @@ -1199,7 +1099,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1248,11 +1147,13 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 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= @@ -1271,6 +1172,8 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/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.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1292,7 +1195,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-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= @@ -1325,7 +1227,6 @@ golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/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= @@ -1347,7 +1248,6 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1362,7 +1262,6 @@ golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7w 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-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/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= @@ -1384,10 +1283,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1398,10 +1295,12 @@ golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1421,16 +1320,13 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/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.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/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= @@ -1448,8 +1344,6 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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= @@ -1458,7 +1352,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn 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-20200103221440-774c71fcf114/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= @@ -1491,7 +1384,6 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= 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= @@ -1499,11 +1391,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 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= @@ -1540,8 +1428,10 @@ google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFd google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 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= @@ -1556,7 +1446,6 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn 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-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 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= @@ -1624,14 +1513,18 @@ google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= @@ -1660,6 +1553,7 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1688,7 +1582,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -1722,7 +1615,6 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= 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-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1733,25 +1625,21 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.23.4/go.mod h1:i77F4JfyNNrhOjZF7OwwNJS5Y1S9dpwvb9iYRYRczfI= -k8s.io/api v0.24.0-beta.0 h1:7knNqNYI1Az5hWcebdyUff4ETyCZkvmUT1N2hi/qS/Y= -k8s.io/api v0.24.0-beta.0/go.mod h1:D7w5dDA57yCeRJnl0vPuRj6KBAwWYxea4Dwo5kgJGIY= +k8s.io/api v0.25.0-alpha.0 h1:BiYeMLWoLcGGWE46gdnlwluFa23+Hr3I2Qp8U6c2wYY= +k8s.io/api v0.25.0-alpha.0/go.mod h1:sOibYBePcsE/DBjbbi+Z+FCG9lFPR7xuKSR2r6RTCNs= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.23.4/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= -k8s.io/apimachinery v0.24.0-beta.0 h1:69KiS/m3i2oi3FaCVX6whePxOelsJkhIfO0J5fGDYv8= -k8s.io/apimachinery v0.24.0-beta.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.25.0-alpha.0 h1:gAzcXIp+FkB3w8+m34na2qxSScwQWKtryRU8JfkS/NU= +k8s.io/apimachinery v0.25.0-alpha.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.23.4/go.mod h1:PKnIL4pqLuvYUK1WU7RLTMYKPiIh7MYShLshtRY9cj0= -k8s.io/client-go v0.24.0-beta.0 h1:ISWwVXNtOr2f1O5afJGi66vxAzC6Gb/3+VWlz4WseFc= -k8s.io/client-go v0.24.0-beta.0/go.mod h1:D4rgRqnNPdFCFMMrcCqCOAouzIwJkPuKXr3zWThEExM= -k8s.io/code-generator v0.23.4/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= +k8s.io/client-go v0.25.0-alpha.0 h1:IwIWODi6Uz7QlOU8J2tv8APdDtytoowveKF9IoKpHa4= +k8s.io/client-go v0.25.0-alpha.0/go.mod h1:V7vCXDCdD8Goobi4oQhsSNXtlWfyBJi+LHFaiYWpR5s= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= @@ -1761,38 +1649,26 @@ k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6 h1:nBQrWPlrNIiw0BsX6a6MKr1itkm0ZS0Nl97kNLitFfI= k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= 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.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/kind v0.12.0 h1:LFynXwQkH1MrWI8pM1FQty0oUwEKjU5EkMaVZaPld8E= @@ -1801,8 +1677,6 @@ sigs.k8s.io/kustomize/api v0.11.4 h1:/0Mr3kfBBNcNPOW5Qwk/3eb8zkswCwnqQxxKtmrTkRo sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= sigs.k8s.io/kustomize/kyaml v0.13.6 h1:eF+wsn4J7GOAXlvajv6OknSunxpcOBQQqsnPxObtkGs= sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= @@ -1811,4 +1685,3 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 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= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From f2fa88ff1fc50e1931e0329a49c5f724fe911f68 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 10:59:11 +0200 Subject: [PATCH 0064/2268] refactor: Allow to pass GitAuthProviders to NewKluctlProjectContext --- cmd/kluctl/commands/utils.go | 2 ++ pkg/kluctl_project/git.go | 4 ++-- pkg/kluctl_project/project.go | 18 +++++++++--------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 829ab5bbb..b8181e29c 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" @@ -44,6 +45,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl LocalSealedSecrets: projectFlags.LocalSealedSecrets, FromArchive: projectFlags.FromArchive, FromArchiveMetadata: projectFlags.FromArchiveMetadata, + GitAuthProviders: auth.NewDefaultAuthProviders(), } p, err := kluctl_project.LoadKluctlProject(loadArgs, tmpDir, j2) diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 4fd28013e..93abdfc9b 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -31,7 +31,7 @@ func (c *KluctlProjectContext) updateGitCaches() error { doUpdateRepo := func(repo *git.MirroredGitRepo) error { return repo.WithLock(func() error { if !repo.HasUpdated() { - return repo.Update(c.gitAuthProviders) + return repo.Update(c.loadArgs.GitAuthProviders) } return nil }) @@ -156,7 +156,7 @@ func (c *KluctlProjectContext) cloneGitProject(gitProject types2.ExternalProject err = mr.MaybeWithLock(doLock, func() error { if !mr.HasUpdated() { - err = mr.Update(c.gitAuthProviders) + err = mr.Update(c.loadArgs.GitAuthProviders) if err != nil { return err } diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 552240a22..c44c64555 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -19,6 +19,8 @@ type LoadKluctlProjectArgs struct { LocalSealedSecrets string FromArchive string FromArchiveMetadata string + + GitAuthProviders *auth2.GitAuthProviders } type KluctlProjectContext struct { @@ -32,9 +34,8 @@ type KluctlProjectContext struct { ClustersDir string SealedSecretsDir string - gitAuthProviders *auth2.GitAuthProviders - involvedRepos map[string][]types.InvolvedRepo - DynamicTargets []*types.DynamicTarget + involvedRepos map[string][]types.InvolvedRepo + DynamicTargets []*types.DynamicTarget mirroredRepos map[string]*git.MirroredGitRepo @@ -43,12 +44,11 @@ type KluctlProjectContext struct { func NewKluctlProjectContext(loadArgs LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) *KluctlProjectContext { o := &KluctlProjectContext{ - loadArgs: loadArgs, - TmpDir: tmpDir, - gitAuthProviders: auth2.NewDefaultAuthProviders(), - involvedRepos: make(map[string][]types.InvolvedRepo), - mirroredRepos: make(map[string]*git.MirroredGitRepo), - J2: j2, + loadArgs: loadArgs, + TmpDir: tmpDir, + involvedRepos: make(map[string][]types.InvolvedRepo), + mirroredRepos: make(map[string]*git.MirroredGitRepo), + J2: j2, } return o } From 4af3416e0484b6f9586c645b03757fc89fd4bf9b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 11:24:02 +0200 Subject: [PATCH 0065/2268] refactor: Introduce ListAuthProvider and use it in GitEnvAuthProvider --- pkg/git/auth/env_auth_provider.go | 47 ++++++----------- pkg/git/auth/list_auth_provider.go | 81 ++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 31 deletions(-) create mode 100644 pkg/git/auth/list_auth_provider.go diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index 2755ab710..908d525f7 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -2,52 +2,37 @@ package auth import ( "github.com/go-git/go-git/v5/plumbing/transport" - "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/go-git/go-git/v5/plumbing/transport/ssh" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" - "strings" + "io/ioutil" ) type GitEnvAuthProvider struct { } func (a *GitEnvAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod { - for _, m := range utils.ParseEnvConfigSets("KLUCTL_GIT") { - host, _ := m["HOST"] - pathPrefix, _ := m["PATH_PREFIX"] - username, _ := m["USERNAME"] - password, _ := m["PASSWORD"] - ssh_key, _ := m["SSH_KEY"] + var la ListAuthProvider - if host != gitUrl.Hostname() { - continue - } - if !strings.HasPrefix(gitUrl.Path, pathPrefix) { - continue - } - if username == "" { - continue - } - if gitUrl.User != nil && gitUrl.User.Username() != "" && gitUrl.User.Username() != username { - continue + for _, m := range utils.ParseEnvConfigSets("KLUCTL_GIT") { + e := AuthEntry{ + Host: m["HOST"], + PathPrefix: m["PATH_PREFIX"], + Username: m["USERNAME"], + Password: m["PASSWORD"], } - if !gitUrl.IsSsh() && password != "" { - return &http.BasicAuth{ - Username: username, - Password: password, - } - } else if gitUrl.IsSsh() && ssh_key != "" { - a, err := ssh.NewPublicKeysFromFile(username, ssh_key, "") + ssh_key_path, _ := m["SSH_KEY"] + if ssh_key_path != "" { + ssh_key_path = utils.ExpandPath(ssh_key_path) + b, err := ioutil.ReadFile(ssh_key_path) if err != nil { - log.Debugf("Failed to parse private key %s: %v", ssh_key, err) + log.Debugf("Failed to read key %s: %w", ssh_key_path, err) } else { - a.HostKeyCallback = verifyHost - return a + e.SshKey = b } } + la.AddEntry(e) } - return nil + return la.BuildAuth(gitUrl) } diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go new file mode 100644 index 000000000..7360afb9e --- /dev/null +++ b/pkg/git/auth/list_auth_provider.go @@ -0,0 +1,81 @@ +package auth + +import ( + "github.com/go-git/go-git/v5/plumbing/transport" + "github.com/go-git/go-git/v5/plumbing/transport/http" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + log "github.com/sirupsen/logrus" + "strings" +) + +type ListAuthProvider struct { + entries []AuthEntry +} + +type AuthEntry struct { + Host string + PathPrefix string + Username string + Password string + SshKey []byte +} + +func (a *ListAuthProvider) AddEntry(e AuthEntry) { + a.entries = append(a.entries, e) +} + +func (a *ListAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod { + for _, e := range a.entries { + if e.Host != "*" && e.Host != gitUrl.Hostname() { + continue + } + if !strings.HasPrefix(gitUrl.Path, e.PathPrefix) { + continue + } + if e.Username == "" { + continue + } + + username := "" + if gitUrl.User != nil { + username = gitUrl.User.Username() + } + + if username != "" && e.Username != "*" && username != e.Username { + continue + } + + if username == "" { + username = e.Username + } + + if e.Username == "*" { + // can't use "*" as username + continue + } + + if gitUrl.IsSsh() { + if e.SshKey == nil { + continue + } + a, err := ssh.NewPublicKeys(username, e.SshKey, "") + if err != nil { + log.Debugf("Failed to parse private key: %v", err) + } else { + a.HostKeyCallback = verifyHost + return a + } + } else { + if e.Password == "" { + // empty password is not accepted + continue + } + return &http.BasicAuth{ + Username: username, + Password: e.Password, + } + } + } + return nil +} From 2556344fa80982bfc25afad5e90d4c05def1d420 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 11:29:33 +0200 Subject: [PATCH 0066/2268] refactor: Allow to add auth providers to the front of the list --- pkg/git/auth/auth_provider.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 6c8872bd6..c8a5baacf 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -13,8 +13,12 @@ type GitAuthProviders struct { authProviders []GitAuthProvider } -func (a *GitAuthProviders) RegisterAuthProvider(p GitAuthProvider) { - a.authProviders = append(a.authProviders, p) +func (a *GitAuthProviders) RegisterAuthProvider(p GitAuthProvider, last bool) { + if last { + a.authProviders = append(a.authProviders, p) + } else { + a.authProviders = append([]GitAuthProvider{p}, a.authProviders...) + } } func (a *GitAuthProviders) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod { @@ -29,8 +33,8 @@ func (a *GitAuthProviders) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod func NewDefaultAuthProviders() *GitAuthProviders { a := &GitAuthProviders{} - a.RegisterAuthProvider(&GitEnvAuthProvider{}) - a.RegisterAuthProvider(&GitCredentialsFileAuthProvider{}) - a.RegisterAuthProvider(&GitSshAuthProvider{}) + a.RegisterAuthProvider(&GitEnvAuthProvider{}, true) + a.RegisterAuthProvider(&GitCredentialsFileAuthProvider{}, true) + a.RegisterAuthProvider(&GitSshAuthProvider{}, true) return a } From faa19d22f3c6b587366f900b6bed96f3f88c47ea Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 11:33:45 +0200 Subject: [PATCH 0067/2268] fix: log.Debugf does not support %w --- pkg/git/auth/env_auth_provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index 908d525f7..362a506bc 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -27,7 +27,7 @@ func (a *GitEnvAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMeth ssh_key_path = utils.ExpandPath(ssh_key_path) b, err := ioutil.ReadFile(ssh_key_path) if err != nil { - log.Debugf("Failed to read key %s: %w", ssh_key_path, err) + log.Debugf("Failed to read key %s: %v", ssh_key_path, err) } else { e.SshKey = b } From c637cb73b2ec99cdbad128e62a063fffec2de762 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 11:59:58 +0200 Subject: [PATCH 0068/2268] feat: Support passing CA bundle and known_hosts for git --- pkg/git/auth/auth_provider.go | 13 +++++--- pkg/git/auth/env_auth_provider.go | 13 ++++++-- pkg/git/auth/git_credentials_file.go | 27 ++++++++-------- pkg/git/auth/list_auth_provider.go | 24 +++++++++----- pkg/git/auth/ssh_auth_provider.go | 13 ++++---- pkg/git/auth/ssh_known_hosts.go | 47 +++++++++++++++++++--------- pkg/git/mirrored_repo.go | 6 ++-- 7 files changed, 94 insertions(+), 49 deletions(-) diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index c8a5baacf..677f4df07 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -5,8 +5,13 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ) +type AuthMethodAndCA struct { + AuthMethod transport.AuthMethod + CABundle []byte +} + type GitAuthProvider interface { - BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod + BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA } type GitAuthProviders struct { @@ -21,14 +26,14 @@ func (a *GitAuthProviders) RegisterAuthProvider(p GitAuthProvider, last bool) { } } -func (a *GitAuthProviders) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod { +func (a *GitAuthProviders) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { for _, p := range a.authProviders { auth := p.BuildAuth(gitUrl) - if auth != nil { + if auth.AuthMethod != nil { return auth } } - return nil + return AuthMethodAndCA{} } func NewDefaultAuthProviders() *GitAuthProviders { diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index 362a506bc..a57fa44f3 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -1,7 +1,6 @@ package auth import ( - "github.com/go-git/go-git/v5/plumbing/transport" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils" log "github.com/sirupsen/logrus" @@ -11,7 +10,7 @@ import ( type GitEnvAuthProvider struct { } -func (a *GitEnvAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod { +func (a *GitEnvAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { var la ListAuthProvider for _, m := range utils.ParseEnvConfigSets("KLUCTL_GIT") { @@ -32,6 +31,16 @@ func (a *GitEnvAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMeth e.SshKey = b } } + ca_bundle_path := m["CA_BUNDLE"] + if ca_bundle_path != "" { + ca_bundle_path = utils.ExpandPath(ca_bundle_path) + b, err := ioutil.ReadFile(ca_bundle_path) + if err != nil { + log.Debugf("Failed to read ca bundle %s: %v", ca_bundle_path, err) + } else { + e.CABundle = b + } + } la.AddEntry(e) } return la.BuildAuth(gitUrl) diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index 49fbe894e..ea665c16f 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -2,7 +2,6 @@ package auth import ( "bufio" - "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/http" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -15,36 +14,36 @@ import ( type GitCredentialsFileAuthProvider struct { } -func (a *GitCredentialsFileAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod { +func (a *GitCredentialsFileAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { if gitUrl.Scheme != "http" && gitUrl.Scheme != "https" { - return nil + return AuthMethodAndCA{} } home, err := os.UserHomeDir() if err != nil { log.Warningf("Could not determine home directory: %v", err) - return nil + return AuthMethodAndCA{} } auth := a.tryBuildAuth(gitUrl, filepath.Join(home, ".git-credentials")) - if auth != nil { - return auth + if auth.AuthMethod != nil { + return *auth } if xdgHome, ok := os.LookupEnv("XDG_CONFIG_HOME"); ok && xdgHome != "" { auth = a.tryBuildAuth(gitUrl, filepath.Join(xdgHome, ".git-credentials")) if auth != nil { - return auth + return *auth } } else { auth = a.tryBuildAuth(gitUrl, filepath.Join(home, ".config/.git-credentials")) if auth != nil { - return auth + return *auth } } - return nil + return AuthMethodAndCA{} } -func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl git_url.GitUrl, gitCredentialsPath string) transport.AuthMethod { +func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl git_url.GitUrl, gitCredentialsPath string) *AuthMethodAndCA { if !utils.IsFile(gitCredentialsPath) { return nil } @@ -70,9 +69,11 @@ func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl git_url.GitUrl, git } if password, ok := u.User.Password(); ok { if u.User.Username() != "" { - return &http.BasicAuth{ - Username: u.User.Username(), - Password: password, + return &AuthMethodAndCA{ + AuthMethod: &http.BasicAuth{ + Username: u.User.Username(), + Password: password, + }, } } } diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index 7360afb9e..d469e500f 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -1,7 +1,6 @@ package auth import ( - "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/plumbing/transport/ssh" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" @@ -18,14 +17,18 @@ type AuthEntry struct { PathPrefix string Username string Password string + SshKey []byte + KnownHosts []byte + + CABundle []byte } func (a *ListAuthProvider) AddEntry(e AuthEntry) { a.entries = append(a.entries, e) } -func (a *ListAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod { +func (a *ListAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { for _, e := range a.entries { if e.Host != "*" && e.Host != gitUrl.Hostname() { continue @@ -63,19 +66,24 @@ func (a *ListAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod if err != nil { log.Debugf("Failed to parse private key: %v", err) } else { - a.HostKeyCallback = verifyHost - return a + a.HostKeyCallback = buildVerifyHostCallback(e.KnownHosts) + return AuthMethodAndCA{ + AuthMethod: a, + } } } else { if e.Password == "" { // empty password is not accepted continue } - return &http.BasicAuth{ - Username: username, - Password: e.Password, + return AuthMethodAndCA{ + AuthMethod: &http.BasicAuth{ + Username: username, + Password: e.Password, + }, + CABundle: e.CABundle, } } } - return nil + return AuthMethodAndCA{} } diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index 6da3e0e98..d3c2a64ca 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -2,7 +2,6 @@ package auth import ( "fmt" - "github.com/go-git/go-git/v5/plumbing/transport" "github.com/kevinburke/ssh_config" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -42,7 +41,7 @@ func (a *sshDefaultIdentityAndAgent) ClientConfig() (*ssh.ClientConfig, error) { User: a.user, Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Signers)}, } - cc.HostKeyCallback = verifyHost + cc.HostKeyCallback = buildVerifyHostCallback(nil) return cc, nil } @@ -90,12 +89,12 @@ func (a *sshDefaultIdentityAndAgent) addAgentIdentities(gitUrl git_url.GitUrl) { } } -func (a *GitSshAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMethod { +func (a *GitSshAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { if !gitUrl.IsSsh() { - return nil + return AuthMethodAndCA{} } if gitUrl.User == nil { - return nil + return AuthMethodAndCA{} } auth := &sshDefaultIdentityAndAgent{ @@ -109,7 +108,9 @@ func (a *GitSshAuthProvider) BuildAuth(gitUrl git_url.GitUrl) transport.AuthMeth auth.addDefaultIdentity(gitUrl) auth.addConfigIdentities(gitUrl) - return auth + return AuthMethodAndCA{ + AuthMethod: auth, + } } // we defer asking for passphrase so that we don't unnecessarily ask for passphrases for keys that are already provided diff --git a/pkg/git/auth/ssh_known_hosts.go b/pkg/git/auth/ssh_known_hosts.go index 2ad01941d..2e0b06785 100644 --- a/pkg/git/auth/ssh_known_hosts.go +++ b/pkg/git/auth/ssh_known_hosts.go @@ -11,7 +11,13 @@ import ( "strconv" ) -func verifyHost(host string, remote net.Addr, key ssh.PublicKey) error { +func buildVerifyHostCallback(knownHosts []byte) func(hostname string, remote net.Addr, key ssh.PublicKey) error { + return func(hostname string, remote net.Addr, key ssh.PublicKey) error { + return verifyHost(hostname, remote, key, knownHosts) + } +} + +func verifyHost(host string, remote net.Addr, key ssh.PublicKey, knownHosts []byte) error { hostKeyChecking := true if x, ok := os.LookupEnv("KLUCTL_SSH_DISABLE_STRICT_HOST_KEY_CHECKING"); ok { if b, err := strconv.ParseBool(x); err == nil && b { @@ -23,23 +29,36 @@ func verifyHost(host string, remote net.Addr, key ssh.PublicKey) error { } allowAdd := false - files := filepath.SplitList(os.Getenv("SSH_KNOWN_HOSTS")) - if len(files) == 0 { - home, err := os.UserHomeDir() - if err != nil { - return err - } - - f := filepath.Join(home, ".ssh", "known_hosts") - if !utils.Exists(filepath.Dir(f)) { - err = os.MkdirAll(filepath.Dir(f), 0o700) + var files []string + if knownHosts == nil { + files = filepath.SplitList(os.Getenv("SSH_KNOWN_HOSTS")) + if len(files) == 0 { + home, err := os.UserHomeDir() if err != nil { return err } - } - files = append(files, f) - allowAdd = true + f := filepath.Join(home, ".ssh", "known_hosts") + if !utils.Exists(filepath.Dir(f)) { + err = os.MkdirAll(filepath.Dir(f), 0o700) + if err != nil { + return err + } + } + + files = append(files, f) + allowAdd = true + } + } else { + tmpFile, err := os.CreateTemp(utils.GetTmpBaseDir(), "known_hosts-") + if err != nil { + return err + } + defer func() { + _ = tmpFile.Close() + _ = os.Remove(tmpFile.Name()) + }() + files = append(files, tmpFile.Name()) } for _, f := range files { diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 82b822ee2..bd7fe5672 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -139,7 +139,8 @@ func (g *MirroredGitRepo) update(repoDir string, authProviders *auth2.GitAuthPro } g.remoteRefs, err = remote.List(&git.ListOptions{ - Auth: auth, + Auth: auth.AuthMethod, + CABundle: auth.CABundle, }) if err != nil { return err @@ -150,7 +151,8 @@ func (g *MirroredGitRepo) update(repoDir string, authProviders *auth2.GitAuthPro } err = remote.Fetch(&git.FetchOptions{ - Auth: auth, + Auth: auth.AuthMethod, + CABundle: auth.CABundle, Progress: os.Stdout, Tags: git.AllTags, Force: true, From 2838933fa4a32373e7e98da355e795faf50da9dd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 15:29:58 +0200 Subject: [PATCH 0069/2268] feat: Use vendored Helm dependency instead of relying on an installed helm binary --- go.mod | 77 +++++- go.sum | 464 +++++++++++++++++++++++++++++++---- pkg/deployment/helm_chart.go | 305 +++++++++++++---------- 3 files changed, 664 insertions(+), 182 deletions(-) diff --git a/go.mod b/go.mod index 73da526f4..6aec263c0 100644 --- a/go.mod +++ b/go.mod @@ -36,17 +36,27 @@ require ( golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b - k8s.io/api v0.25.0-alpha.0 - k8s.io/apimachinery v0.25.0-alpha.0 - k8s.io/client-go v0.25.0-alpha.0 + helm.sh/helm/v3 v3.8.2 + k8s.io/api v0.24.0-rc.1 + k8s.io/apimachinery v0.24.0-rc.1 + k8s.io/client-go v0.24.0-rc.1 sigs.k8s.io/kind v0.12.0 sigs.k8s.io/kustomize/api v0.11.4 sigs.k8s.io/kustomize/kyaml v0.13.6 sigs.k8s.io/structured-merge-diff/v4 v4.2.1 ) +replace ( + k8s.io/api => k8s.io/api v0.23.5 + k8s.io/apimachinery => k8s.io/apimachinery v0.23.5 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.23.5 + k8s.io/client-go => k8s.io/client-go v0.23.5 + k8s.io/component-base => k8s.io/component-base v0.23.5 +) + require ( cloud.google.com/go/compute v1.6.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.27 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect @@ -54,24 +64,39 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/BurntSushi/toml v1.1.0 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.1.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.2 // indirect + github.com/Masterminds/squirrel v1.5.2 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/alessio/shellescape v1.4.1 // indirect + github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect + github.com/containerd/containerd v1.6.3 // indirect + github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v20.10.14+incompatible // indirect github.com/docker/docker v20.10.14+incompatible // indirect github.com/docker/docker-credential-helpers v0.6.4 // indirect - github.com/emicklei/go-restful v2.15.0+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/go-units v0.4.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect github.com/gammazero/deque v0.1.1 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect + github.com/go-gorp/gorp/v3 v3.0.2 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect @@ -81,35 +106,63 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/gnostic v0.5.5 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.1 // indirect + github.com/lib/pq v1.10.5 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5 // indirect + github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.12.1 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.34.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rubenv/sql-migrate v1.1.1 // indirect + github.com/russross/blackfriday v1.6.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/spf13/cast v1.4.1 // indirect github.com/spf13/cobra v1.4.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect @@ -118,13 +171,21 @@ require ( golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 // indirect + google.golang.org/grpc v1.46.0 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/apiextensions-apiserver v0.23.6 // indirect + k8s.io/apiserver v0.23.6 // indirect + k8s.io/cli-runtime v0.24.0-rc.1 // indirect + k8s.io/component-base v0.24.0-rc.1 // indirect k8s.io/klog/v2 v2.60.1 // indirect - k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6 // indirect + k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect + k8s.io/kubectl v0.23.5 // indirect k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + oras.land/oras-go v1.1.1 // indirect sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index fe612bf11..800475ba0 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= 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= @@ -55,27 +56,30 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl 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= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY= github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= @@ -85,7 +89,24 @@ github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= +github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Masterminds/squirrel v1.5.2 h1:UiOEi2ZX4RCSkpiNDQN5kro/XIBpSRk9iTqdIRPzUXE= +github.com/Masterminds/squirrel v1.5.2/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Masterminds/vcs v1.13.3/go.mod h1:TiE7xuEjl1N4j016moRd6vezp6e6Lz23gypeXfzXeW8= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= @@ -105,16 +126,24 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3 github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= +github.com/Microsoft/hcsshim v0.9.2 h1:wB06W5aYFfUB3IvootYAY2WnOmIdgPGfqSI6tufQNnY= +github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +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/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 h1:cSHEbLj0GZeHM1mWG84qEnGFojNEQ83W7cwaPRjcwXU= github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= @@ -130,12 +159,15 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy 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/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -145,36 +177,56 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.44.1 h1:w34ZmPT6K4NTd7Yap1P7SLXPTii0ABmBz3KEh4KJdKc= github.com/aws/aws-sdk-go v1.44.1/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bitnami-labs/sealed-secrets v0.17.5 h1:v5ENZRSrgog3GnFr8fWfVtrUTPlZlNlbsjaro9mn6YY= github.com/bitnami-labs/sealed-secrets v0.17.5/go.mod h1:ZgGUqKixr/SRpsG8LVXnuneyZG7DOX/+eCRvXi8SVjo= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= 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.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= 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= @@ -183,6 +235,7 @@ github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLI github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -197,6 +250,9 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= +github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= @@ -211,11 +267,14 @@ github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4S github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= +github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -229,7 +288,12 @@ github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7 github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= +github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= +github.com/containerd/containerd v1.6.3 h1:JfgUEIAH07xDWk6kqz0P3ArZt+KJ9YeihSC9uyFtSKg= +github.com/containerd/containerd v1.6.3/go.mod h1:gCVGrYRYFm2E8GmuUIbj/NGD7DLZQLzSJQazjVKDOig= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -237,6 +301,7 @@ github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cE github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= @@ -245,6 +310,8 @@ github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1S github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= +github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= +github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= @@ -254,9 +321,11 @@ github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng= github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= @@ -277,15 +346,20 @@ github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNR github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -302,8 +376,11 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= @@ -312,11 +389,17 @@ github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7h 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/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= +github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY= +github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684/go.mod h1:UfCu3YXJJCI+IdnqGgYP82dk2+Joxmv+mUTVBES6wac= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.14+incompatible h1:dSBKJOVesDgHo7rbxlYjYsXe7gPzrTT+/cKQgpDAazg= github.com/docker/cli v20.10.14+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -325,27 +408,33 @@ github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.14+incompatible h1:+T9/PRYWNDo5SZl5qS1r9Mo/0Q8AwxKKPtu9S1yxM0w= github.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.15.0+incompatible h1:8KpYO/Xl/ZudZs5RNOEhWMBY4hmzlZhhRd9cu+jrZP4= -github.com/emicklei/go-restful v2.15.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= @@ -358,6 +447,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m 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/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -367,12 +457,17 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv 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/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -382,6 +477,7 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= github.com/gammazero/deque v0.1.1 h1:xRVkDuSvDmFuMGf3IquHuRc2jlL0+v/WpFCWaauzwbE= github.com/gammazero/deque v0.1.1/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= @@ -389,6 +485,7 @@ github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/ github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= @@ -408,26 +505,42 @@ github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti 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-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4= +github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 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-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.4.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.1/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.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/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.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= 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/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= @@ -444,7 +557,21 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig= github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= +github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= +github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= +github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= +github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= +github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= +github.com/gobuffalo/packr/v2 v2.8.1/go.mod h1:c/PLlOuTU+p3SybaJATW3H6lX/iK7xEz5OeMf+NnJpg= +github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= +github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0= @@ -454,8 +581,11 @@ github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblf github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -469,12 +599,15 @@ github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 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/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/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= @@ -504,13 +637,17 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 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/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= +github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -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= @@ -523,8 +660,10 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/go-containerregistry v0.8.0 h1:mtR24eN6rapCN+shds82qFEIWWmg64NPMuyCNT7/Ogc= github.com/google/go-containerregistry v0.8.0/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -556,6 +695,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/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/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -564,18 +705,27 @@ github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/Oth github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -586,6 +736,7 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= @@ -597,6 +748,7 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= @@ -624,6 +776,9 @@ github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpT github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/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= @@ -635,7 +790,9 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -644,13 +801,20 @@ github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -662,6 +826,10 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -677,6 +845,7 @@ github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHU github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= 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= @@ -689,18 +858,39 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 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/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ= +github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/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/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= +github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= +github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= +github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= +github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= @@ -719,12 +909,22 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -732,12 +932,19 @@ github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WT github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -745,13 +952,24 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= 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= @@ -761,36 +979,46 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/ohler55/ojg v1.14.0 h1:DyHomsCwofNswmKj7BLMdx51xnKbXxgIo1rVWCaBcNk= github.com/ohler55/ojg v1.14.0/go.mod h1:3+GH+0PggMKocQtbZCrFifal3yRpHiBT4QUkxFJI6e8= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -800,14 +1028,17 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5 h1:q37d91F6BO4Jp1UqWiun0dUFYaqv6WsKTLTCaWv+8LY= github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= +github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -818,6 +1049,8 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -828,6 +1061,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= 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-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -839,6 +1074,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= +github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -847,10 +1084,14 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -859,6 +1100,12 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= +github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -868,8 +1115,9 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= @@ -878,20 +1126,34 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 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/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc/go.mod h1:HFLT6i9iR4QBOF5rdCyjddC9t59ArqWJV2xx+jwcCMo= +github.com/rubenv/sql-migrate v1.1.1 h1:haR5Hn8hbW9/SpAICrXoZqXnywS7Q5WijwkQENPeNWY= +github.com/rubenv/sql-migrate v1.1.1/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= @@ -906,6 +1168,7 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -913,10 +1176,13 @@ github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY52 github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= @@ -930,6 +1196,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn 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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= @@ -955,7 +1222,9 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -969,9 +1238,11 @@ github.com/vbauerster/mpb/v7 v7.4.1/go.mod h1:Ygg2mV9Vj9sQBWqsK2m2pidcf9H3s6bNKt github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= @@ -982,8 +1253,12 @@ github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6e github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= @@ -995,13 +1270,20 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -1009,6 +1291,10 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3 go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= +go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= +go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -1016,24 +1302,49 @@ 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.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= +go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= +go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= +go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= +go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= +go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd h1:Uo/x0Ir5vQJ+683GXB9Ug+4fcjsbp7z7Ul8UaZbhsRM= go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 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= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/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-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1041,16 +1352,20 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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-20210817164053-32db794688a5/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-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1134,6 +1449,7 @@ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwY 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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/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-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -1142,12 +1458,17 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1209,6 +1530,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1248,6 +1570,7 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1256,6 +1579,7 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1263,6 +1587,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1274,25 +1599,34 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20210514084401-e8d321eab015/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-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1300,7 +1634,9 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/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.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1318,13 +1654,14 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/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.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1340,10 +1677,12 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw 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-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/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-20191112195655-aa38f8e97acc/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= @@ -1361,21 +1700,25 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK 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-20200308013534-11ec41452d41/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-20200616133436-c1934b75d054/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-20200916195026-c9a70fc28ce3/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-20201224043029-2b0845dc783e/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.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= @@ -1384,6 +1727,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= 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= @@ -1463,17 +1808,20 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG 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-20200423170343-7949de9c1215/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-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 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-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/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-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1520,6 +1868,8 @@ google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 h1:G1IeWbjrqEq9ChWxEuRPJu6laA67+XgTFHVSAvepr38= +google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1554,6 +1904,8 @@ google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5 google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1583,8 +1935,10 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= @@ -1615,6 +1969,8 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +helm.sh/helm/v3 v3.8.2 h1:HDhe2nKek976VLMPZlIgJbNqwcqvHYBp1qy+sXQ4jiY= +helm.sh/helm/v3 v3.8.2/go.mod h1:NxtE2KObf2PrzDl6SIamPFPKyAqWi10iWuvKlQn/Yao= 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= @@ -1622,63 +1978,89 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.25.0-alpha.0 h1:BiYeMLWoLcGGWE46gdnlwluFa23+Hr3I2Qp8U6c2wYY= -k8s.io/api v0.25.0-alpha.0/go.mod h1:sOibYBePcsE/DBjbbi+Z+FCG9lFPR7xuKSR2r6RTCNs= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.25.0-alpha.0 h1:gAzcXIp+FkB3w8+m34na2qxSScwQWKtryRU8JfkS/NU= -k8s.io/apimachinery v0.25.0-alpha.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA= +k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= +k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ= +k8s.io/apiextensions-apiserver v0.23.6 h1:v58cQ6Z0/GK1IXYr+oW0fnYl52o9LTY0WgoWvI8uv5Q= +k8s.io/apiextensions-apiserver v0.23.6/go.mod h1:YVh17Mphv183THQJA5spNFp9XfoidFyL3WoDgZxQIZU= +k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0= +k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.25.0-alpha.0 h1:IwIWODi6Uz7QlOU8J2tv8APdDtytoowveKF9IoKpHa4= -k8s.io/client-go v0.25.0-alpha.0/go.mod h1:V7vCXDCdD8Goobi4oQhsSNXtlWfyBJi+LHFaiYWpR5s= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= +k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw= +k8s.io/apiserver v0.23.6 h1:p94LiXcsSnpSDIl4cv98liBuFKcaygSCNopFNfMg/Ac= +k8s.io/apiserver v0.23.6/go.mod h1:5PU32F82tfErXPmf7FXhd/UcuLfh97tGepjKUgJ2atg= +k8s.io/cli-runtime v0.23.5 h1:Z7XUpGoJZYZB2uNjQfJjMbyDKyVkoBGye62Ap0sWQHY= +k8s.io/cli-runtime v0.23.5/go.mod h1:oY6QDF2qo9xndSq32tqcmRp2UyXssdGrLfjAVymgbx4= +k8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8= +k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= +k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= +k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= +k8s.io/code-generator v0.23.6/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= +k8s.io/component-base v0.23.5 h1:8qgP5R6jG1BBSXmRYW+dsmitIrpk8F/fPEvgDenMCCE= +k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0= +k8s.io/component-helpers v0.23.5/go.mod h1:5riXJgjTIs+ZB8xnf5M2anZ8iQuq37a0B/0BgoPQuSM= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6 h1:nBQrWPlrNIiw0BsX6a6MKr1itkm0ZS0Nl97kNLitFfI= -k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= +k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/kubectl v0.23.5 h1:DmDULqCaF4qstj0Im143XmncvqWtJxHzK8IrW2BzlU0= +k8s.io/kubectl v0.23.5/go.mod h1:lLgw7cVY8xbd7o637vOXPca/w6HC205KsPCRDYRCxwE= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/metrics v0.23.5/go.mod h1:WNAtV2a5BYbmDS8+7jSqYYV6E3efuGTpIwJ8PTD1wgs= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +oras.land/oras-go v1.1.1 h1:gI00ftziRivKXaw1BdMeEoIA4uBgga33iVlOsEwefFs= +oras.land/oras-go v1.1.1/go.mod h1:n2TE1ummt9MUyprGhT+Q7kGZUF4kVUpYysPFxeV2IpQ= 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.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/kind v0.12.0 h1:LFynXwQkH1MrWI8pM1FQty0oUwEKjU5EkMaVZaPld8E= sigs.k8s.io/kind v0.12.0/go.mod h1:EcgDSBVxz8Bvm19fx8xkioFrf9dC30fMJdOTXBSGNoM= +sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8= sigs.k8s.io/kustomize/api v0.11.4 h1:/0Mr3kfBBNcNPOW5Qwk/3eb8zkswCwnqQxxKtmrTkRo= sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= +sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ= +sigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io= +sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E= sigs.k8s.io/kustomize/kyaml v0.13.6 h1:eF+wsn4J7GOAXlvajv6OknSunxpcOBQQqsnPxObtkGs= sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 0b26b404c..e080d8075 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -4,13 +4,23 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/yaml" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/cli/values" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/registry" + "helm.sh/helm/v3/pkg/release" + "helm.sh/helm/v3/pkg/repo" "io/ioutil" "os" - "os/exec" "path/filepath" "regexp" "sort" @@ -36,35 +46,8 @@ func NewHelmChart(configFile string) (*helmChart, error) { return hc, nil } -func (c *helmChart) withRepoContext(cb func(repoName string) error) error { - needRepo := false - repoName := "stable" - - if c.Config.Repo != nil && *c.Config.Repo != "stable" { - needRepo = true - repoName = fmt.Sprintf("kluctl-%s", utils.Sha256String(*c.Config.Repo))[:16] - } - - if needRepo { - _, _ = c.doHelm([]string{"repo", "remove", repoName}, true) - _, err := c.doHelm([]string{"repo", "add", repoName, *c.Config.Repo}, false) - if err != nil { - return err - } - defer func() { - _, _ = c.doHelm([]string{"repo", "remove", repoName}, true) - }() - } else { - _, err := c.doHelm([]string{"repo", "update"}, false) - if err != nil { - return err - } - } - return cb(repoName) -} - func (c *helmChart) GetChartName() (string, error) { - if c.Config.Repo != nil && strings.HasPrefix(*c.Config.Repo, "oci://") { + if c.Config.Repo != nil && registry.IsOCI(*c.Config.Repo) { s := strings.Split(*c.Config.Repo, "/") chartName := s[len(s)-1] if m, _ := regexp.MatchString(`[a-zA-Z_-]+`, chartName); !m { @@ -78,6 +61,16 @@ func (c *helmChart) GetChartName() (string, error) { return *c.Config.ChartName, nil } +func (c *helmChart) buildHelmConfig() (*action.Configuration, error) { + rc, err := registry.NewClient() + if err != nil { + return nil, err + } + return &action.Configuration{ + RegistryClient: rc, + }, nil +} + func (c *helmChart) Pull() error { chartName, err := c.GetChartName() if err != nil { @@ -89,25 +82,37 @@ func (c *helmChart) Pull() error { rmDir := filepath.Join(targetDir, chartName) _ = os.RemoveAll(rmDir) - var args []string - if c.Config.Repo != nil && strings.HasPrefix(*c.Config.Repo, "oci://") { - args = []string{"pull", *c.Config.Repo, "--destination", targetDir, "--untar"} - args = append(args, "--version") - args = append(args, *c.Config.ChartVersion) - _, err = c.doHelm(args, false) + cfg, err := c.buildHelmConfig() + if err != nil { return err + } + a := action.NewPullWithOpts(action.WithConfig(cfg)) + a.Settings = cli.New() + a.Untar = true + a.DestDir = targetDir + a.Version = *c.Config.ChartVersion + + var out string + if registry.IsOCI(*c.Config.Repo) { + out, err = a.Run(*c.Config.Repo) } else { - return c.withRepoContext(func(repoName string) error { - args = []string{"pull", fmt.Sprintf("%s/%s", repoName, chartName), "--destination", targetDir, "--untar"} - args = append(args, "--version", *c.Config.ChartVersion) - _, err = c.doHelm(args, false) - return err - }) + a.RepoURL = *c.Config.Repo + out, err = a.Run(chartName) } + // a bug in the Pull command causes this directory to be created by accident + _ = os.RemoveAll(rmDir + fmt.Sprintf("-%s.tar.gz", a.Version)) + _ = os.RemoveAll(rmDir + fmt.Sprintf("-%s.tgz", a.Version)) + if out != "" { + log.Info(out) + } + if err != nil { + return err + } + return nil } func (c *helmChart) CheckUpdate() (string, bool, error) { - if c.Config.Repo != nil && strings.HasPrefix(*c.Config.Repo, "oci://") { + if c.Config.Repo != nil && registry.IsOCI(*c.Config.Repo) { return "", false, nil } chartName, err := c.GetChartName() @@ -115,37 +120,39 @@ func (c *helmChart) CheckUpdate() (string, bool, error) { return "", false, err } var latestVersion string - err = c.withRepoContext(func(repoName string) error { - chartName := fmt.Sprintf("%s/%s", repoName, chartName) - args := []string{"search", "repo", chartName, "-oyaml", "-l"} - stdout, err := c.doHelm(args, false) - if err != nil { - return err - } - // ensure we didn't get partial matches - var lm []map[string]string - var ls versions.LooseVersionSlice - err = yaml.ReadYamlBytes(stdout, &lm) - if err != nil { - return err - } - for _, x := range lm { - if n, ok := x["name"]; ok && n == chartName { - if v, ok := x["version"]; ok { - ls = append(ls, versions.LooseVersion(v)) - } - } - } - if len(ls) == 0 { - return fmt.Errorf("helm chart %s not found in repository", chartName) - } - sort.Sort(ls) - latestVersion = string(ls[len(ls)-1]) - return nil - }) + + settings := cli.New() + e := repo.Entry{ + URL: *c.Config.Repo, + Name: chartName, + } + r, err := repo.NewChartRepository(&e, getter.All(settings)) + if err != nil { + return "", false, err + } + + indexFile, err := r.DownloadIndexFile() + if err != nil { + return "", false, err + } + + index, err := repo.LoadIndexFile(indexFile) if err != nil { return "", false, err } + + indexEntry, ok := index.Entries[*c.Config.ChartName] + if !ok || len(indexEntry) == 0 { + return "", false, fmt.Errorf("helm chart %s not found in repo index", *c.Config.ChartName) + } + + var ls versions.LooseVersionSlice + for _, x := range indexEntry { + ls = append(ls, versions.LooseVersion(x.Version)) + } + sort.Stable(ls) + latestVersion = string(ls[len(ls)-1]) + updated := latestVersion != *c.Config.ChartVersion return latestVersion, updated, nil } @@ -160,48 +167,109 @@ func (c *helmChart) Render(k *k8s.K8sCluster) error { valuesPath := yaml.FixPathExt(filepath.Join(dir, "helm-values.yml")) outputPath := filepath.Join(dir, c.Config.Output) - args := []string{"template", c.Config.ReleaseName, chartDir} + gvs, err := k.GetAllGroupVersions() + if err != nil { + return err + } + + cfg, err := c.buildHelmConfig() + if err != nil { + return err + } + + settings := cli.New() + valueOpts := values.Options{ + ValueFiles: []string{valuesPath}, + } + + kubeVersion, err := chartutil.ParseKubeVersion(k.ServerVersion.String()) + if err != nil { + return err + } namespace := "default" if c.Config.Namespace != nil { namespace = *c.Config.Namespace } - if utils.Exists(valuesPath) { - args = append(args, "-f", valuesPath) - } - args = append(args, "-n", namespace) + client := action.NewInstall(cfg) + client.DryRun = true + client.Namespace = namespace + client.ReleaseName = c.Config.ReleaseName + client.Replace = true + client.ClientOnly = true + client.KubeVersion = kubeVersion if c.Config.SkipCRDs != nil && *c.Config.SkipCRDs { - args = append(args, "--skip-crds") + client.SkipCRDs = true } else { - args = append(args, "--include-crds") + client.IncludeCRDs = true + } + for _, gv := range gvs { + client.APIVersions = append(client.APIVersions, gv) } - args = append(args, "--skip-tests") - gvs, err := k.GetAllGroupVersions() + p := getter.All(settings) + vals, err := valueOpts.MergeValues(p) if err != nil { return err } - for _, gv := range gvs { - args = append(args, fmt.Sprintf("--api-versions=%s", gv)) + + // Check chart dependencies to make sure all are present in /charts + chartRequested, err := loader.Load(chartDir) + if err != nil { + return err } - args = append(args, fmt.Sprintf("--kube-version=%s", k.ServerVersion.String())) - rendered, err := c.doHelm(args, false) + if err := checkIfInstallable(chartRequested); err != nil { + return err + } + + if chartRequested.Metadata.Deprecated { + log.Warningf("Chart %s is deprecated", *c.Config.ChartName) + } + + rel, err := client.Run(chartRequested, vals) if err != nil { return err } - parsed, err := yaml.ReadYamlAllBytes(rendered) + var parsed []*uo.UnstructuredObject - for _, o := range parsed { - m, ok := o.(map[string]interface{}) - if !ok { - return fmt.Errorf("object is not a map") + doParse := func(s string) error { + m, err := yaml.ReadYamlAllString(s) + if err != nil { + return err } - o := uo.FromMap(m) + for _, x := range m { + x2, ok := x.(map[string]interface{}) + if !ok { + return fmt.Errorf("yaml object is not a map") + } + parsed = append(parsed, uo.FromMap(x2)) + } + return nil + } + err = doParse(rel.Manifest) + if err != nil { + return err + } + + if !client.DisableHooks { + for _, m := range rel.Hooks { + if isTestHook(m) { + continue + } + err = doParse(m.Manifest) + if err != nil { + return err + } + } + } + + var fixed []interface{} + for _, o := range parsed { // "helm install" will deploy resources to the given namespace automatically, but "helm template" does not // add the necessary namespace in the rendered resources err = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { @@ -211,8 +279,9 @@ func (c *helmChart) Render(k *k8s.K8sCluster) error { if err != nil { return err } + fixed = append(fixed, o) } - rendered, err = yaml.WriteYamlAllBytes(parsed) + rendered, err := yaml.WriteYamlAllBytes(fixed) if err != nil { return err } @@ -224,51 +293,21 @@ func (c *helmChart) Render(k *k8s.K8sCluster) error { return nil } -func (c *helmChart) doHelm(args []string, ignoreStdErr bool) ([]byte, error) { - cmd := exec.Command("helm", args...) - cmd.Env = append(os.Environ(), "HELM_EXPERIMENTAL_OCI=true") - - if ignoreStdErr { - stderrPipe, err := cmd.StderrPipe() - if err != nil { - return nil, err - } - go func() { - buf := make([]byte, 1024) - for true { - n, err := stderrPipe.Read(buf) - if err != nil || n == 0 { - return - } - } - }() - } else { - cmd.Stderr = os.Stderr - } - - stdoutPipe, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - - err = cmd.Start() - if err != nil { - return nil, err +func checkIfInstallable(ch *chart.Chart) error { + switch ch.Metadata.Type { + case "", "application": + return nil } - defer cmd.Process.Kill() + return errors.Errorf("%s charts are not installable", ch.Metadata.Type) +} - stdout, err := ioutil.ReadAll(stdoutPipe) - if err != nil { - return nil, err - } - ps, err := cmd.Process.Wait() - if err != nil { - return nil, err - } - if ps.ExitCode() != 0 { - return nil, fmt.Errorf("helm returned non-zero exit code %d", ps.ExitCode()) +func isTestHook(h *release.Hook) bool { + for _, e := range h.Events { + if e == release.HookTest { + return true + } } - return stdout, nil + return false } func (c *helmChart) Save() error { From ffa66e02529d312bdee4f1d7739a66f6166dae80 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 17:16:35 +0200 Subject: [PATCH 0070/2268] build: Remove traces of helm installation --- .github/ISSUE_TEMPLATE/BUG.yml | 8 -------- .github/workflows/tests.yml | 5 ----- 2 files changed, 13 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/BUG.yml b/.github/ISSUE_TEMPLATE/BUG.yml index 1dd5cccc3..be2370e3a 100644 --- a/.github/ISSUE_TEMPLATE/BUG.yml +++ b/.github/ISSUE_TEMPLATE/BUG.yml @@ -41,14 +41,6 @@ body: placeholder: "v1.22.0" validations: required: true - - type: input - id: helm - attributes: - label: Helm Version - description: "Please provide the full Helm version. (Output of `helm version`)" - placeholder: "v3.6.3" - validations: - required: true - type: textarea id: bug-description attributes: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 932808d7f..1ad4ff67a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -211,7 +211,6 @@ jobs: run: | echo "KUBECTL_VERSION=1.21.5" >> $GITHUB_ENV echo "KIND_VERSION=0.11.1" >> $GITHUB_ENV - echo "HELM_VERSION=3.6.3" >> $GITHUB_ENV echo "DOCKER_VERSION=20.10.9" >> $GITHUB_ENV - name: Download required tools shell: bash @@ -220,9 +219,6 @@ jobs: $SUDO mv kubectl$TOOLS_EXE "$TOOLS_TARGET_DIR/" curl -L -o kind$TOOLS_EXE https://github.com/kubernetes-sigs/kind/releases/download/v${KIND_VERSION}/kind-${TOOLS_OS}-amd64 && \ $SUDO mv kind$TOOLS_EXE "$TOOLS_TARGET_DIR/" - curl -L -o helm.tar.gz https://get.helm.sh/helm-v$HELM_VERSION-${TOOLS_OS}-amd64.tar.gz && \ - tar xzf helm.tar.gz && \ - $SUDO mv ${TOOLS_OS}-amd64/helm$TOOLS_EXE "$TOOLS_TARGET_DIR/" if [ "${{ runner.os }}" == "macOS" ]; then curl -L -o docker.tar.gz https://download.docker.com/mac/static/stable/x86_64/docker-$DOCKER_VERSION.tgz tar xzf docker.tar.gz @@ -240,7 +236,6 @@ jobs: run: | kubectl version || true kind version || true - helm version || true - name: Start kind cluster shell: bash run: | From 98f296647e8617d15990d5719d129109c0401218 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 17:16:44 +0200 Subject: [PATCH 0071/2268] fix: Fix crash in GitCredentialsFileAuthProvider --- pkg/git/auth/git_credentials_file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index ea665c16f..b3a3b9f97 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -25,7 +25,7 @@ func (a *GitCredentialsFileAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMe return AuthMethodAndCA{} } auth := a.tryBuildAuth(gitUrl, filepath.Join(home, ".git-credentials")) - if auth.AuthMethod != nil { + if auth != nil { return *auth } From d3ad0d0344e9b8e0e27cf6df4fbe3aa973c6df29 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 1 Apr 2022 15:23:58 +0200 Subject: [PATCH 0072/2268] fix: Allow better debugging of ssh auth errors --- pkg/git/auth/ssh_auth_provider.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index d3c2a64ca..b37d242f1 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -50,32 +50,39 @@ func (a *sshDefaultIdentityAndAgent) Signers() ([]ssh.Signer, error) { } func (a *sshDefaultIdentityAndAgent) addDefaultIdentity(gitUrl git_url.GitUrl) { + log.Debugf("trying to add default identity") u, err := user.Current() if err != nil { log.Debugf("No current user: %v", err) } else { - signer, err := a.authProvider.readKey(filepath.Join(u.HomeDir, ".ssh", "id_rsa")) + path := filepath.Join(u.HomeDir, ".ssh", "id_rsa") + signer, err := a.authProvider.readKey(path) if err != nil && !os.IsNotExist(err) { log.Warningf("Failed to read default identity file for url %s: %v", gitUrl.String(), err) } else if signer != nil { + log.Debugf("...added '%s' as default identity", path) a.signers = append(a.signers, signer) } } } func (a *sshDefaultIdentityAndAgent) addConfigIdentities(gitUrl git_url.GitUrl) { + log.Debugf("trying to add identities from ssh config") for _, id := range ssh_config.GetAll(gitUrl.Hostname(), "IdentityFile") { - id = utils.ExpandPath(id) - signer, err := a.authProvider.readKey(id) + expanded := utils.ExpandPath(id) + log.Debugf("...trying '%s' (expanded: '%s')", id, expanded) + signer, err := a.authProvider.readKey(expanded) if err != nil && !os.IsNotExist(err) { log.Warningf("Failed to read key %s for url %s: %v", id, gitUrl.String(), err) } else if err == nil { + log.Debugf("...added '%s' from ssh config", expanded) a.signers = append(a.signers, signer) } } } func (a *sshDefaultIdentityAndAgent) addAgentIdentities(gitUrl git_url.GitUrl) { + log.Debugf("trying to add agent keys") agent, _, err := sshagent.New() if err != nil { log.Warningf("Failed to connect to ssh agent for url %s: %v", gitUrl.String(), err) @@ -85,6 +92,7 @@ func (a *sshDefaultIdentityAndAgent) addAgentIdentities(gitUrl git_url.GitUrl) { log.Warningf("Failed to get signers from ssh agent for url %s: %v", gitUrl.String(), err) return } + log.Debugf("...added %d agent keys", len(signers)) a.signers = append(a.signers, signers...) } } From cc1be2ab170aeed08757bfabdd1a68d8b61e38ad Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 18:00:52 +0200 Subject: [PATCH 0073/2268] fix: Fix matching of patterns on windows --- pkg/utils/embed_util/packer/packer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/utils/embed_util/packer/packer.go b/pkg/utils/embed_util/packer/packer.go index 9714cedda..d02da2d7f 100644 --- a/pkg/utils/embed_util/packer/packer.go +++ b/pkg/utils/embed_util/packer/packer.go @@ -13,6 +13,7 @@ import ( log "github.com/sirupsen/logrus" "io/fs" "io/ioutil" + "os" "path/filepath" "reflect" "strings" @@ -74,7 +75,7 @@ func findFiles(dir string, patterns []string) (map[string]int64, error) { } match := false for _, p := range globs { - if p.Match(rel) { + if p.Match(strings.ReplaceAll(rel, string(os.PathSeparator), "/")) { match = true break } From 36366c4071c511980c56984197ab0515c7db899b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 22:54:06 +0200 Subject: [PATCH 0074/2268] fix: Support BOMs in yaml files --- pkg/yaml/yaml.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 271c761fe..1726b4dae 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -7,6 +7,8 @@ import ( "fmt" "github.com/goccy/go-yaml" "github.com/kluctl/kluctl/v2/pkg/utils" + "golang.org/x/text/encoding/unicode" + "golang.org/x/text/transform" yaml3 "gopkg.in/yaml.v3" "io" "os" @@ -18,8 +20,9 @@ func newYamlDecoder(r io.Reader) *yaml.Decoder { return yaml.NewDecoder(r, yaml.Strict(), yaml.Validator(Validator)) } -func newYamlEncoder(w io.Writer) *yaml.Encoder { - return yaml.NewEncoder(w) +func newUnicodeReader(r io.Reader) io.Reader { + utf16bom := unicode.BOMOverride(unicode.UTF8.NewDecoder()) + return transform.NewReader(r, utf16bom) } func ReadYamlFile(p string, o interface{}) error { @@ -45,6 +48,8 @@ func ReadYamlBytes(b []byte, o interface{}) error { } func ReadYamlStream(r io.Reader, o interface{}) error { + r = newUnicodeReader(r) + var err error if _, ok := o.(*map[string]interface{}); ok { // much faster @@ -80,6 +85,8 @@ func ReadYamlAllBytes(b []byte) ([]interface{}, error) { } func ReadYamlAllStream(r io.Reader) ([]interface{}, error) { + r = newUnicodeReader(r) + // yaml.v3 is much faster then go-yaml d := yaml3.NewDecoder(r) From 9f5365d7f7d0d21715186b86fb10105a45ec16b5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Apr 2022 23:24:55 +0200 Subject: [PATCH 0075/2268] fix: Show which chart failed --- pkg/deployment/helm_chart.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index e080d8075..343c87b19 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -158,6 +158,18 @@ func (c *helmChart) CheckUpdate() (string, bool, error) { } func (c *helmChart) Render(k *k8s.K8sCluster) error { + chartName, err := c.GetChartName() + if err != nil { + return err + } + err = c.doRender(k) + if err != nil { + return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", chartName, c.Config.ReleaseName, err) + } + return nil +} + +func (c *helmChart) doRender(k *k8s.K8sCluster) error { chartName, err := c.GetChartName() if err != nil { return err From c701aeda77082125e7d28cce837caed5f7fe7a22 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 09:04:30 +0200 Subject: [PATCH 0076/2268] fix: Fallback to kubeconfig CA when kube-root-ca.crt is unavailable --- pkg/k8s/k8s_cluster.go | 6 ++++++ pkg/seal/sealer.go | 26 +++++++++++++++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index fec67980f..d9d9aa93b 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -40,6 +40,7 @@ type K8sCluster struct { context string DryRun bool + restConfig *rest.Config discovery *disk.CachedDiscoveryClient clientPool chan *parallelClientEntry @@ -92,6 +93,7 @@ func NewK8sCluster(context string, dryRun bool) (*K8sCluster, error) { } config.QPS = 10 config.Burst = 20 + k.restConfig = config apiHost, err := url.Parse(config.Host) if err != nil { @@ -162,6 +164,10 @@ func (k *K8sCluster) Context() string { return k.context } +func (k *K8sCluster) GetCA() []byte { + return k.restConfig.CAData +} + func (k *K8sCluster) updateResources(doLock bool) error { if doLock { k.mutex.Lock() diff --git a/pkg/seal/sealer.go b/pkg/seal/sealer.go index 75f8bb187..92dd6953d 100644 --- a/pkg/seal/sealer.go +++ b/pkg/seal/sealer.go @@ -15,6 +15,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "golang.org/x/crypto/scrypt" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" "os" "path/filepath" @@ -61,15 +62,26 @@ func getClusterId(k *k8s.K8sCluster) (string, error) { Name: "kube-root-ca.crt", Namespace: "kube-system", }) - if err != nil { + if err != nil && !errors.IsNotFound(err) { return "", fmt.Errorf("failed to retrieve kube-root-ca.crt: %w", err) } - kubeRootCA, ok, err := o.GetNestedString("data", "ca.crt") - if err != nil { - return "", fmt.Errorf("failed to retrieve kube-root-ca.crt: %w", err) - } - if !ok { - return "", fmt.Errorf("failed to retrieve kube-root-ca.crt: ca.crt key is missing") + + var kubeRootCA string + if o != nil { + x, ok, err := o.GetNestedString("data", "ca.crt") + if err != nil { + return "", fmt.Errorf("failed to retrieve kube-root-ca.crt: %w", err) + } + if !ok { + return "", fmt.Errorf("failed to retrieve kube-root-ca.crt: ca.crt key is missing") + } + kubeRootCA = x + } else { + // fall-back to CA from kubeconfig + ca := k.GetCA() + if ca != nil { + kubeRootCA = string(ca) + } } return utils.Sha256String(kubeRootCA), nil } From f02fc7c234b372762676e850d82a4f4364cc1cb4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 24 Mar 2022 08:14:15 +0100 Subject: [PATCH 0077/2268] fix: Ensure only one "accept host" prompts is shown at a time --- pkg/git/auth/ssh_known_hosts.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/git/auth/ssh_known_hosts.go b/pkg/git/auth/ssh_known_hosts.go index 2e0b06785..6a4225e5e 100644 --- a/pkg/git/auth/ssh_known_hosts.go +++ b/pkg/git/auth/ssh_known_hosts.go @@ -9,8 +9,11 @@ import ( "os" "path/filepath" "strconv" + "sync" ) +var askHostMutex sync.Mutex + func buildVerifyHostCallback(knownHosts []byte) func(hostname string, remote net.Addr, key ssh.PublicKey) error { return func(hostname string, remote net.Addr, key ssh.PublicKey) error { return verifyHost(hostname, remote, key, knownHosts) @@ -18,6 +21,10 @@ func buildVerifyHostCallback(knownHosts []byte) func(hostname string, remote net } func verifyHost(host string, remote net.Addr, key ssh.PublicKey, knownHosts []byte) error { + // Ensure only one prompt happens at a time + askHostMutex.Lock() + defer askHostMutex.Unlock() + hostKeyChecking := true if x, ok := os.LookupEnv("KLUCTL_SSH_DISABLE_STRICT_HOST_KEY_CHECKING"); ok { if b, err := strconv.ParseBool(x); err == nil && b { From f9da55be0f76c10380cfdb4598a8369a2b6979a4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 10:16:43 +0200 Subject: [PATCH 0078/2268] feat: Implememt http based secrets source --- pkg/seal/secrets_loader.go | 10 +++ pkg/seal/secrets_loader_http.go | 138 ++++++++++++++++++++++++++++++++ pkg/types/secrets_source.go | 12 +++ pkg/types/url.go | 27 +++++++ pkg/utils/prompts.go | 29 +++++++ 5 files changed, 216 insertions(+) create mode 100644 pkg/seal/secrets_loader_http.go create mode 100644 pkg/types/url.go diff --git a/pkg/seal/secrets_loader.go b/pkg/seal/secrets_loader.go index ca4a0ac21..8241d9547 100644 --- a/pkg/seal/secrets_loader.go +++ b/pkg/seal/secrets_loader.go @@ -12,15 +12,23 @@ import ( "strings" ) +type usernamePassword struct { + username string + password string +} + type SecretsLoader struct { project *kluctl_project.KluctlProjectContext secretsDir string + + credentialsCache map[string]usernamePassword } func NewSecretsLoader(p *kluctl_project.KluctlProjectContext, secretsDir string) *SecretsLoader { return &SecretsLoader{ project: p, secretsDir: secretsDir, + credentialsCache: map[string]usernamePassword{}, } } @@ -29,6 +37,8 @@ func (s *SecretsLoader) LoadSecrets(source *types.SecretSource) (*uo.Unstructure return s.loadSecretsFile(source) } else if source.SystemEnvVars != nil { return s.loadSecretsSystemEnvs(source) + } else if source.Http != nil { + return s.loadSecretsHttp(source) } else if source.AwsSecretsManager != nil { return s.loadSecretsAwsSecretsManager(source) } else { diff --git a/pkg/seal/secrets_loader_http.go b/pkg/seal/secrets_loader_http.go new file mode 100644 index 000000000..5678d8e2d --- /dev/null +++ b/pkg/seal/secrets_loader_http.go @@ -0,0 +1,138 @@ +package seal + +import ( + "fmt" + "github.com/docker/distribution/registry/client/auth/challenge" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "io" + "io/ioutil" + "net/http" + "strings" +) + +func (s *SecretsLoader) doHttp(httpSource *types.SecretSourceHttp, username string, password string) (*http.Response, string, error) { + client := &http.Client{} + + method := "GET" + if httpSource.Method != nil { + method = *httpSource.Method + } + + var reqBody io.Reader + if httpSource.Body != nil { + reqBody = strings.NewReader(*httpSource.Body) + } + + req, err := http.NewRequest(method, httpSource.Url.String(), reqBody) + if err != nil { + return nil, "", err + } + + if username != "" || password != "" { + req.SetBasicAuth(username, password) + } + + for k, v := range httpSource.Headers { + req.Header.Set(k, v) + } + resp, err := client.Do(req) + if err != nil { + return nil, "", err + } + defer resp.Body.Close() + + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, "", err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return resp, string(respBody), fmt.Errorf("http request to %s failed with status code %d", httpSource.Url.String(), resp.StatusCode) + } + + return resp, string(respBody), nil +} + +func (s *SecretsLoader) loadSecretsHttp(source *types.SecretSource) (*uo.UnstructuredObject, error) { + resp, respBody, err := s.doHttp(source.Http, "", "") + if err != nil { + chgs := challenge.ResponseChallenges(resp) + if len(chgs) == 0 { + return nil, err + } + + var realms []string + for _, chg := range chgs { + if x, ok := chg.Parameters["realm"]; ok { + if x != "" { + realms = append(realms, x) + } + } + } + + credsKey := fmt.Sprintf("%s|%s", source.Http.Url.Host, strings.Join(realms, "+")) + creds, ok := s.credentialsCache[credsKey] + if !ok { + username, password, err := utils.AskForCredentials(fmt.Sprintf("Please enter credentials for host '%s'", source.Http.Url.Host)) + if err != nil { + return nil, err + } + creds = usernamePassword{ + username: username, + password: password, + } + s.credentialsCache[credsKey] = creds + } + + resp, respBody, err = s.doHttp(source.Http, creds.username, creds.password) + if err != nil { + return nil, err + } + } + + var respObj interface{} + var secrets *uo.UnstructuredObject + + err = yaml.ReadYamlString(respBody, &respObj) + if err != nil { + return nil, err + } + if err != nil { + return nil, err + } + if source.Http.JsonPath != nil { + p, err := uo.NewMyJsonPath(*source.Http.JsonPath) + if err != nil { + return nil, err + } + x, ok := p.GetFirst(respObj) + if !ok { + return nil, fmt.Errorf("%s not found in result from http request %s", *source.Http.JsonPath, source.Http.Url.String()) + } + s, ok := x.(string) + if !ok { + return nil, fmt.Errorf("%s in result of http request %s is not a string", *source.Http.JsonPath, source.Http.Url.String()) + } + secrets, err = uo.FromString(s) + if err != nil { + return nil, err + } + } else { + x, ok := respObj.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("result of http request %s is not an object", source.Http.Url.String()) + } + secrets = uo.FromMap(x) + } + secrets, ok, err := secrets.GetNestedObject("secrets") + if err != nil { + return nil, err + } + if !ok { + return uo.New(), nil + } + return secrets, nil +} diff --git a/pkg/types/secrets_source.go b/pkg/types/secrets_source.go index c29f63615..2575ecbde 100644 --- a/pkg/types/secrets_source.go +++ b/pkg/types/secrets_source.go @@ -5,6 +5,14 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) +type SecretSourceHttp struct { + Url YamlUrl `yaml:"url,omitempty" validate:"required"` + Method *string `yaml:"method,omitempty"` + Body *string `yaml:"body,omitempty"` + Headers map[string]string `yaml:"headers,omitempty"` + JsonPath *string `yaml:"jsonPath,omitempty"` +} + type SecretSourceAwsSecretsManager struct { // Name or ARN of the secret. In case a name is given, the region must be specified as well SecretName string `yaml:"secretName" validate:"required"` @@ -17,6 +25,7 @@ type SecretSourceAwsSecretsManager struct { type SecretSource struct { Path *string `yaml:"path,omitempty"` SystemEnvVars *uo.UnstructuredObject `yaml:"systemEnvVars,omitempty"` + Http *SecretSourceHttp `yaml:"http,omitempty"` AwsSecretsManager *SecretSourceAwsSecretsManager `yaml:"awsSecretsManager,omitempty"` } @@ -29,6 +38,9 @@ func ValidateSecretSource(sl validator.StructLevel) { if s.SystemEnvVars != nil { count += 1 } + if s.Http != nil { + count += 1 + } if s.AwsSecretsManager != nil { count += 1 } diff --git a/pkg/types/url.go b/pkg/types/url.go new file mode 100644 index 000000000..703f8dfba --- /dev/null +++ b/pkg/types/url.go @@ -0,0 +1,27 @@ +package types + +import ( + "net/url" +) + +type YamlUrl struct { + url.URL +} + +func (u *YamlUrl) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + err := unmarshal(&s) + if err != nil { + return err + } + u2, err := url.Parse(s) + if err != nil { + return err + } + u.URL = *u2 + return err +} + +func (u YamlUrl) MarshalYAML() (interface{}, error) { + return u.String(), nil +} diff --git a/pkg/utils/prompts.go b/pkg/utils/prompts.go index 5662fe1aa..06cecf885 100644 --- a/pkg/utils/prompts.go +++ b/pkg/utils/prompts.go @@ -1,6 +1,7 @@ package utils import ( + "bufio" "fmt" "github.com/mattn/go-isatty" log "github.com/sirupsen/logrus" @@ -64,3 +65,31 @@ func AskForPassword(prompt string) (string, error) { password := string(bytePassword) return strings.TrimSpace(password), nil } + +func AskForCredentials(prompt string) (string, string, error) { + if !isatty.IsTerminal(os.Stderr.Fd()) { + err := fmt.Errorf("not a terminal, suppressed credentials prompt: %s", prompt) + log.Warning(err) + return "", "", err + } + + reader := bufio.NewReader(os.Stdin) + + _, err := fmt.Fprintf(os.Stderr, prompt+"\n") + if err != nil { + return "", "", err + } + + fmt.Fprint(os.Stderr, "Enter Username: ") + username, err := reader.ReadString('\n') + if err != nil { + return "", "", err + } + + password, err := AskForPassword("Enter Password") + if err != nil { + return "", "", err + } + + return strings.TrimSpace(username), strings.TrimSpace(password), nil +} From 5db07dc5688fdab1ea6f44711ac4b15096b56bdc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 10:17:24 +0200 Subject: [PATCH 0079/2268] feat: Add NTLM support for http secrets --- go.mod | 1 + go.sum | 2 ++ pkg/seal/secrets_loader_http.go | 11 ++++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 6aec263c0..ab56a0893 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/kluctl/kluctl/v2 go 1.18 require ( + github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e github.com/alecthomas/kong v0.5.0 github.com/aws/aws-sdk-go v1.44.1 github.com/bitnami-labs/sealed-secrets v0.17.5 diff --git a/go.sum b/go.sum index 800475ba0..492559201 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ= +github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= diff --git a/pkg/seal/secrets_loader_http.go b/pkg/seal/secrets_loader_http.go index 5678d8e2d..25454a746 100644 --- a/pkg/seal/secrets_loader_http.go +++ b/pkg/seal/secrets_loader_http.go @@ -1,7 +1,9 @@ package seal import ( + "crypto/tls" "fmt" + "github.com/Azure/go-ntlmssp" "github.com/docker/distribution/registry/client/auth/challenge" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -14,7 +16,14 @@ import ( ) func (s *SecretsLoader) doHttp(httpSource *types.SecretSourceHttp, username string, password string) (*http.Response, string, error) { - client := &http.Client{} + client := &http.Client{ + Transport: ntlmssp.Negotiator{ + RoundTripper: &http.Transport{ + // This disables HTTP2.0 support, as it does not play well together with NTLM + TLSNextProto: make(map[string]func(string, *tls.Conn) http.RoundTripper), + }, + }, + } method := "GET" if httpSource.Method != nil { From 2f2dfcb553a7e7f8b0fdbfddf80406de19485a48 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 10:54:29 +0200 Subject: [PATCH 0080/2268] tests: Log validation errors in e2e tests --- e2e/utils.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/e2e/utils.go b/e2e/utils.go index 1a1abc672..622ac374f 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -3,8 +3,10 @@ package e2e import ( "bufio" "bytes" + "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" + log "github.com/sirupsen/logrus" "io" "os/exec" "reflect" @@ -37,6 +39,18 @@ func waitForReadiness(t *testing.T, k *KindCluster, namespace string, resource s if v.Ready { return true } + + if log.IsLevelEnabled(log.DebugLevel) { + errTxt := "" + for _, e := range v.Errors { + if errTxt != "" { + errTxt += "\n" + } + errTxt += fmt.Sprintf("%s: %s", e.Ref.String(), e.Error) + } + log.Debugf("validation failed. errors:\n%s", errTxt) + } + time.Sleep(1 * time.Second) } return false } From 074a253b67fb9dc53a8b1c569e0ab4bccb5d6b56 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 11:50:56 +0200 Subject: [PATCH 0081/2268] fix: Add timeout to git/load operations --- cmd/kluctl/args/project.go | 4 +++ cmd/kluctl/commands/utils.go | 6 ++++- pkg/git/mirrored_repo.go | 43 ++++++++++++++++-------------- pkg/kluctl_project/git.go | 21 ++++++++------- pkg/kluctl_project/load.go | 17 ++++++------ pkg/kluctl_project/load_targets.go | 11 ++++---- 6 files changed, 58 insertions(+), 44 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 8b6f0d583..d6f555799 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -1,5 +1,7 @@ package args +import "time" + type ProjectFlags struct { ProjectUrl string `group:"project" short:"p" help:"Git url of the kluctl project. If not specified, the current directory will be used instead of a remote Git project"` ProjectRef string `group:"project" short:"b" help:"Git ref of the kluctl project. Only used when --project-url was given."` @@ -12,6 +14,8 @@ type ProjectFlags struct { FromArchiveMetadata string `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." type:"existingfile"` OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file." type:"file"` Cluster string `group:"project" help:"Specify/Override cluster"` + + LoadTimeout time.Duration `group:"project" help:"Specify timeout for project loading. This will especially limit the time spent in git operations." default:"1m"` } type ArgsFlags struct { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index b8181e29c..3275ae470 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" @@ -12,6 +13,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" "os" + "time" ) func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl_project.KluctlProjectContext) error) error { @@ -48,7 +50,9 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl GitAuthProviders: auth.NewDefaultAuthProviders(), } - p, err := kluctl_project.LoadKluctlProject(loadArgs, tmpDir, j2) + loadCtx, cancel := context.WithDeadline(context.Background(), time.Now().Add(projectFlags.LoadTimeout)) + defer cancel() + p, err := kluctl_project.LoadKluctlProject(loadCtx, loadArgs, tmpDir, j2) if err != nil { return err } diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index bd7fe5672..b6db9ed33 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -1,6 +1,7 @@ package git import ( + "context" "fmt" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" @@ -13,6 +14,7 @@ import ( "os" "path/filepath" "strings" + "time" ) import "github.com/gofrs/flock" @@ -22,7 +24,6 @@ type MirroredGitRepo struct { url git_url.GitUrl mirrorDir string - hasLock bool hasUpdated bool fileLock *flock.Flock @@ -50,26 +51,28 @@ func (g *MirroredGitRepo) HasUpdated() bool { return g.hasUpdated } -func (g *MirroredGitRepo) Lock() error { - err := g.fileLock.Lock() +func (g *MirroredGitRepo) Lock(ctx context.Context) error { + ok, err := g.fileLock.TryLockContext(ctx, time.Millisecond*100) if err != nil { - return err + return fmt.Errorf("locking of %s failed: %w", g.fileLock.Path(), err) + } + if !ok { + return fmt.Errorf("locking of %s failed: unkown reason", g.fileLock.Path()) } - g.hasLock = true return nil } func (g *MirroredGitRepo) Unlock() error { err := g.fileLock.Unlock() if err != nil { + log.Warningf("Unlock of %s failed: %v", g.fileLock.Path(), err) return err } - g.hasLock = false return nil } -func (g *MirroredGitRepo) WithLock(cb func() error) error { - err := g.Lock() +func (g *MirroredGitRepo) WithLock(ctx context.Context, cb func() error) error { + err := g.Lock(ctx) if err != nil { return err } @@ -77,9 +80,9 @@ func (g *MirroredGitRepo) WithLock(cb func() error) error { return cb() } -func (g *MirroredGitRepo) MaybeWithLock(lock bool, cb func() error) error { +func (g *MirroredGitRepo) MaybeWithLock(ctx context.Context, lock bool, cb func() error) error { if lock { - return g.WithLock(cb) + return g.WithLock(ctx, cb) } return cb() } @@ -124,7 +127,7 @@ func (g *MirroredGitRepo) cleanupMirrorDir() error { return nil } -func (g *MirroredGitRepo) update(repoDir string, authProviders *auth2.GitAuthProviders) error { +func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProviders *auth2.GitAuthProviders) error { log.Infof("Updating mirror repo: url='%v'", g.url.String()) r, err := git.PlainOpen(repoDir) if err != nil { @@ -138,7 +141,7 @@ func (g *MirroredGitRepo) update(repoDir string, authProviders *auth2.GitAuthPro return err } - g.remoteRefs, err = remote.List(&git.ListOptions{ + g.remoteRefs, err = remote.ListContext(ctx, &git.ListOptions{ Auth: auth.AuthMethod, CABundle: auth.CABundle, }) @@ -150,7 +153,7 @@ func (g *MirroredGitRepo) update(repoDir string, authProviders *auth2.GitAuthPro remoteRefsMap[reference.Name()] = true } - err = remote.Fetch(&git.FetchOptions{ + err = remote.FetchContext(ctx, &git.FetchOptions{ Auth: auth.AuthMethod, CABundle: auth.CABundle, Progress: os.Stdout, @@ -197,10 +200,10 @@ func (g *MirroredGitRepo) update(repoDir string, authProviders *auth2.GitAuthPro return nil } -func (g *MirroredGitRepo) cloneOrUpdate(authProviders *auth2.GitAuthProviders) error { +func (g *MirroredGitRepo) cloneOrUpdate(ctx context.Context, authProviders *auth2.GitAuthProviders) error { initMarker := filepath.Join(g.mirrorDir, ".cache2.init") if utils.IsFile(initMarker) { - return g.update(g.mirrorDir, authProviders) + return g.update(ctx, g.mirrorDir, authProviders) } err := g.cleanupMirrorDir() if err != nil { @@ -232,7 +235,7 @@ func (g *MirroredGitRepo) cloneOrUpdate(authProviders *auth2.GitAuthProviders) e return err } - err = g.update(tmpMirrorDir, authProviders) + err = g.update(ctx, tmpMirrorDir, authProviders) if err != nil { return err } @@ -254,8 +257,8 @@ func (g *MirroredGitRepo) cloneOrUpdate(authProviders *auth2.GitAuthProviders) e return nil } -func (g *MirroredGitRepo) Update(authProviders *auth2.GitAuthProviders) error { - err := g.cloneOrUpdate(authProviders) +func (g *MirroredGitRepo) Update(ctx context.Context, authProviders *auth2.GitAuthProviders) error { + err := g.cloneOrUpdate(ctx, authProviders) if err != nil { return err } @@ -263,8 +266,8 @@ func (g *MirroredGitRepo) Update(authProviders *auth2.GitAuthProviders) error { return nil } -func (g *MirroredGitRepo) CloneProject(ref string, targetDir string) error { - if !g.hasLock || !g.hasUpdated { +func (g *MirroredGitRepo) CloneProject(ctx context.Context, ref string, targetDir string) error { + if !g.fileLock.Locked() || !g.hasUpdated { log.Fatalf("tried to clone from a project that is not locked/updated") } diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 93abdfc9b..acd7f2a7f 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -1,6 +1,7 @@ package kluctl_project import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/git" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" @@ -15,7 +16,7 @@ import ( "sync" ) -func (c *KluctlProjectContext) updateGitCaches() error { +func (c *KluctlProjectContext) updateGitCaches(ctx context.Context) error { var waitGroup sync.WaitGroup var firstError error var firstErrorLock sync.Mutex @@ -29,9 +30,9 @@ func (c *KluctlProjectContext) updateGitCaches() error { } doUpdateRepo := func(repo *git.MirroredGitRepo) error { - return repo.WithLock(func() error { + return repo.WithLock(ctx, func() error { if !repo.HasUpdated() { - return repo.Update(c.loadArgs.GitAuthProviders) + return repo.Update(ctx, c.loadArgs.GitAuthProviders) } return nil }) @@ -120,7 +121,7 @@ func (c *KluctlProjectContext) buildCloneDir(u git_url.GitUrl, ref string) (stri return cloneDir, nil } -func (c *KluctlProjectContext) cloneGitProject(gitProject types2.ExternalProject, defaultGitSubDir string, doAddInvolvedRepo bool, doLock bool) (result gitProjectInfo, err error) { +func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject types2.ExternalProject, defaultGitSubDir string, doAddInvolvedRepo bool, doLock bool) (result gitProjectInfo, err error) { err = os.MkdirAll(filepath.Join(c.TmpDir, "git"), 0o700) if err != nil { return @@ -147,21 +148,21 @@ func (c *KluctlProjectContext) cloneGitProject(gitProject types2.ExternalProject return } c.mirroredRepos[gitProject.Project.Url.NormalizedRepoKey()] = mr - err = mr.Lock() + err = mr.Lock(ctx) if err != nil { return } defer mr.Unlock() } - err = mr.MaybeWithLock(doLock, func() error { + err = mr.MaybeWithLock(ctx, doLock, func() error { if !mr.HasUpdated() { - err = mr.Update(c.loadArgs.GitAuthProviders) + err = mr.Update(ctx, c.loadArgs.GitAuthProviders) if err != nil { return err } } - return mr.CloneProject(gitProject.Project.Ref, targetDir) + return mr.CloneProject(ctx, gitProject.Project.Ref, targetDir) }) if err != nil { return @@ -192,7 +193,7 @@ func (c *KluctlProjectContext) localProject(dir string) gitProjectInfo { } } -func (c *KluctlProjectContext) cloneKluctlProject() (gitProjectInfo, error) { +func (c *KluctlProjectContext) cloneKluctlProject(ctx context.Context) (gitProjectInfo, error) { if c.loadArgs.ProjectUrl == nil { p, err := os.Getwd() if err != nil { @@ -200,7 +201,7 @@ func (c *KluctlProjectContext) cloneKluctlProject() (gitProjectInfo, error) { } return c.localProject(p), err } - return c.cloneGitProject(types2.ExternalProject{ + return c.cloneGitProject(ctx, types2.ExternalProject{ Project: &types2.GitProject{ Url: *c.loadArgs.ProjectUrl, Ref: c.loadArgs.ProjectRef, diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 48dffe726..1db05eb24 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -3,6 +3,7 @@ package kluctl_project import ( "archive/tar" "compress/gzip" + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/jinja2" @@ -56,8 +57,8 @@ func (c *KluctlProjectContext) getConfigPath(projectDir string) string { return configPath } -func (c *KluctlProjectContext) load(allowGit bool) error { - kluctlProjectInfo, err := c.cloneKluctlProject() +func (c *KluctlProjectContext) load(ctx context.Context, allowGit bool) error { + kluctlProjectInfo, err := c.cloneKluctlProject(ctx) if err != nil { return err } @@ -72,7 +73,7 @@ func (c *KluctlProjectContext) load(allowGit bool) error { } if allowGit { - err = c.updateGitCaches() + err = c.updateGitCaches(ctx) if err != nil { return err } @@ -115,7 +116,7 @@ func (c *KluctlProjectContext) load(allowGit bool) error { return gitProjectInfo{}, fmt.Errorf("tried to load something from git while it was not allowed") } - return c.cloneGitProject(*ep, defaultGitSubDir, true, true) + return c.cloneGitProject(ctx, *ep, defaultGitSubDir, true, true) } deploymentInfo, err := doClone(c.Config.Deployment, "", c.loadArgs.LocalDeployment) @@ -159,7 +160,7 @@ func (c *KluctlProjectContext) load(allowGit bool) error { return nil } -func LoadKluctlProject(args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*KluctlProjectContext, error) { +func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*KluctlProjectContext, error) { if args.FromArchive != "" { if args.ProjectUrl != nil || args.ProjectRef != "" || args.ProjectConfig != "" || args.LocalClusters != "" || args.LocalDeployment != "" || args.LocalSealedSecrets != "" { return nil, fmt.Errorf("--from-archive can not be combined with any other project related option") @@ -168,18 +169,18 @@ func LoadKluctlProject(args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jin if err != nil { return nil, err } - err = project.load(false) + err = project.load(ctx, false) if err != nil { return nil, err } return project, nil } else { p := NewKluctlProjectContext(args, tmpDir, j2) - err := p.load(true) + err := p.load(ctx, true) if err != nil { return nil, err } - err = p.loadTargets() + err = p.loadTargets(ctx) if err != nil { return nil, err } diff --git a/pkg/kluctl_project/load_targets.go b/pkg/kluctl_project/load_targets.go index 1d86d5d5e..bfafa94d5 100644 --- a/pkg/kluctl_project/load_targets.go +++ b/pkg/kluctl_project/load_targets.go @@ -1,6 +1,7 @@ package kluctl_project import ( + "context" "fmt" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/jinja2" @@ -26,7 +27,7 @@ type dynamicTargetInfo struct { defaultBranch string } -func (c *KluctlProjectContext) loadTargets() error { +func (c *KluctlProjectContext) loadTargets(ctx context.Context) error { targetNames := make(map[string]bool) c.DynamicTargets = nil @@ -39,7 +40,7 @@ func (c *KluctlProjectContext) loadTargets() error { targetInfos = append(targetInfos, l...) } - err := c.cloneDynamicTargets(targetInfos) + err := c.cloneDynamicTargets(ctx, targetInfos) if err != nil { return err } @@ -217,13 +218,13 @@ func (c *KluctlProjectContext) matchRef(s string, pattern string) (bool, string, } } -func (c *KluctlProjectContext) cloneDynamicTargets(dynamicTargets []*dynamicTargetInfo) error { +func (c *KluctlProjectContext) cloneDynamicTargets(ctx context.Context, dynamicTargets []*dynamicTargetInfo) error { wp := utils.NewDebuggerAwareWorkerPool(8) defer wp.StopWait(false) // lock all involved repos first for _, mr := range c.mirroredRepos { - err := mr.Lock() + err := mr.Lock(ctx) if err != nil { return err } @@ -249,7 +250,7 @@ func (c *KluctlProjectContext) cloneDynamicTargets(dynamicTargets []*dynamicTarg gitProject.Ref = *targetInfo.ref ep := types.ExternalProject{Project: &gitProject} - gi, err := c.cloneGitProject(ep, "", false, false) + gi, err := c.cloneGitProject(ctx, ep, "", false, false) mutex.Lock() defer mutex.Unlock() if err != nil { From 9f81fa8fd3b7ddd70540bfebf9b1596ebaa1a1e0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 12:00:53 +0200 Subject: [PATCH 0082/2268] tests: Show which resource failed in waitForReadiness --- e2e/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/utils.go b/e2e/utils.go index 622ac374f..e52c131ac 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -48,7 +48,7 @@ func waitForReadiness(t *testing.T, k *KindCluster, namespace string, resource s } errTxt += fmt.Sprintf("%s: %s", e.Ref.String(), e.Error) } - log.Debugf("validation failed. errors:\n%s", errTxt) + log.Debugf("validation failed for %s/%s. errors:\n%s", namespace, resource, errTxt) } time.Sleep(1 * time.Second) } From 75c514a8be6fd4f487300dd1bb9a366380154780 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 12:22:25 +0200 Subject: [PATCH 0083/2268] build: Less verbose CHANGELOGs --- .goreleaser.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 8967d7158..20c4e885e 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -57,9 +57,11 @@ changelog: sort: asc filters: exclude: - - '^docs:' - - '^test:' + - '^doc.*:' + - '^test.*:' - '^chore:' + - '^build:' + - '^refactor:' release: prerelease: auto From f99211ba0cce0e269f0813dc0df15bdeb228ecb5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 15:43:11 +0200 Subject: [PATCH 0084/2268] fix: Make working/project dir configurable while loading projects --- cmd/kluctl/commands/utils.go | 6 ++++++ pkg/kluctl_project/git.go | 6 +----- pkg/kluctl_project/project.go | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 3275ae470..8ad5e6ad0 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -38,7 +38,13 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl } defer j2.Close() + cwd, err := os.Getwd() + if err != nil { + return err + } + loadArgs := kluctl_project.LoadKluctlProjectArgs{ + ProjectDir: cwd, ProjectUrl: url, ProjectRef: projectFlags.ProjectRef, ProjectConfig: projectFlags.ProjectConfig, diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index acd7f2a7f..3a6b56a00 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -195,11 +195,7 @@ func (c *KluctlProjectContext) localProject(dir string) gitProjectInfo { func (c *KluctlProjectContext) cloneKluctlProject(ctx context.Context) (gitProjectInfo, error) { if c.loadArgs.ProjectUrl == nil { - p, err := os.Getwd() - if err != nil { - return gitProjectInfo{}, err - } - return c.localProject(p), err + return c.localProject(c.loadArgs.ProjectDir), nil } return c.cloneGitProject(ctx, types2.ExternalProject{ Project: &types2.GitProject{ diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index c44c64555..bacb21ae2 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -11,6 +11,7 @@ import ( ) type LoadKluctlProjectArgs struct { + ProjectDir string ProjectUrl *git_url.GitUrl ProjectRef string ProjectConfig string From 798a13e2a792338c2e346311795ce3693889207e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 17:31:22 +0200 Subject: [PATCH 0085/2268] refactor: Allow to pass clientConfig from outside --- cmd/kluctl/commands/cmd_downscale.go | 2 +- cmd/kluctl/commands/cmd_poke_images.go | 2 +- cmd/kluctl/commands/utils.go | 10 ++++++++- pkg/jinja2/vars.go | 2 +- pkg/k8s/k8s_cluster.go | 31 ++++++++------------------ pkg/kluctl_project/target_context.go | 10 +++++++-- 6 files changed, 29 insertions(+), 28 deletions(-) diff --git a/cmd/kluctl/commands/cmd_downscale.go b/cmd/kluctl/commands/cmd_downscale.go index e5383e3b2..7df0a744b 100644 --- a/cmd/kluctl/commands/cmd_downscale.go +++ b/cmd/kluctl/commands/cmd_downscale.go @@ -37,7 +37,7 @@ func (cmd *downscaleCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { - if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to downscale on context/cluster %s?", ctx.targetCtx.K.Context())) { + if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to downscale on context/cluster %s?", ctx.targetCtx.ClusterConfig.Cluster.Context)) { return fmt.Errorf("aborted") } } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 33c62ced3..a65f2e079 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -37,7 +37,7 @@ func (cmd *pokeImagesCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { - if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", ctx.targetCtx.K.Context())) { + if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", ctx.targetCtx.ClusterConfig.Cluster.Context)) { return fmt.Errorf("aborted") } } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 8ad5e6ad0..9c449aa91 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -12,6 +12,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" "os" "time" ) @@ -130,7 +132,13 @@ func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_pr renderOutputDir = tmpDir } - ctx, err := p.NewTargetContext(args.targetFlags.Target, args.projectFlags.Cluster, + clientConfigGetter := func(context string) (*rest.Config, error) { + configLoadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{CurrentContext: context} + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(configLoadingRules, configOverrides).ClientConfig() + } + + ctx, err := p.NewTargetContext(clientConfigGetter, args.targetFlags.Target, args.projectFlags.Cluster, args.dryRunArgs == nil || args.dryRunArgs.DryRun, optionArgs, forSeal, images, inclusion, renderOutputDir) diff --git a/pkg/jinja2/vars.go b/pkg/jinja2/vars.go index b130cafb3..1c42f4c38 100644 --- a/pkg/jinja2/vars.go +++ b/pkg/jinja2/vars.go @@ -97,7 +97,7 @@ func (vc *VarsCtx) loadVarsFromK8sObject(k *k8s.K8sCluster, ref k8s2.ObjectRef, return err } if !found { - return fmt.Errorf("key %s not found in %s on cluster %s", key, ref.String(), k.Context()) + return fmt.Errorf("key %s not found in %s on cluster", key, ref.String()) } err = vc.loadVarsFromString(value) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index d9d9aa93b..59871f052 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -21,7 +21,6 @@ import ( "k8s.io/client-go/metadata" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" "net/http" "net/url" "path/filepath" @@ -37,8 +36,7 @@ var ( ) type K8sCluster struct { - context string - DryRun bool + DryRun bool restConfig *rest.Config discovery *disk.CachedDiscoveryClient @@ -78,30 +76,22 @@ func (p *parallelClientEntry) HandleWarningHeader(code int, agent string, text s }) } -func NewK8sCluster(context string, dryRun bool) (*K8sCluster, error) { +func NewK8sCluster(configIn *rest.Config, dryRun bool) (*K8sCluster, error) { k := &K8sCluster{ - context: context, - DryRun: dryRun, + DryRun: dryRun, } - configLoadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - configOverrides := &clientcmd.ConfigOverrides{CurrentContext: context} + k.restConfig = rest.CopyConfig(configIn) + k.restConfig.QPS = 10 + k.restConfig.Burst = 20 - config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(configLoadingRules, configOverrides).ClientConfig() - if err != nil { - return nil, err - } - config.QPS = 10 - config.Burst = 20 - k.restConfig = config - - apiHost, err := url.Parse(config.Host) + apiHost, err := url.Parse(k.restConfig.Host) if err != nil { return nil, err } discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(), "kube-cache/discovery", apiHost.Hostname()) httpCacheDir := filepath.Join(utils.GetTmpBaseDir(), "kube-cache/http", apiHost.Hostname()) - discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(config), discoveryCacheDir, httpCacheDir, time.Hour*24) + discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(k.restConfig), discoveryCacheDir, httpCacheDir, time.Hour*24) if err != nil { return nil, err } @@ -111,6 +101,7 @@ func NewK8sCluster(context string, dryRun bool) (*K8sCluster, error) { k.clientPool = make(chan *parallelClientEntry, parallelClients) for i := 0; i < parallelClients; i++ { p := ¶llelClientEntry{} + config := rest.CopyConfig(k.restConfig) config.WarningHandler = p p.http, err = rest.HTTPClientFor(config) @@ -160,10 +151,6 @@ func (k *K8sCluster) ReadWrite() *K8sCluster { return &k2 } -func (k *K8sCluster) Context() string { - return k.context -} - func (k *K8sCluster) GetCA() []byte { return k.restConfig.CAData } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 257967bad..29b662338 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -8,6 +8,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "k8s.io/client-go/rest" "path/filepath" ) @@ -20,7 +21,7 @@ type TargetContext struct { DeploymentCollection *deployment.DeploymentCollection } -func (p *KluctlProjectContext) NewTargetContext(targetName string, clusterName string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { +func (p *KluctlProjectContext) NewTargetContext(clientConfigGetter func(context string) (*rest.Config, error), targetName string, clusterName string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { deploymentDir, err := filepath.Abs(p.DeploymentDir) if err != nil { return nil, err @@ -51,7 +52,12 @@ func (p *KluctlProjectContext) NewTargetContext(targetName string, clusterName s return nil, err } - k, err := k8s.NewK8sCluster(clusterConfig.Cluster.Context, dryRun) + clientConfig, err := clientConfigGetter(clusterConfig.Cluster.Context) + if err != nil { + return nil, err + } + + k, err := k8s.NewK8sCluster(clientConfig, dryRun) if err != nil { return nil, err } From ce2ff99362aba95a3c1e87969b71cc16e18d86b6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Apr 2022 17:31:39 +0200 Subject: [PATCH 0086/2268] fix: Ignore missing clusters/sealedSecrets dirs when creating an archive --- pkg/kluctl_project/load.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 1db05eb24..511a514fd 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -287,11 +287,15 @@ func (c *KluctlProjectContext) CreateTGZArchive(archivePath string, embedMetadat if err = utils.AddToTar(tw, c.DeploymentDir, "deployment", filter); err != nil { return err } - if err = utils.AddToTar(tw, c.ClustersDir, "clusters", filter); err != nil { - return err + if utils.Exists(c.ClustersDir) { + if err = utils.AddToTar(tw, c.ClustersDir, "clusters", filter); err != nil { + return err + } } - if err = utils.AddToTar(tw, c.SealedSecretsDir, "sealed-secrets", filter); err != nil { - return err + if utils.Exists(c.SealedSecretsDir) { + if err = utils.AddToTar(tw, c.SealedSecretsDir, "sealed-secrets", filter); err != nil { + return err + } } return nil From 1c97e464d4ce137532634d1588d58e3190c887bb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 29 Apr 2022 08:03:56 +0200 Subject: [PATCH 0087/2268] fix: Check for username instead of e.Username in BuildAuth --- pkg/git/auth/list_auth_provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index d469e500f..b929675af 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -53,7 +53,7 @@ func (a *ListAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { username = e.Username } - if e.Username == "*" { + if username == "*" { // can't use "*" as username continue } From 6d874e4ec742b6287331a0c67953a16597b54654 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 29 Apr 2022 08:29:09 +0200 Subject: [PATCH 0088/2268] fix: Actually write known_hosts file when given --- pkg/git/auth/ssh_known_hosts.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/git/auth/ssh_known_hosts.go b/pkg/git/auth/ssh_known_hosts.go index 6a4225e5e..e5d522d6e 100644 --- a/pkg/git/auth/ssh_known_hosts.go +++ b/pkg/git/auth/ssh_known_hosts.go @@ -65,6 +65,10 @@ func verifyHost(host string, remote net.Addr, key ssh.PublicKey, knownHosts []by _ = tmpFile.Close() _ = os.Remove(tmpFile.Name()) }() + _, err = tmpFile.Write(knownHosts) + if err != nil { + return err + } files = append(files, tmpFile.Name()) } From c6987e0affe47feabe0be34def470ef917fd1152 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 29 Apr 2022 13:32:14 +0200 Subject: [PATCH 0089/2268] refactor: Wrap ListImageTags into RegistryHelper --- .../commands/cmd_check_image_updates.go | 4 +- cmd/kluctl/commands/utils.go | 4 +- pkg/deployment/images.go | 6 +- pkg/registries/registries.go | 104 +++++++++--------- 4 files changed, 63 insertions(+), 55 deletions(-) diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index 8d65ee305..6fd22dfd7 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -37,6 +37,8 @@ func (cmd *checkImageUpdatesCmd) Run() error { return err } + rh := registries.NewRegistryHelper() + wg := utils.NewWorkerPoolWithErrors(8) defer wg.StopWait(false) @@ -52,7 +54,7 @@ func (cmd *checkImageUpdatesCmd) Run() error { repo := s[0] if _, ok := imageTags[repo]; !ok { wg.Submit(func() error { - tags, err := registries.ListImageTags(repo) + tags, err := rh.ListImageTags(repo) mutex.Lock() defer mutex.Unlock() if err != nil { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 9c449aa91..7b0af0754 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -9,6 +9,7 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/registries" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" @@ -100,7 +101,8 @@ func withProjectCommandContext(args projectTargetCommandArgs, cb func(ctx *comma } func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_project.KluctlProjectContext, forSeal bool, cb func(ctx *commandCtx) error) error { - images, err := deployment.NewImages(args.imageFlags.UpdateImages) + rh := registries.NewRegistryHelper() + images, err := deployment.NewImages(rh, args.imageFlags.UpdateImages) if err != nil { return err } diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 654c72025..076a211a6 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -18,6 +18,7 @@ import ( ) type Images struct { + rh *registries.RegistryHelper updateImages bool fixedImages []types.FixedImage seenImages []types.FixedImage @@ -26,8 +27,9 @@ type Images struct { registryCache utils.ThreadSafeMultiCache } -func NewImages(updateImages bool) (*Images, error) { +func NewImages(rh *registries.RegistryHelper, updateImages bool) (*Images, error) { return &Images{ + rh: rh, updateImages: updateImages, }, nil } @@ -73,7 +75,7 @@ func (images *Images) GetFixedImage(image string, namespace string, deployment s func (images *Images) GetLatestImageFromRegistry(image string, latestVersion string) (*string, error) { ret, err := images.registryCache.Get(image, "tag", func() (interface{}, error) { - return registries.ListImageTags(image) + return images.rh.ListImageTags(image) }) if err != nil { return nil, err diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 86ccb63e4..727453199 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -33,11 +33,24 @@ func (e *noAuthRetryError) Error() string { return e.msg } -func ListImageTags(image string) ([]string, error) { +type RegistryHelper struct { + cachedAuth utils.ThreadSafeCache + cachedResponses utils.ThreadSafeMultiCache + authRealms map[string]bool + authErrors map[string]bool + init sync.Once + mutex sync.Mutex +} + +func NewRegistryHelper() *RegistryHelper { + return &RegistryHelper{} +} + +func (rh *RegistryHelper) ListImageTags(image string) ([]string, error) { var nameOpts []name.Option remoteOpts := []remote.Option{ - remote.WithAuthFromKeychain(&globalMyKeychain), - remote.WithTransport(&globalMyKeychain), + remote.WithAuthFromKeychain(rh), + remote.WithTransport(rh), } repo, err := name.NewRepository(image, nameOpts...) @@ -64,18 +77,7 @@ func ListImageTags(image string) ([]string, error) { return ret, err } -var globalMyKeychain myKeychain - -type myKeychain struct { - cachedAuth utils.ThreadSafeCache - cachedResponses utils.ThreadSafeMultiCache - authRealms map[string]bool - authErrors map[string]bool - init sync.Once - mutex sync.Mutex -} - -func (kc *myKeychain) doResolve(resource authn.Resource) (authn.Authenticator, error) { +func (rh *RegistryHelper) doResolve(resource authn.Resource) (authn.Authenticator, error) { registry := resource.RegistryStr() for _, m := range utils.ParseEnvConfigSets("KLUCTL_REGISTRY") { @@ -93,11 +95,11 @@ func (kc *myKeychain) doResolve(resource authn.Resource) (authn.Authenticator, e return authn.DefaultKeychain.Resolve(resource) } -func (kc *myKeychain) Resolve(resource authn.Resource) (authn.Authenticator, error) { +func (rh *RegistryHelper) Resolve(resource authn.Resource) (authn.Authenticator, error) { registry := resource.RegistryStr() - ret, err := kc.cachedAuth.Get(registry, func() (interface{}, error) { - return kc.doResolve(resource) + ret, err := rh.cachedAuth.Get(registry, func() (interface{}, error) { + return rh.doResolve(resource) }) if err != nil { return nil, err @@ -105,15 +107,15 @@ func (kc *myKeychain) Resolve(resource authn.Resource) (authn.Authenticator, err return ret.(authn.Authenticator), nil } -func (kc *myKeychain) realmFromRequest(req *http.Request) string { +func (rh *RegistryHelper) realmFromRequest(req *http.Request) string { return fmt.Sprintf("%s://%s%s", req.URL.Scheme, req.URL.Host, req.URL.Path) } -func (kc *myKeychain) getCachePath(key string) string { +func (rh *RegistryHelper) getCachePath(key string) string { return filepath.Join(utils.GetTmpBaseDir(), "registries-cache", key[0:2], key[2:4], key) } -func (kc *myKeychain) checkInvalidToken(resBody []byte) bool { +func (rh *RegistryHelper) checkInvalidToken(resBody []byte) bool { j, err := uo.FromString(string(resBody)) if err != nil { return false @@ -139,8 +141,8 @@ func (kc *myKeychain) checkInvalidToken(resBody []byte) bool { return false } -func (kc *myKeychain) readCachedResponse(key string) []byte { - cachePath := kc.getCachePath(key) +func (rh *RegistryHelper) readCachedResponse(key string) []byte { + cachePath := rh.getCachePath(key) st, err := os.Stat(cachePath) if err != nil { @@ -163,7 +165,7 @@ func (kc *myKeychain) readCachedResponse(key string) []byte { if err != nil { return nil } - if kc.checkInvalidToken(jb) { + if rh.checkInvalidToken(jb) { return nil } } @@ -171,8 +173,8 @@ func (kc *myKeychain) readCachedResponse(key string) []byte { return b } -func (kc *myKeychain) writeCachedResponse(key string, data []byte) { - cachePath := kc.getCachePath(key) +func (rh *RegistryHelper) writeCachedResponse(key string, data []byte) { + cachePath := rh.getCachePath(key) if !utils.Exists(filepath.Dir(cachePath)) { err := os.MkdirAll(filepath.Dir(cachePath), 0o700) if err != nil { @@ -193,15 +195,15 @@ func (kc *myKeychain) writeCachedResponse(key string, data []byte) { } } -func (kc *myKeychain) RoundTripCached(req *http.Request, extraKey string, onNew func(res *http.Response) error) (*http.Response, error) { +func (rh *RegistryHelper) RoundTripCached(req *http.Request, extraKey string, onNew func(res *http.Response) error) (*http.Response, error) { key := fmt.Sprintf("%s\n%s\n%s\n", req.Host, req.URL.Path, extraKey) key = utils.Sha256String(key) isNew := false - resI, err := kc.cachedResponses.Get(req.Host, key, func() (interface{}, error) { + resI, err := rh.cachedResponses.Get(req.Host, key, func() (interface{}, error) { isNew = true - b := kc.readCachedResponse(key) + b := rh.readCachedResponse(key) if b == nil { res, err := remote.DefaultTransport.RoundTrip(req) if err != nil { @@ -213,7 +215,7 @@ func (kc *myKeychain) RoundTripCached(req *http.Request, extraKey string, onNew } if res.StatusCode < 500 { - kc.writeCachedResponse(key, b) + rh.writeCachedResponse(key, b) } } @@ -240,22 +242,22 @@ func (kc *myKeychain) RoundTripCached(req *http.Request, extraKey string, onNew return res, err } -func (kc *myKeychain) RoundTripInfoReq(req *http.Request) (*http.Response, error) { - return kc.RoundTripCached(req, "info", func(res *http.Response) error { - kc.mutex.Lock() - defer kc.mutex.Unlock() +func (rh *RegistryHelper) RoundTripInfoReq(req *http.Request) (*http.Response, error) { + return rh.RoundTripCached(req, "info", func(res *http.Response) error { + rh.mutex.Lock() + defer rh.mutex.Unlock() chgs := challenge.ResponseChallenges(res) for _, chg := range chgs { if realm, ok := chg.Parameters["realm"]; ok { - kc.authRealms[realm] = true + rh.authRealms[realm] = true } } return nil }) } -func (kc *myKeychain) RoundTripAuth(req *http.Request) (*http.Response, error) { +func (rh *RegistryHelper) RoundTripAuth(req *http.Request) (*http.Response, error) { b := bytes.NewBuffer(nil) err := req.Header.Write(b) if err != nil { @@ -266,42 +268,42 @@ func (kc *myKeychain) RoundTripAuth(req *http.Request) (*http.Response, error) { hash := utils.Sha256String(b.String()) - return kc.RoundTripCached(req, hash, func(res *http.Response) error { - kc.mutex.Lock() - defer kc.mutex.Unlock() + return rh.RoundTripCached(req, hash, func(res *http.Response) error { + rh.mutex.Lock() + defer rh.mutex.Unlock() if res.StatusCode == http.StatusUnauthorized || res.StatusCode == http.StatusForbidden { // if auth fails once for a registry, we must not retry any auth on that registry as we could easily run // into an IP block - kc.authErrors[kc.realmFromRequest(req)] = true + rh.authErrors[rh.realmFromRequest(req)] = true } return nil }) } -func (kc *myKeychain) RoundTrip(req *http.Request) (*http.Response, error) { - kc.init.Do(func() { - kc.authRealms = make(map[string]bool) - kc.authErrors = make(map[string]bool) +func (rh *RegistryHelper) RoundTrip(req *http.Request) (*http.Response, error) { + rh.init.Do(func() { + rh.authRealms = make(map[string]bool) + rh.authErrors = make(map[string]bool) }) if req.URL.Path == "/v2/" { - return kc.RoundTripInfoReq(req) + return rh.RoundTripInfoReq(req) } - kc.mutex.Lock() - realm := kc.realmFromRequest(req) - _, isAuthRealm := kc.authRealms[realm] - _, isAuthError := kc.authErrors[realm] - kc.mutex.Unlock() + rh.mutex.Lock() + realm := rh.realmFromRequest(req) + _, isAuthRealm := rh.authRealms[realm] + _, isAuthError := rh.authErrors[realm] + rh.mutex.Unlock() if isAuthError { return nil, &noAuthRetryError{fmt.Sprintf("previous auth request for %s gave an error, we won't retry", realm)} } if isAuthRealm { - return kc.RoundTripAuth(req) + return rh.RoundTripAuth(req) } resp, err := remote.DefaultTransport.RoundTrip(req) From 6a4a6b435e86ebaa1718a20ddfa3fa9a6dec923e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 29 Apr 2022 13:55:23 +0200 Subject: [PATCH 0090/2268] refactor: Allow passing credentials to RegistryHelper --- cmd/kluctl/commands/utils.go | 4 ++ pkg/registries/registries.go | 92 +++++++++++++++++++++++------------- 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 7b0af0754..ac4944473 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -102,6 +102,10 @@ func withProjectCommandContext(args projectTargetCommandArgs, cb func(ctx *comma func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_project.KluctlProjectContext, forSeal bool, cb func(ctx *commandCtx) error) error { rh := registries.NewRegistryHelper() + err := rh.ParseAuthEntriesFromEnv() + if err != nil { + return fmt.Errorf("failed to parse registry auth from environment: %w", err) + } images, err := deployment.NewImages(rh, args.imageFlags.UpdateImages) if err != nil { return err diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 727453199..2dca60875 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -34,6 +34,8 @@ func (e *noAuthRetryError) Error() string { } type RegistryHelper struct { + authEntries []AuthEntry + cachedAuth utils.ThreadSafeCache cachedResponses utils.ThreadSafeMultiCache authRealms map[string]bool @@ -42,6 +44,13 @@ type RegistryHelper struct { mutex sync.Mutex } +type AuthEntry struct { + Registry string + Username string + Password string + Insecure bool +} + func NewRegistryHelper() *RegistryHelper { return &RegistryHelper{} } @@ -58,7 +67,8 @@ func (rh *RegistryHelper) ListImageTags(image string) ([]string, error) { return nil, err } - if isRegistryInsecure(repo.RegistryStr()) { + e := rh.findAuthEntry(repo.RegistryStr()) + if e != nil && e.Insecure { nameOpts = append(nameOpts, name.Insecure) repo, err = name.NewRepository(image, nameOpts...) } @@ -77,20 +87,58 @@ func (rh *RegistryHelper) ListImageTags(image string) ([]string, error) { return ret, err } -func (rh *RegistryHelper) doResolve(resource authn.Resource) (authn.Authenticator, error) { - registry := resource.RegistryStr() +func (rh *RegistryHelper) AddAuthEntry(e AuthEntry) { + rh.authEntries = append(rh.authEntries, e) +} + +func (rh *RegistryHelper) ParseAuthEntriesFromEnv() error { + defaultTlsVerify := true + if x, ok := os.LookupEnv("KLUCTL_REGISTRY_DEFAULT_TLSVERIFY"); ok { + b, err := strconv.ParseBool(x) + if err != nil { + return fmt.Errorf("failed to parse KLUCTL_REGISTRY_DEFAULT_TLSVERIFY: %w", err) + } + defaultTlsVerify = b + } for _, m := range utils.ParseEnvConfigSets("KLUCTL_REGISTRY") { - host, _ := m["HOST"] - if host == registry { - username, _ := m["USERNAME"] - password, _ := m["PASSWORD"] - return authn.FromConfig(authn.AuthConfig{ - Username: username, - Password: password, - }), nil + e := AuthEntry{ + Registry: m["HOST"], + Username: m["USERNAME"], + Password: m["PASSWORD"], + Insecure: !defaultTlsVerify, + } + tlsverifyStr, ok := m["TLSVERIFY"] + if ok { + tlsverify, err := strconv.ParseBool(tlsverifyStr) + if err != nil { + return fmt.Errorf("failed to parse TLSVERIFY from env: %w", err) + } + e.Insecure = !tlsverify + } + + rh.AddAuthEntry(e) + } + return nil +} + +func (rh *RegistryHelper) findAuthEntry(registry string) *AuthEntry { + for _, e := range rh.authEntries { + if e.Registry == "*" || e.Registry == registry { + return &e } } + return nil +} + +func (rh *RegistryHelper) doResolve(resource authn.Resource) (authn.Authenticator, error) { + e := rh.findAuthEntry(resource.RegistryStr()) + if e != nil { + return authn.FromConfig(authn.AuthConfig{ + Username: e.Username, + Password: e.Password, + }), nil + } return authn.DefaultKeychain.Resolve(resource) } @@ -309,25 +357,3 @@ func (rh *RegistryHelper) RoundTrip(req *http.Request) (*http.Response, error) { resp, err := remote.DefaultTransport.RoundTrip(req) return resp, err } - -func isRegistryInsecure(registry string) bool { - for _, m := range utils.ParseEnvConfigSets("KLUCTL_REGISTRY") { - host, _ := m["HOST"] - if host == registry { - tlsverifyStr, ok := m["TLSVERIFY"] - if ok { - tlsverify, err := strconv.ParseBool(tlsverifyStr) - if err == nil { - return !tlsverify - } - } - } - } - - if x, ok := os.LookupEnv("KLUCTL_REGISTRY_DEFAULT_TLSVERIFY"); ok { - if b, err := strconv.ParseBool(x); err == nil { - return !b - } - } - return false -} From fb064e9ca8411908d556a78705d9b01c36bc68ad Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 29 Apr 2022 14:57:12 +0200 Subject: [PATCH 0091/2268] fix: Allow to configure CA bundles for registries --- pkg/registries/registries.go | 89 ++++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 13 deletions(-) diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 2dca60875..b8123fd25 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -3,6 +3,8 @@ package registries import ( "bufio" "bytes" + "context" + "crypto/x509" "fmt" "github.com/docker/distribution/registry/client/auth/challenge" "github.com/golang-jwt/jwt/v4" @@ -33,21 +35,27 @@ func (e *noAuthRetryError) Error() string { return e.msg } +type transportKeyType int + +var transportKey transportKeyType + type RegistryHelper struct { authEntries []AuthEntry - cachedAuth utils.ThreadSafeCache - cachedResponses utils.ThreadSafeMultiCache - authRealms map[string]bool - authErrors map[string]bool - init sync.Once - mutex sync.Mutex + cachedTransports utils.ThreadSafeCache + cachedAuth utils.ThreadSafeCache + cachedResponses utils.ThreadSafeMultiCache + authRealms map[string]bool + authErrors map[string]bool + init sync.Once + mutex sync.Mutex } type AuthEntry struct { Registry string Username string Password string + CABundle []byte Insecure bool } @@ -57,16 +65,22 @@ func NewRegistryHelper() *RegistryHelper { func (rh *RegistryHelper) ListImageTags(image string) ([]string, error) { var nameOpts []name.Option - remoteOpts := []remote.Option{ - remote.WithAuthFromKeychain(rh), - remote.WithTransport(rh), + repo, err := name.NewRepository(image, nameOpts...) + if err != nil { + return nil, err } - repo, err := name.NewRepository(image, nameOpts...) + t, err := rh.buildTransport(repo.RegistryStr()) if err != nil { return nil, err } + remoteOpts := []remote.Option{ + remote.WithAuthFromKeychain(rh), + remote.WithTransport(rh), + remote.WithContext(context.WithValue(context.Background(), transportKey, t)), + } + e := rh.findAuthEntry(repo.RegistryStr()) if e != nil && e.Insecure { nameOpts = append(nameOpts, name.Insecure) @@ -117,6 +131,17 @@ func (rh *RegistryHelper) ParseAuthEntriesFromEnv() error { e.Insecure = !tlsverify } + ca_bundle_path := m["CA_BUNDLE"] + if ca_bundle_path != "" { + ca_bundle_path = utils.ExpandPath(ca_bundle_path) + b, err := ioutil.ReadFile(ca_bundle_path) + if err != nil { + return fmt.Errorf("failed to read ca bundle %s: %w", ca_bundle_path, err) + } else { + e.CABundle = b + } + } + rh.AddAuthEntry(e) } return nil @@ -131,6 +156,45 @@ func (rh *RegistryHelper) findAuthEntry(registry string) *AuthEntry { return nil } +func (rh *RegistryHelper) loadCA(registry string) (*x509.CertPool, error) { + e := rh.findAuthEntry(registry) + if e == nil || e.CABundle == nil { + return nil, nil + } + + p := x509.NewCertPool() + if !p.AppendCertsFromPEM(e.CABundle) { + return nil, fmt.Errorf("failed to load CA for %s", registry) + } + + return p, nil +} + +func (rh *RegistryHelper) buildTransport(registry string) (http.RoundTripper, error) { + ret, err := rh.cachedTransports.Get(registry, func() (interface{}, error) { + ca, err := rh.loadCA(registry) + if err != nil { + return nil, err + } + if ca == nil { + return remote.DefaultTransport, nil + } + + t := remote.DefaultTransport.Clone() + t.TLSClientConfig.RootCAs = ca + return t, nil + }) + if err != nil { + return nil, err + } + return ret.(http.RoundTripper), nil +} + +func (rh *RegistryHelper) getTransport(req *http.Request) http.RoundTripper { + t := req.Context().Value(transportKey).(http.RoundTripper) + return t +} + func (rh *RegistryHelper) doResolve(resource authn.Resource) (authn.Authenticator, error) { e := rh.findAuthEntry(resource.RegistryStr()) if e != nil { @@ -253,7 +317,7 @@ func (rh *RegistryHelper) RoundTripCached(req *http.Request, extraKey string, on b := rh.readCachedResponse(key) if b == nil { - res, err := remote.DefaultTransport.RoundTrip(req) + res, err := rh.getTransport(req).RoundTrip(req) if err != nil { return nil, err } @@ -354,6 +418,5 @@ func (rh *RegistryHelper) RoundTrip(req *http.Request) (*http.Response, error) { return rh.RoundTripAuth(req) } - resp, err := remote.DefaultTransport.RoundTrip(req) - return resp, err + return rh.getTransport(req).RoundTrip(req) } From df53812fcb12d9c5e873524d0b934f957556c9de Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 29 Apr 2022 18:31:14 +0200 Subject: [PATCH 0092/2268] fix: Also support "auth" field in registry auth --- pkg/registries/registries.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index b8123fd25..da1c32496 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -55,6 +55,7 @@ type AuthEntry struct { Registry string Username string Password string + Auth string CABundle []byte Insecure bool } @@ -120,6 +121,7 @@ func (rh *RegistryHelper) ParseAuthEntriesFromEnv() error { Registry: m["HOST"], Username: m["USERNAME"], Password: m["PASSWORD"], + Auth: m["AUTH"], Insecure: !defaultTlsVerify, } tlsverifyStr, ok := m["TLSVERIFY"] @@ -201,6 +203,7 @@ func (rh *RegistryHelper) doResolve(resource authn.Resource) (authn.Authenticato return authn.FromConfig(authn.AuthConfig{ Username: e.Username, Password: e.Password, + Auth: e.Auth, }), nil } From 5cee2a141b3d7020bff55914e242f4bb806e7f0a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 2 May 2022 16:43:18 +0200 Subject: [PATCH 0093/2268] feat: Switch from kong to cobra --- cmd/kluctl/args/cobra_types.go | 87 +++++++ cmd/kluctl/args/images.go | 8 +- cmd/kluctl/args/misc.go | 2 +- cmd/kluctl/args/project.go | 16 +- cmd/kluctl/commands/cmd_render.go | 2 +- cmd/kluctl/commands/cobra_utils.go | 305 ++++++++++++++++++++++++ cmd/kluctl/commands/root.go | 85 +++---- cmd/kluctl/commands/utils.go | 16 +- go.mod | 16 +- go.sum | 12 +- pkg/deployment/deployment_collection.go | 5 +- pkg/deployment/utils/errors_holder.go | 5 +- pkg/jinja2/jinja2.go | 8 +- pkg/utils/errorlist.go | 5 +- pkg/utils/workerpool.go | 2 +- 15 files changed, 486 insertions(+), 88 deletions(-) create mode 100644 cmd/kluctl/args/cobra_types.go create mode 100644 cmd/kluctl/commands/cobra_utils.go diff --git a/cmd/kluctl/args/cobra_types.go b/cmd/kluctl/args/cobra_types.go new file mode 100644 index 000000000..7989383a1 --- /dev/null +++ b/cmd/kluctl/args/cobra_types.go @@ -0,0 +1,87 @@ +package args + +import ( + "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils" +) + +type existingPathType string + +func (s *existingPathType) Set(val string) error { + if val != "-" { + val = utils.ExpandPath(val) + } + if !utils.Exists(val) { + return fmt.Errorf("%s does not exist", val) + } + *s = existingPathType(val) + return nil +} +func (s *existingPathType) Type() string { + return "existingpath" +} + +func (s *existingPathType) String() string { return string(*s) } + +type existingFileType string + +func (s *existingFileType) Set(val string) error { + if val != "-" { + val = utils.ExpandPath(val) + } + if !utils.Exists(val) { + return fmt.Errorf("%s does not exist", val) + } + if utils.IsDirectory(val) { + return fmt.Errorf("%s exists but is a directory", val) + } + *s = existingFileType(val) + return nil +} +func (s *existingFileType) Type() string { + return "existingfile" +} + +func (s *existingFileType) String() string { return string(*s) } + +type existingDirType string + +func (s *existingDirType) Set(val string) error { + if val != "-" { + val = utils.ExpandPath(val) + } + if !utils.Exists(val) { + return fmt.Errorf("%s does not exist", val) + } + if !utils.IsDirectory(val) { + return fmt.Errorf("%s exists but is not a directory", val) + } + *s = existingDirType(val) + return nil +} +func (s *existingDirType) Type() string { + return "existingdir" +} + +func (s *existingDirType) String() string { return string(*s) } + +type pathType string + +func (s *pathType) Set(val string) error { + if val != "-" { + val = utils.ExpandPath(val) + } + if !utils.Exists(val) { + return fmt.Errorf("%s does not exist", val) + } + if !utils.IsDirectory(val) { + return fmt.Errorf("%s exists but is not a directory", val) + } + *s = pathType(val) + return nil +} +func (s *pathType) Type() string { + return "path" +} + +func (s *pathType) String() string { return string(*s) } diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index bb9de4568..3cdcdb9f7 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -8,16 +8,16 @@ import ( ) type ImageFlags struct { - FixedImage []string `group:"images" short:"F" help:"Pin an image to a given version. Expects '--fixed-image=image<:namespace:deployment:container>=result'"` - FixedImagesFile string `group:"images" help:"Use .yml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format" type:"existingfile"` - UpdateImages bool `group:"images" short:"u" help:"This causes kluctl to prefer the latest image found in registries, based on the 'latest_image' filters provided to 'images.get_image(...)' calls. Use this flag if you want to update to the latest versions/tags of all images. '-u' takes precedence over '--fixed-image/--fixed-images-file', meaning that the latest images are used even if an older image is given via fixed images."` + FixedImage []string `group:"images" short:"F" help:"Pin an image to a given version. Expects '--fixed-image=image<:namespace:deployment:container>=result'"` + FixedImagesFile existingFileType `group:"images" help:"Use .yml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format"` + UpdateImages bool `group:"images" short:"u" help:"This causes kluctl to prefer the latest image found in registries, based on the 'latest_image' filters provided to 'images.get_image(...)' calls. Use this flag if you want to update to the latest versions/tags of all images. '-u' takes precedence over '--fixed-image/--fixed-images-file', meaning that the latest images are used even if an older image is given via fixed images."` } func (args *ImageFlags) LoadFixedImagesFromArgs() ([]types.FixedImage, error) { var ret types.FixedImagesConfig if args.FixedImagesFile != "" { - err := yaml.ReadYamlFile(args.FixedImagesFile, &ret) + err := yaml.ReadYamlFile(args.FixedImagesFile.String(), &ret) if err != nil { return nil, err } diff --git a/cmd/kluctl/args/misc.go b/cmd/kluctl/args/misc.go index a9fc026a8..c4972ca44 100644 --- a/cmd/kluctl/args/misc.go +++ b/cmd/kluctl/args/misc.go @@ -44,5 +44,5 @@ type OutputFlags struct { } type RenderOutputDirFlags struct { - RenderOutputDir string `group:"misc" help:"Specifies the target directory to render the project into. If omitted, a temporary directory is used." type:"path"` + RenderOutputDir pathType `group:"misc" help:"Specifies the target directory to render the project into. If omitted, a temporary directory is used."` } diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index d6f555799..167f04ea7 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -6,14 +6,14 @@ type ProjectFlags struct { ProjectUrl string `group:"project" short:"p" help:"Git url of the kluctl project. If not specified, the current directory will be used instead of a remote Git project"` ProjectRef string `group:"project" short:"b" help:"Git ref of the kluctl project. Only used when --project-url was given."` - ProjectConfig string `group:"project" short:"c" help:"Location of the .kluctl.yml config file. Defaults to $PROJECT/.kluctl.yml" type:"existingfile"` - LocalClusters string `group:"project" help:"Local clusters directory. Overrides the project from .kluctl.yml" type:"existingdir"` - LocalDeployment string `group:"project" help:"Local deployment directory. Overrides the project from .kluctl.yml" type:"existingdir"` - LocalSealedSecrets string `group:"project" help:"Local sealed-secrets directory. Overrides the project from .kluctl.yml" type:"existingdir"` - FromArchive string `group:"project" help:"Load project (.kluctl.yml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." type:"existing"` - FromArchiveMetadata string `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." type:"existingfile"` - OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file." type:"file"` - Cluster string `group:"project" help:"Specify/Override cluster"` + ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yml config file. Defaults to $PROJECT/.kluctl.yml"` + LocalClusters existingDirType `group:"project" help:"Local clusters directory. Overrides the project from .kluctl.yml"` + LocalDeployment existingDirType `group:"project" help:"Local deployment directory. Overrides the project from .kluctl.yml"` + LocalSealedSecrets existingDirType `group:"project" help:"Local sealed-secrets directory. Overrides the project from .kluctl.yml" ` + FromArchive existingPathType `group:"project" help:"Load project (.kluctl.yml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents."` + FromArchiveMetadata existingFileType `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." ` + OutputMetadata pathType `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` + Cluster string `group:"project" help:"Specify/Override cluster"` LoadTimeout time.Duration `group:"project" help:"Specify timeout for project loading. This will especially limit the time spent in git operations." default:"1m"` } diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index a686da313..849175838 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -26,7 +26,7 @@ func (cmd *renderCmd) Run() error { if err != nil { return err } - cmd.RenderOutputDir = p + _ = cmd.RenderOutputDir.Set(p) } ptArgs := projectTargetCommandArgs{ diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go new file mode 100644 index 000000000..083e07693 --- /dev/null +++ b/cmd/kluctl/commands/cobra_utils.go @@ -0,0 +1,305 @@ +package commands + +import ( + "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode" +) + +type helpProvider interface { + Help() string +} + +type runProvider interface { + Run() error +} + +type rootCommand struct { + rootCmd *commandAndGroups + groupInfos []groupInfo +} + +type commandAndGroups struct { + parent *commandAndGroups + cmd *cobra.Command + groups map[string]string +} + +type groupInfo struct { + group string + title string + description string +} + +func buildRootCobraCmd(cmdStruct interface{}, name string, short string, long string, groupInfos []groupInfo) (*cobra.Command, error) { + c := &rootCommand{ + groupInfos: groupInfos, + } + + cmd, err := c.buildCobraCmd(nil, cmdStruct, name, short, long) + if err != nil { + return nil, err + } + c.rootCmd = cmd + + return cmd.cmd, nil +} + +func (c *rootCommand) buildCobraCmd(parent *commandAndGroups, cmdStruct interface{}, name string, short string, long string) (*commandAndGroups, error) { + cg := &commandAndGroups{ + parent: parent, + groups: map[string]string{}, + cmd: &cobra.Command{ + Use: name, + Short: short, + Long: long, + }, + } + + runP, ok := cmdStruct.(runProvider) + if ok { + cg.cmd.RunE = func(cmd *cobra.Command, args []string) error { + return runP.Run() + } + } + + err := c.buildCobraSubCommands(cg, cmdStruct) + if err != nil { + return nil, err + } + err = c.buildCobraArgs(cg, cmdStruct) + if err != nil { + return nil, err + } + + cg.cmd.SetHelpFunc(func(command *cobra.Command, i []string) { + c.helpFunc(cg) + }) + + return cg, nil +} + +func (c *rootCommand) buildCobraSubCommands(cg *commandAndGroups, cmdStruct interface{}) error { + v := reflect.ValueOf(cmdStruct).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + v2 := v.Field(i).Addr().Interface() + name := buildCobraName(f.Name) + + if _, ok := f.Tag.Lookup("cmd"); ok { + // subcommand + shortHelp := f.Tag.Get("help") + longHelp := "" + if hp, ok := v2.(helpProvider); ok { + longHelp = hp.Help() + } + + c2, err := c.buildCobraCmd(cg, v2, name, shortHelp, longHelp) + if err != nil { + return err + } + cg.cmd.AddCommand(c2.cmd) + } + } + return nil +} + +func (c *rootCommand) buildCobraArgs(cg *commandAndGroups, cmdStruct interface{}) error { + v := reflect.ValueOf(cmdStruct).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if _, ok := f.Tag.Lookup("cmd"); ok { + continue + } + + err := c.buildCobraArg(cg, f, v.Field(i)) + if err != nil { + return err + } + } + return nil +} + +func (c *rootCommand) buildCobraArg(cg *commandAndGroups, f reflect.StructField, v reflect.Value) error { + v2 := v.Addr().Interface() + name := buildCobraName(f.Name) + + help := f.Tag.Get("help") + shortFlag := f.Tag.Get("short") + defaultValue := f.Tag.Get("default") + + group := f.Tag.Get("group") + if group != "" { + cg.groups[name] = group + } + + switch v2.(type) { + case pflag.Value: + cg.cmd.PersistentFlags().VarPF(v2.(pflag.Value), name, shortFlag, help) + case *string: + cg.cmd.PersistentFlags().StringVarP(v2.(*string), name, shortFlag, defaultValue, help) + case *[]string: + if defaultValue != "" { + return fmt.Errorf("default not supported for slices") + } + cg.cmd.PersistentFlags().StringSliceVarP(v2.(*[]string), name, shortFlag, nil, help) + case *bool: + parsedDefault := false + if defaultValue != "" { + x, err := strconv.ParseBool(defaultValue) + if err != nil { + return err + } + parsedDefault = x + } + cg.cmd.PersistentFlags().BoolVarP(v2.(*bool), name, shortFlag, parsedDefault, help) + case *time.Duration: + var parsedDefault time.Duration + if defaultValue != "" { + x, err := time.ParseDuration(defaultValue) + if err != nil { + return err + } + parsedDefault = x + } + cg.cmd.PersistentFlags().DurationVarP(v2.(*time.Duration), name, shortFlag, parsedDefault, help) + default: + if f.Anonymous { + return c.buildCobraArgs(cg, v2) + } + return fmt.Errorf("unknown type %s", f.Type.Name()) + } + + return nil +} + +func copyViperValuesToCobraCmd(cmd *cobra.Command) error { + for cmd != nil { + err := copyViperValuesToCobraFlags(cmd.PersistentFlags()) + if err != nil { + return err + } + cmd = cmd.Parent() + } + return nil +} + +func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { + var retErr []error + flags.VisitAll(func(flag *pflag.Flag) { + if flag.Changed { + return + } + v := viper.Get(flag.Name) + if v != nil { + s, ok := v.(string) + if !ok { + retErr = append(retErr, fmt.Errorf("viper flag %s is not a string", flag.Name)) + return + } + err := flag.Value.Set(s) + if err != nil { + retErr = append(retErr, err) + } + } + }) + return utils.NewErrorListOrNil(retErr) +} + +func (c *rootCommand) helpFunc(cg *commandAndGroups) { + cmd := cg.cmd + + termWidth := utils.GetTermWidth() + + h := "Usage: " + if cmd.Runnable() { + h += cmd.UseLine() + } + if cmd.HasAvailableSubCommands() { + h += fmt.Sprintf("%s [command]", cmd.CommandPath()) + } + + h += "\n\n" + if cmd.Short != "" { + h += strings.TrimRightFunc(cmd.Short, unicode.IsSpace) + "\n" + } + if cmd.Long != "" { + h += strings.TrimRightFunc(cmd.Long, unicode.IsSpace) + "\n" + } + + flagsByGroups := c.buildGroupedFlagSets(cg) + + for _, g := range c.groupInfos { + fl, ok := flagsByGroups[g.group] + if !ok { + continue + } + h += "\n" + h += g.title + "\n" + if g.description != "" { + h += " " + g.description + "\n\n" + } + usages := fl.FlagUsagesWrapped(termWidth) + usages = strings.TrimRightFunc(usages, unicode.IsSpace) + usages += "\n" + h += usages + } + + if cmd.HasAvailableSubCommands() { + h += "\nCommands:\n" + for _, subCmd := range cmd.Commands() { + h += " " + rpad(subCmd.Name(), subCmd.NamePadding()) + " " + subCmd.Short + "\n" + } + h += fmt.Sprintf("\nUse \"%s [command] --help\" for more information about a command.\n", cmd.CommandPath()) + } + + _, _ = cmd.OutOrStdout().Write([]byte(h)) +} + +func (c *rootCommand) buildGroupedFlagSets(cg *commandAndGroups) map[string]*pflag.FlagSet { + flagsByGroups := make(map[string]*pflag.FlagSet) + + x := cg + for x != nil { + x.cmd.PersistentFlags().VisitAll(func(flag *pflag.Flag) { + group, ok := x.groups[flag.Name] + if !ok { + log.Panicf("group for %s not found", flag.Name) + } + fl, ok := flagsByGroups[group] + if !ok { + fl = pflag.NewFlagSet(group, pflag.PanicOnError) + flagsByGroups[group] = fl + } + fl.AddFlag(flag) + }) + x = x.parent + } + + return flagsByGroups +} + +func rpad(s string, padding int) string { + template := fmt.Sprintf("%%-%ds", padding) + return fmt.Sprintf(template, s) +} + +var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") +var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") + +func buildCobraName(fieldName string) string { + n := matchFirstCap.ReplaceAllString(fieldName, "${1}-${2}") + n = matchAllCap.ReplaceAllString(n, "${1}-${2}") + return strings.ToLower(n) +} diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 9cfa4bf32..68a8fbb2a 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -16,13 +16,14 @@ limitations under the License. package commands import ( - "github.com/alecthomas/kong" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/version" "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" "net/http" "os" "path/filepath" @@ -55,12 +56,12 @@ type cli struct { Version versionCmd `cmd:"" help:"Print kluctl version"` } -var flagGroups = []kong.Group{ - {Key: "project", Title: "Project arguments:", Description: "Define where and how to load the kluctl project and its components from."}, - {Key: "images", Title: "Image arguments:", Description: "Control fixed images and update behaviour."}, - {Key: "inclusion", Title: "Inclusion/Exclusion arguments:", Description: "Control inclusion/exclusion."}, - {Key: "misc", Title: "Misc arguments:", Description: "Command specific arguments."}, - {Key: "global", Title: "Global arguments:"}, +var flagGroups = []groupInfo{ + {group: "global", title: "Global arguments:"}, + {group: "project", title: "Project arguments:", description: "Define where and how to load the kluctl project and its components from."}, + {group: "images", title: "Image arguments:", description: "Control fixed images and update behaviour."}, + {group: "inclusion", title: "Inclusion/Exclusion arguments:", description: "Control inclusion/exclusion."}, + {group: "misc", title: "Misc arguments:", description: "Command specific arguments."}, } var globalFlagGroup = &flagGroups[len(flagGroups)-1] @@ -125,7 +126,7 @@ func (c *cli) checkNewVersion() { } } -func (c *cli) AfterApply() error { +func (c *cli) preRun() error { if err := c.setupLogs(); err != nil { return err } @@ -133,50 +134,50 @@ func (c *cli) AfterApply() error { return nil } -func (c *cli) Help() string { - return ` -Deploy and manage complex deployments on Kubernetes +func initViper() { + viper.SetConfigName("config") + viper.SetConfigType("yaml") + viper.AddConfigPath("/etc/kluctl/") + viper.AddConfigPath("$HOME/.kluctl") + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + log.Error(err) + os.Exit(1) + } + } -The missing glue to put together large Kubernetes deployments, -composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unified way.` + viper.SetEnvPrefix("kluctl") + viper.AutomaticEnv() } -func ParseArgs(args []string, options ...kong.Option) (*kong.Kong, *kong.Context, error) { - var cli cli +func Execute() { + root := cli{} + rootCmd, err := buildRootCobraCmd(&root, "kluctl", + "Deploy and manage complex deployments on Kubernetes", + `The missing glue to put together large Kubernetes deployments, +composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unified way.`, + flagGroups) - helpOption := kong.HelpOptions{ - Compact: true, - Summary: true, - WrapUpperBound: 120, + if err != nil { + log.Fatal(err) } - var options2 []kong.Option - options2 = append(options2, helpOption) - options2 = append(options2, kong.ExplicitGroups(flagGroups)) - options2 = append(options2, kong.ShortUsageOnError()) - options2 = append(options2, kong.Name("kluctl")) - options2 = append(options2, options...) + rootCmd.Version = version.GetVersion() + rootCmd.SilenceUsage = true - parser, err := kong.New(&cli, options2...) - if err != nil { - panic(err) + rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + err := copyViperValuesToCobraCmd(cmd) + if err != nil { + return err + } + return root.preRun() } - parser.Model.HelpFlag.Group = globalFlagGroup - ctx, err := parser.Parse(args) - return parser, ctx, err -} -func Execute() { - confOption := kong.Configuration(kong.JSON, "/etc/kluctl.json", "~/.kluctl/config.json") - envOption := kong.DefaultEnvars("KLUCTL") - parser, _, err := ExecuteWithArgs(os.Args[1:], confOption, envOption) - parser.FatalIfErrorf(err) -} + initViper() -func ExecuteWithArgs(args []string, options ...kong.Option) (*kong.Kong, *kong.Context, error) { - parser, ctx, err := ParseArgs(args, options...) + err = rootCmd.Execute() if err != nil { - return parser, ctx, err + log.Errorf("%v", err) + os.Exit(1) } - return parser, ctx, ctx.Run() } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index ac4944473..ddbcc2b21 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -50,12 +50,12 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl ProjectDir: cwd, ProjectUrl: url, ProjectRef: projectFlags.ProjectRef, - ProjectConfig: projectFlags.ProjectConfig, - LocalClusters: projectFlags.LocalClusters, - LocalDeployment: projectFlags.LocalDeployment, - LocalSealedSecrets: projectFlags.LocalSealedSecrets, - FromArchive: projectFlags.FromArchive, - FromArchiveMetadata: projectFlags.FromArchiveMetadata, + ProjectConfig: projectFlags.ProjectConfig.String(), + LocalClusters: projectFlags.LocalClusters.String(), + LocalDeployment: projectFlags.LocalDeployment.String(), + LocalSealedSecrets: projectFlags.LocalSealedSecrets.String(), + FromArchive: projectFlags.FromArchive.String(), + FromArchiveMetadata: projectFlags.FromArchiveMetadata.String(), GitAuthProviders: auth.NewDefaultAuthProviders(), } @@ -71,7 +71,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl if err != nil { return err } - err = ioutil.WriteFile(projectFlags.OutputMetadata, b, 0o640) + err = ioutil.WriteFile(projectFlags.OutputMetadata.String(), b, 0o640) if err != nil { return err } @@ -128,7 +128,7 @@ func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_pr return err } - renderOutputDir := args.renderOutputDirFlags.RenderOutputDir + renderOutputDir := args.renderOutputDirFlags.RenderOutputDir.String() if renderOutputDir == "" { tmpDir, err := ioutil.TempDir(p.TmpDir, "rendered") if err != nil { diff --git a/go.mod b/go.mod index ab56a0893..f432a9b25 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.18 require ( github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e - github.com/alecthomas/kong v0.5.0 github.com/aws/aws-sdk-go v1.44.1 github.com/bitnami-labs/sealed-secrets v0.17.5 github.com/docker/distribution v2.8.1+incompatible @@ -28,6 +27,9 @@ require ( github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/sirupsen/logrus v1.8.1 + github.com/spf13/cobra v1.4.0 + github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.10.0 github.com/stretchr/testify v1.7.1 github.com/vbauerster/mpb/v7 v7.4.1 github.com/whilp/git-urls v1.0.0 @@ -36,6 +38,7 @@ require ( golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 + golang.org/x/text v0.3.7 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b helm.sh/helm/v3 v3.8.2 k8s.io/api v0.24.0-rc.1 @@ -93,6 +96,7 @@ require ( github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect + github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/gammazero/deque v0.1.1 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect @@ -115,6 +119,7 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -128,6 +133,7 @@ require ( github.com/leodido/go-urn v1.2.1 // indirect github.com/lib/pq v1.10.5 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect @@ -135,6 +141,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -157,9 +164,10 @@ require ( github.com/russross/blackfriday v1.6.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect + github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect - github.com/spf13/cobra v1.4.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/subosito/gotenv v1.2.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -168,7 +176,6 @@ require ( go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect - golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect google.golang.org/appengine v1.6.7 // indirect @@ -176,6 +183,7 @@ require ( google.golang.org/grpc v1.46.0 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.23.6 // indirect diff --git a/go.sum b/go.sum index 492559201..3c91b81d5 100644 --- a/go.sum +++ b/go.sum @@ -153,10 +153,6 @@ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpH github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/alecthomas/kong v0.5.0 h1:u8Kdw+eeml93qtMZ04iei0CFYve/WPcA5IFh+9wSskE= -github.com/alecthomas/kong v0.5.0/go.mod h1:uzxf/HUh0tj43x1AyJROl3JT7SgsZ5m+icOv1csRhc0= -github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48= -github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= 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= @@ -764,6 +760,7 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA 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/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -879,6 +876,7 @@ github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -952,6 +950,7 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -1175,6 +1174,7 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1190,6 +1190,7 @@ github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -1200,6 +1201,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.10.0 h1:mXH0UwHS4D2HwWZa75im4xIQynLfblmWV7qcWpfv0yk= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -1217,6 +1219,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -1942,6 +1945,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 887e903bf..2d9333612 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -190,10 +190,7 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { } wg.Wait() - if len(errs) != 0 { - return utils.NewErrorList(errs) - } - return nil + return utils.NewErrorListOrNil(errs) } func (c *DeploymentCollection) LocalObjectsByRef() map[k8s2.ObjectRef]bool { diff --git a/pkg/deployment/utils/errors_holder.go b/pkg/deployment/utils/errors_holder.go index 92c184dda..9b702a378 100644 --- a/pkg/deployment/utils/errors_holder.go +++ b/pkg/deployment/utils/errors_holder.go @@ -106,8 +106,5 @@ func (dew *DeploymentErrorsAndWarnings) getPlainErrorsList() []error { func (dew *DeploymentErrorsAndWarnings) GetMultiError() error { l := dew.getPlainErrorsList() - if len(l) == 0 { - return nil - } - return utils.NewErrorList(l) + return utils.NewErrorListOrNil(l) } diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go index 58f115727..bcdd6d4b3 100644 --- a/pkg/jinja2/jinja2.go +++ b/pkg/jinja2/jinja2.go @@ -161,7 +161,7 @@ func (j *Jinja2) RenderStruct(dst interface{}, src interface{}, vars *uo.Unstruc } } if len(errors) != 0 { - return utils.NewErrorList(errors) + return utils.NewErrorListOrNil(errors) } err = m.ToStruct(dst) @@ -277,9 +277,5 @@ func (j *Jinja2) RenderDirectory(rootDir string, searchDirs []string, relSourceD return err } } - if len(errors) != 0 { - return utils.NewErrorList(errors) - } - - return nil + return utils.NewErrorListOrNil(errors) } diff --git a/pkg/utils/errorlist.go b/pkg/utils/errorlist.go index 374564cc5..0191aa7d1 100644 --- a/pkg/utils/errorlist.go +++ b/pkg/utils/errorlist.go @@ -4,7 +4,10 @@ type errorList struct { errors []error } -func NewErrorList(errors []error) *errorList { +func NewErrorListOrNil(errors []error) error { + if len(errors) == 0 { + return nil + } return &errorList{errors: errors} } diff --git a/pkg/utils/workerpool.go b/pkg/utils/workerpool.go index 5621723e6..fe71e32b9 100644 --- a/pkg/utils/workerpool.go +++ b/pkg/utils/workerpool.go @@ -70,7 +70,7 @@ func (wp *WorkerPoolWithErrors) StopWait(restart bool) error { if len(wp.errors) == 0 { return nil } - err := NewErrorList(wp.errors) + err := NewErrorListOrNil(wp.errors) wp.errors = nil return err } From 5866bbfd209d13da3970888dfc4138eb3214457a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 10:58:27 +0200 Subject: [PATCH 0094/2268] fix: Only fetch when refs have changed --- pkg/git/mirrored_repo.go | 58 ++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index b6db9ed33..a5ec3a3ba 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -148,36 +148,54 @@ func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProvid if err != nil { return err } - remoteRefsMap := make(map[plumbing.ReferenceName]bool) + remoteRefsMap := make(map[plumbing.ReferenceName]*plumbing.Reference) for _, reference := range g.remoteRefs { - remoteRefsMap[reference.Name()] = true - } - - err = remote.FetchContext(ctx, &git.FetchOptions{ - Auth: auth.AuthMethod, - CABundle: auth.CABundle, - Progress: os.Stdout, - Tags: git.AllTags, - Force: true, - }) - if err != nil && err != git.NoErrAlreadyUpToDate { - return err + remoteRefsMap[reference.Name()] = reference } localRemoteRefs, err := r.References() if err != nil { return err } - var toDelete []plumbing.Reference - err = localRemoteRefs.ForEach(func(reference *plumbing.Reference) error { - if _, ok := remoteRefsMap[reference.Name()]; !ok { - toDelete = append(toDelete, *reference) - } + + localRemoteRefsMap := make(map[plumbing.ReferenceName]*plumbing.Reference) + _ = localRemoteRefs.ForEach(func(reference *plumbing.Reference) error { + localRemoteRefsMap[reference.Name()] = reference return nil }) - if err != nil { - return err + + var toDelete []*plumbing.Reference + changed := false + for name, ref := range remoteRefsMap { + if name.String() != "HEAD" && !strings.HasPrefix(name.String(), "refs/heads/") && !strings.HasPrefix(name.String(), "refs/tags/") { + // we only fetch branches and tags + continue + } + if x, ok := localRemoteRefsMap[name]; !ok { + changed = true + } else if *x != *ref { + changed = true + } + } + for name, ref := range localRemoteRefsMap { + if _, ok := remoteRefsMap[name]; !ok { + toDelete = append(toDelete, ref) + } } + + if changed { + err = remote.FetchContext(ctx, &git.FetchOptions{ + Auth: auth.AuthMethod, + CABundle: auth.CABundle, + Progress: os.Stdout, + Tags: git.AllTags, + Force: true, + }) + if err != nil && err != git.NoErrAlreadyUpToDate { + return err + } + } + for _, ref := range toDelete { err = r.Storer.RemoveReference(ref.Name()) if err != nil { From 2cb9d8970e0349a25d6b43097c1b6c91ed4284dd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 11:35:21 +0200 Subject: [PATCH 0095/2268] feat: Allow to specify git cache update interval --- cmd/kluctl/args/project.go | 3 +- cmd/kluctl/commands/utils.go | 1 + pkg/git/mirrored_repo.go | 76 +++++++++++++++++++++++------- pkg/kluctl_project/git.go | 25 ++++++---- pkg/kluctl_project/load_targets.go | 5 +- pkg/kluctl_project/project.go | 3 ++ 6 files changed, 85 insertions(+), 28 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 167f04ea7..67da88a05 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -15,7 +15,8 @@ type ProjectFlags struct { OutputMetadata pathType `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` Cluster string `group:"project" help:"Specify/Override cluster"` - LoadTimeout time.Duration `group:"project" help:"Specify timeout for project loading. This will especially limit the time spent in git operations." default:"1m"` + LoadTimeout time.Duration `group:"project" help:"Specify timeout for project loading. This will especially limit the time spent in git operations." default:"1m"` + GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` } type ArgsFlags struct { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index ddbcc2b21..39516c6f8 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -57,6 +57,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl FromArchive: projectFlags.FromArchive.String(), FromArchiveMetadata: projectFlags.FromArchiveMetadata.String(), GitAuthProviders: auth.NewDefaultAuthProviders(), + GitUpdateInterval: projectFlags.GitCacheUpdateInterval, } loadCtx, cancel := context.WithDeadline(context.Background(), time.Now().Add(projectFlags.LoadTimeout)) diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index a5ec3a3ba..0afba2cd9 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -26,8 +26,6 @@ type MirroredGitRepo struct { hasUpdated bool fileLock *flock.Flock - - remoteRefs []*plumbing.Reference } func NewMirroredGitRepo(u git_url.GitUrl) (*MirroredGitRepo, error) { @@ -51,6 +49,10 @@ func (g *MirroredGitRepo) HasUpdated() bool { return g.hasUpdated } +func (g *MirroredGitRepo) SetUpdated(u bool) { + g.hasUpdated = u +} + func (g *MirroredGitRepo) Lock(ctx context.Context) error { ok, err := g.fileLock.TryLockContext(ctx, time.Millisecond*100) if err != nil { @@ -87,24 +89,62 @@ func (g *MirroredGitRepo) MaybeWithLock(ctx context.Context, lock bool, cb func( return cb() } -func (g *MirroredGitRepo) RemoteRefHashesMap() map[string]string { - refs := make(map[string]string) - for _, r := range g.remoteRefs { - refs[r.Name().String()] = r.Hash().String() +func (g *MirroredGitRepo) LastUpdateTime() time.Time { + s, err := ioutil.ReadFile(filepath.Join(g.mirrorDir, ".update-time")) + if err != nil { + return time.Time{} } - return refs + t, err := time.Parse(time.RFC3339Nano, string(s)) + if err != nil { + return time.Time{} + } + return t } -func (g *MirroredGitRepo) DefaultRef() *string { - for _, ref := range g.remoteRefs { - if ref.Name() == "HEAD" { - if ref.Type() == plumbing.SymbolicReference { - s := string(ref.Target()) - return &s +func (g *MirroredGitRepo) RemoteRefHashesMap() (map[string]string, error) { + r, err := git.PlainOpen(g.mirrorDir) + if err != nil { + return nil, err + } + + localRemoteRefs, err := r.References() + if err != nil { + return nil, err + } + + refs := make(map[string]string) + err = localRemoteRefs.ForEach(func(reference *plumbing.Reference) error { + name := reference.Name().String() + hash := reference.Hash().String() + if reference.Hash().IsZero() { + reference, err = r.Reference(reference.Name(), true) + if err != nil { + return err } + hash = reference.Hash().String() } + refs[name] = hash + return nil + }) + if err != nil { + return nil, err } - return nil + return refs, nil +} + +func (g *MirroredGitRepo) DefaultRef() *string { + r, err := git.PlainOpen(g.mirrorDir) + if err != nil { + return nil + } + + ref, err := r.Reference("HEAD", false) + if err != nil { + return nil + } + + s := ref.Target().String() + return &s } func (g *MirroredGitRepo) buildRepositoryObject() (*git.Repository, error) { @@ -141,7 +181,7 @@ func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProvid return err } - g.remoteRefs, err = remote.ListContext(ctx, &git.ListOptions{ + remoteRefs, err := remote.ListContext(ctx, &git.ListOptions{ Auth: auth.AuthMethod, CABundle: auth.CABundle, }) @@ -149,7 +189,7 @@ func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProvid return err } remoteRefsMap := make(map[plumbing.ReferenceName]*plumbing.Reference) - for _, reference := range g.remoteRefs { + for _, reference := range remoteRefs { remoteRefsMap[reference.Name()] = reference } @@ -205,7 +245,7 @@ func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProvid // update default branch, referenced via HEAD // we assume that HEAD is a symbolic ref and don't care about old git versions - for _, ref := range g.remoteRefs { + for _, ref := range remoteRefs { if ref.Name() == "HEAD" { err = r.Storer.SetReference(ref) if err != nil { @@ -215,6 +255,8 @@ func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProvid } } + _ = ioutil.WriteFile(filepath.Join(g.mirrorDir, ".update-time"), []byte(time.Now().Format(time.RFC3339Nano)), 0644) + return nil } diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 3a6b56a00..23072f103 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -14,8 +14,20 @@ import ( "sort" "strings" "sync" + "time" ) +func (c *KluctlProjectContext) updateGitCache(ctx context.Context, mr *git.MirroredGitRepo) error { + if mr.HasUpdated() { + return nil + } + if time.Now().Sub(mr.LastUpdateTime()) <= c.loadArgs.GitUpdateInterval { + mr.SetUpdated(true) + return nil + } + return mr.Update(ctx, c.loadArgs.GitAuthProviders) +} + func (c *KluctlProjectContext) updateGitCaches(ctx context.Context) error { var waitGroup sync.WaitGroup var firstError error @@ -31,10 +43,7 @@ func (c *KluctlProjectContext) updateGitCaches(ctx context.Context) error { doUpdateRepo := func(repo *git.MirroredGitRepo) error { return repo.WithLock(ctx, func() error { - if !repo.HasUpdated() { - return repo.Update(ctx, c.loadArgs.GitAuthProviders) - } - return nil + return c.updateGitCache(ctx, repo) }) } doUpdateGitProject := func(u git_url.GitUrl) error { @@ -156,11 +165,9 @@ func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject t } err = mr.MaybeWithLock(ctx, doLock, func() error { - if !mr.HasUpdated() { - err = mr.Update(ctx, c.loadArgs.GitAuthProviders) - if err != nil { - return err - } + err := c.updateGitCache(ctx, mr) + if err != nil { + return err } return mr.CloneProject(ctx, gitProject.Project.Ref, targetDir) }) diff --git a/pkg/kluctl_project/load_targets.go b/pkg/kluctl_project/load_targets.go index bfafa94d5..ec20c2f0f 100644 --- a/pkg/kluctl_project/load_targets.go +++ b/pkg/kluctl_project/load_targets.go @@ -157,7 +157,10 @@ func (c *KluctlProjectContext) prepareDynamicTargetsExternal(baseTarget *types.T targetConfigRef = defaultBranch } - refs := mr.RemoteRefHashesMap() + refs, err := mr.RemoteRefHashesMap() + if err != nil { + return nil, err + } if targetConfigRef != nil { if _, ok := refs[fmt.Sprintf("refs/heads/%s", *targetConfigRef)]; !ok { diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index bacb21ae2..dce44c7fc 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -8,6 +8,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/types" "regexp" + "time" ) type LoadKluctlProjectArgs struct { @@ -22,6 +23,8 @@ type LoadKluctlProjectArgs struct { FromArchiveMetadata string GitAuthProviders *auth2.GitAuthProviders + + GitUpdateInterval time.Duration } type KluctlProjectContext struct { From ec4dd0966b0a991fb9c42d6ee14461e6a968d5fb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 11:42:36 +0200 Subject: [PATCH 0096/2268] feat: Implement completion for target names --- cmd/kluctl/commands/cobra_utils.go | 5 ++++ cmd/kluctl/commands/completion.go | 41 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 cmd/kluctl/commands/completion.go diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index 083e07693..a339e2485 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -81,6 +81,11 @@ func (c *rootCommand) buildCobraCmd(parent *commandAndGroups, cmdStruct interfac return nil, err } + err = RegisterFlagCompletionFuncs(cmdStruct, cg.cmd) + if err != nil { + return nil, err + } + cg.cmd.SetHelpFunc(func(command *cobra.Command, i []string) { c.helpFunc(cg) }) diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go new file mode 100644 index 000000000..971d33293 --- /dev/null +++ b/cmd/kluctl/commands/completion.go @@ -0,0 +1,41 @@ +package commands + +import ( + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "reflect" + "time" +) + +func RegisterFlagCompletionFuncs(cmd interface{}, ccmd *cobra.Command) error { + v := reflect.ValueOf(cmd).Elem() + projectFlags := v.FieldByName("ProjectFlags") + targetFlags := v.FieldByName("TargetFlags") + + if projectFlags.IsValid() && targetFlags.IsValid() { + _ = ccmd.RegisterFlagCompletionFunc("target", buildTargetCompletionFunc(projectFlags.Addr().Interface().(*args.ProjectFlags))) + } + + return nil +} + +func buildTargetCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var ret []string + // let's not update git caches too often + projectArgs.GitCacheUpdateInterval = time.Second * 60 + err := withKluctlProjectFromArgs(*projectArgs, func(p *kluctl_project.KluctlProjectContext) error { + for _, t := range p.DynamicTargets { + ret = append(ret, t.Target.Name) + } + return nil + }) + if err != nil { + log.Error(err) + return nil, cobra.ShellCompDirectiveError + } + return ret, cobra.ShellCompDirectiveDefault + } +} From 6fbd652f95383fde0d48fb4a4a0eb43dd87e2d1f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 11:45:14 +0200 Subject: [PATCH 0097/2268] build: Install shell completion via homebrew packages --- .goreleaser.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 20c4e885e..ea3c4cf97 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -116,3 +116,12 @@ brews: bin.install "kluctl" test: | system "#{bin}/kluctl version" + + bash_output = Utils.safe_popen_read(bin/"kluctl", "completion", "bash") + (bash_completion/"kluctl").write bash_output + + zsh_output = Utils.safe_popen_read(bin/"kluctl", "completion", "zsh") + (zsh_completion/"_kluctl").write zsh_output + + fish_output = Utils.safe_popen_read(bin/"kluctl", "completion", "fish") + (fish_completion/"kluctl.fish").write fish_output From 9cca84ea32492b35f6e22b35bb466d0270aaf147 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 11:56:56 +0200 Subject: [PATCH 0098/2268] feat: Implement autocompletion for file/dir based args --- cmd/kluctl/args/images.go | 2 +- cmd/kluctl/args/project.go | 6 +++--- cmd/kluctl/commands/cobra_utils.go | 14 +++++++++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index 3cdcdb9f7..70c681705 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -9,7 +9,7 @@ import ( type ImageFlags struct { FixedImage []string `group:"images" short:"F" help:"Pin an image to a given version. Expects '--fixed-image=image<:namespace:deployment:container>=result'"` - FixedImagesFile existingFileType `group:"images" help:"Use .yml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format"` + FixedImagesFile existingFileType `group:"images" help:"Use .yml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format" exts:"yml,yaml"` UpdateImages bool `group:"images" short:"u" help:"This causes kluctl to prefer the latest image found in registries, based on the 'latest_image' filters provided to 'images.get_image(...)' calls. Use this flag if you want to update to the latest versions/tags of all images. '-u' takes precedence over '--fixed-image/--fixed-images-file', meaning that the latest images are used even if an older image is given via fixed images."` } diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 67da88a05..81ea403fa 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -6,12 +6,12 @@ type ProjectFlags struct { ProjectUrl string `group:"project" short:"p" help:"Git url of the kluctl project. If not specified, the current directory will be used instead of a remote Git project"` ProjectRef string `group:"project" short:"b" help:"Git ref of the kluctl project. Only used when --project-url was given."` - ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yml config file. Defaults to $PROJECT/.kluctl.yml"` + ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yml config file. Defaults to $PROJECT/.kluctl.yml" exts:"yml,yaml"` LocalClusters existingDirType `group:"project" help:"Local clusters directory. Overrides the project from .kluctl.yml"` LocalDeployment existingDirType `group:"project" help:"Local deployment directory. Overrides the project from .kluctl.yml"` LocalSealedSecrets existingDirType `group:"project" help:"Local sealed-secrets directory. Overrides the project from .kluctl.yml" ` - FromArchive existingPathType `group:"project" help:"Load project (.kluctl.yml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents."` - FromArchiveMetadata existingFileType `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." ` + FromArchive existingPathType `group:"project" help:"Load project (.kluctl.yml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." exts:"tar.gz,tgz"` + FromArchiveMetadata existingFileType `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." exts:"yml,yaml"` OutputMetadata pathType `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` Cluster string `group:"project" help:"Specify/Override cluster"` diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index a339e2485..17ed89eee 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -151,7 +151,19 @@ func (c *rootCommand) buildCobraArg(cg *commandAndGroups, f reflect.StructField, switch v2.(type) { case pflag.Value: - cg.cmd.PersistentFlags().VarPF(v2.(pflag.Value), name, shortFlag, help) + v3 := v2.(pflag.Value) + cg.cmd.PersistentFlags().VarP(v3, name, shortFlag, help) + switch v3.Type() { + case "existingfile": + exts := strings.Split(f.Tag.Get("exts"), ",") + _ = cg.cmd.MarkPersistentFlagFilename(name, exts...) + case "existingdir": + _ = cg.cmd.MarkPersistentFlagDirname(name) + case "existingpath": + exts := strings.Split(f.Tag.Get("exts"), ",") + _ = cg.cmd.MarkPersistentFlagFilename(name, exts...) + _ = cg.cmd.MarkPersistentFlagDirname(name) + } case *string: cg.cmd.PersistentFlags().StringVarP(v2.(*string), name, shortFlag, defaultValue, help) case *[]string: From 7e5136fc6f6b5e9cc6dd9b1e60df4d5405580931 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 12:23:45 +0200 Subject: [PATCH 0099/2268] feat: Implement autocompletion for --cluster --- cmd/kluctl/commands/completion.go | 48 +++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 971d33293..80bffe0c5 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -3,8 +3,12 @@ package commands import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "os" + "path/filepath" "reflect" "time" ) @@ -14,6 +18,10 @@ func RegisterFlagCompletionFuncs(cmd interface{}, ccmd *cobra.Command) error { projectFlags := v.FieldByName("ProjectFlags") targetFlags := v.FieldByName("TargetFlags") + if projectFlags.IsValid() { + _ = ccmd.RegisterFlagCompletionFunc("cluster", buildClusterCompletionFunc(projectFlags.Addr().Interface().(*args.ProjectFlags))) + } + if projectFlags.IsValid() && targetFlags.IsValid() { _ = ccmd.RegisterFlagCompletionFunc("target", buildTargetCompletionFunc(projectFlags.Addr().Interface().(*args.ProjectFlags))) } @@ -21,12 +29,46 @@ func RegisterFlagCompletionFuncs(cmd interface{}, ccmd *cobra.Command) error { return nil } +func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(p *kluctl_project.KluctlProjectContext) error) error { + // let's not update git caches too often + projectArgs.GitCacheUpdateInterval = time.Second * 60 + return withKluctlProjectFromArgs(*projectArgs, func(p *kluctl_project.KluctlProjectContext) error { + return cb(p) + }) +} + +func buildClusterCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var ret []string + err := withProjectForCompletion(projectArgs, func(p *kluctl_project.KluctlProjectContext) error { + dents, err := os.ReadDir(p.ClustersDir) + if err != nil { + return err + } + for _, de := range dents { + var config types.ClusterConfig + err = yaml.ReadYamlFile(filepath.Join(p.ClustersDir, de.Name()), &config) + if err != nil { + continue + } + if config.Cluster.Name != "" { + ret = append(ret, config.Cluster.Name) + } + } + return nil + }) + if err != nil { + log.Error(err) + return nil, cobra.ShellCompDirectiveError + } + return ret, cobra.ShellCompDirectiveDefault + } +} + func buildTargetCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var ret []string - // let's not update git caches too often - projectArgs.GitCacheUpdateInterval = time.Second * 60 - err := withKluctlProjectFromArgs(*projectArgs, func(p *kluctl_project.KluctlProjectContext) error { + err := withProjectForCompletion(projectArgs, func(p *kluctl_project.KluctlProjectContext) error { for _, t := range p.DynamicTargets { ret = append(ret, t.Target.Name) } From 99f76680c55ecaffd7efb7c801cfa533e3c2fef7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 14:20:44 +0200 Subject: [PATCH 0100/2268] feat: Implement autocompletion for inclusion flags --- cmd/kluctl/commands/cmd_seal.go | 3 +- cmd/kluctl/commands/completion.go | 91 +++++++++++++++++++++++++++- cmd/kluctl/commands/utils.go | 16 +++-- pkg/deployment/deployment_item.go | 31 +++++----- pkg/deployment/deployment_project.go | 8 +-- pkg/jinja2/vars.go | 4 ++ pkg/kluctl_project/target_context.go | 9 ++- pkg/utils/ordered_map.go | 12 ++++ 8 files changed, 140 insertions(+), 34 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 185c07f44..ce918cf90 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -67,11 +67,12 @@ func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.KluctlProjectContext, ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, + forSeal: true, } ptArgs.targetFlags.Target = targetName // pass forSeal=True so that .sealme files are rendered as well - return withProjectTargetCommandContext(ptArgs, p, true, func(ctx *commandCtx) error { + return withProjectTargetCommandContext(ptArgs, p, func(ctx *commandCtx) error { err := loadSecrets(ctx, ctx.targetCtx.Target, secretsLoader) if err != nil { return err diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 80bffe0c5..fcc341c2c 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -4,19 +4,22 @@ import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "os" "path/filepath" "reflect" + "sync" "time" ) -func RegisterFlagCompletionFuncs(cmd interface{}, ccmd *cobra.Command) error { - v := reflect.ValueOf(cmd).Elem() +func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) error { + v := reflect.ValueOf(cmdStruct).Elem() projectFlags := v.FieldByName("ProjectFlags") targetFlags := v.FieldByName("TargetFlags") + inclusionFlags := v.FieldByName("InclusionFlags") if projectFlags.IsValid() { _ = ccmd.RegisterFlagCompletionFunc("cluster", buildClusterCompletionFunc(projectFlags.Addr().Interface().(*args.ProjectFlags))) @@ -26,6 +29,15 @@ func RegisterFlagCompletionFuncs(cmd interface{}, ccmd *cobra.Command) error { _ = ccmd.RegisterFlagCompletionFunc("target", buildTargetCompletionFunc(projectFlags.Addr().Interface().(*args.ProjectFlags))) } + if projectFlags.IsValid() && inclusionFlags.IsValid() { + tagsFunc := buildInclusionCompletionFunc(cmdStruct, false) + dirsFunc := buildInclusionCompletionFunc(cmdStruct, true) + _ = ccmd.RegisterFlagCompletionFunc("include-tag", tagsFunc) + _ = ccmd.RegisterFlagCompletionFunc("exclude-tag", tagsFunc) + _ = ccmd.RegisterFlagCompletionFunc("include-deployment-dir", dirsFunc) + _ = ccmd.RegisterFlagCompletionFunc("exclude-deployment-dir", dirsFunc) + } + return nil } @@ -81,3 +93,78 @@ func buildTargetCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.C return ret, cobra.ShellCompDirectiveDefault } } + +func buildAutocompleteProjectTargetCommandArgs(cmdStruct interface{}) projectTargetCommandArgs { + ptArgs := projectTargetCommandArgs{} + + cmdV := reflect.ValueOf(cmdStruct).Elem() + if cmdV.FieldByName("ProjectFlags").IsValid() { + ptArgs.projectFlags = cmdV.FieldByName("ProjectFlags").Interface().(args.ProjectFlags) + } + if cmdV.FieldByName("TargetFlags").IsValid() { + ptArgs.targetFlags = cmdV.FieldByName("TargetFlags").Interface().(args.TargetFlags) + } + if cmdV.FieldByName("ArgsFlags").IsValid() { + ptArgs.argsFlags = cmdV.FieldByName("ArgsFlags").Interface().(args.ArgsFlags) + } + if cmdV.FieldByName("ImageFlags").IsValid() { + ptArgs.imageFlags = cmdV.FieldByName("ImageFlags").Interface().(args.ImageFlags) + } + if cmdV.FieldByName("InclusionFlags").IsValid() { + ptArgs.inclusionFlags = cmdV.FieldByName("InclusionFlags").Interface().(args.InclusionFlags) + } + + ptArgs.forCompletion = true + return ptArgs +} + +func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + ptArgs := buildAutocompleteProjectTargetCommandArgs(cmdStruct) + + var tags utils.OrderedMap + var deploymentItemDirs utils.OrderedMap + var mutex sync.Mutex + + err := withProjectForCompletion(&ptArgs.projectFlags, func(p *kluctl_project.KluctlProjectContext) error { + var targets []string + if ptArgs.targetFlags.Target == "" { + for _, t := range p.DynamicTargets { + targets = append(targets, t.Target.Name) + } + } else { + targets = append(targets, ptArgs.targetFlags.Target) + } + + var wg sync.WaitGroup + for _, t := range targets { + ptArgs := ptArgs + ptArgs.targetFlags.Target = t + wg.Add(1) + go func() { + _ = withProjectTargetCommandContext(ptArgs, p, func(ctx *commandCtx) error { + mutex.Lock() + defer mutex.Unlock() + for _, di := range ctx.targetCtx.DeploymentCollection.Deployments { + tags.Merge(di.Tags) + deploymentItemDirs.Set(di.RelToRootItemDir, true) + } + return nil + }) + wg.Done() + }() + } + wg.Wait() + return nil + }) + if err != nil { + log.Error(err) + return nil, cobra.ShellCompDirectiveError + } + if forDirs { + return deploymentItemDirs.ListKeys(), cobra.ShellCompDirectiveDefault + } else { + return tags.ListKeys(), cobra.ShellCompDirectiveDefault + } + } +} diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 39516c6f8..a3ba20d4c 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -88,6 +88,9 @@ type projectTargetCommandArgs struct { inclusionFlags args.InclusionFlags dryRunArgs *args.DryRunFlags renderOutputDirFlags args.RenderOutputDirFlags + + forSeal bool + forCompletion bool } type commandCtx struct { @@ -97,11 +100,11 @@ type commandCtx struct { func withProjectCommandContext(args projectTargetCommandArgs, cb func(ctx *commandCtx) error) error { return withKluctlProjectFromArgs(args.projectFlags, func(p *kluctl_project.KluctlProjectContext) error { - return withProjectTargetCommandContext(args, p, false, cb) + return withProjectTargetCommandContext(args, p, cb) }) } -func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_project.KluctlProjectContext, forSeal bool, cb func(ctx *commandCtx) error) error { +func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_project.KluctlProjectContext, cb func(ctx *commandCtx) error) error { rh := registries.NewRegistryHelper() err := rh.ParseAuthEntriesFromEnv() if err != nil { @@ -140,20 +143,23 @@ func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_pr } clientConfigGetter := func(context string) (*rest.Config, error) { + if args.forCompletion { + return nil, nil + } configLoadingRules := clientcmd.NewDefaultClientConfigLoadingRules() configOverrides := &clientcmd.ConfigOverrides{CurrentContext: context} return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(configLoadingRules, configOverrides).ClientConfig() } ctx, err := p.NewTargetContext(clientConfigGetter, args.targetFlags.Target, args.projectFlags.Cluster, - args.dryRunArgs == nil || args.dryRunArgs.DryRun, - optionArgs, forSeal, images, inclusion, + args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, + optionArgs, args.forSeal, images, inclusion, renderOutputDir) if err != nil { return err } - if !forSeal { + if !args.forSeal && !args.forCompletion { err = ctx.DeploymentCollection.Prepare(ctx.K) if err != nil { return err diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index e723c480e..57e9eea52 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -33,9 +33,10 @@ type DeploymentItem struct { WaitReadiness bool Objects []*uo.UnstructuredObject + Tags *utils.OrderedMap relProjectDir string - relToRootItemDir string + RelToRootItemDir string RelToProjectItemDir string relRenderedDir string renderedDir string @@ -53,6 +54,10 @@ func NewDeploymentItem(project *DeploymentProject, collection *DeploymentCollect var err error + // collect tags + di.Tags = di.Project.getTags() + di.Tags.SetMultiple(di.Config.Tags, true) + rootProject := di.Project.getRootProject() di.relProjectDir, err = filepath.Rel(rootProject.dir, di.Project.dir) @@ -61,17 +66,17 @@ func NewDeploymentItem(project *DeploymentProject, collection *DeploymentCollect } if di.dir != nil { - di.relToRootItemDir, err = filepath.Rel(rootProject.dir, *di.dir) + di.RelToRootItemDir, err = filepath.Rel(rootProject.dir, *di.dir) if err != nil { return nil, err } - di.RelToProjectItemDir, err = filepath.Rel(di.relProjectDir, di.relToRootItemDir) + di.RelToProjectItemDir, err = filepath.Rel(di.relProjectDir, di.RelToRootItemDir) if err != nil { return nil, err } - di.relRenderedDir = di.relToRootItemDir + di.relRenderedDir = di.RelToRootItemDir if di.index != 0 { di.relRenderedDir = fmt.Sprintf("%s-%d", di.relRenderedDir, di.index) } @@ -85,7 +90,7 @@ func NewDeploymentItem(project *DeploymentProject, collection *DeploymentCollect func (di *DeploymentItem) getCommonLabels() map[string]string { l := di.Project.GetCommonLabels() i := 0 - for _, t := range di.getTags().ListKeys() { + for _, t := range di.Tags.ListKeys() { l[fmt.Sprintf("kluctl.io/tag-%d", i)] = t i += 1 } @@ -95,7 +100,7 @@ func (di *DeploymentItem) getCommonLabels() map[string]string { func (di *DeploymentItem) getCommonAnnotations() map[string]string { // TODO change it to kluctl.io/deployment_dir a := map[string]string{ - "kluctl.io/kustomize_dir": strings.ReplaceAll(di.relToRootItemDir, string(os.PathSeparator), "/"), + "kluctl.io/kustomize_dir": strings.ReplaceAll(di.RelToRootItemDir, string(os.PathSeparator), "/"), } if di.Config.SkipDeleteIfTags != nil && *di.Config.SkipDeleteIfTags { a["kluctl.io/skip-delete-if-tags"] = "true" @@ -239,21 +244,13 @@ func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { return nil } -func (di *DeploymentItem) getTags() *utils.OrderedMap { - tags := di.Project.getTags() - for _, t := range di.Config.Tags { - tags.Set(t, true) - } - return tags -} - func (di *DeploymentItem) buildInclusionEntries() []utils.InclusionEntry { var values []utils.InclusionEntry - for _, t := range di.getTags().ListKeys() { + for _, t := range di.Tags.ListKeys() { values = append(values, utils.InclusionEntry{Type: "tag", Value: t}) } if di.dir != nil { - dir := strings.ReplaceAll(di.relToRootItemDir, string(os.PathSeparator), "/") + dir := strings.ReplaceAll(di.RelToRootItemDir, string(os.PathSeparator), "/") values = append(values, utils.InclusionEntry{Type: "deploymentItemDir", Value: dir}) } return values @@ -388,7 +385,7 @@ func (di *DeploymentItem) postprocessAndLoadObjects(k *k8s.K8sCluster, images *I o.SetK8sAnnotations(uo.CopyMergeStrMap(o.GetK8sAnnotations(), commonAnnotations)) // Resolve image placeholders - err = images.ResolvePlaceholders(k, o, di.relRenderedDir, di.getTags().ListKeys()) + err = images.ResolvePlaceholders(k, o, di.relRenderedDir, di.Tags.ListKeys()) if err != nil { return err } diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 8193ff13d..fe093ab20 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -284,13 +284,9 @@ func (p *DeploymentProject) getTags() *utils.OrderedMap { var tags utils.OrderedMap for _, e := range p.getParents() { if e.inc != nil { - for _, t := range e.inc.Tags { - tags.Set(t, true) - } - } - for _, t := range e.p.Config.Tags { - tags.Set(t, true) + tags.SetMultiple(e.inc.Tags, true) } + tags.SetMultiple(e.p.Config.Tags, true) } return &tags } diff --git a/pkg/jinja2/vars.go b/pkg/jinja2/vars.go index 1c42f4c38..75dc2af49 100644 --- a/pkg/jinja2/vars.go +++ b/pkg/jinja2/vars.go @@ -87,6 +87,10 @@ func (vc *VarsCtx) loadVarsFile(p string, searchDirs []string) error { } func (vc *VarsCtx) loadVarsFromK8sObject(k *k8s.K8sCluster, ref k8s2.ObjectRef, key string) error { + if k == nil { + return nil + } + o, _, err := k.GetSingleObject(ref) if err != nil { return err diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 29b662338..745b55131 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -57,9 +57,12 @@ func (p *KluctlProjectContext) NewTargetContext(clientConfigGetter func(context return nil, err } - k, err := k8s.NewK8sCluster(clientConfig, dryRun) - if err != nil { - return nil, err + var k *k8s.K8sCluster + if clientConfig != nil { + k, err = k8s.NewK8sCluster(clientConfig, dryRun) + if err != nil { + return nil, err + } } varsCtx := jinja2.NewVarsCtx(p.J2) diff --git a/pkg/utils/ordered_map.go b/pkg/utils/ordered_map.go index ab845989e..b19de7aff 100644 --- a/pkg/utils/ordered_map.go +++ b/pkg/utils/ordered_map.go @@ -23,6 +23,12 @@ func (s *OrderedMap) Set(k string, v interface{}) bool { return true } +func (s *OrderedMap) SetMultiple(k []string, v interface{}) { + for _, x := range k { + s.Set(x, v) + } +} + func (s *OrderedMap) Has(v string) bool { _, ok := s.m[v] return ok @@ -51,3 +57,9 @@ func (s *OrderedMap) ListValues() []interface{} { } return l } + +func (s *OrderedMap) Merge(other *OrderedMap) { + for _, e := range other.l { + s.Set(e.k, e.v) + } +} From 15fa6405d70c1ac182d77d775326859ef99bb997 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 15:50:26 +0200 Subject: [PATCH 0101/2268] fix: Fix crash when parsing --fixed-image --- cmd/kluctl/args/images.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index 70c681705..f9eb511be 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -49,13 +49,13 @@ func buildFixedImageEntryFromArg(arg string) (*types.FixedImage, error) { } if len(s) >= 2 { - e.Namespace = &s[2] + e.Namespace = &s[1] } if len(s) >= 3 { - e.Deployment = &s[3] + e.Deployment = &s[2] } if len(s) >= 4 { - e.Container = &s[4] + e.Container = &s[3] } if len(s) >= 5 { return nil, fmt.Errorf("--fixed-image expects 'image<:namespace:deployment:container>=result'") From 37ee758723c0d207bfd81fb03cbd1db7c1dc73ef Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 15:51:12 +0200 Subject: [PATCH 0102/2268] feat: Allow non-strict template rendering --- pkg/jinja2/jinja2.go | 10 +++++-- pkg/jinja2/jinja2_renderer.go | 4 ++- pkg/jinja2/python_src/jinja2_renderer.py | 34 +++++++++++++++++------- pkg/jinja2/python_src/main.py | 4 +-- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go index bcdd6d4b3..fa04d3cd7 100644 --- a/pkg/jinja2/jinja2.go +++ b/pkg/jinja2/jinja2.go @@ -17,6 +17,7 @@ var paralellism = 4 type Jinja2 struct { pj chan *pythonJinja2Renderer + strict bool globCache map[string]interface{} mutex sync.Mutex } @@ -44,6 +45,7 @@ func NewJinja2() (*Jinja2, error) { j := &Jinja2{ pj: make(chan *pythonJinja2Renderer, paralellism), + strict: true, globCache: map[string]interface{}{}, } @@ -76,16 +78,20 @@ func (j *Jinja2) Close() { } } +func (j *Jinja2) SetStrict(strict bool) { + j.strict = strict +} + func (j *Jinja2) RenderStrings(jobs []*RenderJob, searchDirs []string, vars *uo.UnstructuredObject) error { pj := <-j.pj defer func() { j.pj <- pj }() - return pj.renderHelper(jobs, searchDirs, vars, true) + return pj.renderHelper(jobs, searchDirs, vars, true, j.strict) } func (j *Jinja2) RenderFiles(jobs []*RenderJob, searchDirs []string, vars *uo.UnstructuredObject) error { pj := <-j.pj defer func() { j.pj <- pj }() - return pj.renderHelper(jobs, searchDirs, vars, false) + return pj.renderHelper(jobs, searchDirs, vars, false, j.strict) } func (j *Jinja2) RenderString(template string, searchDirs []string, vars *uo.UnstructuredObject) (string, error) { diff --git a/pkg/jinja2/jinja2_renderer.go b/pkg/jinja2/jinja2_renderer.go index e3299137f..f52869396 100644 --- a/pkg/jinja2/jinja2_renderer.go +++ b/pkg/jinja2/jinja2_renderer.go @@ -108,6 +108,7 @@ type jinja2Args struct { Templates []string `json:"templates"` SearchDirs []string `json:"searchDirs"` Vars string `json:"vars"` + Strict bool `json:"strict"` } type jinja2Result struct { @@ -115,7 +116,7 @@ type jinja2Result struct { Error *string `json:"error,omitempty"` } -func (j *pythonJinja2Renderer) renderHelper(jobs []*RenderJob, searchDirs []string, vars *uo.UnstructuredObject, isString bool) error { +func (j *pythonJinja2Renderer) renderHelper(jobs []*RenderJob, searchDirs []string, vars *uo.UnstructuredObject, isString bool, strict bool) error { varsStr, err := json.Marshal(vars.Object) if err != nil { return err @@ -131,6 +132,7 @@ func (j *pythonJinja2Renderer) renderHelper(jobs []*RenderJob, searchDirs []stri } jargs.Vars = string(varsStr) jargs.SearchDirs = searchDirs + jargs.Strict = strict for _, job := range jobs { if ist, r := j.isMaybeTemplate(job.Template, searchDirs, isString); !ist { diff --git a/pkg/jinja2/python_src/jinja2_renderer.py b/pkg/jinja2/python_src/jinja2_renderer.py index b5ca3411e..735379917 100644 --- a/pkg/jinja2/python_src/jinja2_renderer.py +++ b/pkg/jinja2/python_src/jinja2_renderer.py @@ -1,7 +1,7 @@ import base64 import json -from jinja2 import StrictUndefined, FileSystemLoader +from jinja2 import StrictUndefined, FileSystemLoader, ChainableUndefined from dict_utils import merge_dict from jinja2_cache import KluctlBytecodeCache @@ -13,6 +13,21 @@ end_placeholder = "_end_get_imageXXXXX" +class NullUndefined(ChainableUndefined): + def _return_self(self, other): + return self + + __add__ = __radd__ = __sub__ = __rsub__ = _return_self + __mul__ = __rmul__ = __div__ = __rdiv__ = _return_self + __truediv__ = __rtruediv__ = _return_self + __floordiv__ = __rfloordiv__ = _return_self + __mod__ = __rmod__ = _return_self + __pos__ = __neg__ = _return_self + __call__ = __getitem__ = _return_self + __lt__ = __le__ = __gt__ = __ge__ = _return_self + __int__ = __float__ = __complex__ = _return_self + __pow__ = __rpow__ = _return_self + class Jinja2Renderer: def get_image_wrapper(self, image, latest_version=None): @@ -53,12 +68,13 @@ def regex(r): } return vars - def build_env(self, vars_str, search_dirs): + def build_env(self, vars_str, search_dirs, strict): vars = json.loads(vars_str) image_vars = self.build_images_vars() merge_dict(vars, image_vars, clone=False) - environment = KluctlJinja2Environment(loader=FileSystemLoader(search_dirs), undefined=StrictUndefined, + environment = KluctlJinja2Environment(loader=FileSystemLoader(search_dirs), + undefined=StrictUndefined if strict else NullUndefined, cache_size=10000, bytecode_cache=jinja2_cache, auto_reload=False) merge_dict(environment.globals, vars, clone=False) @@ -66,8 +82,8 @@ def build_env(self, vars_str, search_dirs): add_jinja2_filters(environment) return environment - def render_helper(self, templates, search_dirs, vars, is_string): - env = self.build_env(vars, search_dirs) + def render_helper(self, templates, search_dirs, vars, is_string, strict): + env = self.build_env(vars, search_dirs, strict) result = [] @@ -87,17 +103,17 @@ def render_helper(self, templates, search_dirs, vars, is_string): return result - def RenderStrings(self, templates, search_dirs, vars): + def RenderStrings(self, templates, search_dirs, vars, strict): try: - return self.render_helper(templates, search_dirs, vars, True) + return self.render_helper(templates, search_dirs, vars, True, strict) except Exception as e: return [{ "error": str(e) }] * len(templates) - def RenderFiles(self, templates, search_dirs, vars): + def RenderFiles(self, templates, search_dirs, vars, strict): try: - return self.render_helper(templates, search_dirs, vars, False) + return self.render_helper(templates, search_dirs, vars, False, strict) except Exception as e: return [{ "error": str(e) diff --git a/pkg/jinja2/python_src/main.py b/pkg/jinja2/python_src/main.py index c1d7e54be..4f35ec9b7 100644 --- a/pkg/jinja2/python_src/main.py +++ b/pkg/jinja2/python_src/main.py @@ -14,9 +14,9 @@ def main(): args = json.loads(args) if args["cmd"] == "render-strings": - result = r.RenderStrings(args["templates"], args["searchDirs"] or [], args["vars"]) + result = r.RenderStrings(args["templates"], args["searchDirs"] or [], args["vars"], args["strict"]) elif args["cmd"] == "render-files": - result = r.RenderFiles(args["templates"], args["searchDirs"] or [], args["vars"]) + result = r.RenderFiles(args["templates"], args["searchDirs"] or [], args["vars"], args["strict"]) elif args["cmd"] == "exit": break else: From cf96fef8afecc6c551ee788eba208d1fc80bb764 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 16:01:15 +0200 Subject: [PATCH 0103/2268] fix: Allow offline rendering (images and k8s) --- cmd/kluctl/commands/utils.go | 2 +- pkg/deployment/helm_chart.go | 33 ++++++++++++++++++++------------- pkg/deployment/images.go | 8 +++++++- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index a3ba20d4c..9ac8ac5f5 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -110,7 +110,7 @@ func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_pr if err != nil { return fmt.Errorf("failed to parse registry auth from environment: %w", err) } - images, err := deployment.NewImages(rh, args.imageFlags.UpdateImages) + images, err := deployment.NewImages(rh, args.imageFlags.UpdateImages, args.forCompletion) if err != nil { return err } diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 343c87b19..23a89200f 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -179,11 +179,13 @@ func (c *helmChart) doRender(k *k8s.K8sCluster) error { valuesPath := yaml.FixPathExt(filepath.Join(dir, "helm-values.yml")) outputPath := filepath.Join(dir, c.Config.Output) - gvs, err := k.GetAllGroupVersions() - if err != nil { - return err + var gvs []string + if k != nil { + gvs, err = k.GetAllGroupVersions() + if err != nil { + return err + } } - cfg, err := c.buildHelmConfig() if err != nil { return err @@ -194,9 +196,12 @@ func (c *helmChart) doRender(k *k8s.K8sCluster) error { ValueFiles: []string{valuesPath}, } - kubeVersion, err := chartutil.ParseKubeVersion(k.ServerVersion.String()) - if err != nil { - return err + var kubeVersion *chartutil.KubeVersion + if k != nil { + kubeVersion, err = chartutil.ParseKubeVersion(k.ServerVersion.String()) + if err != nil { + return err + } } namespace := "default" @@ -284,12 +289,14 @@ func (c *helmChart) doRender(k *k8s.K8sCluster) error { for _, o := range parsed { // "helm install" will deploy resources to the given namespace automatically, but "helm template" does not // add the necessary namespace in the rendered resources - err = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { - k.FixNamespace(o, namespace) - return nil - }) - if err != nil { - return err + if k != nil { + err = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { + k.FixNamespace(o, namespace) + return nil + }) + if err != nil { + return err + } } fixed = append(fixed, o) } diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 076a211a6..38d663216 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -20,6 +20,7 @@ import ( type Images struct { rh *registries.RegistryHelper updateImages bool + offline bool fixedImages []types.FixedImage seenImages []types.FixedImage mutex sync.Mutex @@ -27,10 +28,11 @@ type Images struct { registryCache utils.ThreadSafeMultiCache } -func NewImages(rh *registries.RegistryHelper, updateImages bool) (*Images, error) { +func NewImages(rh *registries.RegistryHelper, updateImages bool, offline bool) (*Images, error) { return &Images{ rh: rh, updateImages: updateImages, + offline: offline, }, nil } @@ -74,6 +76,10 @@ func (images *Images) GetFixedImage(image string, namespace string, deployment s } func (images *Images) GetLatestImageFromRegistry(image string, latestVersion string) (*string, error) { + if images.offline { + return nil, nil + } + ret, err := images.registryCache.Get(image, "tag", func() (interface{}, error) { return images.rh.ListImageTags(image) }) From 54be2a55275e848dcb0c83941d5f08af9383945e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 16:01:37 +0200 Subject: [PATCH 0104/2268] f strict --- cmd/kluctl/commands/cmd_archive.go | 2 +- cmd/kluctl/commands/cmd_list_targets.go | 2 +- cmd/kluctl/commands/cmd_seal.go | 2 +- cmd/kluctl/commands/utils.go | 6 ++++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/cmd_archive.go b/cmd/kluctl/commands/cmd_archive.go index bcbeb9f42..5c5325613 100644 --- a/cmd/kluctl/commands/cmd_archive.go +++ b/cmd/kluctl/commands/cmd_archive.go @@ -16,7 +16,7 @@ func (cmd *archiveCmd) Help() string { } func (cmd *archiveCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, func(p *kluctl_project.KluctlProjectContext) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.KluctlProjectContext) error { return p.CreateTGZArchive(cmd.OutputArchive, cmd.ProjectFlags.OutputMetadata == "") }) } diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index 29f9036f3..ba4493622 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -16,7 +16,7 @@ func (cmd *listTargetsCmd) Help() string { } func (cmd *listTargetsCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, func(p *kluctl_project.KluctlProjectContext) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.KluctlProjectContext) error { var result []*types.Target for _, t := range p.DynamicTargets { result = append(result, t.Target) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index ce918cf90..d840403d0 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -119,7 +119,7 @@ func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.KluctlProjectContext, } func (cmd *sealCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, func(p *kluctl_project.KluctlProjectContext) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.KluctlProjectContext) error { hadError := false secretsLoader := seal.NewSecretsLoader(p, cmd.SecretsDir) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 9ac8ac5f5..3d0d860de 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -19,7 +19,7 @@ import ( "time" ) -func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl_project.KluctlProjectContext) error) error { +func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, cb func(p *kluctl_project.KluctlProjectContext) error) error { var url *git_url.GitUrl if projectFlags.ProjectUrl != "" { var err error @@ -41,6 +41,8 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, cb func(p *kluctl } defer j2.Close() + j2.SetStrict(strictTemplates) + cwd, err := os.Getwd() if err != nil { return err @@ -99,7 +101,7 @@ type commandCtx struct { } func withProjectCommandContext(args projectTargetCommandArgs, cb func(ctx *commandCtx) error) error { - return withKluctlProjectFromArgs(args.projectFlags, func(p *kluctl_project.KluctlProjectContext) error { + return withKluctlProjectFromArgs(args.projectFlags, true, func(p *kluctl_project.KluctlProjectContext) error { return withProjectTargetCommandContext(args, p, cb) }) } From fd49ddb0545a9d4ac6271191fb06bd2c3c438fc2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 16:01:58 +0200 Subject: [PATCH 0105/2268] fix: Continue on errors in postprocessAndLoadObjects --- pkg/deployment/deployment_item.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 57e9eea52..336dff69c 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -373,12 +373,15 @@ func (di *DeploymentItem) postprocessAndLoadObjects(k *k8s.K8sCluster, images *I di.Objects = append(di.Objects, uo.FromMap(m)) } + var errList []error for _, o := range di.Objects { commonLabels := di.getCommonLabels() commonAnnotations := di.getCommonAnnotations() - err = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { - k.FixNamespace(o, "default") + _ = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { + if k != nil { + k.FixNamespace(o, "default") + } // Set common labels/annotations o.SetK8sLabels(uo.CopyMergeStrMap(o.GetK8sLabels(), commonLabels)) @@ -387,13 +390,14 @@ func (di *DeploymentItem) postprocessAndLoadObjects(k *k8s.K8sCluster, images *I // Resolve image placeholders err = images.ResolvePlaceholders(k, o, di.relRenderedDir, di.Tags.ListKeys()) if err != nil { - return err + errList = append(errList, err) } return nil }) - if err != nil { - return err - } + } + + if len(errList) != 0 { + return utils.NewErrorListOrNil(errList) } // Need to write it back to disk in case it is needed externally From bdb95ddc6ee02393baa6de6b134f0e59e9d975ae Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 16:02:24 +0200 Subject: [PATCH 0106/2268] fix: Record seenImages even if resolving failed --- pkg/deployment/images.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 38d663216..1becba949 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -239,23 +239,23 @@ func (images *Images) resolveImage(ph *placeHolder, ref k8s2.ObjectRef, deployme result = fixed } + si := types.FixedImage{ + Image: ph.Image, + DeployedImage: deployed, + RegistryImage: registry, + Namespace: &ref.Namespace, + Object: &ref, + Deployment: &deployment, + Container: &ph.Container, + VersionFilter: &ph.LatestVersion, + DeployTags: tags, + DeploymentDir: &deploymentDir, + } if result != nil { - si := types.FixedImage{ - Image: ph.Image, - ResultImage: *result, - DeployedImage: deployed, - RegistryImage: registry, - Namespace: &ref.Namespace, - Object: &ref, - Deployment: &deployment, - Container: &container, - VersionFilter: &ph.LatestVersion, - DeployTags: tags, - DeploymentDir: &deploymentDir, - } - images.mutex.Lock() - images.seenImages = append(images.seenImages, si) - images.mutex.Unlock() + si.ResultImage = *result } + images.mutex.Lock() + images.seenImages = append(images.seenImages, si) + images.mutex.Unlock() return result, nil } From 286fa24d032bfd3b8ef44b80b67491f3b5672ec5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 16:02:47 +0200 Subject: [PATCH 0107/2268] refactor: Split ResolvePlaceholders into ResolvePlaceholders and FindPlaceholders --- pkg/deployment/images.go | 105 +++++++++++++++++++------------- pkg/utils/uo/object_iterator.go | 6 ++ 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 1becba949..517087561 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -114,8 +114,12 @@ type placeHolder struct { Image string `yaml:"image"` LatestVersion string `yaml:"latestVersion"` - startOffset int - endOffset int + Container string + + FieldPath []interface{} + FieldValue string + StartOffset int + EndOffset int } func (images *Images) parsePlaceholder(s string, offset int) (*placeHolder, error) { @@ -137,8 +141,9 @@ func (images *Images) parsePlaceholder(s string, offset int) (*placeHolder, erro if err != nil { return nil, err } - ph.startOffset = start - ph.endOffset = end + len(endPlaceholder) + ph.FieldValue = s + ph.StartOffset = start + ph.EndOffset = end + len(endPlaceholder) return &ph, nil } @@ -155,19 +160,15 @@ func (images *Images) extractContainerName(parent interface{}) string { return "" } -func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredObject, deploymentDir string, tags []string) error { - ref := o.GetK8sRef() - deployment := fmt.Sprintf("%s/%s", ref.GVK.Kind, ref.Name) - - var remoteObject *uo.UnstructuredObject - triedRemoteObject := false +func (images *Images) FindPlaceholders(o *uo.UnstructuredObject) ([]placeHolder, error) { + var ret []placeHolder err := uo.NewObjectIterator(o.Object).IterateLeafs(func(it *uo.ObjectIterator) error { s, ok := it.Value().(string) if !ok { return nil } - newS := "" + container := images.extractContainerName(it.Parent()) offset := 0 @@ -177,54 +178,76 @@ func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredO return err } if ph == nil { - newS += s[offset:] break - } else { - newS += s[offset:ph.startOffset] } + ph.FieldPath = it.KeyPathCopy() + ph.Container = container + ret = append(ret, *ph) + offset = ph.EndOffset + } + return nil + }) + if err != nil { + return nil, err + } + return ret, nil +} + +func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredObject, deploymentDir string, tags []string) error { + placeholders, err := images.FindPlaceholders(o) + if err != nil { + return err + } - if !triedRemoteObject { - triedRemoteObject = true + ref := o.GetK8sRef() + deployment := fmt.Sprintf("%s/%s", ref.GVK.Kind, ref.Name) + + var remoteObject *uo.UnstructuredObject + triedRemoteObject := false + + // iterate backwards so that replacements are easy + for i := len(placeholders) - 1; i >= 0; i-- { + ph := placeholders[i] + + if !triedRemoteObject { + triedRemoteObject = true + if k != nil { remoteObject, _, err = k.GetSingleObject(o.GetK8sRef()) if err != nil && !errors.IsNotFound(err) { return err } } + } - var deployed *string - if remoteObject != nil && ph.startOffset == 0 && ph.endOffset == len(s) { - x, found, _ := remoteObject.GetNestedField(it.KeyPath()...) - if found { - if y, ok := x.(string); ok { - deployed = &y - } + var deployed *string + if remoteObject != nil && ph.StartOffset == 0 && ph.EndOffset == len(ph.FieldValue) { + x, found, _ := remoteObject.GetNestedField(ph.FieldPath...) + if found { + if y, ok := x.(string); ok { + deployed = &y } } + } - resultImage, err := images.resolveImage(ph, ref, deployment, container, deployed, deploymentDir, tags) - if err != nil { - return err - } - if resultImage == nil { - return fmt.Errorf("failed to find image for %s and latest version %s", ph.Image, ph.LatestVersion) - } - newS += *resultImage + resultImage, err := images.resolveImage(ph, ref, deployment, deployed, deploymentDir, tags) + if err != nil { + return err + } + if resultImage == nil { + return fmt.Errorf("failed to find image for %s and latest version %s", ph.Image, ph.LatestVersion) + } - offset = ph.endOffset - if offset >= len(s) { - break - } + ph.FieldValue = ph.FieldValue[:ph.StartOffset] + *resultImage + ph.FieldValue[ph.EndOffset:] + err = o.SetNestedField(ph.FieldValue, ph.FieldPath...) + if err != nil { + return err } - return it.SetValue(newS) - }) - if err != nil { - return err } return nil } -func (images *Images) resolveImage(ph *placeHolder, ref k8s2.ObjectRef, deployment string, container string, deployed *string, deploymentDir string, tags []string) (*string, error) { - fixed := images.GetFixedImage(ph.Image, ref.Namespace, deployment, container) +func (images *Images) resolveImage(ph placeHolder, ref k8s2.ObjectRef, deployment string, deployed *string, deploymentDir string, tags []string) (*string, error) { + fixed := images.GetFixedImage(ph.Image, ref.Namespace, deployment, ph.Container) registry, err := images.GetLatestImageFromRegistry(ph.Image, ph.LatestVersion) if err != nil { diff --git a/pkg/utils/uo/object_iterator.go b/pkg/utils/uo/object_iterator.go index 7c127af98..be922ee93 100644 --- a/pkg/utils/uo/object_iterator.go +++ b/pkg/utils/uo/object_iterator.go @@ -21,6 +21,12 @@ func (it *ObjectIterator) KeyPath() []interface{} { return it.keys } +func (it *ObjectIterator) KeyPathCopy() []interface{} { + ret := make([]interface{}, len(it.keys)) + copy(ret, it.keys) + return ret +} + func (it *ObjectIterator) Key() interface{} { if len(it.keys) == 0 { return nil From 06d1cf2e881b3d97ce9e1c75b0cf5e1937bf70a9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 3 May 2022 16:03:03 +0200 Subject: [PATCH 0108/2268] feat: Implement --fixed-image completion --- cmd/kluctl/commands/completion.go | 72 ++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index fcc341c2c..48c5f8afb 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -11,6 +11,7 @@ import ( "os" "path/filepath" "reflect" + "strings" "sync" "time" ) @@ -20,6 +21,7 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err projectFlags := v.FieldByName("ProjectFlags") targetFlags := v.FieldByName("TargetFlags") inclusionFlags := v.FieldByName("InclusionFlags") + imageFlags := v.FieldByName("ImageFlags") if projectFlags.IsValid() { _ = ccmd.RegisterFlagCompletionFunc("cluster", buildClusterCompletionFunc(projectFlags.Addr().Interface().(*args.ProjectFlags))) @@ -38,13 +40,17 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err _ = ccmd.RegisterFlagCompletionFunc("exclude-deployment-dir", dirsFunc) } + if imageFlags.IsValid() { + _ = ccmd.RegisterFlagCompletionFunc("fixed-image", buildImagesCompletionFunc(cmdStruct)) + } + return nil } func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(p *kluctl_project.KluctlProjectContext) error) error { // let's not update git caches too often projectArgs.GitCacheUpdateInterval = time.Second * 60 - return withKluctlProjectFromArgs(*projectArgs, func(p *kluctl_project.KluctlProjectContext) error { + return withKluctlProjectFromArgs(*projectArgs, false, func(p *kluctl_project.KluctlProjectContext) error { return cb(p) }) } @@ -168,3 +174,67 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd } } } + +func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + ptArgs := buildAutocompleteProjectTargetCommandArgs(cmdStruct) + + if strings.Index(toComplete, "=") != -1 { + return nil, cobra.ShellCompDirectiveDefault + } + + var images utils.OrderedMap + var mutex sync.Mutex + + err := withProjectForCompletion(&ptArgs.projectFlags, func(p *kluctl_project.KluctlProjectContext) error { + var targets []string + if ptArgs.targetFlags.Target == "" { + for _, t := range p.DynamicTargets { + targets = append(targets, t.Target.Name) + } + } else { + targets = append(targets, ptArgs.targetFlags.Target) + } + + var wg sync.WaitGroup + for _, t := range targets { + ptArgs := ptArgs + ptArgs.targetFlags.Target = t + wg.Add(1) + go func() { + _ = withProjectTargetCommandContext(ptArgs, p, func(ctx *commandCtx) error { + err := ctx.targetCtx.DeploymentCollection.Prepare(nil) + if err != nil { + log.Error(err) + } + + mutex.Lock() + defer mutex.Unlock() + for _, si := range ctx.images.SeenImages(false) { + str := si.Image + if si.Namespace != nil { + str += ":" + *si.Namespace + } + if si.Deployment != nil { + str += ":" + *si.Deployment + } + if si.Container != nil { + str += ":" + *si.Container + } + images.Set(str, true) + } + return nil + }) + wg.Done() + }() + } + wg.Wait() + return nil + }) + if err != nil { + log.Error(err) + return nil, cobra.ShellCompDirectiveError + } + return images.ListKeys(), cobra.ShellCompDirectiveNoSpace + } +} From c7ed62bf0aa7397b14249788170c3db45b0f8dc4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 May 2022 09:05:04 +0200 Subject: [PATCH 0109/2268] fix: Properly handle args via env vars with dashes --- cmd/kluctl/commands/root.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 68a8fbb2a..a4f1c35bc 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -147,6 +147,7 @@ func initViper() { } viper.SetEnvPrefix("kluctl") + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) viper.AutomaticEnv() } From 2df3e9cb4f10555850e8d5dbe7ec0ae0df5095df Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 May 2022 09:31:19 +0200 Subject: [PATCH 0110/2268] fix: Fix unexpected log line when skipping deployment items --- pkg/deployment/commands/downscale.go | 2 +- pkg/deployment/commands/poke_images.go | 2 +- pkg/deployment/commands/validate.go | 2 +- pkg/deployment/utils/apply_utils.go | 28 ++++++++++++++++++++++---- pkg/deployment/utils/progress.go | 8 +++++--- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/pkg/deployment/commands/downscale.go b/pkg/deployment/commands/downscale.go index edd25944e..f12d86d6b 100644 --- a/pkg/deployment/commands/downscale.go +++ b/pkg/deployment/commands/downscale.go @@ -39,7 +39,7 @@ func (cmd *DownscaleCommand) Run(k *k8s.K8sCluster) (*types.CommandResult, error if !d.CheckInclusionForDeploy() { continue } - au := ad.NewApplyUtil(utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0)) + au := ad.NewApplyUtil(utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0, true)) for _, o := range d.Objects { o := o ref := o.GetK8sRef() diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 9e2fc9173..d325da3b2 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -75,7 +75,7 @@ func (cmd *PokeImagesCommand) Run(k *k8s.K8sCluster) (*types.CommandResult, erro wg.Add(1) go func() { defer wg.Done() - au := ad.NewApplyUtil(utils2.NewProgressCtx(nil, ref.String(), 0)) + au := ad.NewApplyUtil(utils2.NewProgressCtx(nil, ref.String(), 0, true)) au.ReplaceObject(ref, ru.GetRemoteObject(ref), func(o *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { return doPokeImage(containers, o) }) diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 3a94cc032..5fa33f251 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -39,7 +39,7 @@ func (cmd *ValidateCommand) Run(k *k8s.K8sCluster) (*types.ValidateResult, error if !d.CheckInclusionForDeploy() { continue } - au := ad.NewApplyUtil(utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0)) + au := ad.NewApplyUtil(utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0, true)) h := utils2.NewHooksUtil(au) for _, o := range d.Objects { hook := h.GetHook(o) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index ebbca95c1..3f69a8416 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -529,6 +529,17 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { a.pctx.Finish() } +func (a *ApplyDeploymentsUtil) buildProgressName(d *deployment.DeploymentItem) *string { + if d.RelToProjectItemDir != "" { + return &d.RelToProjectItemDir + } + if len(d.Config.DeleteObjects) != 0 { + s := "" + return &s + } + return nil +} + func (a *ApplyDeploymentsUtil) ApplyDeployments() { log.Infof("Running server-side apply for all objects") @@ -542,8 +553,11 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { maxNameLen := 0 for _, d := range a.deployments { - if len(d.RelToProjectItemDir) > maxNameLen { - maxNameLen = len(d.RelToProjectItemDir) + name := a.buildProgressName(d) + if name != nil { + if len(*name) > maxNameLen { + maxNameLen = len(*name) + } } } @@ -555,7 +569,13 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { _ = sem.Acquire(context.Background(), 1) - pctx := NewProgressCtx(p, d.RelToProjectItemDir, maxNameLen) + progressName := a.buildProgressName(d) + var pctx *progressCtx + if progressName != nil { + pctx = NewProgressCtx(p, *progressName, maxNameLen, true) + } else { + pctx = NewProgressCtx(nil, "", 0, false) + } a2 := a.NewApplyUtil(pctx) wg.Add(1) @@ -568,7 +588,7 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { barrier := (d.Config.Barrier != nil && *d.Config.Barrier) || d.Barrier if barrier { - bpctx := NewProgressCtx(p, "", maxNameLen) + bpctx := NewProgressCtx(p, "", maxNameLen, true) bpctx.SetTotal(1) bpctx.InfofAndStatus("Waiting on barrier...") diff --git a/pkg/deployment/utils/progress.go b/pkg/deployment/utils/progress.go index 8ac3a0f81..c5eadbbfc 100644 --- a/pkg/deployment/utils/progress.go +++ b/pkg/deployment/utils/progress.go @@ -15,6 +15,7 @@ import ( type progressCtx struct { bar *mpb.Bar + doLog bool total int64 name string status string @@ -22,14 +23,15 @@ type progressCtx struct { mutex sync.Mutex } -func NewProgressCtx(p *mpb.Progress, name string, maxNameWidth int) *progressCtx { +func NewProgressCtx(p *mpb.Progress, name string, maxNameWidth int, doLog bool) *progressCtx { pctx := &progressCtx{ status: "Initializing...", total: -1, name: name, startTime: time.Now(), } - if !isatty.IsTerminal(os.Stderr.Fd()) || name == "" { + if !isatty.IsTerminal(os.Stderr.Fd()) || p == nil { + pctx.doLog = doLog return pctx } @@ -51,7 +53,7 @@ func NewProgressCtx(p *mpb.Progress, name string, maxNameWidth int) *progressCtx } func (ctx *progressCtx) Logf(level log.Level, s string, args ...interface{}) { - if ctx.bar == nil { + if ctx.doLog { s = fmt.Sprintf("%s: %s", ctx.name, s) log.StandardLogger().Logf(level, s, args...) } From 2b74f16874771d294706db6f5a11412cfb803220 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 5 May 2022 10:46:19 +0200 Subject: [PATCH 0111/2268] build: Upgrade python to 3.10.4 --- pkg/python/generate/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/python/generate/main.go b/pkg/python/generate/main.go index f3ffc0816..dc68cc409 100644 --- a/pkg/python/generate/main.go +++ b/pkg/python/generate/main.go @@ -15,8 +15,8 @@ import ( const ( pythonVersionBase = "3.10" - pythonVersionFull = "3.10.3" - pythonStandaloneVersion = "20220318" + pythonVersionFull = "3.10.4" + pythonStandaloneVersion = "20220502" ) var pythonDists = map[string]string{ From 6c3830b6e820559017659b4c7cc3cbf5a6ccaa01 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 5 May 2022 10:52:06 +0200 Subject: [PATCH 0112/2268] fix: Use debian:bullseye-slim for docker images --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 991b24a50..cd4912bac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,8 @@ RUN wget -O helm.tar.gz https://get.helm.sh/helm-$HELM_VERSION-$ARCH.tar.gz && \ tar xzf helm.tar.gz && \ mv $ARCH/helm / -FROM alpine +# We must use a glibc based distro due to embedded python not supporting musl libc for aarch64 +FROM debian:bullseye-slim COPY --from=builder /helm /usr/bin COPY kluctl /usr/bin/ ENTRYPOINT ["/usr/bin/kluctl"] From a34a20a06c3529bf696549d4146b9c429b43ec86 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 5 May 2022 16:04:59 +0200 Subject: [PATCH 0113/2268] docs: Mention that go 1.18 is the minimum go version now --- install/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/README.md b/install/README.md index 09f38f6e3..a02fc9390 100644 --- a/install/README.md +++ b/install/README.md @@ -28,7 +28,7 @@ git clone https://github.com/kluctl/kluctl cd kluctl ``` -Build the `kluctl` binary (requires go >= 1.17 and python >= 3.10): +Build the `kluctl` binary (requires go >= 1.18 and python >= 3.10): ```bash make build From 861c75fd209e4b2702f30c882eb1f9ca19d778f9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 6 May 2022 10:04:39 +0200 Subject: [PATCH 0114/2268] refactor: Use filepath.ToSlash and filepath.FromSlash whenever possible --- cmd/kluctl/args/inclusion.go | 6 ++---- pkg/deployment/deployment_item.go | 6 +++--- pkg/jinja2/jinja2.go | 2 +- pkg/kluctl_project/git.go | 2 +- pkg/utils/embed_util/extract.go | 2 +- pkg/utils/embed_util/packer/packer.go | 3 +-- pkg/utils/fs.go | 4 ++-- pkg/utils/tar.go | 9 ++++----- 8 files changed, 15 insertions(+), 19 deletions(-) diff --git a/cmd/kluctl/args/inclusion.go b/cmd/kluctl/args/inclusion.go index 060a6af97..1a63195c2 100644 --- a/cmd/kluctl/args/inclusion.go +++ b/cmd/kluctl/args/inclusion.go @@ -3,9 +3,7 @@ package args import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" - "os" "path/filepath" - "strings" ) type InclusionFlags struct { @@ -27,13 +25,13 @@ func (args *InclusionFlags) ParseInclusionFromArgs() (*utils.Inclusion, error) { if filepath.IsAbs(dir) { return nil, fmt.Errorf("--include-deployment-dir path must be relative") } - inclusion.AddInclude("deploymentItemDir", strings.ReplaceAll(dir, string(os.PathSeparator), "/")) + inclusion.AddInclude("deploymentItemDir", filepath.ToSlash(dir)) } for _, dir := range args.ExcludeDeploymentDir { if filepath.IsAbs(dir) { return nil, fmt.Errorf("--exclude-deployment-dir path must be relative") } - inclusion.AddExclude("deploymentItemDir", strings.ReplaceAll(dir, string(os.PathSeparator), "/")) + inclusion.AddExclude("deploymentItemDir", filepath.ToSlash(dir)) } return inclusion, nil } diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 336dff69c..17f6f746c 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -100,7 +100,7 @@ func (di *DeploymentItem) getCommonLabels() map[string]string { func (di *DeploymentItem) getCommonAnnotations() map[string]string { // TODO change it to kluctl.io/deployment_dir a := map[string]string{ - "kluctl.io/kustomize_dir": strings.ReplaceAll(di.RelToRootItemDir, string(os.PathSeparator), "/"), + "kluctl.io/kustomize_dir": filepath.ToSlash(di.RelToRootItemDir), } if di.Config.SkipDeleteIfTags != nil && *di.Config.SkipDeleteIfTags { a["kluctl.io/skip-delete-if-tags"] = "true" @@ -137,7 +137,7 @@ func (di *DeploymentItem) render(k *k8s.K8sCluster, forSeal bool, wp *utils.Work if yaml.Exists(filepath.Join(p, "helm-chart.yml")) { // never try to render helm charts ep := filepath.Join(di.RelToProjectItemDir, relDir, "charts/**") - ep = strings.ReplaceAll(ep, string(os.PathSeparator), "/") + ep = filepath.ToSlash(ep) excludePatterns = append(excludePatterns, ep) return filepath.SkipDir } @@ -250,7 +250,7 @@ func (di *DeploymentItem) buildInclusionEntries() []utils.InclusionEntry { values = append(values, utils.InclusionEntry{Type: "tag", Value: t}) } if di.dir != nil { - dir := strings.ReplaceAll(di.RelToRootItemDir, string(os.PathSeparator), "/") + dir := filepath.ToSlash(di.RelToRootItemDir) values = append(values, utils.InclusionEntry{Type: "deploymentItemDir", Value: dir}) } return values diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go index fa04d3cd7..023e89c08 100644 --- a/pkg/jinja2/jinja2.go +++ b/pkg/jinja2/jinja2.go @@ -198,7 +198,7 @@ func (j *Jinja2) getGlob(pattern string) (glob.Glob, error) { return g.(glob.Glob), nil } func (j *Jinja2) needsRender(path string, excludedPatterns []string) bool { - path = strings.ReplaceAll(path, string(os.PathSeparator), "/") + path = filepath.ToSlash(path) for _, p := range excludedPatterns { g, err := j.getGlob(p) diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 23072f103..255dcb7cf 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -122,7 +122,7 @@ func (c *KluctlProjectContext) buildCloneDir(u git_url.GitUrl, ref string) (stri } ref = strings.ReplaceAll(ref, "/", "-") ref = strings.ReplaceAll(ref, "\\", "-") - urlPath := strings.ReplaceAll(u.Path, "/", string(os.PathSeparator)) + urlPath := filepath.FromSlash(u.Path) baseName := filepath.Base(urlPath) urlHash := utils.Sha256String(fmt.Sprintf("%s:%s", u.Host, u.Path))[:16] cloneDir := filepath.Join(c.TmpDir, fmt.Sprintf("%s-%s", baseName, urlHash), ref) diff --git a/pkg/utils/embed_util/extract.go b/pkg/utils/embed_util/extract.go index 16ed318ba..22267376c 100644 --- a/pkg/utils/embed_util/extract.go +++ b/pkg/utils/embed_util/extract.go @@ -120,7 +120,7 @@ func BuildFileList(targetPath string) (map[string]int64, error) { if err != nil { return err } - relPath = strings.ReplaceAll(relPath, string(os.PathSeparator), "/") + relPath = filepath.ToSlash(relPath) if info.IsDir() { existingFiles[relPath] = 0 } else { diff --git a/pkg/utils/embed_util/packer/packer.go b/pkg/utils/embed_util/packer/packer.go index d02da2d7f..fd6fdd077 100644 --- a/pkg/utils/embed_util/packer/packer.go +++ b/pkg/utils/embed_util/packer/packer.go @@ -13,7 +13,6 @@ import ( log "github.com/sirupsen/logrus" "io/fs" "io/ioutil" - "os" "path/filepath" "reflect" "strings" @@ -75,7 +74,7 @@ func findFiles(dir string, patterns []string) (map[string]int64, error) { } match := false for _, p := range globs { - if p.Match(strings.ReplaceAll(rel, string(os.PathSeparator), "/")) { + if p.Match(filepath.ToSlash(rel)) { match = true break } diff --git a/pkg/utils/fs.go b/pkg/utils/fs.go index 30dbcfc1c..aa776b51c 100644 --- a/pkg/utils/fs.go +++ b/pkg/utils/fs.go @@ -58,7 +58,7 @@ func CopyFile(src string, dst string) error { } func FsCopyFile(srcFs fs.FS, src, dst string) error { - src = strings.ReplaceAll(src, string(os.PathSeparator), "/") + src = filepath.ToSlash(src) source, err := srcFs.Open(src) if err != nil { return err @@ -92,7 +92,7 @@ func FsCopyDir(srcFs fs.FS, src string, dst string) error { var err error var fds []fs.DirEntry - src = strings.ReplaceAll(src, string(os.PathSeparator), "/") + src = filepath.ToSlash(src) if fds, err = fs.ReadDir(srcFs, src); err != nil { return err diff --git a/pkg/utils/tar.go b/pkg/utils/tar.go index 0cd6807ed..627f531bf 100644 --- a/pkg/utils/tar.go +++ b/pkg/utils/tar.go @@ -9,7 +9,6 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" ) func ExtractTarGzFile(tarGzPath string, targetPath string) error { @@ -48,7 +47,7 @@ func ExtractTarStream(r io.Reader, targetPath string) error { return fmt.Errorf("ExtractTarStream: Next() failed: %w", err) } - header.Name = strings.ReplaceAll(header.Name, "/", string(os.PathSeparator)) + header.Name = filepath.FromSlash(header.Name) p := filepath.Join(targetPath, header.Name) err = os.MkdirAll(filepath.Dir(p), 0755) @@ -105,7 +104,7 @@ func AddToTar(tw *tar.Writer, pth string, name string, filter func(h *tar.Header if err != nil { return err } - h.Name = strings.ReplaceAll(name, string(os.PathSeparator), "/") + h.Name = filepath.ToSlash(name) if filter != nil { s := fi.Size() @@ -159,14 +158,14 @@ func AddToTar(tw *tar.Writer, pth string, name string, filter func(h *tar.Header } func HashTarEntry(dir string, name string) (string, error) { - p := filepath.Join(dir, strings.ReplaceAll(name, "/", string(os.PathSeparator))) + p := filepath.Join(dir, filepath.FromSlash(name)) st, err := os.Lstat(p) if err != nil { return "", err } var hashData []byte if st.Mode().Type() == fs.ModeDir { - hashData = []byte(strings.ReplaceAll(name, string(os.PathSeparator), "/")) + hashData = []byte(filepath.ToSlash(name)) } else if st.Mode().Type() == fs.ModeSymlink { l, err := os.Readlink(p) if err != nil { From 1a0a4f926c29ae8c63fd82b86ad1f6ef420151af Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 6 May 2022 10:06:09 +0200 Subject: [PATCH 0115/2268] fix: Use SecureJoin in places where user-provided paths are joined --- pkg/deployment/commands/seal.go | 8 ++++-- pkg/deployment/deployment_item.go | 16 ++++++++--- pkg/deployment/helm_chart.go | 43 ++++++++++++++++++++++-------- pkg/git/mirrored_repo.go | 2 ++ pkg/jinja2/jinja2.go | 8 ++++-- pkg/kluctl_project/git.go | 11 ++++++-- pkg/kluctl_project/load.go | 10 ++++--- pkg/kluctl_project/load_targets.go | 7 +++-- pkg/seal/secrets_loader.go | 27 ++++++++++--------- pkg/types/cluster_config.go | 7 ++++- pkg/utils/fs.go | 30 +++++++++++++++++++++ pkg/utils/tar.go | 6 ++++- 12 files changed, 136 insertions(+), 39 deletions(-) diff --git a/pkg/deployment/commands/seal.go b/pkg/deployment/commands/seal.go index cc255a91c..69110cf0d 100644 --- a/pkg/deployment/commands/seal.go +++ b/pkg/deployment/commands/seal.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/seal" "io/fs" @@ -33,8 +34,11 @@ func (cmd *SealCommand) Run(sealer *seal.Sealer) error { if err != nil { return err } - targetDir := filepath.Join(cmd.c.Project.SealedSecretsDir, filepath.Dir(relPath)) - targetFile := filepath.Join(targetDir, *cmd.c.Project.Config.SealedSecrets.OutputPattern, filepath.Base(p)) + relTargetFile := filepath.Join(filepath.Dir(relPath), *cmd.c.Project.Config.SealedSecrets.OutputPattern, filepath.Base(p)) + targetFile, err := securejoin.SecureJoin(cmd.c.Project.SealedSecretsDir, relTargetFile) + if err != nil { + return err + } targetFile = targetFile[:len(targetFile)-len(deployment.SealmeExt)] err = sealer.SealFile(p, targetFile) if err != nil { diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 17f6f746c..acb5e7aa3 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -2,6 +2,7 @@ package deployment import ( "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -190,13 +191,17 @@ func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { return nil } - // TODO check for bootstrap - sealedSecretsDir := di.Project.getSealedSecretsDir() baseSourcePath := di.Project.SealedSecretsDir renderedDir := filepath.Join(di.renderedDir, subdir) + // ensure we're not leaving the project + _, err := securejoin.SecureJoin(di.Project.getRootProject().dir, subdir) + if err != nil { + return err + } + y, err := uo.FromFile(yaml.FixPathExt(filepath.Join(renderedDir, "kustomization.yml"))) if err != nil { return err @@ -227,7 +232,12 @@ func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { if sealedSecretsDir == nil { return fmt.Errorf("%s. Sealed secrets dir could not be determined", baseError) } - sourcePath := filepath.Clean(filepath.Join(baseSourcePath, filepath.Join(di.relRenderedDir, subdir), relDir, *sealedSecretsDir, fname)) + // ensure we're not leaving the .sealed-secrets dir + sourcePath, err := securejoin.SecureJoin(baseSourcePath, filepath.Join(di.relRenderedDir, subdir, relDir, *sealedSecretsDir, fname)) + if err != nil { + return err + } + sourcePath = filepath.Clean(sourcePath) targetPath := filepath.Join(renderedDir, relDir, fname) if !utils.IsFile(sourcePath) { return fmt.Errorf("%s. %s not found. You might need to seal it first", baseError, sourcePath) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 23a89200f..8c719c2a2 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -2,6 +2,7 @@ package deployment import ( "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -61,6 +62,22 @@ func (c *helmChart) GetChartName() (string, error) { return *c.Config.ChartName, nil } +func (c *helmChart) GetChartDir() (string, error) { + chartName, err := c.GetChartName() + if err != nil { + return "", err + } + + dir := filepath.Dir(c.configFile) + targetDir := filepath.Join(dir, "charts") + return securejoin.SecureJoin(targetDir, chartName) +} + +func (c *helmChart) GetOutputPath() (string, error) { + dir := filepath.Dir(c.configFile) + return securejoin.SecureJoin(dir, c.Config.Output) +} + func (c *helmChart) buildHelmConfig() (*action.Configuration, error) { rc, err := registry.NewClient() if err != nil { @@ -77,10 +94,13 @@ func (c *helmChart) Pull() error { return err } - dir := filepath.Dir(c.configFile) - targetDir := filepath.Join(dir, "charts") - rmDir := filepath.Join(targetDir, chartName) - _ = os.RemoveAll(rmDir) + chartDir, err := c.GetChartDir() + if err != nil { + return err + } + + targetDir := filepath.Join(filepath.Dir(c.configFile), "charts") + _ = os.RemoveAll(chartDir) cfg, err := c.buildHelmConfig() if err != nil { @@ -100,8 +120,8 @@ func (c *helmChart) Pull() error { out, err = a.Run(chartName) } // a bug in the Pull command causes this directory to be created by accident - _ = os.RemoveAll(rmDir + fmt.Sprintf("-%s.tar.gz", a.Version)) - _ = os.RemoveAll(rmDir + fmt.Sprintf("-%s.tgz", a.Version)) + _ = os.RemoveAll(chartDir + fmt.Sprintf("-%s.tar.gz", a.Version)) + _ = os.RemoveAll(chartDir + fmt.Sprintf("-%s.tgz", a.Version)) if out != "" { log.Info(out) } @@ -170,14 +190,15 @@ func (c *helmChart) Render(k *k8s.K8sCluster) error { } func (c *helmChart) doRender(k *k8s.K8sCluster) error { - chartName, err := c.GetChartName() + chartDir, err := c.GetChartDir() if err != nil { return err } - dir := filepath.Dir(c.configFile) - chartDir := filepath.Join(dir, "charts", chartName) - valuesPath := yaml.FixPathExt(filepath.Join(dir, "helm-values.yml")) - outputPath := filepath.Join(dir, c.Config.Output) + outputPath, err := c.GetOutputPath() + if err != nil { + return err + } + valuesPath := yaml.FixPathExt(filepath.Join(filepath.Dir(c.configFile), "helm-values.yml")) var gvs []string if k != nil { diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 0afba2cd9..fc8d40fdc 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -342,6 +342,8 @@ func (g *MirroredGitRepo) CloneProject(ctx context.Context, ref string, targetDi func buildMirrorRepoName(u git_url.GitUrl) string { r := filepath.Base(u.Path) + r = strings.ReplaceAll(r, "/", "-") + r = strings.ReplaceAll(r, "\\", "-") if strings.HasSuffix(r, ".git") { r = r[:len(r)-len(".git")] } diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go index 023e89c08..83e176870 100644 --- a/pkg/jinja2/jinja2.go +++ b/pkg/jinja2/jinja2.go @@ -2,6 +2,7 @@ package jinja2 import ( "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/gobwas/glob" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -213,11 +214,14 @@ func (j *Jinja2) needsRender(path string, excludedPatterns []string) bool { } func (j *Jinja2) RenderDirectory(rootDir string, searchDirs []string, relSourceDir string, excludePatterns []string, subdir string, targetDir string, vars *uo.UnstructuredObject) error { - walkDir := filepath.Join(rootDir, relSourceDir, subdir) + walkDir, err := securejoin.SecureJoin(rootDir, filepath.Join(relSourceDir, subdir)) + if err != nil { + return err + } var jobs []*RenderJob - err := filepath.WalkDir(walkDir, func(p string, d fs.DirEntry, err error) error { + err = filepath.WalkDir(walkDir, func(p string, d fs.DirEntry, err error) error { relPath, err := filepath.Rel(walkDir, p) if err != nil { return err diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 255dcb7cf..edf405701 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -3,6 +3,7 @@ package kluctl_project import ( "context" "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/git" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" types2 "github.com/kluctl/kluctl/v2/pkg/types" @@ -125,7 +126,10 @@ func (c *KluctlProjectContext) buildCloneDir(u git_url.GitUrl, ref string) (stri urlPath := filepath.FromSlash(u.Path) baseName := filepath.Base(urlPath) urlHash := utils.Sha256String(fmt.Sprintf("%s:%s", u.Host, u.Path))[:16] - cloneDir := filepath.Join(c.TmpDir, fmt.Sprintf("%s-%s", baseName, urlHash), ref) + cloneDir, err := securejoin.SecureJoin(c.TmpDir, filepath.Join(fmt.Sprintf("%s-%s", baseName, urlHash), ref)) + if err != nil { + return "", err + } log.Tracef("buildCloneDir: ref=%s, urlPath=%s, baseName=%s, urlHash=%s, cloneDir=%s", ref, urlPath, baseName, urlHash, cloneDir) return cloneDir, nil } @@ -147,7 +151,10 @@ func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject t } dir := targetDir if subDir != "" { - dir = filepath.Join(dir, subDir) + dir, err = securejoin.SecureJoin(dir, subDir) + if err != nil { + return gitProjectInfo{}, err + } } mr, ok := c.mirroredRepos[gitProject.Project.Url.NormalizedRepoKey()] diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 511a514fd..26efa5dd0 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -5,6 +5,7 @@ import ( "compress/gzip" "context" "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/jinja2" types2 "github.com/kluctl/kluctl/v2/pkg/types" @@ -84,6 +85,7 @@ func (c *KluctlProjectContext) load(ctx context.Context, allowGit bool) error { return c.localProject(localDir), nil } if ep == nil || ep.Project == nil { + // the ExternalProject is pointing to the same repo that the root project is located in p := kluctlProjectInfo.dir if ep != nil { if filepath.IsAbs(*ep.Path) { @@ -98,12 +100,14 @@ func (c *KluctlProjectContext) load(ctx context.Context, allowGit bool) error { if err != nil { return gitProjectInfo{}, err } - p, err = filepath.Abs(filepath.Join(p, *ep.Path)) + relToGitRoot, err := filepath.Rel(gitRoot, p) if err != nil { return gitProjectInfo{}, err } - if !strings.HasPrefix(p, gitRoot) { - return gitProjectInfo{}, fmt.Errorf("path '%s' is not inside git project root '%s'", *ep.Path, gitRoot) + relToGitRoot = filepath.Join(relToGitRoot, *ep.Path) + p, err = securejoin.SecureJoin(gitRoot, relToGitRoot) + if err != nil { + return gitProjectInfo{}, fmt.Errorf("path '%s' is not inside git project root '%s': %w", relToGitRoot, gitRoot, err) } } else { if defaultGitSubDir != "" { diff --git a/pkg/kluctl_project/load_targets.go b/pkg/kluctl_project/load_targets.go index ec20c2f0f..0148e6785 100644 --- a/pkg/kluctl_project/load_targets.go +++ b/pkg/kluctl_project/load_targets.go @@ -3,6 +3,7 @@ package kluctl_project import ( "context" "fmt" + securejoin "github.com/cyphar/filepath-securejoin" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/types" @@ -10,7 +11,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" - "path/filepath" "reflect" "regexp" "sort" @@ -320,7 +320,10 @@ func (c *KluctlProjectContext) buildDynamicTarget(targetInfo *dynamicTargetInfo) if targetInfo.baseTarget.TargetConfig.File != nil { configFile = *targetInfo.baseTarget.TargetConfig.File } - configPath := filepath.Join(targetInfo.dir, configFile) + configPath, err := securejoin.SecureJoin(targetInfo.dir, configFile) + if err != nil { + return nil, err + } if !utils.IsFile(configPath) { return nil, fmt.Errorf("no target config file with name %s found in target", configFile) } diff --git a/pkg/seal/secrets_loader.go b/pkg/seal/secrets_loader.go index 8241d9547..ef0e37fe9 100644 --- a/pkg/seal/secrets_loader.go +++ b/pkg/seal/secrets_loader.go @@ -2,6 +2,7 @@ package seal import ( "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -9,7 +10,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "os" "path/filepath" - "strings" ) type usernamePassword struct { @@ -26,8 +26,8 @@ type SecretsLoader struct { func NewSecretsLoader(p *kluctl_project.KluctlProjectContext, secretsDir string) *SecretsLoader { return &SecretsLoader{ - project: p, - secretsDir: secretsDir, + project: p, + secretsDir: secretsDir, credentialsCache: map[string]usernamePassword{}, } } @@ -48,21 +48,24 @@ func (s *SecretsLoader) LoadSecrets(source *types.SecretSource) (*uo.Unstructure func (s *SecretsLoader) loadSecretsFile(source *types.SecretSource) (*uo.UnstructuredObject, error) { var p string + var err error if utils.Exists(filepath.Join(s.project.DeploymentDir, *source.Path)) { - p = filepath.Join(s.project.DeploymentDir, *source.Path) + p, err = securejoin.SecureJoin(s.project.DeploymentDir, *source.Path) + if err != nil { + return nil, err + } } else if utils.Exists(filepath.Join(s.secretsDir, *source.Path)) { - p = filepath.Join(s.secretsDir, *source.Path) - } - if p == "" || !utils.Exists(p) { + p, err = securejoin.SecureJoin(s.secretsDir, *source.Path) + if err != nil { + return nil, err + } + } else { return nil, fmt.Errorf("secrets file %s does not exist", *source.Path) } - abs, err := filepath.Abs(p) + err = utils.CheckInDir(s.project.DeploymentDir, p) if err != nil { - return nil, err - } - if !strings.HasPrefix(abs, s.project.DeploymentDir) { - return nil, fmt.Errorf("secrets file %s is not part of the deployment project", *source.Path) + return nil, fmt.Errorf("secrets file %s is not part of the deployment project: %w", *source.Path, err) } secrets, err := uo.FromFile(p) diff --git a/pkg/types/cluster_config.go b/pkg/types/cluster_config.go index 0c22be6b1..fe8ef11b2 100644 --- a/pkg/types/cluster_config.go +++ b/pkg/types/cluster_config.go @@ -64,8 +64,13 @@ func LoadClusterConfig(clusterDir string, clusterName string) (*ClusterConfig, e return nil, fmt.Errorf("cluster config for %s not found", clusterName) } + err := utils.CheckInDir(clusterDir, p) + if err != nil { + return nil, fmt.Errorf("cluster config for %s is not in cluster dir", clusterName) + } + var config ClusterConfig - err := yaml.ReadYamlFile(p, &config) + err = yaml.ReadYamlFile(p, &config) if err != nil { return nil, err } diff --git a/pkg/utils/fs.go b/pkg/utils/fs.go index aa776b51c..422538a73 100644 --- a/pkg/utils/fs.go +++ b/pkg/utils/fs.go @@ -37,6 +37,36 @@ func IsDirectory(path string) bool { return fileInfo.IsDir() } +func CheckInDir(root string, path string) error { + absRoot, err := filepath.Abs(filepath.Clean(root)) + if err != nil { + return err + } + absPath, err := filepath.Abs(filepath.Clean(path)) + if err != nil { + return err + } + + absRoot, err = filepath.EvalSymlinks(absRoot) + if err != nil { + return err + } + + absPath, err = filepath.EvalSymlinks(absPath) + if err != nil { + return err + } + + if absRoot == absPath { + return nil + } + + if !strings.HasPrefix(absPath, absRoot+string(os.PathSeparator)) { + return fmt.Errorf("path %s is not inside directory %s", path, root) + } + return nil +} + func Touch(path string) error { f, err := os.Create(path) if err != nil { diff --git a/pkg/utils/tar.go b/pkg/utils/tar.go index 627f531bf..0425dc6e6 100644 --- a/pkg/utils/tar.go +++ b/pkg/utils/tar.go @@ -4,6 +4,7 @@ import ( "archive/tar" "compress/gzip" "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "io" "io/fs" "io/ioutil" @@ -49,7 +50,10 @@ func ExtractTarStream(r io.Reader, targetPath string) error { header.Name = filepath.FromSlash(header.Name) - p := filepath.Join(targetPath, header.Name) + p, err := securejoin.SecureJoin(targetPath, header.Name) + if err != nil { + return err + } err = os.MkdirAll(filepath.Dir(p), 0755) if err != nil { return err From 51c6b4fd037c3f1b100d06bd62d376b85e179c8b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 6 May 2022 10:06:21 +0200 Subject: [PATCH 0116/2268] fix: Stop using http caching for k8s clients This caused errors to be cached which should have been resolved after CRDs got deployed. --- pkg/k8s/k8s_cluster.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 59871f052..33265da1d 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -90,8 +90,7 @@ func NewK8sCluster(configIn *rest.Config, dryRun bool) (*K8sCluster, error) { return nil, err } discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(), "kube-cache/discovery", apiHost.Hostname()) - httpCacheDir := filepath.Join(utils.GetTmpBaseDir(), "kube-cache/http", apiHost.Hostname()) - discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(k.restConfig), discoveryCacheDir, httpCacheDir, time.Hour*24) + discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(k.restConfig), discoveryCacheDir, "", time.Hour*24) if err != nil { return nil, err } From 22ff5ecf437c7e9102f311238e5e256d83f6d39e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 6 May 2022 10:52:00 +0200 Subject: [PATCH 0117/2268] refactor: Allow to pass RepoRoot from outside --- cmd/kluctl/commands/utils.go | 8 ++++ pkg/kluctl_project/git.go | 24 +++++------ pkg/kluctl_project/load.go | 64 +++++++++++++++--------------- pkg/kluctl_project/load_targets.go | 3 +- pkg/kluctl_project/project.go | 1 + 5 files changed, 52 insertions(+), 48 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 3d0d860de..5e90ea31a 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/jinja2" @@ -12,6 +13,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/registries" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" + log "github.com/sirupsen/logrus" "io/ioutil" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -48,7 +50,13 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b return err } + repoRoot, err := git.DetectGitRepositoryRoot(cwd) + if err != nil { + log.Warning("Failed to detect git project root. This might cause follow-up errors") + } + loadArgs := kluctl_project.LoadKluctlProjectArgs{ + RepoRoot: repoRoot, ProjectDir: cwd, ProjectUrl: url, ProjectRef: projectFlags.ProjectRef, diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index edf405701..0c5a641fd 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -134,18 +134,18 @@ func (c *KluctlProjectContext) buildCloneDir(u git_url.GitUrl, ref string) (stri return cloneDir, nil } -func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject types2.ExternalProject, defaultGitSubDir string, doAddInvolvedRepo bool, doLock bool) (result gitProjectInfo, err error) { +func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject *types2.GitProject, defaultGitSubDir string, doAddInvolvedRepo bool, doLock bool) (result gitProjectInfo, err error) { err = os.MkdirAll(filepath.Join(c.TmpDir, "git"), 0o700) if err != nil { return } - targetDir, err := c.buildCloneDir(gitProject.Project.Url, gitProject.Project.Ref) + targetDir, err := c.buildCloneDir(gitProject.Url, gitProject.Ref) if err != nil { return } - subDir := gitProject.Project.SubDir + subDir := gitProject.SubDir if subDir == "" { subDir = defaultGitSubDir } @@ -157,13 +157,13 @@ func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject t } } - mr, ok := c.mirroredRepos[gitProject.Project.Url.NormalizedRepoKey()] + mr, ok := c.mirroredRepos[gitProject.Url.NormalizedRepoKey()] if !ok { - mr, err = git.NewMirroredGitRepo(gitProject.Project.Url) + mr, err = git.NewMirroredGitRepo(gitProject.Url) if err != nil { return } - c.mirroredRepos[gitProject.Project.Url.NormalizedRepoKey()] = mr + c.mirroredRepos[gitProject.Url.NormalizedRepoKey()] = mr err = mr.Lock(ctx) if err != nil { return @@ -176,7 +176,7 @@ func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject t if err != nil { return err } - return mr.CloneProject(ctx, gitProject.Project.Ref, targetDir) + return mr.CloneProject(ctx, gitProject.Ref, targetDir) }) if err != nil { return @@ -187,7 +187,7 @@ func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject t return } - result.url = gitProject.Project.Url + result.url = gitProject.Url result.ref = ri.CheckedOutRef result.commit = ri.CheckedOutCommit result.dir = dir @@ -211,11 +211,9 @@ func (c *KluctlProjectContext) cloneKluctlProject(ctx context.Context) (gitProje if c.loadArgs.ProjectUrl == nil { return c.localProject(c.loadArgs.ProjectDir), nil } - return c.cloneGitProject(ctx, types2.ExternalProject{ - Project: &types2.GitProject{ - Url: *c.loadArgs.ProjectUrl, - Ref: c.loadArgs.ProjectRef, - }, + return c.cloneGitProject(ctx, &types2.GitProject{ + Url: *c.loadArgs.ProjectUrl, + Ref: c.loadArgs.ProjectRef, }, "", true, true) } diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 26efa5dd0..aa40f45fb 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -5,8 +5,6 @@ import ( "compress/gzip" "context" "fmt" - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/jinja2" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -84,43 +82,43 @@ func (c *KluctlProjectContext) load(ctx context.Context, allowGit bool) error { if localDir != "" { return c.localProject(localDir), nil } - if ep == nil || ep.Project == nil { - // the ExternalProject is pointing to the same repo that the root project is located in + + if ep == nil { + // no ExternalProject provided, so we point into the kluctl project + defaultGitSubDir p := kluctlProjectInfo.dir - if ep != nil { - if filepath.IsAbs(*ep.Path) { - return gitProjectInfo{}, fmt.Errorf("only paths relative to the git project root are allowed") - } - // we only allow relative paths pointing into the root git project - gitRoot, err := git.DetectGitRepositoryRoot(p) - if err != nil { - return gitProjectInfo{}, fmt.Errorf("could not determine git project root: %w", err) - } - gitRoot, err = filepath.Abs(gitRoot) - if err != nil { - return gitProjectInfo{}, err - } - relToGitRoot, err := filepath.Rel(gitRoot, p) - if err != nil { - return gitProjectInfo{}, err - } - relToGitRoot = filepath.Join(relToGitRoot, *ep.Path) - p, err = securejoin.SecureJoin(gitRoot, relToGitRoot) - if err != nil { - return gitProjectInfo{}, fmt.Errorf("path '%s' is not inside git project root '%s': %w", relToGitRoot, gitRoot, err) - } - } else { - if defaultGitSubDir != "" { - p = filepath.Join(p, defaultGitSubDir) - } + if defaultGitSubDir != "" { + p = filepath.Join(p, defaultGitSubDir) } return c.localProject(p), nil } - if !allowGit { - return gitProjectInfo{}, fmt.Errorf("tried to load something from git while it was not allowed") + + if ep.Project != nil { + // pointing to an actual external project, so let's try to clone it + if !allowGit { + return gitProjectInfo{}, fmt.Errorf("tried to load something from git while it was not allowed") + } + return c.cloneGitProject(ctx, ep.Project, defaultGitSubDir, true, true) } - return c.cloneGitProject(ctx, *ep, defaultGitSubDir, true, true) + // ExternalProject was provided but without an external repo url, so point into the kluctl project. + // We also allow to leave the kluctl project dir in case RepoRoot is provided via loadArgs, as long as + // the pointed to directory does not leave the RepoRoot + + repoRoot := c.loadArgs.RepoRoot + if repoRoot == "" { + // don't allow to leave the project dir + repoRoot = kluctlProjectInfo.dir + } + + p := filepath.Join(kluctlProjectInfo.dir, *ep.Path) + err = utils.CheckInDir(repoRoot, p) + if err != nil { + if c.loadArgs.RepoRoot == "" { + return gitProjectInfo{}, fmt.Errorf("it looks like your kluctl project is not part of a Git repository, which means you are not allowed to use paths that leave your kluctl project") + } + return gitProjectInfo{}, fmt.Errorf("path '%s' is not inside git project root '%s': %w", p, repoRoot, err) + } + return c.localProject(p), nil } deploymentInfo, err := doClone(c.Config.Deployment, "", c.loadArgs.LocalDeployment) diff --git a/pkg/kluctl_project/load_targets.go b/pkg/kluctl_project/load_targets.go index 0148e6785..1d48ed529 100644 --- a/pkg/kluctl_project/load_targets.go +++ b/pkg/kluctl_project/load_targets.go @@ -251,9 +251,8 @@ func (c *KluctlProjectContext) cloneDynamicTargets(ctx context.Context, dynamicT wp.Submit(func() error { gitProject := *targetInfo.gitProject gitProject.Ref = *targetInfo.ref - ep := types.ExternalProject{Project: &gitProject} - gi, err := c.cloneGitProject(ctx, ep, "", false, false) + gi, err := c.cloneGitProject(ctx, &gitProject, "", false, false) mutex.Lock() defer mutex.Unlock() if err != nil { diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index dce44c7fc..0357f5d34 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -12,6 +12,7 @@ import ( ) type LoadKluctlProjectArgs struct { + RepoRoot string ProjectDir string ProjectUrl *git_url.GitUrl ProjectRef string From be7594c0c95c9ff9044c447587e8b9be3f7441c7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 6 May 2022 10:53:22 +0200 Subject: [PATCH 0118/2268] fix: Run go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f432a9b25..4c81d771f 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e github.com/aws/aws-sdk-go v1.44.1 github.com/bitnami-labs/sealed-secrets v0.17.5 + github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/evanphx/json-patch v5.6.0+incompatible github.com/gammazero/workerpool v1.1.2 @@ -84,7 +85,6 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect github.com/containerd/containerd v1.6.3 // indirect - github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v20.10.14+incompatible // indirect github.com/docker/docker v20.10.14+incompatible // indirect From 45b5d7e163f60975895f9c1eb13c916fe6d79d74 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 6 May 2022 16:16:47 +0200 Subject: [PATCH 0119/2268] fix: Don't try to evaluate symlinks in CheckInDir --- pkg/utils/fs.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pkg/utils/fs.go b/pkg/utils/fs.go index 422538a73..ac567cdb8 100644 --- a/pkg/utils/fs.go +++ b/pkg/utils/fs.go @@ -47,16 +47,6 @@ func CheckInDir(root string, path string) error { return err } - absRoot, err = filepath.EvalSymlinks(absRoot) - if err != nil { - return err - } - - absPath, err = filepath.EvalSymlinks(absPath) - if err != nil { - return err - } - if absRoot == absPath { return nil } From c865e82d54306e3acd52879f1c162f74256ea00d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 6 May 2022 21:57:33 +0200 Subject: [PATCH 0120/2268] feat: Rewrite kluctl project loading and archive handling This fixes external projects pointing to the root repo via relative sub pathes. --- cmd/kluctl/commands/cmd_archive.go | 4 +- cmd/kluctl/commands/cmd_list_targets.go | 2 +- cmd/kluctl/commands/cmd_seal.go | 4 +- cmd/kluctl/commands/completion.go | 12 +- cmd/kluctl/commands/utils.go | 9 +- pkg/kluctl_project/git.go | 98 ++---- pkg/kluctl_project/load.go | 287 +----------------- pkg/kluctl_project/project.go | 66 ++-- pkg/kluctl_project/project_archive.go | 129 ++++++++ pkg/kluctl_project/project_load.go | 259 ++++++++++++++++ pkg/kluctl_project/target_context.go | 6 +- .../{load_targets.go => targets.go} | 18 +- pkg/seal/secrets_loader.go | 4 +- pkg/types/metadata.go | 10 + pkg/yaml/yaml.go | 22 ++ 15 files changed, 509 insertions(+), 421 deletions(-) create mode 100644 pkg/kluctl_project/project_archive.go create mode 100644 pkg/kluctl_project/project_load.go rename pkg/kluctl_project/{load_targets.go => targets.go} (90%) diff --git a/cmd/kluctl/commands/cmd_archive.go b/cmd/kluctl/commands/cmd_archive.go index 5c5325613..250571c51 100644 --- a/cmd/kluctl/commands/cmd_archive.go +++ b/cmd/kluctl/commands/cmd_archive.go @@ -16,7 +16,7 @@ func (cmd *archiveCmd) Help() string { } func (cmd *archiveCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.KluctlProjectContext) error { - return p.CreateTGZArchive(cmd.OutputArchive, cmd.ProjectFlags.OutputMetadata == "") + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.LoadedKluctlProject) error { + return p.WriteArchive(cmd.OutputArchive, cmd.ProjectFlags.OutputMetadata == "") }) } diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index ba4493622..c54241e94 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -16,7 +16,7 @@ func (cmd *listTargetsCmd) Help() string { } func (cmd *listTargetsCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.KluctlProjectContext) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.LoadedKluctlProject) error { var result []*types.Target for _, t := range p.DynamicTargets { result = append(result, t.Target) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index d840403d0..bdabda126 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -61,7 +61,7 @@ func loadSecrets(ctx *commandCtx, target *types.Target, secretsLoader *seal.Secr return nil } -func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.KluctlProjectContext, targetName string, secretsLoader *seal.SecretsLoader) error { +func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.LoadedKluctlProject, targetName string, secretsLoader *seal.SecretsLoader) error { log.Infof("Sealing for target %s", targetName) ptArgs := projectTargetCommandArgs{ @@ -119,7 +119,7 @@ func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.KluctlProjectContext, } func (cmd *sealCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.KluctlProjectContext) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.LoadedKluctlProject) error { hadError := false secretsLoader := seal.NewSecretsLoader(p, cmd.SecretsDir) diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 48c5f8afb..a316051bb 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -47,10 +47,10 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err return nil } -func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(p *kluctl_project.KluctlProjectContext) error) error { +func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(p *kluctl_project.LoadedKluctlProject) error) error { // let's not update git caches too often projectArgs.GitCacheUpdateInterval = time.Second * 60 - return withKluctlProjectFromArgs(*projectArgs, false, func(p *kluctl_project.KluctlProjectContext) error { + return withKluctlProjectFromArgs(*projectArgs, false, func(p *kluctl_project.LoadedKluctlProject) error { return cb(p) }) } @@ -58,7 +58,7 @@ func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(p *kluctl_ func buildClusterCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var ret []string - err := withProjectForCompletion(projectArgs, func(p *kluctl_project.KluctlProjectContext) error { + err := withProjectForCompletion(projectArgs, func(p *kluctl_project.LoadedKluctlProject) error { dents, err := os.ReadDir(p.ClustersDir) if err != nil { return err @@ -86,7 +86,7 @@ func buildClusterCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra. func buildTargetCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var ret []string - err := withProjectForCompletion(projectArgs, func(p *kluctl_project.KluctlProjectContext) error { + err := withProjectForCompletion(projectArgs, func(p *kluctl_project.LoadedKluctlProject) error { for _, t := range p.DynamicTargets { ret = append(ret, t.Target.Name) } @@ -132,7 +132,7 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd var deploymentItemDirs utils.OrderedMap var mutex sync.Mutex - err := withProjectForCompletion(&ptArgs.projectFlags, func(p *kluctl_project.KluctlProjectContext) error { + err := withProjectForCompletion(&ptArgs.projectFlags, func(p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { for _, t := range p.DynamicTargets { @@ -186,7 +186,7 @@ func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, a var images utils.OrderedMap var mutex sync.Mutex - err := withProjectForCompletion(&ptArgs.projectFlags, func(p *kluctl_project.KluctlProjectContext) error { + err := withProjectForCompletion(&ptArgs.projectFlags, func(p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { for _, t := range p.DynamicTargets { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 5e90ea31a..e1adddebb 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -21,7 +21,7 @@ import ( "time" ) -func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, cb func(p *kluctl_project.KluctlProjectContext) error) error { +func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, cb func(p *kluctl_project.LoadedKluctlProject) error) error { var url *git_url.GitUrl if projectFlags.ProjectUrl != "" { var err error @@ -51,7 +51,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b } repoRoot, err := git.DetectGitRepositoryRoot(cwd) - if err != nil { + if err != nil && projectFlags.FromArchive == "" { log.Warning("Failed to detect git project root. This might cause follow-up errors") } @@ -66,6 +66,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b LocalSealedSecrets: projectFlags.LocalSealedSecrets.String(), FromArchive: projectFlags.FromArchive.String(), FromArchiveMetadata: projectFlags.FromArchiveMetadata.String(), + AllowGitClone: projectFlags.FromArchive == "", GitAuthProviders: auth.NewDefaultAuthProviders(), GitUpdateInterval: projectFlags.GitCacheUpdateInterval, } @@ -109,12 +110,12 @@ type commandCtx struct { } func withProjectCommandContext(args projectTargetCommandArgs, cb func(ctx *commandCtx) error) error { - return withKluctlProjectFromArgs(args.projectFlags, true, func(p *kluctl_project.KluctlProjectContext) error { + return withKluctlProjectFromArgs(args.projectFlags, true, func(p *kluctl_project.LoadedKluctlProject) error { return withProjectTargetCommandContext(args, p, cb) }) } -func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_project.KluctlProjectContext, cb func(ctx *commandCtx) error) error { +func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_project.LoadedKluctlProject, cb func(ctx *commandCtx) error) error { rh := registries.NewRegistryHelper() err := rh.ParseAuthEntriesFromEnv() if err != nil { diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 0c5a641fd..ee8ba490c 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -3,12 +3,10 @@ package kluctl_project import ( "context" "fmt" - securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/git" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" "os" "path/filepath" "reflect" @@ -18,7 +16,7 @@ import ( "time" ) -func (c *KluctlProjectContext) updateGitCache(ctx context.Context, mr *git.MirroredGitRepo) error { +func (c *LoadedKluctlProject) updateGitCache(ctx context.Context, mr *git.MirroredGitRepo) error { if mr.HasUpdated() { return nil } @@ -29,7 +27,7 @@ func (c *KluctlProjectContext) updateGitCache(ctx context.Context, mr *git.Mirro return mr.Update(ctx, c.loadArgs.GitAuthProviders) } -func (c *KluctlProjectContext) updateGitCaches(ctx context.Context) error { +func (c *LoadedKluctlProject) updateGitCaches(ctx context.Context) error { var waitGroup sync.WaitGroup var firstError error var firstErrorLock sync.Mutex @@ -110,53 +108,12 @@ func (c *KluctlProjectContext) updateGitCaches(ctx context.Context) error { return firstError } -type gitProjectInfo struct { - url git_url.GitUrl - ref string - commit string - dir string -} - -func (c *KluctlProjectContext) buildCloneDir(u git_url.GitUrl, ref string) (string, error) { - if ref == "" { - ref = "HEAD" - } - ref = strings.ReplaceAll(ref, "/", "-") - ref = strings.ReplaceAll(ref, "\\", "-") - urlPath := filepath.FromSlash(u.Path) - baseName := filepath.Base(urlPath) - urlHash := utils.Sha256String(fmt.Sprintf("%s:%s", u.Host, u.Path))[:16] - cloneDir, err := securejoin.SecureJoin(c.TmpDir, filepath.Join(fmt.Sprintf("%s-%s", baseName, urlHash), ref)) - if err != nil { - return "", err - } - log.Tracef("buildCloneDir: ref=%s, urlPath=%s, baseName=%s, urlHash=%s, cloneDir=%s", ref, urlPath, baseName, urlHash, cloneDir) - return cloneDir, nil -} - -func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject *types2.GitProject, defaultGitSubDir string, doAddInvolvedRepo bool, doLock bool) (result gitProjectInfo, err error) { +func (c *LoadedKluctlProject) cloneGitProject(ctx context.Context, gitProject *types2.GitProject, targetDir string, doLock bool) (info git.GitRepoInfo, err error) { err = os.MkdirAll(filepath.Join(c.TmpDir, "git"), 0o700) if err != nil { return } - targetDir, err := c.buildCloneDir(gitProject.Url, gitProject.Ref) - if err != nil { - return - } - - subDir := gitProject.SubDir - if subDir == "" { - subDir = defaultGitSubDir - } - dir := targetDir - if subDir != "" { - dir, err = securejoin.SecureJoin(dir, subDir) - if err != nil { - return gitProjectInfo{}, err - } - } - mr, ok := c.mirroredRepos[gitProject.Url.NormalizedRepoKey()] if !ok { mr, err = git.NewMirroredGitRepo(gitProject.Url) @@ -182,42 +139,35 @@ func (c *KluctlProjectContext) cloneGitProject(ctx context.Context, gitProject * return } - ri, err := git.GetGitRepoInfo(targetDir) - if err != nil { - return - } - - result.url = gitProject.Url - result.ref = ri.CheckedOutRef - result.commit = ri.CheckedOutCommit - result.dir = dir - - if doAddInvolvedRepo { - c.addInvolvedRepo(result.url, result.ref, map[string]string{ - result.ref: result.commit, - }) - } - + info, err = git.GetGitRepoInfo(targetDir) return } -func (c *KluctlProjectContext) localProject(dir string) gitProjectInfo { - return gitProjectInfo{ - dir: dir, +func (c *LoadedKluctlProject) buildCloneDir(u git_url.GitUrl, ref string) (string, error) { + baseDir := c.TmpDir + if c.archiveDir != "" { + baseDir = c.archiveDir } -} + baseDir = filepath.Join(baseDir, "git") -func (c *KluctlProjectContext) cloneKluctlProject(ctx context.Context) (gitProjectInfo, error) { - if c.loadArgs.ProjectUrl == nil { - return c.localProject(c.loadArgs.ProjectDir), nil + if ref == "" { + ref = "HEAD" } - return c.cloneGitProject(ctx, &types2.GitProject{ - Url: *c.loadArgs.ProjectUrl, - Ref: c.loadArgs.ProjectRef, - }, "", true, true) + ref = strings.ReplaceAll(ref, "/", "-") + ref = strings.ReplaceAll(ref, "\\", "-") + urlPath := filepath.FromSlash(u.Path) + baseName := filepath.Base(urlPath) + urlHash := utils.Sha256String(fmt.Sprintf("%s:%s", u.Host, u.Path))[:16] + cloneDir := filepath.Join(fmt.Sprintf("%s-%s", baseName, urlHash), ref) + cloneDir = filepath.Join(baseDir, cloneDir) + err := utils.CheckInDir(baseDir, cloneDir) + if err != nil { + return "", err + } + return cloneDir, nil } -func (c *KluctlProjectContext) addInvolvedRepo(u git_url.GitUrl, refPattern string, refs map[string]string) { +func (c *LoadedKluctlProject) addInvolvedRepo(u git_url.GitUrl, refPattern string, refs map[string]string) { repoKey := u.NormalizedRepoKey() irs, _ := c.involvedRepos[repoKey] e := &types2.InvolvedRepo{ diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index aa40f45fb..56a1da171 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -1,184 +1,34 @@ package kluctl_project import ( - "archive/tar" - "compress/gzip" "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/jinja2" - types2 "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" + "github.com/kluctl/kluctl/v2/pkg/types" ) -func (c *KluctlProjectContext) mergeClustersDirs(mergedClustersDir string, clustersInfos []gitProjectInfo) error { - err := os.MkdirAll(mergedClustersDir, 0o700) - if err != nil { - return err - } - - for _, ci := range clustersInfos { - if !utils.IsDirectory(ci.dir) { - log.Warningf("Cluster dir '%s' does not exist", ci.dir) - continue - } - files, err := ioutil.ReadDir(ci.dir) - if err != nil { - return err - } - for _, fi := range files { - p := filepath.Join(ci.dir, fi.Name()) - if utils.IsFile(p) { - err = utils.CopyFile(p, filepath.Join(mergedClustersDir, fi.Name())) - if err != nil { - return err - } - } - } - } - return nil -} - -func (c *KluctlProjectContext) getConfigPath(projectDir string) string { - configPath := c.loadArgs.ProjectConfig - if configPath == "" { - p := yaml.FixPathExt(filepath.Join(projectDir, ".kluctl.yml")) - if utils.IsFile(p) { - configPath = p - } - } - return configPath -} +func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*LoadedKluctlProject, error) { + p := &LoadedKluctlProject{ + loadArgs: args, + TmpDir: tmpDir, + J2: j2, -func (c *KluctlProjectContext) load(ctx context.Context, allowGit bool) error { - kluctlProjectInfo, err := c.cloneKluctlProject(ctx) - if err != nil { - return err + involvedRepos: map[string][]types.InvolvedRepo{}, + mirroredRepos: map[string]*git.MirroredGitRepo{}, } - configPath := c.getConfigPath(kluctlProjectInfo.dir) - - if configPath != "" { - err = yaml.ReadYamlFile(configPath, &c.Config) - if err != nil { - return err - } - } - - if allowGit { - err = c.updateGitCaches(ctx) - if err != nil { - return err - } - } - - doClone := func(ep *types2.ExternalProject, defaultGitSubDir string, localDir string) (gitProjectInfo, error) { - if localDir != "" { - return c.localProject(localDir), nil - } - - if ep == nil { - // no ExternalProject provided, so we point into the kluctl project + defaultGitSubDir - p := kluctlProjectInfo.dir - if defaultGitSubDir != "" { - p = filepath.Join(p, defaultGitSubDir) - } - return c.localProject(p), nil - } - - if ep.Project != nil { - // pointing to an actual external project, so let's try to clone it - if !allowGit { - return gitProjectInfo{}, fmt.Errorf("tried to load something from git while it was not allowed") - } - return c.cloneGitProject(ctx, ep.Project, defaultGitSubDir, true, true) - } - - // ExternalProject was provided but without an external repo url, so point into the kluctl project. - // We also allow to leave the kluctl project dir in case RepoRoot is provided via loadArgs, as long as - // the pointed to directory does not leave the RepoRoot - - repoRoot := c.loadArgs.RepoRoot - if repoRoot == "" { - // don't allow to leave the project dir - repoRoot = kluctlProjectInfo.dir - } - - p := filepath.Join(kluctlProjectInfo.dir, *ep.Path) - err = utils.CheckInDir(repoRoot, p) - if err != nil { - if c.loadArgs.RepoRoot == "" { - return gitProjectInfo{}, fmt.Errorf("it looks like your kluctl project is not part of a Git repository, which means you are not allowed to use paths that leave your kluctl project") - } - return gitProjectInfo{}, fmt.Errorf("path '%s' is not inside git project root '%s': %w", p, repoRoot, err) - } - return c.localProject(p), nil - } - - deploymentInfo, err := doClone(c.Config.Deployment, "", c.loadArgs.LocalDeployment) - if err != nil { - return err - } - sealedSecretsInfo, err := doClone(c.Config.SealedSecrets, ".sealed-secrets", c.loadArgs.LocalSealedSecrets) - if err != nil { - return err - } - var clustersInfos []gitProjectInfo - if c.loadArgs.LocalClusters != "" { - clustersInfos = append(clustersInfos, c.localProject(c.loadArgs.LocalClusters)) - } else if len(c.Config.Clusters.Projects) != 0 { - for _, ep := range c.Config.Clusters.Projects { - info, err := doClone(&ep, "clusters", "") - if err != nil { - return err - } - clustersInfos = append(clustersInfos, info) - } - } else { - ci, err := doClone(nil, "clusters", "") - if err != nil { - return err - } - clustersInfos = append(clustersInfos, ci) - } - - mergedClustersDir := filepath.Join(c.TmpDir, "merged-clusters") - err = c.mergeClustersDirs(mergedClustersDir, clustersInfos) - if err != nil { - return err - } - - c.ProjectDir = kluctlProjectInfo.dir - c.DeploymentDir = deploymentInfo.dir - c.ClustersDir = mergedClustersDir - c.SealedSecretsDir = sealedSecretsInfo.dir - - return nil -} - -func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*KluctlProjectContext, error) { if args.FromArchive != "" { if args.ProjectUrl != nil || args.ProjectRef != "" || args.ProjectConfig != "" || args.LocalClusters != "" || args.LocalDeployment != "" || args.LocalSealedSecrets != "" { return nil, fmt.Errorf("--from-archive can not be combined with any other project related option") } - project, err := loadKluctlProjectFromArchive(args, tmpDir, j2) - if err != nil { - return nil, err - } - err = project.load(ctx, false) + err := p.loadFromArchive(ctx) if err != nil { return nil, err } - return project, nil + return p, nil } else { - p := NewKluctlProjectContext(args, tmpDir, j2) - err := p.load(ctx, true) + err := p.loadKluctlProject(ctx) if err != nil { return nil, err } @@ -189,116 +39,3 @@ func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir s return p, nil } } - -func loadKluctlProjectFromArchive(args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*KluctlProjectContext, error) { - var dir string - if utils.IsFile(args.FromArchive) { - err := utils.ExtractTarGzFile(args.FromArchive, tmpDir) - if err != nil { - return nil, fmt.Errorf("failed to extract archive %v: %w", args.FromArchive, err) - } - dir = tmpDir - } else { - dir = args.FromArchive - } - - var metdataPath string - if args.FromArchiveMetadata != "" { - metdataPath = args.FromArchiveMetadata - } else { - metdataPath = yaml.FixPathExt(filepath.Join(dir, "metadata.yml")) - } - - var metadata types2.ProjectMetadata - err := yaml.ReadYamlFile(metdataPath, &metadata) - if err != nil { - return nil, err - } - - p := NewKluctlProjectContext(LoadKluctlProjectArgs{ - ProjectConfig: yaml.FixPathExt(filepath.Join(dir, ".kluctl.yml")), - LocalClusters: filepath.Join(dir, "clusters"), - LocalDeployment: filepath.Join(dir, "deployment"), - LocalSealedSecrets: filepath.Join(dir, "sealed-secrets"), - }, dir, j2) - p.involvedRepos = metadata.InvolvedRepos - p.DynamicTargets = metadata.Targets - return p, nil -} - -func (c *KluctlProjectContext) GetMetadata() *types2.ProjectMetadata { - md := &types2.ProjectMetadata{ - InvolvedRepos: c.involvedRepos, - Targets: c.DynamicTargets, - } - return md -} - -func (c *KluctlProjectContext) CreateTGZArchive(archivePath string, embedMetadata bool) error { - f, err := os.Create(archivePath) - if err != nil { - return err - } - defer f.Close() - gz := gzip.NewWriter(f) - defer gz.Close() - tw := tar.NewWriter(gz) - defer tw.Close() - - filter := func(h *tar.Header, size int64) (*tar.Header, error) { - if strings.HasSuffix(strings.ToLower(h.Name), ".git") { - return nil, nil - } - h.Uid = 0 - h.Gid = 0 - h.Uname = "" - h.Gname = "" - h.ModTime = time.Time{} - h.ChangeTime = time.Time{} - h.AccessTime = time.Time{} - return h, nil - } - - if embedMetadata { - md := c.GetMetadata() - mdStr, err := yaml.WriteYamlBytes(md) - if err != nil { - return err - } - - err = tw.WriteHeader(&tar.Header{ - Name: "metadata.yaml", - Size: int64(len(mdStr)), - Mode: 0o666 | tar.TypeReg, - }) - if err != nil { - return err - } - _, err = tw.Write(mdStr) - if err != nil { - return err - } - } - - if err = utils.AddToTar(tw, c.getConfigPath(c.ProjectDir), yaml.FixNameExt(c.ProjectDir, ".kluctl.yml"), filter); err != nil { - return err - } - if err = utils.AddToTar(tw, c.ProjectDir, "kluctl-project", filter); err != nil { - return err - } - if err = utils.AddToTar(tw, c.DeploymentDir, "deployment", filter); err != nil { - return err - } - if utils.Exists(c.ClustersDir) { - if err = utils.AddToTar(tw, c.ClustersDir, "clusters", filter); err != nil { - return err - } - } - if utils.Exists(c.SealedSecretsDir) { - if err = utils.AddToTar(tw, c.SealedSecretsDir, "sealed-secrets", filter); err != nil { - return err - } - } - - return nil -} diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 0357f5d34..06d555e00 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -3,62 +3,42 @@ package kluctl_project import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/git" - auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/jinja2" - "github.com/kluctl/kluctl/v2/pkg/types" + types2 "github.com/kluctl/kluctl/v2/pkg/types" "regexp" - "time" ) -type LoadKluctlProjectArgs struct { - RepoRoot string - ProjectDir string - ProjectUrl *git_url.GitUrl - ProjectRef string - ProjectConfig string - LocalClusters string - LocalDeployment string - LocalSealedSecrets string - FromArchive string - FromArchiveMetadata string - - GitAuthProviders *auth2.GitAuthProviders - - GitUpdateInterval time.Duration -} - -type KluctlProjectContext struct { +type LoadedKluctlProject struct { loadArgs LoadKluctlProjectArgs - TmpDir string - Config types.KluctlProject + TmpDir string + archiveDir string + + projectRootDir string + ProjectDir string - ProjectDir string DeploymentDir string ClustersDir string - SealedSecretsDir string - - involvedRepos map[string][]types.InvolvedRepo - DynamicTargets []*types.DynamicTarget + sealedSecretsDir string + involvedRepos map[string][]types2.InvolvedRepo mirroredRepos map[string]*git.MirroredGitRepo + Config types2.KluctlProject + DynamicTargets []*types2.DynamicTarget + J2 *jinja2.Jinja2 } -func NewKluctlProjectContext(loadArgs LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) *KluctlProjectContext { - o := &KluctlProjectContext{ - loadArgs: loadArgs, - TmpDir: tmpDir, - involvedRepos: make(map[string][]types.InvolvedRepo), - mirroredRepos: make(map[string]*git.MirroredGitRepo), - J2: j2, +func (c *LoadedKluctlProject) GetMetadata() *types2.ProjectMetadata { + md := &types2.ProjectMetadata{ + InvolvedRepos: c.involvedRepos, + Targets: c.DynamicTargets, } - return o + return md } -func (c *KluctlProjectContext) FindBaseTarget(name string) (*types.Target, error) { +func (c *LoadedKluctlProject) FindBaseTarget(name string) (*types2.Target, error) { for _, target := range c.Config.Targets { if target.Name == name { return target, nil @@ -67,7 +47,7 @@ func (c *KluctlProjectContext) FindBaseTarget(name string) (*types.Target, error return nil, fmt.Errorf("target %s not existent in kluctl project config", name) } -func (c *KluctlProjectContext) FindDynamicTarget(name string) (*types.DynamicTarget, error) { +func (c *LoadedKluctlProject) FindDynamicTarget(name string) (*types2.DynamicTarget, error) { for _, target := range c.DynamicTargets { if target.Target.Name == name { return target, nil @@ -76,12 +56,12 @@ func (c *KluctlProjectContext) FindDynamicTarget(name string) (*types.DynamicTar return nil, fmt.Errorf("target %s not existent in kluctl project config", name) } -func (c *KluctlProjectContext) LoadClusterConfig(clusterName string) (*types.ClusterConfig, error) { - return types.LoadClusterConfig(c.ClustersDir, clusterName) +func (c *LoadedKluctlProject) LoadClusterConfig(clusterName string) (*types2.ClusterConfig, error) { + return types2.LoadClusterConfig(c.ClustersDir, clusterName) } -func (c *KluctlProjectContext) CheckDynamicArg(target *types.Target, argName string, argValue string) error { - var dynArg *types.DynamicArg +func (c *LoadedKluctlProject) CheckDynamicArg(target *types2.Target, argName string, argValue string) error { + var dynArg *types2.DynamicArg for _, x := range target.DynamicArgs { if x.Name == argName { dynArg = &x diff --git a/pkg/kluctl_project/project_archive.go b/pkg/kluctl_project/project_archive.go new file mode 100644 index 000000000..864e6a9c2 --- /dev/null +++ b/pkg/kluctl_project/project_archive.go @@ -0,0 +1,129 @@ +package kluctl_project + +import ( + "archive/tar" + "compress/gzip" + "context" + "fmt" + types2 "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "os" + "path/filepath" + "strings" + "time" +) + +func (c *LoadedKluctlProject) loadFromArchive(ctx context.Context) error { + var dir string + if utils.IsFile(c.loadArgs.FromArchive) { + dir = filepath.Join(c.TmpDir, "archive") + err := utils.ExtractTarGzFile(c.loadArgs.FromArchive, dir) + if err != nil { + return fmt.Errorf("failed to extract archive %v: %w", c.loadArgs.FromArchive, err) + } + } else { + dir = c.loadArgs.FromArchive + } + + var pmdPath string + if c.loadArgs.FromArchiveMetadata != "" { + pmdPath = c.loadArgs.FromArchiveMetadata + } else { + pmdPath = yaml.FixPathExt(filepath.Join(dir, "project-metadata.yml")) + } + + var pmd types2.ProjectMetadata + err := yaml.ReadYamlFile(pmdPath, &pmd) + if err != nil { + return err + } + + var amd types2.ArchiveMetadata + err = yaml.ReadYamlFile(yaml.FixPathExt(filepath.Join(dir, "archive-metadata.yml")), &amd) + if err != nil { + return err + } + + c.loadArgs.RepoRoot = filepath.Join(dir, amd.ProjectRootDir) + c.loadArgs.ProjectDir = filepath.Join(dir, amd.ProjectRootDir, amd.ProjectSubDir) + + err = utils.CheckInDir(dir, c.loadArgs.RepoRoot) + if err != nil { + return err + } + err = utils.CheckInDir(dir, c.loadArgs.ProjectDir) + if err != nil { + return err + } + + c.archiveDir = dir + c.involvedRepos = pmd.InvolvedRepos + c.DynamicTargets = pmd.Targets + + err = c.loadKluctlProject(ctx) + if err != nil { + return err + } + return nil +} + +func (c *LoadedKluctlProject) WriteArchive(archivePath string, embedProjectMetadata bool) error { + f, err := os.Create(archivePath) + if err != nil { + return err + } + defer f.Close() + gz := gzip.NewWriter(f) + defer gz.Close() + tw := tar.NewWriter(gz) + defer tw.Close() + + filter := func(h *tar.Header, size int64) (*tar.Header, error) { + if strings.HasSuffix(strings.ToLower(h.Name), ".git") { + return nil, nil + } + h.Uid = 0 + h.Gid = 0 + h.Uname = "" + h.Gname = "" + h.ModTime = time.Time{} + h.ChangeTime = time.Time{} + h.AccessTime = time.Time{} + return h, nil + } + + if embedProjectMetadata { + err = yaml.WriteYamlToTar(tw, c.GetMetadata(), "project-metadata.yaml") + if err != nil { + return nil + } + } + + amd := types2.ArchiveMetadata{ + ProjectRootDir: "kluctl-project", + } + + amd.ProjectSubDir, err = filepath.Rel(c.projectRootDir, c.ProjectDir) + if err != nil { + return err + } + + err = yaml.WriteYamlToTar(tw, &amd, "archive-metadata.yaml") + if err != nil { + return nil + } + + if err = utils.AddToTar(tw, c.projectRootDir, "kluctl-project", filter); err != nil { + return err + } + gitReposBaseDir := filepath.Join(c.TmpDir, "git") + if utils.Exists(gitReposBaseDir) { + err = utils.AddToTar(tw, gitReposBaseDir, "git", filter) + if err != nil { + return err + } + } + + return nil +} diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go new file mode 100644 index 000000000..75f24ac15 --- /dev/null +++ b/pkg/kluctl_project/project_load.go @@ -0,0 +1,259 @@ +package kluctl_project + +import ( + "context" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/git" + auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + types2 "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/yaml" + log "github.com/sirupsen/logrus" + "io/ioutil" + "os" + "path/filepath" + "time" +) + +type LoadKluctlProjectArgs struct { + RepoRoot string + ProjectDir string + ProjectUrl *git_url.GitUrl + ProjectRef string + ProjectConfig string + LocalClusters string + LocalDeployment string + LocalSealedSecrets string + FromArchive string + FromArchiveMetadata string + + AllowGitClone bool + GitAuthProviders *auth2.GitAuthProviders + + GitUpdateInterval time.Duration +} + +type gitProjectInfo struct { + url git_url.GitUrl + ref string + commit string + repoRoot string + dir string +} + +func (c *LoadedKluctlProject) getConfigPath() string { + configPath := c.loadArgs.ProjectConfig + if configPath == "" { + p := yaml.FixPathExt(filepath.Join(c.ProjectDir, ".kluctl.yml")) + if utils.IsFile(p) { + configPath = p + } + } + return configPath +} + +func (c *LoadedKluctlProject) localProject(dir string) gitProjectInfo { + return gitProjectInfo{ + dir: dir, + } +} + +func (c *LoadedKluctlProject) loadGitProject(ctx context.Context, gitProject *types2.GitProject, defaultSubDir string, doLock bool, doAddInvolvedRepo bool) (ret gitProjectInfo, err error) { + cloneDir, err := c.buildCloneDir(gitProject.Url, gitProject.Ref) + if err != nil { + return + } + + if c.archiveDir != "" { + var md types2.GitRepoMetadata + err = yaml.ReadYamlFile(filepath.Join(cloneDir, ".git-metadata.yaml"), &md) + if err != nil { + return + } + ret.url = gitProject.Url + ret.ref = gitProject.Ref + ret.commit = md.Commit + ret.repoRoot = cloneDir + } else { + if !c.loadArgs.AllowGitClone { + err = fmt.Errorf("tried to load an external project from git, which is not allowed") + return + } + + var ri git.GitRepoInfo + ri, err = c.cloneGitProject(ctx, gitProject, cloneDir, doLock) + if err != nil { + return + } + + var md types2.GitRepoMetadata + md.Ref = ri.CheckedOutRef + md.Commit = ri.CheckedOutCommit + err = yaml.WriteYamlFile(filepath.Join(cloneDir, ".git-metadata.yaml"), &md) + if err != nil { + return gitProjectInfo{}, err + } + + ret.url = gitProject.Url + ret.ref = ri.CheckedOutRef + ret.commit = ri.CheckedOutCommit + ret.repoRoot = cloneDir + + if doAddInvolvedRepo { + c.addInvolvedRepo(ret.url, ret.ref, map[string]string{ + ret.ref: ret.commit, + }) + } + } + + subDir := gitProject.SubDir + if subDir == "" { + subDir = defaultSubDir + } + ret.dir = filepath.Join(ret.repoRoot, subDir) + err = utils.CheckInDir(ret.repoRoot, ret.dir) + if err != nil { + return + } + + return +} + +func (c *LoadedKluctlProject) loadExternalProject(ctx context.Context, ep *types2.ExternalProject, defaultGitSubDir string, localDir string) (gitProjectInfo, error) { + if localDir != "" { + return c.localProject(localDir), nil + } + + if ep == nil { + // no ExternalProject provided, so we point into the kluctl project + defaultGitSubDir + p := filepath.Join(c.ProjectDir, defaultGitSubDir) + return c.localProject(p), nil + } + + if ep.Project != nil { + // pointing to an actual external project, so let's try to clone it + return c.loadGitProject(ctx, ep.Project, defaultGitSubDir, true, true) + } + + // ExternalProject was provided but without an external repo url, so point into the kluctl project. + // We also allow to leave the kluctl project dir but limit it to the git project + + p := filepath.Join(c.ProjectDir, *ep.Path) + err := utils.CheckInDir(c.projectRootDir, p) + if err != nil { + return gitProjectInfo{}, fmt.Errorf("path '%s' is not inside git project root '%s': %w", p, c.projectRootDir, err) + } + return c.localProject(p), nil +} + +func (c *LoadedKluctlProject) loadKluctlProject(ctx context.Context) error { + var err error + + if c.loadArgs.ProjectUrl == nil { + c.projectRootDir = c.loadArgs.RepoRoot + c.ProjectDir = c.loadArgs.ProjectDir + err = utils.CheckInDir(c.projectRootDir, c.ProjectDir) + if err != nil { + return err + } + } else { + gi, err := c.loadGitProject(ctx, &types2.GitProject{ + Url: *c.loadArgs.ProjectUrl, + Ref: c.loadArgs.ProjectRef, + }, "", true, true) + if err != nil { + return err + } + c.projectRootDir = gi.repoRoot + c.ProjectDir = gi.dir + } + + configPath := c.getConfigPath() + + if configPath != "" { + err = yaml.ReadYamlFile(configPath, &c.Config) + if err != nil { + return err + } + } + + if c.loadArgs.AllowGitClone { + err = os.MkdirAll(filepath.Join(c.TmpDir, "git"), 0o755) + if err != nil { + return err + } + + err = c.updateGitCaches(ctx) + if err != nil { + return err + } + } + + deploymentInfo, err := c.loadExternalProject(ctx, c.Config.Deployment, "", c.loadArgs.LocalDeployment) + if err != nil { + return err + } + sealedSecretsInfo, err := c.loadExternalProject(ctx, c.Config.SealedSecrets, ".sealed-secrets", c.loadArgs.LocalSealedSecrets) + if err != nil { + return err + } + var clustersInfos []gitProjectInfo + if c.loadArgs.LocalClusters != "" { + clustersInfos = append(clustersInfos, c.localProject(c.loadArgs.LocalClusters)) + } else if len(c.Config.Clusters.Projects) != 0 { + for _, ep := range c.Config.Clusters.Projects { + info, err := c.loadExternalProject(ctx, &ep, "clusters", "") + if err != nil { + return err + } + clustersInfos = append(clustersInfos, info) + } + } else { + ci, err := c.loadExternalProject(ctx, nil, "clusters", "") + if err != nil { + return err + } + clustersInfos = append(clustersInfos, ci) + } + + mergedClustersDir := filepath.Join(c.TmpDir, "merged-clusters") + err = c.mergeClustersDirs(mergedClustersDir, clustersInfos) + if err != nil { + return err + } + + c.DeploymentDir = deploymentInfo.dir + c.ClustersDir = mergedClustersDir + c.sealedSecretsDir = sealedSecretsInfo.dir + + return nil +} + +func (c *LoadedKluctlProject) mergeClustersDirs(mergedClustersDir string, clustersInfos []gitProjectInfo) error { + err := os.MkdirAll(mergedClustersDir, 0o700) + if err != nil { + return err + } + + for _, ci := range clustersInfos { + if !utils.IsDirectory(ci.dir) { + log.Warningf("Cluster dir '%s' does not exist", ci.dir) + continue + } + files, err := ioutil.ReadDir(ci.dir) + if err != nil { + return err + } + for _, fi := range files { + p := filepath.Join(ci.dir, fi.Name()) + if utils.IsFile(p) { + err = utils.CopyFile(p, filepath.Join(mergedClustersDir, fi.Name())) + if err != nil { + return err + } + } + } + } + return nil +} diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 745b55131..e2038eb75 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -13,7 +13,7 @@ import ( ) type TargetContext struct { - KluctlProject *KluctlProjectContext + KluctlProject *LoadedKluctlProject Target *types.Target ClusterConfig *types.ClusterConfig K *k8s.K8sCluster @@ -21,7 +21,7 @@ type TargetContext struct { DeploymentCollection *deployment.DeploymentCollection } -func (p *KluctlProjectContext) NewTargetContext(clientConfigGetter func(context string) (*rest.Config, error), targetName string, clusterName string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { +func (p *LoadedKluctlProject) NewTargetContext(clientConfigGetter func(context string) (*rest.Config, error), targetName string, clusterName string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { deploymentDir, err := filepath.Abs(p.DeploymentDir) if err != nil { return nil, err @@ -106,7 +106,7 @@ func (p *KluctlProjectContext) NewTargetContext(clientConfigGetter func(context } varsCtx.UpdateChild("target", targetVars) - d, err := deployment.NewDeploymentProject(k, varsCtx, deploymentDir, p.SealedSecretsDir, nil) + d, err := deployment.NewDeploymentProject(k, varsCtx, deploymentDir, p.sealedSecretsDir, nil) if err != nil { return nil, err } diff --git a/pkg/kluctl_project/load_targets.go b/pkg/kluctl_project/targets.go similarity index 90% rename from pkg/kluctl_project/load_targets.go rename to pkg/kluctl_project/targets.go index 1d48ed529..426560204 100644 --- a/pkg/kluctl_project/load_targets.go +++ b/pkg/kluctl_project/targets.go @@ -27,7 +27,7 @@ type dynamicTargetInfo struct { defaultBranch string } -func (c *KluctlProjectContext) loadTargets(ctx context.Context) error { +func (c *LoadedKluctlProject) loadTargets(ctx context.Context) error { targetNames := make(map[string]bool) c.DynamicTargets = nil @@ -77,7 +77,7 @@ func (c *KluctlProjectContext) loadTargets(ctx context.Context) error { return nil } -func (c *KluctlProjectContext) renderTarget(target *types.Target) (*types.Target, error) { +func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, error) { // Try rendering the target multiple times, until all values can be rendered successfully. This allows the target // to reference itself in complex ways. We'll also try loading the cluster vars in each iteration. @@ -111,7 +111,7 @@ func (c *KluctlProjectContext) renderTarget(target *types.Target) (*types.Target return curTarget, nil } -func (c *KluctlProjectContext) prepareDynamicTargets(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { +func (c *LoadedKluctlProject) prepareDynamicTargets(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { if baseTarget.TargetConfig != nil && baseTarget.TargetConfig.Project != nil { return c.prepareDynamicTargetsExternal(baseTarget) } else { @@ -119,7 +119,7 @@ func (c *KluctlProjectContext) prepareDynamicTargets(baseTarget *types.Target) ( } } -func (c *KluctlProjectContext) prepareDynamicTargetsSimple(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { +func (c *LoadedKluctlProject) prepareDynamicTargetsSimple(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { if baseTarget.TargetConfig != nil { if baseTarget.TargetConfig.Ref != nil || baseTarget.TargetConfig.RefPattern != nil { return nil, fmt.Errorf("'ref' and/or 'refPattern' are not allowed for non-external dynamic targets") @@ -134,7 +134,7 @@ func (c *KluctlProjectContext) prepareDynamicTargetsSimple(baseTarget *types.Tar return dynamicTargets, nil } -func (c *KluctlProjectContext) prepareDynamicTargetsExternal(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { +func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { mr, ok := c.mirroredRepos[baseTarget.TargetConfig.Project.Url.NormalizedRepoKey()] if !ok { return nil, fmt.Errorf("repo not found in mirroredRepos, this is unexpected and probably a bug") @@ -196,7 +196,7 @@ func (c *KluctlProjectContext) prepareDynamicTargetsExternal(baseTarget *types.T return dynamicTargets, nil } -func (c *KluctlProjectContext) matchRef(s string, pattern string) (bool, string, error) { +func (c *LoadedKluctlProject) matchRef(s string, pattern string) (bool, string, error) { if strings.HasPrefix(pattern, "refs/") { p, err := regexp.Compile(fmt.Sprintf("^%s$", pattern)) if err != nil { @@ -221,7 +221,7 @@ func (c *KluctlProjectContext) matchRef(s string, pattern string) (bool, string, } } -func (c *KluctlProjectContext) cloneDynamicTargets(ctx context.Context, dynamicTargets []*dynamicTargetInfo) error { +func (c *LoadedKluctlProject) cloneDynamicTargets(ctx context.Context, dynamicTargets []*dynamicTargetInfo) error { wp := utils.NewDebuggerAwareWorkerPool(8) defer wp.StopWait(false) @@ -252,7 +252,7 @@ func (c *KluctlProjectContext) cloneDynamicTargets(ctx context.Context, dynamicT gitProject := *targetInfo.gitProject gitProject.Ref = *targetInfo.ref - gi, err := c.cloneGitProject(ctx, &gitProject, "", false, false) + gi, err := c.loadGitProject(ctx, &gitProject, "", false, false) mutex.Lock() defer mutex.Unlock() if err != nil { @@ -305,7 +305,7 @@ func (c *KluctlProjectContext) cloneDynamicTargets(ctx context.Context, dynamicT return nil } -func (c *KluctlProjectContext) buildDynamicTarget(targetInfo *dynamicTargetInfo) (*types.Target, error) { +func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) (*types.Target, error) { var target types.Target err := utils.DeepCopy(&target, targetInfo.baseTarget) if err != nil { diff --git a/pkg/seal/secrets_loader.go b/pkg/seal/secrets_loader.go index ef0e37fe9..fb863224c 100644 --- a/pkg/seal/secrets_loader.go +++ b/pkg/seal/secrets_loader.go @@ -18,13 +18,13 @@ type usernamePassword struct { } type SecretsLoader struct { - project *kluctl_project.KluctlProjectContext + project *kluctl_project.LoadedKluctlProject secretsDir string credentialsCache map[string]usernamePassword } -func NewSecretsLoader(p *kluctl_project.KluctlProjectContext, secretsDir string) *SecretsLoader { +func NewSecretsLoader(p *kluctl_project.LoadedKluctlProject, secretsDir string) *SecretsLoader { return &SecretsLoader{ project: p, secretsDir: secretsDir, diff --git a/pkg/types/metadata.go b/pkg/types/metadata.go index 638f4bca9..0742a92ad 100644 --- a/pkg/types/metadata.go +++ b/pkg/types/metadata.go @@ -9,3 +9,13 @@ type ProjectMetadata struct { InvolvedRepos map[string][]InvolvedRepo `yaml:"involvedRepos"` Targets []*DynamicTarget `yaml:"targets"` } + +type GitRepoMetadata struct { + Ref string `yaml:"ref"` + Commit string `yaml:"commit"` +} + +type ArchiveMetadata struct { + ProjectRootDir string `yaml:"projectRootDir"` + ProjectSubDir string `yaml:"projectSubDir"` +} diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 1726b4dae..2c24f990a 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -1,6 +1,7 @@ package yaml import ( + "archive/tar" "bytes" "encoding/json" "errors" @@ -163,6 +164,27 @@ func WriteYamlAllStream(w io.Writer, l []interface{}) error { return nil } +func WriteYamlToTar(tw *tar.Writer, o interface{}, name string) error { + str, err := WriteYamlBytes(o) + if err != nil { + return err + } + + err = tw.WriteHeader(&tar.Header{ + Name: name, + Size: int64(len(str)), + Mode: 0o666 | tar.TypeReg, + }) + if err != nil { + return err + } + _, err = tw.Write(str) + if err != nil { + return err + } + return nil +} + func ConvertYamlToJson(b []byte) ([]byte, error) { var x interface{} err := ReadYamlBytes(b, &x) From e524b81e4848277e78d5e6c3b0323ec9dc47ae3c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 6 May 2022 23:35:24 +0200 Subject: [PATCH 0121/2268] fix: Ignore GroupDiscoveryFailedError from discovery client Orphan apirources cause discovery errors that must be ignored. Luckily the returned list is still complete and valid. --- pkg/k8s/k8s_cluster.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 33265da1d..5c901aafa 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -15,6 +15,7 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/disk" "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" @@ -168,7 +169,7 @@ func (k *K8sCluster) updateResources(doLock bool) error { }{} _, arls, err := k.discovery.ServerGroupsAndResources() - if err != nil { + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { return err } for _, arl := range arls { @@ -202,6 +203,9 @@ func (k *K8sCluster) updateResources(doLock bool) error { } arls, err = k.discovery.ServerPreferredResources() + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { + return err + } for _, arl := range arls { for _, ar := range arl.APIResources { if strings.Index(ar.Name, "/") != -1 { From d8b92b9e35b772312ed8e9eb6f8bcefb4e09d79f Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Mon, 9 May 2022 09:46:30 +0200 Subject: [PATCH 0122/2268] feat: add sorted output for list images (#27) * feat: add sorted output for list images * refactor: remove empty line * feat: always sort image-list output but only if we have more than 1 image * refactor: remove null and len check No need to check for nil or > 1 as a nil slice is by definition an empty slice. * refactor: move sort image to SeenImages --- pkg/deployment/images.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 517087561..f7ef098e7 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -13,6 +13,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/apimachinery/pkg/api/errors" + "sort" "strings" "sync" ) @@ -52,6 +53,9 @@ func (images *Images) SeenImages(simple bool) []types.FixedImage { ret = append(ret, fi) } } + sort.Slice(ret, func(i, j int) bool { + return ret[i].Image < ret[j].Image + }) return ret } From a29832737e9e9e24691f06f91c431dd05872e837 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 7 May 2022 17:18:40 +0200 Subject: [PATCH 0123/2268] refactor: Use dynamic client for fetchCert and friends --- pkg/seal/fetch_cert.go | 74 ++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/pkg/seal/fetch_cert.go b/pkg/seal/fetch_cert.go index 4e5e27d5a..5af09d342 100644 --- a/pkg/seal/fetch_cert.go +++ b/pkg/seal/fetch_cert.go @@ -1,60 +1,52 @@ package seal import ( - "context" "crypto/rsa" "errors" "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" log "github.com/sirupsen/logrus" "io/ioutil" v12 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/util/cert" ) func fetchCert(k *k8s.K8sCluster, namespace string, controllerName string) (*rsa.PublicKey, error) { - var certData []byte - - err := k.WithCoreV1(func(client *v1.CoreV1Client) error { - s, err := openCertFromController(client, namespace, controllerName) - if err != nil { - if controllerName == "sealed-secrets-controller" { - s2, err2 := openCertFromController(client, namespace, "sealed-secrets") - if err2 == nil { - log.Warningf("Looks like you have sealed-secrets controller installed with name 'sealed-secrets', which comes from a legacy kluctl version that deployed it with a non-default name. Please consider re-deploying sealed-secrets operator manually.") - err = nil - s = s2 - } + certData, err := openCertFromController(k, namespace, controllerName) + if err != nil { + if controllerName == "sealed-secrets-controller" { + s2, err2 := openCertFromController(k, namespace, "sealed-secrets") + if err2 == nil { + log.Warningf("Looks like you have sealed-secrets controller installed with name 'sealed-secrets', which comes from a legacy kluctl version that deployed it with a non-default name. Please consider re-deploying sealed-secrets operator manually.") + err = nil + certData = s2 } + } + if err != nil { + log.Warningf("Failed to retrieve public certificate from sealed-secrets-controller, re-trying with bootstrap secret") + certData, err = openCertFromBootstrap(k, namespace) if err != nil { - log.Warningf("Failed to retrieve public certificate from sealed-secrets-controller, re-trying with bootstrap secret") - s, err = openCertFromBootstrap(client, namespace) - if err != nil { - return fmt.Errorf("failed to retrieve sealed secrets public key: %w", err) - } + return nil, fmt.Errorf("failed to retrieve sealed secrets public key: %w", err) } } - certData = s - return nil - }) - if err != nil { - return nil, err } - cert, err := parseKey(certData) - return cert, err + return parseKey(certData) } -func openCertFromBootstrap(c *v1.CoreV1Client, namespace string) ([]byte, error) { - cm, err := c.ConfigMaps(namespace).Get(context.Background(), configMapName, metav1.GetOptions{}) +func openCertFromBootstrap(k *k8s.K8sCluster, namespace string) ([]byte, error) { + ref := k8s2.NewObjectRef("", "v1", "ConfigMap", configMapName, namespace) + cm, _, err := k.GetSingleObject(ref) if err != nil { return nil, err } - v, ok := cm.Data[v12.TLSCertKey] + v, ok, err := cm.GetNestedString("data", v12.TLSCertKey) + if err != nil { + return nil, err + } if !ok { return nil, fmt.Errorf("%s key not found in ConfigMap %s", v12.TLSCertKey, configMapName) } @@ -62,12 +54,12 @@ func openCertFromBootstrap(c *v1.CoreV1Client, namespace string) ([]byte, error) return []byte(v), nil } -func openCertFromController(c v1.CoreV1Interface, namespace, name string) ([]byte, error) { - portName, err := getServicePortName(c, namespace, name) +func openCertFromController(k *k8s.K8sCluster, namespace, name string) ([]byte, error) { + portName, err := getServicePortName(k, namespace, name) if err != nil { return nil, err } - r, err := c.Services(namespace).ProxyGet("http", name, portName, "/v1/cert.pem", nil).Stream(context.Background()) + r, err := k.ProxyGet("http", namespace, name, portName, "/v1/cert.pem", nil) if err != nil { return nil, fmt.Errorf("cannot fetch certificate: %v", err) } @@ -81,12 +73,22 @@ func openCertFromController(c v1.CoreV1Interface, namespace, name string) ([]byt return cert, nil } -func getServicePortName(client v1.CoreV1Interface, namespace, serviceName string) (string, error) { - service, err := client.Services(namespace).Get(context.Background(), serviceName, metav1.GetOptions{}) +func getServicePortName(k *k8s.K8sCluster, namespace, serviceName string) (string, error) { + ref := k8s2.NewObjectRef("", "v1", "Service", serviceName, namespace) + service, _, err := k.GetSingleObject(ref) if err != nil { return "", fmt.Errorf("cannot get sealed secret service: %v", err) } - return service.Spec.Ports[0].Name, nil + + n, ok, err := service.GetNestedString("spec", "ports", 0, "name") + if err != nil { + return "", err + } + if !ok { + return "", fmt.Errorf("spec.ports[0].name not in service object %s", serviceName) + } + + return n, nil } func parseKey(data []byte) (*rsa.PublicKey, error) { From e6435625df446edd53151f29ed3a38ebe16da267 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 7 May 2022 17:20:37 +0200 Subject: [PATCH 0124/2268] fix: Properly pass around and use context --- cmd/kluctl/args/project.go | 2 +- cmd/kluctl/commands/cmd_archive.go | 3 +- .../commands/cmd_check_image_updates.go | 18 +-- cmd/kluctl/commands/cmd_deploy.go | 2 +- cmd/kluctl/commands/cmd_diff.go | 2 +- cmd/kluctl/commands/cmd_downscale.go | 2 +- cmd/kluctl/commands/cmd_list_targets.go | 3 +- cmd/kluctl/commands/cmd_poke_images.go | 2 +- cmd/kluctl/commands/cmd_seal.go | 9 +- cmd/kluctl/commands/cmd_validate.go | 2 +- cmd/kluctl/commands/completion.go | 19 +-- cmd/kluctl/commands/utils.go | 26 ++-- pkg/deployment/commands/deploy.go | 7 +- pkg/deployment/commands/diff.go | 5 +- pkg/deployment/commands/downscale.go | 5 +- pkg/deployment/commands/poke_images.go | 5 +- pkg/deployment/commands/validate.go | 5 +- pkg/deployment/deployment_collection.go | 5 +- pkg/deployment/utils/apply_utils.go | 5 +- pkg/git/mirrored_repo.go | 35 +++-- pkg/k8s/k8s_cluster.go | 141 +++++++++++------- pkg/kluctl_project/git.go | 25 ++-- pkg/kluctl_project/load.go | 7 +- pkg/kluctl_project/project.go | 3 + pkg/kluctl_project/project_archive.go | 5 +- pkg/kluctl_project/project_load.go | 23 ++- pkg/kluctl_project/target_context.go | 11 +- pkg/kluctl_project/targets.go | 11 +- pkg/registries/registries.go | 10 +- 29 files changed, 229 insertions(+), 169 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 81ea403fa..120e1fabd 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -15,7 +15,7 @@ type ProjectFlags struct { OutputMetadata pathType `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` Cluster string `group:"project" help:"Specify/Override cluster"` - LoadTimeout time.Duration `group:"project" help:"Specify timeout for project loading. This will especially limit the time spent in git operations." default:"1m"` + Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"5m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` } diff --git a/cmd/kluctl/commands/cmd_archive.go b/cmd/kluctl/commands/cmd_archive.go index 250571c51..a6a2f349d 100644 --- a/cmd/kluctl/commands/cmd_archive.go +++ b/cmd/kluctl/commands/cmd_archive.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" ) @@ -16,7 +17,7 @@ func (cmd *archiveCmd) Help() string { } func (cmd *archiveCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return p.WriteArchive(cmd.OutputArchive, cmd.ProjectFlags.OutputMetadata == "") }) } diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index 6fd22dfd7..c8956e026 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -3,7 +3,6 @@ package commands import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/registries" - "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/versions" log "github.com/sirupsen/logrus" @@ -24,20 +23,19 @@ func (cmd *checkImageUpdatesCmd) Help() string { } func (cmd *checkImageUpdatesCmd) Run() error { - var renderedImages map[k8s.ObjectRef][]string ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, } - err := withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - renderedImages = ctx.targetCtx.DeploymentCollection.FindRenderedImages() - return nil + return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { + return runCheckImageUpdates(ctx) }) - if err != nil { - return err - } +} + +func runCheckImageUpdates(ctx *commandCtx) error { + renderedImages := ctx.targetCtx.DeploymentCollection.FindRenderedImages() - rh := registries.NewRegistryHelper() + rh := registries.NewRegistryHelper(ctx.ctx) wg := utils.NewWorkerPoolWithErrors(8) defer wg.StopWait(false) @@ -67,7 +65,7 @@ func (cmd *checkImageUpdatesCmd) Run() error { } } } - err = wg.StopWait(false) + err := wg.StopWait(false) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index e234d8b49..c99d69319 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -62,7 +62,7 @@ func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { cb = nil } - result, err := cmd2.Run(ctx.targetCtx.K, cb) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K, cb) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 0f7f70b63..7b2efec67 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -43,7 +43,7 @@ func (cmd *diffCmd) Run() error { cmd2.IgnoreTags = cmd.IgnoreTags cmd2.IgnoreLabels = cmd.IgnoreLabels cmd2.IgnoreAnnotations = cmd.IgnoreAnnotations - result, err := cmd2.Run(ctx.targetCtx.K) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_downscale.go b/cmd/kluctl/commands/cmd_downscale.go index 7df0a744b..78523c4b9 100644 --- a/cmd/kluctl/commands/cmd_downscale.go +++ b/cmd/kluctl/commands/cmd_downscale.go @@ -44,7 +44,7 @@ func (cmd *downscaleCmd) Run() error { cmd2 := commands.NewDownscaleCommand(ctx.targetCtx.DeploymentCollection) - result, err := cmd2.Run(ctx.targetCtx.K) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index c54241e94..64921e6f7 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/types" @@ -16,7 +17,7 @@ func (cmd *listTargetsCmd) Help() string { } func (cmd *listTargetsCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var result []*types.Target for _, t := range p.DynamicTargets { result = append(result, t.Target) diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index a65f2e079..88c2ab7ff 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -44,7 +44,7 @@ func (cmd *pokeImagesCmd) Run() error { cmd2 := commands.NewPokeImagesCommand(ctx.targetCtx.DeploymentCollection) - result, err := cmd2.Run(ctx.targetCtx.K) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index bdabda126..5df4a45f1 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" @@ -61,7 +62,7 @@ func loadSecrets(ctx *commandCtx, target *types.Target, secretsLoader *seal.Secr return nil } -func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.LoadedKluctlProject, targetName string, secretsLoader *seal.SecretsLoader) error { +func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.LoadedKluctlProject, targetName string, secretsLoader *seal.SecretsLoader) error { log.Infof("Sealing for target %s", targetName) ptArgs := projectTargetCommandArgs{ @@ -72,7 +73,7 @@ func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.LoadedKluctlProject, t ptArgs.targetFlags.Target = targetName // pass forSeal=True so that .sealme files are rendered as well - return withProjectTargetCommandContext(ptArgs, p, func(ctx *commandCtx) error { + return withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { err := loadSecrets(ctx, ctx.targetCtx.Target, secretsLoader) if err != nil { return err @@ -119,7 +120,7 @@ func (cmd *sealCmd) runCmdSealForTarget(p *kluctl_project.LoadedKluctlProject, t } func (cmd *sealCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { hadError := false secretsLoader := seal.NewSecretsLoader(p, cmd.SecretsDir) @@ -155,7 +156,7 @@ func (cmd *sealCmd) Run() error { sealTarget = baseTarget } - err := cmd.runCmdSealForTarget(p, sealTarget.Name, secretsLoader) + err := cmd.runCmdSealForTarget(ctx, p, sealTarget.Name, secretsLoader) if err != nil { log.Warningf("Sealing for target %s failed: %v", sealTarget.Name, err) hadError = true diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index 4a9bf23e4..6f59906e2 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -39,7 +39,7 @@ func (cmd *validateCmd) Run() error { startTime := time.Now() cmd2 := commands.NewValidateCommand(ctx.targetCtx.DeploymentCollection) for true { - result, err := cmd2.Run(ctx.targetCtx.K) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index a316051bb..99cfdd5b1 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/types" @@ -47,18 +48,18 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err return nil } -func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(p *kluctl_project.LoadedKluctlProject) error) error { +func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { // let's not update git caches too often projectArgs.GitCacheUpdateInterval = time.Second * 60 - return withKluctlProjectFromArgs(*projectArgs, false, func(p *kluctl_project.LoadedKluctlProject) error { - return cb(p) + return withKluctlProjectFromArgs(*projectArgs, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return cb(ctx, p) }) } func buildClusterCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var ret []string - err := withProjectForCompletion(projectArgs, func(p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(projectArgs, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { dents, err := os.ReadDir(p.ClustersDir) if err != nil { return err @@ -86,7 +87,7 @@ func buildClusterCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra. func buildTargetCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var ret []string - err := withProjectForCompletion(projectArgs, func(p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(projectArgs, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { for _, t := range p.DynamicTargets { ret = append(ret, t.Target.Name) } @@ -132,7 +133,7 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd var deploymentItemDirs utils.OrderedMap var mutex sync.Mutex - err := withProjectForCompletion(&ptArgs.projectFlags, func(p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(&ptArgs.projectFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { for _, t := range p.DynamicTargets { @@ -148,7 +149,7 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd ptArgs.targetFlags.Target = t wg.Add(1) go func() { - _ = withProjectTargetCommandContext(ptArgs, p, func(ctx *commandCtx) error { + _ = withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { mutex.Lock() defer mutex.Unlock() for _, di := range ctx.targetCtx.DeploymentCollection.Deployments { @@ -186,7 +187,7 @@ func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, a var images utils.OrderedMap var mutex sync.Mutex - err := withProjectForCompletion(&ptArgs.projectFlags, func(p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(&ptArgs.projectFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { for _, t := range p.DynamicTargets { @@ -202,7 +203,7 @@ func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, a ptArgs.targetFlags.Target = t wg.Add(1) go func() { - _ = withProjectTargetCommandContext(ptArgs, p, func(ctx *commandCtx) error { + _ = withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { err := ctx.targetCtx.DeploymentCollection.Prepare(nil) if err != nil { log.Error(err) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index e1adddebb..a599aad54 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -18,10 +18,9 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "os" - "time" ) -func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, cb func(p *kluctl_project.LoadedKluctlProject) error) error { +func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { var url *git_url.GitUrl if projectFlags.ProjectUrl != "" { var err error @@ -71,9 +70,10 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b GitUpdateInterval: projectFlags.GitCacheUpdateInterval, } - loadCtx, cancel := context.WithDeadline(context.Background(), time.Now().Add(projectFlags.LoadTimeout)) + ctx := context.Background() + ctx, cancel := context.WithTimeout(ctx, projectFlags.Timeout) defer cancel() - p, err := kluctl_project.LoadKluctlProject(loadCtx, loadArgs, tmpDir, j2) + p, err := kluctl_project.LoadKluctlProject(ctx, loadArgs, tmpDir, j2) if err != nil { return err } @@ -88,7 +88,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b return err } } - return cb(p) + return cb(ctx, p) } type projectTargetCommandArgs struct { @@ -105,18 +105,19 @@ type projectTargetCommandArgs struct { } type commandCtx struct { + ctx context.Context targetCtx *kluctl_project.TargetContext images *deployment.Images } func withProjectCommandContext(args projectTargetCommandArgs, cb func(ctx *commandCtx) error) error { - return withKluctlProjectFromArgs(args.projectFlags, true, func(p *kluctl_project.LoadedKluctlProject) error { - return withProjectTargetCommandContext(args, p, cb) + return withKluctlProjectFromArgs(args.projectFlags, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withProjectTargetCommandContext(ctx, args, p, cb) }) } -func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_project.LoadedKluctlProject, cb func(ctx *commandCtx) error) error { - rh := registries.NewRegistryHelper() +func withProjectTargetCommandContext(ctx context.Context, args projectTargetCommandArgs, p *kluctl_project.LoadedKluctlProject, cb func(ctx *commandCtx) error) error { + rh := registries.NewRegistryHelper(ctx) err := rh.ParseAuthEntriesFromEnv() if err != nil { return fmt.Errorf("failed to parse registry auth from environment: %w", err) @@ -162,7 +163,7 @@ func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_pr return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(configLoadingRules, configOverrides).ClientConfig() } - ctx, err := p.NewTargetContext(clientConfigGetter, args.targetFlags.Target, args.projectFlags.Cluster, + targetCtx, err := p.NewTargetContext(ctx, clientConfigGetter, args.targetFlags.Target, args.projectFlags.Cluster, args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, optionArgs, args.forSeal, images, inclusion, renderOutputDir) @@ -171,14 +172,15 @@ func withProjectTargetCommandContext(args projectTargetCommandArgs, p *kluctl_pr } if !args.forSeal && !args.forCompletion { - err = ctx.DeploymentCollection.Prepare(ctx.K) + err = targetCtx.DeploymentCollection.Prepare(targetCtx.K) if err != nil { return err } } cmdCtx := &commandCtx{ - targetCtx: ctx, + ctx: ctx, + targetCtx: targetCtx, images: images, } diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index ed762c0e1..d61461d70 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -25,7 +26,7 @@ func NewDeployCommand(c *deployment.DeploymentCollection) *DeployCommand { } } -func (cmd *DeployCommand) Run(k *k8s.K8sCluster, diffResultCb func(diffResult *types.CommandResult) error) (*types.CommandResult, error) { +func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResultCb func(diffResult *types.CommandResult) error) (*types.CommandResult, error) { dew := utils2.NewDeploymentErrorsAndWarnings() ru := utils2.NewRemoteObjectsUtil(dew) @@ -46,7 +47,7 @@ func (cmd *DeployCommand) Run(k *k8s.K8sCluster, diffResultCb func(diffResult *t } if diffResultCb != nil { - au := utils2.NewApplyDeploymentsUtil(dew, cmd.c.Deployments, ru, k, o) + au := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, o) au.ApplyDeployments() du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, au.GetAppliedObjectsMap()) @@ -75,7 +76,7 @@ func (cmd *DeployCommand) Run(k *k8s.K8sCluster, diffResultCb func(diffResult *t o.DryRun = k.DryRun o.AbortOnError = cmd.AbortOnError - au := utils2.NewApplyDeploymentsUtil(dew, cmd.c.Deployments, ru, k, o) + au := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, o) au.ApplyDeployments() du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, au.GetAppliedObjectsMap()) diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index c7e35a3d5..5dafbe374 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -24,7 +25,7 @@ func NewDiffCommand(c *deployment.DeploymentCollection) *DiffCommand { } } -func (cmd *DiffCommand) Run(k *k8s.K8sCluster) (*types.CommandResult, error) { +func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.CommandResult, error) { dew := utils.NewDeploymentErrorsAndWarnings() ru := utils.NewRemoteObjectsUtil(dew) @@ -41,7 +42,7 @@ func (cmd *DiffCommand) Run(k *k8s.K8sCluster) (*types.CommandResult, error) { AbortOnError: false, WaitObjectTimeout: 0, } - au := utils.NewApplyDeploymentsUtil(dew, cmd.c.Deployments, ru, k, o) + au := utils.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, o) au.ApplyDeployments() du := utils.NewDiffUtil(dew, cmd.c.Deployments, ru, au.GetAppliedObjectsMap()) diff --git a/pkg/deployment/commands/downscale.go b/pkg/deployment/commands/downscale.go index f12d86d6b..3f0ee568d 100644 --- a/pkg/deployment/commands/downscale.go +++ b/pkg/deployment/commands/downscale.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -20,7 +21,7 @@ func NewDownscaleCommand(c *deployment.DeploymentCollection) *DownscaleCommand { } } -func (cmd *DownscaleCommand) Run(k *k8s.K8sCluster) (*types.CommandResult, error) { +func (cmd *DownscaleCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.CommandResult, error) { var wg sync.WaitGroup dew := utils2.NewDeploymentErrorsAndWarnings() @@ -31,7 +32,7 @@ func (cmd *DownscaleCommand) Run(k *k8s.K8sCluster) (*types.CommandResult, error return nil, err } - ad := utils2.NewApplyDeploymentsUtil(dew, cmd.c.Deployments, ru, k, &utils2.ApplyUtilOptions{}) + ad := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, &utils2.ApplyUtilOptions{}) appliedObjects := make(map[k8s2.ObjectRef]*uo.UnstructuredObject) diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index d325da3b2..9c8a50ad6 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" @@ -21,7 +22,7 @@ func NewPokeImagesCommand(c *deployment.DeploymentCollection) *PokeImagesCommand } } -func (cmd *PokeImagesCommand) Run(k *k8s.K8sCluster) (*types.CommandResult, error) { +func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.CommandResult, error) { var wg sync.WaitGroup dew := utils2.NewDeploymentErrorsAndWarnings() @@ -67,7 +68,7 @@ func (cmd *PokeImagesCommand) Run(k *k8s.K8sCluster) (*types.CommandResult, erro return o, nil } - ad := utils2.NewApplyDeploymentsUtil(dew, cmd.c.Deployments, ru, k, &utils2.ApplyUtilOptions{}) + ad := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, &utils2.ApplyUtilOptions{}) for ref, containers := range containersAndImages { ref := ref diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 5fa33f251..8e35d5e54 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -24,7 +25,7 @@ func NewValidateCommand(c *deployment.DeploymentCollection) *ValidateCommand { return cmd } -func (cmd *ValidateCommand) Run(k *k8s.K8sCluster) (*types.ValidateResult, error) { +func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.ValidateResult, error) { var result types.ValidateResult cmd.dew.Init() @@ -34,7 +35,7 @@ func (cmd *ValidateCommand) Run(k *k8s.K8sCluster) (*types.ValidateResult, error return nil, err } - ad := utils2.NewApplyDeploymentsUtil(cmd.dew, cmd.c.Deployments, cmd.ru, k, &utils2.ApplyUtilOptions{}) + ad := utils2.NewApplyDeploymentsUtil(ctx, cmd.dew, cmd.c.Deployments, cmd.ru, k, &utils2.ApplyUtilOptions{}) for _, d := range cmd.c.Deployments { if !d.CheckInclusionForDeploy() { continue diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 2d9333612..80ea9ac7e 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -14,6 +14,8 @@ import ( ) type DeploymentCollection struct { + ctx context.Context + Project *DeploymentProject Images *Images Inclusion *utils.Inclusion @@ -24,8 +26,9 @@ type DeploymentCollection struct { mutex sync.Mutex } -func NewDeploymentCollection(project *DeploymentProject, images *Images, inclusion *utils.Inclusion, renderDir string, forSeal bool) (*DeploymentCollection, error) { +func NewDeploymentCollection(ctx context.Context, project *DeploymentProject, images *Images, inclusion *utils.Inclusion, renderDir string, forSeal bool) (*DeploymentCollection, error) { dc := &DeploymentCollection{ + ctx: ctx, Project: project, Images: images, Inclusion: inclusion, diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 3f69a8416..ce8c0b31e 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -55,6 +55,8 @@ type ApplyUtil struct { } type ApplyDeploymentsUtil struct { + ctx context.Context + dew *DeploymentErrorsAndWarnings deployments []*deployment.DeploymentItem ru *RemoteObjectUtils @@ -67,8 +69,9 @@ type ApplyDeploymentsUtil struct { results []*ApplyUtil } -func NewApplyDeploymentsUtil(dew *DeploymentErrorsAndWarnings, deployments []*deployment.DeploymentItem, ru *RemoteObjectUtils, k *k8s.K8sCluster, o *ApplyUtilOptions) *ApplyDeploymentsUtil { +func NewApplyDeploymentsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnings, deployments []*deployment.DeploymentItem, ru *RemoteObjectUtils, k *k8s.K8sCluster, o *ApplyUtilOptions) *ApplyDeploymentsUtil { ret := &ApplyDeploymentsUtil{ + ctx: ctx, dew: dew, deployments: deployments, ru: ru, diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index fc8d40fdc..8940c6047 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -21,6 +21,8 @@ import "github.com/gofrs/flock" var cacheBaseDir = filepath.Join(utils.GetTmpBaseDir(), "git-cache") type MirroredGitRepo struct { + ctx context.Context + url git_url.GitUrl mirrorDir string @@ -28,9 +30,10 @@ type MirroredGitRepo struct { fileLock *flock.Flock } -func NewMirroredGitRepo(u git_url.GitUrl) (*MirroredGitRepo, error) { +func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl) (*MirroredGitRepo, error) { mirrorRepoName := buildMirrorRepoName(u) o := &MirroredGitRepo{ + ctx: ctx, url: u, mirrorDir: filepath.Join(cacheBaseDir, mirrorRepoName), } @@ -53,8 +56,8 @@ func (g *MirroredGitRepo) SetUpdated(u bool) { g.hasUpdated = u } -func (g *MirroredGitRepo) Lock(ctx context.Context) error { - ok, err := g.fileLock.TryLockContext(ctx, time.Millisecond*100) +func (g *MirroredGitRepo) Lock() error { + ok, err := g.fileLock.TryLockContext(g.ctx, time.Millisecond*100) if err != nil { return fmt.Errorf("locking of %s failed: %w", g.fileLock.Path(), err) } @@ -73,8 +76,8 @@ func (g *MirroredGitRepo) Unlock() error { return nil } -func (g *MirroredGitRepo) WithLock(ctx context.Context, cb func() error) error { - err := g.Lock(ctx) +func (g *MirroredGitRepo) WithLock(cb func() error) error { + err := g.Lock() if err != nil { return err } @@ -82,9 +85,9 @@ func (g *MirroredGitRepo) WithLock(ctx context.Context, cb func() error) error { return cb() } -func (g *MirroredGitRepo) MaybeWithLock(ctx context.Context, lock bool, cb func() error) error { +func (g *MirroredGitRepo) MaybeWithLock(lock bool, cb func() error) error { if lock { - return g.WithLock(ctx, cb) + return g.WithLock(cb) } return cb() } @@ -167,7 +170,7 @@ func (g *MirroredGitRepo) cleanupMirrorDir() error { return nil } -func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProviders *auth2.GitAuthProviders) error { +func (g *MirroredGitRepo) update(repoDir string, authProviders *auth2.GitAuthProviders) error { log.Infof("Updating mirror repo: url='%v'", g.url.String()) r, err := git.PlainOpen(repoDir) if err != nil { @@ -181,7 +184,7 @@ func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProvid return err } - remoteRefs, err := remote.ListContext(ctx, &git.ListOptions{ + remoteRefs, err := remote.ListContext(g.ctx, &git.ListOptions{ Auth: auth.AuthMethod, CABundle: auth.CABundle, }) @@ -224,7 +227,7 @@ func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProvid } if changed { - err = remote.FetchContext(ctx, &git.FetchOptions{ + err = remote.FetchContext(g.ctx, &git.FetchOptions{ Auth: auth.AuthMethod, CABundle: auth.CABundle, Progress: os.Stdout, @@ -260,10 +263,10 @@ func (g *MirroredGitRepo) update(ctx context.Context, repoDir string, authProvid return nil } -func (g *MirroredGitRepo) cloneOrUpdate(ctx context.Context, authProviders *auth2.GitAuthProviders) error { +func (g *MirroredGitRepo) cloneOrUpdate(authProviders *auth2.GitAuthProviders) error { initMarker := filepath.Join(g.mirrorDir, ".cache2.init") if utils.IsFile(initMarker) { - return g.update(ctx, g.mirrorDir, authProviders) + return g.update(g.mirrorDir, authProviders) } err := g.cleanupMirrorDir() if err != nil { @@ -295,7 +298,7 @@ func (g *MirroredGitRepo) cloneOrUpdate(ctx context.Context, authProviders *auth return err } - err = g.update(ctx, tmpMirrorDir, authProviders) + err = g.update(tmpMirrorDir, authProviders) if err != nil { return err } @@ -317,8 +320,8 @@ func (g *MirroredGitRepo) cloneOrUpdate(ctx context.Context, authProviders *auth return nil } -func (g *MirroredGitRepo) Update(ctx context.Context, authProviders *auth2.GitAuthProviders) error { - err := g.cloneOrUpdate(ctx, authProviders) +func (g *MirroredGitRepo) Update(authProviders *auth2.GitAuthProviders) error { + err := g.cloneOrUpdate(authProviders) if err != nil { return err } @@ -326,7 +329,7 @@ func (g *MirroredGitRepo) Update(ctx context.Context, authProviders *auth2.GitAu return nil } -func (g *MirroredGitRepo) CloneProject(ctx context.Context, ref string, targetDir string) error { +func (g *MirroredGitRepo) CloneProject(ref string, targetDir string) error { if !g.fileLock.Locked() || !g.hasUpdated { log.Fatalf("tried to clone from a project that is not locked/updated") } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 5c901aafa..ea633652a 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -10,6 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" + "io" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -37,6 +38,8 @@ var ( ) type K8sCluster struct { + ctx context.Context + DryRun bool restConfig *rest.Config @@ -77,8 +80,9 @@ func (p *parallelClientEntry) HandleWarningHeader(code int, agent string, text s }) } -func NewK8sCluster(configIn *rest.Config, dryRun bool) (*K8sCluster, error) { +func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8sCluster, error) { k := &K8sCluster{ + ctx: ctx, DryRun: dryRun, } @@ -168,10 +172,34 @@ func (k *K8sCluster) updateResources(doLock bool) error { err error }{} - _, arls, err := k.discovery.ServerGroupsAndResources() - if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { - return err + // the discovery client doesn't support cancellation, so we need to run it in the background and wait for it + var arls []*v1.APIResourceList + var preferredArls []*v1.APIResourceList + finished := make(chan error) + go func() { + var err error + _, arls, err = k.discovery.ServerGroupsAndResources() + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { + finished <- err + return + } + preferredArls, err = k.discovery.ServerPreferredResources() + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { + finished <- err + return + } + finished <- nil + }() + + select { + case err := <-finished: + if err != nil { + return err + } + case <-k.ctx.Done(): + return fmt.Errorf("failed listing api resources: %w", k.ctx.Err()) } + for _, arl := range arls { for _, ar := range arl.APIResources { if strings.Index(ar.Name, "/") != -1 { @@ -202,11 +230,7 @@ func (k *K8sCluster) updateResources(doLock bool) error { } } - arls, err = k.discovery.ServerPreferredResources() - if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { - return err - } - for _, arl := range arls { + for _, arl := range preferredArls { for _, ar := range arl.APIResources { if strings.Index(ar.Name, "/") != -1 { // skip subresources @@ -420,50 +444,46 @@ func (k *K8sCluster) GetSchemaForGVK(gvk schema.GroupVersionKind) (*uo.Unstructu return nil, fmt.Errorf("schema for %s not found", gvk.String()) } -func (k *K8sCluster) WithCoreV1(cb func(client *corev1.CoreV1Client) error) error { - p := <-k.clientPool - defer func() { k.clientPool <- p }() - return cb(p.corev1) +func (k *K8sCluster) withClientFromPool(cb func(p *parallelClientEntry) error) ([]ApiWarning, error) { + select { + case p := <-k.clientPool: + defer func() { k.clientPool <- p }() + p.warnings = nil + err := cb(p) + return append([]ApiWarning(nil), p.warnings...), err + case <-k.ctx.Done(): + return nil, fmt.Errorf("failed waiting for free client: %w", k.ctx.Err()) + } } func (k *K8sCluster) withDynamicClientForGVK(gvk schema.GroupVersionKind, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { - gvr, namespaced, err := k.getGVRForGVK(gvk) - if err != nil { - return nil, err - } - - p := <-k.clientPool - defer func() { k.clientPool <- p }() - - p.warnings = nil + return k.withClientFromPool(func(p *parallelClientEntry) error { + gvr, namespaced, err := k.getGVRForGVK(gvk) + if err != nil { + return err + } - if namespaced && namespace != "" { - err = cb(p.dynamicClient.Resource(*gvr).Namespace(namespace)) - return append([]ApiWarning(nil), p.warnings...), err - } else { - err = cb(p.dynamicClient.Resource(*gvr)) - return append([]ApiWarning(nil), p.warnings...), err - } + if namespaced && namespace != "" { + return cb(p.dynamicClient.Resource(*gvr).Namespace(namespace)) + } else { + return cb(p.dynamicClient.Resource(*gvr)) + } + }) } func (k *K8sCluster) withMetadataClientForGVK(gvk schema.GroupVersionKind, namespace string, cb func(r metadata.ResourceInterface) error) ([]ApiWarning, error) { - gvr, namespaced, err := k.getGVRForGVK(gvk) - if err != nil { - return nil, err - } - - p := <-k.clientPool - defer func() { k.clientPool <- p }() - - p.warnings = nil + return k.withClientFromPool(func(p *parallelClientEntry) error { + gvr, namespaced, err := k.getGVRForGVK(gvk) + if err != nil { + return err + } - if namespaced && namespace != "" { - err = cb(p.metadataClient.Resource(*gvr).Namespace(namespace)) - return append([]ApiWarning(nil), p.warnings...), err - } else { - err = cb(p.metadataClient.Resource(*gvr)) - return append([]ApiWarning(nil), p.warnings...), err - } + if namespaced && namespace != "" { + return cb(p.metadataClient.Resource(*gvr).Namespace(namespace)) + } else { + return cb(p.metadataClient.Resource(*gvr)) + } + }) } func (k *K8sCluster) buildLabelSelector(labels map[string]string) string { @@ -485,7 +505,7 @@ func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, o := v1.ListOptions{ LabelSelector: k.buildLabelSelector(labels), } - x, err := r.List(context.Background(), o) + x, err := r.List(k.ctx, o) if err != nil { return err } @@ -504,7 +524,7 @@ func (k *K8sCluster) ListObjectsMetadata(gvk schema.GroupVersionKind, namespace o := v1.ListOptions{ LabelSelector: k.buildLabelSelector(labels), } - x, err := r.List(context.Background(), o) + x, err := r.List(k.ctx, o) if err != nil { return err } @@ -585,7 +605,7 @@ func (k *K8sCluster) GetSingleObject(ref k8s.ObjectRef) (*uo.UnstructuredObject, var result *uo.UnstructuredObject apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { o := v1.GetOptions{} - x, err := r.Get(context.Background(), ref.Name, o) + x, err := r.Get(k.ctx, ref.Name, o) if err != nil { return err } @@ -648,7 +668,7 @@ func (k *K8sCluster) DeleteSingleObject(ref k8s.ObjectRef, options DeleteOptions } apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { - err := r.Delete(context.Background(), ref.Name, o) + err := r.Delete(k.ctx, ref.Name, o) if err != nil { if options.IgnoreNotFoundError && errors.IsNotFound(err) { return nil @@ -681,7 +701,12 @@ func (k *K8sCluster) waitForDeletedObject(ref k8s.ObjectRef) error { return err } - time.Sleep(time.Millisecond * 100) + select { + case <-time.After(500 * time.Millisecond): + continue + case <-k.ctx.Done(): + return fmt.Errorf("failed waiting for deletion of %s: %w", ref.String(), k.ctx.Err()) + } } return nil } @@ -800,7 +825,7 @@ func (k *K8sCluster) PatchObject(o *uo.UnstructuredObject, options PatchOptions) log2.Debugf("patching") var result *uo.UnstructuredObject apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { - x, err := r.Patch(context.Background(), ref.Name, types.ApplyPatchType, data, po) + x, err := r.Patch(k.ctx, ref.Name, types.ApplyPatchType, data, po) if err != nil { return fmt.Errorf("failed to patch %s: %w", ref.String(), err) } @@ -829,7 +854,7 @@ func (k *K8sCluster) UpdateObject(o *uo.UnstructuredObject, options UpdateOption log2.Debugf("updating") var result *uo.UnstructuredObject apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { - x, err := r.Update(context.Background(), o.ToUnstructured(), updateOpts) + x, err := r.Update(k.ctx, o.ToUnstructured(), updateOpts) if err != nil { return err } @@ -838,3 +863,15 @@ func (k *K8sCluster) UpdateObject(o *uo.UnstructuredObject, options UpdateOption }) return result, apiWarnings, err } + +func (k *K8sCluster) ProxyGet(scheme, namespace, name, port, path string, params map[string]string) (io.ReadCloser, error) { + var ret rest.ResponseWrapper + _, err := k.withClientFromPool(func(p *parallelClientEntry) error { + ret = p.corev1.Services(namespace).ProxyGet(scheme, name, port, path, params) + return nil + }) + if err != nil { + return nil, err + } + return ret.Stream(k.ctx) +} diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index ee8ba490c..1be037384 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -1,7 +1,6 @@ package kluctl_project import ( - "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/git" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" @@ -16,7 +15,7 @@ import ( "time" ) -func (c *LoadedKluctlProject) updateGitCache(ctx context.Context, mr *git.MirroredGitRepo) error { +func (c *LoadedKluctlProject) updateGitCache(mr *git.MirroredGitRepo) error { if mr.HasUpdated() { return nil } @@ -24,10 +23,10 @@ func (c *LoadedKluctlProject) updateGitCache(ctx context.Context, mr *git.Mirror mr.SetUpdated(true) return nil } - return mr.Update(ctx, c.loadArgs.GitAuthProviders) + return mr.Update(c.loadArgs.GitAuthProviders) } -func (c *LoadedKluctlProject) updateGitCaches(ctx context.Context) error { +func (c *LoadedKluctlProject) updateGitCaches() error { var waitGroup sync.WaitGroup var firstError error var firstErrorLock sync.Mutex @@ -41,8 +40,8 @@ func (c *LoadedKluctlProject) updateGitCaches(ctx context.Context) error { } doUpdateRepo := func(repo *git.MirroredGitRepo) error { - return repo.WithLock(ctx, func() error { - return c.updateGitCache(ctx, repo) + return repo.WithLock(func() error { + return c.updateGitCache(repo) }) } doUpdateGitProject := func(u git_url.GitUrl) error { @@ -50,7 +49,7 @@ func (c *LoadedKluctlProject) updateGitCaches(ctx context.Context) error { if ok { return nil } - mr, err := git.NewMirroredGitRepo(u) + mr, err := git.NewMirroredGitRepo(c.ctx, u) if err != nil { return err } @@ -108,7 +107,7 @@ func (c *LoadedKluctlProject) updateGitCaches(ctx context.Context) error { return firstError } -func (c *LoadedKluctlProject) cloneGitProject(ctx context.Context, gitProject *types2.GitProject, targetDir string, doLock bool) (info git.GitRepoInfo, err error) { +func (c *LoadedKluctlProject) cloneGitProject(gitProject *types2.GitProject, targetDir string, doLock bool) (info git.GitRepoInfo, err error) { err = os.MkdirAll(filepath.Join(c.TmpDir, "git"), 0o700) if err != nil { return @@ -116,24 +115,24 @@ func (c *LoadedKluctlProject) cloneGitProject(ctx context.Context, gitProject *t mr, ok := c.mirroredRepos[gitProject.Url.NormalizedRepoKey()] if !ok { - mr, err = git.NewMirroredGitRepo(gitProject.Url) + mr, err = git.NewMirroredGitRepo(c.ctx, gitProject.Url) if err != nil { return } c.mirroredRepos[gitProject.Url.NormalizedRepoKey()] = mr - err = mr.Lock(ctx) + err = mr.Lock() if err != nil { return } defer mr.Unlock() } - err = mr.MaybeWithLock(ctx, doLock, func() error { - err := c.updateGitCache(ctx, mr) + err = mr.MaybeWithLock(doLock, func() error { + err := c.updateGitCache(mr) if err != nil { return err } - return mr.CloneProject(ctx, gitProject.Ref, targetDir) + return mr.CloneProject(gitProject.Ref, targetDir) }) if err != nil { return diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 56a1da171..398a6c958 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -10,6 +10,7 @@ import ( func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*LoadedKluctlProject, error) { p := &LoadedKluctlProject{ + ctx: ctx, loadArgs: args, TmpDir: tmpDir, J2: j2, @@ -22,17 +23,17 @@ func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir s if args.ProjectUrl != nil || args.ProjectRef != "" || args.ProjectConfig != "" || args.LocalClusters != "" || args.LocalDeployment != "" || args.LocalSealedSecrets != "" { return nil, fmt.Errorf("--from-archive can not be combined with any other project related option") } - err := p.loadFromArchive(ctx) + err := p.loadFromArchive() if err != nil { return nil, err } return p, nil } else { - err := p.loadKluctlProject(ctx) + err := p.loadKluctlProject() if err != nil { return nil, err } - err = p.loadTargets(ctx) + err = p.loadTargets() if err != nil { return nil, err } diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 06d555e00..3b0d0efff 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -1,6 +1,7 @@ package kluctl_project import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/jinja2" @@ -9,6 +10,8 @@ import ( ) type LoadedKluctlProject struct { + ctx context.Context + loadArgs LoadKluctlProjectArgs TmpDir string diff --git a/pkg/kluctl_project/project_archive.go b/pkg/kluctl_project/project_archive.go index 864e6a9c2..d9e94f053 100644 --- a/pkg/kluctl_project/project_archive.go +++ b/pkg/kluctl_project/project_archive.go @@ -3,7 +3,6 @@ package kluctl_project import ( "archive/tar" "compress/gzip" - "context" "fmt" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -14,7 +13,7 @@ import ( "time" ) -func (c *LoadedKluctlProject) loadFromArchive(ctx context.Context) error { +func (c *LoadedKluctlProject) loadFromArchive() error { var dir string if utils.IsFile(c.loadArgs.FromArchive) { dir = filepath.Join(c.TmpDir, "archive") @@ -61,7 +60,7 @@ func (c *LoadedKluctlProject) loadFromArchive(ctx context.Context) error { c.involvedRepos = pmd.InvolvedRepos c.DynamicTargets = pmd.Targets - err = c.loadKluctlProject(ctx) + err = c.loadKluctlProject() if err != nil { return err } diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 75f24ac15..c4d8b9527 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -1,7 +1,6 @@ package kluctl_project import ( - "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/git" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" @@ -59,7 +58,7 @@ func (c *LoadedKluctlProject) localProject(dir string) gitProjectInfo { } } -func (c *LoadedKluctlProject) loadGitProject(ctx context.Context, gitProject *types2.GitProject, defaultSubDir string, doLock bool, doAddInvolvedRepo bool) (ret gitProjectInfo, err error) { +func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defaultSubDir string, doLock bool, doAddInvolvedRepo bool) (ret gitProjectInfo, err error) { cloneDir, err := c.buildCloneDir(gitProject.Url, gitProject.Ref) if err != nil { return @@ -82,7 +81,7 @@ func (c *LoadedKluctlProject) loadGitProject(ctx context.Context, gitProject *ty } var ri git.GitRepoInfo - ri, err = c.cloneGitProject(ctx, gitProject, cloneDir, doLock) + ri, err = c.cloneGitProject(gitProject, cloneDir, doLock) if err != nil { return } @@ -120,7 +119,7 @@ func (c *LoadedKluctlProject) loadGitProject(ctx context.Context, gitProject *ty return } -func (c *LoadedKluctlProject) loadExternalProject(ctx context.Context, ep *types2.ExternalProject, defaultGitSubDir string, localDir string) (gitProjectInfo, error) { +func (c *LoadedKluctlProject) loadExternalProject(ep *types2.ExternalProject, defaultGitSubDir string, localDir string) (gitProjectInfo, error) { if localDir != "" { return c.localProject(localDir), nil } @@ -133,7 +132,7 @@ func (c *LoadedKluctlProject) loadExternalProject(ctx context.Context, ep *types if ep.Project != nil { // pointing to an actual external project, so let's try to clone it - return c.loadGitProject(ctx, ep.Project, defaultGitSubDir, true, true) + return c.loadGitProject(ep.Project, defaultGitSubDir, true, true) } // ExternalProject was provided but without an external repo url, so point into the kluctl project. @@ -147,7 +146,7 @@ func (c *LoadedKluctlProject) loadExternalProject(ctx context.Context, ep *types return c.localProject(p), nil } -func (c *LoadedKluctlProject) loadKluctlProject(ctx context.Context) error { +func (c *LoadedKluctlProject) loadKluctlProject() error { var err error if c.loadArgs.ProjectUrl == nil { @@ -158,7 +157,7 @@ func (c *LoadedKluctlProject) loadKluctlProject(ctx context.Context) error { return err } } else { - gi, err := c.loadGitProject(ctx, &types2.GitProject{ + gi, err := c.loadGitProject(&types2.GitProject{ Url: *c.loadArgs.ProjectUrl, Ref: c.loadArgs.ProjectRef, }, "", true, true) @@ -184,17 +183,17 @@ func (c *LoadedKluctlProject) loadKluctlProject(ctx context.Context) error { return err } - err = c.updateGitCaches(ctx) + err = c.updateGitCaches() if err != nil { return err } } - deploymentInfo, err := c.loadExternalProject(ctx, c.Config.Deployment, "", c.loadArgs.LocalDeployment) + deploymentInfo, err := c.loadExternalProject(c.Config.Deployment, "", c.loadArgs.LocalDeployment) if err != nil { return err } - sealedSecretsInfo, err := c.loadExternalProject(ctx, c.Config.SealedSecrets, ".sealed-secrets", c.loadArgs.LocalSealedSecrets) + sealedSecretsInfo, err := c.loadExternalProject(c.Config.SealedSecrets, ".sealed-secrets", c.loadArgs.LocalSealedSecrets) if err != nil { return err } @@ -203,14 +202,14 @@ func (c *LoadedKluctlProject) loadKluctlProject(ctx context.Context) error { clustersInfos = append(clustersInfos, c.localProject(c.loadArgs.LocalClusters)) } else if len(c.Config.Clusters.Projects) != 0 { for _, ep := range c.Config.Clusters.Projects { - info, err := c.loadExternalProject(ctx, &ep, "clusters", "") + info, err := c.loadExternalProject(&ep, "clusters", "") if err != nil { return err } clustersInfos = append(clustersInfos, info) } } else { - ci, err := c.loadExternalProject(ctx, nil, "clusters", "") + ci, err := c.loadExternalProject(nil, "clusters", "") if err != nil { return err } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index e2038eb75..50d8a7f27 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -1,6 +1,7 @@ package kluctl_project import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/jinja2" @@ -21,7 +22,7 @@ type TargetContext struct { DeploymentCollection *deployment.DeploymentCollection } -func (p *LoadedKluctlProject) NewTargetContext(clientConfigGetter func(context string) (*rest.Config, error), targetName string, clusterName string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { +func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, clientConfigGetter func(context string) (*rest.Config, error), targetName string, clusterName string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { deploymentDir, err := filepath.Abs(p.DeploymentDir) if err != nil { return nil, err @@ -59,7 +60,7 @@ func (p *LoadedKluctlProject) NewTargetContext(clientConfigGetter func(context s var k *k8s.K8sCluster if clientConfig != nil { - k, err = k8s.NewK8sCluster(clientConfig, dryRun) + k, err = k8s.NewK8sCluster(ctx, clientConfig, dryRun) if err != nil { return nil, err } @@ -110,12 +111,12 @@ func (p *LoadedKluctlProject) NewTargetContext(clientConfigGetter func(context s if err != nil { return nil, err } - c, err := deployment.NewDeploymentCollection(d, images, inclusion, renderOutputDir, forSeal) + c, err := deployment.NewDeploymentCollection(ctx, d, images, inclusion, renderOutputDir, forSeal) if err != nil { return nil, err } - ctx := &TargetContext{ + targetCtx := &TargetContext{ KluctlProject: p, Target: target, ClusterConfig: clusterConfig, @@ -124,5 +125,5 @@ func (p *LoadedKluctlProject) NewTargetContext(clientConfigGetter func(context s DeploymentCollection: c, } - return ctx, nil + return targetCtx, nil } diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 426560204..baaff34d2 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -1,7 +1,6 @@ package kluctl_project import ( - "context" "fmt" securejoin "github.com/cyphar/filepath-securejoin" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" @@ -27,7 +26,7 @@ type dynamicTargetInfo struct { defaultBranch string } -func (c *LoadedKluctlProject) loadTargets(ctx context.Context) error { +func (c *LoadedKluctlProject) loadTargets() error { targetNames := make(map[string]bool) c.DynamicTargets = nil @@ -40,7 +39,7 @@ func (c *LoadedKluctlProject) loadTargets(ctx context.Context) error { targetInfos = append(targetInfos, l...) } - err := c.cloneDynamicTargets(ctx, targetInfos) + err := c.cloneDynamicTargets(targetInfos) if err != nil { return err } @@ -221,13 +220,13 @@ func (c *LoadedKluctlProject) matchRef(s string, pattern string) (bool, string, } } -func (c *LoadedKluctlProject) cloneDynamicTargets(ctx context.Context, dynamicTargets []*dynamicTargetInfo) error { +func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTargetInfo) error { wp := utils.NewDebuggerAwareWorkerPool(8) defer wp.StopWait(false) // lock all involved repos first for _, mr := range c.mirroredRepos { - err := mr.Lock(ctx) + err := mr.Lock() if err != nil { return err } @@ -252,7 +251,7 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(ctx context.Context, dynamicTa gitProject := *targetInfo.gitProject gitProject.Ref = *targetInfo.ref - gi, err := c.loadGitProject(ctx, &gitProject, "", false, false) + gi, err := c.loadGitProject(&gitProject, "", false, false) mutex.Lock() defer mutex.Unlock() if err != nil { diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index da1c32496..93f11f95b 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -40,6 +40,8 @@ type transportKeyType int var transportKey transportKeyType type RegistryHelper struct { + ctx context.Context + authEntries []AuthEntry cachedTransports utils.ThreadSafeCache @@ -60,8 +62,10 @@ type AuthEntry struct { Insecure bool } -func NewRegistryHelper() *RegistryHelper { - return &RegistryHelper{} +func NewRegistryHelper(ctx context.Context) *RegistryHelper { + return &RegistryHelper{ + ctx: ctx, + } } func (rh *RegistryHelper) ListImageTags(image string) ([]string, error) { @@ -79,7 +83,7 @@ func (rh *RegistryHelper) ListImageTags(image string) ([]string, error) { remoteOpts := []remote.Option{ remote.WithAuthFromKeychain(rh), remote.WithTransport(rh), - remote.WithContext(context.WithValue(context.Background(), transportKey, t)), + remote.WithContext(context.WithValue(rh.ctx, transportKey, t)), } e := rh.findAuthEntry(repo.RegistryStr()) From 132b0833058848fc58edbeab7c781f6f5adf3778 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 7 May 2022 18:14:20 +0200 Subject: [PATCH 0125/2268] fix: Proper timeout/deadline handling in ApplyUtil --- cmd/kluctl/args/misc.go | 2 +- cmd/kluctl/args/project.go | 2 +- cmd/kluctl/commands/cmd_deploy.go | 2 +- pkg/deployment/commands/deploy.go | 4 +-- pkg/deployment/commands/diff.go | 2 +- pkg/deployment/commands/downscale.go | 2 +- pkg/deployment/commands/poke_images.go | 2 +- pkg/deployment/commands/validate.go | 2 +- pkg/deployment/utils/apply_utils.go | 47 +++++++++++++++++++------- 9 files changed, 44 insertions(+), 21 deletions(-) diff --git a/cmd/kluctl/args/misc.go b/cmd/kluctl/args/misc.go index c4972ca44..86ef05ae5 100644 --- a/cmd/kluctl/args/misc.go +++ b/cmd/kluctl/args/misc.go @@ -22,7 +22,7 @@ type ReplaceOnErrorFlags struct { } type HookFlags struct { - HookTimeout time.Duration `group:"misc" help:"Maximum time to wait for hook readiness. The timeout is meant per-hook. Timeouts are in the duration format (1s, 1m, 1h, ...). If not specified, a default timeout of 5m is used." default:"5m"` + ReadinessTimeout time.Duration `group:"misc" help:"Maximum time to wait for object readiness. The timeout is meant per-object. Timeouts are in the duration format (1s, 1m, 1h, ...). If not specified, a default timeout of 5m is used." default:"5m"` } type IgnoreFlags struct { diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 120e1fabd..ab9da298f 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -15,7 +15,7 @@ type ProjectFlags struct { OutputMetadata pathType `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` Cluster string `group:"project" help:"Specify/Override cluster"` - Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"5m"` + Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index c99d69319..a0db7c000 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -54,7 +54,7 @@ func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { cmd2.ReplaceOnError = cmd.ReplaceOnError cmd2.ForceReplaceOnError = cmd.ForceReplaceOnError cmd2.AbortOnError = cmd.AbortOnError - cmd2.HookTimeout = cmd.HookTimeout + cmd2.ReadinessTimeout = cmd.ReadinessTimeout cmd2.NoWait = cmd.NoWait cb := cmd.diffResultCb diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index d61461d70..575fb26a0 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -16,7 +16,7 @@ type DeployCommand struct { ReplaceOnError bool ForceReplaceOnError bool AbortOnError bool - HookTimeout time.Duration + ReadinessTimeout time.Duration NoWait bool } @@ -42,7 +42,7 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult ForceReplaceOnError: cmd.ForceReplaceOnError, DryRun: true, AbortOnError: false, - WaitObjectTimeout: cmd.HookTimeout, + ReadinessTimeout: cmd.ReadinessTimeout, NoWait: cmd.NoWait, } diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index 5dafbe374..0bdd5b9e1 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -40,7 +40,7 @@ func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.Comm ForceReplaceOnError: cmd.ForceReplaceOnError, DryRun: true, AbortOnError: false, - WaitObjectTimeout: 0, + ReadinessTimeout: 0, } au := utils.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, o) au.ApplyDeployments() diff --git a/pkg/deployment/commands/downscale.go b/pkg/deployment/commands/downscale.go index 3f0ee568d..ce51e6e35 100644 --- a/pkg/deployment/commands/downscale.go +++ b/pkg/deployment/commands/downscale.go @@ -40,7 +40,7 @@ func (cmd *DownscaleCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types if !d.CheckInclusionForDeploy() { continue } - au := ad.NewApplyUtil(utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0, true)) + au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0, true)) for _, o := range d.Objects { o := o ref := o.GetK8sRef() diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 9c8a50ad6..e2dc24b23 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -76,7 +76,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type wg.Add(1) go func() { defer wg.Done() - au := ad.NewApplyUtil(utils2.NewProgressCtx(nil, ref.String(), 0, true)) + au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(nil, ref.String(), 0, true)) au.ReplaceObject(ref, ru.GetRemoteObject(ref), func(o *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { return doPokeImage(containers, o) }) diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 8e35d5e54..11716e66d 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -40,7 +40,7 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types. if !d.CheckInclusionForDeploy() { continue } - au := ad.NewApplyUtil(utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0, true)) + au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0, true)) h := utils2.NewHooksUtil(au) for _, o := range d.Objects { hook := h.GetHook(o) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index ce8c0b31e..b14afa455 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -31,11 +31,13 @@ type ApplyUtilOptions struct { ForceReplaceOnError bool DryRun bool AbortOnError bool - WaitObjectTimeout time.Duration + ReadinessTimeout time.Duration NoWait bool } type ApplyUtil struct { + ctx context.Context + dew *DeploymentErrorsAndWarnings errorCount int warningCount int @@ -83,8 +85,9 @@ func NewApplyDeploymentsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnin return ret } -func (ad *ApplyDeploymentsUtil) NewApplyUtil(pctx *progressCtx) *ApplyUtil { +func (ad *ApplyDeploymentsUtil) NewApplyUtil(ctx context.Context, pctx *progressCtx) *ApplyUtil { ret := &ApplyUtil{ + ctx: ctx, dew: ad.dew, appliedObjects: map[k8s2.ObjectRef]*uo.UnstructuredObject{}, appliedHookObjects: map[k8s2.ObjectRef]*uo.UnstructuredObject{}, @@ -132,6 +135,10 @@ func (a *ApplyUtil) HandleError(ref k8s2.ObjectRef, err error) { a.mutex.Lock() defer a.mutex.Unlock() + if errors2.Is(err, context.DeadlineExceeded) || errors2.Is(err, context.Canceled) { + a.abortSignal.Store(true) + } + if a.o.AbortOnError && a.abortSignal != nil { a.abortSignal.Store(true) } @@ -356,8 +363,9 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo } if timeout == 0 { - timeout = a.o.WaitObjectTimeout + timeout = a.o.ReadinessTimeout } + timeoutTimer := time.NewTimer(timeout) a.pctx.Debugf("Waiting for %s to get ready", ref.String()) @@ -397,13 +405,6 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo return false } - if timeout > 0 && time.Now().Sub(startTime) >= timeout { - err := fmt.Errorf("timed out while waiting for %s", ref.String()) - a.pctx.Warningf("%s (%ds elapsed)", err.Error(), elapsed) - a.HandleError(ref, err) - return false - } - a.pctx.SetStatus(fmt.Sprintf("Waiting for %s to get ready...", ref.String())) if !didLog { @@ -415,7 +416,20 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo lastLogTime = time.Now() } - time.Sleep(500 * time.Millisecond) + select { + case <-time.After(500 * time.Millisecond): + continue + case <-timeoutTimer.C: + err := fmt.Errorf("timed out while waiting for readiness of %s", ref.String()) + a.pctx.Warningf("%s (%ds elapsed)", err.Error(), elapsed) + a.HandleError(ref, err) + return false + case <-a.ctx.Done(): + err := fmt.Errorf("failed waiting for readiness of %s: %w", ref.String(), err) + a.pctx.Warningf("%s (%ds elapsed)", err.Error(), elapsed) + a.HandleError(ref, err) + return false + } } return false } @@ -505,6 +519,9 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { a.WaitReadiness(o.GetK8sRef(), 0) } } + if a.abortSignal.Load().(bool) { + return + } h.RunHooks(postHooks) @@ -579,7 +596,7 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { } else { pctx = NewProgressCtx(nil, "", 0, false) } - a2 := a.NewApplyUtil(pctx) + a2 := a.NewApplyUtil(a.ctx, pctx) wg.Add(1) go func() { @@ -587,6 +604,7 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { defer sem.Release(1) a2.applyDeploymentItem(d) + pctx.Finish() }() barrier := (d.Config.Barrier != nil && *d.Config.Barrier) || d.Barrier @@ -622,6 +640,11 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { func (a *ApplyUtil) ReplaceObject(ref k8s2.ObjectRef, firstVersion *uo.UnstructuredObject, callback func(o *uo.UnstructuredObject) (*uo.UnstructuredObject, error)) { firstCall := true for true { + if a.ctx.Err() != nil { + a.HandleError(ref, fmt.Errorf("failed replacing %s: %w", ref.String(), a.ctx.Err())) + return + } + var remote *uo.UnstructuredObject if firstCall && firstVersion != nil { remote = firstVersion From 5b5d61e709aef30957e44329387db3f6e4e4ac6e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 8 May 2022 14:19:53 +0200 Subject: [PATCH 0126/2268] fix: Support int flags in buildCobraArg --- cmd/kluctl/commands/cobra_utils.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index 17ed89eee..77260d052 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -181,6 +181,16 @@ func (c *rootCommand) buildCobraArg(cg *commandAndGroups, f reflect.StructField, parsedDefault = x } cg.cmd.PersistentFlags().BoolVarP(v2.(*bool), name, shortFlag, parsedDefault, help) + case *int: + parsedDefault := 0 + if defaultValue != "" { + x, err := strconv.ParseInt(defaultValue, 0, 32) + if err != nil { + return err + } + parsedDefault = int(x) + } + cg.cmd.PersistentFlags().IntVarP(v2.(*int), name, shortFlag, parsedDefault, help) case *time.Duration: var parsedDefault time.Duration if defaultValue != "" { From 7c367052712dda93fe391aea25ac35bdf9c58375 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 8 May 2022 14:20:05 +0200 Subject: [PATCH 0127/2268] refactor: Remove unused code --- pkg/utils/uo/unstructured.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pkg/utils/uo/unstructured.go b/pkg/utils/uo/unstructured.go index 118b76df1..f0793e843 100644 --- a/pkg/utils/uo/unstructured.go +++ b/pkg/utils/uo/unstructured.go @@ -2,20 +2,8 @@ package uo import ( "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func CopyUnstructured(u *unstructured.Unstructured) *unstructured.Unstructured { - var ret unstructured.Unstructured - err := utils.DeepCopy(&ret.Object, &u.Object) - if err != nil { - log.Fatal(err) - } - return &ret -} - func MergeStrMap(a map[string]string, b map[string]string) { for k, v := range b { a[k] = v From 11cfc884212210df903a0fd8debb32d3e0de7d75 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 8 May 2022 14:19:32 +0200 Subject: [PATCH 0128/2268] refactor: Use custom status handling instead of logrus for user facing status --- .../commands/cmd_check_image_updates.go | 6 +- cmd/kluctl/commands/cmd_delete.go | 2 +- cmd/kluctl/commands/cmd_helm_pull.go | 12 +- cmd/kluctl/commands/cmd_helm_update.go | 30 +++-- cmd/kluctl/commands/cmd_prune.go | 2 +- cmd/kluctl/commands/cmd_render.go | 4 +- cmd/kluctl/commands/cmd_seal.go | 32 +++--- cmd/kluctl/commands/cmd_validate.go | 2 +- cmd/kluctl/commands/cobra_utils.go | 3 +- cmd/kluctl/commands/completion.go | 12 +- cmd/kluctl/commands/root.go | 36 +++--- cmd/kluctl/commands/utils.go | 7 +- pkg/deployment/commands/delete.go | 5 +- pkg/deployment/commands/deploy.go | 2 +- pkg/deployment/commands/diff.go | 2 +- pkg/deployment/commands/downscale.go | 4 +- pkg/deployment/commands/poke_images.go | 4 +- pkg/deployment/commands/prune.go | 5 +- pkg/deployment/commands/validate.go | 6 +- pkg/deployment/deployment_collection.go | 23 +++- pkg/deployment/deployment_item.go | 2 +- pkg/deployment/deployment_project.go | 16 ++- pkg/deployment/helm_chart.go | 15 +-- pkg/deployment/utils/apply_utils.go | 12 +- pkg/deployment/utils/progress.go | 24 ++-- pkg/deployment/utils/remote_objects_utils.go | 14 ++- pkg/diff/diff.go | 3 +- pkg/diff/managed_fields.go | 3 +- pkg/git/auth/auth_provider.go | 7 +- pkg/git/auth/env_auth_provider.go | 11 +- pkg/git/auth/git_credentials_file.go | 17 +-- pkg/git/auth/list_auth_provider.go | 7 +- pkg/git/auth/ssh_auth_provider.go | 45 ++++---- pkg/git/mirrored_repo.go | 27 ++--- pkg/jinja2/generate/main.go | 11 +- pkg/jinja2/source.go | 3 +- pkg/k8s/k8s_cluster.go | 14 +-- pkg/kluctl_project/project_load.go | 4 +- pkg/kluctl_project/target_context.go | 2 +- pkg/kluctl_project/targets.go | 6 +- pkg/python/embed.go | 7 +- pkg/registries/registries.go | 8 +- pkg/seal/bootstrap.go | 7 +- pkg/seal/fetch_cert.go | 9 +- pkg/seal/sealer.go | 23 ++-- pkg/status/status.go | 107 ++++++++++++++++++ pkg/status/status_handler.go | 67 +++++++++++ pkg/utils/env.go | 3 +- pkg/utils/prompts.go | 13 ++- pkg/utils/uo/jsonpath.go | 3 +- pkg/utils/uo/k8s_fields.go | 38 +++---- pkg/utils/uo/object_iterator.go | 8 +- pkg/utils/uo/uo.go | 5 +- pkg/utils/utils.go | 3 +- pkg/utils/versions/latest_version.go | 5 +- pkg/utils/versions/latest_version_parse.go | 3 +- 56 files changed, 488 insertions(+), 263 deletions(-) create mode 100644 pkg/status/status.go create mode 100644 pkg/status/status_handler.go diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index c8956e026..1da6883a3 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -3,9 +3,9 @@ package commands import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/registries" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/versions" - log "github.com/sirupsen/logrus" "os" "regexp" "sort" @@ -80,7 +80,7 @@ func runCheckImageUpdates(ctx *commandCtx) error { for _, image := range images { s := strings.SplitN(image, ":", 2) if len(s) == 1 { - log.Warningf("%s: Ignoring image %s as it doesn't specify a tag", ref.String(), image) + status.Warning(ctx.ctx, "%s: Ignoring image %s as it doesn't specify a tag", ref.String(), image) continue } repo := s[0] @@ -88,7 +88,7 @@ func runCheckImageUpdates(ctx *commandCtx) error { repoTags, _ := imageTags[repo].([]string) err, _ := imageTags[repo].(error) if err != nil { - log.Warningf("%s: Failed to list tags for %s. %v", ref.String(), repo, err) + status.Warning(ctx.ctx, "%s: Failed to list tags for %s. %v", ref.String(), repo, err) continue } diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index ebb02bfea..2cd7607fa 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -55,7 +55,7 @@ func (cmd *deleteCmd) Run() error { cmd2.OverrideDeleteByLabels = deleteByLabels - objects, err := cmd2.Run(ctx.targetCtx.K) + objects, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 38aec11c5..6d5d5b6fe 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -1,8 +1,9 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/pkg/deployment" - log "github.com/sirupsen/logrus" + "github.com/kluctl/kluctl/v2/pkg/status" "io/fs" "path/filepath" ) @@ -17,7 +18,7 @@ func (cmd *helmPullCmd) Help() string { pulling is only needed when really required (e.g. when the chart version changes).` } -func (cmd *helmPullCmd) Run() error { +func (cmd *helmPullCmd) Run(ctx context.Context) error { rootPath := "." if cmd.LocalDeployment != "" { rootPath = cmd.LocalDeployment @@ -25,15 +26,18 @@ func (cmd *helmPullCmd) Run() error { err := filepath.WalkDir(rootPath, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { - log.Infof("Pulling for %s", p) + s := status.Start(ctx, "Pulling for %s", p) chart, err := deployment.NewHelmChart(p) if err != nil { + s.FailedWithMessage(err.Error()) return err } - err = chart.Pull() + err = chart.Pull(ctx) if err != nil { + s.FailedWithMessage(err.Error()) return err } + s.Success() } return nil }) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index f74b41aaa..b9f9d8caf 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -1,11 +1,12 @@ package commands import ( + "context" "fmt" "github.com/go-git/go-git/v5" "github.com/kluctl/kluctl/v2/pkg/deployment" git2 "github.com/kluctl/kluctl/v2/pkg/git" - log "github.com/sirupsen/logrus" + "github.com/kluctl/kluctl/v2/pkg/status" "io/fs" "path/filepath" ) @@ -20,7 +21,7 @@ func (cmd *helmUpdateCmd) Help() string { return `Optionally performs the actual upgrade and/or add a commit to version control.` } -func (cmd *helmUpdateCmd) Run() error { +func (cmd *helmUpdateCmd) Run(ctx context.Context) error { rootPath := "." if cmd.LocalDeployment != "" { rootPath = cmd.LocalDeployment @@ -37,6 +38,11 @@ func (cmd *helmUpdateCmd) Run() error { if err != nil { return err } + + statusPrefix := filepath.Base(p) + s := status.Start(ctx, "%s: Checking for updates", statusPrefix) + defer s.Failed() + newVersion, updated, err := chart.CheckUpdate() if err != nil { return err @@ -44,11 +50,12 @@ func (cmd *helmUpdateCmd) Run() error { if !updated { return nil } - log.Infof("Chart %s has new version %s available. Old version is %s.", p, newVersion, *chart.Config.ChartVersion) + s.Update(fmt.Sprintf("Chart has new version %s available. Old version is %s.", newVersion, *chart.Config.ChartVersion)) if cmd.Upgrade { if chart.Config.SkipUpdate != nil && *chart.Config.SkipUpdate { - log.Infof("NOT upgrading chart %s as skipUpdate was set to true", p) + s.Update("%s: NOT upgrading chart as skipUpdate was set to true", statusPrefix) + s.Success() return nil } @@ -75,8 +82,10 @@ func (cmd *helmUpdateCmd) Run() error { return err } - log.Infof("Pulling for %s", p) - err = chart.Pull() + s.Update("%s: Pulling new version", statusPrefix) + defer s.Failed() + + err = chart.Pull(ctx) if err != nil { return err } @@ -93,8 +102,10 @@ func (cmd *helmUpdateCmd) Run() error { } if cmd.Commit { - msg := fmt.Sprintf("Updated helm chart %s from %s to %s", filepath.Dir(p), oldVersion, newVersion) - log.Infof("Committing: %s", msg) + commitMsg := fmt.Sprintf("Updated helm chart %s from %s to %s", filepath.Dir(p), oldVersion, newVersion) + + s.Update(fmt.Sprintf("%s: Updating chart from %s to %s", statusPrefix, oldVersion, newVersion)) + r, err := git.PlainOpen(gitRootPath) if err != nil { return err @@ -117,11 +128,12 @@ func (cmd *helmUpdateCmd) Run() error { return err } } - _, err = wt.Commit(msg, &git.CommitOptions{}) + _, err = wt.Commit(commitMsg, &git.CommitOptions{}) if err != nil { return err } } + s.Success() } } return nil diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index e24578871..602d3f3a2 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -43,7 +43,7 @@ func (cmd *pruneCmd) Run() error { func (cmd *pruneCmd) runCmdPrune(ctx *commandCtx) error { cmd2 := commands.NewPruneCommand(ctx.targetCtx.DeploymentCollection) - objects, err := cmd2.Run(ctx.targetCtx.K) + objects, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 849175838..c22b242af 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -2,8 +2,8 @@ package commands import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" "io/ioutil" ) @@ -37,7 +37,7 @@ func (cmd *renderCmd) Run() error { renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - log.Infof("Rendered into %s", ctx.targetCtx.DeploymentCollection.RenderDir) + status.Info(ctx.ctx, "Rendered into %s", ctx.targetCtx.DeploymentCollection.RenderDir) return nil }) } diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 5df4a45f1..b7a4c01f4 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -7,9 +7,9 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/seal" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - log "github.com/sirupsen/logrus" ) type sealCmd struct { @@ -63,7 +63,13 @@ func loadSecrets(ctx *commandCtx, target *types.Target, secretsLoader *seal.Secr } func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.LoadedKluctlProject, targetName string, secretsLoader *seal.SecretsLoader) error { - log.Infof("Sealing for target %s", targetName) + s := status.Start(ctx, "%s: Sealing for target", targetName) + defer s.FailedWithMessage("%s: Sealing failed", targetName) + + doFail := func(err error) error { + s.FailedWithMessage(fmt.Sprintf("Sealing failed: %v", err)) + return err + } ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, @@ -76,11 +82,11 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L return withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { err := loadSecrets(ctx, ctx.targetCtx.Target, secretsLoader) if err != nil { - return err + return doFail(err) } err = ctx.targetCtx.DeploymentCollection.RenderDeployments(ctx.targetCtx.K) if err != nil { - return err + return doFail(err) } sealedSecretsNamespace := "kube-system" @@ -94,28 +100,29 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L } } if p.Config.SecretsConfig == nil || p.Config.SecretsConfig.SealedSecrets == nil || p.Config.SecretsConfig.SealedSecrets.Bootstrap == nil || *p.Config.SecretsConfig.SealedSecrets.Bootstrap { - err = seal.BootstrapSealedSecrets(ctx.targetCtx.K, sealedSecretsNamespace) + err = seal.BootstrapSealedSecrets(ctx.ctx, ctx.targetCtx.K, sealedSecretsNamespace) if err != nil { - return err + return doFail(err) } } clusterConfig, err := p.LoadClusterConfig(ctx.targetCtx.Target.Cluster) if err != nil { - return err + return doFail(err) } - sealer, err := seal.NewSealer(ctx.targetCtx.K, sealedSecretsNamespace, sealedSecretsControllerName, clusterConfig.Cluster, cmd.ForceReseal) + sealer, err := seal.NewSealer(ctx.ctx, ctx.targetCtx.K, sealedSecretsNamespace, sealedSecretsControllerName, clusterConfig.Cluster, cmd.ForceReseal) if err != nil { - return err + return doFail(err) } cmd2 := commands.NewSealCommand(ctx.targetCtx.DeploymentCollection) err = cmd2.Run(sealer) if err != nil { - return err + return doFail(err) } - return err + s.Success() + return nil }) } @@ -135,7 +142,7 @@ func (cmd *sealCmd) Run() error { continue } if target.Target.SealingConfig == nil { - log.Infof("Target %s has no sealingConfig", target.Target.Name) + status.Info(ctx, "Target %s has no sealingConfig", target.Target.Name) continue } noTargetMatch = false @@ -158,7 +165,6 @@ func (cmd *sealCmd) Run() error { err := cmd.runCmdSealForTarget(ctx, p, sealTarget.Name, secretsLoader) if err != nil { - log.Warningf("Sealing for target %s failed: %v", sealTarget.Name, err) hadError = true } } diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index 6f59906e2..308f1abfb 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -37,7 +37,7 @@ func (cmd *validateCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { startTime := time.Now() - cmd2 := commands.NewValidateCommand(ctx.targetCtx.DeploymentCollection) + cmd2 := commands.NewValidateCommand(ctx.ctx, ctx.targetCtx.DeploymentCollection) for true { result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) if err != nil { diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index 77260d052..c45c33f14 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -3,7 +3,6 @@ package commands import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -302,7 +301,7 @@ func (c *rootCommand) buildGroupedFlagSets(cg *commandAndGroups) map[string]*pfl x.cmd.PersistentFlags().VisitAll(func(flag *pflag.Flag) { group, ok := x.groups[flag.Name] if !ok { - log.Panicf("group for %s not found", flag.Name) + panic(fmt.Sprintf("group for %s not found", flag.Name)) } fl, ok := flagsByGroups[group] if !ok { diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 99cfdd5b1..1e2b383a4 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -4,10 +4,10 @@ import ( "context" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "os" "path/filepath" @@ -77,7 +77,7 @@ func buildClusterCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra. return nil }) if err != nil { - log.Error(err) + status.Error(cliCtx, err.Error()) return nil, cobra.ShellCompDirectiveError } return ret, cobra.ShellCompDirectiveDefault @@ -94,7 +94,7 @@ func buildTargetCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.C return nil }) if err != nil { - log.Error(err) + status.Error(cliCtx, err.Error()) return nil, cobra.ShellCompDirectiveError } return ret, cobra.ShellCompDirectiveDefault @@ -165,7 +165,7 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd return nil }) if err != nil { - log.Error(err) + status.Error(cliCtx, err.Error()) return nil, cobra.ShellCompDirectiveError } if forDirs { @@ -206,7 +206,7 @@ func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, a _ = withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { err := ctx.targetCtx.DeploymentCollection.Prepare(nil) if err != nil { - log.Error(err) + status.Error(cliCtx, err.Error()) } mutex.Lock() @@ -233,7 +233,7 @@ func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, a return nil }) if err != nil { - log.Error(err) + status.Error(cliCtx, err.Error()) return nil, cobra.ShellCompDirectiveError } return images.ListKeys(), cobra.ShellCompDirectiveNoSpace diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index a4f1c35bc..44e4ef541 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -16,12 +16,14 @@ limitations under the License. package commands import ( + "context" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/version" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" "net/http" @@ -34,8 +36,8 @@ import ( const latestReleaseUrl = "https://api.github.com/repos/kluctl/kluctl/releases/latest" type cli struct { - Verbosity string `group:"global" short:"v" help:"Log level (debug, info, warn, error, fatal, panic)." default:"info"` - NoUpdateCheck bool `group:"global" help:"Disable update check on startup"` + Debug bool `group:"global" help:"Enable debug logging"` + NoUpdateCheck bool `group:"global" help:"Disable update check on startup"` Archive archiveCmd `cmd:"" help:"Write project and all related components into single tgz"` CheckImageUpdates checkImageUpdatesCmd `cmd:"" help:"Render deployment and check if any images have new tags available"` @@ -63,14 +65,12 @@ var flagGroups = []groupInfo{ {group: "inclusion", title: "Inclusion/Exclusion arguments:", description: "Control inclusion/exclusion."}, {group: "misc", title: "Misc arguments:", description: "Command specific arguments."}, } -var globalFlagGroup = &flagGroups[len(flagGroups)-1] -func (c *cli) setupLogs() error { - lvl, err := log.ParseLevel(c.Verbosity) - if err != nil { - return err - } - log.SetLevel(lvl) +var cliCtx = context.Background() + +func (c *cli) setupStatusHandler() error { + sh := status.NewMultiLineStatusHandler(os.Stderr, c.Debug) + cliCtx = status.NewContext(cliCtx, sh) return nil } @@ -98,7 +98,8 @@ func (c *cli) checkNewVersion() { versionCheckState.LastVersionCheck = time.Now() _ = yaml.WriteYamlFile(versionCheckPath, &versionCheckState) - log.Debugf("Checking for new kluctl version") + s := status.Start(cliCtx, "Checking for new kluctl version") + defer s.Failed() r, err := http.Get(latestReleaseUrl) if err != nil { @@ -122,12 +123,15 @@ func (c *cli) checkNewVersion() { latestVersion := versions.LooseVersion(latestVersionStr) localVersion := versions.LooseVersion(version.GetVersion()) if localVersion.Less(latestVersion, true) { - log.Warningf("You are using an outdated version (%v) of kluctl. You should update soon to version %v", localVersion, latestVersion) + s.Update(fmt.Sprintf("You are using an outdated version (%v) of kluctl. You should update soon to version %v", localVersion, latestVersion)) + } else { + s.Update("Your kluctl version is up-to-date") } + s.Success() } func (c *cli) preRun() error { - if err := c.setupLogs(); err != nil { + if err := c.setupStatusHandler(); err != nil { return err } c.checkNewVersion() @@ -141,7 +145,7 @@ func initViper() { viper.AddConfigPath("$HOME/.kluctl") if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - log.Error(err) + status.Error(cliCtx, err.Error()) os.Exit(1) } } @@ -160,7 +164,7 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif flagGroups) if err != nil { - log.Fatal(err) + panic(err) } rootCmd.Version = version.GetVersion() @@ -178,7 +182,7 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif err = rootCmd.Execute() if err != nil { - log.Errorf("%v", err) + status.Error(cliCtx, err.Error()) os.Exit(1) } } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index a599aad54..f0ee358a7 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -11,9 +11,9 @@ import ( "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/registries" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" "io/ioutil" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -51,7 +51,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b repoRoot, err := git.DetectGitRepositoryRoot(cwd) if err != nil && projectFlags.FromArchive == "" { - log.Warning("Failed to detect git project root. This might cause follow-up errors") + status.Warning(cliCtx, "", "Failed to detect git project root. This might cause follow-up errors") } loadArgs := kluctl_project.LoadKluctlProjectArgs{ @@ -70,8 +70,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b GitUpdateInterval: projectFlags.GitCacheUpdateInterval, } - ctx := context.Background() - ctx, cancel := context.WithTimeout(ctx, projectFlags.Timeout) + ctx, cancel := context.WithTimeout(cliCtx, projectFlags.Timeout) defer cancel() p, err := kluctl_project.LoadKluctlProject(ctx, loadArgs, tmpDir, j2) if err != nil { diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index 872e8a7ad..d794d355e 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -18,10 +19,10 @@ func NewDeleteCommand(c *deployment.DeploymentCollection) *DeleteCommand { } } -func (cmd *DeleteCommand) Run(k *k8s.K8sCluster) ([]k8s2.ObjectRef, error) { +func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.ObjectRef, error) { dew := utils2.NewDeploymentErrorsAndWarnings() - ru := utils2.NewRemoteObjectsUtil(dew) + ru := utils2.NewRemoteObjectsUtil(ctx, dew) var labels map[string]string if len(cmd.OverrideDeleteByLabels) != 0 { diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index 575fb26a0..0fd1823ca 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -29,7 +29,7 @@ func NewDeployCommand(c *deployment.DeploymentCollection) *DeployCommand { func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResultCb func(diffResult *types.CommandResult) error) (*types.CommandResult, error) { dew := utils2.NewDeploymentErrorsAndWarnings() - ru := utils2.NewRemoteObjectsUtil(dew) + ru := utils2.NewRemoteObjectsUtil(ctx, dew) err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs()) if err != nil { return nil, err diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index 0bdd5b9e1..dd811a84a 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -28,7 +28,7 @@ func NewDiffCommand(c *deployment.DeploymentCollection) *DiffCommand { func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.CommandResult, error) { dew := utils.NewDeploymentErrorsAndWarnings() - ru := utils.NewRemoteObjectsUtil(dew) + ru := utils.NewRemoteObjectsUtil(ctx, dew) err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs()) if err != nil { return nil, err diff --git a/pkg/deployment/commands/downscale.go b/pkg/deployment/commands/downscale.go index ce51e6e35..60cadd857 100644 --- a/pkg/deployment/commands/downscale.go +++ b/pkg/deployment/commands/downscale.go @@ -26,7 +26,7 @@ func (cmd *DownscaleCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types dew := utils2.NewDeploymentErrorsAndWarnings() - ru := utils2.NewRemoteObjectsUtil(dew) + ru := utils2.NewRemoteObjectsUtil(ctx, dew) err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs()) if err != nil { return nil, err @@ -40,7 +40,7 @@ func (cmd *DownscaleCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types if !d.CheckInclusionForDeploy() { continue } - au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0, true)) + au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(ctx, nil, d.RelToProjectItemDir, 0, true)) for _, o := range d.Objects { o := o ref := o.GetK8sRef() diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index e2dc24b23..c794e8a8a 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -27,7 +27,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type dew := utils2.NewDeploymentErrorsAndWarnings() - ru := utils2.NewRemoteObjectsUtil(dew) + ru := utils2.NewRemoteObjectsUtil(ctx, dew) err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs()) if err != nil { return nil, err @@ -76,7 +76,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type wg.Add(1) go func() { defer wg.Done() - au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(nil, ref.String(), 0, true)) + au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(ctx, nil, ref.String(), 0, true)) au.ReplaceObject(ref, ru.GetRemoteObject(ref), func(o *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { return doPokeImage(containers, o) }) diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index 873dc3a11..bc2336e74 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -17,10 +18,10 @@ func NewPruneCommand(c *deployment.DeploymentCollection) *PruneCommand { } } -func (cmd *PruneCommand) Run(k *k8s.K8sCluster) ([]k8s2.ObjectRef, error) { +func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.ObjectRef, error) { dew := utils2.NewDeploymentErrorsAndWarnings() - ru := utils2.NewRemoteObjectsUtil(dew) + ru := utils2.NewRemoteObjectsUtil(ctx, dew) err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), nil) if err != nil { return nil, err diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 11716e66d..fbe9102ec 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -16,12 +16,12 @@ type ValidateCommand struct { ru *utils2.RemoteObjectUtils } -func NewValidateCommand(c *deployment.DeploymentCollection) *ValidateCommand { +func NewValidateCommand(ctx context.Context, c *deployment.DeploymentCollection) *ValidateCommand { cmd := &ValidateCommand{ c: c, dew: utils2.NewDeploymentErrorsAndWarnings(), } - cmd.ru = utils2.NewRemoteObjectsUtil(cmd.dew) + cmd.ru = utils2.NewRemoteObjectsUtil(ctx, cmd.dew) return cmd } @@ -40,7 +40,7 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types. if !d.CheckInclusionForDeploy() { continue } - au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(nil, d.RelToProjectItemDir, 0, true)) + au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(ctx, nil, d.RelToProjectItemDir, 0, true)) h := utils2.NewHooksUtil(au) for _, o := range d.Objects { hook := h.GetHook(o) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 80ea9ac7e..9c26fa86e 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -4,10 +4,10 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" "golang.org/x/sync/semaphore" "path/filepath" "sync" @@ -52,7 +52,7 @@ func (c *DeploymentCollection) createBarrierDummy(project *DeploymentProject) *D } di, err := NewDeploymentItem(project, c, tmpDiConfig, nil, 0) if err != nil { - log.Fatal(err) + panic(err) } return di } @@ -67,7 +67,7 @@ func findDeploymentItemIndex(project *DeploymentProject, pth *string, indexes ma absDir, err := filepath.Abs(dir) if err != nil { // we pre-checked directories, so this should not happen - log.Fatal(err) + panic(err) } if _, ok := indexes[absDir]; !ok { @@ -86,7 +86,7 @@ func (c *DeploymentCollection) collectDeployments(project *DeploymentProject, in if diConfig.Include != nil { includedProject, ok := project.includes[i] if !ok { - log.Fatalf("Did not find find index %d in project.includes", i) + panic(fmt.Sprintf("Did not find find index %d in project.includes", i)) } ret2, err := c.collectDeployments(includedProject, indexes) if err != nil { @@ -110,7 +110,8 @@ func (c *DeploymentCollection) collectDeployments(project *DeploymentProject, in } func (c *DeploymentCollection) RenderDeployments(k *k8s.K8sCluster) error { - log.Infof("Rendering templates and Helm charts") + s := status.Start(c.ctx, "Rendering templates") + defer s.Failed() wp := utils.NewDebuggerAwareWorkerPool(16) defer wp.StopWait(false) @@ -125,6 +126,10 @@ func (c *DeploymentCollection) RenderDeployments(k *k8s.K8sCluster) error { if err != nil { return err } + s.Success() + + s = status.Start(c.ctx, "Rendering Helm Charts") + defer s.Failed() for _, d := range c.Deployments { err := d.renderHelmCharts(k, wp) @@ -137,6 +142,7 @@ func (c *DeploymentCollection) RenderDeployments(k *k8s.K8sCluster) error { return err } + s.Success() return nil } @@ -155,7 +161,8 @@ func (c *DeploymentCollection) resolveSealedSecrets() error { } func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { - log.Infof("Building kustomize objects") + s := status.Start(c.ctx, "Building kustomize objects") + defer s.Failed() var wg sync.WaitGroup var errs []error @@ -193,6 +200,10 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { } wg.Wait() + if len(errs) == 0 { + s.Success() + } + return utils.NewErrorListOrNil(errs) } diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index acb5e7aa3..1904a8842 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -176,7 +176,7 @@ func (di *DeploymentItem) renderHelmCharts(k *k8s.K8sCluster, wp *utils.WorkerPo if err != nil { return err } - return chart.Render(k) + return chart.Render(di.Project.ctx, k) }) return nil }) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index fe093ab20..f0c0cedd3 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -1,14 +1,15 @@ package deployment import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" "path/filepath" "reflect" "strings" @@ -17,6 +18,8 @@ import ( var warnOnce utils.OnceByKey type DeploymentProject struct { + ctx context.Context + VarsCtx *jinja2.VarsCtx dir string SealedSecretsDir string @@ -29,8 +32,9 @@ type DeploymentProject struct { parentProjectInclude *types.DeploymentItemConfig } -func NewDeploymentProject(k *k8s.K8sCluster, varsCtx *jinja2.VarsCtx, dir string, sealedSecretsDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { +func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsCtx *jinja2.VarsCtx, dir string, sealedSecretsDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { dp := &DeploymentProject{ + ctx: ctx, VarsCtx: varsCtx.Copy(), dir: dir, SealedSecretsDir: sealedSecretsDir, @@ -86,14 +90,14 @@ func (p *DeploymentProject) loadConfig(k *k8s.K8sCluster) error { } if len(p.Config.KustomizeDirs) != 0 { warnOnce.Do("kustomizeDirs", func() { - log.Warningf("'kustomizeDirs' is deprecated, use 'deployments' instead") + status.Warning(p.ctx, "'kustomizeDirs' is deprecated, use 'deployments' instead") }) p.Config.Deployments = p.Config.KustomizeDirs p.Config.KustomizeDirs = nil } if len(p.Config.Includes) != 0 { warnOnce.Do("includes", func() { - log.Warningf("'includes' is deprecated, use 'deployments' instead") + status.Warning(p.ctx, "'includes' is deprecated, use 'deployments' instead") }) for _, inc := range p.Config.Includes { c := *inc @@ -127,7 +131,7 @@ func (p *DeploymentProject) loadConfig(k *k8s.K8sCluster) error { } if len(p.Config.DeleteByLabels) != 0 { warnOnce.Do("deleteByLabels", func() { - log.Warningf("'deleteByLabels' is deprecated and ignored from now on") + status.Warning(p.ctx, "'deleteByLabels' is deprecated and ignored from now on") }) if !reflect.DeepEqual(p.Config.CommonLabels, p.Config.DeleteByLabels) { return fmt.Errorf("commonLabels and deleteByLabels do not match") @@ -196,7 +200,7 @@ func (p *DeploymentProject) loadIncludes(k *k8s.K8sCluster) error { return err } - newProject, err := NewDeploymentProject(k, varsCtx, incDir, p.SealedSecretsDir, p) + newProject, err := NewDeploymentProject(p.ctx, k, varsCtx, incDir, p.SealedSecretsDir, p) if err != nil { return err } diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 8c719c2a2..eb97d66b1 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -1,15 +1,16 @@ package deployment import ( + "context" "fmt" securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/pkg/errors" - log "github.com/sirupsen/logrus" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" @@ -88,7 +89,7 @@ func (c *helmChart) buildHelmConfig() (*action.Configuration, error) { }, nil } -func (c *helmChart) Pull() error { +func (c *helmChart) Pull(ctx context.Context) error { chartName, err := c.GetChartName() if err != nil { return err @@ -123,7 +124,7 @@ func (c *helmChart) Pull() error { _ = os.RemoveAll(chartDir + fmt.Sprintf("-%s.tar.gz", a.Version)) _ = os.RemoveAll(chartDir + fmt.Sprintf("-%s.tgz", a.Version)) if out != "" { - log.Info(out) + status.PlainText(ctx, out) } if err != nil { return err @@ -177,19 +178,19 @@ func (c *helmChart) CheckUpdate() (string, bool, error) { return latestVersion, updated, nil } -func (c *helmChart) Render(k *k8s.K8sCluster) error { +func (c *helmChart) Render(ctx context.Context, k *k8s.K8sCluster) error { chartName, err := c.GetChartName() if err != nil { return err } - err = c.doRender(k) + err = c.doRender(ctx, k) if err != nil { return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", chartName, c.Config.ReleaseName, err) } return nil } -func (c *helmChart) doRender(k *k8s.K8sCluster) error { +func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { chartDir, err := c.GetChartDir() if err != nil { return err @@ -264,7 +265,7 @@ func (c *helmChart) doRender(k *k8s.K8sCluster) error { } if chartRequested.Metadata.Deprecated { - log.Warningf("Chart %s is deprecated", *c.Config.ChartName) + status.Warning(ctx, "Chart %s is deprecated", *c.Config.ChartName) } rel, err := client.Run(chartRequested, vals) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index b14afa455..b8c78acbb 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -7,12 +7,12 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/diff" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" - log "github.com/sirupsen/logrus" "github.com/vbauerster/mpb/v7" "golang.org/x/sync/semaphore" "k8s.io/apimachinery/pkg/api/errors" @@ -561,7 +561,7 @@ func (a *ApplyDeploymentsUtil) buildProgressName(d *deployment.DeploymentItem) * } func (a *ApplyDeploymentsUtil) ApplyDeployments() { - log.Infof("Running server-side apply for all objects") + status.Info(a.ctx, "Running server-side apply for all objects") var wg sync.WaitGroup sem := semaphore.NewWeighted(8) @@ -592,9 +592,9 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { progressName := a.buildProgressName(d) var pctx *progressCtx if progressName != nil { - pctx = NewProgressCtx(p, *progressName, maxNameLen, true) + pctx = NewProgressCtx(a.ctx, p, *progressName, maxNameLen, true) } else { - pctx = NewProgressCtx(nil, "", 0, false) + pctx = NewProgressCtx(a.ctx, nil, "", 0, false) } a2 := a.NewApplyUtil(a.ctx, pctx) @@ -609,7 +609,7 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { barrier := (d.Config.Barrier != nil && *d.Config.Barrier) || d.Barrier if barrier { - bpctx := NewProgressCtx(p, "", maxNameLen, true) + bpctx := NewProgressCtx(a.ctx, p, "", maxNameLen, true) bpctx.SetTotal(1) bpctx.InfofAndStatus("Waiting on barrier...") @@ -678,7 +678,7 @@ func (a *ApplyUtil) ReplaceObject(ref k8s2.ObjectRef, firstVersion *uo.Unstructu a.dew.AddApiWarnings(ref, apiWarnings) if err != nil { if errors.IsConflict(err) { - log.Debugf("Conflict while patching %s. Retrying...", ref.String()) + status.Trace(a.ctx, "Conflict while patching %s. Retrying...", ref.String()) continue } else { a.HandleError(ref, err) diff --git a/pkg/deployment/utils/progress.go b/pkg/deployment/utils/progress.go index c5eadbbfc..34d03a61c 100644 --- a/pkg/deployment/utils/progress.go +++ b/pkg/deployment/utils/progress.go @@ -1,9 +1,10 @@ package utils import ( + "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/mattn/go-isatty" - log "github.com/sirupsen/logrus" "github.com/vbauerster/mpb/v7" "github.com/vbauerster/mpb/v7/decor" "math" @@ -14,6 +15,7 @@ import ( ) type progressCtx struct { + ctx context.Context bar *mpb.Bar doLog bool total int64 @@ -23,8 +25,9 @@ type progressCtx struct { mutex sync.Mutex } -func NewProgressCtx(p *mpb.Progress, name string, maxNameWidth int, doLog bool) *progressCtx { +func NewProgressCtx(ctx context.Context, p *mpb.Progress, name string, maxNameWidth int, doLog bool) *progressCtx { pctx := &progressCtx{ + ctx: ctx, status: "Initializing...", total: -1, name: name, @@ -52,23 +55,22 @@ func NewProgressCtx(p *mpb.Progress, name string, maxNameWidth int, doLog bool) return pctx } -func (ctx *progressCtx) Logf(level log.Level, s string, args ...interface{}) { +func (ctx *progressCtx) Infof(s string, args ...interface{}) { if ctx.doLog { - s = fmt.Sprintf("%s: %s", ctx.name, s) - log.StandardLogger().Logf(level, s, args...) + status.Info(ctx.ctx, s, args...) } } -func (ctx *progressCtx) Infof(s string, args ...interface{}) { - ctx.Logf(log.InfoLevel, s, args...) -} - func (ctx *progressCtx) Warningf(s string, args ...interface{}) { - ctx.Logf(log.WarnLevel, s, args...) + if ctx.doLog { + status.Warning(ctx.ctx, s, args...) + } } func (ctx *progressCtx) Debugf(s string, args ...interface{}) { - ctx.Logf(log.DebugLevel, s, args...) + if ctx.doLog { + status.Trace(ctx.ctx, s, args...) + } } func (ctx *progressCtx) InfofAndStatus(s string, args ...interface{}) { diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index 71df19fbe..65ddf867e 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -1,24 +1,27 @@ package utils import ( + "context" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime/schema" "sync" ) type RemoteObjectUtils struct { + ctx context.Context dew *DeploymentErrorsAndWarnings remoteObjects map[k8s2.ObjectRef]*uo.UnstructuredObject remoteNamespaces map[string]*uo.UnstructuredObject mutex sync.Mutex } -func NewRemoteObjectsUtil(dew *DeploymentErrorsAndWarnings) *RemoteObjectUtils { +func NewRemoteObjectsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnings) *RemoteObjectUtils { return &RemoteObjectUtils{ + ctx: ctx, dew: dew, remoteObjects: map[k8s2.ObjectRef]*uo.UnstructuredObject{}, remoteNamespaces: map[string]*uo.UnstructuredObject{}, @@ -30,7 +33,8 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st return nil } - log.Infof("Getting remote objects by commonLabels") + status.Info(u.ctx, "Getting remote objects by commonLabels") + allObjects, apiWarnings, err := k.ListAllObjects([]string{"get"}, "", labels, false) for gvk, aw := range apiWarnings { u.dew.AddApiWarnings(k8s2.ObjectRef{GVK: gvk}, aw) @@ -57,7 +61,7 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st u.mutex.Unlock() if len(notFoundRefsList) != 0 { - log.Infof("Getting %d additional remote objects", len(notFoundRefsList)) + status.Info(u.ctx, "Getting %d additional remote objects", len(notFoundRefsList)) r, apiWarnings, err := k.GetObjectsByRefs(notFoundRefsList) for ref, aw := range apiWarnings { u.dew.AddApiWarnings(ref, aw) @@ -72,7 +76,7 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st u.mutex.Unlock() } - log.Infof("Getting namespaces") + status.Info(u.ctx, "Getting namespaces") r, _, err := k.ListObjects(schema.GroupVersionKind{ Group: "", Version: "v1", diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index e490578a5..99eb13188 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -9,7 +9,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" diff2 "github.com/r3labs/diff/v2" - "log" "reflect" "sort" "strconv" @@ -125,7 +124,7 @@ func stableSortChanges(changes []types.Change) { for i, _ := range changes { y, err := yaml.WriteYamlString(changes[i]) if err != nil { - log.Panic(err) + panic(err) } changesStrs[i] = y changesIndexes[i] = i diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index 5ac3f6261..3a0772165 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - log "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" "regexp" @@ -192,7 +191,7 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr } remoteValue, found, err := remote.GetNestedField(p...) if !found { - log.Fatalf("field '%s' not found in remote object...which can't be!", cause.Field) + panic(fmt.Sprintf("field '%s' not found in remote object...which can't be!", cause.Field)) } overwrite := true diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 677f4df07..2a3f91757 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -1,6 +1,7 @@ package auth import ( + "context" "github.com/go-git/go-git/v5/plumbing/transport" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ) @@ -11,7 +12,7 @@ type AuthMethodAndCA struct { } type GitAuthProvider interface { - BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA + BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA } type GitAuthProviders struct { @@ -26,9 +27,9 @@ func (a *GitAuthProviders) RegisterAuthProvider(p GitAuthProvider, last bool) { } } -func (a *GitAuthProviders) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *GitAuthProviders) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { for _, p := range a.authProviders { - auth := p.BuildAuth(gitUrl) + auth := p.BuildAuth(ctx, gitUrl) if auth.AuthMethod != nil { return auth } diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index a57fa44f3..f05516e00 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -1,16 +1,17 @@ package auth import ( + "context" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" "io/ioutil" ) type GitEnvAuthProvider struct { } -func (a *GitEnvAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { var la ListAuthProvider for _, m := range utils.ParseEnvConfigSets("KLUCTL_GIT") { @@ -26,7 +27,7 @@ func (a *GitEnvAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { ssh_key_path = utils.ExpandPath(ssh_key_path) b, err := ioutil.ReadFile(ssh_key_path) if err != nil { - log.Debugf("Failed to read key %s: %v", ssh_key_path, err) + status.Trace(ctx, "Failed to read key %s: %v", ssh_key_path, err) } else { e.SshKey = b } @@ -36,12 +37,12 @@ func (a *GitEnvAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { ca_bundle_path = utils.ExpandPath(ca_bundle_path) b, err := ioutil.ReadFile(ca_bundle_path) if err != nil { - log.Debugf("Failed to read ca bundle %s: %v", ca_bundle_path, err) + status.Trace(ctx, "Failed to read ca bundle %s: %v", ca_bundle_path, err) } else { e.CABundle = b } } la.AddEntry(e) } - return la.BuildAuth(gitUrl) + return la.BuildAuth(ctx, gitUrl) } diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index b3a3b9f97..7ee6775c3 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -2,10 +2,11 @@ package auth import ( "bufio" + "context" "github.com/go-git/go-git/v5/plumbing/transport/http" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" giturls "github.com/whilp/git-urls" "os" "path/filepath" @@ -14,28 +15,28 @@ import ( type GitCredentialsFileAuthProvider struct { } -func (a *GitCredentialsFileAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { if gitUrl.Scheme != "http" && gitUrl.Scheme != "https" { return AuthMethodAndCA{} } home, err := os.UserHomeDir() if err != nil { - log.Warningf("Could not determine home directory: %v", err) + status.Warning(ctx, "Could not determine home directory: %v", err) return AuthMethodAndCA{} } - auth := a.tryBuildAuth(gitUrl, filepath.Join(home, ".git-credentials")) + auth := a.tryBuildAuth(ctx, gitUrl, filepath.Join(home, ".git-credentials")) if auth != nil { return *auth } if xdgHome, ok := os.LookupEnv("XDG_CONFIG_HOME"); ok && xdgHome != "" { - auth = a.tryBuildAuth(gitUrl, filepath.Join(xdgHome, ".git-credentials")) + auth = a.tryBuildAuth(ctx, gitUrl, filepath.Join(xdgHome, ".git-credentials")) if auth != nil { return *auth } } else { - auth = a.tryBuildAuth(gitUrl, filepath.Join(home, ".config/.git-credentials")) + auth = a.tryBuildAuth(ctx, gitUrl, filepath.Join(home, ".config/.git-credentials")) if auth != nil { return *auth } @@ -43,14 +44,14 @@ func (a *GitCredentialsFileAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMe return AuthMethodAndCA{} } -func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl git_url.GitUrl, gitCredentialsPath string) *AuthMethodAndCA { +func (a *GitCredentialsFileAuthProvider) tryBuildAuth(ctx context.Context, gitUrl git_url.GitUrl, gitCredentialsPath string) *AuthMethodAndCA { if !utils.IsFile(gitCredentialsPath) { return nil } f, err := os.Open(gitCredentialsPath) if err != nil { - log.Warningf("Failed to open %s: %v", gitCredentialsPath, err) + status.Warning(ctx, "Failed to open %s: %v", gitCredentialsPath, err) return nil } defer f.Close() diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index b929675af..241a8d5b2 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -1,10 +1,11 @@ package auth import ( + "context" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/plumbing/transport/ssh" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - log "github.com/sirupsen/logrus" + "github.com/kluctl/kluctl/v2/pkg/status" "strings" ) @@ -28,7 +29,7 @@ func (a *ListAuthProvider) AddEntry(e AuthEntry) { a.entries = append(a.entries, e) } -func (a *ListAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { for _, e := range a.entries { if e.Host != "*" && e.Host != gitUrl.Hostname() { continue @@ -64,7 +65,7 @@ func (a *ListAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { } a, err := ssh.NewPublicKeys(username, e.SshKey, "") if err != nil { - log.Debugf("Failed to parse private key: %v", err) + status.Trace(ctx, "Failed to parse private key: %v", err) } else { a.HostKeyCallback = buildVerifyHostCallback(e.KnownHosts) return AuthMethodAndCA{ diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index b37d242f1..686e90b47 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -1,11 +1,12 @@ package auth import ( + "context" "fmt" "github.com/kevinburke/ssh_config" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" sshagent "github.com/xanzy/ssh-agent" "golang.org/x/crypto/ssh" "io" @@ -22,6 +23,7 @@ type GitSshAuthProvider struct { } type sshDefaultIdentityAndAgent struct { + ctx context.Context authProvider *GitSshAuthProvider hostname string user string @@ -50,54 +52,54 @@ func (a *sshDefaultIdentityAndAgent) Signers() ([]ssh.Signer, error) { } func (a *sshDefaultIdentityAndAgent) addDefaultIdentity(gitUrl git_url.GitUrl) { - log.Debugf("trying to add default identity") + status.Trace(a.ctx, "trying to add default identity") u, err := user.Current() if err != nil { - log.Debugf("No current user: %v", err) + status.Trace(a.ctx, "No current user: %v", err) } else { path := filepath.Join(u.HomeDir, ".ssh", "id_rsa") - signer, err := a.authProvider.readKey(path) + signer, err := a.authProvider.readKey(a.ctx, path) if err != nil && !os.IsNotExist(err) { - log.Warningf("Failed to read default identity file for url %s: %v", gitUrl.String(), err) + status.Warning(a.ctx, "Failed to read default identity file for url %s: %v", gitUrl.String(), err) } else if signer != nil { - log.Debugf("...added '%s' as default identity", path) + status.Trace(a.ctx, "...added '%s' as default identity", path) a.signers = append(a.signers, signer) } } } func (a *sshDefaultIdentityAndAgent) addConfigIdentities(gitUrl git_url.GitUrl) { - log.Debugf("trying to add identities from ssh config") + status.Trace(a.ctx, "trying to add identities from ssh config") for _, id := range ssh_config.GetAll(gitUrl.Hostname(), "IdentityFile") { expanded := utils.ExpandPath(id) - log.Debugf("...trying '%s' (expanded: '%s')", id, expanded) - signer, err := a.authProvider.readKey(expanded) + status.Trace(a.ctx, "...trying '%s' (expanded: '%s')", id, expanded) + signer, err := a.authProvider.readKey(a.ctx, expanded) if err != nil && !os.IsNotExist(err) { - log.Warningf("Failed to read key %s for url %s: %v", id, gitUrl.String(), err) + status.Warning(a.ctx, "Failed to read key %s for url %s: %v", id, gitUrl.String(), err) } else if err == nil { - log.Debugf("...added '%s' from ssh config", expanded) + status.Trace(a.ctx, "...added '%s' from ssh config", expanded) a.signers = append(a.signers, signer) } } } func (a *sshDefaultIdentityAndAgent) addAgentIdentities(gitUrl git_url.GitUrl) { - log.Debugf("trying to add agent keys") + status.Trace(a.ctx, "trying to add agent keys") agent, _, err := sshagent.New() if err != nil { - log.Warningf("Failed to connect to ssh agent for url %s: %v", gitUrl.String(), err) + status.Warning(a.ctx, "Failed to connect to ssh agent for url %s: %v", gitUrl.String(), err) } else { signers, err := agent.Signers() if err != nil { - log.Warningf("Failed to get signers from ssh agent for url %s: %v", gitUrl.String(), err) + status.Warning(a.ctx, "Failed to get signers from ssh agent for url %s: %v", gitUrl.String(), err) return } - log.Debugf("...added %d agent keys", len(signers)) + status.Trace(a.ctx, "...added %d agent keys", len(signers)) a.signers = append(a.signers, signers...) } } -func (a *GitSshAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *GitSshAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { if !gitUrl.IsSsh() { return AuthMethodAndCA{} } @@ -106,6 +108,7 @@ func (a *GitSshAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { } auth := &sshDefaultIdentityAndAgent{ + ctx: ctx, authProvider: a, hostname: gitUrl.Hostname(), user: gitUrl.User.Username(), @@ -124,6 +127,7 @@ func (a *GitSshAuthProvider) BuildAuth(gitUrl git_url.GitUrl) AuthMethodAndCA { // we defer asking for passphrase so that we don't unnecessarily ask for passphrases for keys that are already provided // by ssh-agent type deferredPassphraseKey struct { + ctx context.Context a *GitSshAuthProvider path string err error @@ -157,21 +161,21 @@ func (k *deferredPassphraseKey) parse() { passphrase, err := k.getPassphrase() if err != nil { k.err = err - log.Warningf("Failed to parse key %s: %v", k.path, err) + status.Warning(k.ctx, "Failed to parse key %s: %v", k.path, err) return } pemBytes, err := ioutil.ReadFile(k.path) if err != nil { k.err = err - log.Warningf("Failed to parse key %s: %v", k.path, err) + status.Warning(k.ctx, "Failed to parse key %s: %v", k.path, err) return } s, err := ssh.ParsePrivateKeyWithPassphrase(pemBytes, passphrase) if err != nil { k.err = err - log.Warningf("Failed to parse key %s: %v", k.path, err) + status.Warning(k.ctx, "Failed to parse key %s: %v", k.path, err) return } k.parsed = s @@ -223,7 +227,7 @@ func (k *deferredPassphraseKey) Sign(rand io.Reader, data []byte) (*ssh.Signatur return k.parsed.Sign(rand, data) } -func (a *GitSshAuthProvider) readKey(path string) (ssh.Signer, error) { +func (a *GitSshAuthProvider) readKey(ctx context.Context, path string) (ssh.Signer, error) { pemBytes, err := ioutil.ReadFile(path) if err != nil { return nil, err @@ -235,6 +239,7 @@ func (a *GitSshAuthProvider) readKey(path string) (ssh.Signer, error) { if _, ok := err.(*ssh.PassphraseMissingError); ok { signer = &deferredPassphraseKey{ + ctx: ctx, a: a, path: path, } diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 8940c6047..2eeb19bfd 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -8,8 +8,8 @@ import ( "github.com/go-git/go-git/v5/plumbing" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" - log "github.com/sirupsen/logrus" "io/ioutil" "os" "path/filepath" @@ -70,7 +70,7 @@ func (g *MirroredGitRepo) Lock() error { func (g *MirroredGitRepo) Unlock() error { err := g.fileLock.Unlock() if err != nil { - log.Warningf("Unlock of %s failed: %v", g.fileLock.Path(), err) + status.Warning(g.ctx, "Unlock of %s failed: %v", g.fileLock.Path(), err) return err } return nil @@ -170,14 +170,13 @@ func (g *MirroredGitRepo) cleanupMirrorDir() error { return nil } -func (g *MirroredGitRepo) update(repoDir string, authProviders *auth2.GitAuthProviders) error { - log.Infof("Updating mirror repo: url='%v'", g.url.String()) +func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string, authProviders *auth2.GitAuthProviders) error { r, err := git.PlainOpen(repoDir) if err != nil { return err } - auth := authProviders.BuildAuth(g.url) + auth := authProviders.BuildAuth(g.ctx, g.url) remote, err := r.Remote("origin") if err != nil { @@ -260,21 +259,20 @@ func (g *MirroredGitRepo) update(repoDir string, authProviders *auth2.GitAuthPro _ = ioutil.WriteFile(filepath.Join(g.mirrorDir, ".update-time"), []byte(time.Now().Format(time.RFC3339Nano)), 0644) + s.Success() return nil } -func (g *MirroredGitRepo) cloneOrUpdate(authProviders *auth2.GitAuthProviders) error { +func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext, authProviders *auth2.GitAuthProviders) error { initMarker := filepath.Join(g.mirrorDir, ".cache2.init") if utils.IsFile(initMarker) { - return g.update(g.mirrorDir, authProviders) + return g.update(s, g.mirrorDir, authProviders) } err := g.cleanupMirrorDir() if err != nil { return err } - log.Infof("Cloning mirror repo at %v", g.mirrorDir) - tmpMirrorDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "mirror-") if err != nil { return err @@ -298,7 +296,7 @@ func (g *MirroredGitRepo) cloneOrUpdate(authProviders *auth2.GitAuthProviders) e return err } - err = g.update(tmpMirrorDir, authProviders) + err = g.update(s, tmpMirrorDir, authProviders) if err != nil { return err } @@ -321,20 +319,23 @@ func (g *MirroredGitRepo) cloneOrUpdate(authProviders *auth2.GitAuthProviders) e } func (g *MirroredGitRepo) Update(authProviders *auth2.GitAuthProviders) error { - err := g.cloneOrUpdate(authProviders) + s := status.Start(g.ctx, "Updating git cache for %s", g.url.String()) + err := g.cloneOrUpdate(s, authProviders) if err != nil { + s.FailedWithMessage(err.Error()) return err } g.hasUpdated = true + s.Success() return nil } func (g *MirroredGitRepo) CloneProject(ref string, targetDir string) error { if !g.fileLock.Locked() || !g.hasUpdated { - log.Fatalf("tried to clone from a project that is not locked/updated") + panic("tried to clone from a project that is not locked/updated") } - log.Debugf("Cloning git project: url='%s', ref='%s', target='%s'", g.url.String(), ref, targetDir) + status.Trace(g.ctx, "Cloning git project: url='%s', ref='%s', target='%s'", g.url.String(), ref, targetDir) err := PoorMansClone(g.mirrorDir, targetDir, ref) if err != nil { diff --git a/pkg/jinja2/generate/main.go b/pkg/jinja2/generate/main.go index a767884b3..9da3d4f4e 100644 --- a/pkg/jinja2/generate/main.go +++ b/pkg/jinja2/generate/main.go @@ -2,7 +2,6 @@ package main import ( "github.com/kluctl/kluctl/v2/pkg/utils/embed_util/packer" - log "github.com/sirupsen/logrus" "os" "os/exec" "path/filepath" @@ -23,12 +22,12 @@ func pipWheel() { cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { - log.Panic(err) + panic(err) } wheels, err := os.ReadDir("python_src/wheel") if err != nil { - log.Panic(err) + panic(err) } for _, w := range wheels { if !strings.HasSuffix(w.Name(), ".whl") { @@ -40,17 +39,17 @@ func pipWheel() { err = cmd.Run() if err != nil { - log.Panic(err) + panic(err) } err = os.Remove(filepath.Join("python_src/wheel", w.Name())) if err != nil { - log.Panic(err) + panic(err) } } err = packer.Pack("embed/python_src.tar.gz", "python_src", "*") if err != nil { - log.Panic(err) + panic(err) } } diff --git a/pkg/jinja2/source.go b/pkg/jinja2/source.go index f07623b74..d9ab49bb4 100644 --- a/pkg/jinja2/source.go +++ b/pkg/jinja2/source.go @@ -4,7 +4,6 @@ import ( "embed" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/embed_util" - log "github.com/sirupsen/logrus" "path/filepath" ) @@ -17,7 +16,7 @@ var pythonSrcExtracted string func init() { srcDir, err := extractSource() if err != nil { - log.Panic(err) + panic(err) } pythonSrcExtracted = srcDir } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index ea633652a..b6f4939b6 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -5,11 +5,11 @@ import ( "encoding/json" "fmt" goversion "github.com/hashicorp/go-version" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" "io" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -531,11 +531,11 @@ func (k *K8sCluster) ListObjectsMetadata(gvk schema.GroupVersionKind, namespace for _, o := range x.Items { b, err := json.Marshal(o) if err != nil { - log.Panic(err) + panic(err) } u, err := uo.FromString(string(b)) if err != nil { - log.Panic(err) + panic(err) } u.SetK8sGVK(gvk) result = append(result, u) @@ -805,7 +805,6 @@ type PatchOptions struct { func (k *K8sCluster) PatchObject(o *uo.UnstructuredObject, options PatchOptions) (*uo.UnstructuredObject, []ApiWarning, error) { dryRun := k.DryRun || options.ForceDryRun ref := o.GetK8sRef() - log2 := log.WithField("ref", ref) data, err := yaml.WriteYamlBytes(o) if err != nil { @@ -822,7 +821,8 @@ func (k *K8sCluster) PatchObject(o *uo.UnstructuredObject, options PatchOptions) po.Force = &options.ForceApply } - log2.Debugf("patching") + status.Trace(k.ctx, "patching %s", ref.String()) + var result *uo.UnstructuredObject apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { x, err := r.Patch(k.ctx, ref.Name, types.ApplyPatchType, data, po) @@ -842,7 +842,6 @@ type UpdateOptions struct { func (k *K8sCluster) UpdateObject(o *uo.UnstructuredObject, options UpdateOptions) (*uo.UnstructuredObject, []ApiWarning, error) { dryRun := k.DryRun || options.ForceDryRun ref := o.GetK8sRef() - log2 := log.WithField("ref", ref) updateOpts := v1.UpdateOptions{ FieldManager: "kluctl", @@ -851,7 +850,8 @@ func (k *K8sCluster) UpdateObject(o *uo.UnstructuredObject, options UpdateOption updateOpts.DryRun = []string{"All"} } - log2.Debugf("updating") + status.Trace(k.ctx, "updating %s", ref.String()) + var result *uo.UnstructuredObject apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { x, err := r.Update(k.ctx, o.ToUnstructured(), updateOpts) diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index c4d8b9527..4c5b74fb6 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -5,10 +5,10 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/status" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" "io/ioutil" "os" "path/filepath" @@ -237,7 +237,7 @@ func (c *LoadedKluctlProject) mergeClustersDirs(mergedClustersDir string, cluste for _, ci := range clustersInfos { if !utils.IsDirectory(ci.dir) { - log.Warningf("Cluster dir '%s' does not exist", ci.dir) + status.Warning(c.ctx, "Cluster dir '%s' does not exist", ci.dir) continue } files, err := ioutil.ReadDir(ci.dir) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 50d8a7f27..bf4d1b369 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -107,7 +107,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, clientConfig } varsCtx.UpdateChild("target", targetVars) - d, err := deployment.NewDeploymentProject(k, varsCtx, deploymentDir, p.sealedSecretsDir, nil) + d, err := deployment.NewDeploymentProject(ctx, k, varsCtx, deploymentDir, p.sealedSecretsDir, nil) if err != nil { return nil, err } diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index baaff34d2..8492422b2 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -5,11 +5,11 @@ import ( securejoin "github.com/cyphar/filepath-securejoin" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" "reflect" "regexp" "sort" @@ -51,7 +51,7 @@ func (c *LoadedKluctlProject) loadTargets() error { if targetInfo.refPattern == nil { return err } - log.Warningf("Failed to load dynamic target config for project: %v", err) + status.Warning(c.ctx, "Failed to load dynamic target config for project: %v", err) continue } @@ -61,7 +61,7 @@ func (c *LoadedKluctlProject) loadTargets() error { } if _, ok := targetNames[target.Name]; ok { - log.Warningf("Duplicate target %s", target.Name) + status.Warning(c.ctx, "Duplicate target %s", target.Name) } else { targetNames[target.Name] = true c.DynamicTargets = append(c.DynamicTargets, &types.DynamicTarget{ diff --git a/pkg/python/embed.go b/pkg/python/embed.go index 4a67eee0d..500e68ed8 100644 --- a/pkg/python/embed.go +++ b/pkg/python/embed.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/embed_util" - "log" "path/filepath" "runtime" ) @@ -21,20 +20,20 @@ func decompressPython() string { tarName := fmt.Sprintf("embed/python-%s-%s.tar.gz", runtime.GOOS, runtime.GOARCH) tgz, err := pythonLib.Open(tarName) if err != nil { - log.Panic(err) + panic(err) } defer tgz.Close() fileList, err := pythonLib.Open(tarName + ".files") if err != nil { - log.Panic(err) + panic(err) } defer fileList.Close() path := filepath.Join(utils.GetTmpBaseDir(), fmt.Sprintf("python-%s", runtime.GOOS)) path, err = embed_util.ExtractTarToTmp(tgz, fileList, path) if err != nil { - log.Panic(err) + panic(err) } return path diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 93f11f95b..9f2171d83 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -12,9 +12,9 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - log "github.com/sirupsen/logrus" "io/ioutil" "net/http" "net/http/httputil" @@ -297,19 +297,19 @@ func (rh *RegistryHelper) writeCachedResponse(key string, data []byte) { if !utils.Exists(filepath.Dir(cachePath)) { err := os.MkdirAll(filepath.Dir(cachePath), 0o700) if err != nil { - log.Warningf("writeCachedResponse failed: %v", err) + status.Warning(rh.ctx, "writeCachedResponse failed: %v", err) return } } err := ioutil.WriteFile(cachePath+".tmp", data, 0o600) if err != nil { - log.Warningf("writeCachedResponse failed: %v", err) + status.Warning(rh.ctx, "writeCachedResponse failed: %v", err) return } err = os.Rename(cachePath+".tmp", cachePath) if err != nil { - log.Warningf("writeCachedResponse failed: %v", err) + status.Warning(rh.ctx, "writeCachedResponse failed: %v", err) return } } diff --git a/pkg/seal/bootstrap.go b/pkg/seal/bootstrap.go index 02865c4e9..84761159d 100644 --- a/pkg/seal/bootstrap.go +++ b/pkg/seal/bootstrap.go @@ -1,15 +1,16 @@ package seal import ( + "context" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/pem" "github.com/bitnami-labs/sealed-secrets/pkg/crypto" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - log "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/schema" certUtil "k8s.io/client-go/util/cert" @@ -21,7 +22,7 @@ const sealedSecretsKeyLabel = "sealedsecrets.bitnami.com/sealed-secrets-key" const secretName = "sealed-secrets-key-kluctl-bootstrap" const configMapName = "sealed-secrets-key-kluctl-bootstrap" -func BootstrapSealedSecrets(k *k8s.K8sCluster, namespace string) error { +func BootstrapSealedSecrets(ctx context.Context, k *k8s.K8sCluster, namespace string) error { existing, _, err := k.GetSingleObject(k8s2.ObjectRef{ GVK: schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}, Name: "sealedsecrets.bitnami.com", @@ -41,7 +42,7 @@ func BootstrapSealedSecrets(k *k8s.K8sCluster, namespace string) error { return nil } - log.Infof("Bootstrapping sealed-secrets with a self-generated key") + status.Info(ctx, "Bootstrapping sealed-secrets with a self-generated key") key, cert, err := crypto.GeneratePrivateKeyAndCert(2048, 10*365*24*time.Hour, "bootstrap.kluctl.io") if err != nil { diff --git a/pkg/seal/fetch_cert.go b/pkg/seal/fetch_cert.go index 5af09d342..95b1c154a 100644 --- a/pkg/seal/fetch_cert.go +++ b/pkg/seal/fetch_cert.go @@ -1,31 +1,32 @@ package seal import ( + "context" "crypto/rsa" "errors" "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - log "github.com/sirupsen/logrus" "io/ioutil" v12 "k8s.io/api/core/v1" "k8s.io/client-go/util/cert" ) -func fetchCert(k *k8s.K8sCluster, namespace string, controllerName string) (*rsa.PublicKey, error) { +func fetchCert(ctx context.Context, k *k8s.K8sCluster, namespace string, controllerName string) (*rsa.PublicKey, error) { certData, err := openCertFromController(k, namespace, controllerName) if err != nil { if controllerName == "sealed-secrets-controller" { s2, err2 := openCertFromController(k, namespace, "sealed-secrets") if err2 == nil { - log.Warningf("Looks like you have sealed-secrets controller installed with name 'sealed-secrets', which comes from a legacy kluctl version that deployed it with a non-default name. Please consider re-deploying sealed-secrets operator manually.") + status.Warning(ctx, "Looks like you have sealed-secrets controller installed with name 'sealed-secrets', which comes from a legacy kluctl version that deployed it with a non-default name. Please consider re-deploying sealed-secrets operator manually.") err = nil certData = s2 } } if err != nil { - log.Warningf("Failed to retrieve public certificate from sealed-secrets-controller, re-trying with bootstrap secret") + status.Warning(ctx, "Failed to retrieve public certificate from sealed-secrets-controller, re-trying with bootstrap secret") certData, err = openCertFromBootstrap(k, namespace) if err != nil { return nil, fmt.Errorf("failed to retrieve sealed secrets public key: %w", err) diff --git a/pkg/seal/sealer.go b/pkg/seal/sealer.go index 92dd6953d..93d1af66f 100644 --- a/pkg/seal/sealer.go +++ b/pkg/seal/sealer.go @@ -1,6 +1,7 @@ package seal import ( + "context" "crypto/rand" "crypto/rsa" "encoding/base64" @@ -8,12 +9,12 @@ import ( "fmt" "github.com/bitnami-labs/sealed-secrets/pkg/crypto" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" "golang.org/x/crypto/scrypt" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" @@ -27,18 +28,20 @@ const hashAnnotation = "kluctl.io/sealedsecret-hashes" const clusterIdAnnotation = "kluctl.io/sealedsecret-cluster-id" type Sealer struct { + ctx context.Context clusterConfig *types.ClusterConfig2 forceReseal bool cert *rsa.PublicKey clusterId string } -func NewSealer(k *k8s.K8sCluster, sealedSecretsNamespace string, sealedSecretsControllerName string, clusterConfig *types.ClusterConfig2, forceReseal bool) (*Sealer, error) { +func NewSealer(ctx context.Context, k *k8s.K8sCluster, sealedSecretsNamespace string, sealedSecretsControllerName string, clusterConfig *types.ClusterConfig2, forceReseal bool) (*Sealer, error) { s := &Sealer{ + ctx: ctx, clusterConfig: clusterConfig, forceReseal: forceReseal, } - cert, err := fetchCert(k, sealedSecretsNamespace, sealedSecretsControllerName) + cert, err := fetchCert(ctx, k, sealedSecretsNamespace, sealedSecretsControllerName) if err != nil { return nil, err } @@ -96,7 +99,7 @@ func (s *Sealer) doHash(key string, secret []byte, secretName string, secretName } h, err := scrypt.Key(secret, []byte(salt), 1<<14, 8, 1, 64) if err != nil { - log.Fatal(err) + panic(err) } return hex.EncodeToString(h) } @@ -242,10 +245,10 @@ func (s *Sealer) SealFile(p string, targetFile string) error { resealAll := false if s.forceReseal { resealAll = true - log.Infof("Forcing reseal of secrets in %s", secretName) + status.Info(s.ctx, "Forcing reseal of secrets in %s", secretName) } else if existingClusterId != s.clusterId { resealAll = true - log.Infof("Target cluster for secret %s has changed, forcing reseal", secretName) + status.Info(s.ctx, "Target cluster for secret %s has changed, forcing reseal", secretName) } for k, v := range secrets { @@ -254,19 +257,19 @@ func (s *Sealer) SealFile(p string, targetFile string) error { doEncrypt := resealAll if !doEncrypt && hash != existingHash { - log.Infof("Secret %s and key %s has changed, resealing", secretName, k) + status.Info(s.ctx, "Secret %s and key %s has changed, resealing", secretName, k) doEncrypt = true } if !doEncrypt { e, ok, _ := existingContent.GetNestedString("spec", "encryptedData", k) if ok { - log.Debugf("Secret %s and key %s is unchanged", secretName, k) + status.Trace(s.ctx, "Secret %s and key %s is unchanged", secretName, k) result.SetNestedField(e, "spec", "encryptedData", k) resultSecretHashes[k] = hash continue } else { - log.Infof("Old encrypted secret %s and key %s not found", secretName, k) + status.Info(s.ctx, "Old encrypted secret %s and key %s not found", secretName, k) doEncrypt = true } } @@ -287,7 +290,7 @@ func (s *Sealer) SealFile(p string, targetFile string) error { result.SetK8sAnnotation(clusterIdAnnotation, s.clusterId) if reflect.DeepEqual(existingContent, result) { - log.Infof("Skipped %s as it did not change", baseName) + status.Info(s.ctx, "Skipped %s as it did not change", baseName) return nil } diff --git a/pkg/status/status.go b/pkg/status/status.go new file mode 100644 index 000000000..a9b8b4df6 --- /dev/null +++ b/pkg/status/status.go @@ -0,0 +1,107 @@ +package status + +import ( + "context" + "fmt" +) + +// StatusContext is used to report user-facing status/progress +type StatusContext struct { + ctx context.Context + sh StatusHandler + sl StatusLine + finished bool + failed bool +} + +type StatusLine interface { + Update(message string) + End(success bool) +} + +type StatusHandler interface { + StartStatus(message string) StatusLine + + Info(message string) + Warning(message string) + Error(message string) + Trace(message string) + + PlainText(text string) +} + +type contextKey struct{} + +func NewContext(ctx context.Context, slh StatusHandler) context.Context { + return context.WithValue(ctx, contextKey{}, slh) +} + +func FromContext(ctx context.Context) StatusHandler { + v := ctx.Value(contextKey{}) + if v == nil { + return nil + } + return v.(StatusHandler) +} + +func Start(ctx context.Context, status string, args ...any) *StatusContext { + sh := FromContext(ctx) + s := &StatusContext{ + ctx: ctx, + sh: sh, + } + + s.sl = sh.StartStatus(fmt.Sprintf(status, args...)) + + return s +} + +func (s *StatusContext) Update(message string, args ...any) { + s.sl.Update(fmt.Sprintf(message, args...)) +} + +func (s *StatusContext) Failed() { + if s.finished { + return + } + s.sl.End(false) + s.failed = true +} + +func (s *StatusContext) FailedWithMessage(msg string, args ...any) { + if s.finished { + return + } + s.Update(msg, args...) + s.Failed() +} + +func (s *StatusContext) Success() { + s.sl.End(true) + s.finished = true +} + +func PlainText(ctx context.Context, text string) { + slh := FromContext(ctx) + slh.PlainText(text) +} + +func Info(ctx context.Context, status string, args ...any) { + slh := FromContext(ctx) + slh.Info(fmt.Sprintf(status, args...)) +} + +func Warning(ctx context.Context, status string, args ...any) { + slh := FromContext(ctx) + slh.Warning(fmt.Sprintf(status, args...)) +} + +func Trace(ctx context.Context, status string, args ...any) { + slh := FromContext(ctx) + slh.Trace(fmt.Sprintf(status, args...)) +} + +func Error(ctx context.Context, status string, args ...any) { + slh := FromContext(ctx) + slh.Error(fmt.Sprintf(status, args...)) +} diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go new file mode 100644 index 000000000..15b08848c --- /dev/null +++ b/pkg/status/status_handler.go @@ -0,0 +1,67 @@ +package status + +import ( + "fmt" + "io" +) + +type MultiLineStatusHandler struct { + out io.Writer + trace bool +} + +type statusLine struct { + slh *MultiLineStatusHandler + message string +} + +func NewMultiLineStatusHandler(out io.Writer, trace bool) *MultiLineStatusHandler { + sh := &MultiLineStatusHandler{ + out: out, + trace: trace, + } + return sh +} + +func (s *MultiLineStatusHandler) StartStatus(message string) StatusLine { + sl := &statusLine{ + slh: s, + message: message, + } + s.writeOut(sl.message) + return sl +} + +func (s *MultiLineStatusHandler) writeOut(message string) { + _, _ = fmt.Fprintf(s.out, "%s\n", message) +} + +func (s *MultiLineStatusHandler) Info(message string) { + s.writeOut(message) +} + +func (s *MultiLineStatusHandler) Warning(message string) { + s.writeOut(message) +} + +func (s *MultiLineStatusHandler) Error(message string) { + s.writeOut(message) +} + +func (s *MultiLineStatusHandler) Trace(message string) { + if s.trace { + s.writeOut(message) + } +} + +func (s *MultiLineStatusHandler) PlainText(text string) { + s.writeOut(text) +} + +func (sl *statusLine) Update(message string) { + sl.message = message + sl.slh.writeOut(sl.message) +} + +func (sl *statusLine) End(success bool) { +} diff --git a/pkg/utils/env.go b/pkg/utils/env.go index 91def48c2..5dbca1e69 100644 --- a/pkg/utils/env.go +++ b/pkg/utils/env.go @@ -2,7 +2,6 @@ package utils import ( "fmt" - log "github.com/sirupsen/logrus" "os" "regexp" "strconv" @@ -18,7 +17,7 @@ func ParseEnvConfigSets(prefix string) map[int]map[string]string { for _, e := range os.Environ() { eq := strings.Index(e, "=") if eq == -1 { - log.Panicf("unexpected env var %s", e) + panic(fmt.Sprintf("unexpected env var %s", e)) } n := e[:eq] v := e[eq+1:] diff --git a/pkg/utils/prompts.go b/pkg/utils/prompts.go index 06cecf885..f95098796 100644 --- a/pkg/utils/prompts.go +++ b/pkg/utils/prompts.go @@ -4,13 +4,16 @@ import ( "bufio" "fmt" "github.com/mattn/go-isatty" - log "github.com/sirupsen/logrus" "golang.org/x/term" "os" "strings" "syscall" ) +func doWarn(f string, args ...any) { + _, _ = fmt.Fprintf(os.Stderr, f, args...) +} + // AskForConfirmation uses Scanln to parse user input. A user must type in "yes" or "no" and // then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", and "Yes" all count as // confirmations. If the input is not recognized, it will ask again. The function does not return @@ -18,13 +21,13 @@ import ( // before calling askForConfirmation. E.g. fmt.Println("WARNING: Are you sure? (yes/no)") func AskForConfirmation(prompt string) bool { if !isatty.IsTerminal(os.Stderr.Fd()) { - log.Warningf("Not a terminal, suppressed prompt: %s", prompt) + doWarn("Not a terminal, suppressed prompt: %s", prompt) return false } _, err := os.Stderr.WriteString(prompt + " (y/N) ") if err != nil { - log.Fatal(err) + panic(err) } var response string @@ -47,7 +50,7 @@ func AskForConfirmation(prompt string) bool { func AskForPassword(prompt string) (string, error) { if !isatty.IsTerminal(os.Stderr.Fd()) { err := fmt.Errorf("not a terminal, suppressed credentials prompt: %s", prompt) - log.Warning(err) + doWarn(err.Error()) return "", err } @@ -69,7 +72,7 @@ func AskForPassword(prompt string) (string, error) { func AskForCredentials(prompt string) (string, string, error) { if !isatty.IsTerminal(os.Stderr.Fd()) { err := fmt.Errorf("not a terminal, suppressed credentials prompt: %s", prompt) - log.Warning(err) + doWarn(err.Error()) return "", "", err } diff --git a/pkg/utils/uo/jsonpath.go b/pkg/utils/uo/jsonpath.go index a7bdbb760..b375aca7e 100644 --- a/pkg/utils/uo/jsonpath.go +++ b/pkg/utils/uo/jsonpath.go @@ -3,7 +3,6 @@ package uo import ( "fmt" "github.com/ohler55/ojg/jp" - log "github.com/sirupsen/logrus" "regexp" "strings" ) @@ -58,7 +57,7 @@ func NewMyJsonPath(p string) (*MyJsonPath, error) { func NewMyJsonPathMust(p string) *MyJsonPath { j, err := NewMyJsonPath(p) if err != nil { - log.Fatal(err) + panic(err) } return j } diff --git a/pkg/utils/uo/k8s_fields.go b/pkg/utils/uo/k8s_fields.go index be195b0f1..69c295757 100644 --- a/pkg/utils/uo/k8s_fields.go +++ b/pkg/utils/uo/k8s_fields.go @@ -1,8 +1,8 @@ package uo import ( + "fmt" "github.com/kluctl/kluctl/v2/pkg/types/k8s" - log "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "reflect" @@ -13,15 +13,15 @@ import ( func (uo *UnstructuredObject) GetK8sGVK() schema.GroupVersionKind { kind, _, err := uo.GetNestedString("kind") if err != nil { - log.Fatal(err) + panic(err) } apiVersion, _, err := uo.GetNestedString("apiVersion") if err != nil { - log.Fatal(err) + panic(err) } gv, err := schema.ParseGroupVersion(apiVersion) if err != nil { - log.Fatal(err) + panic(err) } return schema.GroupVersionKind{ Group: gv.Group, @@ -33,11 +33,11 @@ func (uo *UnstructuredObject) GetK8sGVK() schema.GroupVersionKind { func (uo *UnstructuredObject) SetK8sGVK(gvk schema.GroupVersionKind) { err := uo.SetNestedField(gvk.GroupVersion().String(), "apiVersion") if err != nil { - log.Fatal(err) + panic(err) } err = uo.SetNestedField(gvk.Kind, "kind") if err != nil { - log.Fatal(err) + panic(err) } } @@ -48,7 +48,7 @@ func (uo *UnstructuredObject) SetK8sGVKs(g string, v string, k string) { func (uo *UnstructuredObject) GetK8sName() string { s, _, err := uo.GetNestedString("metadata", "name") if err != nil { - log.Fatal(err) + panic(err) } return s } @@ -56,14 +56,14 @@ func (uo *UnstructuredObject) GetK8sName() string { func (uo *UnstructuredObject) SetK8sName(name string) { err := uo.SetNestedField(name, "metadata", "name") if err != nil { - log.Fatal(err) + panic(err) } } func (uo *UnstructuredObject) GetK8sNamespace() string { s, _, err := uo.GetNestedString("metadata", "namespace") if err != nil { - log.Fatal(err) + panic(err) } return s } @@ -72,12 +72,12 @@ func (uo *UnstructuredObject) SetK8sNamespace(namespace string) { if namespace != "" { err := uo.SetNestedField(namespace, "metadata", "namespace") if err != nil { - log.Fatal(err) + panic(err) } } else { err := uo.RemoveNestedField("metadata", "namespace") if err != nil { - log.Fatal(err) + panic(err) } } @@ -94,7 +94,7 @@ func (uo *UnstructuredObject) GetK8sRef() k8s.ObjectRef { func (uo *UnstructuredObject) GetK8sLabels() map[string]string { ret, ok, err := uo.GetNestedStringMapCopy("metadata", "labels") if err != nil { - log.Fatal(err) + panic(err) } if !ok { return map[string]string{} @@ -112,7 +112,7 @@ func (uo *UnstructuredObject) SetK8sLabels(labels map[string]string) { func (uo *UnstructuredObject) GetK8sLabel(name string) *string { ret, ok, err := uo.GetNestedString("metadata", "labels", name) if err != nil { - log.Fatal(err) + panic(err) } if !ok { return nil @@ -123,7 +123,7 @@ func (uo *UnstructuredObject) GetK8sLabel(name string) *string { func (uo *UnstructuredObject) SetK8sLabel(name string, value string) { err := uo.SetNestedField(value, "metadata", "labels", name) if err != nil { - log.Fatal(err) + panic(err) } } @@ -142,7 +142,7 @@ func (uo *UnstructuredObject) GetK8sLabelsWithRegex(r interface{}) map[string]st func (uo *UnstructuredObject) GetK8sAnnotations() map[string]string { ret, ok, err := uo.GetNestedStringMapCopy("metadata", "annotations") if err != nil { - log.Fatal(err) + panic(err) } if !ok { return map[string]string{} @@ -153,7 +153,7 @@ func (uo *UnstructuredObject) GetK8sAnnotations() map[string]string { func (uo *UnstructuredObject) GetK8sAnnotation(name string) *string { ret, ok, err := uo.GetNestedString("metadata", "annotations", name) if err != nil { - log.Fatal(err) + panic(err) } if !ok { return nil @@ -171,7 +171,7 @@ func (uo *UnstructuredObject) SetK8sAnnotations(annotations map[string]string) { func (uo *UnstructuredObject) SetK8sAnnotation(name string, value string) { err := uo.SetNestedField(value, "metadata", "annotations", name) if err != nil { - log.Fatal(err) + panic(err) } } @@ -198,7 +198,7 @@ func (uo *UnstructuredObject) SetK8sResourceVersion(rv string) { } else { err := uo.SetNestedField(rv, "metadata", "resourceVersion") if err != nil { - log.Fatal(err) + panic(err) } } } @@ -232,6 +232,6 @@ func (ui *UnstructuredObject) getRegexp(r interface{}) *regexp.Regexp { return regexp.MustCompile(x) } } - log.Panicf("unknown type %s", reflect.TypeOf(r).String()) + panic(fmt.Sprintf("unknown type %s", reflect.TypeOf(r).String())) return nil } diff --git a/pkg/utils/uo/object_iterator.go b/pkg/utils/uo/object_iterator.go index be922ee93..debb5c227 100644 --- a/pkg/utils/uo/object_iterator.go +++ b/pkg/utils/uo/object_iterator.go @@ -1,9 +1,5 @@ package uo -import ( - log "github.com/sirupsen/logrus" -) - type ObjectIteratorFunc func(it *ObjectIterator) error type ObjectIterator struct { path []interface{} @@ -72,7 +68,7 @@ func (it *ObjectIterator) iterateInterface() error { func (it *ObjectIterator) iterateMap() error { m, ok := it.Value().(map[string]interface{}) if !ok { - log.Fatalf("!ok") + panic("!ok") } for k, v := range m { @@ -92,7 +88,7 @@ func (it *ObjectIterator) iterateMap() error { func (it *ObjectIterator) iterateList() error { l, ok := it.Value().([]interface{}) if !ok { - log.Fatalf("!ok") + panic("!ok") } for i, e := range l { diff --git a/pkg/utils/uo/uo.go b/pkg/utils/uo/uo.go index ffb513110..0356a98ac 100644 --- a/pkg/utils/uo/uo.go +++ b/pkg/utils/uo/uo.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/jinzhu/copier" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "reflect" ) @@ -76,7 +75,7 @@ func FromString(s string) (*UnstructuredObject, error) { func FromStringMust(s string) *UnstructuredObject { o, err := FromString(s) if err != nil { - log.Panic(err) + panic(err) } return o } @@ -112,7 +111,7 @@ func (uo *UnstructuredObject) Clone() *UnstructuredObject { DeepCopy: true, }) if err != nil { - log.Fatal(err) + panic(err) } return FromMap(c) } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index ae338d920..4bcd0bf70 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -4,7 +4,6 @@ import ( "crypto/sha256" "encoding/hex" "github.com/jinzhu/copier" - "log" "os" "path/filepath" "strconv" @@ -18,7 +17,7 @@ func GetTmpBaseDir() string { createTmpBaseDirOnce.Do(func() { err := os.MkdirAll(dir, 0o700) if err != nil { - log.Fatal(err) + panic(err) } }) return dir diff --git a/pkg/utils/versions/latest_version.go b/pkg/utils/versions/latest_version.go index 7cd524a9c..24edbc5b2 100644 --- a/pkg/utils/versions/latest_version.go +++ b/pkg/utils/versions/latest_version.go @@ -3,7 +3,6 @@ package versions import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" - "log" "regexp" "strconv" ) @@ -43,7 +42,7 @@ func NewRegexVersionFilter(pattern string) (LatestVersionFilter, error) { func NewRegexVersionFilterMust(pattern string) LatestVersionFilter { ret, err := NewRegexVersionFilter(pattern) if err != nil { - log.Fatal(err) + panic(err) } return ret } @@ -114,7 +113,7 @@ func NewPrefixVersionFilter(prefix string, suffix LatestVersionFilter) (LatestVe func NewPrefixVersionFilterMust(prefix string, suffix LatestVersionFilter) LatestVersionFilter { ret, err := NewPrefixVersionFilter(prefix, suffix) if err != nil { - log.Fatal(err) + panic(err) } return ret } diff --git a/pkg/utils/versions/latest_version_parse.go b/pkg/utils/versions/latest_version_parse.go index 6fe06ed23..8317e46e6 100644 --- a/pkg/utils/versions/latest_version_parse.go +++ b/pkg/utils/versions/latest_version_parse.go @@ -3,7 +3,6 @@ package versions import ( "fmt" scanner "github.com/kluctl/kluctl/v2/pkg/utils/python_scanner" - log "github.com/sirupsen/logrus" "strconv" "strings" ) @@ -27,7 +26,7 @@ func (p *preparsed) CurToken() rune { func (p *preparsed) CurTokenText() string { if p.nextPos > len(p.tokens) { - log.Panicf("CurTokenText at EOF") + panic("CurTokenText at EOF") } return p.tokens[p.nextPos-1].text } From 65db7632a10789ad8f7bcff6888c42d5e7443748 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 11:45:12 +0200 Subject: [PATCH 0129/2268] refactor: Use mpb library for status reporting --- cmd/kluctl/commands/root.go | 2 +- pkg/status/status_handler.go | 70 +++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 44e4ef541..a35f2c063 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -69,7 +69,7 @@ var flagGroups = []groupInfo{ var cliCtx = context.Background() func (c *cli) setupStatusHandler() error { - sh := status.NewMultiLineStatusHandler(os.Stderr, c.Debug) + sh := status.NewMultiLineStatusHandler(cliCtx, os.Stderr, c.Debug) cliCtx = status.NewContext(cliCtx, sh) return nil } diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index 15b08848c..7f6bd0c39 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -1,25 +1,46 @@ package status import ( + "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/vbauerster/mpb/v7" + "github.com/vbauerster/mpb/v7/decor" "io" + "math" ) type MultiLineStatusHandler struct { - out io.Writer - trace bool + out io.Writer + progress *mpb.Progress + stop chan any + trace bool } type statusLine struct { slh *MultiLineStatusHandler + bar *mpb.Bar + filler mpb.BarFiller message string + + finished bool + success bool } -func NewMultiLineStatusHandler(out io.Writer, trace bool) *MultiLineStatusHandler { +func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, trace bool) *MultiLineStatusHandler { sh := &MultiLineStatusHandler{ out: out, trace: trace, + stop: make(chan any), } + + sh.progress = mpb.NewWithContext( + ctx, + mpb.WithWidth(utils.GetTermWidth()), + mpb.WithOutput(out), + mpb.PopCompletedMode(), + ) + return sh } @@ -28,40 +49,65 @@ func (s *MultiLineStatusHandler) StartStatus(message string) StatusLine { slh: s, message: message, } - s.writeOut(sl.message) + sl.filler = mpb.SpinnerStyle().PositionLeft().Build() + sl.bar = s.progress.Add(1, sl, + mpb.BarWidth(1), + mpb.AppendDecorators(decor.Any(sl.DecorMessage, decor.WCSyncWidthR)), + ) + + s.writeLog(sl.message) return sl } -func (s *MultiLineStatusHandler) writeOut(message string) { - _, _ = fmt.Fprintf(s.out, "%s\n", message) +func (sl *statusLine) DecorMessage(s decor.Statistics) string { + return sl.message +} + +func (sl *statusLine) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { + if !sl.finished { + sl.filler.Fill(w, reqWidth, stat) + } else if sl.success { + fmt.Fprintf(w, "✓") + } else { + fmt.Fprintf(w, "✗") + } +} + +func (s *MultiLineStatusHandler) writeLog(message string) { + //_, _ = fmt.Fprintf(s.out, "%s\n", message) } func (s *MultiLineStatusHandler) Info(message string) { - s.writeOut(message) + s.writeLog(message) } func (s *MultiLineStatusHandler) Warning(message string) { - s.writeOut(message) + s.writeLog(message) } func (s *MultiLineStatusHandler) Error(message string) { - s.writeOut(message) + s.writeLog(message) } func (s *MultiLineStatusHandler) Trace(message string) { if s.trace { - s.writeOut(message) + s.writeLog(message) } } func (s *MultiLineStatusHandler) PlainText(text string) { - s.writeOut(text) + s.writeLog(text) } func (sl *statusLine) Update(message string) { sl.message = message - sl.slh.writeOut(sl.message) + sl.slh.writeLog(sl.message) } func (sl *statusLine) End(success bool) { + sl.finished = true + sl.success = success + // make sure that the bar es rendered on top so that it can be properly popped + sl.bar.SetPriority(math.MinInt) + sl.bar.Increment() } From 46d44006be9b591ba4c24eb4e7f6b97d9eafc57c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 14:43:23 +0200 Subject: [PATCH 0130/2268] fix: Few status reporting improvements --- pkg/deployment/deployment_collection.go | 44 +++++++++++++++---------- pkg/kluctl_project/project_load.go | 5 +++ pkg/kluctl_project/target_context.go | 4 +++ 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 9c26fa86e..fc6d76ef8 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -161,14 +161,12 @@ func (c *DeploymentCollection) resolveSealedSecrets() error { } func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { - s := status.Start(c.ctx, "Building kustomize objects") - defer s.Failed() - var wg sync.WaitGroup var errs []error var mutex sync.Mutex sem := semaphore.NewWeighted(16) + s := status.Start(c.ctx, "Building kustomize objects") for _, d_ := range c.Deployments { d := d_ @@ -179,20 +177,32 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { mutex.Lock() errs = append(errs, fmt.Errorf("building kustomize objects for %s failed. %w", *d.dir, err)) mutex.Unlock() - } else { - wg.Add(1) - go func() { - _ = sem.Acquire(context.Background(), 1) - defer sem.Release(1) - - err := d.postprocessAndLoadObjects(k, c.Images) - if err != nil { - mutex.Lock() - errs = append(errs, fmt.Errorf("postprocessing kustomize objects for %s failed: %w", *d.dir, err)) - mutex.Unlock() - } - wg.Done() - }() + } + wg.Done() + }() + } + wg.Wait() + + if len(errs) != 0 { + s.Failed() + return utils.NewErrorListOrNil(errs) + } + s.Success() + + s = status.Start(c.ctx, "Postprocessing objects") + for _, d_ := range c.Deployments { + d := d_ + + wg.Add(1) + go func() { + _ = sem.Acquire(context.Background(), 1) + defer sem.Release(1) + + err := d.postprocessAndLoadObjects(k, c.Images) + if err != nil { + mutex.Lock() + errs = append(errs, fmt.Errorf("postprocessing kustomize objects for %s failed: %w", *d.dir, err)) + mutex.Unlock() } wg.Done() diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 4c5b74fb6..bd99e8291 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -189,6 +189,9 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { } } + s := status.Start(c.ctx, "Loading kluctl project") + defer s.Failed() + deploymentInfo, err := c.loadExternalProject(c.Config.Deployment, "", c.loadArgs.LocalDeployment) if err != nil { return err @@ -226,6 +229,8 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { c.ClustersDir = mergedClustersDir c.sealedSecretsDir = sealedSecretsInfo.dir + s.Success() + return nil } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index bf4d1b369..1d6398ae0 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -6,6 +6,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -60,10 +61,13 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, clientConfig var k *k8s.K8sCluster if clientConfig != nil { + s := status.Start(ctx, "Initializing k8s client") k, err = k8s.NewK8sCluster(ctx, clientConfig, dryRun) if err != nil { + s.Failed() return nil, err } + s.Success() } varsCtx := jinja2.NewVarsCtx(p.J2) From 0949a6ba0a1b0d9ce81d92cb0b74312beea72e3f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 14:44:03 +0200 Subject: [PATCH 0131/2268] fix: Stop printing git progress --- pkg/git/mirrored_repo.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 2eeb19bfd..874942200 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -229,7 +229,6 @@ func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string, authPr err = remote.FetchContext(g.ctx, &git.FetchOptions{ Auth: auth.AuthMethod, CABundle: auth.CABundle, - Progress: os.Stdout, Tags: git.AllTags, Force: true, }) @@ -259,7 +258,6 @@ func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string, authPr _ = ioutil.WriteFile(filepath.Join(g.mirrorDir, ".update-time"), []byte(time.Now().Format(time.RFC3339Nano)), 0644) - s.Success() return nil } From 4873a78afdf3aeafd1d411b143300fba82da06d7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 14:45:52 +0200 Subject: [PATCH 0132/2268] fix: Redirect klog output to status handler --- cmd/kluctl/commands/root.go | 5 +++ pkg/status/utils.go | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 pkg/status/utils.go diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index a35f2c063..795c5451c 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -26,6 +26,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/spf13/cobra" "github.com/spf13/viper" + "k8s.io/klog/v2" "net/http" "os" "path/filepath" @@ -71,6 +72,10 @@ var cliCtx = context.Background() func (c *cli) setupStatusHandler() error { sh := status.NewMultiLineStatusHandler(cliCtx, os.Stderr, c.Debug) cliCtx = status.NewContext(cliCtx, sh) + + klog.LogToStderr(false) + klog.SetOutput(status.NewLineRedirector(sh.Info)) + return nil } diff --git a/pkg/status/utils.go b/pkg/status/utils.go new file mode 100644 index 000000000..b6d6fbb64 --- /dev/null +++ b/pkg/status/utils.go @@ -0,0 +1,65 @@ +package status + +import ( + "bufio" + "io" +) + +func NewLineRedirector(cb func(line string)) io.WriteCloser { + r, w := io.Pipe() + + go func() { + br := bufio.NewReader(r) + scanner := bufio.NewScanner(&replaceRReader{reader: br}) + for scanner.Scan() { + msg := scanner.Text() + if msg == "" { + continue + } + cb(msg) + } + }() + + return w +} + +type replaceRReader struct { + reader *bufio.Reader + lastR bool +} + +func (r *replaceRReader) Read(p []byte) (int, error) { + written := 0 + for true { + b, err := r.reader.ReadByte() + if err != nil { + if err == io.EOF { + if written == 0 { + return 0, err + } + return written, nil + } + return 0, err + } + + if b == '\r' { + p[written] = '\n' + written++ + r.lastR = true + break + } else if b == '\n' { + if r.lastR { + continue + } + p[written] = '\n' + written++ + r.lastR = false + break + } else { + p[written] = b + written++ + r.lastR = false + } + } + return written, nil +} From ccb8ad8b7fa0190f42eb80f8db472b1da9c67529 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 16:16:33 +0200 Subject: [PATCH 0133/2268] feat: Use status handler for deployment progress reporting --- pkg/deployment/commands/downscale.go | 2 +- pkg/deployment/commands/poke_images.go | 2 +- pkg/deployment/commands/validate.go | 2 +- pkg/deployment/utils/apply_utils.go | 110 +++++++--------- pkg/deployment/utils/hooks_util.go | 12 +- pkg/deployment/utils/progress.go | 126 ------------------- pkg/deployment/utils/remote_objects_utils.go | 10 +- pkg/status/status.go | 115 ++++++++++++++++- pkg/status/status_handler.go | 81 +++++++----- 9 files changed, 228 insertions(+), 232 deletions(-) delete mode 100644 pkg/deployment/utils/progress.go diff --git a/pkg/deployment/commands/downscale.go b/pkg/deployment/commands/downscale.go index 60cadd857..82e89b2cc 100644 --- a/pkg/deployment/commands/downscale.go +++ b/pkg/deployment/commands/downscale.go @@ -40,7 +40,7 @@ func (cmd *DownscaleCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types if !d.CheckInclusionForDeploy() { continue } - au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(ctx, nil, d.RelToProjectItemDir, 0, true)) + au := ad.NewApplyUtil(ctx, nil) for _, o := range d.Objects { o := o ref := o.GetK8sRef() diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index c794e8a8a..03ae6dbc3 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -76,7 +76,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type wg.Add(1) go func() { defer wg.Done() - au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(ctx, nil, ref.String(), 0, true)) + au := ad.NewApplyUtil(ctx, nil) au.ReplaceObject(ref, ru.GetRemoteObject(ref), func(o *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { return doPokeImage(containers, o) }) diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index fbe9102ec..d9a63720d 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -40,7 +40,7 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types. if !d.CheckInclusionForDeploy() { continue } - au := ad.NewApplyUtil(ctx, utils2.NewProgressCtx(ctx, nil, d.RelToProjectItemDir, 0, true)) + au := ad.NewApplyUtil(ctx, nil) h := utils2.NewHooksUtil(au) for _, o := range d.Objects { hook := h.GetHook(o) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index b8c78acbb..a93028eae 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -13,11 +13,9 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" - "github.com/vbauerster/mpb/v7" "golang.org/x/sync/semaphore" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" - "os" "reflect" "strings" "sync" @@ -53,7 +51,7 @@ type ApplyUtil struct { ru *RemoteObjectUtils k *k8s.K8sCluster o *ApplyUtilOptions - pctx *progressCtx + sctx *status.StatusContext } type ApplyDeploymentsUtil struct { @@ -85,7 +83,7 @@ func NewApplyDeploymentsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnin return ret } -func (ad *ApplyDeploymentsUtil) NewApplyUtil(ctx context.Context, pctx *progressCtx) *ApplyUtil { +func (ad *ApplyDeploymentsUtil) NewApplyUtil(ctx context.Context, statusCtx *status.StatusContext) *ApplyUtil { ret := &ApplyUtil{ ctx: ctx, dew: ad.dew, @@ -98,7 +96,7 @@ func (ad *ApplyDeploymentsUtil) NewApplyUtil(ctx context.Context, pctx *progress ru: ad.ru, k: ad.k, o: ad.o, - pctx: pctx, + sctx: statusCtx, } ad.results = append(ad.results, ret) return ret @@ -184,7 +182,7 @@ func (a *ApplyUtil) retryApplyForceReplace(x *uo.UnstructuredObject, hook bool, warn := fmt.Errorf("patching %s failed, retrying by deleting and re-applying", ref.String()) a.HandleWarning(ref, warn) - a.pctx.Warningf(warn.Error()) + status.Warning(a.ctx, warn.Error()) if !a.DeleteObject(ref, hook) { return @@ -216,7 +214,7 @@ func (a *ApplyUtil) retryApplyWithReplace(x *uo.UnstructuredObject, hook bool, r warn := fmt.Errorf("patching %s failed, retrying with replace instead of patch", ref.String()) a.HandleWarning(ref, warn) - a.pctx.Warningf(warn.Error()) + status.Warning(a.ctx, warn.Error()) rv := remoteObject.GetK8sResourceVersion() x2 := x.Clone() @@ -367,7 +365,7 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo } timeoutTimer := time.NewTimer(timeout) - a.pctx.Debugf("Waiting for %s to get ready", ref.String()) + status.Trace(a.ctx, "Waiting for %s to get ready", ref.String()) lastLogTime := time.Now() didLog := false @@ -380,7 +378,7 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo if err != nil { if errors.IsNotFound(err) { if didLog { - a.pctx.Warningf("Cancelled waiting for %s as it disappeared while waiting for it (%ds elapsed)", ref.String(), elapsed) + status.Warning(a.ctx, "Cancelled waiting for %s as it disappeared while waiting for it (%ds elapsed)", ref.String(), elapsed) } a.HandleError(ref, fmt.Errorf("%s disappeared while waiting for it to become ready", ref.String())) return false @@ -391,13 +389,13 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo v := validation.ValidateObject(a.k, o, false) if v.Ready { if didLog { - a.pctx.Infof("Finished waiting for %s (%ds elapsed)", ref.String(), elapsed) + a.sctx.InfoFallback("Finished waiting for %s (%ds elapsed)", ref.String(), elapsed) } return true } if len(v.Errors) != 0 { if didLog { - a.pctx.Warningf("Cancelled waiting for %s due to errors (%ds elapsed)", ref.String(), elapsed) + status.Warning(a.ctx, "Cancelled waiting for %s due to errors (%ds elapsed)", ref.String(), elapsed) } for _, e := range v.Errors { a.HandleError(ref, fmt.Errorf(e.Error)) @@ -405,14 +403,14 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo return false } - a.pctx.SetStatus(fmt.Sprintf("Waiting for %s to get ready...", ref.String())) + a.sctx.Update(fmt.Sprintf("Waiting for %s to get ready...", ref.String())) if !didLog { - a.pctx.Infof("Waiting for %s to get ready... (%ds elapsed)", ref.String(), elapsed) + a.sctx.InfoFallback("Waiting for %s to get ready... (%ds elapsed)", ref.String(), elapsed) didLog = true lastLogTime = time.Now() } else if didLog && time.Now().Sub(lastLogTime) >= 10*time.Second { - a.pctx.Infof("Still waiting for %s to get ready... (%ds elapsed)", ref.String(), elapsed) + a.sctx.InfoFallback("Still waiting for %s to get ready... (%ds elapsed)", ref.String(), elapsed) lastLogTime = time.Now() } @@ -421,12 +419,12 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo continue case <-timeoutTimer.C: err := fmt.Errorf("timed out while waiting for readiness of %s", ref.String()) - a.pctx.Warningf("%s (%ds elapsed)", err.Error(), elapsed) + status.Warning(a.ctx, "%s (%ds elapsed)", err.Error(), elapsed) a.HandleError(ref, err) return false case <-a.ctx.Done(): err := fmt.Errorf("failed waiting for readiness of %s: %w", ref.String(), err) - a.pctx.Warningf("%s (%ds elapsed)", err.Error(), elapsed) + status.Warning(a.ctx, "%s (%ds elapsed)", err.Error(), elapsed) a.HandleError(ref, err) return false } @@ -476,26 +474,26 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { // +1 to ensure that we don't prematurely complete the bar (which would happen as we don't count for waiting) total := len(applyObjects) + len(preHooks) + len(postHooks) + 1 - a.pctx.SetTotal(int64(total)) + a.sctx.SetTotal(total) if !d.CheckInclusionForDeploy() { - a.pctx.InfofAndStatus("Skipped") - a.pctx.Finish() + a.sctx.UpdateAndInfoFallback("Skipped") + a.sctx.Success() return } if len(toDelete) != 0 { - a.pctx.Infof("Deleting %d objects", len(toDelete)) + a.sctx.InfoFallback("Deleting %d objects", len(toDelete)) for i, ref := range toDelete { - a.pctx.SetStatus(fmt.Sprintf("Deleting object %s (%d of %d)", ref.String(), i+1, len(toDelete))) + a.sctx.Update(fmt.Sprintf("Deleting object %s (%d of %d)", ref.String(), i+1, len(toDelete))) a.DeleteObject(ref, false) - a.pctx.Increment() + a.sctx.Increment() } } h.RunHooks(preHooks) if len(applyObjects) != 0 { - a.pctx.Infof("Applying %d objects", len(applyObjects)) + a.sctx.InfoFallback("Applying %d objects", len(applyObjects)) } startTime := time.Now() didLog := false @@ -505,11 +503,11 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { } ref := o.GetK8sRef() - a.pctx.SetStatus(fmt.Sprintf("Applying object %s (%d of %d)", ref.String(), i+1, len(applyObjects))) + a.sctx.Update(fmt.Sprintf("Applying object %s (%d of %d)", ref.String(), i+1, len(applyObjects))) a.ApplyObject(o, false, false) - a.pctx.Increment() + a.sctx.Increment() if time.Now().Sub(startTime) >= 10*time.Second || (didLog && i == len(applyObjects)-1) { - a.pctx.Infof("...applied %d of %d objects", i+1, len(applyObjects)) + a.sctx.InfoFallback("...applied %d of %d objects", i+1, len(applyObjects)) startTime = time.Now() didLog = true } @@ -545,8 +543,13 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { finalStatus += fmt.Sprintf(" Encountered %d warnings.", a.warningCount) } - a.pctx.SetStatus(strings.TrimSpace(finalStatus)) - a.pctx.Finish() + a.sctx.Update(strings.TrimSpace(finalStatus)) + + if a.errorCount == 0 { + a.sctx.Success() + } else { + a.sctx.Failed() + } } func (a *ApplyDeploymentsUtil) buildProgressName(d *deployment.DeploymentItem) *string { @@ -561,15 +564,11 @@ func (a *ApplyDeploymentsUtil) buildProgressName(d *deployment.DeploymentItem) * } func (a *ApplyDeploymentsUtil) ApplyDeployments() { - status.Info(a.ctx, "Running server-side apply for all objects") + s := status.Start(a.ctx, "Running server-side apply for all objects") + defer s.Failed() var wg sync.WaitGroup sem := semaphore.NewWeighted(8) - p := mpb.New( - mpb.WithWidth(utils.GetTermWidth()), - mpb.WithOutput(os.Stderr), - mpb.PopCompletedMode(), - ) maxNameLen := 0 for _, d := range a.deployments { @@ -590,13 +589,15 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { _ = sem.Acquire(context.Background(), 1) progressName := a.buildProgressName(d) - var pctx *progressCtx + var sctx *status.StatusContext if progressName != nil { - pctx = NewProgressCtx(a.ctx, p, *progressName, maxNameLen, true) - } else { - pctx = NewProgressCtx(a.ctx, nil, "", 0, false) + sctx = status.StartWithOptions(a.ctx, + status.WithTotal(-1), + status.WithPrefix(*progressName), + status.WithStatus("Initializing..."), + ) } - a2 := a.NewApplyUtil(a.ctx, pctx) + a2 := a.NewApplyUtil(a.ctx, sctx) wg.Add(1) go func() { @@ -604,37 +605,22 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { defer sem.Release(1) a2.applyDeploymentItem(d) - pctx.Finish() + + // if success was not signalled, get into failed status + sctx.Failed() }() barrier := (d.Config.Barrier != nil && *d.Config.Barrier) || d.Barrier if barrier { - bpctx := NewProgressCtx(a.ctx, p, "", maxNameLen, true) - bpctx.SetTotal(1) - - bpctx.InfofAndStatus("Waiting on barrier...") - stopCh := make(chan int) - doneCh := make(chan int) - go func() { - for { - select { - case <-stopCh: - bpctx.SetStatus(fmt.Sprintf("Finished waiting")) - bpctx.Finish() - doneCh <- 1 - return - case <-time.After(time.Second): - bpctx.SetStatus(fmt.Sprintf("Waiting on barrier...")) - } - } - }() + sctx := status.Start(a.ctx, "", status.WithTotal(1)) + sctx.UpdateAndInfoFallback("Waiting on barrier...") wg.Wait() - stopCh <- 1 - <-doneCh + sctx.UpdateAndInfoFallback(fmt.Sprintf("Finished waiting")) + sctx.Success() } } wg.Wait() - p.Wait() + s.Success() } func (a *ApplyUtil) ReplaceObject(ref k8s2.ObjectRef, firstVersion *uo.UnstructuredObject, callback func(o *uo.UnstructuredObject) (*uo.UnstructuredObject, error)) { diff --git a/pkg/deployment/utils/hooks_util.go b/pkg/deployment/utils/hooks_util.go index bee3b6811..b811962c7 100644 --- a/pkg/deployment/utils/hooks_util.go +++ b/pkg/deployment/utils/hooks_util.go @@ -82,12 +82,12 @@ func (u *HooksUtil) RunHooks(hooks []*hook) { for p := range h.deletePolicies { dpStr = append(dpStr, p) } - u.a.pctx.InfofAndStatus("Deleting hook %s due to hook-delete-policy %s (%d of %d)", ref.String(), strings.Join(dpStr, ","), i+1, cnt) + u.a.sctx.UpdateAndInfoFallback("Deleting hook %s due to hook-delete-policy %s (%d of %d)", ref.String(), strings.Join(dpStr, ","), i+1, cnt) return u.a.DeleteObject(ref, true) } if len(deleteBeforeObjects) != 0 { - u.a.pctx.Infof("Deleting %d hooks before hook execution", len(deleteBeforeObjects)) + u.a.sctx.InfoFallback("Deleting %d hooks before hook execution", len(deleteBeforeObjects)) } for i, h := range deleteBeforeObjects { doDeleteForPolicy(h, i, len(deleteBeforeObjects)) @@ -95,14 +95,14 @@ func (u *HooksUtil) RunHooks(hooks []*hook) { waitResults := make(map[k8s.ObjectRef]bool) if len(applyObjects) != 0 { - u.a.pctx.Infof("Applying %d hooks", len(applyObjects)) + u.a.sctx.InfoFallback("Applying %d hooks", len(applyObjects)) } for i, h := range applyObjects { ref := h.object.GetK8sRef() _, replaced := h.deletePolicies["before-hook-creation"] - u.a.pctx.DebugfAndStatus("Applying hook %s (%d of %d)", ref.String(), i+1, len(applyObjects)) + u.a.sctx.UpdateAndInfoFallback("Applying hook %s (%d of %d)", ref.String(), i+1, len(applyObjects)) u.a.ApplyObject(h.object, replaced, true) - u.a.pctx.Increment() + u.a.sctx.Increment() if u.a.HadError(ref) { continue @@ -133,7 +133,7 @@ func (u *HooksUtil) RunHooks(hooks []*hook) { } if len(deleteAfterObjects) != 0 { - u.a.pctx.Infof("Deleting %d hooks after hook execution", len(deleteAfterObjects)) + u.a.sctx.InfoFallback("Deleting %d hooks after hook execution", len(deleteAfterObjects)) } for i, h := range deleteAfterObjects { doDeleteForPolicy(h, i, len(deleteAfterObjects)) diff --git a/pkg/deployment/utils/progress.go b/pkg/deployment/utils/progress.go deleted file mode 100644 index 34d03a61c..000000000 --- a/pkg/deployment/utils/progress.go +++ /dev/null @@ -1,126 +0,0 @@ -package utils - -import ( - "context" - "fmt" - "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/mattn/go-isatty" - "github.com/vbauerster/mpb/v7" - "github.com/vbauerster/mpb/v7/decor" - "math" - "os" - "strings" - "sync" - "time" -) - -type progressCtx struct { - ctx context.Context - bar *mpb.Bar - doLog bool - total int64 - name string - status string - startTime time.Time - mutex sync.Mutex -} - -func NewProgressCtx(ctx context.Context, p *mpb.Progress, name string, maxNameWidth int, doLog bool) *progressCtx { - pctx := &progressCtx{ - ctx: ctx, - status: "Initializing...", - total: -1, - name: name, - startTime: time.Now(), - } - if !isatty.IsTerminal(os.Stderr.Fd()) || p == nil { - pctx.doLog = doLog - return pctx - } - - name += ":" - if len(name) < maxNameWidth+1 { - name += strings.Repeat(" ", maxNameWidth-len(name)+1) - } - name += " " - - if p != nil { - pctx.bar = p.AddBar(-1, - mpb.BarWidth(40), - mpb.PrependDecorators(decor.Name(name)), - mpb.PrependDecorators(decor.Any(pctx.ElapsedDecorFunc)), - mpb.AppendDecorators(decor.Any(pctx.StatusDecorFunc)), - ) - } - return pctx -} - -func (ctx *progressCtx) Infof(s string, args ...interface{}) { - if ctx.doLog { - status.Info(ctx.ctx, s, args...) - } -} - -func (ctx *progressCtx) Warningf(s string, args ...interface{}) { - if ctx.doLog { - status.Warning(ctx.ctx, s, args...) - } -} - -func (ctx *progressCtx) Debugf(s string, args ...interface{}) { - if ctx.doLog { - status.Trace(ctx.ctx, s, args...) - } -} - -func (ctx *progressCtx) InfofAndStatus(s string, args ...interface{}) { - ctx.Infof(s, args...) - ctx.SetStatus(fmt.Sprintf(s, args...)) -} - -func (ctx *progressCtx) DebugfAndStatus(s string, args ...interface{}) { - ctx.Debugf(s, args...) - ctx.SetStatus(fmt.Sprintf(s, args...)) -} - -func (ctx *progressCtx) SetStatus(s string) { - ctx.mutex.Lock() - defer ctx.mutex.Unlock() - ctx.status = s -} - -func (ctx *progressCtx) StatusDecorFunc(st decor.Statistics) string { - ctx.mutex.Lock() - defer ctx.mutex.Unlock() - return ctx.status -} - -func (ctx *progressCtx) ElapsedDecorFunc(st decor.Statistics) string { - s := fmt.Sprintf("%.3fs", time.Now().Sub(ctx.startTime).Seconds()) - if len(s) < 8 { - s = strings.Repeat(" ", 8-len(s)) + s - } - return s -} - -func (ctx *progressCtx) SetTotal(total int64) { - ctx.total = total - if ctx.bar != nil { - ctx.bar.SetTotal(total, false) - ctx.bar.EnableTriggerComplete() - } -} - -func (ctx *progressCtx) Increment() { - if ctx.bar != nil { - ctx.bar.Increment() - } -} - -func (ctx *progressCtx) Finish() { - if ctx.bar != nil { - // make sure that the bar es rendered on top so that it can be properly popped - ctx.bar.SetPriority(math.MinInt) - ctx.bar.SetCurrent(ctx.total) - } -} diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index 65ddf867e..ea2c3339a 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -33,7 +33,8 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st return nil } - status.Info(u.ctx, "Getting remote objects by commonLabels") + s := status.Start(u.ctx, "Getting remote objects by commonLabels") + defer s.Failed() allObjects, apiWarnings, err := k.ListAllObjects([]string{"get"}, "", labels, false) for gvk, aw := range apiWarnings { @@ -61,7 +62,7 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st u.mutex.Unlock() if len(notFoundRefsList) != 0 { - status.Info(u.ctx, "Getting %d additional remote objects", len(notFoundRefsList)) + s.UpdateAndInfoFallback("Getting %d additional remote objects", len(notFoundRefsList)) r, apiWarnings, err := k.GetObjectsByRefs(notFoundRefsList) for ref, aw := range apiWarnings { u.dew.AddApiWarnings(ref, aw) @@ -76,7 +77,7 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st u.mutex.Unlock() } - status.Info(u.ctx, "Getting namespaces") + s.UpdateAndInfoFallback("Getting namespaces") r, _, err := k.ListObjects(schema.GroupVersionKind{ Group: "", Version: "v1", @@ -88,6 +89,9 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st for _, o := range r { u.remoteNamespaces[o.GetK8sName()] = o } + + s.Success() + return nil } diff --git a/pkg/status/status.go b/pkg/status/status.go index a9b8b4df6..41914412b 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -12,15 +12,24 @@ type StatusContext struct { sl StatusLine finished bool failed bool + + prefix string + startMessage string + startPriority int + startTotal int + disableLogs bool } type StatusLine interface { + SetTotal(total int) + Increment() + Update(message string) End(success bool) } type StatusHandler interface { - StartStatus(message string) StatusLine + StartStatus(total int, message string) StatusLine Info(message string) Warning(message string) @@ -28,6 +37,7 @@ type StatusHandler interface { Trace(message string) PlainText(text string) + InfoFallback(sprintf string) } type contextKey struct{} @@ -44,23 +54,109 @@ func FromContext(ctx context.Context) StatusHandler { return v.(StatusHandler) } -func Start(ctx context.Context, status string, args ...any) *StatusContext { +type Option func(s *StatusContext) + +func WithPrefix(prefix string) Option { + return func(s *StatusContext) { + s.prefix = prefix + } +} + +func WithStatus(message string, args ...any) Option { + return func(s *StatusContext) { + s.startMessage = fmt.Sprintf(message, args...) + } +} + +func WithPriority(p int) Option { + return func(s *StatusContext) { + s.startPriority = p + } +} + +func WithTotal(t int) Option { + return func(s *StatusContext) { + s.startTotal = t + } +} + +func WithDisableLogs() Option { + return func(s *StatusContext) { + s.disableLogs = true + } +} + +func StartWithOptions(ctx context.Context, opts ...Option) *StatusContext { sh := FromContext(ctx) s := &StatusContext{ ctx: ctx, sh: sh, } - s.sl = sh.StartStatus(fmt.Sprintf(status, args...)) + for _, o := range opts { + o(s) + } + + s.sl = sh.StartStatus(s.startTotal, s.buildMessage(s.startMessage)) return s } +func Start(ctx context.Context, status string, args ...any) *StatusContext { + return StartWithOptions(ctx, + WithTotal(1), + WithStatus(status, args...), + ) +} + +func (s *StatusContext) buildMessage(message string, args ...any) string { + m := fmt.Sprintf(message, args...) + if s.prefix == "" { + return m + } + return fmt.Sprintf("%s: %s", s.prefix, m) +} + +func (s *StatusContext) SetTotal(total int) { + if s == nil { + return + } + s.sl.SetTotal(total) +} + +func (s *StatusContext) Increment() { + if s == nil { + return + } + s.sl.Increment() +} + func (s *StatusContext) Update(message string, args ...any) { - s.sl.Update(fmt.Sprintf(message, args...)) + if s == nil { + return + } + s.sl.Update(s.buildMessage(message, args...)) +} + +func (s *StatusContext) InfoFallback(message string, args ...any) { + if s == nil { + return + } + InfoFallback(s.ctx, s.buildMessage(message, args...)) +} + +func (s *StatusContext) UpdateAndInfoFallback(message string, args ...any) { + if s == nil { + return + } + s.Update(message, args...) + s.InfoFallback(message, args...) } func (s *StatusContext) Failed() { + if s == nil { + return + } if s.finished { return } @@ -69,6 +165,9 @@ func (s *StatusContext) Failed() { } func (s *StatusContext) FailedWithMessage(msg string, args ...any) { + if s == nil { + return + } if s.finished { return } @@ -77,6 +176,9 @@ func (s *StatusContext) FailedWithMessage(msg string, args ...any) { } func (s *StatusContext) Success() { + if s == nil { + return + } s.sl.End(true) s.finished = true } @@ -91,6 +193,11 @@ func Info(ctx context.Context, status string, args ...any) { slh.Info(fmt.Sprintf(status, args...)) } +func InfoFallback(ctx context.Context, status string, args ...any) { + slh := FromContext(ctx) + slh.InfoFallback(fmt.Sprintf(status, args...)) +} + func Warning(ctx context.Context, status string, args ...any) { slh := FromContext(ctx) slh.Warning(fmt.Sprintf(status, args...)) diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index 7f6bd0c39..0443aeec4 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -2,7 +2,6 @@ package status import ( "context" - "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/vbauerster/mpb/v7" "github.com/vbauerster/mpb/v7/decor" @@ -19,12 +18,12 @@ type MultiLineStatusHandler struct { type statusLine struct { slh *MultiLineStatusHandler + total int bar *mpb.Bar filler mpb.BarFiller message string - finished bool - success bool + barOverride string } func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, trace bool) *MultiLineStatusHandler { @@ -44,18 +43,29 @@ func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, trace bool) * return sh } -func (s *MultiLineStatusHandler) StartStatus(message string) StatusLine { +func (s *MultiLineStatusHandler) StartStatus(total int, message string) StatusLine { + return s.startStatus(total, message, 0, "") +} + +func (s *MultiLineStatusHandler) startStatus(total int, message string, priority int, barOverride string) *statusLine { sl := &statusLine{ - slh: s, - message: message, + slh: s, + total: total, + message: message, + barOverride: barOverride, } sl.filler = mpb.SpinnerStyle().PositionLeft().Build() - sl.bar = s.progress.Add(1, sl, + + opts := []mpb.BarOption{ mpb.BarWidth(1), mpb.AppendDecorators(decor.Any(sl.DecorMessage, decor.WCSyncWidthR)), - ) + } + if priority != 0 { + opts = append(opts, mpb.BarPriority(priority)) + } + + sl.bar = s.progress.Add(int64(total), sl, opts...) - s.writeLog(sl.message) return sl } @@ -64,50 +74,65 @@ func (sl *statusLine) DecorMessage(s decor.Statistics) string { } func (sl *statusLine) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { - if !sl.finished { - sl.filler.Fill(w, reqWidth, stat) - } else if sl.success { - fmt.Fprintf(w, "✓") - } else { - fmt.Fprintf(w, "✗") + if sl.barOverride != "" { + _, _ = io.WriteString(w, sl.barOverride) + return } -} -func (s *MultiLineStatusHandler) writeLog(message string) { - //_, _ = fmt.Fprintf(s.out, "%s\n", message) + sl.filler.Fill(w, reqWidth, stat) } func (s *MultiLineStatusHandler) Info(message string) { - s.writeLog(message) + s.startStatus(1, message, math.MinInt, "ⓘ").end("ⓘ") +} + +func (s *MultiLineStatusHandler) InfoFallback(message string) { + // no fallback needed } func (s *MultiLineStatusHandler) Warning(message string) { - s.writeLog(message) + s.startStatus(1, message, math.MinInt, "⚠").end("⚠") } func (s *MultiLineStatusHandler) Error(message string) { - s.writeLog(message) + s.startStatus(1, message, math.MinInt, "✗").end("✗") } func (s *MultiLineStatusHandler) Trace(message string) { if s.trace { - s.writeLog(message) + s.Info(message) } } func (s *MultiLineStatusHandler) PlainText(text string) { - s.writeLog(text) + s.Info(text) +} + +func (sl *statusLine) SetTotal(total int) { + sl.total = total + sl.bar.SetTotal(int64(total), false) + sl.bar.EnableTriggerComplete() +} + +func (sl *statusLine) Increment() { + sl.bar.Increment() } func (sl *statusLine) Update(message string) { sl.message = message - sl.slh.writeLog(sl.message) } -func (sl *statusLine) End(success bool) { - sl.finished = true - sl.success = success +func (sl *statusLine) end(barOverride string) { + sl.barOverride = barOverride // make sure that the bar es rendered on top so that it can be properly popped sl.bar.SetPriority(math.MinInt) - sl.bar.Increment() + sl.bar.SetCurrent(int64(sl.total)) +} + +func (sl *statusLine) End(success bool) { + if success { + sl.end("✓") + } else { + sl.end("✗") + } } From cbe9e577718e6d22b24353de08a582ccafe5b288 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 16:39:09 +0200 Subject: [PATCH 0134/2268] fix: Propery stop status reporting before exit --- cmd/kluctl/commands/root.go | 4 ++++ pkg/status/status.go | 1 + pkg/status/status_handler.go | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 795c5451c..da3ab1d28 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -186,6 +186,10 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif initViper() err = rootCmd.Execute() + + sh := status.FromContext(cliCtx) + sh.Stop() + if err != nil { status.Error(cliCtx, err.Error()) os.Exit(1) diff --git a/pkg/status/status.go b/pkg/status/status.go index 41914412b..734e233d0 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -29,6 +29,7 @@ type StatusLine interface { } type StatusHandler interface { + Stop() StartStatus(total int, message string) StatusLine Info(message string) diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index 0443aeec4..1f8c89bdc 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -43,6 +43,10 @@ func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, trace bool) * return sh } +func (s *MultiLineStatusHandler) Stop() { + s.progress.Wait() +} + func (s *MultiLineStatusHandler) StartStatus(total int, message string) StatusLine { return s.startStatus(total, message, 0, "") } From dce7349822c44be68fcc0dc2879bd96b16873211 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 16:40:03 +0200 Subject: [PATCH 0135/2268] feat: Support colors in status reports --- pkg/deployment/utils/apply_utils.go | 2 +- pkg/status/status.go | 22 +++++++++++++++++++--- pkg/status/status_handler.go | 22 ++++++++++++++-------- pkg/status/utils.go | 13 +++++++++++++ 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index a93028eae..581245aef 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -477,7 +477,7 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { a.sctx.SetTotal(total) if !d.CheckInclusionForDeploy() { a.sctx.UpdateAndInfoFallback("Skipped") - a.sctx.Success() + a.sctx.Warning() return } diff --git a/pkg/status/status.go b/pkg/status/status.go index 734e233d0..4a49fa72c 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -20,12 +20,20 @@ type StatusContext struct { disableLogs bool } +type EndResult int + +const ( + EndSuccess EndResult = iota + EndWarning + EndError +) + type StatusLine interface { SetTotal(total int) Increment() Update(message string) - End(success bool) + End(result EndResult) } type StatusHandler interface { @@ -161,7 +169,7 @@ func (s *StatusContext) Failed() { if s.finished { return } - s.sl.End(false) + s.sl.End(EndError) s.failed = true } @@ -180,7 +188,15 @@ func (s *StatusContext) Success() { if s == nil { return } - s.sl.End(true) + s.sl.End(EndSuccess) + s.finished = true +} + +func (s *StatusContext) Warning() { + if s == nil { + return + } + s.sl.End(EndWarning) s.finished = true } diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index 1f8c89bdc..a1bd50036 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -87,7 +87,8 @@ func (sl *statusLine) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { } func (s *MultiLineStatusHandler) Info(message string) { - s.startStatus(1, message, math.MinInt, "ⓘ").end("ⓘ") + o := withColor("green", "ⓘ") + s.startStatus(1, message, math.MinInt, o).end(o) } func (s *MultiLineStatusHandler) InfoFallback(message string) { @@ -95,11 +96,13 @@ func (s *MultiLineStatusHandler) InfoFallback(message string) { } func (s *MultiLineStatusHandler) Warning(message string) { - s.startStatus(1, message, math.MinInt, "⚠").end("⚠") + o := withColor("yellow", "⚠") + s.startStatus(1, message, math.MinInt, o).end(o) } func (s *MultiLineStatusHandler) Error(message string) { - s.startStatus(1, message, math.MinInt, "✗").end("✗") + o := withColor("red", "✗") + s.startStatus(1, message, math.MinInt, o).end(o) } func (s *MultiLineStatusHandler) Trace(message string) { @@ -133,10 +136,13 @@ func (sl *statusLine) end(barOverride string) { sl.bar.SetCurrent(int64(sl.total)) } -func (sl *statusLine) End(success bool) { - if success { - sl.end("✓") - } else { - sl.end("✗") +func (sl *statusLine) End(result EndResult) { + switch result { + case EndSuccess: + sl.end(withColor("green", "✓")) + case EndWarning: + sl.end(withColor("yellow", "⚠")) + case EndError: + sl.end(withColor("red", "✗")) } } diff --git a/pkg/status/utils.go b/pkg/status/utils.go index b6d6fbb64..0a2edfc82 100644 --- a/pkg/status/utils.go +++ b/pkg/status/utils.go @@ -2,6 +2,7 @@ package status import ( "bufio" + "fmt" "io" ) @@ -63,3 +64,15 @@ func (r *replaceRReader) Read(p []byte) (int, error) { } return written, nil } + +func withColor(c string, s string) string { + switch c { + case "red": + c = "\x1b[31m" + case "green": + c = "\x1b[32m" + case "yellow": + c = "\x1b[33m" + } + return fmt.Sprintf("%s%s\x1b[0m", c, s) +} From b7f22b91800b5262a3205362eae0da6634caefdf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 17:03:49 +0200 Subject: [PATCH 0136/2268] feat: Implement simple status reporter for non-terminal sessions --- cmd/kluctl/commands/root.go | 8 ++- pkg/deployment/utils/apply_utils.go | 4 +- pkg/deployment/utils/diff_utils_test.go | 3 +- pkg/status/simple_status_handler.go | 75 +++++++++++++++++++++++++ pkg/status/status.go | 5 +- 5 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 pkg/status/simple_status_handler.go diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index da3ab1d28..306ad2f4d 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -24,6 +24,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/version" "github.com/kluctl/kluctl/v2/pkg/yaml" + "github.com/mattn/go-isatty" "github.com/spf13/cobra" "github.com/spf13/viper" "k8s.io/klog/v2" @@ -70,7 +71,12 @@ var flagGroups = []groupInfo{ var cliCtx = context.Background() func (c *cli) setupStatusHandler() error { - sh := status.NewMultiLineStatusHandler(cliCtx, os.Stderr, c.Debug) + var sh status.StatusHandler + if isatty.IsTerminal(os.Stderr.Fd()) { + sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, c.Debug) + } else { + sh = status.NewSimpleStatusHandler(os.Stderr, c.Debug, "") + } cliCtx = status.NewContext(cliCtx, sh) klog.LogToStderr(false) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 581245aef..e72211935 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -594,7 +594,6 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { sctx = status.StartWithOptions(a.ctx, status.WithTotal(-1), status.WithPrefix(*progressName), - status.WithStatus("Initializing..."), ) } a2 := a.NewApplyUtil(a.ctx, sctx) @@ -612,8 +611,7 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { barrier := (d.Config.Barrier != nil && *d.Config.Barrier) || d.Barrier if barrier { - sctx := status.Start(a.ctx, "", status.WithTotal(1)) - sctx.UpdateAndInfoFallback("Waiting on barrier...") + sctx := status.StartWithOptions(a.ctx, status.WithStatus("Waiting on barrier..."), status.WithTotal(1)) wg.Wait() sctx.UpdateAndInfoFallback(fmt.Sprintf("Finished waiting")) sctx.Success() diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index bb494fcb4..0e1aa9d81 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -1,6 +1,7 @@ package utils import ( + "context" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" @@ -22,7 +23,7 @@ type diffTestConfig struct { } func (dtc *diffTestConfig) newRemoteObjects(dew *DeploymentErrorsAndWarnings) *RemoteObjectUtils { - ru := NewRemoteObjectsUtil(dew) + ru := NewRemoteObjectsUtil(context.TODO(), dew) for _, ro := range dtc.ro { ru.remoteObjects[ro.GetK8sRef()] = ro } diff --git a/pkg/status/simple_status_handler.go b/pkg/status/simple_status_handler.go new file mode 100644 index 000000000..9cd40fa59 --- /dev/null +++ b/pkg/status/simple_status_handler.go @@ -0,0 +1,75 @@ +package status + +import ( + "fmt" + "io" +) + +type simpleStatusHandler struct { + out io.Writer + trace bool + prefix string +} + +type simpleStatusLine struct { +} + +func NewSimpleStatusHandler(out io.Writer, trace bool, prefix string) StatusHandler { + return &simpleStatusHandler{ + out: out, + trace: trace, + prefix: prefix, + } +} + +func (s *simpleStatusHandler) Stop() { +} + +func (s *simpleStatusHandler) StartStatus(total int, message string) StatusLine { + if message != "" { + s.Info(message) + } + return &simpleStatusLine{} +} + +func (s *simpleStatusHandler) Info(message string) { + if s.prefix == "" { + fmt.Fprintf(s.out, "%s\n", message) + } else { + fmt.Fprintf(s.out, "%s: %s\n", s.prefix, message) + } +} + +func (s *simpleStatusHandler) Warning(message string) { + s.InfoFallback(message) +} + +func (s *simpleStatusHandler) Error(message string) { + s.InfoFallback(message) +} + +func (s *simpleStatusHandler) Trace(message string) { + if s.trace { + s.Info(message) + } +} + +func (s *simpleStatusHandler) PlainText(text string) { + _, _ = io.WriteString(s.out, text) +} + +func (s *simpleStatusHandler) InfoFallback(message string) { + s.Info(message) +} + +func (sl *simpleStatusLine) SetTotal(total int) { +} + +func (sl *simpleStatusLine) Increment() { +} + +func (sl *simpleStatusLine) Update(message string) { +} + +func (sl *simpleStatusLine) End(result EndResult) { +} diff --git a/pkg/status/status.go b/pkg/status/status.go index 4a49fa72c..5219a8fc9 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -46,7 +46,7 @@ type StatusHandler interface { Trace(message string) PlainText(text string) - InfoFallback(sprintf string) + InfoFallback(message string) } type contextKey struct{} @@ -119,6 +119,9 @@ func Start(ctx context.Context, status string, args ...any) *StatusContext { } func (s *StatusContext) buildMessage(message string, args ...any) string { + if message == "" { + return "" + } m := fmt.Sprintf(message, args...) if s.prefix == "" { return m From e45a8191f96cd442684a8dd3e5c4e4e18b94c5f4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 17:25:35 +0200 Subject: [PATCH 0137/2268] fix: Fix invalid call to status.Warning --- cmd/kluctl/commands/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index f0ee358a7..30395bc61 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -51,7 +51,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b repoRoot, err := git.DetectGitRepositoryRoot(cwd) if err != nil && projectFlags.FromArchive == "" { - status.Warning(cliCtx, "", "Failed to detect git project root. This might cause follow-up errors") + status.Warning(cliCtx, "Failed to detect git project root. This might cause follow-up errors") } loadArgs := kluctl_project.LoadKluctlProjectArgs{ From f29b38f53817381644e9d94e7399531b361ec689 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 17:34:03 +0200 Subject: [PATCH 0138/2268] refactor: Pass callback instead of io.Writer to simpleStatusHandler --- cmd/kluctl/commands/root.go | 4 +++- pkg/status/simple_status_handler.go | 25 +++++++------------------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 306ad2f4d..be41c6de7 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -75,7 +75,9 @@ func (c *cli) setupStatusHandler() error { if isatty.IsTerminal(os.Stderr.Fd()) { sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, c.Debug) } else { - sh = status.NewSimpleStatusHandler(os.Stderr, c.Debug, "") + sh = status.NewSimpleStatusHandler(func(message string) { + _, _ = fmt.Fprintf(os.Stderr, "%s\n", message) + }, c.Debug) } cliCtx = status.NewContext(cliCtx, sh) diff --git a/pkg/status/simple_status_handler.go b/pkg/status/simple_status_handler.go index 9cd40fa59..8c15e1626 100644 --- a/pkg/status/simple_status_handler.go +++ b/pkg/status/simple_status_handler.go @@ -1,24 +1,17 @@ package status -import ( - "fmt" - "io" -) - type simpleStatusHandler struct { - out io.Writer - trace bool - prefix string + cb func(message string) + trace bool } type simpleStatusLine struct { } -func NewSimpleStatusHandler(out io.Writer, trace bool, prefix string) StatusHandler { +func NewSimpleStatusHandler(cb func(message string), trace bool) StatusHandler { return &simpleStatusHandler{ - out: out, - trace: trace, - prefix: prefix, + cb: cb, + trace: trace, } } @@ -33,11 +26,7 @@ func (s *simpleStatusHandler) StartStatus(total int, message string) StatusLine } func (s *simpleStatusHandler) Info(message string) { - if s.prefix == "" { - fmt.Fprintf(s.out, "%s\n", message) - } else { - fmt.Fprintf(s.out, "%s: %s\n", s.prefix, message) - } + s.cb(message) } func (s *simpleStatusHandler) Warning(message string) { @@ -55,7 +44,7 @@ func (s *simpleStatusHandler) Trace(message string) { } func (s *simpleStatusHandler) PlainText(text string) { - _, _ = io.WriteString(s.out, text) + s.Info(text) } func (s *simpleStatusHandler) InfoFallback(message string) { From 380548c8daf6e8457acac27f14afaf64b98f9b36 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 17:40:13 +0200 Subject: [PATCH 0139/2268] tests: Fix windows tests --- e2e/project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/project.go b/e2e/project.go index c19bd3b67..6068a7ceb 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -520,7 +520,7 @@ func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { sep := ":" if runtime.GOOS == "windows" { sep = ";" - args = append(args, "-vdebug") + args = append(args, "--debug") } env := os.Environ() env = append(env, fmt.Sprintf("KUBECONFIG=%s", strings.Join(p.kubeconfigs, sep))) From 35f752cdd46a5a908096b2ab1ff8be7b0e2532f5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 9 May 2022 17:54:19 +0200 Subject: [PATCH 0140/2268] fix: Fix crash due to reuse of status handler --- cmd/kluctl/commands/root.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index be41c6de7..491bff9fa 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -196,10 +196,11 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif err = rootCmd.Execute() sh := status.FromContext(cliCtx) - sh.Stop() if err != nil { status.Error(cliCtx, err.Error()) + sh.Stop() os.Exit(1) } + sh.Stop() } From e21c0b78e58b1b21264e16e2a3c4a4b96da7b0b1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 11:43:16 +0200 Subject: [PATCH 0141/2268] fix: Fix and enforce Run() implementation in commands --- cmd/kluctl/commands/cmd_helm_pull.go | 7 +++---- cmd/kluctl/commands/cmd_helm_update.go | 7 +++---- cmd/kluctl/commands/cobra_utils.go | 9 +++++---- cmd/kluctl/commands/root.go | 4 ++++ 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 6d5d5b6fe..5739b54ed 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -1,7 +1,6 @@ package commands import ( - "context" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/status" "io/fs" @@ -18,7 +17,7 @@ func (cmd *helmPullCmd) Help() string { pulling is only needed when really required (e.g. when the chart version changes).` } -func (cmd *helmPullCmd) Run(ctx context.Context) error { +func (cmd *helmPullCmd) Run() error { rootPath := "." if cmd.LocalDeployment != "" { rootPath = cmd.LocalDeployment @@ -26,13 +25,13 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { err := filepath.WalkDir(rootPath, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { - s := status.Start(ctx, "Pulling for %s", p) + s := status.Start(cliCtx, "Pulling for %s", p) chart, err := deployment.NewHelmChart(p) if err != nil { s.FailedWithMessage(err.Error()) return err } - err = chart.Pull(ctx) + err = chart.Pull(cliCtx) if err != nil { s.FailedWithMessage(err.Error()) return err diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index b9f9d8caf..2d9f7e5f6 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -1,7 +1,6 @@ package commands import ( - "context" "fmt" "github.com/go-git/go-git/v5" "github.com/kluctl/kluctl/v2/pkg/deployment" @@ -21,7 +20,7 @@ func (cmd *helmUpdateCmd) Help() string { return `Optionally performs the actual upgrade and/or add a commit to version control.` } -func (cmd *helmUpdateCmd) Run(ctx context.Context) error { +func (cmd *helmUpdateCmd) Run() error { rootPath := "." if cmd.LocalDeployment != "" { rootPath = cmd.LocalDeployment @@ -40,7 +39,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { } statusPrefix := filepath.Base(p) - s := status.Start(ctx, "%s: Checking for updates", statusPrefix) + s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) defer s.Failed() newVersion, updated, err := chart.CheckUpdate() @@ -85,7 +84,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { s.Update("%s: Pulling new version", statusPrefix) defer s.Failed() - err = chart.Pull(ctx) + err = chart.Pull(cliCtx) if err != nil { return err } diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index c45c33f14..493b15605 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -65,10 +65,11 @@ func (c *rootCommand) buildCobraCmd(parent *commandAndGroups, cmdStruct interfac } runP, ok := cmdStruct.(runProvider) - if ok { - cg.cmd.RunE = func(cmd *cobra.Command, args []string) error { - return runP.Run() - } + if !ok { + panic(fmt.Sprintf("%s does not implement runProvider", name)) + } + cg.cmd.RunE = func(cmd *cobra.Command, args []string) error { + return runP.Run() } err := c.buildCobraSubCommands(cg, cmdStruct) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 491bff9fa..f5c40918b 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -151,6 +151,10 @@ func (c *cli) preRun() error { return nil } +func (c *cli) Run() error { + return nil +} + func initViper() { viper.SetConfigName("config") viper.SetConfigType("yaml") From 930a64a4e6cac108d85496491ffd5fac1fbd18df Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 11:43:51 +0200 Subject: [PATCH 0142/2268] fix: Always initialize status handler even if no command is run --- cmd/kluctl/commands/root.go | 15 ++++++--------- pkg/status/simple_status_handler.go | 4 ++++ pkg/status/status.go | 1 + pkg/status/status_handler.go | 4 ++++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index f5c40918b..dad3ae8bf 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -70,21 +70,19 @@ var flagGroups = []groupInfo{ var cliCtx = context.Background() -func (c *cli) setupStatusHandler() error { +func setupStatusHandler() { var sh status.StatusHandler if isatty.IsTerminal(os.Stderr.Fd()) { - sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, c.Debug) + sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, false) } else { sh = status.NewSimpleStatusHandler(func(message string) { _, _ = fmt.Fprintf(os.Stderr, "%s\n", message) - }, c.Debug) + }, false) } cliCtx = status.NewContext(cliCtx, sh) klog.LogToStderr(false) klog.SetOutput(status.NewLineRedirector(sh.Info)) - - return nil } type VersionCheckState struct { @@ -144,9 +142,7 @@ func (c *cli) checkNewVersion() { } func (c *cli) preRun() error { - if err := c.setupStatusHandler(); err != nil { - return err - } + status.FromContext(cliCtx).SetTrace(c.Debug) c.checkNewVersion() return nil } @@ -195,9 +191,10 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif return root.preRun() } + setupStatusHandler() initViper() - err = rootCmd.Execute() + err = rootCmd.ExecuteContext(cliCtx) sh := status.FromContext(cliCtx) diff --git a/pkg/status/simple_status_handler.go b/pkg/status/simple_status_handler.go index 8c15e1626..21a55a6ba 100644 --- a/pkg/status/simple_status_handler.go +++ b/pkg/status/simple_status_handler.go @@ -15,6 +15,10 @@ func NewSimpleStatusHandler(cb func(message string), trace bool) StatusHandler { } } +func (s *simpleStatusHandler) SetTrace(trace bool) { + s.trace = trace +} + func (s *simpleStatusHandler) Stop() { } diff --git a/pkg/status/status.go b/pkg/status/status.go index 5219a8fc9..d58c774f2 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -37,6 +37,7 @@ type StatusLine interface { } type StatusHandler interface { + SetTrace(trace bool) Stop() StartStatus(total int, message string) StatusLine diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index a1bd50036..c0205333a 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -43,6 +43,10 @@ func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, trace bool) * return sh } +func (s *MultiLineStatusHandler) SetTrace(trace bool) { + s.trace = trace +} + func (s *MultiLineStatusHandler) Stop() { s.progress.Wait() } From 64a316a81213c0502c82890e8c162660bc5ab1f3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 13:41:13 +0200 Subject: [PATCH 0143/2268] refactor: Move clientPool initialization into own function --- pkg/k8s/k8s_cluster.go | 62 +++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index b6f4939b6..b0b40d311 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -80,6 +80,8 @@ func (p *parallelClientEntry) HandleWarningHeader(code int, agent string, text s }) } +const parallelClients = 16 + func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8sCluster, error) { k := &K8sCluster{ ctx: ctx, @@ -101,7 +103,39 @@ func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8 } k.discovery = discovery2 - parallelClients := 16 + err = k.initClientPool() + if err != nil { + return nil, err + } + + v, err := k.discovery.ServerVersion() + if err != nil { + return nil, err + } + v2, err := goversion.NewVersion(v.String()) + if err != nil { + return nil, err + } + k.ServerVersion = v2 + + err = k.updateResources(true) + if err != nil { + return nil, err + } + + return k, nil +} + +func (k *K8sCluster) initClientPool() error { + var err error + + if k.clientPool != nil { + for i := 0; i < parallelClients; i++ { + p := <-k.clientPool + p.http.CloseIdleConnections() + } + } + k.clientPool = make(chan *parallelClientEntry, parallelClients) for i := 0; i < parallelClients; i++ { p := ¶llelClientEntry{} @@ -110,43 +144,27 @@ func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8 p.http, err = rest.HTTPClientFor(config) if err != nil { - return nil, err + return err } p.corev1, err = corev1.NewForConfigAndClient(config, p.http) if err != nil { - return nil, err + return err } p.dynamicClient, err = dynamic.NewForConfigAndClient(config, p.http) if err != nil { - return nil, err + return err } p.metadataClient, err = metadata.NewForConfigAndClient(config, p.http) if err != nil { - return nil, err + return err } k.clientPool <- p } - - v, err := k.discovery.ServerVersion() - if err != nil { - return nil, err - } - v2, err := goversion.NewVersion(v.String()) - if err != nil { - return nil, err - } - k.ServerVersion = v2 - - err = k.updateResources(true) - if err != nil { - return nil, err - } - - return k, nil + return nil } func (k *K8sCluster) ReadWrite() *K8sCluster { From 393fa8f934ea3a40a9855f3749c0309cfeb8442f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 13:42:33 +0200 Subject: [PATCH 0144/2268] fix: Take CRDs into account when checking wether an object is namespaced --- pkg/deployment/deployment_collection.go | 31 +++++++++++++-- pkg/deployment/deployment_item.go | 52 +++++++++++++++++++++++-- pkg/k8s/k8s_cluster.go | 30 +++++++++----- 3 files changed, 96 insertions(+), 17 deletions(-) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index fc6d76ef8..def63872c 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -166,6 +166,12 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { var mutex sync.Mutex sem := semaphore.NewWeighted(16) + handleError := func(err error) { + mutex.Lock() + errs = append(errs, err) + mutex.Unlock() + } + s := status.Start(c.ctx, "Building kustomize objects") for _, d_ := range c.Deployments { d := d_ @@ -174,9 +180,7 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { go func() { err := d.buildKustomize() if err != nil { - mutex.Lock() - errs = append(errs, fmt.Errorf("building kustomize objects for %s failed. %w", *d.dir, err)) - mutex.Unlock() + handleError(fmt.Errorf("building kustomize objects for %s failed. %w", *d.dir, err)) } wg.Done() }() @@ -190,6 +194,25 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { s.Success() s = status.Start(c.ctx, "Postprocessing objects") + for _, d_ := range c.Deployments { + d := d_ + + wg.Add(1) + go func() { + err := d.loadObjects() + if err != nil { + handleError(fmt.Errorf("loading objects failed: %w", err)) + } else { + err = d.postprocessCRDs(k) + if err != nil { + handleError(fmt.Errorf("postprocessing CRDs failed: %w", err)) + } + } + wg.Done() + }() + } + wg.Wait() + for _, d_ := range c.Deployments { d := d_ @@ -198,7 +221,7 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { _ = sem.Acquire(context.Background(), 1) defer sem.Release(1) - err := d.postprocessAndLoadObjects(k, c.Images) + err := d.postprocessObjects(k, c.Images) if err != nil { mutex.Lock() errs = append(errs, fmt.Errorf("postprocessing kustomize objects for %s failed: %w", *d.dir, err)) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 1904a8842..c3f41e749 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -10,6 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" "io/fs" "io/ioutil" + "k8s.io/apimachinery/pkg/runtime/schema" "os" "path/filepath" "sigs.k8s.io/kustomize/api/filesys" @@ -364,7 +365,7 @@ func (di *DeploymentItem) buildKustomize() error { return nil } -func (di *DeploymentItem) postprocessAndLoadObjects(k *k8s.K8sCluster, images *Images) error { +func (di *DeploymentItem) loadObjects() error { if di.dir == nil { return nil } @@ -382,6 +383,49 @@ func (di *DeploymentItem) postprocessAndLoadObjects(k *k8s.K8sCluster, images *I } di.Objects = append(di.Objects, uo.FromMap(m)) } + return nil +} + +var crdGV = schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"} + +// postprocessCRDs will set namespace overrides into the K8sCluster so that future IsNamespaced return the correct +// value even if the CRD is not deployed yet. +func (di *DeploymentItem) postprocessCRDs(k *k8s.K8sCluster) error { + if di.dir == nil { + return nil + } + + for _, o := range di.Objects { + gvk := o.GetK8sGVK() + if gvk.GroupKind() != crdGV { + continue + } + + scope, _, err := o.GetNestedString("spec", "scope") + if err != nil { + return err + } + namespaced := strings.ToLower(scope) == "namespaced" + group, _, err := o.GetNestedString("spec", "group") + if err != nil { + return err + } + kind, _, err := o.GetNestedString("spec", "names", "kind") + if err != nil { + return err + } + + k.SetNamespaced(schema.GroupKind{Group: group, Kind: kind}, namespaced) + } + return nil +} + +func (di *DeploymentItem) postprocessObjects(k *k8s.K8sCluster, images *Images) error { + if di.dir == nil { + return nil + } + + var objects []interface{} var errList []error for _, o := range di.Objects { @@ -398,12 +442,14 @@ func (di *DeploymentItem) postprocessAndLoadObjects(k *k8s.K8sCluster, images *I o.SetK8sAnnotations(uo.CopyMergeStrMap(o.GetK8sAnnotations(), commonAnnotations)) // Resolve image placeholders - err = images.ResolvePlaceholders(k, o, di.relRenderedDir, di.Tags.ListKeys()) + err := images.ResolvePlaceholders(k, o, di.relRenderedDir, di.Tags.ListKeys()) if err != nil { errList = append(errList, err) } return nil }) + + objects = append(objects, o.Object) } if len(errList) != 0 { @@ -411,7 +457,7 @@ func (di *DeploymentItem) postprocessAndLoadObjects(k *k8s.K8sCluster, images *I } // Need to write it back to disk in case it is needed externally - err = yaml.WriteYamlAllFile(di.renderedYamlPath, objects) + err := yaml.WriteYamlAllFile(di.renderedYamlPath, objects) if err != nil { return err } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index b0b40d311..f7a6a8596 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -48,9 +48,10 @@ type K8sCluster struct { ServerVersion *goversion.Version - allResources map[schema.GroupVersionKind]*v1.APIResource - preferredResources map[schema.GroupKind]*v1.APIResource - crdCache map[k8s.ObjectRef]struct { + allResources map[schema.GroupVersionKind]*v1.APIResource + preferredResources map[schema.GroupKind]*v1.APIResource + namespacedResources map[schema.GroupKind]bool + crdCache map[k8s.ObjectRef]struct { crd *uo.UnstructuredObject err error } @@ -84,8 +85,9 @@ const parallelClients = 16 func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8sCluster, error) { k := &K8sCluster{ - ctx: ctx, - DryRun: dryRun, + ctx: ctx, + DryRun: dryRun, + namespacedResources: map[schema.GroupKind]bool{}, } k.restConfig = rest.CopyConfig(configIn) @@ -284,19 +286,27 @@ func (k *K8sCluster) RediscoverResources() error { return k.updateResources(false) } -func (k *K8sCluster) IsNamespaced(gvk schema.GroupVersionKind) bool { +func (k *K8sCluster) SetNamespaced(gv schema.GroupKind, namespaced bool) { k.mutex.Lock() defer k.mutex.Unlock() - r, ok := k.allResources[gvk] + k.namespacedResources[gv] = namespaced +} + +func (k *K8sCluster) IsNamespaced(gv schema.GroupKind) bool { + k.mutex.Lock() + defer k.mutex.Unlock() + + r, ok := k.preferredResources[gv] if !ok { - return false + n, _ := k.namespacedResources[gv] + return n } return r.Namespaced } func (k *K8sCluster) FixNamespace(o *uo.UnstructuredObject, def string) { ref := o.GetK8sRef() - namespaced := k.IsNamespaced(ref.GVK) + namespaced := k.IsNamespaced(ref.GVK.GroupKind()) if !namespaced && ref.Namespace != "" { o.SetK8sNamespace("") } else if namespaced && ref.Namespace == "" { @@ -305,7 +315,7 @@ func (k *K8sCluster) FixNamespace(o *uo.UnstructuredObject, def string) { } func (k *K8sCluster) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { - namespaced := k.IsNamespaced(ref.GVK) + namespaced := k.IsNamespaced(ref.GVK.GroupKind()) if !namespaced && ref.Namespace != "" { ref.Namespace = "" } else if namespaced && ref.Namespace == "" { From 5051001787d076b624c202ea4db2f71420ba0f9f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 14:00:31 +0200 Subject: [PATCH 0145/2268] fix: Redirect log output to status handler --- cmd/kluctl/commands/root.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index dad3ae8bf..56f91a2d6 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -28,6 +28,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "k8s.io/klog/v2" + "log" "net/http" "os" "path/filepath" @@ -83,6 +84,7 @@ func setupStatusHandler() { klog.LogToStderr(false) klog.SetOutput(status.NewLineRedirector(sh.Info)) + log.SetOutput(status.NewLineRedirector(sh.Info)) } type VersionCheckState struct { From e84c83f7024636cd1dc6c1d0ad993c3f33361589 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 14:00:47 +0200 Subject: [PATCH 0146/2268] fix: Fix crash when diffing empty slices --- pkg/diff/diff.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index 99eb13188..3746c3c5f 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -173,14 +173,18 @@ func objectToDiffableString(o interface{}, showType bool) (string, error) { return "", err } if showType { - s += fmt.Sprintf(" (type: %s)", reflect.TypeOf(o).Name()) + t := "" + if o != nil { + t = reflect.TypeOf(o).Name() + } + s += fmt.Sprintf(" (type: %s)", t) } return s, nil } func objectToDiffableStringNoType(o interface{}) (string, error) { if o == nil { - return "null", nil + return "", nil } if o == notPresent { return "", nil From 6a29d878aa0da3094cff1fa4857ed7ba5d98ae60 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 15:07:55 +0200 Subject: [PATCH 0147/2268] refactor: Move out resources related code into own class --- pkg/deployment/deployment_item.go | 4 +- pkg/deployment/helm_chart.go | 4 +- pkg/deployment/utils/apply_utils.go | 4 +- pkg/deployment/utils/delete_utils.go | 4 +- pkg/k8s/k8s_cluster.go | 358 ++------------------------ pkg/k8s/resources.go | 370 +++++++++++++++++++++++++++ pkg/validation/validation.go | 2 +- 7 files changed, 399 insertions(+), 347 deletions(-) create mode 100644 pkg/k8s/resources.go diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index c3f41e749..52c563251 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -415,7 +415,7 @@ func (di *DeploymentItem) postprocessCRDs(k *k8s.K8sCluster) error { return err } - k.SetNamespaced(schema.GroupKind{Group: group, Kind: kind}, namespaced) + k.Resources.SetNamespaced(schema.GroupKind{Group: group, Kind: kind}, namespaced) } return nil } @@ -434,7 +434,7 @@ func (di *DeploymentItem) postprocessObjects(k *k8s.K8sCluster, images *Images) _ = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { if k != nil { - k.FixNamespace(o, "default") + k.Resources.FixNamespace(o, "default") } // Set common labels/annotations diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index eb97d66b1..84aac2481 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -203,7 +203,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { var gvs []string if k != nil { - gvs, err = k.GetAllGroupVersions() + gvs, err = k.Resources.GetAllGroupVersions() if err != nil { return err } @@ -313,7 +313,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { // add the necessary namespace in the rendered resources if k != nil { err = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { - k.FixNamespace(o, namespace) + k.Resources.FixNamespace(o, namespace) return nil }) if err != nil { diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index e72211935..af13ea6d9 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -337,7 +337,7 @@ func (a *ApplyUtil) handleNewCRDs(x *uo.UnstructuredObject, err error) (bool, er // retry the patch if a.deployedNewCRD.Load().(bool) { a.deployedNewCRD.Store(false) - err = a.k.RediscoverResources() + err = a.k.Resources.RediscoverResources() if err != nil { return false, err } @@ -435,7 +435,7 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { var toDelete []k8s2.ObjectRef for _, x := range d.Config.DeleteObjects { - for _, gvk := range a.k.GetGVKs(x.Group, x.Version, x.Kind) { + for _, gvk := range a.k.Resources.GetGVKs(x.Group, x.Version, x.Kind) { ref := k8s2.ObjectRef{ GVK: gvk, Name: x.Name, diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index 655f4a1ca..803b050f1 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -41,14 +41,14 @@ var deleteOrder = [][]string{ } func objectRefForExclusion(k *k8s.K8sCluster, ref k8s2.ObjectRef) k8s2.ObjectRef { - ref = k.FixNamespaceInRef(ref) + ref = k.Resources.FixNamespaceInRef(ref) ref.GVK.Version = "" return ref } func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, apiFilter []string, inclusionHasTags bool, excludedObjects map[k8s2.ObjectRef]bool) ([]*uo.UnstructuredObject, error) { filteredResources := make(map[schema.GroupKind]bool) - for _, gk := range k.GetFilteredGKs(apiFilter) { + for _, gk := range k.Resources.GetFilteredGKs(apiFilter) { filteredResources[gk] = true } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index f7a6a8596..06b4d6615 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -16,17 +16,12 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/discovery" - "k8s.io/client-go/discovery/cached/disk" "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/metadata" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" "net/http" - "net/url" - "path/filepath" - "strings" "sync" "time" ) @@ -43,19 +38,11 @@ type K8sCluster struct { DryRun bool restConfig *rest.Config - discovery *disk.CachedDiscoveryClient clientPool chan *parallelClientEntry ServerVersion *goversion.Version - allResources map[schema.GroupVersionKind]*v1.APIResource - preferredResources map[schema.GroupKind]*v1.APIResource - namespacedResources map[schema.GroupKind]bool - crdCache map[k8s.ObjectRef]struct { - crd *uo.UnstructuredObject - err error - } - mutex sync.Mutex + Resources *k8sResources } type parallelClientEntry struct { @@ -84,33 +71,28 @@ func (p *parallelClientEntry) HandleWarningHeader(code int, agent string, text s const parallelClients = 16 func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8sCluster, error) { - k := &K8sCluster{ - ctx: ctx, - DryRun: dryRun, - namespacedResources: map[schema.GroupKind]bool{}, - } - - k.restConfig = rest.CopyConfig(configIn) - k.restConfig.QPS = 10 - k.restConfig.Burst = 20 + restConfig := rest.CopyConfig(configIn) + restConfig.QPS = 10 + restConfig.Burst = 20 - apiHost, err := url.Parse(k.restConfig.Host) + resources, err := newK8sResources(ctx, restConfig) if err != nil { return nil, err } - discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(), "kube-cache/discovery", apiHost.Hostname()) - discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(k.restConfig), discoveryCacheDir, "", time.Hour*24) - if err != nil { - return nil, err + + k := &K8sCluster{ + ctx: ctx, + DryRun: dryRun, + restConfig: restConfig, + Resources: resources, } - k.discovery = discovery2 err = k.initClientPool() if err != nil { return nil, err } - v, err := k.discovery.ServerVersion() + v, err := k.Resources.discovery.ServerVersion() if err != nil { return nil, err } @@ -120,7 +102,7 @@ func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8 } k.ServerVersion = v2 - err = k.updateResources(true) + err = k.Resources.updateResources(true) if err != nil { return nil, err } @@ -179,299 +161,6 @@ func (k *K8sCluster) GetCA() []byte { return k.restConfig.CAData } -func (k *K8sCluster) updateResources(doLock bool) error { - if doLock { - k.mutex.Lock() - defer k.mutex.Unlock() - } - - k.allResources = map[schema.GroupVersionKind]*v1.APIResource{} - k.preferredResources = map[schema.GroupKind]*v1.APIResource{} - k.crdCache = map[k8s.ObjectRef]struct { - crd *uo.UnstructuredObject - err error - }{} - - // the discovery client doesn't support cancellation, so we need to run it in the background and wait for it - var arls []*v1.APIResourceList - var preferredArls []*v1.APIResourceList - finished := make(chan error) - go func() { - var err error - _, arls, err = k.discovery.ServerGroupsAndResources() - if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { - finished <- err - return - } - preferredArls, err = k.discovery.ServerPreferredResources() - if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { - finished <- err - return - } - finished <- nil - }() - - select { - case err := <-finished: - if err != nil { - return err - } - case <-k.ctx.Done(): - return fmt.Errorf("failed listing api resources: %w", k.ctx.Err()) - } - - for _, arl := range arls { - for _, ar := range arl.APIResources { - if strings.Index(ar.Name, "/") != -1 { - // skip subresources - continue - } - gv, err := schema.ParseGroupVersion(arl.GroupVersion) - if err != nil { - continue - } - - ar := ar - ar.Group = gv.Group - ar.Version = gv.Version - - gvk := schema.GroupVersionKind{ - Group: ar.Group, - Version: ar.Version, - Kind: ar.Kind, - } - if _, ok := deprecatedResources[gvk.GroupKind()]; ok { - continue - } - if _, ok := k.allResources[gvk]; ok { - ok = false - } - k.allResources[gvk] = &ar - } - } - - for _, arl := range preferredArls { - for _, ar := range arl.APIResources { - if strings.Index(ar.Name, "/") != -1 { - // skip subresources - continue - } - gv, err := schema.ParseGroupVersion(arl.GroupVersion) - if err != nil { - continue - } - - ar := ar - ar.Group = gv.Group - ar.Version = gv.Version - - gk := schema.GroupKind{ - Group: ar.Group, - Kind: ar.Kind, - } - if _, ok := deprecatedResources[gk]; ok { - continue - } - k.preferredResources[gk] = &ar - } - } - return nil -} - -func (k *K8sCluster) RediscoverResources() error { - k.mutex.Lock() - defer k.mutex.Unlock() - - k.discovery.Invalidate() - return k.updateResources(false) -} - -func (k *K8sCluster) SetNamespaced(gv schema.GroupKind, namespaced bool) { - k.mutex.Lock() - defer k.mutex.Unlock() - k.namespacedResources[gv] = namespaced -} - -func (k *K8sCluster) IsNamespaced(gv schema.GroupKind) bool { - k.mutex.Lock() - defer k.mutex.Unlock() - - r, ok := k.preferredResources[gv] - if !ok { - n, _ := k.namespacedResources[gv] - return n - } - return r.Namespaced -} - -func (k *K8sCluster) FixNamespace(o *uo.UnstructuredObject, def string) { - ref := o.GetK8sRef() - namespaced := k.IsNamespaced(ref.GVK.GroupKind()) - if !namespaced && ref.Namespace != "" { - o.SetK8sNamespace("") - } else if namespaced && ref.Namespace == "" { - o.SetK8sNamespace(def) - } -} - -func (k *K8sCluster) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { - namespaced := k.IsNamespaced(ref.GVK.GroupKind()) - if !namespaced && ref.Namespace != "" { - ref.Namespace = "" - } else if namespaced && ref.Namespace == "" { - ref.Namespace = "default" - } - return ref -} - -func (k *K8sCluster) GetAllGroupVersions() ([]string, error) { - k.mutex.Lock() - defer k.mutex.Unlock() - - m := make(map[string]bool) - var l []string - - for gvk, _ := range k.allResources { - gv := gvk.GroupVersion().String() - if _, ok := m[gv]; !ok { - m[gv] = true - l = append(l, gv) - } - } - return l, nil -} - -func (k *K8sCluster) GetFilteredGKs(filters []string) []schema.GroupKind { - k.mutex.Lock() - defer k.mutex.Unlock() - - m := make(map[schema.GroupKind]bool) - var l []schema.GroupKind - for gk, ar := range k.preferredResources { - found := len(filters) == 0 - for _, f := range filters { - if ar.Name == f || ar.Group == f || ar.Kind == f { - found = true - break - } - } - if found { - if _, ok := m[gk]; !ok { - m[gk] = true - l = append(l, gk) - } - } - } - return l -} - -func (k *K8sCluster) GetGVKs(group *string, version *string, kind *string) []schema.GroupVersionKind { - k.mutex.Lock() - defer k.mutex.Unlock() - - var ret []schema.GroupVersionKind - for gvk := range k.allResources { - if group != nil && *group != gvk.Group { - continue - } - if version != nil && *version != gvk.Version { - continue - } - if kind != nil && *kind != gvk.Kind { - continue - } - ret = append(ret, gvk) - } - return ret -} - -func (k *K8sCluster) getGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupVersionResource, bool, error) { - k.mutex.Lock() - defer k.mutex.Unlock() - - ar, ok := k.allResources[gvk] - if !ok { - return nil, false, &meta.NoKindMatchError{ - GroupKind: gvk.GroupKind(), - SearchedVersions: []string{gvk.Version}, - } - } - - return &schema.GroupVersionResource{ - Group: ar.Group, - Version: ar.Version, - Resource: ar.Name, - }, ar.Namespaced, nil -} - -func (k *K8sCluster) GetApiResourceForGVK(gvk schema.GroupVersionKind) *v1.APIResource { - k.mutex.Lock() - defer k.mutex.Unlock() - - ar, _ := k.allResources[gvk] - return ar -} - -func (k *K8sCluster) GetCRDForGVK(gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { - ar := k.GetApiResourceForGVK(gvk) - if ar == nil { - return nil, fmt.Errorf("APIResource for %s not found", gvk.String()) - } - - crdRef := k8s.ObjectRef{ - GVK: schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}, - Name: fmt.Sprintf("%s.%s", ar.Name, ar.Group), - } - - k.mutex.Lock() - x, ok := k.crdCache[crdRef] - k.mutex.Unlock() - if ok { - return x.crd, x.err - } - - crd, _, err := k.GetSingleObject(crdRef) - - k.mutex.Lock() - x.crd = crd - x.err = err - k.crdCache[crdRef] = x - k.mutex.Unlock() - - return crd, err -} - -func (k *K8sCluster) GetSchemaForGVK(gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { - crd, err := k.GetCRDForGVK(gvk) - if err != nil { - return nil, err - } - - versions, ok, err := crd.GetNestedObjectList("spec", "versions") - if err != nil { - return nil, err - } - if !ok { - return nil, fmt.Errorf("versions not found in CRD") - } - - for _, version := range versions { - name, _, _ := version.GetNestedString("name") - if name != gvk.Version { - continue - } - s, ok, err := version.GetNestedObject("schema", "openAPIV3Schema") - if err != nil { - return nil, err - } - if !ok { - return nil, fmt.Errorf("version %s has no schema", name) - } - return s, nil - } - return nil, fmt.Errorf("schema for %s not found", gvk.String()) -} - func (k *K8sCluster) withClientFromPool(cb func(p *parallelClientEntry) error) ([]ApiWarning, error) { select { case p := <-k.clientPool: @@ -486,7 +175,7 @@ func (k *K8sCluster) withClientFromPool(cb func(p *parallelClientEntry) error) ( func (k *K8sCluster) withDynamicClientForGVK(gvk schema.GroupVersionKind, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { return k.withClientFromPool(func(p *parallelClientEntry) error { - gvr, namespaced, err := k.getGVRForGVK(gvk) + gvr, namespaced, err := k.Resources.getGVRForGVK(gvk) if err != nil { return err } @@ -501,7 +190,7 @@ func (k *K8sCluster) withDynamicClientForGVK(gvk schema.GroupVersionKind, namesp func (k *K8sCluster) withMetadataClientForGVK(gvk schema.GroupVersionKind, namespace string, cb func(r metadata.ResourceInterface) error) ([]ApiWarning, error) { return k.withClientFromPool(func(p *parallelClientEntry) error { - gvr, namespaced, err := k.getGVRForGVK(gvk) + gvr, namespaced, err := k.Resources.getGVRForGVK(gvk) if err != nil { return err } @@ -581,8 +270,7 @@ func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map retApiWarnings := make(map[schema.GroupVersionKind][]ApiWarning) var mutex sync.Mutex - k.mutex.Lock() - for _, ar := range k.preferredResources { + filter := func(ar *v1.APIResource) bool { foundVerb := false for _, v := range verbs { if utils.FindStrInSlice(ar.Verbs, v) != -1 { @@ -590,14 +278,10 @@ func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map break } } - if !foundVerb { - continue - } - gvk := schema.GroupVersionKind{ - Group: ar.Group, - Version: ar.Version, - Kind: ar.Kind, - } + return foundVerb + } + + for _, gvk := range k.Resources.GetFilteredPreferredGVKs(filter) { wp.Submit(func() error { var l []*uo.UnstructuredObject var apiWarnings []ApiWarning @@ -619,8 +303,6 @@ func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map return nil }) } - // release it early and let the goroutines finish (deadlocking otherwise) - k.mutex.Unlock() err := wp.StopWait(false) if err != nil { diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go new file mode 100644 index 000000000..d671e2048 --- /dev/null +++ b/pkg/k8s/resources.go @@ -0,0 +1,370 @@ +package k8s + +import ( + "context" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "k8s.io/apimachinery/pkg/api/meta" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + "k8s.io/client-go/discovery/cached/disk" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" + "net/url" + "path/filepath" + "strings" + "sync" + "time" +) + +type crdCacheEntry struct { + crd *uo.UnstructuredObject + err error +} + +type k8sResources struct { + ctx context.Context + discovery *disk.CachedDiscoveryClient + + allResources map[schema.GroupVersionKind]*v1.APIResource + preferredResources map[schema.GroupKind]*v1.APIResource + namespacedResources map[schema.GroupKind]bool + crdCache map[k8s.ObjectRef]crdCacheEntry + mutex sync.Mutex +} + +func newK8sResources(ctx context.Context, config *rest.Config) (*k8sResources, error) { + k := &k8sResources{ + ctx: ctx, + allResources: map[schema.GroupVersionKind]*v1.APIResource{}, + preferredResources: map[schema.GroupKind]*v1.APIResource{}, + namespacedResources: map[schema.GroupKind]bool{}, + crdCache: map[k8s.ObjectRef]crdCacheEntry{}, + mutex: sync.Mutex{}, + } + + apiHost, err := url.Parse(config.Host) + if err != nil { + return nil, err + } + discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(), "kube-cache/discovery", apiHost.Hostname()) + discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(config), discoveryCacheDir, "", time.Hour*24) + if err != nil { + return nil, err + } + k.discovery = discovery2 + + return k, nil +} + +func (k *k8sResources) updateResources(doLock bool) error { + if doLock { + k.mutex.Lock() + defer k.mutex.Unlock() + } + + k.allResources = map[schema.GroupVersionKind]*v1.APIResource{} + k.preferredResources = map[schema.GroupKind]*v1.APIResource{} + k.crdCache = map[k8s.ObjectRef]crdCacheEntry{} + + // the discovery client doesn't support cancellation, so we need to run it in the background and wait for it + var arls []*v1.APIResourceList + var preferredArls []*v1.APIResourceList + finished := make(chan error) + go func() { + var err error + _, arls, err = k.discovery.ServerGroupsAndResources() + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { + finished <- err + return + } + preferredArls, err = k.discovery.ServerPreferredResources() + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { + finished <- err + return + } + finished <- nil + }() + + select { + case err := <-finished: + if err != nil { + return err + } + case <-k.ctx.Done(): + return fmt.Errorf("failed listing api resources: %w", k.ctx.Err()) + } + + for _, arl := range arls { + for _, ar := range arl.APIResources { + if strings.Index(ar.Name, "/") != -1 { + // skip subresources + continue + } + gv, err := schema.ParseGroupVersion(arl.GroupVersion) + if err != nil { + continue + } + + ar := ar + ar.Group = gv.Group + ar.Version = gv.Version + + gvk := schema.GroupVersionKind{ + Group: ar.Group, + Version: ar.Version, + Kind: ar.Kind, + } + if _, ok := deprecatedResources[gvk.GroupKind()]; ok { + continue + } + if _, ok := k.allResources[gvk]; ok { + ok = false + } + k.allResources[gvk] = &ar + } + } + + for _, arl := range preferredArls { + for _, ar := range arl.APIResources { + if strings.Index(ar.Name, "/") != -1 { + // skip subresources + continue + } + gv, err := schema.ParseGroupVersion(arl.GroupVersion) + if err != nil { + continue + } + + ar := ar + ar.Group = gv.Group + ar.Version = gv.Version + + gk := schema.GroupKind{ + Group: ar.Group, + Kind: ar.Kind, + } + if _, ok := deprecatedResources[gk]; ok { + continue + } + k.preferredResources[gk] = &ar + } + } + return nil +} + +func (k *k8sResources) RediscoverResources() error { + k.mutex.Lock() + defer k.mutex.Unlock() + + k.discovery.Invalidate() + return k.updateResources(false) +} + +func (k *k8sResources) SetNamespaced(gv schema.GroupKind, namespaced bool) { + k.mutex.Lock() + defer k.mutex.Unlock() + k.namespacedResources[gv] = namespaced +} + +func (k *k8sResources) IsNamespaced(gv schema.GroupKind) bool { + k.mutex.Lock() + defer k.mutex.Unlock() + + r, ok := k.preferredResources[gv] + if !ok { + n, _ := k.namespacedResources[gv] + return n + } + return r.Namespaced +} + +func (k *k8sResources) FixNamespace(o *uo.UnstructuredObject, def string) { + ref := o.GetK8sRef() + namespaced := k.IsNamespaced(ref.GVK.GroupKind()) + if !namespaced && ref.Namespace != "" { + o.SetK8sNamespace("") + } else if namespaced && ref.Namespace == "" { + o.SetK8sNamespace(def) + } +} + +func (k *k8sResources) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { + namespaced := k.IsNamespaced(ref.GVK.GroupKind()) + if !namespaced && ref.Namespace != "" { + ref.Namespace = "" + } else if namespaced && ref.Namespace == "" { + ref.Namespace = "default" + } + return ref +} + +func (k *k8sResources) GetAllGroupVersions() ([]string, error) { + k.mutex.Lock() + defer k.mutex.Unlock() + + m := make(map[string]bool) + var l []string + + for gvk, _ := range k.allResources { + gv := gvk.GroupVersion().String() + if _, ok := m[gv]; !ok { + m[gv] = true + l = append(l, gv) + } + } + return l, nil +} + +func (k *k8sResources) GetFilteredPreferredGVKs(filter func(ar *v1.APIResource) bool) []schema.GroupVersionKind { + k.mutex.Lock() + defer k.mutex.Unlock() + + var ret []schema.GroupVersionKind + for _, ar := range k.preferredResources { + if !filter(ar) { + continue + } + gvk := schema.GroupVersionKind{ + Group: ar.Group, + Version: ar.Version, + Kind: ar.Kind, + } + ret = append(ret, gvk) + } + return ret +} + +func (k *k8sResources) GetFilteredGKs(filters []string) []schema.GroupKind { + k.mutex.Lock() + defer k.mutex.Unlock() + + m := make(map[schema.GroupKind]bool) + var l []schema.GroupKind + for gk, ar := range k.preferredResources { + found := len(filters) == 0 + for _, f := range filters { + if ar.Name == f || ar.Group == f || ar.Kind == f { + found = true + break + } + } + if found { + if _, ok := m[gk]; !ok { + m[gk] = true + l = append(l, gk) + } + } + } + return l +} + +func (k *k8sResources) GetGVKs(group *string, version *string, kind *string) []schema.GroupVersionKind { + k.mutex.Lock() + defer k.mutex.Unlock() + + var ret []schema.GroupVersionKind + for gvk := range k.allResources { + if group != nil && *group != gvk.Group { + continue + } + if version != nil && *version != gvk.Version { + continue + } + if kind != nil && *kind != gvk.Kind { + continue + } + ret = append(ret, gvk) + } + return ret +} + +func (k *k8sResources) getGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupVersionResource, bool, error) { + k.mutex.Lock() + defer k.mutex.Unlock() + + ar, ok := k.allResources[gvk] + if !ok { + return nil, false, &meta.NoKindMatchError{ + GroupKind: gvk.GroupKind(), + SearchedVersions: []string{gvk.Version}, + } + } + + return &schema.GroupVersionResource{ + Group: ar.Group, + Version: ar.Version, + Resource: ar.Name, + }, ar.Namespaced, nil +} + +func (k *k8sResources) GetApiResourceForGVK(gvk schema.GroupVersionKind) *v1.APIResource { + k.mutex.Lock() + defer k.mutex.Unlock() + + ar, _ := k.allResources[gvk] + return ar +} + +func (k *k8sResources) GetCRDForGVK(k2 *K8sCluster, gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { + ar := k.GetApiResourceForGVK(gvk) + if ar == nil { + return nil, fmt.Errorf("APIResource for %s not found", gvk.String()) + } + + crdRef := k8s.ObjectRef{ + GVK: schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}, + Name: fmt.Sprintf("%s.%s", ar.Name, ar.Group), + } + + k.mutex.Lock() + x, ok := k.crdCache[crdRef] + k.mutex.Unlock() + if ok { + return x.crd, x.err + } + + crd, _, err := k2.GetSingleObject(crdRef) + + k.mutex.Lock() + x.crd = crd + x.err = err + k.crdCache[crdRef] = x + k.mutex.Unlock() + + return crd, err +} + +func (k *k8sResources) GetSchemaForGVK(k2 *K8sCluster, gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { + crd, err := k.GetCRDForGVK(k2, gvk) + if err != nil { + return nil, err + } + + versions, ok, err := crd.GetNestedObjectList("spec", "versions") + if err != nil { + return nil, err + } + if !ok { + return nil, fmt.Errorf("versions not found in CRD") + } + + for _, version := range versions { + name, _, _ := version.GetNestedString("name") + if name != gvk.Version { + continue + } + s, ok, err := version.GetNestedObject("schema", "openAPIV3Schema") + if err != nil { + return nil, err + } + if !ok { + return nil, fmt.Errorf("version %s has no schema", name) + } + return s, nil + } + return nil, fmt.Errorf("schema for %s not found", gvk.String()) +} diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 52584cedf..0331074bc 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -117,7 +117,7 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError // can't really say anything... return } - s, err := k.GetSchemaForGVK(ref.GVK) + s, err := k.Resources.GetSchemaForGVK(k, ref.GVK) if err != nil && !errors.IsNotFound(err) { addError(err.Error()) return From 735e2f99d6d0cff8debf44af2e6e28c943acce0e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 15:10:30 +0200 Subject: [PATCH 0148/2268] refactor: Return GroupVersion from GetAllGroupVersions --- pkg/deployment/helm_chart.go | 5 +++-- pkg/k8s/resources.go | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 84aac2481..2e2727fb8 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -22,6 +22,7 @@ import ( "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/repo" "io/ioutil" + "k8s.io/apimachinery/pkg/runtime/schema" "os" "path/filepath" "regexp" @@ -201,7 +202,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { } valuesPath := yaml.FixPathExt(filepath.Join(filepath.Dir(c.configFile), "helm-values.yml")) - var gvs []string + var gvs []schema.GroupVersion if k != nil { gvs, err = k.Resources.GetAllGroupVersions() if err != nil { @@ -245,7 +246,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { client.IncludeCRDs = true } for _, gv := range gvs { - client.APIVersions = append(client.APIVersions, gv) + client.APIVersions = append(client.APIVersions, gv.String()) } p := getter.All(settings) diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index d671e2048..69e9e33ff 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -202,15 +202,15 @@ func (k *k8sResources) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { return ref } -func (k *k8sResources) GetAllGroupVersions() ([]string, error) { +func (k *k8sResources) GetAllGroupVersions() ([]schema.GroupVersion, error) { k.mutex.Lock() defer k.mutex.Unlock() - m := make(map[string]bool) - var l []string + m := make(map[schema.GroupVersion]bool) + var l []schema.GroupVersion for gvk, _ := range k.allResources { - gv := gvk.GroupVersion().String() + gv := gvk.GroupVersion() if _, ok := m[gv]; !ok { m[gv] = true l = append(l, gv) From 0cde1a7bf521e06f558d3e7ed0d8a0b9ca065d62 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 15:13:48 +0200 Subject: [PATCH 0149/2268] refactor: Get rid of GetFilteredGKs and use GetFilteredPreferredGVKs instead --- pkg/deployment/utils/delete_utils.go | 17 +++++++++++++++-- pkg/k8s/resources.go | 24 ------------------------ 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index 803b050f1..b79f93d5f 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -7,6 +7,7 @@ import ( k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "golang.org/x/sync/semaphore" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "strconv" "sync" @@ -47,9 +48,21 @@ func objectRefForExclusion(k *k8s.K8sCluster, ref k8s2.ObjectRef) k8s2.ObjectRef } func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, apiFilter []string, inclusionHasTags bool, excludedObjects map[k8s2.ObjectRef]bool) ([]*uo.UnstructuredObject, error) { + filterFunc := func(ar *v1.APIResource) bool { + if len(apiFilter) == 0 { + return true + } + for _, f := range apiFilter { + if ar.Name == f || ar.Group == f || ar.Kind == f { + return true + } + } + return false + } + filteredResources := make(map[schema.GroupKind]bool) - for _, gk := range k.Resources.GetFilteredGKs(apiFilter) { - filteredResources[gk] = true + for _, gvk := range k.Resources.GetFilteredPreferredGVKs(filterFunc) { + filteredResources[gvk.GroupKind()] = true } var ret []*uo.UnstructuredObject diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 69e9e33ff..39f537f48 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -238,30 +238,6 @@ func (k *k8sResources) GetFilteredPreferredGVKs(filter func(ar *v1.APIResource) return ret } -func (k *k8sResources) GetFilteredGKs(filters []string) []schema.GroupKind { - k.mutex.Lock() - defer k.mutex.Unlock() - - m := make(map[schema.GroupKind]bool) - var l []schema.GroupKind - for gk, ar := range k.preferredResources { - found := len(filters) == 0 - for _, f := range filters { - if ar.Name == f || ar.Group == f || ar.Kind == f { - found = true - break - } - } - if found { - if _, ok := m[gk]; !ok { - m[gk] = true - l = append(l, gk) - } - } - } - return l -} - func (k *k8sResources) GetGVKs(group *string, version *string, kind *string) []schema.GroupVersionKind { k.mutex.Lock() defer k.mutex.Unlock() From 3c096381c7996702a9e0f950d8c42e86f721d4cd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 15:18:55 +0200 Subject: [PATCH 0150/2268] refactor: Get rid of GetGVKs and use GetFilteredGVKs instead --- pkg/deployment/utils/apply_utils.go | 2 +- pkg/k8s/resources.go | 34 ++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index af13ea6d9..12274398d 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -435,7 +435,7 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { var toDelete []k8s2.ObjectRef for _, x := range d.Config.DeleteObjects { - for _, gvk := range a.k.Resources.GetGVKs(x.Group, x.Version, x.Kind) { + for _, gvk := range a.k.Resources.GetFilteredGVKs(k8s.BuildGVKFilter(x.Group, x.Version, x.Kind)) { ref := k8s2.ObjectRef{ GVK: gvk, Name: x.Name, diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 39f537f48..739c18aae 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -219,12 +219,12 @@ func (k *k8sResources) GetAllGroupVersions() ([]schema.GroupVersion, error) { return l, nil } -func (k *k8sResources) GetFilteredPreferredGVKs(filter func(ar *v1.APIResource) bool) []schema.GroupVersionKind { +func (k *k8sResources) GetFilteredGVKs(filter func(ar *v1.APIResource) bool) []schema.GroupVersionKind { k.mutex.Lock() defer k.mutex.Unlock() var ret []schema.GroupVersionKind - for _, ar := range k.preferredResources { + for _, ar := range k.allResources { if !filter(ar) { continue } @@ -238,26 +238,40 @@ func (k *k8sResources) GetFilteredPreferredGVKs(filter func(ar *v1.APIResource) return ret } -func (k *k8sResources) GetGVKs(group *string, version *string, kind *string) []schema.GroupVersionKind { +func (k *k8sResources) GetFilteredPreferredGVKs(filter func(ar *v1.APIResource) bool) []schema.GroupVersionKind { k.mutex.Lock() defer k.mutex.Unlock() var ret []schema.GroupVersionKind - for gvk := range k.allResources { - if group != nil && *group != gvk.Group { - continue - } - if version != nil && *version != gvk.Version { + for _, ar := range k.preferredResources { + if !filter(ar) { continue } - if kind != nil && *kind != gvk.Kind { - continue + gvk := schema.GroupVersionKind{ + Group: ar.Group, + Version: ar.Version, + Kind: ar.Kind, } ret = append(ret, gvk) } return ret } +func BuildGVKFilter(group *string, version *string, kind *string) func(ar *v1.APIResource) bool { + return func(ar *v1.APIResource) bool { + if group != nil && *group != ar.Group { + return false + } + if version != nil && *version != ar.Version { + return false + } + if kind != nil && *kind != ar.Kind { + return false + } + return true + } +} + func (k *k8sResources) getGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupVersionResource, bool, error) { k.mutex.Lock() defer k.mutex.Unlock() From 75d39cee9700d8642e1e0c7667da0645f8aef7b8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 15:26:00 +0200 Subject: [PATCH 0151/2268] refactor: Get rid of GetApiResourceForGVK and use GetGVRForGVK instead --- pkg/k8s/k8s_cluster.go | 4 ++-- pkg/k8s/resources.go | 18 +++++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 06b4d6615..b5fa4a81a 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -175,7 +175,7 @@ func (k *K8sCluster) withClientFromPool(cb func(p *parallelClientEntry) error) ( func (k *K8sCluster) withDynamicClientForGVK(gvk schema.GroupVersionKind, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { return k.withClientFromPool(func(p *parallelClientEntry) error { - gvr, namespaced, err := k.Resources.getGVRForGVK(gvk) + gvr, namespaced, err := k.Resources.GetGVRForGVK(gvk) if err != nil { return err } @@ -190,7 +190,7 @@ func (k *K8sCluster) withDynamicClientForGVK(gvk schema.GroupVersionKind, namesp func (k *K8sCluster) withMetadataClientForGVK(gvk schema.GroupVersionKind, namespace string, cb func(r metadata.ResourceInterface) error) ([]ApiWarning, error) { return k.withClientFromPool(func(p *parallelClientEntry) error { - gvr, namespaced, err := k.Resources.getGVRForGVK(gvk) + gvr, namespaced, err := k.Resources.GetGVRForGVK(gvk) if err != nil { return err } diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 739c18aae..67b98505f 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -272,7 +272,7 @@ func BuildGVKFilter(group *string, version *string, kind *string) func(ar *v1.AP } } -func (k *k8sResources) getGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupVersionResource, bool, error) { +func (k *k8sResources) GetGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupVersionResource, bool, error) { k.mutex.Lock() defer k.mutex.Unlock() @@ -291,23 +291,15 @@ func (k *k8sResources) getGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupV }, ar.Namespaced, nil } -func (k *k8sResources) GetApiResourceForGVK(gvk schema.GroupVersionKind) *v1.APIResource { - k.mutex.Lock() - defer k.mutex.Unlock() - - ar, _ := k.allResources[gvk] - return ar -} - func (k *k8sResources) GetCRDForGVK(k2 *K8sCluster, gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { - ar := k.GetApiResourceForGVK(gvk) - if ar == nil { - return nil, fmt.Errorf("APIResource for %s not found", gvk.String()) + gvr, _, err := k.GetGVRForGVK(gvk) + if err != nil { + return nil, err } crdRef := k8s.ObjectRef{ GVK: schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}, - Name: fmt.Sprintf("%s.%s", ar.Name, ar.Group), + Name: fmt.Sprintf("%s.%s", gvr.Resource, gvr.Group), } k.mutex.Lock() From e22509b7bf11a5c0759c0d578023bbca01781114 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 15:30:55 +0200 Subject: [PATCH 0152/2268] refactor: Implement and use GetPreferredResource --- pkg/k8s/resources.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 67b98505f..6100ad0a8 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -171,15 +171,15 @@ func (k *k8sResources) SetNamespaced(gv schema.GroupKind, namespaced bool) { } func (k *k8sResources) IsNamespaced(gv schema.GroupKind) bool { - k.mutex.Lock() - defer k.mutex.Unlock() + ar := k.GetPreferredResource(gv) + if ar == nil { + k.mutex.Lock() + defer k.mutex.Unlock() - r, ok := k.preferredResources[gv] - if !ok { n, _ := k.namespacedResources[gv] return n } - return r.Namespaced + return ar.Namespaced } func (k *k8sResources) FixNamespace(o *uo.UnstructuredObject, def string) { @@ -219,6 +219,17 @@ func (k *k8sResources) GetAllGroupVersions() ([]schema.GroupVersion, error) { return l, nil } +func (k *k8sResources) GetPreferredResource(gk schema.GroupKind) *v1.APIResource { + k.mutex.Lock() + defer k.mutex.Unlock() + + ar, ok := k.preferredResources[gk] + if !ok { + return nil + } + return ar +} + func (k *k8sResources) GetFilteredGVKs(filter func(ar *v1.APIResource) bool) []schema.GroupVersionKind { k.mutex.Lock() defer k.mutex.Unlock() From 5a7d27dc18ea68cf51cbb92d8d7f0d3142a196b8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 23:04:06 +0200 Subject: [PATCH 0153/2268] fix: Update api resources from CRDs, including not deployed ones --- pkg/deployment/deployment_item.go | 15 +- pkg/deployment/utils/apply_utils.go | 36 +---- pkg/k8s/k8s_cluster.go | 19 ++- pkg/k8s/resources.go | 241 +++++++++++++++++++--------- pkg/validation/validation.go | 2 +- 5 files changed, 179 insertions(+), 134 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 52c563251..7f4912747 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -388,7 +388,7 @@ func (di *DeploymentItem) loadObjects() error { var crdGV = schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"} -// postprocessCRDs will set namespace overrides into the K8sCluster so that future IsNamespaced return the correct +// postprocessCRDs will update api resources from freshly deployed CRDs // value even if the CRD is not deployed yet. func (di *DeploymentItem) postprocessCRDs(k *k8s.K8sCluster) error { if di.dir == nil { @@ -401,21 +401,10 @@ func (di *DeploymentItem) postprocessCRDs(k *k8s.K8sCluster) error { continue } - scope, _, err := o.GetNestedString("spec", "scope") + err := k.Resources.UpdateResourcesFromCRD(o) if err != nil { return err } - namespaced := strings.ToLower(scope) == "namespaced" - group, _, err := o.GetNestedString("spec", "group") - if err != nil { - return err - } - kind, _, err := o.GetNestedString("spec", "names", "kind") - if err != nil { - return err - } - - k.Resources.SetNamespaced(schema.GroupKind{Group: group, Kind: kind}, namespaced) } return nil } diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 12274398d..ff10287a5 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -45,8 +45,7 @@ type ApplyUtil struct { deletedHookObjects map[k8s2.ObjectRef]bool mutex sync.Mutex - abortSignal *atomic.Value - deployedNewCRD *atomic.Value + abortSignal *atomic.Value ru *RemoteObjectUtils k *k8s.K8sCluster @@ -63,8 +62,7 @@ type ApplyDeploymentsUtil struct { k *k8s.K8sCluster o *ApplyUtilOptions - abortSignal atomic.Value - deployedNewCRD atomic.Value + abortSignal atomic.Value results []*ApplyUtil } @@ -79,7 +77,6 @@ func NewApplyDeploymentsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnin o: o, } ret.abortSignal.Store(false) - ret.deployedNewCRD.Store(false) return ret } @@ -92,7 +89,6 @@ func (ad *ApplyDeploymentsUtil) NewApplyUtil(ctx context.Context, statusCtx *sta deletedObjects: map[k8s2.ObjectRef]bool{}, deletedHookObjects: map[k8s2.ObjectRef]bool{}, abortSignal: &ad.abortSignal, - deployedNewCRD: &ad.deployedNewCRD, ru: ad.ru, k: ad.k, o: ad.o, @@ -307,10 +303,6 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo ForceDryRun: a.o.DryRun, } r, apiWarnings, err := a.k.PatchObject(x, options) - retry, err := a.handleNewCRDs(r, err) - if retry { - r, apiWarnings, err = a.k.PatchObject(x, options) - } if r != nil && usesDummyName { tmpName := r.GetK8sName() _ = r.ReplaceKeys(tmpName, ref.Name) @@ -331,30 +323,6 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo } } -func (a *ApplyUtil) handleNewCRDs(x *uo.UnstructuredObject, err error) (bool, error) { - if err != nil && meta.IsNoMatchError(err) { - // maybe this was a resource for which the CRD was only deployed recently, so we should do rediscovery and then - // retry the patch - if a.deployedNewCRD.Load().(bool) { - a.deployedNewCRD.Store(false) - err = a.k.Resources.RediscoverResources() - if err != nil { - return false, err - } - return true, nil - } - } else if err == nil { - ref := x.GetK8sRef() - if ref.GVK.Group == "apiextensions.k8s.io" && ref.GVK.Kind == "CustomResourceDefinition" { - // this is a freshly deployed CRD, so we must perform rediscovery in case an api resource can't be found - a.deployedNewCRD.Store(true) - return true, nil - } - return false, nil - } - return false, err -} - func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) bool { if a.o.DryRun { return true diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index b5fa4a81a..b34e828dc 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -71,20 +71,21 @@ func (p *parallelClientEntry) HandleWarningHeader(code int, agent string, text s const parallelClients = 16 func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8sCluster, error) { + var err error + restConfig := rest.CopyConfig(configIn) restConfig.QPS = 10 restConfig.Burst = 20 - resources, err := newK8sResources(ctx, restConfig) - if err != nil { - return nil, err - } - k := &K8sCluster{ ctx: ctx, DryRun: dryRun, restConfig: restConfig, - Resources: resources, + } + + k.Resources, err = newK8sResources(ctx, restConfig, k) + if err != nil { + return nil, err } err = k.initClientPool() @@ -102,7 +103,11 @@ func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8 } k.ServerVersion = v2 - err = k.Resources.updateResources(true) + err = k.Resources.updateResources() + if err != nil { + return nil, err + } + err = k.Resources.updateResourcesFromCRDs() if err != nil { return nil, err } diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 6100ad0a8..266b7d090 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -8,42 +8,40 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/disk" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "net/url" "path/filepath" + "sort" "strings" "sync" "time" ) -type crdCacheEntry struct { - crd *uo.UnstructuredObject - err error -} - type k8sResources struct { ctx context.Context + k *K8sCluster discovery *disk.CachedDiscoveryClient - allResources map[schema.GroupVersionKind]*v1.APIResource - preferredResources map[schema.GroupKind]*v1.APIResource - namespacedResources map[schema.GroupKind]bool - crdCache map[k8s.ObjectRef]crdCacheEntry - mutex sync.Mutex + allResources map[schema.GroupVersionKind]v1.APIResource + preferredResources map[schema.GroupKind]v1.APIResource + crds map[schema.GroupKind]*uo.UnstructuredObject + mutex sync.Mutex } -func newK8sResources(ctx context.Context, config *rest.Config) (*k8sResources, error) { +func newK8sResources(ctx context.Context, config *rest.Config, k2 *K8sCluster) (*k8sResources, error) { k := &k8sResources{ - ctx: ctx, - allResources: map[schema.GroupVersionKind]*v1.APIResource{}, - preferredResources: map[schema.GroupKind]*v1.APIResource{}, - namespacedResources: map[schema.GroupKind]bool{}, - crdCache: map[k8s.ObjectRef]crdCacheEntry{}, - mutex: sync.Mutex{}, + ctx: ctx, + k: k2, + allResources: map[schema.GroupVersionKind]v1.APIResource{}, + preferredResources: map[schema.GroupKind]v1.APIResource{}, + crds: map[schema.GroupKind]*uo.UnstructuredObject{}, + mutex: sync.Mutex{}, } apiHost, err := url.Parse(config.Host) @@ -60,15 +58,13 @@ func newK8sResources(ctx context.Context, config *rest.Config) (*k8sResources, e return k, nil } -func (k *k8sResources) updateResources(doLock bool) error { - if doLock { - k.mutex.Lock() - defer k.mutex.Unlock() - } +func (k *k8sResources) updateResources() error { + k.mutex.Lock() + defer k.mutex.Unlock() - k.allResources = map[schema.GroupVersionKind]*v1.APIResource{} - k.preferredResources = map[schema.GroupKind]*v1.APIResource{} - k.crdCache = map[k8s.ObjectRef]crdCacheEntry{} + k.allResources = map[schema.GroupVersionKind]v1.APIResource{} + k.preferredResources = map[schema.GroupKind]v1.APIResource{} + k.crds = map[schema.GroupKind]*uo.UnstructuredObject{} // the discovery client doesn't support cancellation, so we need to run it in the background and wait for it var arls []*v1.APIResourceList @@ -124,7 +120,7 @@ func (k *k8sResources) updateResources(doLock bool) error { if _, ok := k.allResources[gvk]; ok { ok = false } - k.allResources[gvk] = &ar + k.allResources[gvk] = ar } } @@ -150,53 +146,160 @@ func (k *k8sResources) updateResources(doLock bool) error { if _, ok := deprecatedResources[gk]; ok { continue } - k.preferredResources[gk] = &ar + k.preferredResources[gk] = ar } } return nil } -func (k *k8sResources) RediscoverResources() error { - k.mutex.Lock() - defer k.mutex.Unlock() +var crdGK = schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"} - k.discovery.Invalidate() - return k.updateResources(false) +func (k *k8sResources) updateResourcesFromCRDs() error { + ar := k.GetPreferredResource(crdGK) + if ar == nil { + return fmt.Errorf("api resource for CRDs not found") + } + gvr, _, err := k.GetGVRForGVK(schema.GroupVersionKind{ + Group: ar.Group, + Version: ar.Version, + Kind: ar.Kind, + }) + if err != nil { + return err + } + + var crds *unstructured.UnstructuredList + _, err = k.k.withClientFromPool(func(p *parallelClientEntry) error { + var err error + crds, err = p.dynamicClient.Resource(*gvr).List(k.ctx, v1.ListOptions{}) + return err + }) + if err != nil { + return err + } + + for _, x := range crds.Items { + x2 := x + crd := uo.FromUnstructured(&x2) + err = k.UpdateResourcesFromCRD(crd) + if err != nil { + return err + } + } + + return nil } -func (k *k8sResources) SetNamespaced(gv schema.GroupKind, namespaced bool) { +func (k *k8sResources) UpdateResourcesFromCRD(crd *uo.UnstructuredObject) error { + var err error + var ar v1.APIResource + ar.Group, _, err = crd.GetNestedString("spec", "group") + if err != nil { + return err + } + ar.Name, _, err = crd.GetNestedString("spec", "names", "plural") + if err != nil { + return err + } + ar.Kind, _, err = crd.GetNestedString("spec", "names", "kind") + if err != nil { + return err + } + ar.SingularName, _, err = crd.GetNestedString("spec", "names", "singular") + if err != nil { + return err + } + scope, _, err := crd.GetNestedString("spec", "scope") + if err != nil { + return err + } + ar.Namespaced = strings.ToLower(scope) == "namespaced" + ar.ShortNames, _, err = crd.GetNestedStringList("spec", "names", "shortNames") + if err != nil { + return err + } + ar.Categories, _, err = crd.GetNestedStringList("spec", "names", "categories") + if err != nil { + return err + } + versions, _, err := crd.GetNestedObjectList("spec", "versions") + if err != nil { + return err + } + + ar.Verbs = []string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"} + + gk := schema.GroupKind{ + Group: ar.Group, + Kind: ar.Kind, + } + k.mutex.Lock() defer k.mutex.Unlock() - k.namespacedResources[gv] = namespaced + + k.crds[gk] = crd + + var versionStrs []string + for _, v := range versions { + name, _, err := v.GetNestedString("name") + if err != nil { + return err + } + versionStrs = append(versionStrs, name) + } + + // Sort the same way as api discovery does it. The first entry is then the preferred version + sort.Slice(versionStrs, func(i, j int) bool { + return version.CompareKubeAwareVersionStrings(versionStrs[i], versionStrs[j]) > 0 + }) + + for i, v := range versionStrs { + ar2 := ar + ar2.Version = v + gvk := schema.GroupVersionKind{ + Group: gk.Group, + Version: ar2.Version, + Kind: gk.Kind, + } + k.allResources[gvk] = ar2 + + if i == 0 { + k.preferredResources[gk] = ar2 + } + } + + return nil } -func (k *k8sResources) IsNamespaced(gv schema.GroupKind) bool { +func (k *k8sResources) IsNamespaced(gv schema.GroupKind) *bool { ar := k.GetPreferredResource(gv) if ar == nil { - k.mutex.Lock() - defer k.mutex.Unlock() - - n, _ := k.namespacedResources[gv] - return n + return nil } - return ar.Namespaced + return &ar.Namespaced } func (k *k8sResources) FixNamespace(o *uo.UnstructuredObject, def string) { ref := o.GetK8sRef() namespaced := k.IsNamespaced(ref.GVK.GroupKind()) - if !namespaced && ref.Namespace != "" { + if namespaced == nil { + return + } + if !*namespaced && ref.Namespace != "" { o.SetK8sNamespace("") - } else if namespaced && ref.Namespace == "" { + } else if *namespaced && ref.Namespace == "" { o.SetK8sNamespace(def) } } func (k *k8sResources) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { namespaced := k.IsNamespaced(ref.GVK.GroupKind()) - if !namespaced && ref.Namespace != "" { + if namespaced == nil { + return ref + } + if !*namespaced && ref.Namespace != "" { ref.Namespace = "" - } else if namespaced && ref.Namespace == "" { + } else if *namespaced && ref.Namespace == "" { ref.Namespace = "default" } return ref @@ -227,7 +330,7 @@ func (k *k8sResources) GetPreferredResource(gk schema.GroupKind) *v1.APIResource if !ok { return nil } - return ar + return &ar } func (k *k8sResources) GetFilteredGVKs(filter func(ar *v1.APIResource) bool) []schema.GroupVersionKind { @@ -236,7 +339,7 @@ func (k *k8sResources) GetFilteredGVKs(filter func(ar *v1.APIResource) bool) []s var ret []schema.GroupVersionKind for _, ar := range k.allResources { - if !filter(ar) { + if !filter(&ar) { continue } gvk := schema.GroupVersionKind{ @@ -255,7 +358,7 @@ func (k *k8sResources) GetFilteredPreferredGVKs(filter func(ar *v1.APIResource) var ret []schema.GroupVersionKind for _, ar := range k.preferredResources { - if !filter(ar) { + if !filter(&ar) { continue } gvk := schema.GroupVersionKind{ @@ -302,39 +405,19 @@ func (k *k8sResources) GetGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupV }, ar.Namespaced, nil } -func (k *k8sResources) GetCRDForGVK(k2 *K8sCluster, gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { - gvr, _, err := k.GetGVRForGVK(gvk) - if err != nil { - return nil, err - } - - crdRef := k8s.ObjectRef{ - GVK: schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}, - Name: fmt.Sprintf("%s.%s", gvr.Resource, gvr.Group), - } - +func (k *k8sResources) GetCRDForGK(gk schema.GroupKind) *uo.UnstructuredObject { k.mutex.Lock() - x, ok := k.crdCache[crdRef] - k.mutex.Unlock() - if ok { - return x.crd, x.err - } - - crd, _, err := k2.GetSingleObject(crdRef) + defer k.mutex.Unlock() - k.mutex.Lock() - x.crd = crd - x.err = err - k.crdCache[crdRef] = x - k.mutex.Unlock() + crd, _ := k.crds[gk] - return crd, err + return crd } -func (k *k8sResources) GetSchemaForGVK(k2 *K8sCluster, gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { - crd, err := k.GetCRDForGVK(k2, gvk) - if err != nil { - return nil, err +func (k *k8sResources) GetSchemaForGVK(gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { + crd := k.GetCRDForGK(gvk.GroupKind()) + if crd == nil { + return nil, nil } versions, ok, err := crd.GetNestedObjectList("spec", "versions") @@ -345,12 +428,12 @@ func (k *k8sResources) GetSchemaForGVK(k2 *K8sCluster, gvk schema.GroupVersionKi return nil, fmt.Errorf("versions not found in CRD") } - for _, version := range versions { - name, _, _ := version.GetNestedString("name") + for _, v := range versions { + name, _, _ := v.GetNestedString("name") if name != gvk.Version { continue } - s, ok, err := version.GetNestedObject("schema", "openAPIV3Schema") + s, ok, err := v.GetNestedObject("schema", "openAPIV3Schema") if err != nil { return nil, err } diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 0331074bc..6e631470f 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -117,7 +117,7 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError // can't really say anything... return } - s, err := k.Resources.GetSchemaForGVK(k, ref.GVK) + s, err := k.Resources.GetSchemaForGVK(ref.GVK) if err != nil && !errors.IsNotFound(err) { addError(err.Error()) return From 2e9a7c47cac94e9d702e0abc3fa0f1dcfdd6c9bb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 23:22:59 +0200 Subject: [PATCH 0154/2268] refactor: Move client pool handling into own class --- pkg/k8s/client.go | 141 +++++++++++++++++++++++++++++++++++++++++ pkg/k8s/k8s_cluster.go | 130 +++---------------------------------- pkg/k8s/resources.go | 12 ++-- 3 files changed, 157 insertions(+), 126 deletions(-) create mode 100644 pkg/k8s/client.go diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go new file mode 100644 index 000000000..fbda2dbd5 --- /dev/null +++ b/pkg/k8s/client.go @@ -0,0 +1,141 @@ +package k8s + +import ( + "context" + "fmt" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/metadata" + "k8s.io/client-go/rest" + "net/http" +) + +type k8sClients struct { + ctx context.Context + restConfig *rest.Config + clientPool chan *parallelClientEntry + count int +} + +type parallelClientEntry struct { + http *http.Client + corev1 *corev1.CoreV1Client + dynamicClient dynamic.Interface + metadataClient metadata.Interface + + warnings []ApiWarning +} + +type ApiWarning struct { + Code int + Agent string + Text string +} + +func (p *parallelClientEntry) HandleWarningHeader(code int, agent string, text string) { + p.warnings = append(p.warnings, ApiWarning{ + Code: code, + Agent: agent, + Text: text, + }) +} + +func newK8sClients(ctx context.Context, restConfig *rest.Config, count int) (*k8sClients, error) { + var err error + + k := &k8sClients{ + ctx: ctx, + restConfig: restConfig, + clientPool: make(chan *parallelClientEntry, count), + count: count, + } + + for i := 0; i < count; i++ { + p := ¶llelClientEntry{} + config := rest.CopyConfig(k.restConfig) + config.WarningHandler = p + + p.http, err = rest.HTTPClientFor(config) + if err != nil { + return nil, err + } + + p.corev1, err = corev1.NewForConfigAndClient(config, p.http) + if err != nil { + return nil, err + } + + p.dynamicClient, err = dynamic.NewForConfigAndClient(config, p.http) + if err != nil { + return nil, err + } + + p.metadataClient, err = metadata.NewForConfigAndClient(config, p.http) + if err != nil { + return nil, err + } + + k.clientPool <- p + } + return k, nil +} + +func (k *k8sClients) close() { + if k.clientPool != nil { + for i := 0; i < k.count; i++ { + p := <-k.clientPool + p.http.CloseIdleConnections() + } + } + k.clientPool = nil + k.count = 0 +} + +func (k *k8sClients) withClientFromPool(cb func(p *parallelClientEntry) error) ([]ApiWarning, error) { + select { + case p := <-k.clientPool: + defer func() { k.clientPool <- p }() + p.warnings = nil + err := cb(p) + return append([]ApiWarning(nil), p.warnings...), err + case <-k.ctx.Done(): + return nil, fmt.Errorf("failed waiting for free client: %w", k.ctx.Err()) + } +} + +func (k *k8sClients) withDynamicClientForGVR(gvr *schema.GroupVersionResource, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { + return k.withClientFromPool(func(p *parallelClientEntry) error { + if namespace != "" { + return cb(p.dynamicClient.Resource(*gvr).Namespace(namespace)) + } else { + return cb(p.dynamicClient.Resource(*gvr)) + } + }) +} + +func (k *k8sClients) withMetadataClientForGVR(gvr *schema.GroupVersionResource, namespace string, cb func(r metadata.ResourceInterface) error) ([]ApiWarning, error) { + return k.withClientFromPool(func(p *parallelClientEntry) error { + if namespace != "" { + return cb(p.metadataClient.Resource(*gvr).Namespace(namespace)) + } else { + return cb(p.metadataClient.Resource(*gvr)) + } + }) +} + +func (k *k8sClients) withDynamicClientForGVK(resources *k8sResources, gvk schema.GroupVersionKind, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { + gvr, err := resources.GetGVRForGVK(gvk) + if err != nil { + return nil, err + } + return k.withDynamicClientForGVR(gvr, namespace, cb) +} + +func (k *k8sClients) withMetadataClientForGVK(resources *k8sResources, gvk schema.GroupVersionKind, namespace string, cb func(r metadata.ResourceInterface) error) ([]ApiWarning, error) { + gvr, err := resources.GetGVRForGVK(gvk) + if err != nil { + return nil, err + } + return k.withMetadataClientForGVR(gvr, namespace, cb) +} diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index b34e828dc..3a7993f2f 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -17,11 +17,9 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/dynamic" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/metadata" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" - "net/http" "sync" "time" ) @@ -38,38 +36,13 @@ type K8sCluster struct { DryRun bool restConfig *rest.Config - clientPool chan *parallelClientEntry + clients *k8sClients ServerVersion *goversion.Version Resources *k8sResources } -type parallelClientEntry struct { - http *http.Client - corev1 *corev1.CoreV1Client - dynamicClient dynamic.Interface - metadataClient metadata.Interface - - warnings []ApiWarning -} - -type ApiWarning struct { - Code int - Agent string - Text string -} - -func (p *parallelClientEntry) HandleWarningHeader(code int, agent string, text string) { - p.warnings = append(p.warnings, ApiWarning{ - Code: code, - Agent: agent, - Text: text, - }) -} - -const parallelClients = 16 - func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8sCluster, error) { var err error @@ -88,7 +61,7 @@ func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8 return nil, err } - err = k.initClientPool() + k.clients, err = newK8sClients(ctx, restConfig, 16) if err != nil { return nil, err } @@ -107,7 +80,7 @@ func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8 if err != nil { return nil, err } - err = k.Resources.updateResourcesFromCRDs() + err = k.Resources.updateResourcesFromCRDs(k.clients) if err != nil { return nil, err } @@ -115,47 +88,6 @@ func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8 return k, nil } -func (k *K8sCluster) initClientPool() error { - var err error - - if k.clientPool != nil { - for i := 0; i < parallelClients; i++ { - p := <-k.clientPool - p.http.CloseIdleConnections() - } - } - - k.clientPool = make(chan *parallelClientEntry, parallelClients) - for i := 0; i < parallelClients; i++ { - p := ¶llelClientEntry{} - config := rest.CopyConfig(k.restConfig) - config.WarningHandler = p - - p.http, err = rest.HTTPClientFor(config) - if err != nil { - return err - } - - p.corev1, err = corev1.NewForConfigAndClient(config, p.http) - if err != nil { - return err - } - - p.dynamicClient, err = dynamic.NewForConfigAndClient(config, p.http) - if err != nil { - return err - } - - p.metadataClient, err = metadata.NewForConfigAndClient(config, p.http) - if err != nil { - return err - } - - k.clientPool <- p - } - return nil -} - func (k *K8sCluster) ReadWrite() *K8sCluster { k2 := *k k2.DryRun = false @@ -166,48 +98,6 @@ func (k *K8sCluster) GetCA() []byte { return k.restConfig.CAData } -func (k *K8sCluster) withClientFromPool(cb func(p *parallelClientEntry) error) ([]ApiWarning, error) { - select { - case p := <-k.clientPool: - defer func() { k.clientPool <- p }() - p.warnings = nil - err := cb(p) - return append([]ApiWarning(nil), p.warnings...), err - case <-k.ctx.Done(): - return nil, fmt.Errorf("failed waiting for free client: %w", k.ctx.Err()) - } -} - -func (k *K8sCluster) withDynamicClientForGVK(gvk schema.GroupVersionKind, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { - return k.withClientFromPool(func(p *parallelClientEntry) error { - gvr, namespaced, err := k.Resources.GetGVRForGVK(gvk) - if err != nil { - return err - } - - if namespaced && namespace != "" { - return cb(p.dynamicClient.Resource(*gvr).Namespace(namespace)) - } else { - return cb(p.dynamicClient.Resource(*gvr)) - } - }) -} - -func (k *K8sCluster) withMetadataClientForGVK(gvk schema.GroupVersionKind, namespace string, cb func(r metadata.ResourceInterface) error) ([]ApiWarning, error) { - return k.withClientFromPool(func(p *parallelClientEntry) error { - gvr, namespaced, err := k.Resources.GetGVRForGVK(gvk) - if err != nil { - return err - } - - if namespaced && namespace != "" { - return cb(p.metadataClient.Resource(*gvr).Namespace(namespace)) - } else { - return cb(p.metadataClient.Resource(*gvr)) - } - }) -} - func (k *K8sCluster) buildLabelSelector(labels map[string]string) string { ret := "" @@ -223,7 +113,7 @@ func (k *K8sCluster) buildLabelSelector(labels map[string]string) string { func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { var result []*uo.UnstructuredObject - apiWarnings, err := k.withDynamicClientForGVK(gvk, namespace, func(r dynamic.ResourceInterface) error { + apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, gvk, namespace, func(r dynamic.ResourceInterface) error { o := v1.ListOptions{ LabelSelector: k.buildLabelSelector(labels), } @@ -242,7 +132,7 @@ func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, func (k *K8sCluster) ListObjectsMetadata(gvk schema.GroupVersionKind, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { var result []*uo.UnstructuredObject - apiWarnings, err := k.withMetadataClientForGVK(gvk, namespace, func(r metadata.ResourceInterface) error { + apiWarnings, err := k.clients.withMetadataClientForGVK(k.Resources, gvk, namespace, func(r metadata.ResourceInterface) error { o := v1.ListOptions{ LabelSelector: k.buildLabelSelector(labels), } @@ -318,7 +208,7 @@ func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map func (k *K8sCluster) GetSingleObject(ref k8s.ObjectRef) (*uo.UnstructuredObject, []ApiWarning, error) { var result *uo.UnstructuredObject - apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { + apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { o := v1.GetOptions{} x, err := r.Get(k.ctx, ref.Name, o) if err != nil { @@ -382,7 +272,7 @@ func (k *K8sCluster) DeleteSingleObject(ref k8s.ObjectRef, options DeleteOptions o.DryRun = []string{"All"} } - apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { + apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { err := r.Delete(k.ctx, ref.Name, o) if err != nil { if options.IgnoreNotFoundError && errors.IsNotFound(err) { @@ -539,7 +429,7 @@ func (k *K8sCluster) PatchObject(o *uo.UnstructuredObject, options PatchOptions) status.Trace(k.ctx, "patching %s", ref.String()) var result *uo.UnstructuredObject - apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { + apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { x, err := r.Patch(k.ctx, ref.Name, types.ApplyPatchType, data, po) if err != nil { return fmt.Errorf("failed to patch %s: %w", ref.String(), err) @@ -568,7 +458,7 @@ func (k *K8sCluster) UpdateObject(o *uo.UnstructuredObject, options UpdateOption status.Trace(k.ctx, "updating %s", ref.String()) var result *uo.UnstructuredObject - apiWarnings, err := k.withDynamicClientForGVK(ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { + apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { x, err := r.Update(k.ctx, o.ToUnstructured(), updateOpts) if err != nil { return err @@ -581,7 +471,7 @@ func (k *K8sCluster) UpdateObject(o *uo.UnstructuredObject, options UpdateOption func (k *K8sCluster) ProxyGet(scheme, namespace, name, port, path string, params map[string]string) (io.ReadCloser, error) { var ret rest.ResponseWrapper - _, err := k.withClientFromPool(func(p *parallelClientEntry) error { + _, err := k.clients.withClientFromPool(func(p *parallelClientEntry) error { ret = p.corev1.Services(namespace).ProxyGet(scheme, name, port, path, params) return nil }) diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 266b7d090..359b9451b 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -154,12 +154,12 @@ func (k *k8sResources) updateResources() error { var crdGK = schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"} -func (k *k8sResources) updateResourcesFromCRDs() error { +func (k *k8sResources) updateResourcesFromCRDs(clients *k8sClients) error { ar := k.GetPreferredResource(crdGK) if ar == nil { return fmt.Errorf("api resource for CRDs not found") } - gvr, _, err := k.GetGVRForGVK(schema.GroupVersionKind{ + gvr, err := k.GetGVRForGVK(schema.GroupVersionKind{ Group: ar.Group, Version: ar.Version, Kind: ar.Kind, @@ -169,7 +169,7 @@ func (k *k8sResources) updateResourcesFromCRDs() error { } var crds *unstructured.UnstructuredList - _, err = k.k.withClientFromPool(func(p *parallelClientEntry) error { + _, err = clients.withClientFromPool(func(p *parallelClientEntry) error { var err error crds, err = p.dynamicClient.Resource(*gvr).List(k.ctx, v1.ListOptions{}) return err @@ -386,13 +386,13 @@ func BuildGVKFilter(group *string, version *string, kind *string) func(ar *v1.AP } } -func (k *k8sResources) GetGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupVersionResource, bool, error) { +func (k *k8sResources) GetGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupVersionResource, error) { k.mutex.Lock() defer k.mutex.Unlock() ar, ok := k.allResources[gvk] if !ok { - return nil, false, &meta.NoKindMatchError{ + return nil, &meta.NoKindMatchError{ GroupKind: gvk.GroupKind(), SearchedVersions: []string{gvk.Version}, } @@ -402,7 +402,7 @@ func (k *k8sResources) GetGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupV Group: ar.Group, Version: ar.Version, Resource: ar.Name, - }, ar.Namespaced, nil + }, nil } func (k *k8sResources) GetCRDForGK(gk schema.GroupKind) *uo.UnstructuredObject { From a61cdf06046228bb3af3f964e823b42e369d1890 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 23:50:31 +0200 Subject: [PATCH 0155/2268] refactor: Don't rely on dynamic client for CRD retrieval --- pkg/k8s/resources.go | 47 +++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 359b9451b..3010d295a 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -6,9 +6,10 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/meta" v1 "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/version" "k8s.io/client-go/discovery" @@ -21,12 +22,14 @@ import ( "strings" "sync" "time" + + apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" ) type k8sResources struct { - ctx context.Context - k *K8sCluster - discovery *disk.CachedDiscoveryClient + ctx context.Context + restConfig *rest.Config + discovery *disk.CachedDiscoveryClient allResources map[schema.GroupVersionKind]v1.APIResource preferredResources map[schema.GroupKind]v1.APIResource @@ -34,10 +37,10 @@ type k8sResources struct { mutex sync.Mutex } -func newK8sResources(ctx context.Context, config *rest.Config, k2 *K8sCluster) (*k8sResources, error) { +func newK8sResources(ctx context.Context, config *rest.Config) (*k8sResources, error) { k := &k8sResources{ ctx: ctx, - k: k2, + restConfig: config, allResources: map[schema.GroupVersionKind]v1.APIResource{}, preferredResources: map[schema.GroupKind]v1.APIResource{}, crds: map[schema.GroupKind]*uo.UnstructuredObject{}, @@ -155,32 +158,26 @@ func (k *k8sResources) updateResources() error { var crdGK = schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"} func (k *k8sResources) updateResourcesFromCRDs(clients *k8sClients) error { - ar := k.GetPreferredResource(crdGK) - if ar == nil { - return fmt.Errorf("api resource for CRDs not found") - } - gvr, err := k.GetGVRForGVK(schema.GroupVersionKind{ - Group: ar.Group, - Version: ar.Version, - Kind: ar.Kind, - }) - if err != nil { - return err - } + var crdList *apiextensionsv1.CustomResourceDefinitionList + _, err := clients.withClientFromPool(func(p *parallelClientEntry) error { + c, err := apiextensionsclient.NewForConfigAndClient(k.restConfig, p.http) + if err != nil { + return err + } - var crds *unstructured.UnstructuredList - _, err = clients.withClientFromPool(func(p *parallelClientEntry) error { - var err error - crds, err = p.dynamicClient.Resource(*gvr).List(k.ctx, v1.ListOptions{}) + crdList, err = c.CustomResourceDefinitions().List(k.ctx, v1.ListOptions{}) return err }) if err != nil { return err } - for _, x := range crds.Items { - x2 := x - crd := uo.FromUnstructured(&x2) + for _, x := range crdList.Items { + u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&x) + if err != nil { + return err + } + crd := uo.FromMap(u) err = k.UpdateResourcesFromCRD(crd) if err != nil { return err From 919e5ee2c3376bf5b662a7d156755e3d3c530950 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 23:51:16 +0200 Subject: [PATCH 0156/2268] refactor: Run updateResources and updateResourcesFromCRDs in parallel --- pkg/k8s/k8s_cluster.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 3a7993f2f..ecf422fc3 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -56,7 +56,7 @@ func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8 restConfig: restConfig, } - k.Resources, err = newK8sResources(ctx, restConfig, k) + k.Resources, err = newK8sResources(ctx, restConfig) if err != nil { return nil, err } @@ -76,13 +76,26 @@ func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8 } k.ServerVersion = v2 - err = k.Resources.updateResources() - if err != nil { - return nil, err - } - err = k.Resources.updateResourcesFromCRDs(k.clients) - if err != nil { - return nil, err + var wg sync.WaitGroup + wg.Add(2) + + var err1 error + var err2 error + go func() { + err1 = k.Resources.updateResources() + wg.Done() + }() + go func() { + err2 = k.Resources.updateResourcesFromCRDs(k.clients) + wg.Done() + }() + wg.Wait() + + if err1 != nil { + return nil, err1 + } + if err2 != nil { + return nil, err2 } return k, nil From 828e0265d354dafa68fab8ab43e9e73f4d6ba990 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 10 May 2022 23:57:02 +0200 Subject: [PATCH 0157/2268] fix: Fix loop variable capture --- pkg/k8s/k8s_cluster.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index ecf422fc3..3813ac550 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -190,6 +190,7 @@ func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map } for _, gvk := range k.Resources.GetFilteredPreferredGVKs(filter) { + gvk := gvk wp.Submit(func() error { var l []*uo.UnstructuredObject var apiWarnings []ApiWarning From 25253f767132b9a6727b01c50ed75fe82e075c74 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 May 2022 13:22:55 +0200 Subject: [PATCH 0158/2268] fix: Fix hang after failed postprocessing stage --- pkg/deployment/deployment_collection.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index def63872c..4b845757b 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -235,6 +235,8 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { if len(errs) == 0 { s.Success() + } else { + s.Failed() } return utils.NewErrorListOrNil(errs) From c88700b6e9c907b89f8e784816c955ccae21fb54 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 May 2022 14:55:28 +0200 Subject: [PATCH 0159/2268] fix: Add --offline-images flag to allow disabling registry queries --- cmd/kluctl/args/images.go | 1 + cmd/kluctl/commands/utils.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index f9eb511be..f11fba0a4 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -11,6 +11,7 @@ type ImageFlags struct { FixedImage []string `group:"images" short:"F" help:"Pin an image to a given version. Expects '--fixed-image=image<:namespace:deployment:container>=result'"` FixedImagesFile existingFileType `group:"images" help:"Use .yml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format" exts:"yml,yaml"` UpdateImages bool `group:"images" short:"u" help:"This causes kluctl to prefer the latest image found in registries, based on the 'latest_image' filters provided to 'images.get_image(...)' calls. Use this flag if you want to update to the latest versions/tags of all images. '-u' takes precedence over '--fixed-image/--fixed-images-file', meaning that the latest images are used even if an older image is given via fixed images."` + OfflineImages bool `group:"images" help:"Omit contacting image registries and do not query for latest image tags."` } func (args *ImageFlags) LoadFixedImagesFromArgs() ([]types.FixedImage, error) { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 30395bc61..c6f69df50 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -121,7 +121,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm if err != nil { return fmt.Errorf("failed to parse registry auth from environment: %w", err) } - images, err := deployment.NewImages(rh, args.imageFlags.UpdateImages, args.forCompletion) + images, err := deployment.NewImages(rh, args.imageFlags.UpdateImages, args.imageFlags.OfflineImages || args.forCompletion) if err != nil { return err } From b6ea0929119b406957201b1c3006f85be803cf20 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 May 2022 14:56:01 +0200 Subject: [PATCH 0160/2268] refactor: Introduce KeyPath type --- pkg/diff/diff.go | 4 ++-- pkg/diff/managed_fields.go | 8 ++++---- pkg/k8s/k8s_cluster.go | 8 ++++---- pkg/kluctl_project/targets.go | 2 +- pkg/seal/secrets_loader.go | 6 +++--- pkg/utils/uo/jsonpath.go | 10 ++++++---- pkg/utils/uo/nested_fields.go | 12 ++++++------ pkg/utils/uo/object_iterator.go | 12 ++++-------- 8 files changed, 30 insertions(+), 32 deletions(-) diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index 3746c3c5f..5781b9312 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -18,7 +18,7 @@ import ( var notPresent = struct{}{} func convertPath(path []string, o interface{}) (string, error) { - var ret []interface{} + var ret uo.KeyPath for _, p := range path { if i, err := strconv.ParseInt(p, 10, 32); err == nil { x, found, _ := uo.GetChild(o, int(i)) @@ -35,7 +35,7 @@ func convertPath(path []string, o interface{}) (string, error) { ret = append(ret, p) o = x } - return uo.KeyListToJsonPath(ret), nil + return ret.ToJsonPath(), nil } func Diff(oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) ([]types.Change, error) { diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index 3a0772165..36200458d 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -59,7 +59,7 @@ func checkListItemMatch(o interface{}, pathElement fieldpath.PathElement, index } } -func convertToKeyList(remote *uo.UnstructuredObject, path fieldpath.Path) ([]interface{}, bool, error) { +func convertToKeyList(remote *uo.UnstructuredObject, path fieldpath.Path) (uo.KeyPath, bool, error) { var ret []interface{} var o interface{} = remote.Object for _, e := range path { @@ -157,7 +157,7 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr return nil, nil, err } for _, f := range fields { - forceApplyFields[uo.KeyListToJsonPath(f)] = true + forceApplyFields[f.ToJsonPath()] = true } } @@ -209,13 +209,13 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr break } } - if _, ok := forceApplyFields[uo.KeyListToJsonPath(p)]; ok { + if _, ok := forceApplyFields[p.ToJsonPath()]; ok { overwrite = true } } if !overwrite { - j, err := uo.NewMyJsonPath(uo.KeyListToJsonPath(p)) + j, err := uo.NewMyJsonPath(p.ToJsonPath()) if err != nil { return nil, nil, err } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 3813ac550..160a0ad8b 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -352,7 +352,7 @@ func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.Unstructure return } - ports, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfMaps(o) + ports, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfMaps(o.Object) if !found { return } @@ -368,7 +368,7 @@ func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.Unstructure if !needsTypeConversionFix { return } - d, found, _ := uo.NewMyJsonPathMust(p).GetFirstMap(o) + d, found, _ := uo.NewMyJsonPathMust(p).GetFirstMap(o.Object) if !found { return } @@ -389,7 +389,7 @@ func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.Unstructure } fixContainers := func(p string) { - containers, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfMaps(o) + containers, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfMaps(o.Object) if !found { return } @@ -399,7 +399,7 @@ func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.Unstructure } fixLimits := func(p string) { - limits, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfMaps(o) + limits, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfMaps(o.Object) if !found { return } diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 8492422b2..8e139fa64 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -336,7 +336,7 @@ func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) if targetConfig.Args != nil { err = targetConfig.Args.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { strValue := fmt.Sprintf("%v", it.Value()) - err := c.CheckDynamicArg(&target, it.JsonPath(), strValue) + err := c.CheckDynamicArg(&target, it.KeyPath().ToJsonPath(), strValue) if err != nil { return err } diff --git a/pkg/seal/secrets_loader.go b/pkg/seal/secrets_loader.go index fb863224c..ff0a13431 100644 --- a/pkg/seal/secrets_loader.go +++ b/pkg/seal/secrets_loader.go @@ -87,15 +87,15 @@ func (s *SecretsLoader) loadSecretsSystemEnvs(source *types.SecretSource) (*uo.U err := source.SystemEnvVars.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { envName, ok := it.Value().(string) if !ok { - return fmt.Errorf("value at %s is not a string", it.JsonPath()) + return fmt.Errorf("value at %s is not a string", it.KeyPath().ToJsonPath()) } envValue, ok := os.LookupEnv(envName) if !ok { - return fmt.Errorf("environment variable %s not found for secret %s", envName, it.JsonPath()) + return fmt.Errorf("environment variable %s not found for secret %s", envName, it.KeyPath().ToJsonPath()) } err := secrets.SetNestedField(envValue, it.KeyPath()...) if err != nil { - return fmt.Errorf("failed to set secret %s: %w", it.JsonPath(), err) + return fmt.Errorf("failed to set secret %s: %w", it.KeyPath().ToJsonPath(), err) } return nil }) diff --git a/pkg/utils/uo/jsonpath.go b/pkg/utils/uo/jsonpath.go index b375aca7e..3843259f2 100644 --- a/pkg/utils/uo/jsonpath.go +++ b/pkg/utils/uo/jsonpath.go @@ -9,9 +9,11 @@ import ( var isSimpleIdentifier = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]+$`) -func KeyListToJsonPath(keys []interface{}) string { +type KeyPath []interface{} + +func (kl KeyPath) ToJsonPath() string { p := "" - for _, k := range keys { + for _, k := range kl { if i, ok := k.(int); ok { p = fmt.Sprintf("%s[%d]", p, i) } else if s, ok := k.(string); ok { @@ -62,8 +64,8 @@ func NewMyJsonPathMust(p string) *MyJsonPath { return j } -func (j *MyJsonPath) ListMatchingFields(o *UnstructuredObject) ([][]interface{}, error) { - var ret [][]interface{} +func (j *MyJsonPath) ListMatchingFields(o *UnstructuredObject) ([]KeyPath, error) { + var ret []KeyPath o = o.Clone() magic := struct{}{} diff --git a/pkg/utils/uo/nested_fields.go b/pkg/utils/uo/nested_fields.go index 0de90bab6..325c581f3 100644 --- a/pkg/utils/uo/nested_fields.go +++ b/pkg/utils/uo/nested_fields.go @@ -84,7 +84,7 @@ func (uo *UnstructuredObject) GetNestedString(keys ...interface{}) (string, bool } s, ok := v.(string) if !ok { - return "", false, fmt.Errorf("value at %s is not a string", KeyListToJsonPath(keys)) + return "", false, fmt.Errorf("value at %s is not a string", KeyPath(keys).ToJsonPath()) } return s, true, nil } @@ -102,7 +102,7 @@ func (uo *UnstructuredObject) GetNestedInt(keys ...interface{}) (int64, bool, er if i2, ok := v.(int); ok { i = int64(i2) } else { - return 0, false, fmt.Errorf("value at %s is not an int", KeyListToJsonPath(keys)) + return 0, false, fmt.Errorf("value at %s is not an int", KeyPath(keys).ToJsonPath()) } } return i, true, nil @@ -118,7 +118,7 @@ func (uo *UnstructuredObject) GetNestedList(keys ...interface{}) ([]interface{}, } l, ok := v.([]interface{}) if !ok { - return nil, false, fmt.Errorf("value at %s is not a slice", KeyListToJsonPath(keys)) + return nil, false, fmt.Errorf("value at %s is not a slice", KeyPath(keys).ToJsonPath()) } return l, true, nil } @@ -135,7 +135,7 @@ func (uo *UnstructuredObject) GetNestedStringList(keys ...interface{}) ([]string for i, x := range l { s, ok := x.(string) if !ok { - return nil, false, fmt.Errorf("value at index %s is not a slice of strings", KeyListToJsonPath(keys)) + return nil, false, fmt.Errorf("value at index %s is not a slice of strings", KeyPath(keys).ToJsonPath()) } ret[i] = s } @@ -209,13 +209,13 @@ func (uo *UnstructuredObject) GetNestedStringMapCopy(keys ...interface{}) (map[s } m, ok := v.(map[string]interface{}) if !ok { - return nil, false, fmt.Errorf("value at %s is not a map", KeyListToJsonPath(keys)) + return nil, false, fmt.Errorf("value at %s is not a map", KeyPath(keys).ToJsonPath()) } ret := make(map[string]string) for k, v := range m { s, ok := v.(string) if !ok { - return nil, false, fmt.Errorf("value at %s.%s is not a string", KeyListToJsonPath(keys), k) + return nil, false, fmt.Errorf("value at %s.%s is not a string", KeyPath(keys).ToJsonPath(), k) } ret[k] = s } diff --git a/pkg/utils/uo/object_iterator.go b/pkg/utils/uo/object_iterator.go index debb5c227..f1859aa95 100644 --- a/pkg/utils/uo/object_iterator.go +++ b/pkg/utils/uo/object_iterator.go @@ -3,7 +3,7 @@ package uo type ObjectIteratorFunc func(it *ObjectIterator) error type ObjectIterator struct { path []interface{} - keys []interface{} + keys KeyPath cb ObjectIteratorFunc } @@ -13,12 +13,12 @@ func NewObjectIterator(o map[string]interface{}) *ObjectIterator { } } -func (it *ObjectIterator) KeyPath() []interface{} { +func (it *ObjectIterator) KeyPath() KeyPath { return it.keys } -func (it *ObjectIterator) KeyPathCopy() []interface{} { - ret := make([]interface{}, len(it.keys)) +func (it *ObjectIterator) KeyPathCopy() KeyPath { + ret := make(KeyPath, len(it.keys)) copy(ret, it.keys) return ret } @@ -45,10 +45,6 @@ func (it *ObjectIterator) SetValue(v interface{}) error { return SetChild(it.Parent(), it.Key(), v) } -func (it *ObjectIterator) JsonPath() string { - return KeyListToJsonPath(it.keys) -} - func (it *ObjectIterator) IterateLeafs(cb ObjectIteratorFunc) error { it.cb = cb return it.iterateInterface() From ad4316a37cad9d6a0b62124e5b97def115508d21 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 May 2022 15:09:48 +0200 Subject: [PATCH 0161/2268] fix: Add missing dot in concatenated json path --- pkg/k8s/k8s_cluster.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 160a0ad8b..3bbfe7a0c 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -384,8 +384,8 @@ func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.Unstructure fixContainer := func(p string) { fixPorts(p + ".ports") - fixStringType(p+"resources.limits", "cpu") - fixStringType(p+"resources.requests", "cpu") + fixStringType(p+".resources.limits", "cpu") + fixStringType(p+".resources.requests", "cpu") } fixContainers := func(p string) { From 487974f47eee61bb79e58cc23c5486b32e740818 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 May 2022 15:10:18 +0200 Subject: [PATCH 0162/2268] fix: Make MyJsonPath work with UnstructuredObject --- pkg/diff/managed_fields.go | 2 +- pkg/diff/normalize.go | 4 +-- pkg/k8s/k8s_cluster.go | 17 ++++++------ pkg/seal/secrets_loader_http.go | 2 +- pkg/utils/uo/jsonpath.go | 48 +++++++++++++++++++++++---------- 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index 36200458d..8dd6ce197 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -219,7 +219,7 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr if err != nil { return nil, nil, err } - err = j.Del(ret.Object) + err = j.Del(ret) if err != nil { return nil, nil, err } diff --git a/pkg/diff/normalize.go b/pkg/diff/normalize.go index f2599dc97..dd69cf073 100644 --- a/pkg/diff/normalize.go +++ b/pkg/diff/normalize.go @@ -164,7 +164,7 @@ func NormalizeObject(o_ *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreFo if err != nil { continue } - _ = jp.Del(o.Object) + _ = jp.Del(o) } } @@ -179,7 +179,7 @@ func NormalizeObject(o_ *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreFo if err != nil { continue } - _ = j.Del(o.Object) + _ = j.Del(o) } return o diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 3bbfe7a0c..51fb487b9 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -352,14 +352,15 @@ func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.Unstructure return } - ports, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfMaps(o.Object) + ports, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfObjects(o) if !found { return } for _, port := range ports { - if _, ok := port["protocol"]; !ok { - port["protocol"] = "TCP" + _, ok, _ := port.GetNestedField("protocol") + if !ok { + _ = port.SetNestedField("TCP", "protocol") } } } @@ -368,17 +369,17 @@ func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.Unstructure if !needsTypeConversionFix { return } - d, found, _ := uo.NewMyJsonPathMust(p).GetFirstMap(o.Object) + d, found, _ := uo.NewMyJsonPathMust(p).GetFirstObject(o) if !found { return } - v, ok := d[k] + v, ok, _ := d.GetNestedField(k) if !ok { return } _, ok = v.(string) if !ok { - d[k] = fmt.Sprintf("%v", v) + _ = d.SetNestedField(fmt.Sprintf("%v", v), k) } } @@ -389,7 +390,7 @@ func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.Unstructure } fixContainers := func(p string) { - containers, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfMaps(o.Object) + containers, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfObjects(o) if !found { return } @@ -399,7 +400,7 @@ func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.Unstructure } fixLimits := func(p string) { - limits, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfMaps(o.Object) + limits, found, _ := uo.NewMyJsonPathMust(p).GetFirstListOfObjects(o) if !found { return } diff --git a/pkg/seal/secrets_loader_http.go b/pkg/seal/secrets_loader_http.go index 25454a746..da0bba85d 100644 --- a/pkg/seal/secrets_loader_http.go +++ b/pkg/seal/secrets_loader_http.go @@ -117,7 +117,7 @@ func (s *SecretsLoader) loadSecretsHttp(source *types.SecretSource) (*uo.Unstruc if err != nil { return nil, err } - x, ok := p.GetFirst(respObj) + x, ok := p.GetFirstFromAny(respObj) if !ok { return nil, fmt.Errorf("%s not found in result from http request %s", *source.Http.JsonPath, source.Http.Url.String()) } diff --git a/pkg/utils/uo/jsonpath.go b/pkg/utils/uo/jsonpath.go index 3843259f2..5263f8639 100644 --- a/pkg/utils/uo/jsonpath.go +++ b/pkg/utils/uo/jsonpath.go @@ -87,11 +87,11 @@ func (j *MyJsonPath) ListMatchingFields(o *UnstructuredObject) ([]KeyPath, error return ret, nil } -func (j *MyJsonPath) Get(o interface{}) []interface{} { - return j.exp.Get(o) +func (j *MyJsonPath) Get(o *UnstructuredObject) []interface{} { + return j.exp.Get(o.Object) } -func (j *MyJsonPath) GetFirst(o interface{}) (interface{}, bool) { +func (j *MyJsonPath) GetFirst(o *UnstructuredObject) (interface{}, bool) { l := j.Get(o) if len(l) == 0 { return nil, false @@ -99,30 +99,50 @@ func (j *MyJsonPath) GetFirst(o interface{}) (interface{}, bool) { return l[0], true } -func (j *MyJsonPath) GetFirstMap(o interface{}) (map[string]interface{}, bool, error) { - o, found := j.GetFirst(o) +func (j *MyJsonPath) GetFromAny(o any) []interface{} { + return j.exp.Get(o) +} + +func (j *MyJsonPath) GetFirstFromAny(o any) (interface{}, bool) { + l := j.GetFromAny(o) + if len(l) == 0 { + return nil, false + } + return l[0], true +} + +func (j *MyJsonPath) GetFirstObject(o *UnstructuredObject) (*UnstructuredObject, bool, error) { + x, found := j.GetFirst(o) if !found { return nil, false, nil } - m, ok := o.(map[string]interface{}) + m, ok := x.(map[string]interface{}) if !ok { return nil, false, fmt.Errorf("child is not a map") } - return m, true, nil + return FromMap(m), true, nil } -func (j *MyJsonPath) GetFirstListOfMaps(o interface{}) ([]map[string]interface{}, bool, error) { - o, found := j.GetFirst(o) +func (j *MyJsonPath) GetFirstListOfObjects(o *UnstructuredObject) ([]*UnstructuredObject, bool, error) { + x, found := j.GetFirst(o) if !found { return nil, false, nil } - m, ok := o.([]map[string]interface{}) + l, ok := x.([]interface{}) if !ok { - return nil, false, fmt.Errorf("child is not a list of maps") + return nil, false, fmt.Errorf("child is not a list") + } + var ret []*UnstructuredObject + for _, x := range l { + m, ok := x.(map[string]interface{}) + if !ok { + return nil, false, fmt.Errorf("child is not a list of maps") + } + ret = append(ret, FromMap(m)) } - return m, true, nil + return ret, true, nil } -func (j *MyJsonPath) Del(o interface{}) error { - return j.exp.Del(o) +func (j *MyJsonPath) Del(o *UnstructuredObject) error { + return j.exp.Del(o.Object) } From a50be74a1a484cd1f4bfca175f3d805b6af16b72 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 May 2022 15:18:08 +0200 Subject: [PATCH 0163/2268] fix: Fix crash in loadSecretsHttp in case host is not reachable --- pkg/seal/secrets_loader_http.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/seal/secrets_loader_http.go b/pkg/seal/secrets_loader_http.go index da0bba85d..01582a9d1 100644 --- a/pkg/seal/secrets_loader_http.go +++ b/pkg/seal/secrets_loader_http.go @@ -67,7 +67,7 @@ func (s *SecretsLoader) doHttp(httpSource *types.SecretSourceHttp, username stri func (s *SecretsLoader) loadSecretsHttp(source *types.SecretSource) (*uo.UnstructuredObject, error) { resp, respBody, err := s.doHttp(source.Http, "", "") - if err != nil { + if err != nil && resp != nil && resp.StatusCode == http.StatusUnauthorized { chgs := challenge.ResponseChallenges(resp) if len(chgs) == 0 { return nil, err @@ -100,6 +100,8 @@ func (s *SecretsLoader) loadSecretsHttp(source *types.SecretSource) (*uo.Unstruc if err != nil { return nil, err } + } else if err != nil { + return nil, err } var respObj interface{} From e814c6fa86f8bf27d4b698c9e4d813ca308558b5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 May 2022 16:48:17 +0200 Subject: [PATCH 0164/2268] fix: Add workaround to fix progress printing when asking for continuation --- cmd/kluctl/commands/cmd_deploy.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index a0db7c000..1e514ca9b 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -6,6 +6,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" + "time" ) type deployCmd struct { @@ -77,6 +78,9 @@ func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { } func (cmd *deployCmd) diffResultCb(diffResult *types.CommandResult) error { + // workaround to ensure that progress has been completely written/updated + time.Sleep(130 * time.Millisecond) + err := outputCommandResult(nil, diffResult) if err != nil { return err From 5b7949b2246b25340822126d6d61b7649167d660 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 May 2022 16:48:43 +0200 Subject: [PATCH 0165/2268] feat: Make target.name the default output pattern for sealed secrets --- cmd/kluctl/commands/cmd_seal.go | 9 ++++++++- pkg/deployment/commands/seal.go | 14 ++++++-------- pkg/deployment/deployment_item.go | 6 ++---- pkg/deployment/deployment_project.go | 19 +++++++++++-------- pkg/kluctl_project/target_context.go | 3 +++ 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index b7a4c01f4..5cd3d78d3 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -115,7 +115,14 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L return doFail(err) } - cmd2 := commands.NewSealCommand(ctx.targetCtx.DeploymentCollection) + outputPattern := targetName + if ctx.targetCtx.DeploymentProject.Config.SealedSecrets != nil && ctx.targetCtx.DeploymentProject.Config.SealedSecrets.OutputPattern != nil { + // the outputPattern is rendered already at this point, meaning that for example + // '{{ cluster.name }}/{{ target.name }}' will already be rendered to 'my-cluster/my-target' + outputPattern = *ctx.targetCtx.DeploymentProject.Config.SealedSecrets.OutputPattern + } + + cmd2 := commands.NewSealCommand(ctx.targetCtx.DeploymentCollection, outputPattern) err = cmd2.Run(sealer) if err != nil { diff --git a/pkg/deployment/commands/seal.go b/pkg/deployment/commands/seal.go index 69110cf0d..0397133ca 100644 --- a/pkg/deployment/commands/seal.go +++ b/pkg/deployment/commands/seal.go @@ -11,20 +11,18 @@ import ( ) type SealCommand struct { - c *deployment.DeploymentCollection + c *deployment.DeploymentCollection + outputPattern string } -func NewSealCommand(c *deployment.DeploymentCollection) *SealCommand { +func NewSealCommand(c *deployment.DeploymentCollection, outputPattern string) *SealCommand { return &SealCommand{ - c: c, + c: c, + outputPattern: outputPattern, } } func (cmd *SealCommand) Run(sealer *seal.Sealer) error { - if cmd.c.Project.Config.SealedSecrets == nil || cmd.c.Project.Config.SealedSecrets.OutputPattern == nil { - return fmt.Errorf("sealedSecrets.outputPattern is not defined") - } - err := filepath.WalkDir(cmd.c.RenderDir, func(p string, d fs.DirEntry, err error) error { if !strings.HasSuffix(p, deployment.SealmeExt) { return nil @@ -34,7 +32,7 @@ func (cmd *SealCommand) Run(sealer *seal.Sealer) error { if err != nil { return err } - relTargetFile := filepath.Join(filepath.Dir(relPath), *cmd.c.Project.Config.SealedSecrets.OutputPattern, filepath.Base(p)) + relTargetFile := filepath.Join(filepath.Dir(relPath), cmd.outputPattern, filepath.Base(p)) targetFile, err := securejoin.SecureJoin(cmd.c.Project.SealedSecretsDir, relTargetFile) if err != nil { return err diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 7f4912747..e4ccacd3a 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -230,11 +230,9 @@ func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { fname := filepath.Base(p) baseError := fmt.Sprintf("failed to resolve SealedSecret %s", filepath.Clean(filepath.Join(di.Project.dir, resource))) - if sealedSecretsDir == nil { - return fmt.Errorf("%s. Sealed secrets dir could not be determined", baseError) - } + // ensure we're not leaving the .sealed-secrets dir - sourcePath, err := securejoin.SecureJoin(baseSourcePath, filepath.Join(di.relRenderedDir, subdir, relDir, *sealedSecretsDir, fname)) + sourcePath, err := securejoin.SecureJoin(baseSourcePath, filepath.Join(di.relRenderedDir, subdir, relDir, sealedSecretsDir, fname)) if err != nil { return err } diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index f0c0cedd3..3446c5ca4 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -20,9 +20,11 @@ var warnOnce utils.OnceByKey type DeploymentProject struct { ctx context.Context - VarsCtx *jinja2.VarsCtx - dir string - SealedSecretsDir string + VarsCtx *jinja2.VarsCtx + dir string + + SealedSecretsDir string + DefaultSealedSecretsOutputPattern string Config types.DeploymentProjectConfig @@ -200,7 +202,8 @@ func (p *DeploymentProject) loadIncludes(k *k8s.K8sCluster) error { return err } - newProject, err := NewDeploymentProject(p.ctx, k, varsCtx, incDir, p.SealedSecretsDir, p) + newProject, err := NewDeploymentProject(p.ctx, k, varsCtx, incDir, + p.SealedSecretsDir, p) if err != nil { return err } @@ -211,12 +214,12 @@ func (p *DeploymentProject) loadIncludes(k *k8s.K8sCluster) error { return nil } -func (p *DeploymentProject) getSealedSecretsDir() *string { +func (p *DeploymentProject) getSealedSecretsDir() string { root := p.getRootProject() - if root.Config.SealedSecrets == nil { - return nil + if root.Config.SealedSecrets == nil || root.Config.SealedSecrets.OutputPattern == nil { + return root.DefaultSealedSecretsOutputPattern } - return root.Config.SealedSecrets.OutputPattern + return *root.Config.SealedSecrets.OutputPattern } func (p *DeploymentProject) getRootProject() *DeploymentProject { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 1d6398ae0..3d3bd22f4 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -115,6 +115,9 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, clientConfig if err != nil { return nil, err } + + d.DefaultSealedSecretsOutputPattern = targetName + c, err := deployment.NewDeploymentCollection(ctx, d, images, inclusion, renderOutputDir, forSeal) if err != nil { return nil, err From 41260f4e8d5f5ec98002fc934bacd3139d4afe60 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 May 2022 16:48:55 +0200 Subject: [PATCH 0166/2268] tests: Fix e2e tests compilation --- e2e/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/utils.go b/e2e/utils.go index e52c131ac..97df8657c 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -96,7 +96,7 @@ func assertNestedFieldEquals(t *testing.T, o *uo.UnstructuredObject, expected in t.Fatal(err) } if !ok { - t.Fatalf("field %s not found in object", uo.KeyListToJsonPath(keys)) + t.Fatalf("field %s not found in object", uo.KeyPath(keys).ToJsonPath()) } if !reflect.DeepEqual(v, expected) { t.Fatalf("%v != %v", v, expected) From 04d547f67eb60712036303fc69388eaa82b092d8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 17 May 2022 18:21:02 +0200 Subject: [PATCH 0167/2268] feat: Make cluster per target optional and default to the current context --- cmd/kluctl/commands/cmd_archive.go | 2 +- cmd/kluctl/commands/cmd_list_targets.go | 2 +- cmd/kluctl/commands/cmd_seal.go | 6 +-- cmd/kluctl/commands/completion.go | 2 +- cmd/kluctl/commands/utils.go | 42 ++++++++++++++----- pkg/kluctl_project/project.go | 4 -- pkg/kluctl_project/project_load.go | 4 ++ pkg/kluctl_project/target_context.go | 56 +++++++++++++++++++------ pkg/kluctl_project/targets.go | 2 +- pkg/types/kluctl_project.go | 2 +- 10 files changed, 88 insertions(+), 34 deletions(-) diff --git a/cmd/kluctl/commands/cmd_archive.go b/cmd/kluctl/commands/cmd_archive.go index a6a2f349d..d5c22af07 100644 --- a/cmd/kluctl/commands/cmd_archive.go +++ b/cmd/kluctl/commands/cmd_archive.go @@ -17,7 +17,7 @@ func (cmd *archiveCmd) Help() string { } func (cmd *archiveCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return p.WriteArchive(cmd.OutputArchive, cmd.ProjectFlags.OutputMetadata == "") }) } diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index 64921e6f7..f8a6b5626 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -17,7 +17,7 @@ func (cmd *listTargetsCmd) Help() string { } func (cmd *listTargetsCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var result []*types.Target for _, t := range p.DynamicTargets { result = append(result, t.Target) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 5cd3d78d3..ca2a93981 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -106,7 +106,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L } } - clusterConfig, err := p.LoadClusterConfig(ctx.targetCtx.Target.Cluster) + clusterConfig, _, err := p.LoadClusterConfig(ctx.targetCtx.Target.Cluster) if err != nil { return doFail(err) } @@ -134,7 +134,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L } func (cmd *sealCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { hadError := false secretsLoader := seal.NewSecretsLoader(p, cmd.SecretsDir) @@ -145,7 +145,7 @@ func (cmd *sealCmd) Run() error { if cmd.Target != "" && cmd.Target != target.Target.Name { continue } - if cmd.Cluster != "" && cmd.Cluster != target.Target.Cluster { + if cmd.Cluster != "" && target.Target.Cluster != nil && cmd.Cluster != *target.Target.Cluster { continue } if target.Target.SealingConfig == nil { diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 1e2b383a4..a541ab565 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -51,7 +51,7 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { // let's not update git caches too often projectArgs.GitCacheUpdateInterval = time.Second * 60 - return withKluctlProjectFromArgs(*projectArgs, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(*projectArgs, false, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return cb(ctx, p) }) } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index c6f69df50..c283ea0de 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -17,10 +17,11 @@ import ( "io/ioutil" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/clientcmd/api" "os" ) -func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { +func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { var url *git_url.GitUrl if projectFlags.ProjectUrl != "" { var err error @@ -68,6 +69,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b AllowGitClone: projectFlags.FromArchive == "", GitAuthProviders: auth.NewDefaultAuthProviders(), GitUpdateInterval: projectFlags.GitCacheUpdateInterval, + ClientConfigGetter: clientConfigGetter(forCompletion), } ctx, cancel := context.WithTimeout(cliCtx, projectFlags.Timeout) @@ -110,7 +112,7 @@ type commandCtx struct { } func withProjectCommandContext(args projectTargetCommandArgs, cb func(ctx *commandCtx) error) error { - return withKluctlProjectFromArgs(args.projectFlags, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(args.projectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return withProjectTargetCommandContext(ctx, args, p, cb) }) } @@ -153,16 +155,12 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm renderOutputDir = tmpDir } - clientConfigGetter := func(context string) (*rest.Config, error) { - if args.forCompletion { - return nil, nil - } - configLoadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - configOverrides := &clientcmd.ConfigOverrides{CurrentContext: context} - return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(configLoadingRules, configOverrides).ClientConfig() + var clusterName *string + if args.projectFlags.Cluster != "" { + clusterName = &args.projectFlags.Cluster } - targetCtx, err := p.NewTargetContext(ctx, clientConfigGetter, args.targetFlags.Target, args.projectFlags.Cluster, + targetCtx, err := p.NewTargetContext(ctx, args.targetFlags.Target, clusterName, args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, optionArgs, args.forSeal, images, inclusion, renderOutputDir) @@ -185,3 +183,27 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm return cb(cmdCtx) } + +func clientConfigGetter(forCompletion bool) func(context *string) (*rest.Config, *api.Config, error) { + return func(context *string) (*rest.Config, *api.Config, error) { + if forCompletion { + return nil, nil, nil + } + + configLoadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + if context != nil { + configOverrides.CurrentContext = *context + } + clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(configLoadingRules, configOverrides) + rawConfig, err := clientConfig.RawConfig() + if err != nil { + return nil, nil, err + } + restConfig, err := clientConfig.ClientConfig() + if err != nil { + return nil, nil, err + } + return restConfig, &rawConfig, nil + } +} diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 3b0d0efff..6592651d2 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -59,10 +59,6 @@ func (c *LoadedKluctlProject) FindDynamicTarget(name string) (*types2.DynamicTar return nil, fmt.Errorf("target %s not existent in kluctl project config", name) } -func (c *LoadedKluctlProject) LoadClusterConfig(clusterName string) (*types2.ClusterConfig, error) { - return types2.LoadClusterConfig(c.ClustersDir, clusterName) -} - func (c *LoadedKluctlProject) CheckDynamicArg(target *types2.Target, argName string, argValue string) error { var dynArg *types2.DynamicArg for _, x := range target.DynamicArgs { diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index bd99e8291..a5b9ca83e 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -10,6 +10,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd/api" "os" "path/filepath" "time" @@ -31,6 +33,8 @@ type LoadKluctlProjectArgs struct { GitAuthProviders *auth2.GitAuthProviders GitUpdateInterval time.Duration + + ClientConfigGetter func(context *string) (*rest.Config, *api.Config, error) } type gitProjectInfo struct { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 3d3bd22f4..7e647c2d2 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -11,6 +11,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd/api" "path/filepath" ) @@ -23,7 +24,7 @@ type TargetContext struct { DeploymentCollection *deployment.DeploymentCollection } -func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, clientConfigGetter func(context string) (*rest.Config, error), targetName string, clusterName string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { +func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName string, clusterName *string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { deploymentDir, err := filepath.Abs(p.DeploymentDir) if err != nil { return nil, err @@ -42,26 +43,18 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, clientConfig } } - if clusterName == "" { - if target == nil { - return nil, fmt.Errorf("you must specify an existing --cluster when not providing a --target") - } + if clusterName == nil && target != nil { clusterName = target.Cluster } - clusterConfig, err := p.LoadClusterConfig(clusterName) - if err != nil { - return nil, err - } - - clientConfig, err := clientConfigGetter(clusterConfig.Cluster.Context) + clusterConfig, clientConfig, err := p.LoadClusterConfig(clusterName) if err != nil { return nil, err } var k *k8s.K8sCluster if clientConfig != nil { - s := status.Start(ctx, "Initializing k8s client") + s := status.Start(ctx, fmt.Sprintf("Initializing k8s client for context %s", clusterConfig.Cluster.Context)) k, err = k8s.NewK8sCluster(ctx, clientConfig, dryRun) if err != nil { s.Failed() @@ -134,3 +127,42 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, clientConfig return targetCtx, nil } + +func (p *LoadedKluctlProject) LoadClusterConfig(clusterName *string) (*types.ClusterConfig, *rest.Config, error) { + var err error + var clusterConfig *types.ClusterConfig + + if clusterName != nil { + clusterConfig, err = types.LoadClusterConfig(p.ClustersDir, *clusterName) + if err != nil { + return nil, nil, err + } + } + + var clientConfig *rest.Config + if clusterConfig != nil { + clientConfig, _, err = p.loadArgs.ClientConfigGetter(&clusterConfig.Cluster.Context) + if err != nil { + return nil, nil, err + } + } else { + var rawConfig *api.Config + clientConfig, rawConfig, err = p.loadArgs.ClientConfigGetter(nil) + if err != nil { + return nil, nil, err + } + ctx, ok := rawConfig.Contexts[rawConfig.CurrentContext] + if !ok { + return nil, nil, fmt.Errorf("context %s not found", rawConfig.CurrentContext) + } + clusterConfig = &types.ClusterConfig{ + Cluster: &types.ClusterConfig2{ + Name: ctx.Cluster, + Context: rawConfig.CurrentContext, + Vars: uo.New(), + }, + } + } + + return clusterConfig, clientConfig, nil +} diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 8e139fa64..c911cb5a8 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -89,7 +89,7 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, return nil, err } - cc, err := types.LoadClusterConfig(c.ClustersDir, target.Cluster) + cc, _, err := c.LoadClusterConfig(target.Cluster) if err == nil { err = varsCtx.UpdateChildFromStruct("cluster", cc.Cluster) if err != nil { diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index b0ea0257f..ad2982be6 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -29,7 +29,7 @@ type SealingConfig struct { type Target struct { Name string `yaml:"name" validate:"required"` - Cluster string `yaml:"cluster" validate:"required"` + Cluster *string `yaml:"cluster,omitempty"` Args *uo.UnstructuredObject `yaml:"args,omitempty"` DynamicArgs []DynamicArg `yaml:"dynamicArgs,omitempty"` TargetConfig *ExternalTargetConfig `yaml:"targetConfig,omitempty"` From 1389ac304319da892b1a0580669feba1d937d4be Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 17 May 2022 18:27:54 +0200 Subject: [PATCH 0168/2268] fix: Don't swallow sealing errors --- cmd/kluctl/commands/cmd_seal.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index ca2a93981..45446a91a 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -173,6 +173,7 @@ func (cmd *sealCmd) Run() error { err := cmd.runCmdSealForTarget(ctx, p, sealTarget.Name, secretsLoader) if err != nil { hadError = true + status.Error(ctx, err.Error()) } } if hadError { From 1ca9f460ca4a00c5b8022e6eea0621da71f0dd9b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 10:26:55 +0200 Subject: [PATCH 0169/2268] refactor: Introduce MirroredGitRepoCollection for git cache handling --- pkg/git/repo_collection.go | 90 ++++++++++++++++++++++++++++++ pkg/kluctl_project/git.go | 55 +++--------------- pkg/kluctl_project/load.go | 5 +- pkg/kluctl_project/project.go | 4 +- pkg/kluctl_project/project_load.go | 8 +-- pkg/kluctl_project/targets.go | 17 ++---- 6 files changed, 111 insertions(+), 68 deletions(-) create mode 100644 pkg/git/repo_collection.go diff --git a/pkg/git/repo_collection.go b/pkg/git/repo_collection.go new file mode 100644 index 000000000..fbdf14ed6 --- /dev/null +++ b/pkg/git/repo_collection.go @@ -0,0 +1,90 @@ +package git + +import ( + "context" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/git/auth" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "sync" + "time" +) + +type MirroredGitRepoCollection struct { + ctx context.Context + authProviders *auth.GitAuthProviders + updateInterval time.Duration + repos map[string]*entry + mutex sync.Mutex +} + +type entry struct { + mr *MirroredGitRepo + updateMutex sync.Mutex +} + +func NewMirroredGitRepoCollection(ctx context.Context, authProviders *auth.GitAuthProviders, updateInterval time.Duration) *MirroredGitRepoCollection { + return &MirroredGitRepoCollection{ + ctx: ctx, + authProviders: authProviders, + updateInterval: updateInterval, + repos: map[string]*entry{}, + } +} + +func (g *MirroredGitRepoCollection) UnlockAll() { + g.mutex.Lock() + defer g.mutex.Unlock() + + for _, e := range g.repos { + _ = e.mr.Unlock() + } +} + +func (g *MirroredGitRepoCollection) GetMirroredGitRepo(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*MirroredGitRepo, error) { + e, err := func() (*entry, error) { + g.mutex.Lock() + defer g.mutex.Unlock() + + e, ok := g.repos[url.NormalizedRepoKey()] + if !ok { + if !allowCreate { + return nil, fmt.Errorf("git repo %s not found", url.NormalizedRepoKey()) + } + mr, err := NewMirroredGitRepo(g.ctx, url) + if err != nil { + return nil, err + } + e = &entry{ + mr: mr, + } + g.repos[url.NormalizedRepoKey()] = e + + if lockRepo { + err = e.mr.Lock() + if err != nil { + return nil, err + } + } + } + return e, nil + }() + if err != nil { + return nil, err + } + + e.updateMutex.Lock() + defer e.updateMutex.Unlock() + + if update && !e.mr.HasUpdated() { + if time.Now().Sub(e.mr.LastUpdateTime()) <= g.updateInterval { + e.mr.SetUpdated(true) + } else { + err = e.mr.Update(g.authProviders) + if err != nil { + return nil, err + } + } + } + + return e.mr, nil +} diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 1be037384..b64c6ebb7 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -12,20 +12,8 @@ import ( "sort" "strings" "sync" - "time" ) -func (c *LoadedKluctlProject) updateGitCache(mr *git.MirroredGitRepo) error { - if mr.HasUpdated() { - return nil - } - if time.Now().Sub(mr.LastUpdateTime()) <= c.loadArgs.GitUpdateInterval { - mr.SetUpdated(true) - return nil - } - return mr.Update(c.loadArgs.GitAuthProviders) -} - func (c *LoadedKluctlProject) updateGitCaches() error { var waitGroup sync.WaitGroup var firstError error @@ -39,26 +27,12 @@ func (c *LoadedKluctlProject) updateGitCaches() error { } } - doUpdateRepo := func(repo *git.MirroredGitRepo) error { - return repo.WithLock(func() error { - return c.updateGitCache(repo) - }) - } doUpdateGitProject := func(u git_url.GitUrl) error { - mr, ok := c.mirroredRepos[u.NormalizedRepoKey()] - if ok { - return nil - } - mr, err := git.NewMirroredGitRepo(c.ctx, u) - if err != nil { - return err - } - c.mirroredRepos[u.NormalizedRepoKey()] = mr - waitGroup.Add(1) go func() { defer waitGroup.Done() - err := doUpdateRepo(mr) + + _, err := c.grc.GetMirroredGitRepo(u, true, true, true) if err != nil { doError(fmt.Errorf("failed to update git project %v: %v", u.String(), err)) } @@ -107,33 +81,18 @@ func (c *LoadedKluctlProject) updateGitCaches() error { return firstError } -func (c *LoadedKluctlProject) cloneGitProject(gitProject *types2.GitProject, targetDir string, doLock bool) (info git.GitRepoInfo, err error) { +func (c *LoadedKluctlProject) cloneGitProject(gitProject *types2.GitProject, targetDir string) (info git.GitRepoInfo, err error) { err = os.MkdirAll(filepath.Join(c.TmpDir, "git"), 0o700) if err != nil { return } - mr, ok := c.mirroredRepos[gitProject.Url.NormalizedRepoKey()] - if !ok { - mr, err = git.NewMirroredGitRepo(c.ctx, gitProject.Url) - if err != nil { - return - } - c.mirroredRepos[gitProject.Url.NormalizedRepoKey()] = mr - err = mr.Lock() - if err != nil { - return - } - defer mr.Unlock() + mr, err := c.grc.GetMirroredGitRepo(gitProject.Url, true, true, true) + if err != nil { + return git.GitRepoInfo{}, err } - err = mr.MaybeWithLock(doLock, func() error { - err := c.updateGitCache(mr) - if err != nil { - return err - } - return mr.CloneProject(gitProject.Ref, targetDir) - }) + err = mr.CloneProject(gitProject.Ref, targetDir) if err != nil { return } diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 398a6c958..5938483bd 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -9,14 +9,17 @@ import ( ) func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*LoadedKluctlProject, error) { + grc := git.NewMirroredGitRepoCollection(ctx, args.GitAuthProviders, args.GitUpdateInterval) + defer grc.UnlockAll() + p := &LoadedKluctlProject{ ctx: ctx, loadArgs: args, TmpDir: tmpDir, J2: j2, + grc: grc, involvedRepos: map[string][]types.InvolvedRepo{}, - mirroredRepos: map[string]*git.MirroredGitRepo{}, } if args.FromArchive != "" { diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 6592651d2..5196cc387 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -25,12 +25,12 @@ type LoadedKluctlProject struct { sealedSecretsDir string involvedRepos map[string][]types2.InvolvedRepo - mirroredRepos map[string]*git.MirroredGitRepo Config types2.KluctlProject DynamicTargets []*types2.DynamicTarget - J2 *jinja2.Jinja2 + J2 *jinja2.Jinja2 + grc *git.MirroredGitRepoCollection } func (c *LoadedKluctlProject) GetMetadata() *types2.ProjectMetadata { diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index a5b9ca83e..c1d9e3be9 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -62,7 +62,7 @@ func (c *LoadedKluctlProject) localProject(dir string) gitProjectInfo { } } -func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defaultSubDir string, doLock bool, doAddInvolvedRepo bool) (ret gitProjectInfo, err error) { +func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defaultSubDir string, doAddInvolvedRepo bool) (ret gitProjectInfo, err error) { cloneDir, err := c.buildCloneDir(gitProject.Url, gitProject.Ref) if err != nil { return @@ -85,7 +85,7 @@ func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defa } var ri git.GitRepoInfo - ri, err = c.cloneGitProject(gitProject, cloneDir, doLock) + ri, err = c.cloneGitProject(gitProject, cloneDir) if err != nil { return } @@ -136,7 +136,7 @@ func (c *LoadedKluctlProject) loadExternalProject(ep *types2.ExternalProject, de if ep.Project != nil { // pointing to an actual external project, so let's try to clone it - return c.loadGitProject(ep.Project, defaultGitSubDir, true, true) + return c.loadGitProject(ep.Project, defaultGitSubDir, true) } // ExternalProject was provided but without an external repo url, so point into the kluctl project. @@ -164,7 +164,7 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { gi, err := c.loadGitProject(&types2.GitProject{ Url: *c.loadArgs.ProjectUrl, Ref: c.loadArgs.ProjectRef, - }, "", true, true) + }, "", true) if err != nil { return err } diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index c911cb5a8..bf43b50b9 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -134,9 +134,9 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsSimple(baseTarget *types.Targ } func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { - mr, ok := c.mirroredRepos[baseTarget.TargetConfig.Project.Url.NormalizedRepoKey()] - if !ok { - return nil, fmt.Errorf("repo not found in mirroredRepos, this is unexpected and probably a bug") + mr, err := c.grc.GetMirroredGitRepo(baseTarget.TargetConfig.Project.Url, true, true, true) + if err != nil { + return nil, err } if baseTarget.TargetConfig.Ref != nil && baseTarget.TargetConfig.RefPattern != nil { @@ -224,15 +224,6 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTarge wp := utils.NewDebuggerAwareWorkerPool(8) defer wp.StopWait(false) - // lock all involved repos first - for _, mr := range c.mirroredRepos { - err := mr.Lock() - if err != nil { - return err - } - defer mr.Unlock() - } - uniqueClones := make(map[string]interface{}) var mutex sync.Mutex @@ -251,7 +242,7 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTarge gitProject := *targetInfo.gitProject gitProject.Ref = *targetInfo.ref - gi, err := c.loadGitProject(&gitProject, "", false, false) + gi, err := c.loadGitProject(&gitProject, "", false) mutex.Lock() defer mutex.Unlock() if err != nil { From 2c2eb04d22306915586e9530aaa743e08f59d8ef Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 11:36:50 +0200 Subject: [PATCH 0170/2268] fix: Override CurrentContext in api.Config as it is not done by RawConfig() --- cmd/kluctl/commands/utils.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index c283ea0de..a7ad73631 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -200,6 +200,9 @@ func clientConfigGetter(forCompletion bool) func(context *string) (*rest.Config, if err != nil { return nil, nil, err } + if context != nil { + rawConfig.CurrentContext = *context + } restConfig, err := clientConfig.ClientConfig() if err != nil { return nil, nil, err From d5f79f7ff004e2ecce7064fb265902d57fb04ae6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 11:38:39 +0200 Subject: [PATCH 0171/2268] feat: Allow to set target cluster via target.context --- cmd/kluctl/commands/cmd_seal.go | 2 +- pkg/kluctl_project/target_context.go | 8 +++++--- pkg/kluctl_project/targets.go | 2 +- pkg/types/kluctl_project.go | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 45446a91a..a25d36236 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -106,7 +106,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L } } - clusterConfig, _, err := p.LoadClusterConfig(ctx.targetCtx.Target.Cluster) + clusterConfig, _, err := p.LoadClusterConfig(ctx.targetCtx.Target.Cluster, ctx.targetCtx.Target.Context) if err != nil { return doFail(err) } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 7e647c2d2..7900fc972 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -43,11 +43,13 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s } } + var contextName *string if clusterName == nil && target != nil { clusterName = target.Cluster + contextName = target.Context } - clusterConfig, clientConfig, err := p.LoadClusterConfig(clusterName) + clusterConfig, clientConfig, err := p.LoadClusterConfig(clusterName, contextName) if err != nil { return nil, err } @@ -128,7 +130,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s return targetCtx, nil } -func (p *LoadedKluctlProject) LoadClusterConfig(clusterName *string) (*types.ClusterConfig, *rest.Config, error) { +func (p *LoadedKluctlProject) LoadClusterConfig(clusterName *string, contextName *string) (*types.ClusterConfig, *rest.Config, error) { var err error var clusterConfig *types.ClusterConfig @@ -147,7 +149,7 @@ func (p *LoadedKluctlProject) LoadClusterConfig(clusterName *string) (*types.Clu } } else { var rawConfig *api.Config - clientConfig, rawConfig, err = p.loadArgs.ClientConfigGetter(nil) + clientConfig, rawConfig, err = p.loadArgs.ClientConfigGetter(contextName) if err != nil { return nil, nil, err } diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index bf43b50b9..0eca76935 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -89,7 +89,7 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, return nil, err } - cc, _, err := c.LoadClusterConfig(target.Cluster) + cc, _, err := c.LoadClusterConfig(target.Cluster, target.Context) if err == nil { err = varsCtx.UpdateChildFromStruct("cluster", cc.Cluster) if err != nil { diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index ad2982be6..dc1276557 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -30,6 +30,7 @@ type SealingConfig struct { type Target struct { Name string `yaml:"name" validate:"required"` Cluster *string `yaml:"cluster,omitempty"` + Context *string `yaml:"context,omitempty"` Args *uo.UnstructuredObject `yaml:"args,omitempty"` DynamicArgs []DynamicArg `yaml:"dynamicArgs,omitempty"` TargetConfig *ExternalTargetConfig `yaml:"targetConfig,omitempty"` From 0acb6fddfdaeabdc913d5bafce0882a89be82bdf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 11:39:17 +0200 Subject: [PATCH 0172/2268] fix: Merge target vars into varsCtx before checking required args --- pkg/kluctl_project/target_context.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 7900fc972..156b3ed12 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -70,6 +70,11 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s if err != nil { return nil, err } + targetVars, err := uo.FromStruct(target) + if err != nil { + return nil, err + } + varsCtx.UpdateChild("target", targetVars) allArgs := uo.New() @@ -100,12 +105,6 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s varsCtx.UpdateChild("args", allArgs) - targetVars, err := uo.FromStruct(target) - if err != nil { - return nil, err - } - varsCtx.UpdateChild("target", targetVars) - d, err := deployment.NewDeploymentProject(ctx, k, varsCtx, deploymentDir, p.sealedSecretsDir, nil) if err != nil { return nil, err From 911faab7bf0a91da6e6b9d65edfc9a5cb8f42ad6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 11:42:06 +0200 Subject: [PATCH 0173/2268] feat: Allow to load vars from git --- pkg/git/mirrored_repo.go | 46 ++++++++++++++++++++++++++++ pkg/jinja2/vars.go | 34 +++++++++++++++++++- pkg/kluctl_project/target_context.go | 2 +- pkg/kluctl_project/targets.go | 2 +- pkg/types/deployment.go | 11 +++++++ 5 files changed, 92 insertions(+), 3 deletions(-) diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 874942200..a18128a5b 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -6,6 +6,7 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/status" @@ -342,6 +343,51 @@ func (g *MirroredGitRepo) CloneProject(ref string, targetDir string) error { return nil } +func (g *MirroredGitRepo) ReadFile(ref string, path string) ([]byte, error) { + if !g.fileLock.Locked() || !g.hasUpdated { + panic("tried to read a file from a project that is not locked/updated") + } + + doError := func(err error) ([]byte, error) { + return nil, fmt.Errorf("failed to read file from git repostory: %w", err) + } + + r, err := git.PlainOpen(g.mirrorDir) + if err != nil { + return nil, err + } + + if ref == "" { + ref = "HEAD" + } + + h, err := r.ResolveRevision(plumbing.Revision(ref)) + if err != nil { + return doError(err) + } + + commit, err := object.GetCommit(r.Storer, *h) + if err != nil { + return doError(err) + } + tree, err := commit.Tree() + if err != nil { + return doError(err) + } + + f, err := tree.File(path) + if err != nil { + return doError(err) + } + reader, err := f.Reader() + if err != nil { + return doError(err) + } + defer reader.Close() + + return ioutil.ReadAll(reader) +} + func buildMirrorRepoName(u git_url.GitUrl) string { r := filepath.Base(u.Path) r = strings.ReplaceAll(r, "/", "-") diff --git a/pkg/jinja2/vars.go b/pkg/jinja2/vars.go index 75dc2af49..c1d97103b 100644 --- a/pkg/jinja2/vars.go +++ b/pkg/jinja2/vars.go @@ -2,21 +2,27 @@ package jinja2 import ( "fmt" + "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" + "io/ioutil" + "os" ) type VarsCtx struct { J2 *Jinja2 + grc *git.MirroredGitRepoCollection Vars *uo.UnstructuredObject } -func NewVarsCtx(j2 *Jinja2) *VarsCtx { +func NewVarsCtx(j2 *Jinja2, grc *git.MirroredGitRepoCollection) *VarsCtx { vc := &VarsCtx{ J2: j2, + grc: grc, Vars: uo.New(), } return vc @@ -25,6 +31,7 @@ func NewVarsCtx(j2 *Jinja2) *VarsCtx { func (vc *VarsCtx) Copy() *VarsCtx { cp := &VarsCtx{ J2: vc.J2, + grc: vc.grc, Vars: vc.Vars.Clone(), } return cp @@ -56,6 +63,11 @@ func (vc *VarsCtx) LoadVarsList(k *k8s.K8sCluster, searchDirs []string, varsList if err != nil { return err } + } else if v.Git != nil { + err := vc.loadVarsGitFile(v.Git) + if err != nil { + return err + } } else if v.ClusterConfigMap != nil { ref := k8s2.NewObjectRef("", "v1", "ConfigMap", v.ClusterConfigMap.Name, v.ClusterConfigMap.Namespace) err := vc.loadVarsFromK8sObject(k, ref, v.ClusterConfigMap.Key) @@ -86,6 +98,26 @@ func (vc *VarsCtx) loadVarsFile(p string, searchDirs []string) error { return nil } +func (vc *VarsCtx) loadVarsGitFile(gitFile *types.VarsListItemGit) error { + mr, err := vc.grc.GetMirroredGitRepo(gitFile.Url, true, true, true) + if err != nil { + return fmt.Errorf("failed to load vars from git repository %s: %w", gitFile.Url.String(), err) + } + + tmpDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "git-vars") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + file, err := mr.ReadFile(gitFile.Ref, gitFile.Path) + if err != nil { + return fmt.Errorf("failed to load vars from git repository %s and path %s: %w", gitFile.Url.String(), gitFile.Path, err) + } + + return vc.loadVarsFromString(string(file)) +} + func (vc *VarsCtx) loadVarsFromK8sObject(k *k8s.K8sCluster, ref k8s2.ObjectRef, key string) error { if k == nil { return nil diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 156b3ed12..ba4fcf7f2 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -65,7 +65,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s s.Success() } - varsCtx := jinja2.NewVarsCtx(p.J2) + varsCtx := jinja2.NewVarsCtx(p.J2, p.grc) err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) if err != nil { return nil, err diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 0eca76935..4224a0c09 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -83,7 +83,7 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, var errors []error curTarget := target for i := 0; i < 10; i++ { - varsCtx := jinja2.NewVarsCtx(c.J2) + varsCtx := jinja2.NewVarsCtx(c.J2, c.grc) err := varsCtx.UpdateChildFromStruct("target", curTarget) if err != nil { return nil, err diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index e2069d00f..8a84675b3 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -2,6 +2,7 @@ package types import ( "github.com/go-playground/validator/v10" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" ) @@ -49,6 +50,12 @@ type DeploymentArg struct { Default interface{} `yaml:"default,omitempty"` } +type VarsListItemGit struct { + Url git_url.GitUrl `yaml:"url" validate:"required"` + Ref string `yaml:"ref,omitempty"` + Path string `yaml:"path" validate:"required"` +} + type VarsListItemClusterConfigMapOrSecret struct { Name string `yaml:"name" validate:"required"` Namespace string `yaml:"namespace,omitempty"` @@ -58,6 +65,7 @@ type VarsListItemClusterConfigMapOrSecret struct { type VarsListItem struct { Values *uo.UnstructuredObject `yaml:"values,omitempty"` File *string `yaml:"file,omitempty"` + Git *VarsListItemGit `yaml:"git,omitempty"` ClusterConfigMap *VarsListItemClusterConfigMapOrSecret `yaml:"clusterConfigMap,omitempty"` ClusterSecret *VarsListItemClusterConfigMapOrSecret `yaml:"clusterSecret,omitempty"` } @@ -71,6 +79,9 @@ func ValidateVarsListItem(sl validator.StructLevel) { if s.File != nil { count += 1 } + if s.Git != nil { + count += 1 + } if s.ClusterConfigMap != nil { count += 1 } From 45af6d4d9a0c9ca5f95df55a80fc6e13b79b13ce Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 11:44:36 +0200 Subject: [PATCH 0174/2268] refactor: Move VarsCtx into own package --- pkg/deployment/deployment_project.go | 6 +++--- pkg/deployment/external_args.go | 4 ++-- pkg/kluctl_project/target_context.go | 4 ++-- pkg/kluctl_project/targets.go | 4 ++-- pkg/{jinja2 => vars}/vars.go | 7 ++++--- 5 files changed, 13 insertions(+), 12 deletions(-) rename pkg/{jinja2 => vars}/vars.go (96%) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 3446c5ca4..5004917fb 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -3,12 +3,12 @@ package deployment import ( "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" "path/filepath" "reflect" @@ -20,7 +20,7 @@ var warnOnce utils.OnceByKey type DeploymentProject struct { ctx context.Context - VarsCtx *jinja2.VarsCtx + VarsCtx *vars.VarsCtx dir string SealedSecretsDir string @@ -34,7 +34,7 @@ type DeploymentProject struct { parentProjectInclude *types.DeploymentItemConfig } -func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsCtx *jinja2.VarsCtx, dir string, sealedSecretsDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { +func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsCtx *vars.VarsCtx, dir string, sealedSecretsDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { dp := &DeploymentProject{ ctx: ctx, VarsCtx: varsCtx.Copy(), diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index b6db19f41..e3c6600ed 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -2,9 +2,9 @@ package deployment import ( "fmt" - "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" "path/filepath" "regexp" @@ -40,7 +40,7 @@ func ConvertArgsToVars(args map[string]string) *uo.UnstructuredObject { return vars } -func CheckRequiredDeployArgs(dir string, varsCtx *jinja2.VarsCtx, deployArgs *uo.UnstructuredObject) error { +func CheckRequiredDeployArgs(dir string, varsCtx *vars.VarsCtx, deployArgs *uo.UnstructuredObject) error { // First try to load the config without templating to avoid getting errors while rendering because required // args were not set. Otherwise we won't be able to iterator through the 'args' array in the deployment.yml // when the rendering error is actually args related. diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index ba4fcf7f2..29dbc2278 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -4,12 +4,12 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/deployment" - "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/vars" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd/api" "path/filepath" @@ -65,7 +65,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s s.Success() } - varsCtx := jinja2.NewVarsCtx(p.J2, p.grc) + varsCtx := vars.NewVarsCtx(p.J2, p.grc) err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) if err != nil { return nil, err diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 4224a0c09..d5bbdb66b 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -4,11 +4,11 @@ import ( "fmt" securejoin "github.com/cyphar/filepath-securejoin" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" "reflect" "regexp" @@ -83,7 +83,7 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, var errors []error curTarget := target for i := 0; i < 10; i++ { - varsCtx := jinja2.NewVarsCtx(c.J2, c.grc) + varsCtx := vars.NewVarsCtx(c.J2, c.grc) err := varsCtx.UpdateChildFromStruct("target", curTarget) if err != nil { return nil, err diff --git a/pkg/jinja2/vars.go b/pkg/vars/vars.go similarity index 96% rename from pkg/jinja2/vars.go rename to pkg/vars/vars.go index c1d97103b..67f818de5 100644 --- a/pkg/jinja2/vars.go +++ b/pkg/vars/vars.go @@ -1,8 +1,9 @@ -package jinja2 +package vars import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" @@ -14,12 +15,12 @@ import ( ) type VarsCtx struct { - J2 *Jinja2 + J2 *jinja2.Jinja2 grc *git.MirroredGitRepoCollection Vars *uo.UnstructuredObject } -func NewVarsCtx(j2 *Jinja2, grc *git.MirroredGitRepoCollection) *VarsCtx { +func NewVarsCtx(j2 *jinja2.Jinja2, grc *git.MirroredGitRepoCollection) *VarsCtx { vc := &VarsCtx{ J2: j2, grc: grc, From af1b74f921c145841f71f9f0d6b2497b4b76c17f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 12:21:42 +0200 Subject: [PATCH 0175/2268] feat: Remove deprecated fields from deployment config --- pkg/deployment/deployment_project.go | 37 +--------------------------- pkg/types/deployment.go | 5 ---- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 5004917fb..5b6638843 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -4,19 +4,15 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" "path/filepath" - "reflect" "strings" ) -var warnOnce utils.OnceByKey - type DeploymentProject struct { ctx context.Context @@ -86,30 +82,6 @@ func (p *DeploymentProject) loadConfig(k *k8s.K8sCluster) error { return fmt.Errorf("failed to load deployment.yml vars: %w", err) } - // TODO remove obsolete code - if len(p.Config.Deployments) != 0 && (len(p.Config.KustomizeDirs) != 0 || len(p.Config.Includes) != 0) { - return fmt.Errorf("using 'deployments' and 'kustomizeDirs' at the same time is not allowed") - } - if len(p.Config.KustomizeDirs) != 0 { - warnOnce.Do("kustomizeDirs", func() { - status.Warning(p.ctx, "'kustomizeDirs' is deprecated, use 'deployments' instead") - }) - p.Config.Deployments = p.Config.KustomizeDirs - p.Config.KustomizeDirs = nil - } - if len(p.Config.Includes) != 0 { - warnOnce.Do("includes", func() { - status.Warning(p.ctx, "'includes' is deprecated, use 'deployments' instead") - }) - for _, inc := range p.Config.Includes { - c := *inc - c.Include = c.Path - c.Path = nil - p.Config.Deployments = append(p.Config.Deployments, &c) - } - p.Config.Includes = nil - } - // If there are no explicit tags set, interpret the path as a tag, which allows to // enable/disable single deployments via included/excluded tags for _, item := range p.Config.Deployments { @@ -131,14 +103,7 @@ func (p *DeploymentProject) loadConfig(k *k8s.K8sCluster) error { if len(p.GetCommonLabels()) == 0 { return fmt.Errorf("no commonLabels in root deployment. This is not allowed") } - if len(p.Config.DeleteByLabels) != 0 { - warnOnce.Do("deleteByLabels", func() { - status.Warning(p.ctx, "'deleteByLabels' is deprecated and ignored from now on") - }) - if !reflect.DeepEqual(p.Config.CommonLabels, p.Config.DeleteByLabels) { - return fmt.Errorf("commonLabels and deleteByLabels do not match") - } - } + if len(p.Config.Args) != 0 && p.parentProject != nil { return fmt.Errorf("only the root deployment.yml can define args") } diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index 8a84675b3..754f54080 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -132,12 +132,7 @@ type DeploymentProjectConfig struct { Deployments []*DeploymentItemConfig `yaml:"deployments,omitempty"` - // Obsolete - KustomizeDirs []*DeploymentItemConfig `yaml:"kustomizeDirs,omitempty"` - Includes []*DeploymentItemConfig `yaml:"includes,omitempty"` - CommonLabels map[string]string `yaml:"commonLabels,omitempty"` - DeleteByLabels map[string]string `yaml:"deleteByLabels,omitempty"` OverrideNamespace *string `yaml:"overrideNamespace,omitempty"` Tags []string `yaml:"tags,omitempty"` From b939b8ab019eaa8d66120d4be65a2e1ddc0cffd6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 12:22:13 +0200 Subject: [PATCH 0176/2268] feat: Deprecate cluster configuration and warn about removal in the future --- pkg/kluctl_project/project_load.go | 3 ++- pkg/kluctl_project/target_context.go | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index c1d9e3be9..b3811864e 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -206,8 +206,10 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { } var clustersInfos []gitProjectInfo if c.loadArgs.LocalClusters != "" { + status.Warning(c.ctx, "--local-clusters is deprecated and will be removed in an upcoming version. Use variables loaded from git instead.") clustersInfos = append(clustersInfos, c.localProject(c.loadArgs.LocalClusters)) } else if len(c.Config.Clusters.Projects) != 0 { + status.Warning(c.ctx, "'clusters' is deprecated and will be removed in an upcoming version. Use variables loaded from git instead.") for _, ep := range c.Config.Clusters.Projects { info, err := c.loadExternalProject(&ep, "clusters", "") if err != nil { @@ -246,7 +248,6 @@ func (c *LoadedKluctlProject) mergeClustersDirs(mergedClustersDir string, cluste for _, ci := range clustersInfos { if !utils.IsDirectory(ci.dir) { - status.Warning(c.ctx, "Cluster dir '%s' does not exist", ci.dir) continue } files, err := ioutil.ReadDir(ci.dir) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 29dbc2278..dbcf672f6 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -134,6 +134,8 @@ func (p *LoadedKluctlProject) LoadClusterConfig(clusterName *string, contextName var clusterConfig *types.ClusterConfig if clusterName != nil { + status.Warning(p.ctx, "Cluster configurations have been deprecated and support for them will be removed in a future kluctl release.") + clusterConfig, err = types.LoadClusterConfig(p.ClustersDir, *clusterName) if err != nil { return nil, nil, err From 021ee097f76f4f7ef9c8d3117b1eccad5ff6ec68 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 12:41:41 +0200 Subject: [PATCH 0177/2268] refactor: Rename SecretsSource to VarsSource --- cmd/kluctl/commands/cmd_seal.go | 2 +- pkg/seal/secrets_loader.go | 8 ++++---- pkg/seal/secrets_loader_http.go | 4 ++-- pkg/types/kluctl_project.go | 6 +++--- pkg/types/{secrets_source.go => vars_source.go} | 16 ++++++++-------- 5 files changed, 18 insertions(+), 18 deletions(-) rename pkg/types/{secrets_source.go => vars_source.go} (72%) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index a25d36236..85d8518f3 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -46,7 +46,7 @@ func loadSecrets(ctx *commandCtx, target *types.Target, secretsLoader *seal.Secr return err } for _, source := range secretEntry.Sources { - var renderedSource types.SecretSource + var renderedSource types.VarsSource err = ctx.targetCtx.KluctlProject.J2.RenderStruct(&renderedSource, &source, ctx.targetCtx.DeploymentProject.VarsCtx.Vars) if err != nil { return err diff --git a/pkg/seal/secrets_loader.go b/pkg/seal/secrets_loader.go index ff0a13431..e49537647 100644 --- a/pkg/seal/secrets_loader.go +++ b/pkg/seal/secrets_loader.go @@ -32,7 +32,7 @@ func NewSecretsLoader(p *kluctl_project.LoadedKluctlProject, secretsDir string) } } -func (s *SecretsLoader) LoadSecrets(source *types.SecretSource) (*uo.UnstructuredObject, error) { +func (s *SecretsLoader) LoadSecrets(source *types.VarsSource) (*uo.UnstructuredObject, error) { if source.Path != nil { return s.loadSecretsFile(source) } else if source.SystemEnvVars != nil { @@ -46,7 +46,7 @@ func (s *SecretsLoader) LoadSecrets(source *types.SecretSource) (*uo.Unstructure } } -func (s *SecretsLoader) loadSecretsFile(source *types.SecretSource) (*uo.UnstructuredObject, error) { +func (s *SecretsLoader) loadSecretsFile(source *types.VarsSource) (*uo.UnstructuredObject, error) { var p string var err error if utils.Exists(filepath.Join(s.project.DeploymentDir, *source.Path)) { @@ -82,7 +82,7 @@ func (s *SecretsLoader) loadSecretsFile(source *types.SecretSource) (*uo.Unstruc return secrets, nil } -func (s *SecretsLoader) loadSecretsSystemEnvs(source *types.SecretSource) (*uo.UnstructuredObject, error) { +func (s *SecretsLoader) loadSecretsSystemEnvs(source *types.VarsSource) (*uo.UnstructuredObject, error) { secrets := uo.New() err := source.SystemEnvVars.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { envName, ok := it.Value().(string) @@ -105,7 +105,7 @@ func (s *SecretsLoader) loadSecretsSystemEnvs(source *types.SecretSource) (*uo.U return secrets, nil } -func (s *SecretsLoader) loadSecretsAwsSecretsManager(source *types.SecretSource) (*uo.UnstructuredObject, error) { +func (s *SecretsLoader) loadSecretsAwsSecretsManager(source *types.VarsSource) (*uo.UnstructuredObject, error) { secret, err := aws.GetAwsSecretsManagerSecret(source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) if err != nil { return nil, err diff --git a/pkg/seal/secrets_loader_http.go b/pkg/seal/secrets_loader_http.go index 01582a9d1..a4fb00e23 100644 --- a/pkg/seal/secrets_loader_http.go +++ b/pkg/seal/secrets_loader_http.go @@ -15,7 +15,7 @@ import ( "strings" ) -func (s *SecretsLoader) doHttp(httpSource *types.SecretSourceHttp, username string, password string) (*http.Response, string, error) { +func (s *SecretsLoader) doHttp(httpSource *types.VarsSourceHttp, username string, password string) (*http.Response, string, error) { client := &http.Client{ Transport: ntlmssp.Negotiator{ RoundTripper: &http.Transport{ @@ -65,7 +65,7 @@ func (s *SecretsLoader) doHttp(httpSource *types.SecretSourceHttp, username stri return resp, string(respBody), nil } -func (s *SecretsLoader) loadSecretsHttp(source *types.SecretSource) (*uo.UnstructuredObject, error) { +func (s *SecretsLoader) loadSecretsHttp(source *types.VarsSource) (*uo.UnstructuredObject, error) { resp, respBody, err := s.doHttp(source.Http, "", "") if err != nil && resp != nil && resp.StatusCode == http.StatusUnauthorized { chgs := challenge.ResponseChallenges(resp) diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index dc1276557..b51b500c2 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -44,8 +44,8 @@ type DynamicTarget struct { } type SecretSet struct { - Name string `yaml:"name" validate:"required"` - Sources []SecretSource `yaml:"sources" validate:"required,gt=0"` + Name string `yaml:"name" validate:"required"` + Sources []VarsSource `yaml:"sources" validate:"required,gt=0"` } type GlobalSealedSecretsConfig struct { @@ -68,5 +68,5 @@ type KluctlProject struct { } func init() { - yaml.Validator.RegisterStructValidation(ValidateSecretSource, SecretSource{}) + yaml.Validator.RegisterStructValidation(ValidateSecretSource, VarsSource{}) } diff --git a/pkg/types/secrets_source.go b/pkg/types/vars_source.go similarity index 72% rename from pkg/types/secrets_source.go rename to pkg/types/vars_source.go index 2575ecbde..dec89ddd3 100644 --- a/pkg/types/secrets_source.go +++ b/pkg/types/vars_source.go @@ -5,7 +5,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) -type SecretSourceHttp struct { +type VarsSourceHttp struct { Url YamlUrl `yaml:"url,omitempty" validate:"required"` Method *string `yaml:"method,omitempty"` Body *string `yaml:"body,omitempty"` @@ -13,7 +13,7 @@ type SecretSourceHttp struct { JsonPath *string `yaml:"jsonPath,omitempty"` } -type SecretSourceAwsSecretsManager struct { +type VarsSourceAwsSecretsManager struct { // Name or ARN of the secret. In case a name is given, the region must be specified as well SecretName string `yaml:"secretName" validate:"required"` // The aws region @@ -22,15 +22,15 @@ type SecretSourceAwsSecretsManager struct { Profile *string `yaml:"profile,omitempty"` } -type SecretSource struct { - Path *string `yaml:"path,omitempty"` - SystemEnvVars *uo.UnstructuredObject `yaml:"systemEnvVars,omitempty"` - Http *SecretSourceHttp `yaml:"http,omitempty"` - AwsSecretsManager *SecretSourceAwsSecretsManager `yaml:"awsSecretsManager,omitempty"` +type VarsSource struct { + Path *string `yaml:"path,omitempty"` + SystemEnvVars *uo.UnstructuredObject `yaml:"systemEnvVars,omitempty"` + Http *VarsSourceHttp `yaml:"http,omitempty"` + AwsSecretsManager *VarsSourceAwsSecretsManager `yaml:"awsSecretsManager,omitempty"` } func ValidateSecretSource(sl validator.StructLevel) { - s := sl.Current().Interface().(SecretSource) + s := sl.Current().Interface().(VarsSource) count := 0 if s.Path != nil { count += 1 From 9334eb4046b5d393e8011b95a30c3d7d79e048c0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 14:36:40 +0200 Subject: [PATCH 0178/2268] refactor: Move varsCtx building into buildVars --- cmd/kluctl/commands/cmd_downscale.go | 2 +- cmd/kluctl/commands/cmd_poke_images.go | 2 +- pkg/kluctl_project/target_context.go | 85 +++++++++++++++----------- 3 files changed, 51 insertions(+), 38 deletions(-) diff --git a/cmd/kluctl/commands/cmd_downscale.go b/cmd/kluctl/commands/cmd_downscale.go index 78523c4b9..d3a0ffc10 100644 --- a/cmd/kluctl/commands/cmd_downscale.go +++ b/cmd/kluctl/commands/cmd_downscale.go @@ -37,7 +37,7 @@ func (cmd *downscaleCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { - if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to downscale on context/cluster %s?", ctx.targetCtx.ClusterConfig.Cluster.Context)) { + if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to downscale on context/cluster %s?", ctx.targetCtx.ClusterContext)) { return fmt.Errorf("aborted") } } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 88c2ab7ff..c7121e515 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -37,7 +37,7 @@ func (cmd *pokeImagesCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { - if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", ctx.targetCtx.ClusterConfig.Cluster.Context)) { + if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", ctx.targetCtx.ClusterContext)) { return fmt.Errorf("aborted") } } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index dbcf672f6..038452a3d 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -18,7 +18,7 @@ import ( type TargetContext struct { KluctlProject *LoadedKluctlProject Target *types.Target - ClusterConfig *types.ClusterConfig + ClusterContext string K *k8s.K8sCluster DeploymentProject *deployment.DeploymentProject DeploymentCollection *deployment.DeploymentCollection @@ -43,20 +43,14 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s } } - var contextName *string - if clusterName == nil && target != nil { - clusterName = target.Cluster - contextName = target.Context - } - - clusterConfig, clientConfig, err := p.LoadClusterConfig(clusterName, contextName) + varsCtx, clientConfig, clusterContext, err := p.buildVars(target, clusterName, args, forSeal) if err != nil { return nil, err } var k *k8s.K8sCluster if clientConfig != nil { - s := status.Start(ctx, fmt.Sprintf("Initializing k8s client for context %s", clusterConfig.Cluster.Context)) + s := status.Start(ctx, fmt.Sprintf("Initializing k8s client")) k, err = k8s.NewK8sCluster(ctx, clientConfig, dryRun) if err != nil { s.Failed() @@ -65,14 +59,54 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s s.Success() } + d, err := deployment.NewDeploymentProject(ctx, k, varsCtx, deploymentDir, p.sealedSecretsDir, nil) + if err != nil { + return nil, err + } + + d.DefaultSealedSecretsOutputPattern = targetName + + c, err := deployment.NewDeploymentCollection(ctx, d, images, inclusion, renderOutputDir, forSeal) + if err != nil { + return nil, err + } + + targetCtx := &TargetContext{ + KluctlProject: p, + Target: target, + ClusterContext: clusterContext, + K: k, + DeploymentProject: d, + DeploymentCollection: c, + } + + return targetCtx, nil +} + +func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *string, args map[string]string, forSeal bool) (*vars.VarsCtx, *rest.Config, string, error) { + doError := func(err error) (*vars.VarsCtx, *rest.Config, string, error) { + return nil, nil, "", err + } + + var contextName *string + if clusterName == nil && target != nil { + clusterName = target.Cluster + contextName = target.Context + } + + clusterConfig, clientConfig, err := p.LoadClusterConfig(clusterName, contextName) + if err != nil { + return doError(err) + } + varsCtx := vars.NewVarsCtx(p.J2, p.grc) err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) if err != nil { - return nil, err + return doError(err) } targetVars, err := uo.FromStruct(target) if err != nil { - return nil, err + return doError(err) } varsCtx.UpdateChild("target", targetVars) @@ -82,7 +116,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s for argName, argValue := range args { err = p.CheckDynamicArg(target, argName, argValue) if err != nil { - return nil, err + return doError(err) } } } @@ -98,35 +132,14 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s } } - err = deployment.CheckRequiredDeployArgs(deploymentDir, varsCtx, allArgs) + err = deployment.CheckRequiredDeployArgs(p.DeploymentDir, varsCtx, allArgs) if err != nil { - return nil, err + return doError(err) } varsCtx.UpdateChild("args", allArgs) - d, err := deployment.NewDeploymentProject(ctx, k, varsCtx, deploymentDir, p.sealedSecretsDir, nil) - if err != nil { - return nil, err - } - - d.DefaultSealedSecretsOutputPattern = targetName - - c, err := deployment.NewDeploymentCollection(ctx, d, images, inclusion, renderOutputDir, forSeal) - if err != nil { - return nil, err - } - - targetCtx := &TargetContext{ - KluctlProject: p, - Target: target, - ClusterConfig: clusterConfig, - K: k, - DeploymentProject: d, - DeploymentCollection: c, - } - - return targetCtx, nil + return varsCtx, clientConfig, clusterConfig.Cluster.Context, nil } func (p *LoadedKluctlProject) LoadClusterConfig(clusterName *string, contextName *string) (*types.ClusterConfig, *rest.Config, error) { From 07785dd3d89f28df12838eae720ad049b8917e58 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 14:43:46 +0200 Subject: [PATCH 0179/2268] feat: Remove --secrets-dir in seal command --- cmd/kluctl/commands/cmd_seal.go | 5 ++--- pkg/seal/secrets_loader.go | 11 ++--------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 85d8518f3..b2ae41aee 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -16,8 +16,7 @@ type sealCmd struct { args.ProjectFlags args.TargetFlags - SecretsDir string `group:"misc" help:"Specifies where to find unencrypted secret files. The given directory is NOT meant to be part of your source repository! The given path only matters for secrets of type 'path'. Defaults to the current working directory."` - ForceReseal bool `group:"misc" help:"Lets kluctl ignore secret hashes found in already sealed secrets and thus forces resealing of those."` + ForceReseal bool `group:"misc" help:"Lets kluctl ignore secret hashes found in already sealed secrets and thus forces resealing of those."` } func (cmd *sealCmd) Help() string { @@ -137,7 +136,7 @@ func (cmd *sealCmd) Run() error { return withKluctlProjectFromArgs(cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { hadError := false - secretsLoader := seal.NewSecretsLoader(p, cmd.SecretsDir) + secretsLoader := seal.NewSecretsLoader(p) baseTargets := make(map[string]bool) noTargetMatch := true diff --git a/pkg/seal/secrets_loader.go b/pkg/seal/secrets_loader.go index e49537647..f1bb1b1cc 100644 --- a/pkg/seal/secrets_loader.go +++ b/pkg/seal/secrets_loader.go @@ -18,16 +18,14 @@ type usernamePassword struct { } type SecretsLoader struct { - project *kluctl_project.LoadedKluctlProject - secretsDir string + project *kluctl_project.LoadedKluctlProject credentialsCache map[string]usernamePassword } -func NewSecretsLoader(p *kluctl_project.LoadedKluctlProject, secretsDir string) *SecretsLoader { +func NewSecretsLoader(p *kluctl_project.LoadedKluctlProject) *SecretsLoader { return &SecretsLoader{ project: p, - secretsDir: secretsDir, credentialsCache: map[string]usernamePassword{}, } } @@ -54,11 +52,6 @@ func (s *SecretsLoader) loadSecretsFile(source *types.VarsSource) (*uo.Unstructu if err != nil { return nil, err } - } else if utils.Exists(filepath.Join(s.secretsDir, *source.Path)) { - p, err = securejoin.SecureJoin(s.secretsDir, *source.Path) - if err != nil { - return nil, err - } } else { return nil, fmt.Errorf("secrets file %s does not exist", *source.Path) } From 409b86e9c417cd8f9250b8dbca4cd7287d4a9e27 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 14:55:49 +0200 Subject: [PATCH 0180/2268] refactor: Move loading of secrets into kluctl project loading code --- cmd/kluctl/commands/cmd_seal.go | 47 ++----------------- .../secrets_loader.go | 7 ++- .../secrets_loader_http.go | 2 +- pkg/kluctl_project/target_context.go | 40 ++++++++++++++++ 4 files changed, 47 insertions(+), 49 deletions(-) rename pkg/{seal => kluctl_project}/secrets_loader.go (94%) rename pkg/{seal => kluctl_project}/secrets_loader_http.go (99%) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index b2ae41aee..6749b559d 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -8,8 +8,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/seal" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) type sealCmd struct { @@ -28,40 +26,7 @@ kubeseal on each '.sealme' file and stores secrets in the directory specified by If no '--target' is specified, sealing is performed for all targets.` } -func findSecretsEntry(ctx *commandCtx, name string) (*types.SecretSet, error) { - for _, e := range ctx.targetCtx.KluctlProject.Config.SecretsConfig.SecretSets { - if e.Name == name { - return &e, nil - } - } - return nil, fmt.Errorf("secret Set with name %s was not found", name) -} - -func loadSecrets(ctx *commandCtx, target *types.Target, secretsLoader *seal.SecretsLoader) error { - secrets := uo.New() - for _, secretSetName := range target.SealingConfig.SecretSets { - secretEntry, err := findSecretsEntry(ctx, secretSetName) - if err != nil { - return err - } - for _, source := range secretEntry.Sources { - var renderedSource types.VarsSource - err = ctx.targetCtx.KluctlProject.J2.RenderStruct(&renderedSource, &source, ctx.targetCtx.DeploymentProject.VarsCtx.Vars) - if err != nil { - return err - } - s, err := secretsLoader.LoadSecrets(&renderedSource) - if err != nil { - return err - } - secrets.Merge(s) - } - } - ctx.targetCtx.DeploymentProject.MergeSecretsIntoAllChildren(secrets) - return nil -} - -func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.LoadedKluctlProject, targetName string, secretsLoader *seal.SecretsLoader) error { +func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.LoadedKluctlProject, targetName string) error { s := status.Start(ctx, "%s: Sealing for target", targetName) defer s.FailedWithMessage("%s: Sealing failed", targetName) @@ -79,11 +44,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L // pass forSeal=True so that .sealme files are rendered as well return withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { - err := loadSecrets(ctx, ctx.targetCtx.Target, secretsLoader) - if err != nil { - return doFail(err) - } - err = ctx.targetCtx.DeploymentCollection.RenderDeployments(ctx.targetCtx.K) + err := ctx.targetCtx.DeploymentCollection.RenderDeployments(ctx.targetCtx.K) if err != nil { return doFail(err) } @@ -136,8 +97,6 @@ func (cmd *sealCmd) Run() error { return withKluctlProjectFromArgs(cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { hadError := false - secretsLoader := seal.NewSecretsLoader(p) - baseTargets := make(map[string]bool) noTargetMatch := true for _, target := range p.DynamicTargets { @@ -169,7 +128,7 @@ func (cmd *sealCmd) Run() error { sealTarget = baseTarget } - err := cmd.runCmdSealForTarget(ctx, p, sealTarget.Name, secretsLoader) + err := cmd.runCmdSealForTarget(ctx, p, sealTarget.Name) if err != nil { hadError = true status.Error(ctx, err.Error()) diff --git a/pkg/seal/secrets_loader.go b/pkg/kluctl_project/secrets_loader.go similarity index 94% rename from pkg/seal/secrets_loader.go rename to pkg/kluctl_project/secrets_loader.go index f1bb1b1cc..091f13476 100644 --- a/pkg/seal/secrets_loader.go +++ b/pkg/kluctl_project/secrets_loader.go @@ -1,9 +1,8 @@ -package seal +package kluctl_project import ( "fmt" securejoin "github.com/cyphar/filepath-securejoin" - "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/aws" @@ -18,12 +17,12 @@ type usernamePassword struct { } type SecretsLoader struct { - project *kluctl_project.LoadedKluctlProject + project *LoadedKluctlProject credentialsCache map[string]usernamePassword } -func NewSecretsLoader(p *kluctl_project.LoadedKluctlProject) *SecretsLoader { +func NewSecretsLoader(p *LoadedKluctlProject) *SecretsLoader { return &SecretsLoader{ project: p, credentialsCache: map[string]usernamePassword{}, diff --git a/pkg/seal/secrets_loader_http.go b/pkg/kluctl_project/secrets_loader_http.go similarity index 99% rename from pkg/seal/secrets_loader_http.go rename to pkg/kluctl_project/secrets_loader_http.go index a4fb00e23..07b225c88 100644 --- a/pkg/seal/secrets_loader_http.go +++ b/pkg/kluctl_project/secrets_loader_http.go @@ -1,4 +1,4 @@ -package seal +package kluctl_project import ( "crypto/tls" diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 038452a3d..611acdb88 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -59,6 +59,13 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s s.Success() } + if forSeal { + err = p.loadSecrets(target, varsCtx) + if err != nil { + return nil, err + } + } + d, err := deployment.NewDeploymentProject(ctx, k, varsCtx, deploymentDir, p.sealedSecretsDir, nil) if err != nil { return nil, err @@ -142,6 +149,39 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin return varsCtx, clientConfig, clusterConfig.Cluster.Context, nil } +func (p *LoadedKluctlProject) findSecretsEntry(name string) (*types.SecretSet, error) { + for _, e := range p.Config.SecretsConfig.SecretSets { + if e.Name == name { + return &e, nil + } + } + return nil, fmt.Errorf("secret Set with name %s was not found", name) +} + +func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.VarsCtx) error { + secretsLoader := NewSecretsLoader(p) + + for _, secretSetName := range target.SealingConfig.SecretSets { + secretEntry, err := p.findSecretsEntry(secretSetName) + if err != nil { + return err + } + for _, source := range secretEntry.Sources { + var renderedSource types.VarsSource + err = p.J2.RenderStruct(&renderedSource, &source, varsCtx.Vars) + if err != nil { + return err + } + s, err := secretsLoader.LoadSecrets(&renderedSource) + if err != nil { + return err + } + varsCtx.Vars.MergeChild("secrets", s) + } + } + return nil +} + func (p *LoadedKluctlProject) LoadClusterConfig(clusterName *string, contextName *string) (*types.ClusterConfig, *rest.Config, error) { var err error var clusterConfig *types.ClusterConfig From 08c4b8d66a449747dcfd0225a8f2424ef63ace98 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 15:47:48 +0200 Subject: [PATCH 0181/2268] refactor: Start unifying secrets and vars loading --- pkg/kluctl_project/secrets_loader.go | 118 ---------------- pkg/kluctl_project/target_context.go | 21 +-- pkg/types/kluctl_project.go | 4 +- pkg/vars/vars_loader.go | 126 ++++++++++++++++++ .../vars_loader_http.go} | 50 +++---- 5 files changed, 162 insertions(+), 157 deletions(-) delete mode 100644 pkg/kluctl_project/secrets_loader.go create mode 100644 pkg/vars/vars_loader.go rename pkg/{kluctl_project/secrets_loader_http.go => vars/vars_loader_http.go} (73%) diff --git a/pkg/kluctl_project/secrets_loader.go b/pkg/kluctl_project/secrets_loader.go deleted file mode 100644 index 091f13476..000000000 --- a/pkg/kluctl_project/secrets_loader.go +++ /dev/null @@ -1,118 +0,0 @@ -package kluctl_project - -import ( - "fmt" - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/aws" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "os" - "path/filepath" -) - -type usernamePassword struct { - username string - password string -} - -type SecretsLoader struct { - project *LoadedKluctlProject - - credentialsCache map[string]usernamePassword -} - -func NewSecretsLoader(p *LoadedKluctlProject) *SecretsLoader { - return &SecretsLoader{ - project: p, - credentialsCache: map[string]usernamePassword{}, - } -} - -func (s *SecretsLoader) LoadSecrets(source *types.VarsSource) (*uo.UnstructuredObject, error) { - if source.Path != nil { - return s.loadSecretsFile(source) - } else if source.SystemEnvVars != nil { - return s.loadSecretsSystemEnvs(source) - } else if source.Http != nil { - return s.loadSecretsHttp(source) - } else if source.AwsSecretsManager != nil { - return s.loadSecretsAwsSecretsManager(source) - } else { - return nil, fmt.Errorf("invalid secrets entry") - } -} - -func (s *SecretsLoader) loadSecretsFile(source *types.VarsSource) (*uo.UnstructuredObject, error) { - var p string - var err error - if utils.Exists(filepath.Join(s.project.DeploymentDir, *source.Path)) { - p, err = securejoin.SecureJoin(s.project.DeploymentDir, *source.Path) - if err != nil { - return nil, err - } - } else { - return nil, fmt.Errorf("secrets file %s does not exist", *source.Path) - } - - err = utils.CheckInDir(s.project.DeploymentDir, p) - if err != nil { - return nil, fmt.Errorf("secrets file %s is not part of the deployment project: %w", *source.Path, err) - } - - secrets, err := uo.FromFile(p) - if err != nil { - return nil, err - } - secrets, ok, err := secrets.GetNestedObject("secrets") - if err != nil { - return nil, err - } - if !ok { - return uo.New(), nil - } - return secrets, nil -} - -func (s *SecretsLoader) loadSecretsSystemEnvs(source *types.VarsSource) (*uo.UnstructuredObject, error) { - secrets := uo.New() - err := source.SystemEnvVars.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { - envName, ok := it.Value().(string) - if !ok { - return fmt.Errorf("value at %s is not a string", it.KeyPath().ToJsonPath()) - } - envValue, ok := os.LookupEnv(envName) - if !ok { - return fmt.Errorf("environment variable %s not found for secret %s", envName, it.KeyPath().ToJsonPath()) - } - err := secrets.SetNestedField(envValue, it.KeyPath()...) - if err != nil { - return fmt.Errorf("failed to set secret %s: %w", it.KeyPath().ToJsonPath(), err) - } - return nil - }) - if err != nil { - return nil, err - } - return secrets, nil -} - -func (s *SecretsLoader) loadSecretsAwsSecretsManager(source *types.VarsSource) (*uo.UnstructuredObject, error) { - secret, err := aws.GetAwsSecretsManagerSecret(source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) - if err != nil { - return nil, err - } - - secrets, err := uo.FromString(secret) - if err != nil { - return nil, fmt.Errorf("failed to parse yaml from AWS Secrets Manager (secretName=%s): %w", source.AwsSecretsManager.SecretName, err) - } - secrets, ok, err := secrets.GetNestedObject("secrets") - if err != nil { - return nil, err - } - if !ok { - return uo.New(), nil - } - return secrets, nil -} diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 611acdb88..d3e86dc44 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -60,7 +60,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s } if forSeal { - err = p.loadSecrets(target, varsCtx) + err = p.loadSecrets(k, target, varsCtx) if err != nil { return nil, err } @@ -158,25 +158,18 @@ func (p *LoadedKluctlProject) findSecretsEntry(name string) (*types.SecretSet, e return nil, fmt.Errorf("secret Set with name %s was not found", name) } -func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.VarsCtx) error { - secretsLoader := NewSecretsLoader(p) +func (p *LoadedKluctlProject) loadSecrets(k *k8s.K8sCluster, target *types.Target, varsCtx *vars.VarsCtx) error { + searchDirs := []string{p.DeploymentDir} + secretsLoader := vars.NewVarsLoader(k, varsCtx, searchDirs, "secrets") for _, secretSetName := range target.SealingConfig.SecretSets { secretEntry, err := p.findSecretsEntry(secretSetName) if err != nil { return err } - for _, source := range secretEntry.Sources { - var renderedSource types.VarsSource - err = p.J2.RenderStruct(&renderedSource, &source, varsCtx.Vars) - if err != nil { - return err - } - s, err := secretsLoader.LoadSecrets(&renderedSource) - if err != nil { - return err - } - varsCtx.Vars.MergeChild("secrets", s) + err = secretsLoader.LoadVarsList(secretEntry.Sources) + if err != nil { + return err } } return nil diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index b51b500c2..c3084e7f3 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -44,8 +44,8 @@ type DynamicTarget struct { } type SecretSet struct { - Name string `yaml:"name" validate:"required"` - Sources []VarsSource `yaml:"sources" validate:"required,gt=0"` + Name string `yaml:"name" validate:"required"` + Sources []*VarsSource `yaml:"sources" validate:"required,gt=0"` } type GlobalSealedSecretsConfig struct { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go new file mode 100644 index 000000000..87bd01764 --- /dev/null +++ b/pkg/vars/vars_loader.go @@ -0,0 +1,126 @@ +package vars + +import ( + "fmt" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils/aws" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "os" +) + +type usernamePassword struct { + username string + password string +} + +type VarsLoader struct { + k *k8s.K8sCluster + varsCtx *VarsCtx + searchDirs []string + rootKey string + + credentialsCache map[string]usernamePassword +} + +func NewVarsLoader(k *k8s.K8sCluster, varsCtx *VarsCtx, searchDirs []string, rootKey string) *VarsLoader { + return &VarsLoader{ + k: k, + varsCtx: varsCtx, + searchDirs: searchDirs, + rootKey: rootKey, + credentialsCache: map[string]usernamePassword{}, + } +} + +func (s *VarsLoader) LoadVarsList(varsList []*types.VarsSource) error { + for _, source := range varsList { + err := s.LoadVars(source) + if err != nil { + return err + } + } + return nil +} + +func (s *VarsLoader) LoadVars(sourceIn *types.VarsSource) error { + var source types.VarsSource + + err := s.varsCtx.J2.RenderStruct(&source, sourceIn, s.varsCtx.Vars) + if err != nil { + return err + } + + if source.Path != nil { + return s.loadFile(&source) + } else if source.SystemEnvVars != nil { + return s.loadSystemEnvs(&source) + } else if source.Http != nil { + return s.loadHttp(&source) + } else if source.AwsSecretsManager != nil { + return s.loadAwsSecretsManager(&source) + } else { + return fmt.Errorf("invalid vars source") + } +} + +func (s *VarsLoader) mergeVars(newVars *uo.UnstructuredObject) { + if s.rootKey == "" { + s.varsCtx.Update(newVars) + } else { + s.varsCtx.UpdateChild(s.rootKey, newVars) + } +} + +func (s *VarsLoader) loadFile(source *types.VarsSource) error { + var newVars uo.UnstructuredObject + err := s.varsCtx.RenderYamlFile(*source.Path, s.searchDirs, &newVars) + if err != nil { + return fmt.Errorf("failed to load vars from %s: %w", *source.Path, err) + } + s.mergeVars(&newVars) + return nil +} + +func (s *VarsLoader) loadSystemEnvs(source *types.VarsSource) error { + newVars := uo.New() + err := source.SystemEnvVars.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { + envName, ok := it.Value().(string) + if !ok { + return fmt.Errorf("value at %s is not a string", it.KeyPath().ToJsonPath()) + } + envValue, ok := os.LookupEnv(envName) + if !ok { + return fmt.Errorf("environment variable %s not found for %s", envName, it.KeyPath().ToJsonPath()) + } + err := newVars.SetNestedField(envValue, it.KeyPath()...) + if err != nil { + return fmt.Errorf("failed to set value for %s: %w", it.KeyPath().ToJsonPath(), err) + } + return nil + }) + if err != nil { + return err + } + s.mergeVars(newVars) + return nil +} + +func (s *VarsLoader) loadAwsSecretsManager(source *types.VarsSource) error { + secret, err := aws.GetAwsSecretsManagerSecret(source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) + if err != nil { + return err + } + + newVars, err := uo.FromString(secret) + if err != nil { + return fmt.Errorf("failed to parse yaml from AWS Secrets Manager (secretName=%s): %w", source.AwsSecretsManager.SecretName, err) + } + if s.rootKey != "" { + newVars, _, err = newVars.GetNestedObject(s.rootKey) + } + if newVars != nil { + s.mergeVars(newVars) + } + return nil +} diff --git a/pkg/kluctl_project/secrets_loader_http.go b/pkg/vars/vars_loader_http.go similarity index 73% rename from pkg/kluctl_project/secrets_loader_http.go rename to pkg/vars/vars_loader_http.go index 07b225c88..2d6b5fb1a 100644 --- a/pkg/kluctl_project/secrets_loader_http.go +++ b/pkg/vars/vars_loader_http.go @@ -1,4 +1,4 @@ -package kluctl_project +package vars import ( "crypto/tls" @@ -15,7 +15,7 @@ import ( "strings" ) -func (s *SecretsLoader) doHttp(httpSource *types.VarsSourceHttp, username string, password string) (*http.Response, string, error) { +func (s *VarsLoader) doHttp(httpSource *types.VarsSourceHttp, username string, password string) (*http.Response, string, error) { client := &http.Client{ Transport: ntlmssp.Negotiator{ RoundTripper: &http.Transport{ @@ -65,12 +65,12 @@ func (s *SecretsLoader) doHttp(httpSource *types.VarsSourceHttp, username string return resp, string(respBody), nil } -func (s *SecretsLoader) loadSecretsHttp(source *types.VarsSource) (*uo.UnstructuredObject, error) { +func (s *VarsLoader) loadHttp(source *types.VarsSource) error { resp, respBody, err := s.doHttp(source.Http, "", "") if err != nil && resp != nil && resp.StatusCode == http.StatusUnauthorized { chgs := challenge.ResponseChallenges(resp) if len(chgs) == 0 { - return nil, err + return err } var realms []string @@ -87,7 +87,7 @@ func (s *SecretsLoader) loadSecretsHttp(source *types.VarsSource) (*uo.Unstructu if !ok { username, password, err := utils.AskForCredentials(fmt.Sprintf("Please enter credentials for host '%s'", source.Http.Url.Host)) if err != nil { - return nil, err + return err } creds = usernamePassword{ username: username, @@ -98,52 +98,56 @@ func (s *SecretsLoader) loadSecretsHttp(source *types.VarsSource) (*uo.Unstructu resp, respBody, err = s.doHttp(source.Http, creds.username, creds.password) if err != nil { - return nil, err + return err } } else if err != nil { - return nil, err + return err } var respObj interface{} - var secrets *uo.UnstructuredObject + var newVars *uo.UnstructuredObject err = yaml.ReadYamlString(respBody, &respObj) if err != nil { - return nil, err + return err } if err != nil { - return nil, err + return err } if source.Http.JsonPath != nil { p, err := uo.NewMyJsonPath(*source.Http.JsonPath) if err != nil { - return nil, err + return err } x, ok := p.GetFirstFromAny(respObj) if !ok { - return nil, fmt.Errorf("%s not found in result from http request %s", *source.Http.JsonPath, source.Http.Url.String()) + return fmt.Errorf("%s not found in result from http request %s", *source.Http.JsonPath, source.Http.Url.String()) } s, ok := x.(string) if !ok { - return nil, fmt.Errorf("%s in result of http request %s is not a string", *source.Http.JsonPath, source.Http.Url.String()) + return fmt.Errorf("%s in result of http request %s is not a string", *source.Http.JsonPath, source.Http.Url.String()) } - secrets, err = uo.FromString(s) + newVars, err = uo.FromString(s) if err != nil { - return nil, err + return err } } else { x, ok := respObj.(map[string]interface{}) if !ok { - return nil, fmt.Errorf("result of http request %s is not an object", source.Http.Url.String()) + return fmt.Errorf("result of http request %s is not an object", source.Http.Url.String()) } - secrets = uo.FromMap(x) + newVars = uo.FromMap(x) } - secrets, ok, err := secrets.GetNestedObject("secrets") - if err != nil { - return nil, err + + if s.rootKey != "" { + newVars, _, err = newVars.GetNestedObject(s.rootKey) + if err != nil { + return err + } } - if !ok { - return uo.New(), nil + + if newVars != nil { + s.mergeVars(newVars) } - return secrets, nil + return nil } From 0fe611a7b5aa499d40ff19a4274a23fee4d94eb2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 18:04:53 +0200 Subject: [PATCH 0182/2268] feat: Unify loading of vars and secrets --- pkg/deployment/deployment_collection.go | 2 +- pkg/deployment/deployment_item.go | 5 +- pkg/deployment/deployment_project.go | 25 ++-- pkg/kluctl_project/target_context.go | 11 +- pkg/types/deployment.go | 52 +------- pkg/types/kluctl_project.go | 5 - pkg/types/vars_source.go | 49 ++++++-- pkg/vars/vars.go | 120 ------------------- pkg/vars/vars_loader.go | 152 ++++++++++++++++++------ pkg/vars/vars_loader_http.go | 18 +-- 10 files changed, 192 insertions(+), 247 deletions(-) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 4b845757b..64b100788 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -117,7 +117,7 @@ func (c *DeploymentCollection) RenderDeployments(k *k8s.K8sCluster) error { defer wp.StopWait(false) for _, d := range c.Deployments { - err := d.render(k, c.forSeal, wp) + err := d.render(c.forSeal, wp) if err != nil { return err } diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index e4ccacd3a..15463a61a 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -110,7 +110,7 @@ func (di *DeploymentItem) getCommonAnnotations() map[string]string { return a } -func (di *DeploymentItem) render(k *k8s.K8sCluster, forSeal bool, wp *utils.WorkerPoolWithErrors) error { +func (di *DeploymentItem) render(forSeal bool, wp *utils.WorkerPoolWithErrors) error { if di.dir == nil { return nil } @@ -123,7 +123,8 @@ func (di *DeploymentItem) render(k *k8s.K8sCluster, forSeal bool, wp *utils.Work } varsCtx := di.Project.VarsCtx.Copy() - err = varsCtx.LoadVarsList(k, di.Project.getRenderSearchDirs(), di.Config.Vars) + + err = di.Project.loadVarsList(varsCtx, di.Config.Vars) if err != nil { return err } diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 5b6638843..6ac7de718 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -16,8 +16,10 @@ import ( type DeploymentProject struct { ctx context.Context - VarsCtx *vars.VarsCtx - dir string + varsLoader *vars.VarsLoader + VarsCtx *vars.VarsCtx + + dir string SealedSecretsDir string DefaultSealedSecretsOutputPattern string @@ -30,9 +32,10 @@ type DeploymentProject struct { parentProjectInclude *types.DeploymentItemConfig } -func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsCtx *vars.VarsCtx, dir string, sealedSecretsDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { +func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsLoader *vars.VarsLoader, varsCtx *vars.VarsCtx, dir string, sealedSecretsDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { dp := &DeploymentProject{ ctx: ctx, + varsLoader: varsLoader, VarsCtx: varsCtx.Copy(), dir: dir, SealedSecretsDir: sealedSecretsDir, @@ -44,7 +47,7 @@ func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsCtx *vars. return nil, fmt.Errorf("%s does not exist or is not a directory", dir) } - err := dp.loadConfig(k) + err := dp.loadConfig() if err != nil { return nil, fmt.Errorf("failed to load deployment config for %s: %w", dir, err) } @@ -57,13 +60,11 @@ func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsCtx *vars. return dp, nil } -func (p *DeploymentProject) MergeSecretsIntoAllChildren(vars *uo.UnstructuredObject) { - for _, c := range p.getChildren(true, true) { - c.VarsCtx.UpdateChild("secrets", vars) - } +func (p *DeploymentProject) loadVarsList(varsCtx *vars.VarsCtx, varsList []*types.VarsSource) error { + return p.varsLoader.LoadVarsList(varsCtx, varsList, p.getRenderSearchDirs(), "") } -func (p *DeploymentProject) loadConfig(k *k8s.K8sCluster) error { +func (p *DeploymentProject) loadConfig() error { configPath := filepath.Join(p.dir, "deployment.yml") if !yaml.Exists(configPath) { if yaml.Exists(filepath.Join(p.dir, "kustomization.yml")) { @@ -77,7 +78,7 @@ func (p *DeploymentProject) loadConfig(k *k8s.K8sCluster) error { return fmt.Errorf("failed to load deployment.yml: %w", err) } - err = p.VarsCtx.LoadVarsList(k, p.getRenderSearchDirs(), p.Config.Vars) + err = p.loadVarsList(p.VarsCtx, p.Config.Vars) if err != nil { return fmt.Errorf("failed to load deployment.yml vars: %w", err) } @@ -162,12 +163,12 @@ func (p *DeploymentProject) loadIncludes(k *k8s.K8sCluster) error { incDir := filepath.Join(p.dir, *inc.Include) varsCtx := p.VarsCtx.Copy() - err := varsCtx.LoadVarsList(k, p.getRenderSearchDirs(), inc.Vars) + err := p.loadVarsList(varsCtx, inc.Vars) if err != nil { return err } - newProject, err := NewDeploymentProject(p.ctx, k, varsCtx, incDir, + newProject, err := NewDeploymentProject(p.ctx, k, p.varsLoader, varsCtx, incDir, p.SealedSecretsDir, p) if err != nil { return err diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index d3e86dc44..e8a5def9a 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -59,14 +59,16 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s s.Success() } + varsLoader := vars.NewVarsLoader(ctx, k, p.grc) + if forSeal { - err = p.loadSecrets(k, target, varsCtx) + err = p.loadSecrets(target, varsCtx, varsLoader) if err != nil { return nil, err } } - d, err := deployment.NewDeploymentProject(ctx, k, varsCtx, deploymentDir, p.sealedSecretsDir, nil) + d, err := deployment.NewDeploymentProject(ctx, k, varsLoader, varsCtx, deploymentDir, p.sealedSecretsDir, nil) if err != nil { return nil, err } @@ -158,16 +160,15 @@ func (p *LoadedKluctlProject) findSecretsEntry(name string) (*types.SecretSet, e return nil, fmt.Errorf("secret Set with name %s was not found", name) } -func (p *LoadedKluctlProject) loadSecrets(k *k8s.K8sCluster, target *types.Target, varsCtx *vars.VarsCtx) error { +func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.VarsCtx, varsLoader *vars.VarsLoader) error { searchDirs := []string{p.DeploymentDir} - secretsLoader := vars.NewVarsLoader(k, varsCtx, searchDirs, "secrets") for _, secretSetName := range target.SealingConfig.SecretSets { secretEntry, err := p.findSecretsEntry(secretSetName) if err != nil { return err } - err = secretsLoader.LoadVarsList(secretEntry.Sources) + err = varsLoader.LoadVarsList(varsCtx, secretEntry.Sources, searchDirs, "secrets") if err != nil { return err } diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index 754f54080..c9e3dce4b 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -2,8 +2,6 @@ package types import ( "github.com/go-playground/validator/v10" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" ) @@ -13,7 +11,7 @@ type DeploymentItemConfig struct { Tags []string `yaml:"tags,omitempty"` Barrier *bool `yaml:"barrier,omitempty"` WaitReadiness *bool `yaml:"waitReadiness,omitempty"` - Vars []*VarsListItem `yaml:"vars,omitempty"` + Vars []*VarsSource `yaml:"vars,omitempty"` SkipDeleteIfTags *bool `yaml:"skipDeleteIfTags,omitempty"` OnlyRender *bool `yaml:"onlyRender,omitempty"` AlwaysDeploy *bool `yaml:"alwaysDeploy,omitempty"` @@ -50,51 +48,6 @@ type DeploymentArg struct { Default interface{} `yaml:"default,omitempty"` } -type VarsListItemGit struct { - Url git_url.GitUrl `yaml:"url" validate:"required"` - Ref string `yaml:"ref,omitempty"` - Path string `yaml:"path" validate:"required"` -} - -type VarsListItemClusterConfigMapOrSecret struct { - Name string `yaml:"name" validate:"required"` - Namespace string `yaml:"namespace,omitempty"` - Key string `yaml:"key" validate:"required"` -} - -type VarsListItem struct { - Values *uo.UnstructuredObject `yaml:"values,omitempty"` - File *string `yaml:"file,omitempty"` - Git *VarsListItemGit `yaml:"git,omitempty"` - ClusterConfigMap *VarsListItemClusterConfigMapOrSecret `yaml:"clusterConfigMap,omitempty"` - ClusterSecret *VarsListItemClusterConfigMapOrSecret `yaml:"clusterSecret,omitempty"` -} - -func ValidateVarsListItem(sl validator.StructLevel) { - s := sl.Current().Interface().(VarsListItem) - count := 0 - if s.Values != nil { - count += 1 - } - if s.File != nil { - count += 1 - } - if s.Git != nil { - count += 1 - } - if s.ClusterConfigMap != nil { - count += 1 - } - if s.ClusterSecret != nil { - count += 1 - } - if count == 0 { - sl.ReportError(s, "self", "self", "invalidvars", "unknown vars type") - } else if count != 1 { - sl.ReportError(s, "self", "self", "invalidvars", "more then one vars type") - } -} - type SealedSecretsConfig struct { OutputPattern *string `yaml:"outputPattern,omitempty"` } @@ -127,7 +80,7 @@ type IgnoreForDiffItemConfig struct { type DeploymentProjectConfig struct { Args []*DeploymentArg `yaml:"args,omitempty"` - Vars []*VarsListItem `yaml:"vars,omitempty"` + Vars []*VarsSource `yaml:"vars,omitempty"` SealedSecrets *SealedSecretsConfig `yaml:"sealedSecrets,omitempty"` Deployments []*DeploymentItemConfig `yaml:"deployments,omitempty"` @@ -141,7 +94,6 @@ type DeploymentProjectConfig struct { } func init() { - yaml.Validator.RegisterStructValidation(ValidateVarsListItem, VarsListItem{}) yaml.Validator.RegisterStructValidation(ValidateDeploymentItemConfig, DeploymentItemConfig{}) yaml.Validator.RegisterStructValidation(ValidateDeleteObjectItemConfig, DeleteObjectItemConfig{}) } diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index c3084e7f3..12420ca83 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -2,7 +2,6 @@ package types import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" ) type DynamicArg struct { @@ -66,7 +65,3 @@ type KluctlProject struct { Targets []*Target `yaml:"targets,omitempty"` SecretsConfig *SecretsConfig `yaml:"secretsConfig,omitempty"` } - -func init() { - yaml.Validator.RegisterStructValidation(ValidateSecretSource, VarsSource{}) -} diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index dec89ddd3..1dfa61beb 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -2,9 +2,23 @@ package types import ( "github.com/go-playground/validator/v10" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" ) +type VarsSourceGit struct { + Url git_url.GitUrl `yaml:"url" validate:"required"` + Ref string `yaml:"ref,omitempty"` + Path string `yaml:"path" validate:"required"` +} + +type VarsSourceClusterConfigMapOrSecret struct { + Name string `yaml:"name" validate:"required"` + Namespace string `yaml:"namespace,omitempty"` + Key string `yaml:"key" validate:"required"` +} + type VarsSourceHttp struct { Url YamlUrl `yaml:"url,omitempty" validate:"required"` Method *string `yaml:"method,omitempty"` @@ -23,18 +37,35 @@ type VarsSourceAwsSecretsManager struct { } type VarsSource struct { - Path *string `yaml:"path,omitempty"` - SystemEnvVars *uo.UnstructuredObject `yaml:"systemEnvVars,omitempty"` - Http *VarsSourceHttp `yaml:"http,omitempty"` - AwsSecretsManager *VarsSourceAwsSecretsManager `yaml:"awsSecretsManager,omitempty"` + Values *uo.UnstructuredObject `yaml:"values,omitempty"` + File *string `yaml:"file,omitempty"` + Path *string `yaml:"path,omitempty"` + Git *VarsSourceGit `yaml:"git,omitempty"` + ClusterConfigMap *VarsSourceClusterConfigMapOrSecret `yaml:"clusterConfigMap,omitempty"` + ClusterSecret *VarsSourceClusterConfigMapOrSecret `yaml:"clusterSecret,omitempty"` + SystemEnvVars *uo.UnstructuredObject `yaml:"systemEnvVars,omitempty"` + Http *VarsSourceHttp `yaml:"http,omitempty"` + AwsSecretsManager *VarsSourceAwsSecretsManager `yaml:"awsSecretsManager,omitempty"` } -func ValidateSecretSource(sl validator.StructLevel) { +func ValidateVarsSource(sl validator.StructLevel) { s := sl.Current().Interface().(VarsSource) count := 0 if s.Path != nil { count += 1 } + if s.File != nil { + count += 1 + } + if s.Git != nil { + count += 1 + } + if s.ClusterConfigMap != nil { + count += 1 + } + if s.ClusterSecret != nil { + count += 1 + } if s.SystemEnvVars != nil { count += 1 } @@ -45,8 +76,12 @@ func ValidateSecretSource(sl validator.StructLevel) { count += 1 } if count == 0 { - sl.ReportError(s, "self", "self", "invalidsource", "unknown secret source type") + sl.ReportError(s, "self", "self", "invalidsource", "unknown vars source type") } else if count != 1 { - sl.ReportError(s, "self", "self", "invalidsource", "more then one secret source type") + sl.ReportError(s, "self", "self", "invalidsource", "more then one vars source type") } } + +func init() { + yaml.Validator.RegisterStructValidation(ValidateVarsSource, VarsSource{}) +} diff --git a/pkg/vars/vars.go b/pkg/vars/vars.go index 67f818de5..b94ccff98 100644 --- a/pkg/vars/vars.go +++ b/pkg/vars/vars.go @@ -1,17 +1,10 @@ package vars import ( - "fmt" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/jinja2" - "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/types" - k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - "io/ioutil" - "os" ) type VarsCtx struct { @@ -55,119 +48,6 @@ func (vc *VarsCtx) UpdateChildFromStruct(child string, o interface{}) error { return nil } -func (vc *VarsCtx) LoadVarsList(k *k8s.K8sCluster, searchDirs []string, varsList []*types.VarsListItem) error { - for _, v := range varsList { - if v.Values != nil { - vc.Update(v.Values) - } else if v.File != nil { - err := vc.loadVarsFile(*v.File, searchDirs) - if err != nil { - return err - } - } else if v.Git != nil { - err := vc.loadVarsGitFile(v.Git) - if err != nil { - return err - } - } else if v.ClusterConfigMap != nil { - ref := k8s2.NewObjectRef("", "v1", "ConfigMap", v.ClusterConfigMap.Name, v.ClusterConfigMap.Namespace) - err := vc.loadVarsFromK8sObject(k, ref, v.ClusterConfigMap.Key) - if err != nil { - return err - } - } else if v.ClusterSecret != nil { - ref := k8s2.NewObjectRef("", "v1", "Secret", v.ClusterSecret.Name, v.ClusterSecret.Namespace) - err := vc.loadVarsFromK8sObject(k, ref, v.ClusterSecret.Key) - if err != nil { - return err - } - } else { - return fmt.Errorf("invalid vars entry: %v", v) - } - } - - return nil -} - -func (vc *VarsCtx) loadVarsFile(p string, searchDirs []string) error { - var newVars uo.UnstructuredObject - err := vc.RenderYamlFile(p, searchDirs, &newVars) - if err != nil { - return fmt.Errorf("failed to load vars from %s: %w", p, err) - } - vc.Update(&newVars) - return nil -} - -func (vc *VarsCtx) loadVarsGitFile(gitFile *types.VarsListItemGit) error { - mr, err := vc.grc.GetMirroredGitRepo(gitFile.Url, true, true, true) - if err != nil { - return fmt.Errorf("failed to load vars from git repository %s: %w", gitFile.Url.String(), err) - } - - tmpDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "git-vars") - if err != nil { - return err - } - defer os.RemoveAll(tmpDir) - - file, err := mr.ReadFile(gitFile.Ref, gitFile.Path) - if err != nil { - return fmt.Errorf("failed to load vars from git repository %s and path %s: %w", gitFile.Url.String(), gitFile.Path, err) - } - - return vc.loadVarsFromString(string(file)) -} - -func (vc *VarsCtx) loadVarsFromK8sObject(k *k8s.K8sCluster, ref k8s2.ObjectRef, key string) error { - if k == nil { - return nil - } - - o, _, err := k.GetSingleObject(ref) - if err != nil { - return err - } - - value, found, err := o.GetNestedString("data", key) - if err != nil { - return err - } - if !found { - return fmt.Errorf("key %s not found in %s on cluster", key, ref.String()) - } - - err = vc.loadVarsFromString(value) - if err != nil { - return fmt.Errorf("failed to load vars from kubernetes object %s and key %s: %w", ref.String(), key, err) - } - return nil -} - -func (vc *VarsCtx) loadVarsFromString(s string) error { - var newVars uo.UnstructuredObject - err := vc.renderYamlString(s, &newVars) - if err != nil { - return err - } - vc.Update(&newVars) - return nil -} - -func (vc *VarsCtx) renderYamlString(s string, out interface{}) error { - ret, err := vc.J2.RenderString(s, nil, vc.Vars) - if err != nil { - return err - } - - err = yaml.ReadYamlString(ret, out) - if err != nil { - return err - } - - return nil -} - func (vc *VarsCtx) RenderYamlFile(p string, searchDirs []string, out interface{}) error { ret, err := vc.J2.RenderFile(p, searchDirs, vc.Vars) if err != nil { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 87bd01764..df011336f 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -1,11 +1,18 @@ package vars import ( + "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/aws" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "io/ioutil" "os" ) @@ -15,27 +22,25 @@ type usernamePassword struct { } type VarsLoader struct { - k *k8s.K8sCluster - varsCtx *VarsCtx - searchDirs []string - rootKey string + ctx context.Context + k *k8s.K8sCluster + grc *git.MirroredGitRepoCollection credentialsCache map[string]usernamePassword } -func NewVarsLoader(k *k8s.K8sCluster, varsCtx *VarsCtx, searchDirs []string, rootKey string) *VarsLoader { +func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, grc *git.MirroredGitRepoCollection) *VarsLoader { return &VarsLoader{ + ctx: ctx, k: k, - varsCtx: varsCtx, - searchDirs: searchDirs, - rootKey: rootKey, + grc: grc, credentialsCache: map[string]usernamePassword{}, } } -func (s *VarsLoader) LoadVarsList(varsList []*types.VarsSource) error { +func (v *VarsLoader) LoadVarsList(varsCtx *VarsCtx, varsList []*types.VarsSource, searchDirs []string, rootKey string) error { for _, source := range varsList { - err := s.LoadVars(source) + err := v.LoadVars(varsCtx, source, searchDirs, rootKey) if err != nil { return err } @@ -43,46 +48,58 @@ func (s *VarsLoader) LoadVarsList(varsList []*types.VarsSource) error { return nil } -func (s *VarsLoader) LoadVars(sourceIn *types.VarsSource) error { +func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, searchDirs []string, rootKey string) error { var source types.VarsSource - err := s.varsCtx.J2.RenderStruct(&source, sourceIn, s.varsCtx.Vars) + err := varsCtx.J2.RenderStruct(&source, sourceIn, varsCtx.Vars) if err != nil { return err } - if source.Path != nil { - return s.loadFile(&source) + if source.Values != nil { + v.mergeVars(varsCtx, source.Values, rootKey) + } else if source.Path != nil { + status.Warning(v.ctx, "'path' is deprecated as vars source, use 'file' instead") + return v.loadFile(varsCtx, *source.Path, searchDirs, rootKey) + } else if source.File != nil { + return v.loadFile(varsCtx, *source.File, searchDirs, rootKey) + } else if source.Git != nil { + return v.loadGit(varsCtx, source.Git, rootKey) + } else if source.ClusterConfigMap != nil { + ref := k8s2.NewObjectRef("", "v1", "ConfigMap", source.ClusterConfigMap.Name, source.ClusterConfigMap.Namespace) + return v.loadFromK8sObject(varsCtx, ref, source.ClusterConfigMap.Key, rootKey) + } else if source.ClusterSecret != nil { + ref := k8s2.NewObjectRef("", "v1", "Secret", source.ClusterSecret.Name, source.ClusterSecret.Namespace) + return v.loadFromK8sObject(varsCtx, ref, source.ClusterSecret.Key, rootKey) } else if source.SystemEnvVars != nil { - return s.loadSystemEnvs(&source) + return v.loadSystemEnvs(varsCtx, &source, rootKey) } else if source.Http != nil { - return s.loadHttp(&source) + return v.loadHttp(varsCtx, &source, rootKey) } else if source.AwsSecretsManager != nil { - return s.loadAwsSecretsManager(&source) - } else { - return fmt.Errorf("invalid vars source") + return v.loadAwsSecretsManager(varsCtx, &source, rootKey) } + return fmt.Errorf("invalid vars source") } -func (s *VarsLoader) mergeVars(newVars *uo.UnstructuredObject) { - if s.rootKey == "" { - s.varsCtx.Update(newVars) +func (v *VarsLoader) mergeVars(varsCtx *VarsCtx, newVars *uo.UnstructuredObject, rootKey string) { + if rootKey == "" { + varsCtx.Update(newVars) } else { - s.varsCtx.UpdateChild(s.rootKey, newVars) + varsCtx.UpdateChild(rootKey, newVars) } } -func (s *VarsLoader) loadFile(source *types.VarsSource) error { +func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, searchDirs []string, rootKey string) error { var newVars uo.UnstructuredObject - err := s.varsCtx.RenderYamlFile(*source.Path, s.searchDirs, &newVars) + err := varsCtx.RenderYamlFile(path, searchDirs, &newVars) if err != nil { - return fmt.Errorf("failed to load vars from %s: %w", *source.Path, err) + return fmt.Errorf("failed to load vars from %s: %w", path, err) } - s.mergeVars(&newVars) + v.mergeVars(varsCtx, &newVars, rootKey) return nil } -func (s *VarsLoader) loadSystemEnvs(source *types.VarsSource) error { +func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { newVars := uo.New() err := source.SystemEnvVars.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { envName, ok := it.Value().(string) @@ -102,25 +119,88 @@ func (s *VarsLoader) loadSystemEnvs(source *types.VarsSource) error { if err != nil { return err } - s.mergeVars(newVars) + v.mergeVars(varsCtx, newVars, rootKey) return nil } -func (s *VarsLoader) loadAwsSecretsManager(source *types.VarsSource) error { +func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { secret, err := aws.GetAwsSecretsManagerSecret(source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) if err != nil { return err } + return v.loadFromString(varsCtx, secret, rootKey) +} + +func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, rootKey string) error { + mr, err := v.grc.GetMirroredGitRepo(gitFile.Url, true, true, true) + if err != nil { + return fmt.Errorf("failed to load vars from git repository %s: %w", gitFile.Url.String(), err) + } - newVars, err := uo.FromString(secret) + tmpDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "git-vars") if err != nil { - return fmt.Errorf("failed to parse yaml from AWS Secrets Manager (secretName=%s): %w", source.AwsSecretsManager.SecretName, err) + return err } - if s.rootKey != "" { - newVars, _, err = newVars.GetNestedObject(s.rootKey) + defer os.RemoveAll(tmpDir) + + file, err := mr.ReadFile(gitFile.Ref, gitFile.Path) + if err != nil { + return fmt.Errorf("failed to load vars from git repository %s and path %s: %w", gitFile.Url.String(), gitFile.Path, err) } - if newVars != nil { - s.mergeVars(newVars) + + return v.loadFromString(varsCtx, string(file), rootKey) +} + +func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, ref k8s2.ObjectRef, key string, rootKey string) error { + if v.k == nil { + return nil } + + o, _, err := v.k.GetSingleObject(ref) + if err != nil { + return err + } + + value, found, err := o.GetNestedString("data", key) + if err != nil { + return err + } + if !found { + return fmt.Errorf("key %s not found in %s on cluster", key, ref.String()) + } + + err = v.loadFromString(varsCtx, value, rootKey) + if err != nil { + return fmt.Errorf("failed to load vars from kubernetes object %s and key %s: %w", ref.String(), key, err) + } + return nil +} + +func (v *VarsLoader) loadFromString(varsCtx *VarsCtx, s string, rootKey string) error { + newVars := uo.New() + err := v.renderYamlString(varsCtx, s, newVars) + if err != nil { + return err + } + + if rootKey != "" { + newVars, _, err = newVars.GetNestedObject(rootKey) + } + + v.mergeVars(varsCtx, newVars, rootKey) + return nil +} + +func (v *VarsLoader) renderYamlString(varsCtx *VarsCtx, s string, out interface{}) error { + ret, err := varsCtx.J2.RenderString(s, nil, varsCtx.Vars) + if err != nil { + return err + } + + err = yaml.ReadYamlString(ret, out) + if err != nil { + return err + } + return nil } diff --git a/pkg/vars/vars_loader_http.go b/pkg/vars/vars_loader_http.go index 2d6b5fb1a..18cd60695 100644 --- a/pkg/vars/vars_loader_http.go +++ b/pkg/vars/vars_loader_http.go @@ -15,7 +15,7 @@ import ( "strings" ) -func (s *VarsLoader) doHttp(httpSource *types.VarsSourceHttp, username string, password string) (*http.Response, string, error) { +func (v *VarsLoader) doHttp(httpSource *types.VarsSourceHttp, username string, password string) (*http.Response, string, error) { client := &http.Client{ Transport: ntlmssp.Negotiator{ RoundTripper: &http.Transport{ @@ -65,8 +65,8 @@ func (s *VarsLoader) doHttp(httpSource *types.VarsSourceHttp, username string, p return resp, string(respBody), nil } -func (s *VarsLoader) loadHttp(source *types.VarsSource) error { - resp, respBody, err := s.doHttp(source.Http, "", "") +func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { + resp, respBody, err := v.doHttp(source.Http, "", "") if err != nil && resp != nil && resp.StatusCode == http.StatusUnauthorized { chgs := challenge.ResponseChallenges(resp) if len(chgs) == 0 { @@ -83,7 +83,7 @@ func (s *VarsLoader) loadHttp(source *types.VarsSource) error { } credsKey := fmt.Sprintf("%s|%s", source.Http.Url.Host, strings.Join(realms, "+")) - creds, ok := s.credentialsCache[credsKey] + creds, ok := v.credentialsCache[credsKey] if !ok { username, password, err := utils.AskForCredentials(fmt.Sprintf("Please enter credentials for host '%s'", source.Http.Url.Host)) if err != nil { @@ -93,10 +93,10 @@ func (s *VarsLoader) loadHttp(source *types.VarsSource) error { username: username, password: password, } - s.credentialsCache[credsKey] = creds + v.credentialsCache[credsKey] = creds } - resp, respBody, err = s.doHttp(source.Http, creds.username, creds.password) + resp, respBody, err = v.doHttp(source.Http, creds.username, creds.password) if err != nil { return err } @@ -139,15 +139,15 @@ func (s *VarsLoader) loadHttp(source *types.VarsSource) error { newVars = uo.FromMap(x) } - if s.rootKey != "" { - newVars, _, err = newVars.GetNestedObject(s.rootKey) + if rootKey != "" { + newVars, _, err = newVars.GetNestedObject(rootKey) if err != nil { return err } } if newVars != nil { - s.mergeVars(newVars) + v.mergeVars(varsCtx, newVars, rootKey) } return nil } From b5a746eb7616ec08accebd3e00ef3394a81824c0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 18 May 2022 18:22:29 +0200 Subject: [PATCH 0183/2268] feat: Rename sources to vars in secretSets --- pkg/kluctl_project/target_context.go | 7 ++++++- pkg/types/kluctl_project.go | 20 ++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index e8a5def9a..f8d73fce4 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -168,7 +168,12 @@ func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.Va if err != nil { return err } - err = varsLoader.LoadVarsList(varsCtx, secretEntry.Sources, searchDirs, "secrets") + if len(secretEntry.Sources) != 0 { + status.Warning(p.ctx, "'sources' in secretSets is deprecated, use 'vars' instead") + err = varsLoader.LoadVarsList(varsCtx, secretEntry.Sources, searchDirs, "secrets") + } else { + err = varsLoader.LoadVarsList(varsCtx, secretEntry.Vars, searchDirs, "secrets") + } if err != nil { return err } diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 12420ca83..e25426570 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -1,7 +1,9 @@ package types import ( + "github.com/go-playground/validator/v10" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" ) type DynamicArg struct { @@ -43,8 +45,18 @@ type DynamicTarget struct { } type SecretSet struct { - Name string `yaml:"name" validate:"required"` - Sources []*VarsSource `yaml:"sources" validate:"required,gt=0"` + Name string `yaml:"name" validate:"required"` + // TODO deprecated, use vars instead + Sources []*VarsSource `yaml:"sources,omitempty"` + Vars []*VarsSource `yaml:"vars,omitempty"` +} + +func ValidateSecretSet(sl validator.StructLevel) { + s := sl.Current().Interface().(SecretSet) + + if len(s.Sources) != 0 && len(s.Vars) != 0 { + sl.ReportError(s, "vars", "vars", "invalidsource", "sources and vars can't be set at the same time") + } } type GlobalSealedSecretsConfig struct { @@ -65,3 +77,7 @@ type KluctlProject struct { Targets []*Target `yaml:"targets,omitempty"` SecretsConfig *SecretsConfig `yaml:"secretsConfig,omitempty"` } + +func init() { + yaml.Validator.RegisterStructValidation(ValidateSecretSet, SecretSet{}) +} From 3e8b4fd7a590ad60f4339e18f72bb4f9565a679e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 10:00:45 +0200 Subject: [PATCH 0184/2268] fix: Fix status reporting in helm-update 1. Add missing prefix when new versions are detected 2. Use proper prefix (dirName instead of baseName) 3. Properly report success when no new version is detected 4. Show skipUpdate info when new versions are detected --- cmd/kluctl/commands/cmd_helm_update.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 2d9f7e5f6..59e3df5d9 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -38,21 +38,31 @@ func (cmd *helmUpdateCmd) Run() error { return err } - statusPrefix := filepath.Base(p) + statusPrefix := filepath.Base(filepath.Dir(p)) s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) defer s.Failed() + skipUpdate := chart.Config.SkipUpdate != nil && *chart.Config.SkipUpdate + newVersion, updated, err := chart.CheckUpdate() if err != nil { return err } if !updated { + s.Update("%s: Version %s is already up-to-date.", statusPrefix, *chart.Config.ChartVersion) + s.Success() return nil } - s.Update(fmt.Sprintf("Chart has new version %s available. Old version is %s.", newVersion, *chart.Config.ChartVersion)) + msg := fmt.Sprintf("%s: Chart has new version %s available. Old version is %s.", statusPrefix, newVersion, *chart.Config.ChartVersion) + if skipUpdate { + msg += " skipUpdate is set to true." + } + s.Update(msg) - if cmd.Upgrade { - if chart.Config.SkipUpdate != nil && *chart.Config.SkipUpdate { + if !cmd.Upgrade { + s.Success() + } else { + if skipUpdate { s.Update("%s: NOT upgrading chart as skipUpdate was set to true", statusPrefix) s.Success() return nil From c203138f652ae0fe73430555f273eb93357a569f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 10:43:57 +0200 Subject: [PATCH 0185/2268] docs: Add a few badges to README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 099728ed9..ba92599c6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # kluctl +[![tests](https://github.com/kluctl/kluctl/workflows/tests/badge.svg)](https://github.com/kluctl/kluctl/actions) +[![license](https://img.shields.io/github/license/kluctl/kluctl.svg)](https://github.com/kluctl/kluctl/blob/main/LICENSE) +[![release](https://img.shields.io/github/release/kluctl/kluctl/all.svg)](https://github.com/kluctl/kluctl/releases) + kluctl From 94e2cb275b9b0deb306ed0aa0e3df31bb2e2104e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 10:46:38 +0200 Subject: [PATCH 0186/2268] docs: Update README.md with contents from kluctl.io/docs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ba92599c6..6cd3ba354 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Kluctl does not depend on external operators/controllers and allows to use the s as long as access to the kluctl project and clusters is available. This means, that you can use it from your local machine, from your CI/CD pipelines or any automation platform/system that allows to call custom tools. -Flux support is in development and will come soon. +Flux support is in alpha statium and available via the [flux-kluctl-controller](https://github.com/kluctl/flux-kluctl-controller). ## Installation @@ -41,7 +41,7 @@ Documentation can be found here: https://kluctl.io/docs | --- | --- | | 💪 Kluctl handles all your deployments | You can manage all your deployments with Kluctl, including infrastructure related and your applications. | | 🪶 Complex or simple, all the same | You can manage complex and simple deployments with Kluctl. Simple deployments are lightweight while complex deployment are easily manageable. | -| 🤖 Native git support | Kluctl has native Git support integrated, meaning that it can easily deploy remote Kluctl projects or externalize parts (e.g. cluster configs) of your Kluctl project. | +| 🤖 Native git support | Kluctl has native Git support integrated, meaning that it can easily deploy remote Kluctl projects or externalize parts (e.g. configuration) of your Kluctl project. | | 🪐 Multiple environments | Deploy the same deployment to multiple environments (dev, test, prod, ...), with flexible differences in configuration. | | 🌌 Multiple clusters | Manage multiple target clusters (in multiple clouds or bare-metal if you want). | | 🔩 Configuration and Templating | Kluctl allows to use templating in nearly all places, making it easy to have dynamic configuration. | From b4b4969b122001219001bb1ef03e92ed8a86584a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 11:41:03 +0200 Subject: [PATCH 0187/2268] fix: Switch go go-internal based lockedfile This will hopefull fix the deadlocking tests seen on Windows. --- go.mod | 6 ++-- go.sum | 1 - pkg/git/mirrored_repo.go | 60 +++++++++++++++++++-------------- pkg/utils/embed_util/extract.go | 7 ++-- 4 files changed, 41 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 4c81d771f..4958074bb 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/go-playground/validator/v10 v10.10.1 github.com/gobwas/glob v0.2.3 github.com/goccy/go-yaml v1.9.5 - github.com/gofrs/flock v0.8.1 github.com/golang-jwt/jwt/v4 v4.4.1 github.com/google/go-containerregistry v0.8.0 github.com/hashicorp/go-version v1.4.0 @@ -27,6 +26,7 @@ require ( github.com/ohler55/ojg v1.14.0 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 + github.com/rogpeppe/go-internal v1.8.0 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 @@ -43,8 +43,10 @@ require ( gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b helm.sh/helm/v3 v3.8.2 k8s.io/api v0.24.0-rc.1 + k8s.io/apiextensions-apiserver v0.23.6 k8s.io/apimachinery v0.24.0-rc.1 k8s.io/client-go v0.24.0-rc.1 + k8s.io/klog/v2 v2.60.1 sigs.k8s.io/kind v0.12.0 sigs.k8s.io/kustomize/api v0.11.4 sigs.k8s.io/kustomize/kyaml v0.13.6 @@ -186,11 +188,9 @@ require ( gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.23.6 // indirect k8s.io/apiserver v0.23.6 // indirect k8s.io/cli-runtime v0.24.0-rc.1 // indirect k8s.io/component-base v0.24.0-rc.1 // indirect - k8s.io/klog/v2 v2.60.1 // indirect k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect k8s.io/kubectl v0.23.5 // indirect k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect diff --git a/go.sum b/go.sum index 3c91b81d5..dc7ef08f9 100644 --- a/go.sum +++ b/go.sum @@ -581,7 +581,6 @@ github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index a18128a5b..60abfe25e 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -11,13 +11,14 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/rogpeppe/go-internal/lockedfile" "io/ioutil" "os" "path/filepath" "strings" + "sync" "time" ) -import "github.com/gofrs/flock" var cacheBaseDir = filepath.Join(utils.GetTmpBaseDir(), "git-cache") @@ -28,7 +29,11 @@ type MirroredGitRepo struct { mirrorDir string hasUpdated bool - fileLock *flock.Flock + + fileLock *lockedfile.File + fileLockPath string + + mutex sync.Mutex } func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl) (*MirroredGitRepo, error) { @@ -45,7 +50,8 @@ func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl) (*MirroredGitRepo return nil, fmt.Errorf("failed to create mirror repo for %v: %w", u.String(), err) } } - o.fileLock = flock.New(filepath.Join(o.mirrorDir, ".cache.lock")) + + o.fileLockPath = filepath.Join(o.mirrorDir, ".cache.lock") return o, nil } @@ -58,39 +64,43 @@ func (g *MirroredGitRepo) SetUpdated(u bool) { } func (g *MirroredGitRepo) Lock() error { - ok, err := g.fileLock.TryLockContext(g.ctx, time.Millisecond*100) - if err != nil { - return fmt.Errorf("locking of %s failed: %w", g.fileLock.Path(), err) + g.mutex.Lock() + defer g.mutex.Unlock() + + if g.fileLock != nil { + return fmt.Errorf("file %s already locked", g.fileLockPath) } - if !ok { - return fmt.Errorf("locking of %s failed: unkown reason", g.fileLock.Path()) + + var err error + g.fileLock, err = lockedfile.Create(g.fileLockPath) + if err != nil { + return fmt.Errorf("locking of %s failed: %w", g.fileLockPath, err) } + return nil } func (g *MirroredGitRepo) Unlock() error { - err := g.fileLock.Unlock() - if err != nil { - status.Warning(g.ctx, "Unlock of %s failed: %v", g.fileLock.Path(), err) - return err + g.mutex.Lock() + defer g.mutex.Unlock() + + if g.fileLock == nil { + return fmt.Errorf("file %s is not locked", g.fileLockPath) } - return nil -} -func (g *MirroredGitRepo) WithLock(cb func() error) error { - err := g.Lock() + err := g.fileLock.Close() if err != nil { + status.Warning(g.ctx, "Unlock of %s failed: %v", g.fileLockPath, err) return err } - defer g.Unlock() - return cb() + g.fileLock = nil + return nil } -func (g *MirroredGitRepo) MaybeWithLock(lock bool, cb func() error) error { - if lock { - return g.WithLock(cb) - } - return cb() +func (g *MirroredGitRepo) IsLocked() bool { + g.mutex.Lock() + defer g.mutex.Unlock() + return g.fileLock != nil } func (g *MirroredGitRepo) LastUpdateTime() time.Time { @@ -330,7 +340,7 @@ func (g *MirroredGitRepo) Update(authProviders *auth2.GitAuthProviders) error { } func (g *MirroredGitRepo) CloneProject(ref string, targetDir string) error { - if !g.fileLock.Locked() || !g.hasUpdated { + if !g.IsLocked() || !g.hasUpdated { panic("tried to clone from a project that is not locked/updated") } @@ -344,7 +354,7 @@ func (g *MirroredGitRepo) CloneProject(ref string, targetDir string) error { } func (g *MirroredGitRepo) ReadFile(ref string, path string) ([]byte, error) { - if !g.fileLock.Locked() || !g.hasUpdated { + if !g.IsLocked() || !g.hasUpdated { panic("tried to read a file from a project that is not locked/updated") } diff --git a/pkg/utils/embed_util/extract.go b/pkg/utils/embed_util/extract.go index 22267376c..e3c5dc0d4 100644 --- a/pkg/utils/embed_util/extract.go +++ b/pkg/utils/embed_util/extract.go @@ -2,8 +2,8 @@ package embed_util import ( "fmt" - "github.com/gofrs/flock" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/rogpeppe/go-internal/lockedfile" "io" "io/fs" "io/ioutil" @@ -21,12 +21,11 @@ func ExtractTarToTmp(r io.Reader, fileListR io.Reader, targetPrefix string) (str targetPath := fmt.Sprintf("%s-%s", targetPrefix, utils.Sha256Bytes(fileList)[:16]) - fl := flock.New(targetPath + ".lock") - err = fl.Lock() + fl, err := lockedfile.Create(targetPath + ".lock") if err != nil { return "", err } - defer fl.Unlock() + defer fl.Close() needsExtract, expectedTarGzHash, err := checkExtractNeeded(targetPath, string(fileList)) if err != nil { From 5e605e3140a52743724be06cf9751b10e3fe4227 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 11:41:26 +0200 Subject: [PATCH 0188/2268] fix: Hold git locks for the whole time git is accessed and then release all locks --- cmd/kluctl/commands/utils.go | 14 ++++++++++---- pkg/git/repo_collection.go | 8 ++++++-- pkg/kluctl_project/git.go | 4 ++-- pkg/kluctl_project/load.go | 6 +----- pkg/kluctl_project/project.go | 2 +- pkg/kluctl_project/project_load.go | 8 ++------ pkg/kluctl_project/target_context.go | 4 ++-- pkg/kluctl_project/targets.go | 4 ++-- 8 files changed, 26 insertions(+), 24 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index a7ad73631..8dab17d0d 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -55,6 +55,12 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b status.Warning(cliCtx, "Failed to detect git project root. This might cause follow-up errors") } + ctx, cancel := context.WithTimeout(cliCtx, projectFlags.Timeout) + defer cancel() + + grc := git.NewMirroredGitRepoCollection(ctx, auth.NewDefaultAuthProviders(), projectFlags.GitCacheUpdateInterval) + defer grc.Clear() + loadArgs := kluctl_project.LoadKluctlProjectArgs{ RepoRoot: repoRoot, ProjectDir: cwd, @@ -67,13 +73,10 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b FromArchive: projectFlags.FromArchive.String(), FromArchiveMetadata: projectFlags.FromArchiveMetadata.String(), AllowGitClone: projectFlags.FromArchive == "", - GitAuthProviders: auth.NewDefaultAuthProviders(), - GitUpdateInterval: projectFlags.GitCacheUpdateInterval, + GRC: grc, ClientConfigGetter: clientConfigGetter(forCompletion), } - ctx, cancel := context.WithTimeout(cliCtx, projectFlags.Timeout) - defer cancel() p, err := kluctl_project.LoadKluctlProject(ctx, loadArgs, tmpDir, j2) if err != nil { return err @@ -181,6 +184,9 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm images: images, } + // we can assume that all git access is done at this point, so we can clear grc and thus unlock all repos + p.GRC.Clear() + return cb(cmdCtx) } diff --git a/pkg/git/repo_collection.go b/pkg/git/repo_collection.go index fbdf14ed6..0269bf69f 100644 --- a/pkg/git/repo_collection.go +++ b/pkg/git/repo_collection.go @@ -31,13 +31,17 @@ func NewMirroredGitRepoCollection(ctx context.Context, authProviders *auth.GitAu } } -func (g *MirroredGitRepoCollection) UnlockAll() { +func (g *MirroredGitRepoCollection) Clear() { g.mutex.Lock() defer g.mutex.Unlock() for _, e := range g.repos { - _ = e.mr.Unlock() + if e.mr.IsLocked() { + _ = e.mr.Unlock() + } } + + g.repos = map[string]*entry{} } func (g *MirroredGitRepoCollection) GetMirroredGitRepo(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*MirroredGitRepo, error) { diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index b64c6ebb7..3d3a2584a 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -32,7 +32,7 @@ func (c *LoadedKluctlProject) updateGitCaches() error { go func() { defer waitGroup.Done() - _, err := c.grc.GetMirroredGitRepo(u, true, true, true) + _, err := c.GRC.GetMirroredGitRepo(u, true, true, true) if err != nil { doError(fmt.Errorf("failed to update git project %v: %v", u.String(), err)) } @@ -87,7 +87,7 @@ func (c *LoadedKluctlProject) cloneGitProject(gitProject *types2.GitProject, tar return } - mr, err := c.grc.GetMirroredGitRepo(gitProject.Url, true, true, true) + mr, err := c.GRC.GetMirroredGitRepo(gitProject.Url, true, true, true) if err != nil { return git.GitRepoInfo{}, err } diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 5938483bd..801d66f6d 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -3,21 +3,17 @@ package kluctl_project import ( "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/types" ) func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*LoadedKluctlProject, error) { - grc := git.NewMirroredGitRepoCollection(ctx, args.GitAuthProviders, args.GitUpdateInterval) - defer grc.UnlockAll() - p := &LoadedKluctlProject{ ctx: ctx, loadArgs: args, TmpDir: tmpDir, J2: j2, - grc: grc, + GRC: args.GRC, involvedRepos: map[string][]types.InvolvedRepo{}, } diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 5196cc387..98a460568 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -30,7 +30,7 @@ type LoadedKluctlProject struct { DynamicTargets []*types2.DynamicTarget J2 *jinja2.Jinja2 - grc *git.MirroredGitRepoCollection + GRC *git.MirroredGitRepoCollection } func (c *LoadedKluctlProject) GetMetadata() *types2.ProjectMetadata { diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index b3811864e..f241a485d 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -3,7 +3,6 @@ package kluctl_project import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/git" - auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/status" types2 "github.com/kluctl/kluctl/v2/pkg/types" @@ -14,7 +13,6 @@ import ( "k8s.io/client-go/tools/clientcmd/api" "os" "path/filepath" - "time" ) type LoadKluctlProjectArgs struct { @@ -29,10 +27,8 @@ type LoadKluctlProjectArgs struct { FromArchive string FromArchiveMetadata string - AllowGitClone bool - GitAuthProviders *auth2.GitAuthProviders - - GitUpdateInterval time.Duration + AllowGitClone bool + GRC *git.MirroredGitRepoCollection ClientConfigGetter func(context *string) (*rest.Config, *api.Config, error) } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index f8d73fce4..27a0b163e 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -59,7 +59,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s s.Success() } - varsLoader := vars.NewVarsLoader(ctx, k, p.grc) + varsLoader := vars.NewVarsLoader(ctx, k, p.GRC) if forSeal { err = p.loadSecrets(target, varsCtx, varsLoader) @@ -108,7 +108,7 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin return doError(err) } - varsCtx := vars.NewVarsCtx(p.J2, p.grc) + varsCtx := vars.NewVarsCtx(p.J2, p.GRC) err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) if err != nil { return doError(err) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index d5bbdb66b..f7c18262f 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -83,7 +83,7 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, var errors []error curTarget := target for i := 0; i < 10; i++ { - varsCtx := vars.NewVarsCtx(c.J2, c.grc) + varsCtx := vars.NewVarsCtx(c.J2, c.GRC) err := varsCtx.UpdateChildFromStruct("target", curTarget) if err != nil { return nil, err @@ -134,7 +134,7 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsSimple(baseTarget *types.Targ } func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { - mr, err := c.grc.GetMirroredGitRepo(baseTarget.TargetConfig.Project.Url, true, true, true) + mr, err := c.GRC.GetMirroredGitRepo(baseTarget.TargetConfig.Project.Url, true, true, true) if err != nil { return nil, err } From 79bec8bd6a0361743ccce384b91986e12cfec1fc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 12:23:01 +0200 Subject: [PATCH 0189/2268] build: Create draft releases instead of directly publishing them --- .goreleaser.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index ea3c4cf97..029ace461 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -64,6 +64,7 @@ changelog: - '^refactor:' release: + draft: true prerelease: auto name_template: "{{.ProjectName}}-{{.Tag}}" From 52720402e8cbd5c2a312fa7a74dcffee4fe62c6a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 13:56:53 +0200 Subject: [PATCH 0190/2268] fix: Remove KluctlBytecodeCache from jinja2 renderer This cache implementation caused errors when the same file was present in two different projects. Turns out rendering is actually so fast that we really don't need that cache. --- pkg/jinja2/python_src/jinja2_cache.py | 170 ----------------------- pkg/jinja2/python_src/jinja2_renderer.py | 4 +- 2 files changed, 1 insertion(+), 173 deletions(-) delete mode 100644 pkg/jinja2/python_src/jinja2_cache.py diff --git a/pkg/jinja2/python_src/jinja2_cache.py b/pkg/jinja2/python_src/jinja2_cache.py deleted file mode 100644 index 8d37ec2d4..000000000 --- a/pkg/jinja2/python_src/jinja2_cache.py +++ /dev/null @@ -1,170 +0,0 @@ -import errno -import fnmatch -import os -import stat -import tempfile -import typing -from datetime import datetime -from types import CodeType - -from jinja2 import BytecodeCache -from jinja2.bccache import Bucket - -def get_tmp_base_dir(): - dir = os.path.join(tempfile.gettempdir(), "kluctl-workdir") - os.makedirs(dir, exist_ok=True) - return dir - -""" -A bytecode cache that is able to share bytecode between different directories. It does this by removing filenames -from dumped code and then later re-adding them after loading. - -This cache is also thread and multi-process safe. -""" -class KluctlBytecodeCache(BytecodeCache): - def __init__(self, max_cache_files): - self.cache_dir = self._get_cache_dir() - self.max_cache_files = max_cache_files - self.did_touch = set() - self.clear_old_entries() - - def _get_cache_dir(self) -> str: - def _unsafe_dir() -> "te.NoReturn": - raise RuntimeError( - "Cannot determine safe temp directory. You " - "need to explicitly provide one." - ) - - tmpdir = os.path.join(get_tmp_base_dir(), "jinja2-cache") - - # On windows the temporary directory is used specific unless - # explicitly forced otherwise. We can just use that. - if os.name == "nt": - os.makedirs(tmpdir, exist_ok=True) - return tmpdir - if not hasattr(os, "getuid"): - _unsafe_dir() - - tmpdir = os.path.join(tmpdir, str(os.getuid())) - - try: - os.makedirs(tmpdir, exist_ok=True, mode=stat.S_IRWXU) - except OSError as e: - if e.errno != errno.EEXIST: - raise - try: - os.chmod(tmpdir, stat.S_IRWXU) - actual_dir_stat = os.lstat(tmpdir) - if ( - actual_dir_stat.st_uid != os.getuid() - or not stat.S_ISDIR(actual_dir_stat.st_mode) - or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU - ): - _unsafe_dir() - except OSError as e: - if e.errno != errno.EEXIST: - raise - - actual_dir_stat = os.lstat(tmpdir) - if ( - actual_dir_stat.st_uid != os.getuid() - or not stat.S_ISDIR(actual_dir_stat.st_mode) - or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU - ): - _unsafe_dir() - - return tmpdir - - def get_cache_key(self, name: str, filename: typing.Optional[typing.Union[str]] = None): - return (name, filename) - - def _get_cache_filename(self, bucket: Bucket) -> str: - return os.path.join(self.cache_dir, bucket.checksum[:2], bucket.checksum) + ".cache" - - def touch_loaded_marker(self, bucket): - filename = self._get_cache_filename(bucket) + ".loaded" - if filename in self.did_touch: - return - try: - with open(filename + ".tmp", mode="wt") as f: - f.write(str(datetime.utcnow())) - os.rename(filename + ".tmp", filename) - except: - # Failure here it "ok" and is mostly happening on Windows here (permission denied for opened files...ugh..) - pass - self.did_touch.add(filename) - - def replace_code_filenames(self, code, old, new): - co_filename = code.co_filename - co_consts = [] - if co_filename == old: - co_filename = new - - for c in code.co_consts: - if isinstance(c, CodeType): - co_consts.append(self.replace_code_filenames(c, old, new)) - else: - co_consts.append(c) - - return code.replace(co_filename=co_filename, co_consts=tuple(co_consts)) - - def load_bytecode(self, bucket: Bucket) -> None: - filename = self._get_cache_filename(bucket) - - if os.path.exists(filename): - for i in range(4): - try: - with open(filename, "rb") as f: - bucket.load_bytecode(f) - if bucket.code is not None: - bucket.code = self.replace_code_filenames(bucket.code, "", bucket.key[1]) - except PermissionError: - # Retry. Windows is still failing from time to time... - continue - self.touch_loaded_marker(bucket) - - def dump_bytecode(self, bucket: Bucket) -> None: - # thread/multi-process safe version of super().dump_bytecode() - - try: - filename = self._get_cache_filename(bucket) - os.makedirs(os.path.dirname(filename), exist_ok=True) - - with open(filename + ".tmp", "wb") as f: - orig_code = bucket.code - bucket.code = self.replace_code_filenames(bucket.code, bucket.key[1], "") - bucket.write_bytecode(f) - bucket.code = orig_code - os.rename(filename + ".tmp", filename) - self.touch_loaded_marker(bucket) - except: - # Failure here it "ok" and is mostly happening on Windows here (permission denied for opened files...ugh..) - pass - - def clear_old_entries(self): - files = [] - for d in os.listdir(self.cache_dir): - path = os.path.join(self.cache_dir, d) - if not os.path.isdir(path): - continue - for n in os.listdir(path): - if not fnmatch.fnmatch(n, "*.cache"): - continue - files.append(os.path.join(path, n)) - - if len(files) <= self.max_cache_files: - return - times = {} - for f in files: - try: - with open(f + ".loaded", mode="rt") as f2: - times[f] = datetime.fromisoformat(f2.read()) - except: - times[f] = datetime.min - files.sort(key=lambda f: times[f], reverse=True) - for f in files[self.max_cache_files:]: - try: - os.remove(f) - os.remove(f + ".loaded") - except: - pass diff --git a/pkg/jinja2/python_src/jinja2_renderer.py b/pkg/jinja2/python_src/jinja2_renderer.py index 735379917..52e868b30 100644 --- a/pkg/jinja2/python_src/jinja2_renderer.py +++ b/pkg/jinja2/python_src/jinja2_renderer.py @@ -4,10 +4,8 @@ from jinja2 import StrictUndefined, FileSystemLoader, ChainableUndefined from dict_utils import merge_dict -from jinja2_cache import KluctlBytecodeCache from jinja2_utils import KluctlJinja2Environment, add_jinja2_filters, extract_template_error -jinja2_cache = KluctlBytecodeCache(max_cache_files=10000) begin_placeholder = "XXXXXbegin_get_image_" end_placeholder = "_end_get_imageXXXXX" @@ -76,7 +74,7 @@ def build_env(self, vars_str, search_dirs, strict): environment = KluctlJinja2Environment(loader=FileSystemLoader(search_dirs), undefined=StrictUndefined if strict else NullUndefined, cache_size=10000, - bytecode_cache=jinja2_cache, auto_reload=False) + auto_reload=False) merge_dict(environment.globals, vars, clone=False) add_jinja2_filters(environment) From 3d960f6247dec5c8816b900f9bb25e4474de9e04 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 13:58:16 +0200 Subject: [PATCH 0191/2268] fix: Report struct validation errors as tags The "param" of ReportError is ignored later when the error is printed, so we can't use it for custom error messages. The output looks a little bit strange now, but better this way than having it omitted completely. --- pkg/types/deployment.go | 6 +++--- pkg/types/git_project.go | 6 +++--- pkg/types/kluctl_project.go | 2 +- pkg/types/vars_source.go | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index c9e3dce4b..b129aa8b8 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -21,10 +21,10 @@ type DeploymentItemConfig struct { func ValidateDeploymentItemConfig(sl validator.StructLevel) { s := sl.Current().Interface().(DeploymentItemConfig) if s.Path != nil && s.Include != nil { - sl.ReportError(s, "path", "Path", "pathinclude", "path and include can not be set at the same time") + sl.ReportError(s, "path", "Path", "path and include can not be set at the same time", "") } if s.Path == nil && s.WaitReadiness != nil { - sl.ReportError(s, "waitReadiness", "WaitReadiness", "waitreadiness", "only kustomize deployments are allowed to have waitReadiness set") + sl.ReportError(s, "waitReadiness", "WaitReadiness", "only kustomize deployments are allowed to have waitReadiness set", "") } } @@ -39,7 +39,7 @@ type DeleteObjectItemConfig struct { func ValidateDeleteObjectItemConfig(sl validator.StructLevel) { s := sl.Current().Interface().(DeleteObjectItemConfig) if s.Group == nil && s.Version == nil && s.Kind == nil { - sl.ReportError(s, "self", "self", "missingfield", "at least one of group/version/kind must be set") + sl.ReportError(s, "self", "self", "at least one of group/version/kind must be set", "") } } diff --git a/pkg/types/git_project.go b/pkg/types/git_project.go index 6d6cbc4a3..efa7c6dc8 100644 --- a/pkg/types/git_project.go +++ b/pkg/types/git_project.go @@ -25,7 +25,7 @@ func (gp *GitProject) UnmarshalYAML(unmarshal func(interface{}) error) error { func ValidateGitProject(sl validator.StructLevel) { gp := sl.Current().Interface().(GitProject) if strings.Index(gp.SubDir, "/") != -1 { - sl.ReportError(gp.SubDir, "subDir", "SubDir", "subdirinvalid", "/ is not allowed in subdir") + sl.ReportError(gp.SubDir, "subDir", "SubDir", "/ is not allowed in subDir", "") } } @@ -37,9 +37,9 @@ type ExternalProject struct { func ValidateExternalProject(sl validator.StructLevel) { p := sl.Current().Interface().(ExternalProject) if p.Project == nil && p.Path == nil { - sl.ReportError(p, ".", ".", "empty", "either project or path must be set") + sl.ReportError(p, ".", ".", "either project or path must be set", "") } else if p.Project != nil && p.Path != nil { - sl.ReportError(p, ".", ".", "empty", "only one of project or path can be set") + sl.ReportError(p, ".", ".", "only one of project or path can be set", "") } } diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index e25426570..9ecb1fcdd 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -55,7 +55,7 @@ func ValidateSecretSet(sl validator.StructLevel) { s := sl.Current().Interface().(SecretSet) if len(s.Sources) != 0 && len(s.Vars) != 0 { - sl.ReportError(s, "vars", "vars", "invalidsource", "sources and vars can't be set at the same time") + sl.ReportError(s, "vars", "vars", "sources and vars can't be set at the same time", "") } } diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index 1dfa61beb..60fee3214 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -76,9 +76,9 @@ func ValidateVarsSource(sl validator.StructLevel) { count += 1 } if count == 0 { - sl.ReportError(s, "self", "self", "invalidsource", "unknown vars source type") + sl.ReportError(s, "self", "self", "unknown vars source type", "") } else if count != 1 { - sl.ReportError(s, "self", "self", "invalidsource", "more then one vars source type") + sl.ReportError(s, "self", "self", "more then one vars source type", "") } } From 6d47b4ce6916e1c15e8969bc9a11a222825c31fa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 13:59:07 +0200 Subject: [PATCH 0192/2268] fix: Fix validation of VarsSource Use reflection for counting the number of set fields, as otherwise we'll miss fields again in the future (e.g. as happened with Values). --- pkg/types/vars_source.go | 31 ++++++++----------------------- pkg/vars/vars_loader.go | 1 + 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index 60fee3214..84e15f393 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -5,6 +5,7 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" + "reflect" ) type VarsSourceGit struct { @@ -50,31 +51,15 @@ type VarsSource struct { func ValidateVarsSource(sl validator.StructLevel) { s := sl.Current().Interface().(VarsSource) + count := 0 - if s.Path != nil { - count += 1 - } - if s.File != nil { - count += 1 - } - if s.Git != nil { - count += 1 - } - if s.ClusterConfigMap != nil { - count += 1 - } - if s.ClusterSecret != nil { - count += 1 - } - if s.SystemEnvVars != nil { - count += 1 - } - if s.Http != nil { - count += 1 - } - if s.AwsSecretsManager != nil { - count += 1 + v := reflect.ValueOf(s) + for i := 0; i < v.NumField(); i++ { + if !v.Field(i).IsNil() { + count += 1 + } } + if count == 0 { sl.ReportError(s, "self", "self", "unknown vars source type", "") } else if count != 1 { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index df011336f..e6f912b29 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -58,6 +58,7 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear if source.Values != nil { v.mergeVars(varsCtx, source.Values, rootKey) + return nil } else if source.Path != nil { status.Warning(v.ctx, "'path' is deprecated as vars source, use 'file' instead") return v.loadFile(varsCtx, *source.Path, searchDirs, rootKey) From 3d5319f915a9752b09c0796c95e4f567bcde1c75 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 14:26:38 +0200 Subject: [PATCH 0193/2268] refactor: No need to pass grc to VarsCtx --- pkg/kluctl_project/target_context.go | 2 +- pkg/kluctl_project/targets.go | 2 +- pkg/vars/vars.go | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 27a0b163e..f18b0a880 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -108,7 +108,7 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin return doError(err) } - varsCtx := vars.NewVarsCtx(p.J2, p.GRC) + varsCtx := vars.NewVarsCtx(p.J2) err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) if err != nil { return doError(err) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index f7c18262f..d8a20bcef 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -83,7 +83,7 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, var errors []error curTarget := target for i := 0; i < 10; i++ { - varsCtx := vars.NewVarsCtx(c.J2, c.GRC) + varsCtx := vars.NewVarsCtx(c.J2) err := varsCtx.UpdateChildFromStruct("target", curTarget) if err != nil { return nil, err diff --git a/pkg/vars/vars.go b/pkg/vars/vars.go index b94ccff98..c29d4ba18 100644 --- a/pkg/vars/vars.go +++ b/pkg/vars/vars.go @@ -1,7 +1,6 @@ package vars import ( - "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -9,14 +8,12 @@ import ( type VarsCtx struct { J2 *jinja2.Jinja2 - grc *git.MirroredGitRepoCollection Vars *uo.UnstructuredObject } -func NewVarsCtx(j2 *jinja2.Jinja2, grc *git.MirroredGitRepoCollection) *VarsCtx { +func NewVarsCtx(j2 *jinja2.Jinja2) *VarsCtx { vc := &VarsCtx{ J2: j2, - grc: grc, Vars: uo.New(), } return vc @@ -25,7 +22,6 @@ func NewVarsCtx(j2 *jinja2.Jinja2, grc *git.MirroredGitRepoCollection) *VarsCtx func (vc *VarsCtx) Copy() *VarsCtx { cp := &VarsCtx{ J2: vc.J2, - grc: vc.grc, Vars: vc.Vars.Clone(), } return cp From 5cec077190d5dbc3ba6cab1b723a0f0c2b0e1067 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 14:26:45 +0200 Subject: [PATCH 0194/2268] tests: Add vars_test.go --- pkg/vars/vars_test.go | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 pkg/vars/vars_test.go diff --git a/pkg/vars/vars_test.go b/pkg/vars/vars_test.go new file mode 100644 index 000000000..b922b7279 --- /dev/null +++ b/pkg/vars/vars_test.go @@ -0,0 +1,66 @@ +package vars + +import ( + "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestVarsCtx(t *testing.T) { + j2, err := jinja2.NewJinja2() + if err != nil { + t.Fatal(err) + } + defer j2.Close() + + varsCtx := NewVarsCtx(j2) + varsCtx.Update(uo.FromMap(map[string]interface{}{ + "test1": map[string]interface{}{ + "test2": 42, + }, + })) + v, _, _ := varsCtx.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) +} + +func TestVarsCtxChild(t *testing.T) { + j2, err := jinja2.NewJinja2() + if err != nil { + t.Fatal(err) + } + defer j2.Close() + + varsCtx := NewVarsCtx(j2) + varsCtx.UpdateChild("child", uo.FromMap(map[string]interface{}{ + "test1": map[string]interface{}{ + "test2": 42, + }, + })) + v, _, _ := varsCtx.Vars.GetNestedInt("child", "test1", "test2") + assert.Equal(t, int64(42), v) +} + +func TestVarsCtxStruct(t *testing.T) { + j2, err := jinja2.NewJinja2() + if err != nil { + t.Fatal(err) + } + defer j2.Close() + + varsCtx := NewVarsCtx(j2) + + s := struct { + Test1 struct { + Test2 int + } + }{ + Test1: struct{ Test2 int }{Test2: 42}, + } + + err = varsCtx.UpdateChildFromStruct("child", s) + assert.NoError(t, err) + + v, _, _ := varsCtx.Vars.GetNestedInt("child", "test1", "test2") + assert.Equal(t, int64(42), v) +} From a3fcd851255bbb9d43643e2dcb687b55f34a6203 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 16:09:41 +0200 Subject: [PATCH 0195/2268] refactor: Move test git server code into own package to make it reusable --- e2e/project.go | 235 +++++------------------------- internal/test-utils/git_server.go | 223 ++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+), 199 deletions(-) create mode 100644 internal/test-utils/git_server.go diff --git a/e2e/project.go b/e2e/project.go index 6068a7ceb..fa5f18e8c 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -1,17 +1,12 @@ package e2e import ( - "context" "fmt" "github.com/go-git/go-git/v5" - http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - log "github.com/sirupsen/logrus" - "io/ioutil" - "net" - "net/http" "os" "os/exec" "path/filepath" @@ -36,49 +31,37 @@ type testProject struct { kubeconfigs []string - baseDir string - - gitServer *http_server.Server - gitHttpServer *http.Server - gitServerPort int + gitServer *test_utils.GitServer } func (p *testProject) init(t *testing.T, projectName string) { p.t = t + p.gitServer = test_utils.NewGitServer(t) p.projectName = projectName - baseDir, err := ioutil.TempDir(os.TempDir(), "kluctl-e2e-") - if err != nil { - p.t.Fatal(err) - } - p.baseDir = baseDir - - _ = os.MkdirAll(p.getKluctlProjectDir(), 0o700) - _ = os.MkdirAll(filepath.Join(p.getClustersDir(), "clusters"), 0o700) - _ = os.MkdirAll(filepath.Join(p.getSealedSecretsDir(), ".sealed-secrets"), 0o700) - _ = os.MkdirAll(p.getDeploymentDir(), 0o700) - - p.initGitServer() - p.gitInit(p.getKluctlProjectDir()) + p.gitServer.GitInit(p.getKluctlProjectRepo()) if p.clustersExternal { - p.gitInit(p.getClustersDir()) + p.gitServer.GitInit(p.getClustersRepo()) } if p.deploymentExternal { - p.gitInit(p.getDeploymentDir()) + p.gitServer.GitInit(p.getDeploymentRepo()) } if p.sealedSecretsExternal { - p.gitInit(p.getSealedSecretsDir()) + p.gitServer.GitInit(p.getSealedSecretsRepo()) } + _ = os.MkdirAll(filepath.Join(p.gitServer.LocalRepoDir(p.getClustersRepo()), "clusters"), 0o700) + _ = os.MkdirAll(filepath.Join(p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()), ".sealed-secrets"), 0o700) + p.updateKluctlYaml(func(o *uo.UnstructuredObject) error { if p.clustersExternal { - o.SetNestedField(p.buildLocalGitUrl(p.getClustersDir()), "clusters", "project") + o.SetNestedField(p.gitServer.LocalGitUrl(p.getClustersRepo()), "clusters", "project") } if p.deploymentExternal { - o.SetNestedField(p.buildLocalGitUrl(p.getDeploymentDir()), "deployment", "project") + o.SetNestedField(p.gitServer.LocalGitUrl(p.getDeploymentRepo()), "deployment", "project") } if p.sealedSecretsExternal { - o.SetNestedField(p.buildLocalGitUrl(p.getSealedSecretsDir()), "sealedSecrets", "project") + o.SetNestedField(p.gitServer.LocalGitUrl(p.getSealedSecretsRepo()), "sealedSecrets", "project") } return nil }) @@ -87,147 +70,19 @@ func (p *testProject) init(t *testing.T, projectName string) { }) } -func (p *testProject) initGitServer() { - p.gitServer = http_server.New(p.baseDir) - - p.gitHttpServer = &http.Server{ - Addr: "127.0.0.1:0", - Handler: p.gitServer, - } - - ln, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - log.Fatal(err) - } - a := ln.Addr().(*net.TCPAddr) - p.gitServerPort = a.Port - - go func() { - _ = p.gitHttpServer.Serve(ln) - }() -} - func (p *testProject) cleanup() { - if p.gitHttpServer != nil { - _ = p.gitHttpServer.Shutdown(context.Background()) - p.gitHttpServer = nil + if p.gitServer != nil { + p.gitServer.Cleanup() p.gitServer = nil } - - if p.baseDir == "" { - return - } - _ = os.RemoveAll(p.baseDir) - p.baseDir = "" -} - -func (p *testProject) gitInit(dir string) { - err := os.MkdirAll(dir, 0o700) - if err != nil { - p.t.Fatal(err) - } - - r, err := git.PlainInit(dir, false) - if err != nil { - p.t.Fatal(err) - } - config, err := r.Config() - if err != nil { - p.t.Fatal(err) - } - wt, err := r.Worktree() - if err != nil { - p.t.Fatal(err) - } - - config.User.Name = "Test User" - config.User.Email = "no@mail.com" - config.Author = config.User - config.Committer = config.User - err = r.SetConfig(config) - if err != nil { - p.t.Fatal(err) - } - err = utils.Touch(filepath.Join(dir, ".dummy")) - if err != nil { - p.t.Fatal(err) - } - _, err = wt.Add(".dummy") - if err != nil { - p.t.Fatal(err) - } - _, err = wt.Commit("initial", &git.CommitOptions{}) - if err != nil { - p.t.Fatal(err) - } -} - -func (p *testProject) commitFiles(repo string, add []string, all bool, message string) { - r, err := git.PlainOpen(repo) - if err != nil { - p.t.Fatal(err) - } - wt, err := r.Worktree() - if err != nil { - p.t.Fatal(err) - } - for _, a := range add { - _, err = wt.Add(a) - if err != nil { - p.t.Fatal(err) - } - } - _, err = wt.Commit(message, &git.CommitOptions{ - All: all, - }) - if err != nil { - p.t.Fatal(err) - } -} - -func (p *testProject) commitYaml(y *uo.UnstructuredObject, repo string, pth string, message string) { - err := yaml.WriteYamlFile(filepath.Join(repo, pth), y) - if err != nil { - p.t.Fatal(err) - } - if message == "" { - relPath, err := filepath.Rel(p.baseDir, repo) - if err != nil { - p.t.Fatal(err) - } - message = fmt.Sprintf("update %s", filepath.Join(relPath, pth)) - } - p.commitFiles(repo, []string{pth}, false, message) -} - -func (p *testProject) updateYaml(repo string, pth string, update func(o *uo.UnstructuredObject) error, message string) { - if !strings.HasPrefix(repo, p.baseDir) { - p.t.Fatal() - } - o := uo.New() - if utils.Exists(filepath.Join(repo, pth)) { - err := yaml.ReadYamlFile(filepath.Join(repo, pth), o) - if err != nil { - p.t.Fatal(err) - } - } - orig := o.Clone() - err := update(o) - if err != nil { - p.t.Fatal(err) - } - if reflect.DeepEqual(o, orig) { - return - } - p.commitYaml(o, repo, pth, message) } func (p *testProject) updateKluctlYaml(update func(o *uo.UnstructuredObject) error) { - p.updateYaml(p.getKluctlProjectDir(), ".kluctl.yml", update, "") + p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), ".kluctl.yml", update, "") } func (p *testProject) updateDeploymentYaml(dir string, update func(o *uo.UnstructuredObject) error) { - p.updateYaml(p.getDeploymentDir(), filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { + p.gitServer.UpdateYaml(p.getDeploymentRepo(), filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { if dir == "." { o.SetNestedField(p.projectName, "commonLabels", "project_name") } @@ -236,7 +91,7 @@ func (p *testProject) updateDeploymentYaml(dir string, update func(o *uo.Unstruc } func (p *testProject) getDeploymentYaml(dir string) *uo.UnstructuredObject { - o, err := uo.FromFile(filepath.Join(p.getDeploymentDir(), dir, "deployment.yml")) + o, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getDeploymentRepo()), dir, "deployment.yml")) if err != nil { p.t.Fatal(err) } @@ -268,24 +123,17 @@ func (p *testProject) listDeploymentItemPathes(dir string, fullPath bool) []stri } func (p *testProject) updateKustomizeDeployment(dir string, update func(o *uo.UnstructuredObject, wt *git.Worktree) error) { - r, err := git.PlainOpen(p.getDeploymentDir()) - if err != nil { - p.t.Fatal(err) - } - wt, err := r.Worktree() - if err != nil { - p.t.Fatal(err) - } + wt := p.gitServer.GetWorktree(p.getDeploymentRepo()) pth := filepath.Join(dir, "kustomization.yml") - p.updateYaml(p.getDeploymentDir(), pth, func(o *uo.UnstructuredObject) error { + p.gitServer.UpdateYaml(p.getDeploymentRepo(), pth, func(o *uo.UnstructuredObject) error { return update(o, wt) }, fmt.Sprintf("Update kustomization.yml for %s", dir)) } func (p *testProject) updateCluster(name string, context string, vars *uo.UnstructuredObject) { pth := filepath.Join("clusters", fmt.Sprintf("%s.yml", name)) - p.updateYaml(p.getClustersDir(), pth, func(o *uo.UnstructuredObject) error { + p.gitServer.UpdateYaml(p.getClustersRepo(), pth, func(o *uo.UnstructuredObject) error { o.Clear() o.SetNestedField(name, "cluster", "name") o.SetNestedField(context, "cluster", "context") @@ -379,7 +227,7 @@ func (p *testProject) addKustomizeDeployment(dir string, resources []kustomizeRe p.addDeploymentIncludes(deploymentDir) } - absKustomizeDir := filepath.Join(p.getDeploymentDir(), dir) + absKustomizeDir := filepath.Join(p.gitServer.LocalRepoDir(p.getDeploymentRepo()), dir) err := os.MkdirAll(absKustomizeDir, 0o700) if err != nil { @@ -438,7 +286,7 @@ func (p *testProject) addKustomizeResources(dir string, resources []kustomizeRes for _, r := range resources { l = append(l, r.name) x := p.convertInterfaceToList(r.content) - err := yaml.WriteYamlAllFile(filepath.Join(p.getDeploymentDir(), dir, r.name), x) + err := yaml.WriteYamlAllFile(filepath.Join(p.gitServer.LocalRepoDir(p.getDeploymentRepo()), dir, r.name), x) if err != nil { return err } @@ -467,34 +315,31 @@ func (p *testProject) deleteKustomizeDeployment(dir string) { }) } -func (p *testProject) getKluctlProjectDir() string { - return filepath.Join(p.baseDir, "kluctl-project") +func (p *testProject) getKluctlProjectRepo() string { + return "kluctl-project" } -func (p *testProject) getClustersDir() string { +func (p *testProject) getClustersRepo() string { if p.clustersExternal { - return filepath.Join(p.baseDir, "external-clusters") + return "external-clusters" } - return p.getKluctlProjectDir() + return p.getKluctlProjectRepo() } -func (p *testProject) getDeploymentDir() string { +func (p *testProject) getDeploymentRepo() string { if p.deploymentExternal { - return filepath.Join(p.baseDir, "external-deployment") + return "external-deployment" } - return p.getKluctlProjectDir() + return p.getKluctlProjectRepo() } -func (p *testProject) getSealedSecretsDir() string { +func (p *testProject) getSealedSecretsRepo() string { if p.sealedSecretsExternal { - return filepath.Join(p.baseDir, "external-sealed-secrets") + return "external-sealed-secrets" } - return p.getKluctlProjectDir() + return p.getKluctlProjectRepo() } -const stdoutStartMarker = "========= stdout start =========" -const stdoutEndMarker = "========= stdout end =========" - func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { var args []string args = append(args, argsIn...) @@ -502,9 +347,9 @@ func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { cwd := "" if p.kluctlProjectExternal { - args = append(args, "--project-url", p.buildLocalGitUrl(p.getKluctlProjectDir())) + args = append(args, "--project-url", p.gitServer.LocalGitUrl(p.getKluctlProjectRepo())) } else { - cwd = p.getKluctlProjectDir() + cwd = p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) } if p.localClusters != nil { @@ -561,11 +406,3 @@ func (p *testProject) KluctlMust(argsIn ...string) (string, string) { } return stdout, stderr } - -func (p *testProject) buildLocalGitUrl(repo string) string { - relPth, err := filepath.Rel(p.baseDir, repo) - if err != nil { - log.Panic(err) - } - return fmt.Sprintf("http://localhost:%d/%s/.git", p.gitServerPort, relPth) -} diff --git a/internal/test-utils/git_server.go b/internal/test-utils/git_server.go new file mode 100644 index 000000000..29727fdeb --- /dev/null +++ b/internal/test-utils/git_server.go @@ -0,0 +1,223 @@ +package test_utils + +import ( + "context" + "fmt" + "github.com/go-git/go-git/v5" + http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "io/ioutil" + "log" + "net" + "net/http" + "os" + "path/filepath" + "reflect" + "testing" +) + +type GitServer struct { + t *testing.T + + baseDir string + + gitServer *http_server.Server + gitHttpServer *http.Server + gitServerPort int +} + +func NewGitServer(t *testing.T) *GitServer { + p := &GitServer{ + t: t, + } + + baseDir, err := ioutil.TempDir(os.TempDir(), "kluctl-tests-") + if err != nil { + p.t.Fatal(err) + } + p.baseDir = baseDir + + p.initGitServer() + + t.Cleanup(func() { + p.Cleanup() + }) + + return p +} + +func (p *GitServer) initGitServer() { + p.gitServer = http_server.New(p.baseDir) + + p.gitHttpServer = &http.Server{ + Addr: "127.0.0.1:0", + Handler: p.gitServer, + } + + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + log.Fatal(err) + } + a := ln.Addr().(*net.TCPAddr) + p.gitServerPort = a.Port + + go func() { + _ = p.gitHttpServer.Serve(ln) + }() +} + +func (p *GitServer) Cleanup() { + if p.gitHttpServer != nil { + _ = p.gitHttpServer.Shutdown(context.Background()) + p.gitHttpServer = nil + p.gitServer = nil + } + + if p.baseDir == "" { + return + } + _ = os.RemoveAll(p.baseDir) + p.baseDir = "" +} + +func (p *GitServer) GitInit(repo string) { + dir := p.LocalRepoDir(repo) + + err := os.MkdirAll(dir, 0o700) + if err != nil { + p.t.Fatal(err) + } + + r, err := git.PlainInit(dir, false) + if err != nil { + p.t.Fatal(err) + } + config, err := r.Config() + if err != nil { + p.t.Fatal(err) + } + wt, err := r.Worktree() + if err != nil { + p.t.Fatal(err) + } + + config.User.Name = "Test User" + config.User.Email = "no@mail.com" + config.Author = config.User + config.Committer = config.User + err = r.SetConfig(config) + if err != nil { + p.t.Fatal(err) + } + err = utils.Touch(filepath.Join(dir, ".dummy")) + if err != nil { + p.t.Fatal(err) + } + _, err = wt.Add(".dummy") + if err != nil { + p.t.Fatal(err) + } + _, err = wt.Commit("initial", &git.CommitOptions{}) + if err != nil { + p.t.Fatal(err) + } +} + +func (p *GitServer) CommitFiles(repo string, add []string, all bool, message string) { + r, err := git.PlainOpen(p.LocalRepoDir(repo)) + if err != nil { + p.t.Fatal(err) + } + wt, err := r.Worktree() + if err != nil { + p.t.Fatal(err) + } + for _, a := range add { + _, err = wt.Add(a) + if err != nil { + p.t.Fatal(err) + } + } + _, err = wt.Commit(message, &git.CommitOptions{ + All: all, + }) + if err != nil { + p.t.Fatal(err) + } +} + +func (p *GitServer) CommitYaml(repo string, pth string, message string, y *uo.UnstructuredObject) { + fullPath := filepath.Join(p.LocalRepoDir(repo), pth) + + err := yaml.WriteYamlFile(fullPath, y) + if err != nil { + p.t.Fatal(err) + } + if message == "" { + message = fmt.Sprintf("update %s", filepath.Join(repo, pth)) + } + p.CommitFiles(repo, []string{pth}, false, message) +} + +func (p *GitServer) UpdateYaml(repo string, pth string, update func(o *uo.UnstructuredObject) error, message string) { + fullPath := filepath.Join(p.LocalRepoDir(repo), pth) + + o := uo.New() + if utils.Exists(fullPath) { + err := yaml.ReadYamlFile(fullPath, o) + if err != nil { + p.t.Fatal(err) + } + } + orig := o.Clone() + err := update(o) + if err != nil { + p.t.Fatal(err) + } + if reflect.DeepEqual(o, orig) { + return + } + p.CommitYaml(repo, pth, message, o) +} + +func (p *GitServer) convertInterfaceToList(x interface{}) []interface{} { + var ret []interface{} + if l, ok := x.([]interface{}); ok { + return l + } + if l, ok := x.([]*uo.UnstructuredObject); ok { + for _, y := range l { + ret = append(ret, y) + } + return ret + } + if l, ok := x.([]map[string]interface{}); ok { + for _, y := range l { + ret = append(ret, y) + } + return ret + } + return []interface{}{x} +} + +func (p *GitServer) LocalGitUrl(repo string) string { + return fmt.Sprintf("http://localhost:%d/%s/.git", p.gitServerPort, repo) +} + +func (p *GitServer) LocalRepoDir(repo string) string { + return filepath.Join(p.baseDir, repo) +} + +func (p *GitServer) GetWorktree(repo string) *git.Worktree { + r, err := git.PlainOpen(p.LocalRepoDir(repo)) + if err != nil { + p.t.Fatal(err) + } + wt, err := r.Worktree() + if err != nil { + p.t.Fatal(err) + } + return wt +} From 7a9102fb841247c5643b8e5667919fb1e2689ddb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 17:21:30 +0200 Subject: [PATCH 0196/2268] refactor: Use dynamic client for CRD discovery --- pkg/k8s/resources.go | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 3010d295a..6cdf84ec6 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -155,17 +155,13 @@ func (k *k8sResources) updateResources() error { return nil } -var crdGK = schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"} +var crdGVR = schema.GroupVersionResource{Group: "apiextensions.k8s.io", Version: "v1", Resource: "customresourcedefinitions"} func (k *k8sResources) updateResourcesFromCRDs(clients *k8sClients) error { - var crdList *apiextensionsv1.CustomResourceDefinitionList - _, err := clients.withClientFromPool(func(p *parallelClientEntry) error { - c, err := apiextensionsclient.NewForConfigAndClient(k.restConfig, p.http) - if err != nil { - return err - } - - crdList, err = c.CustomResourceDefinitions().List(k.ctx, v1.ListOptions{}) + var crdList *unstructured.UnstructuredList + _, err := clients.withDynamicClientForGVR(&crdGVR, "", func(r dynamic.ResourceInterface) error { + var err error + crdList, err = r.List(k.ctx, v1.ListOptions{}) return err }) if err != nil { @@ -173,11 +169,8 @@ func (k *k8sResources) updateResourcesFromCRDs(clients *k8sClients) error { } for _, x := range crdList.Items { - u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&x) - if err != nil { - return err - } - crd := uo.FromMap(u) + x := x + crd := uo.FromUnstructured(&x) err = k.UpdateResourcesFromCRD(crd) if err != nil { return err From a2209cb2687cc6056bbae600e0a2fe5a42a106a5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 17:45:15 +0200 Subject: [PATCH 0197/2268] refactor: Split out k8s client creation to allow fake clients --- pkg/k8s/client.go | 40 +++++-------- pkg/k8s/client_factory.go | 87 ++++++++++++++++++++++++++++ pkg/k8s/k8s_cluster.go | 22 +++---- pkg/k8s/resources.go | 28 ++------- pkg/kluctl_project/target_context.go | 6 +- 5 files changed, 122 insertions(+), 61 deletions(-) create mode 100644 pkg/k8s/client_factory.go diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index fbda2dbd5..76f01135e 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -7,20 +7,17 @@ import ( "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/metadata" - "k8s.io/client-go/rest" - "net/http" ) type k8sClients struct { - ctx context.Context - restConfig *rest.Config - clientPool chan *parallelClientEntry - count int + ctx context.Context + clientFactory ClientFactory + clientPool chan *parallelClientEntry + count int } type parallelClientEntry struct { - http *http.Client - corev1 *corev1.CoreV1Client + corev1 corev1.CoreV1Interface dynamicClient dynamic.Interface metadataClient metadata.Interface @@ -41,37 +38,30 @@ func (p *parallelClientEntry) HandleWarningHeader(code int, agent string, text s }) } -func newK8sClients(ctx context.Context, restConfig *rest.Config, count int) (*k8sClients, error) { +func newK8sClients(ctx context.Context, clientFactory ClientFactory, count int) (*k8sClients, error) { var err error k := &k8sClients{ - ctx: ctx, - restConfig: restConfig, - clientPool: make(chan *parallelClientEntry, count), - count: count, + ctx: ctx, + clientFactory: clientFactory, + clientPool: make(chan *parallelClientEntry, count), + count: count, } for i := 0; i < count; i++ { p := ¶llelClientEntry{} - config := rest.CopyConfig(k.restConfig) - config.WarningHandler = p - p.http, err = rest.HTTPClientFor(config) + p.corev1, err = clientFactory.CoreV1Client(p) if err != nil { return nil, err } - p.corev1, err = corev1.NewForConfigAndClient(config, p.http) + p.dynamicClient, err = clientFactory.DynamicClient(p) if err != nil { return nil, err } - p.dynamicClient, err = dynamic.NewForConfigAndClient(config, p.http) - if err != nil { - return nil, err - } - - p.metadataClient, err = metadata.NewForConfigAndClient(config, p.http) + p.metadataClient, err = clientFactory.MetadataClient(p) if err != nil { return nil, err } @@ -82,10 +72,10 @@ func newK8sClients(ctx context.Context, restConfig *rest.Config, count int) (*k8 } func (k *k8sClients) close() { + k.clientFactory.CloseIdleConnections() if k.clientPool != nil { for i := 0; i < k.count; i++ { - p := <-k.clientPool - p.http.CloseIdleConnections() + _ = <-k.clientPool } } k.clientPool = nil diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go new file mode 100644 index 000000000..0205c475c --- /dev/null +++ b/pkg/k8s/client_factory.go @@ -0,0 +1,87 @@ +package k8s + +import ( + "github.com/kluctl/kluctl/v2/pkg/utils" + "k8s.io/client-go/discovery" + "k8s.io/client-go/discovery/cached/disk" + "k8s.io/client-go/dynamic" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/metadata" + "k8s.io/client-go/rest" + "net/http" + "net/url" + "path/filepath" + "time" +) + +type ClientFactory interface { + GetCA() []byte + + CloseIdleConnections() + + DiscoveryClient() (discovery.DiscoveryInterface, error) + CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1Interface, error) + DynamicClient(wh rest.WarningHandler) (dynamic.Interface, error) + MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) +} + +type realClientFactory struct { + config *rest.Config + httpClient *http.Client +} + +func (r *realClientFactory) GetCA() []byte { + return r.config.CAData +} + +func (r *realClientFactory) DiscoveryClient() (discovery.DiscoveryInterface, error) { + + apiHost, err := url.Parse(r.config.Host) + if err != nil { + return nil, err + } + discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(), "kube-cache/discovery", apiHost.Hostname()) + discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(r.config), discoveryCacheDir, "", time.Hour*24) + if err != nil { + return nil, err + } + return discovery2, nil +} + +func (r *realClientFactory) CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1Interface, error) { + config := rest.CopyConfig(r.config) + config.WarningHandler = wh + return corev1.NewForConfigAndClient(config, r.httpClient) +} + +func (r *realClientFactory) DynamicClient(wh rest.WarningHandler) (dynamic.Interface, error) { + config := rest.CopyConfig(r.config) + config.WarningHandler = wh + return dynamic.NewForConfigAndClient(config, r.httpClient) +} + +func (r *realClientFactory) MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) { + config := rest.CopyConfig(r.config) + config.WarningHandler = wh + return metadata.NewForConfigAndClient(config, r.httpClient) +} + +func (r *realClientFactory) CloseIdleConnections() { + r.httpClient.CloseIdleConnections() +} + +func NewClientFactory(configIn *rest.Config) (ClientFactory, error) { + restConfig := rest.CopyConfig(configIn) + restConfig.QPS = 10 + restConfig.Burst = 20 + + httpClient, err := rest.HTTPClientFor(restConfig) + if err != nil { + return nil, err + } + + return &realClientFactory{ + config: restConfig, + httpClient: httpClient, + }, nil +} diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 51fb487b9..5e30e45c6 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -35,33 +35,29 @@ type K8sCluster struct { DryRun bool - restConfig *rest.Config - clients *k8sClients + clientFactory ClientFactory + clients *k8sClients ServerVersion *goversion.Version Resources *k8sResources } -func NewK8sCluster(ctx context.Context, configIn *rest.Config, dryRun bool) (*K8sCluster, error) { +func NewK8sCluster(ctx context.Context, clientFactory ClientFactory, dryRun bool) (*K8sCluster, error) { var err error - restConfig := rest.CopyConfig(configIn) - restConfig.QPS = 10 - restConfig.Burst = 20 - k := &K8sCluster{ - ctx: ctx, - DryRun: dryRun, - restConfig: restConfig, + ctx: ctx, + DryRun: dryRun, + clientFactory: clientFactory, } - k.Resources, err = newK8sResources(ctx, restConfig) + k.Resources, err = newK8sResources(ctx, clientFactory) if err != nil { return nil, err } - k.clients, err = newK8sClients(ctx, restConfig, 16) + k.clients, err = newK8sClients(ctx, clientFactory, 16) if err != nil { return nil, err } @@ -108,7 +104,7 @@ func (k *K8sCluster) ReadWrite() *K8sCluster { } func (k *K8sCluster) GetCA() []byte { - return k.restConfig.CAData + return k.clientFactory.GetCA() } func (k *K8sCluster) buildLabelSelector(labels map[string]string) string { diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 6cdf84ec6..c1338ecee 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -4,32 +4,22 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/discovery" - "k8s.io/client-go/discovery/cached/disk" "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" - "net/url" - "path/filepath" "sort" "strings" "sync" - "time" - - apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" ) type k8sResources struct { - ctx context.Context - restConfig *rest.Config - discovery *disk.CachedDiscoveryClient + ctx context.Context + discovery discovery.DiscoveryInterface allResources map[schema.GroupVersionKind]v1.APIResource preferredResources map[schema.GroupKind]v1.APIResource @@ -37,26 +27,20 @@ type k8sResources struct { mutex sync.Mutex } -func newK8sResources(ctx context.Context, config *rest.Config) (*k8sResources, error) { +func newK8sResources(ctx context.Context, clientFactory ClientFactory) (*k8sResources, error) { k := &k8sResources{ ctx: ctx, - restConfig: config, allResources: map[schema.GroupVersionKind]v1.APIResource{}, preferredResources: map[schema.GroupKind]v1.APIResource{}, crds: map[schema.GroupKind]*uo.UnstructuredObject{}, mutex: sync.Mutex{}, } - apiHost, err := url.Parse(config.Host) - if err != nil { - return nil, err - } - discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(), "kube-cache/discovery", apiHost.Hostname()) - discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(config), discoveryCacheDir, "", time.Hour*24) + var err error + k.discovery, err = clientFactory.DiscoveryClient() if err != nil { return nil, err } - k.discovery = discovery2 return k, nil } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index f18b0a880..eae3b64e5 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -51,7 +51,11 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s var k *k8s.K8sCluster if clientConfig != nil { s := status.Start(ctx, fmt.Sprintf("Initializing k8s client")) - k, err = k8s.NewK8sCluster(ctx, clientConfig, dryRun) + clientFactory, err := k8s.NewClientFactory(clientConfig) + if err != nil { + return nil, err + } + k, err = k8s.NewK8sCluster(ctx, clientFactory, dryRun) if err != nil { s.Failed() return nil, err From fa80323c466ae123e0fa273d0362795c5b3b6f82 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 17:45:38 +0200 Subject: [PATCH 0198/2268] fix: Allow uint as well in GetNestedInt --- pkg/utils/uo/nested_fields.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pkg/utils/uo/nested_fields.go b/pkg/utils/uo/nested_fields.go index 325c581f3..b3927dc9d 100644 --- a/pkg/utils/uo/nested_fields.go +++ b/pkg/utils/uo/nested_fields.go @@ -1,6 +1,9 @@ package uo -import "fmt" +import ( + "fmt" + "reflect" +) func (uo *UnstructuredObject) GetNestedField(keys ...interface{}) (interface{}, bool, error) { var o interface{} = uo.Object @@ -97,15 +100,14 @@ func (uo *UnstructuredObject) GetNestedInt(keys ...interface{}) (int64, bool, er if !found { return 0, false, nil } - i, ok := v.(int64) - if !ok { - if i2, ok := v.(int); ok { - i = int64(i2) - } else { - return 0, false, fmt.Errorf("value at %s is not an int", KeyPath(keys).ToJsonPath()) - } + vv := reflect.ValueOf(v) + if vv.CanInt() { + return vv.Int(), true, nil + } else if vv.CanUint() { + return int64(vv.Uint()), true, nil + } else { + return 0, false, fmt.Errorf("value at %s is not an int", KeyPath(keys).ToJsonPath()) } - return i, true, nil } func (uo *UnstructuredObject) GetNestedList(keys ...interface{}) ([]interface{}, bool, error) { From 603dd593fbf3f14eee82623df06db0175acde1ab Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 19 May 2022 17:46:18 +0200 Subject: [PATCH 0199/2268] fix: Add NoopStatusHandler so that status handling is ignored while testing --- pkg/status/noop.go | 49 ++++++++++++++++++++++++++++++++++++++++++++ pkg/status/status.go | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 pkg/status/noop.go diff --git a/pkg/status/noop.go b/pkg/status/noop.go new file mode 100644 index 000000000..1f30341a0 --- /dev/null +++ b/pkg/status/noop.go @@ -0,0 +1,49 @@ +package status + +type NoopStatusHandler struct { +} + +type NoopStatusLine struct { +} + +func (n NoopStatusHandler) SetTrace(trace bool) { +} + +func (n NoopStatusHandler) Stop() { +} + +func (n NoopStatusHandler) StartStatus(total int, message string) StatusLine { + return &NoopStatusLine{} +} + +func (n NoopStatusHandler) Info(message string) { +} + +func (n NoopStatusHandler) Warning(message string) { +} + +func (n NoopStatusHandler) Error(message string) { +} + +func (n NoopStatusHandler) Trace(message string) { +} + +func (n NoopStatusHandler) PlainText(text string) { +} + +func (n NoopStatusHandler) InfoFallback(message string) { +} + +var _ StatusHandler = &NoopStatusHandler{} + +func (n NoopStatusLine) SetTotal(total int) { +} + +func (n NoopStatusLine) Increment() { +} + +func (n NoopStatusLine) Update(message string) { +} + +func (n NoopStatusLine) End(result EndResult) { +} diff --git a/pkg/status/status.go b/pkg/status/status.go index d58c774f2..f1f60395c 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -59,7 +59,7 @@ func NewContext(ctx context.Context, slh StatusHandler) context.Context { func FromContext(ctx context.Context) StatusHandler { v := ctx.Value(contextKey{}) if v == nil { - return nil + return &NoopStatusHandler{} } return v.(StatusHandler) } From f7ee0228cefd95566f50786122225a1586a8cdea Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 20 May 2022 14:51:15 +0200 Subject: [PATCH 0200/2268] refactor: Use semver to compare kubernetes versions This fixes issues with k8s versions while testing. --- go.mod | 3 +-- go.sum | 2 -- pkg/k8s/k8s_cluster.go | 22 ++++++++++------------ 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 4958074bb..8ba0c8880 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e + github.com/Masterminds/semver/v3 v3.1.1 github.com/aws/aws-sdk-go v1.44.1 github.com/bitnami-labs/sealed-secrets v0.17.5 github.com/cyphar/filepath-securejoin v0.2.3 @@ -16,7 +17,6 @@ require ( github.com/goccy/go-yaml v1.9.5 github.com/golang-jwt/jwt/v4 v4.4.1 github.com/google/go-containerregistry v0.8.0 - github.com/hashicorp/go-version v1.4.0 github.com/hexops/gotextdiff v1.0.3 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 @@ -73,7 +73,6 @@ require ( github.com/BurntSushi/toml v1.1.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Masterminds/squirrel v1.5.2 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect diff --git a/go.sum b/go.sum index dc7ef08f9..fe9ab20ed 100644 --- a/go.sum +++ b/go.sum @@ -753,8 +753,6 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= -github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 5e30e45c6..289e3d53a 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" "fmt" - goversion "github.com/hashicorp/go-version" + "github.com/Masterminds/semver/v3" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -16,6 +16,7 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/dynamic" "k8s.io/client-go/metadata" _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -38,7 +39,7 @@ type K8sCluster struct { clientFactory ClientFactory clients *k8sClients - ServerVersion *goversion.Version + ServerVersion *version.Info Resources *k8sResources } @@ -66,11 +67,7 @@ func NewK8sCluster(ctx context.Context, clientFactory ClientFactory, dryRun bool if err != nil { return nil, err } - v2, err := goversion.NewVersion(v.String()) - if err != nil { - return nil, err - } - k.ServerVersion = v2 + k.ServerVersion = v var wg sync.WaitGroup wg.Add(2) @@ -326,17 +323,18 @@ func (k *K8sCluster) waitForDeletedObject(ref k8s.ObjectRef) error { return nil } -var v1_21, _ = goversion.NewVersion("1.21") -var v1_1000, _ = goversion.NewVersion("1.1000") - func (k *K8sCluster) FixObjectForPatch(o *uo.UnstructuredObject) *uo.UnstructuredObject { // A bug in versions < 1.20 cause errors when applying resources that have some fields omitted which have // default values. We need to fix these resources. // UPDATE even though https://github.com/kubernetes-sigs/structured-merge-diff/issues/130 says it's fixed, the // issue is still present. - needsDefaultsFix := k.ServerVersion.LessThan(v1_21) || true + k8sVersion, err := semver.NewVersion(k.ServerVersion.String()) + if err != nil { + return o + } + needsDefaultsFix := k8sVersion.LessThan(semver.MustParse("1.21")) || true // TODO check when this is actually fixed (see https://github.com/kubernetes/kubernetes/issues/94275) - needsTypeConversionFix := k.ServerVersion.LessThan(v1_1000) + needsTypeConversionFix := k8sVersion.LessThan(semver.MustParse("1.100")) if !needsDefaultsFix && !needsTypeConversionFix { return o } From 3ed29312d874368ab02ea4d426b33c8178afad85 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 20 May 2022 14:52:35 +0200 Subject: [PATCH 0201/2268] fix: Stop using ServerPreferredResources() from discovery client Instead, use a custom implementation of the preferred version detection. ServerPreferredResources() is not implemented in the fake discovery client, making it unusable while testing. --- pkg/k8s/resources.go | 51 +++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index c1338ecee..e8a3d843e 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -54,17 +54,12 @@ func (k *k8sResources) updateResources() error { k.crds = map[schema.GroupKind]*uo.UnstructuredObject{} // the discovery client doesn't support cancellation, so we need to run it in the background and wait for it + var ags []*v1.APIGroup var arls []*v1.APIResourceList - var preferredArls []*v1.APIResourceList finished := make(chan error) go func() { var err error - _, arls, err = k.discovery.ServerGroupsAndResources() - if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { - finished <- err - return - } - preferredArls, err = k.discovery.ServerPreferredResources() + ags, arls, err = k.discovery.ServerGroupsAndResources() if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { finished <- err return @@ -82,6 +77,17 @@ func (k *k8sResources) updateResources() error { } for _, arl := range arls { + var ag *v1.APIGroup + for _, x := range ags { + if x.Name == arl.GroupVersionKind().Group { + ag = x + break + } + } + if ag == nil { + continue + } + for _, ar := range arl.APIResources { if strings.Index(ar.Name, "/") != -1 { // skip subresources @@ -104,38 +110,15 @@ func (k *k8sResources) updateResources() error { if _, ok := deprecatedResources[gvk.GroupKind()]; ok { continue } - if _, ok := k.allResources[gvk]; ok { - ok = false - } - k.allResources[gvk] = ar - } - } - - for _, arl := range preferredArls { - for _, ar := range arl.APIResources { - if strings.Index(ar.Name, "/") != -1 { - // skip subresources - continue - } - gv, err := schema.ParseGroupVersion(arl.GroupVersion) - if err != nil { - continue - } - ar := ar - ar.Group = gv.Group - ar.Version = gv.Version + k.allResources[gvk] = ar - gk := schema.GroupKind{ - Group: ar.Group, - Kind: ar.Kind, + if gvk.Version == ag.PreferredVersion.Version { + k.preferredResources[gvk.GroupKind()] = ar } - if _, ok := deprecatedResources[gk]; ok { - continue - } - k.preferredResources[gk] = ar } } + return nil } From b2b7d0d0f4c5139d7e0b43b7db92b3cb5d5f61c1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 20 May 2022 14:53:07 +0200 Subject: [PATCH 0202/2268] refactor: Move kyaml background init into utils package --- pkg/deployment/deployment_item.go | 28 +--------------------------- pkg/utils/init_kyaml.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 27 deletions(-) create mode 100644 pkg/utils/init_kyaml.go diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 15463a61a..7051ce9c3 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -15,10 +15,7 @@ import ( "path/filepath" "sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/krusty" - "sigs.k8s.io/kustomize/kyaml/openapi" - yaml2 "sigs.k8s.io/kustomize/kyaml/yaml" "strings" - "sync" ) const SealmeExt = ".sealme" @@ -336,7 +333,7 @@ func (di *DeploymentItem) buildKustomize() error { return err } - waitForOpenapiInitDone() + utils.WaitForOpenapiInitDone() ko := krusty.MakeDefaultOptions() k := krusty.MakeKustomizer(ko) @@ -452,26 +449,3 @@ func (di *DeploymentItem) postprocessObjects(k *k8s.K8sCluster, images *Images) return nil } - -var openapiInitDoneMutex sync.Mutex -var openapiInitDoneOnce sync.Once - -func waitForOpenapiInitDone() { - openapiInitDoneOnce.Do(func() { - openapiInitDoneMutex.Lock() - openapiInitDoneMutex.Unlock() - }) -} - -func init() { - openapiInitDoneMutex.Lock() - go func() { - // we do a single call to IsNamespaceScoped to enforce openapi schema initialization - // this is required here to ensure that it is later not done in parallel which would cause race conditions - openapi.IsNamespaceScoped(yaml2.TypeMeta{ - APIVersion: "", - Kind: "ConfigMap", - }) - openapiInitDoneMutex.Unlock() - }() -} diff --git a/pkg/utils/init_kyaml.go b/pkg/utils/init_kyaml.go new file mode 100644 index 000000000..a25bddbb0 --- /dev/null +++ b/pkg/utils/init_kyaml.go @@ -0,0 +1,30 @@ +package utils + +import ( + "sigs.k8s.io/kustomize/kyaml/openapi" + yaml2 "sigs.k8s.io/kustomize/kyaml/yaml" + "sync" +) + +var openapiInitDoneMutex sync.Mutex +var openapiInitDoneOnce sync.Once + +func WaitForOpenapiInitDone() { + openapiInitDoneOnce.Do(func() { + openapiInitDoneMutex.Lock() + openapiInitDoneMutex.Unlock() + }) +} + +func init() { + openapiInitDoneMutex.Lock() + go func() { + // we do a single call to IsNamespaceScoped to enforce openapi schema initialization + // this is required here to ensure that it is later not done in parallel which would cause race conditions + openapi.IsNamespaceScoped(yaml2.TypeMeta{ + APIVersion: "", + Kind: "ConfigMap", + }) + openapiInitDoneMutex.Unlock() + }() +} From d6f236c6c579ce3c1efff8e0618bb1200b80fff4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 22 May 2022 16:44:07 +0200 Subject: [PATCH 0203/2268] fix: Fix broken ARN parsing --- pkg/utils/aws/arn.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/utils/aws/arn.go b/pkg/utils/aws/arn.go index a8f88ee45..3b7141e99 100644 --- a/pkg/utils/aws/arn.go +++ b/pkg/utils/aws/arn.go @@ -17,7 +17,7 @@ type Arn struct { func ParseArn(arn string) (Arn, error) { // http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html - elements := strings.SplitN(arn, ":", 5) + elements := strings.SplitN(arn, ":", 6) if len(elements) < 6 { return Arn{}, fmt.Errorf("%s is not a valid arn", arn) } @@ -29,11 +29,11 @@ func ParseArn(arn string) (Arn, error) { result.Account = elements[4] result.Resource = elements[5] - if strings.Index(result.Resource, "/") != 0 { + if strings.Index(result.Resource, "/") != -1 { s := strings.SplitN(result.Resource, "/", 2) result.ResourceType = s[0] result.Resource = s[1] - } else if strings.Index(result.Resource, ":") != 0 { + } else if strings.Index(result.Resource, ":") != -1 { s := strings.SplitN(result.Resource, ":", 2) result.ResourceType = s[0] result.Resource = s[1] From a79d1722816e8548af6b2aa1baf25314ca77e3b7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 22 May 2022 16:45:39 +0200 Subject: [PATCH 0204/2268] refactor: Introduce AWS client factory for VarsLoader This allows mocking of AWS clients. --- pkg/kluctl_project/target_context.go | 3 +- pkg/{utils => vars}/aws/arn.go | 0 pkg/vars/aws/clientfactory.go | 44 ++++++++++++++++++++++ pkg/{utils => vars}/aws/secrets_manager.go | 23 +++-------- pkg/vars/vars_loader.go | 12 ++++-- 5 files changed, 61 insertions(+), 21 deletions(-) rename pkg/{utils => vars}/aws/arn.go (100%) create mode 100644 pkg/vars/aws/clientfactory.go rename pkg/{utils => vars}/aws/secrets_manager.go (55%) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index eae3b64e5..400d9a118 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -10,6 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars" + "github.com/kluctl/kluctl/v2/pkg/vars/aws" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd/api" "path/filepath" @@ -63,7 +64,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s s.Success() } - varsLoader := vars.NewVarsLoader(ctx, k, p.GRC) + varsLoader := vars.NewVarsLoader(ctx, k, p.GRC, aws.NewClientFactory()) if forSeal { err = p.loadSecrets(target, varsCtx, varsLoader) diff --git a/pkg/utils/aws/arn.go b/pkg/vars/aws/arn.go similarity index 100% rename from pkg/utils/aws/arn.go rename to pkg/vars/aws/arn.go diff --git a/pkg/vars/aws/clientfactory.go b/pkg/vars/aws/clientfactory.go new file mode 100644 index 000000000..db7e43d0e --- /dev/null +++ b/pkg/vars/aws/clientfactory.go @@ -0,0 +1,44 @@ +package aws + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/secretsmanager" + "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" + "os" +) + +type AwsClientFactory interface { + SecretsManagerClient(profile *string, region *string) (secretsmanageriface.SecretsManagerAPI, error) +} + +type awsClientFactory struct { +} + +func (a *awsClientFactory) getSession(profile *string) (*session.Session, error) { + var opts session.Options + opts.SharedConfigState = session.SharedConfigEnable + // Environment variable always takes precedence + if _, ok := os.LookupEnv("AWS_PROFILE"); !ok && profile != nil { + opts.Profile = *profile + } + s, err := session.NewSessionWithOptions(opts) + if err != nil { + return nil, err + } + + return s, nil +} + +func (a *awsClientFactory) SecretsManagerClient(profile *string, region *string) (secretsmanageriface.SecretsManagerAPI, error) { + s, err := a.getSession(profile) + if err != nil { + return nil, err + } + + return secretsmanager.New(s, &aws.Config{Region: region}), nil +} + +func NewClientFactory() AwsClientFactory { + return &awsClientFactory{} +} diff --git a/pkg/utils/aws/secrets_manager.go b/pkg/vars/aws/secrets_manager.go similarity index 55% rename from pkg/utils/aws/secrets_manager.go rename to pkg/vars/aws/secrets_manager.go index bd440439b..1a57bd5ae 100644 --- a/pkg/utils/aws/secrets_manager.go +++ b/pkg/vars/aws/secrets_manager.go @@ -2,25 +2,10 @@ package aws import ( "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/secretsmanager" - - "os" ) -func GetAwsSecretsManagerSecret(profile *string, region *string, secretName string) (string, error) { - var opts session.Options - opts.SharedConfigState = session.SharedConfigEnable - // Environment variable always takes precedence - if _, ok := os.LookupEnv("AWS_PROFILE"); !ok && region != nil { - opts.Profile = *profile - } - s, err := session.NewSessionWithOptions(opts) - if err != nil { - return "", err - } - +func GetAwsSecretsManagerSecret(aws AwsClientFactory, profile *string, region *string, secretName string) (string, error) { if region == nil { arn, err := ParseArn(secretName) if err != nil { @@ -29,7 +14,11 @@ func GetAwsSecretsManagerSecret(profile *string, region *string, secretName stri region = &arn.Region } - smClient := secretsmanager.New(s, &aws.Config{Region: region}) + smClient, err := aws.SecretsManagerClient(profile, region) + if err != nil { + return "", fmt.Errorf("getting secret %s from AWS secrets manager failed: %w", secretName, err) + } + r, err := smClient.GetSecretValue(&secretsmanager.GetSecretValueInput{ SecretId: &secretName, }) diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index e6f912b29..9900ce36b 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -9,8 +9,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/aws" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/vars/aws" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" "os" @@ -25,15 +25,17 @@ type VarsLoader struct { ctx context.Context k *k8s.K8sCluster grc *git.MirroredGitRepoCollection + aws aws.AwsClientFactory credentialsCache map[string]usernamePassword } -func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, grc *git.MirroredGitRepoCollection) *VarsLoader { +func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, grc *git.MirroredGitRepoCollection, aws aws.AwsClientFactory) *VarsLoader { return &VarsLoader{ ctx: ctx, k: k, grc: grc, + aws: aws, credentialsCache: map[string]usernamePassword{}, } } @@ -125,7 +127,11 @@ func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, } func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { - secret, err := aws.GetAwsSecretsManagerSecret(source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) + if v.aws == nil { + return fmt.Errorf("no AWS client factory provided") + } + + secret, err := aws.GetAwsSecretsManagerSecret(v.aws, source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) if err != nil { return err } From 5a9b84ad8fc091a570a97a141ba406edc8786caa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 22 May 2022 16:46:03 +0200 Subject: [PATCH 0205/2268] fix: Fix base64 decooding of vars loaded from k8s secrets --- pkg/vars/vars_loader.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 9900ce36b..6e1310bee 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -2,6 +2,7 @@ package vars import ( "context" + "encoding/base64" "fmt" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -70,10 +71,10 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear return v.loadGit(varsCtx, source.Git, rootKey) } else if source.ClusterConfigMap != nil { ref := k8s2.NewObjectRef("", "v1", "ConfigMap", source.ClusterConfigMap.Name, source.ClusterConfigMap.Namespace) - return v.loadFromK8sObject(varsCtx, ref, source.ClusterConfigMap.Key, rootKey) + return v.loadFromK8sObject(varsCtx, ref, source.ClusterConfigMap.Key, rootKey, false) } else if source.ClusterSecret != nil { ref := k8s2.NewObjectRef("", "v1", "Secret", source.ClusterSecret.Name, source.ClusterSecret.Namespace) - return v.loadFromK8sObject(varsCtx, ref, source.ClusterSecret.Key, rootKey) + return v.loadFromK8sObject(varsCtx, ref, source.ClusterSecret.Key, rootKey, true) } else if source.SystemEnvVars != nil { return v.loadSystemEnvs(varsCtx, &source, rootKey) } else if source.Http != nil { @@ -158,7 +159,7 @@ func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, roo return v.loadFromString(varsCtx, string(file), rootKey) } -func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, ref k8s2.ObjectRef, key string, rootKey string) error { +func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, ref k8s2.ObjectRef, key string, rootKey string, base64Decode bool) error { if v.k == nil { return nil } @@ -168,7 +169,7 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, ref k8s2.ObjectRef, key return err } - value, found, err := o.GetNestedString("data", key) + f, found, err := o.GetNestedField("data", key) if err != nil { return err } @@ -176,6 +177,21 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, ref k8s2.ObjectRef, key return fmt.Errorf("key %s not found in %s on cluster", key, ref.String()) } + var value string + if b, ok := f.([]byte); ok { + value = string(b) + } else if s, ok := f.(string); ok { + if base64Decode { + b, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return err + } + value = string(b) + } else { + value = s + } + } + err = v.loadFromString(varsCtx, value, rootKey) if err != nil { return fmt.Errorf("failed to load vars from kubernetes object %s and key %s: %w", ref.String(), key, err) From 4782041c2094dae9f5bdac4c7055362b140a2b1c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 22 May 2022 16:46:24 +0200 Subject: [PATCH 0206/2268] tests: Implement VarsLoader tests --- pkg/k8s/fake_client_factory.go | 106 +++++++ pkg/vars/aws/fake_clientfactory.go | 39 +++ pkg/vars/vars_loader_test.go | 460 +++++++++++++++++++++++++++++ pkg/vars/vars_test.go | 25 +- 4 files changed, 617 insertions(+), 13 deletions(-) create mode 100644 pkg/k8s/fake_client_factory.go create mode 100644 pkg/vars/aws/fake_clientfactory.go create mode 100644 pkg/vars/vars_loader_test.go diff --git a/pkg/k8s/fake_client_factory.go b/pkg/k8s/fake_client_factory.go new file mode 100644 index 000000000..8af606ad5 --- /dev/null +++ b/pkg/k8s/fake_client_factory.go @@ -0,0 +1,106 @@ +package k8s + +import ( + "github.com/kluctl/kluctl/v2/pkg/utils" + v1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" + fake_dynamic "k8s.io/client-go/dynamic/fake" + "k8s.io/client-go/kubernetes/fake" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/metadata" + metadata_fake "k8s.io/client-go/metadata/fake" + "k8s.io/client-go/rest" + "sigs.k8s.io/kustomize/kyaml/openapi" + "sigs.k8s.io/kustomize/kyaml/yaml" + "strings" +) + +type fakeClientFactory struct { + clientSet *fake.Clientset + objects []runtime.Object + scheme *runtime.Scheme +} + +func (f *fakeClientFactory) GetCA() []byte { + return []byte{} +} + +func (f *fakeClientFactory) CloseIdleConnections() { +} + +func (f *fakeClientFactory) DiscoveryClient() (discovery.DiscoveryInterface, error) { + return f.clientSet.Discovery(), nil +} + +func (f *fakeClientFactory) CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1Interface, error) { + return f.clientSet.CoreV1(), nil +} + +func (f *fakeClientFactory) DynamicClient(wh rest.WarningHandler) (dynamic.Interface, error) { + return fake_dynamic.NewSimpleDynamicClient(f.scheme, f.objects...), nil +} + +func (f *fakeClientFactory) MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) { + return metadata_fake.NewSimpleMetadataClient(f.scheme, f.objects...), nil +} + +func NewFakeClientFactory(objects ...runtime.Object) ClientFactory { + scheme := runtime.NewScheme() + _ = v1.AddToScheme(scheme) + _ = apiextensionsv1.AddToScheme(scheme) + clientSet := fake.NewSimpleClientset(objects...) + + clientSet.Fake.Resources = ConvertSchemeToAPIResources(scheme) + + return &fakeClientFactory{ + clientSet: clientSet, + objects: objects, + scheme: scheme, + } +} + +func ConvertSchemeToAPIResources(s *runtime.Scheme) []*metav1.APIResourceList { + utils.WaitForOpenapiInitDone() + + m := map[schema.GroupVersion][]metav1.APIResource{} + + for gvk, _ := range s.AllKnownTypes() { + // we misuse kyaml here + n, _ := openapi.IsNamespaceScoped(yaml.TypeMeta{ + APIVersion: gvk.GroupVersion().String(), + Kind: gvk.Kind, + }) + + ar := metav1.APIResource{ + Name: buildPluralName(gvk.Kind), + Namespaced: n, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + Verbs: []string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"}, + } + + l, _ := m[gvk.GroupVersion()] + l = append(l, ar) + m[gvk.GroupVersion()] = l + } + + var ret []*metav1.APIResourceList + for gv, arl := range m { + ret = append(ret, &metav1.APIResourceList{ + GroupVersion: gv.String(), + APIResources: arl, + }) + } + + return ret +} + +func buildPluralName(n string) string { + return strings.ToLower(n) + "s" +} diff --git a/pkg/vars/aws/fake_clientfactory.go b/pkg/vars/aws/fake_clientfactory.go new file mode 100644 index 000000000..219dc1259 --- /dev/null +++ b/pkg/vars/aws/fake_clientfactory.go @@ -0,0 +1,39 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/service/secretsmanager" + "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" +) + +type FakeAwsClientFactory struct { + secretsmanageriface.SecretsManagerAPI + + Secrets map[string]string +} + +func (f *FakeAwsClientFactory) GetSecretValue(in *secretsmanager.GetSecretValueInput) (*secretsmanager.GetSecretValueOutput, error) { + name := *in.SecretId + arn, err := ParseArn(*in.SecretId) + if err == nil { + name = arn.Resource + } + + s, ok := f.Secrets[name] + if ok { + return &secretsmanager.GetSecretValueOutput{ + Name: &name, + SecretString: &s, + }, nil + } + + return nil, fmt.Errorf("secret %s not found", *in.SecretId) +} + +func (f *FakeAwsClientFactory) SecretsManagerClient(profile *string, region *string) (secretsmanageriface.SecretsManagerAPI, error) { + return f, nil +} + +func NewFakeClientFactory() *FakeAwsClientFactory { + return &FakeAwsClientFactory{} +} diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go new file mode 100644 index 000000000..763b6c1ae --- /dev/null +++ b/pkg/vars/vars_loader_test.go @@ -0,0 +1,460 @@ +package vars + +import ( + "context" + git2 "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/git/auth" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/vars/aws" + "github.com/stretchr/testify/assert" + "io/ioutil" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "testing" +) + +func newTestDir(t *testing.T) string { + tmp, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + _ = os.RemoveAll(tmp) + }) + return tmp +} + +func newGRC(t *testing.T) *git.MirroredGitRepoCollection { + grc := git.NewMirroredGitRepoCollection(context.TODO(), auth.NewDefaultAuthProviders(), 0) + t.Cleanup(func() { + grc.Clear() + }) + return grc +} + +func testVarsLoader(t *testing.T, test func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory), objects ...runtime.Object) { + k, err := k8s.NewK8sCluster(context.TODO(), k8s.NewFakeClientFactory(objects...), false) + if err != nil { + t.Fatal(err) + } + grc := newGRC(t) + fakeAws := aws.NewFakeClientFactory() + + vl := NewVarsLoader(context.TODO(), k, grc, fakeAws) + vc := NewVarsCtx(newJinja2Must(t)) + + test(vl, vc, fakeAws) +} + +func TestVarsLoader_Values(t *testing.T) { + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + Values: uo.FromStringMust(`{"test1": {"test2": 42}}`), + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + +func TestVarsLoader_File(t *testing.T) { + d := newTestDir(t) + _ = ioutil.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": 42}}`), 0o600) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + File: utils.StrPtr("test.yaml"), + }, []string{d}, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + +func TestVarsLoader_FileWithLoad(t *testing.T) { + d := newTestDir(t) + _ = ioutil.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{ load_template("test2.txt") }}}}`), 0o600) + _ = ioutil.WriteFile(filepath.Join(d, "test2.txt"), []byte(`42`), 0o600) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + File: utils.StrPtr("test.yaml"), + }, []string{d}, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + +func TestVarsLoader_FileWithLoadSubDir(t *testing.T) { + d := newTestDir(t) + _ = os.Mkdir(filepath.Join(d, "subdir"), 0o700) + _ = ioutil.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{ load_template("test2.txt") }}}}`), 0o600) + _ = ioutil.WriteFile(filepath.Join(d, "subdir/test2.txt"), []byte(`42`), 0o600) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + File: utils.StrPtr("test.yaml"), + }, []string{d, filepath.Join(d, "subdir")}, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + +func TestVarsLoader_FileWithLoadNotExists(t *testing.T) { + d := newTestDir(t) + _ = ioutil.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{load_template("test3.txt")}}}}`), 0o600) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + File: utils.StrPtr("test.yaml"), + }, []string{d}, "") + assert.EqualError(t, err, "failed to load vars from test.yaml: template test3.txt not found") + }) +} + +func TestVarsLoader_Git(t *testing.T) { + gs := test_utils.NewGitServer(t) + gs.GitInit("repo") + gs.UpdateYaml("repo", "test.yaml", func(o *uo.UnstructuredObject) error { + *o = *uo.FromStringMust(`{"test1": {"test2": 42}}`) + return nil + }, "") + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + url, _ := git_url.Parse(gs.LocalGitUrl("repo")) + err := vl.LoadVars(vc, &types.VarsSource{ + Git: &types.VarsSourceGit{ + Url: *url, + Path: "test.yaml", + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + +func TestVarsLoader_GitBranch(t *testing.T) { + gs := test_utils.NewGitServer(t) + gs.GitInit("repo") + + wt := gs.GetWorktree("repo") + err := wt.Checkout(&git2.CheckoutOptions{ + Branch: plumbing.NewBranchReferenceName("testbranch"), + Create: true, + }) + assert.NoError(t, err) + + gs.UpdateYaml("repo", "test.yaml", func(o *uo.UnstructuredObject) error { + *o = *uo.FromStringMust(`{"test1": {"test2": 42}}`) + return nil + }, "") + + err = wt.Checkout(&git2.CheckoutOptions{ + Branch: plumbing.Master, + }) + assert.NoError(t, err) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + url, _ := git_url.Parse(gs.LocalGitUrl("repo")) + err = vl.LoadVars(vc, &types.VarsSource{ + Git: &types.VarsSourceGit{ + Url: *url, + Path: "test.yaml", + Ref: "testbranch", + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + +func TestVarsLoader_ClusterConfigMap(t *testing.T) { + cm := corev1.ConfigMap{ + ObjectMeta: v1.ObjectMeta{Name: "cm", Namespace: "ns"}, + Data: map[string]string{ + "vars": `{"test1": {"test2": 42}}`, + }, + } + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + ClusterConfigMap: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "cm", + Namespace: "ns", + Key: "vars", + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }, &cm) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + ClusterConfigMap: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "cm1", + Namespace: "ns", + Key: "vars1", + }, + }, nil, "") + assert.EqualError(t, err, "configmaps \"cm1\" not found") + + err = vl.LoadVars(vc, &types.VarsSource{ + ClusterConfigMap: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "cm", + Namespace: "ns", + Key: "vars1", + }, + }, nil, "") + assert.EqualError(t, err, "key vars1 not found in ns/ConfigMap/cm on cluster") + }, &cm) +} + +func TestVarsLoader_ClusterSecret(t *testing.T) { + secret := corev1.Secret{ + ObjectMeta: v1.ObjectMeta{Name: "s", Namespace: "ns"}, + Data: map[string][]byte{ + "vars": []byte(`{"test1": {"test2": 42}}`), + }, + } + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + ClusterSecret: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "s", + Namespace: "ns", + Key: "vars", + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }, &secret) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + ClusterSecret: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "s1", + Namespace: "ns", + Key: "vars1", + }, + }, nil, "") + assert.EqualError(t, err, "secrets \"s1\" not found") + + err = vl.LoadVars(vc, &types.VarsSource{ + ClusterSecret: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "s", + Namespace: "ns", + Key: "vars1", + }, + }, nil, "") + assert.EqualError(t, err, "key vars1 not found in ns/Secret/s on cluster") + }, &secret) +} + +func TestVarsLoader_SystemEnv(t *testing.T) { + t.Setenv("TEST1", "42") + t.Setenv("TEST2", "43") + t.Setenv("TEST4", "44") + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + SystemEnvVars: uo.FromMap(map[string]interface{}{ + "test1": "TEST1", + "test2": "TEST2", + "test3": map[string]interface{}{ + "test4": "TEST4", + }, + }), + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedString("test1") + assert.Equal(t, "42", v) + + v, _, _ = vc.Vars.GetNestedString("test2") + assert.Equal(t, "43", v) + + v, _, _ = vc.Vars.GetNestedString("test3", "test4") + assert.Equal(t, "44", v) + }) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + SystemEnvVars: uo.FromMap(map[string]interface{}{ + "test5": "TEST5", + }), + }, nil, "") + assert.EqualError(t, err, "environment variable TEST5 not found for test5") + }) +} + +func TestVarsLoader_Http_GET(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(`{"test1": {"test2": 42}}`)) + })) + defer ts.Close() + + u, _ := url.Parse(ts.URL) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + Http: &types.VarsSourceHttp{ + Url: types.YamlUrl{URL: *u}, + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + +func TestVarsLoader_Http_POST(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(542) + return + } + body, _ := ioutil.ReadAll(r.Body) + if string(body) != "body" { + w.WriteHeader(543) + return + } + if r.Header.Get("h") != "h" { + w.WriteHeader(544) + return + } + _, _ = w.Write([]byte(`{"test1": {"test2": 42}}`)) + })) + defer ts.Close() + + u, _ := url.Parse(ts.URL) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + Http: &types.VarsSourceHttp{ + Url: types.YamlUrl{URL: *u}, + }, + }, nil, "") + assert.ErrorContains(t, err, "failed with status code 542") + + err = vl.LoadVars(vc, &types.VarsSource{ + Http: &types.VarsSourceHttp{ + Url: types.YamlUrl{URL: *u}, + Method: utils.StrPtr("POST"), + }, + }, nil, "") + assert.ErrorContains(t, err, "failed with status code 543") + + err = vl.LoadVars(vc, &types.VarsSource{ + Http: &types.VarsSourceHttp{ + Url: types.YamlUrl{URL: *u}, + Method: utils.StrPtr("POST"), + Body: utils.StrPtr("body"), + }, + }, nil, "") + assert.ErrorContains(t, err, "failed with status code 544") + + err = vl.LoadVars(vc, &types.VarsSource{ + Http: &types.VarsSourceHttp{ + Url: types.YamlUrl{URL: *u}, + Method: utils.StrPtr("POST"), + Body: utils.StrPtr("body"), + Headers: map[string]string{"h": "h"}, + }, + }, nil, "") + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + +func TestVarsLoader_Http_JsonPath(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(`{"test1": "{\"test2\": 42}"}`)) + })) + defer ts.Close() + + u, _ := url.Parse(ts.URL) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + Http: &types.VarsSourceHttp{ + Url: types.YamlUrl{URL: *u}, + JsonPath: utils.StrPtr("test1"), + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test2") + assert.Equal(t, int64(42), v) + }) +} + +func TestVarsLoader_AwsSecretsManager(t *testing.T) { + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + aws.Secrets = map[string]string{ + "secret": `{"test1": {"test2": 42}}`, + } + + err := vl.LoadVars(vc, &types.VarsSource{ + AwsSecretsManager: &types.VarsSourceAwsSecretsManager{ + SecretName: "secret", + }, + }, nil, "") + assert.EqualError(t, err, "when omitting the AWS region, the secret name must be a valid ARN") + + err = vl.LoadVars(vc, &types.VarsSource{ + AwsSecretsManager: &types.VarsSourceAwsSecretsManager{ + SecretName: "secret", + Region: utils.StrPtr("eu-central1"), + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + aws.Secrets = map[string]string{ + "secret": `{"test1": {"test2": 42}}`, + } + + err := vl.LoadVars(vc, &types.VarsSource{ + AwsSecretsManager: &types.VarsSourceAwsSecretsManager{ + SecretName: "arn:aws:secretsmanager:eu-central-1:12345:secret:secret", + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} diff --git a/pkg/vars/vars_test.go b/pkg/vars/vars_test.go index b922b7279..66e7bb092 100644 --- a/pkg/vars/vars_test.go +++ b/pkg/vars/vars_test.go @@ -7,12 +7,19 @@ import ( "testing" ) -func TestVarsCtx(t *testing.T) { +func newJinja2Must(t *testing.T) *jinja2.Jinja2 { j2, err := jinja2.NewJinja2() if err != nil { t.Fatal(err) } - defer j2.Close() + t.Cleanup(func() { + j2.Close() + }) + return j2 +} + +func TestVarsCtx(t *testing.T) { + j2 := newJinja2Must(t) varsCtx := NewVarsCtx(j2) varsCtx.Update(uo.FromMap(map[string]interface{}{ @@ -25,11 +32,7 @@ func TestVarsCtx(t *testing.T) { } func TestVarsCtxChild(t *testing.T) { - j2, err := jinja2.NewJinja2() - if err != nil { - t.Fatal(err) - } - defer j2.Close() + j2 := newJinja2Must(t) varsCtx := NewVarsCtx(j2) varsCtx.UpdateChild("child", uo.FromMap(map[string]interface{}{ @@ -42,11 +45,7 @@ func TestVarsCtxChild(t *testing.T) { } func TestVarsCtxStruct(t *testing.T) { - j2, err := jinja2.NewJinja2() - if err != nil { - t.Fatal(err) - } - defer j2.Close() + j2 := newJinja2Must(t) varsCtx := NewVarsCtx(j2) @@ -58,7 +57,7 @@ func TestVarsCtxStruct(t *testing.T) { Test1: struct{ Test2 int }{Test2: 42}, } - err = varsCtx.UpdateChildFromStruct("child", s) + err := varsCtx.UpdateChildFromStruct("child", s) assert.NoError(t, err) v, _, _ := varsCtx.Vars.GetNestedInt("child", "test1", "test2") From b992f22ff89b3226e8648b6e45c30c5707897e9d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 22 May 2022 16:55:40 +0200 Subject: [PATCH 0207/2268] feat: Make helm-values.yml optional This removes the need to provide an empty helm-values.yml in case no values need to be overridden. Fixes #30 --- pkg/deployment/helm_chart.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 2e2727fb8..509773443 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -215,8 +216,10 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { } settings := cli.New() - valueOpts := values.Options{ - ValueFiles: []string{valuesPath}, + valueOpts := values.Options{} + + if utils.Exists(valuesPath) { + valueOpts.ValueFiles = append(valueOpts.ValueFiles, valuesPath) } var kubeVersion *chartutil.KubeVersion From 3b1297d98152983ab8d2d0535dc7315b42af853d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 22 May 2022 17:51:42 +0200 Subject: [PATCH 0208/2268] feat: Load Helm credentials from repositories config --- pkg/deployment/helm_chart.go | 51 +++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 509773443..cc6d43776 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -115,6 +115,17 @@ func (c *helmChart) Pull(ctx context.Context) error { a.DestDir = targetDir a.Version = *c.Config.ChartVersion + s := c.getRepoSettings(a.Settings, *c.Config.Repo) + if s != nil { + a.Username = s.Username + a.Password = s.Password + a.CertFile = s.CertFile + a.CaFile = s.CAFile + a.KeyFile = s.KeyFile + a.InsecureSkipTLSverify = s.InsecureSkipTLSverify + a.PassCredentialsAll = s.PassCredentialsAll + } + var out string if registry.IsOCI(*c.Config.Repo) { out, err = a.Run(*c.Config.Repo) @@ -145,11 +156,14 @@ func (c *helmChart) CheckUpdate() (string, bool, error) { var latestVersion string settings := cli.New() - e := repo.Entry{ - URL: *c.Config.Repo, - Name: chartName, + e := c.getRepoSettings(settings, *c.Config.Repo) + if e == nil { + e = &repo.Entry{ + URL: *c.Config.Repo, + } } - r, err := repo.NewChartRepository(&e, getter.All(settings)) + + r, err := repo.NewChartRepository(e, getter.All(settings)) if err != nil { return "", false, err } @@ -164,9 +178,9 @@ func (c *helmChart) CheckUpdate() (string, bool, error) { return "", false, err } - indexEntry, ok := index.Entries[*c.Config.ChartName] + indexEntry, ok := index.Entries[chartName] if !ok || len(indexEntry) == 0 { - return "", false, fmt.Errorf("helm chart %s not found in repo index", *c.Config.ChartName) + return "", false, fmt.Errorf("helm chart %s not found in repo index", chartName) } var ls versions.LooseVersionSlice @@ -338,6 +352,31 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { return nil } +func (c *helmChart) getRepoSettings(env *cli.EnvSettings, repoUrl string) *repo.Entry { + f, err := repo.LoadFile(env.RepositoryConfig) + if err != nil { + return nil + } + + removeTrailingSlash := func(s string) string { + if len(s) == 0 { + return s + } + if s[len(s)-1] == '/' { + return s[:len(s)-1] + } + return s + } + repoUrl = removeTrailingSlash(repoUrl) + + for _, e := range f.Repositories { + if removeTrailingSlash(e.URL) == repoUrl { + return e + } + } + return nil +} + func checkIfInstallable(ch *chart.Chart) error { switch ch.Metadata.Type { case "", "application": From 3acd4a2871fe00af8484eeee4e1309d68faff3fb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 22 May 2022 17:55:59 +0200 Subject: [PATCH 0209/2268] refactor: No need to use pointers for booleans with default "false" value --- cmd/kluctl/commands/cmd_helm_update.go | 6 ++---- pkg/deployment/deployment_collection.go | 5 ++--- pkg/deployment/deployment_item.go | 9 ++++----- pkg/deployment/helm_chart.go | 2 +- pkg/deployment/utils/apply_utils.go | 4 ++-- pkg/types/deployment.go | 12 ++++++------ pkg/types/helm_chart.go | 4 ++-- 7 files changed, 19 insertions(+), 23 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 59e3df5d9..17dc10c71 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -42,8 +42,6 @@ func (cmd *helmUpdateCmd) Run() error { s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) defer s.Failed() - skipUpdate := chart.Config.SkipUpdate != nil && *chart.Config.SkipUpdate - newVersion, updated, err := chart.CheckUpdate() if err != nil { return err @@ -54,7 +52,7 @@ func (cmd *helmUpdateCmd) Run() error { return nil } msg := fmt.Sprintf("%s: Chart has new version %s available. Old version is %s.", statusPrefix, newVersion, *chart.Config.ChartVersion) - if skipUpdate { + if chart.Config.SkipUpdate { msg += " skipUpdate is set to true." } s.Update(msg) @@ -62,7 +60,7 @@ func (cmd *helmUpdateCmd) Run() error { if !cmd.Upgrade { s.Success() } else { - if skipUpdate { + if chart.Config.SkipUpdate { s.Update("%s: NOT upgrading chart as skipUpdate was set to true", statusPrefix) s.Success() return nil diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 64b100788..03883a86c 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -46,9 +46,8 @@ func NewDeploymentCollection(ctx context.Context, project *DeploymentProject, im } func (c *DeploymentCollection) createBarrierDummy(project *DeploymentProject) *DeploymentItem { - b := true tmpDiConfig := &types.DeploymentItemConfig{ - Barrier: &b, + Barrier: true, } di, err := NewDeploymentItem(project, c, tmpDiConfig, nil, 0) if err != nil { @@ -93,7 +92,7 @@ func (c *DeploymentCollection) collectDeployments(project *DeploymentProject, in return nil, err } ret = append(ret, ret2...) - if diConfig.Barrier != nil && *diConfig.Barrier { + if diConfig.Barrier { ret = append(ret, c.createBarrierDummy(project)) } } else { diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 7051ce9c3..82038c71b 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -101,7 +101,7 @@ func (di *DeploymentItem) getCommonAnnotations() map[string]string { a := map[string]string{ "kluctl.io/kustomize_dir": filepath.ToSlash(di.RelToRootItemDir), } - if di.Config.SkipDeleteIfTags != nil && *di.Config.SkipDeleteIfTags { + if di.Config.SkipDeleteIfTags { a["kluctl.io/skip-delete-if-tags"] = "true" } return a @@ -267,10 +267,10 @@ func (di *DeploymentItem) CheckInclusionForDeploy() bool { if di.Inclusion == nil { return true } - if di.Config.OnlyRender != nil && *di.Config.OnlyRender { + if di.Config.OnlyRender { return true } - if di.Config.AlwaysDeploy != nil && *di.Config.AlwaysDeploy { + if di.Config.AlwaysDeploy { return true } values := di.buildInclusionEntries() @@ -281,9 +281,8 @@ func (di *DeploymentItem) checkInclusionForDelete() bool { if di.Inclusion == nil { return true } - skipDeleteIfTags := di.Config.SkipDeleteIfTags != nil && *di.Config.SkipDeleteIfTags values := di.buildInclusionEntries() - return di.Inclusion.CheckIncluded(values, skipDeleteIfTags) + return di.Inclusion.CheckIncluded(values, di.Config.SkipDeleteIfTags) } func (di *DeploymentItem) prepareKustomizationYaml() error { diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index cc6d43776..93ba7e85a 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -257,7 +257,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { client.ClientOnly = true client.KubeVersion = kubeVersion - if c.Config.SkipCRDs != nil && *c.Config.SkipCRDs { + if c.Config.SkipCRDs { client.SkipCRDs = true } else { client.IncludeCRDs = true diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index ff10287a5..46b246351 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -480,7 +480,7 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { didLog = true } - waitReadiness := (d.Config.WaitReadiness != nil && *d.Config.WaitReadiness) || d.WaitReadiness || utils.ParseBoolOrFalse(o.GetK8sAnnotation("kluctl.io/wait-readiness")) + waitReadiness := d.Config.WaitReadiness || d.WaitReadiness || utils.ParseBoolOrFalse(o.GetK8sAnnotation("kluctl.io/wait-readiness")) if !a.o.NoWait && waitReadiness { a.WaitReadiness(o.GetK8sRef(), 0) } @@ -577,7 +577,7 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { sctx.Failed() }() - barrier := (d.Config.Barrier != nil && *d.Config.Barrier) || d.Barrier + barrier := d.Config.Barrier || d.Barrier if barrier { sctx := status.StartWithOptions(a.ctx, status.WithStatus("Waiting on barrier..."), status.WithTotal(1)) wg.Wait() diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index b129aa8b8..b9db6bb96 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -9,12 +9,12 @@ type DeploymentItemConfig struct { Path *string `yaml:"path,omitempty"` Include *string `yaml:"include,omitempty"` Tags []string `yaml:"tags,omitempty"` - Barrier *bool `yaml:"barrier,omitempty"` - WaitReadiness *bool `yaml:"waitReadiness,omitempty"` + Barrier bool `yaml:"barrier,omitempty"` + WaitReadiness bool `yaml:"waitReadiness,omitempty"` Vars []*VarsSource `yaml:"vars,omitempty"` - SkipDeleteIfTags *bool `yaml:"skipDeleteIfTags,omitempty"` - OnlyRender *bool `yaml:"onlyRender,omitempty"` - AlwaysDeploy *bool `yaml:"alwaysDeploy,omitempty"` + SkipDeleteIfTags bool `yaml:"skipDeleteIfTags,omitempty"` + OnlyRender bool `yaml:"onlyRender,omitempty"` + AlwaysDeploy bool `yaml:"alwaysDeploy,omitempty"` DeleteObjects []DeleteObjectItemConfig `yaml:"deleteObjects,omitempty"` } @@ -23,7 +23,7 @@ func ValidateDeploymentItemConfig(sl validator.StructLevel) { if s.Path != nil && s.Include != nil { sl.ReportError(s, "path", "Path", "path and include can not be set at the same time", "") } - if s.Path == nil && s.WaitReadiness != nil { + if s.Path == nil && s.WaitReadiness { sl.ReportError(s, "waitReadiness", "WaitReadiness", "only kustomize deployments are allowed to have waitReadiness set", "") } } diff --git a/pkg/types/helm_chart.go b/pkg/types/helm_chart.go index c8e0e91f3..88a960a12 100644 --- a/pkg/types/helm_chart.go +++ b/pkg/types/helm_chart.go @@ -7,8 +7,8 @@ type HelmChartConfig2 struct { ReleaseName string `yaml:"releaseName" validate:"required"` Namespace *string `yaml:"namespace,omitempty"` Output string `yaml:"output" validate:"required"` - SkipCRDs *bool `yaml:"skipCRDs,omitempty"` - SkipUpdate *bool `yaml:"skipUpdate,omitempty"` + SkipCRDs bool `yaml:"skipCRDs,omitempty"` + SkipUpdate bool `yaml:"skipUpdate,omitempty"` } type HelmChartConfig struct { From 754bae1bd4bf309ebb75ec4cc70b22c108b4d55c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 09:29:52 +0200 Subject: [PATCH 0210/2268] feat: Allow to specify helm repository credentials via CLI Fixes: #31 --- cmd/kluctl/args/helm_credentials.go | 81 ++++++++++++++++++++++++++ cmd/kluctl/commands/cmd_helm_pull.go | 19 ++++++ cmd/kluctl/commands/cmd_helm_update.go | 18 +++++- pkg/deployment/helm_chart.go | 49 +++++----------- pkg/types/helm_chart.go | 17 +++--- 5 files changed, 138 insertions(+), 46 deletions(-) create mode 100644 cmd/kluctl/args/helm_credentials.go diff --git a/cmd/kluctl/args/helm_credentials.go b/cmd/kluctl/args/helm_credentials.go new file mode 100644 index 000000000..23db78c6d --- /dev/null +++ b/cmd/kluctl/args/helm_credentials.go @@ -0,0 +1,81 @@ +package args + +import ( + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/repo" + "strings" +) + +type HelmCredentials struct { + Username []string `group:"misc" help:"Specify username to use for Helm Repository authentication. Must be in the form --username=:, where must match the id specified in the helm-chart.yaml."` + Password []string `group:"misc" help:"Specify password to use for Helm Repository authentication. Must be in the form --password=:, where must match the id specified in the helm-chart.yaml."` + KeyFile []string `group:"misc" help:"Specify client certificate to use for Helm Repository authentication. Must be in the form --key-file=:, where must match the id specified in the helm-chart.yaml."` + InsecureSkipTlsVerify []string `group:"misc" help:"Controls skipping of TLS verification. Must be in the form --insecure-skip-tls-verify=, where must match the id specified in the helm-chart.yaml."` +} + +func (c *HelmCredentials) FindCredentials(repoUrl string, credentialsId *string) *repo.Entry { + if credentialsId != nil { + splitIdAndValue := func(s string) (string, bool) { + x := strings.SplitN(s, ":", 2) + if len(x) < 0 { + return "", false + } + if x[0] != *credentialsId { + return "", false + } + return x[1], true + } + + var e repo.Entry + for _, x := range c.Username { + if v, ok := splitIdAndValue(x); ok { + e.Username = v + } + } + for _, x := range c.Password { + if v, ok := splitIdAndValue(x); ok { + e.Password = v + } + } + for _, x := range c.KeyFile { + if v, ok := splitIdAndValue(x); ok { + e.KeyFile = v + } + } + for _, x := range c.InsecureSkipTlsVerify { + if x == *credentialsId { + e.InsecureSkipTLSverify = true + } + } + + if e != (repo.Entry{}) { + return &e + } + } + + env := cli.New() + + f, err := repo.LoadFile(env.RepositoryConfig) + if err != nil { + return nil + } + + removeTrailingSlash := func(s string) string { + if len(s) == 0 { + return s + } + if s[len(s)-1] == '/' { + return s[:len(s)-1] + } + return s + } + repoUrl = removeTrailingSlash(repoUrl) + + for _, e := range f.Repositories { + if removeTrailingSlash(e.URL) == repoUrl { + return e + } + } + + return nil +} diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 5739b54ed..d727ee594 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -1,6 +1,8 @@ package commands import ( + "fmt" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/status" "io/fs" @@ -8,6 +10,8 @@ import ( ) type helmPullCmd struct { + args.HelmCredentials + LocalDeployment string `group:"project" help:"Local deployment directory. Defaults to current directory"` } @@ -22,6 +26,7 @@ func (cmd *helmPullCmd) Run() error { if cmd.LocalDeployment != "" { rootPath = cmd.LocalDeployment } + err := filepath.WalkDir(rootPath, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { @@ -31,6 +36,15 @@ func (cmd *helmPullCmd) Run() error { s.FailedWithMessage(err.Error()) return err } + + creds := cmd.HelmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) + if chart.Config.CredentialsId != nil && creds == nil { + err := fmt.Errorf("no credentials provided for %s", p) + s.FailedWithMessage(err.Error()) + return err + } + chart.SetCredentials(creds) + err = chart.Pull(cliCtx) if err != nil { s.FailedWithMessage(err.Error()) @@ -40,5 +54,10 @@ func (cmd *helmPullCmd) Run() error { } return nil }) + + if err != nil { + return fmt.Errorf("command failed") + } + return err } diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 17dc10c71..fb6b35972 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -3,6 +3,7 @@ package commands import ( "fmt" "github.com/go-git/go-git/v5" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/status" @@ -11,6 +12,8 @@ import ( ) type helmUpdateCmd struct { + args.HelmCredentials + LocalDeployment string `group:"project" help:"Local deployment directory. Defaults to current directory"` Upgrade bool `group:"misc" help:"Write new versions into helm-chart.yml and perform helm-pull afterwards"` Commit bool `group:"misc" help:"Create a git commit for every updated chart"` @@ -33,14 +36,23 @@ func (cmd *helmUpdateCmd) Run() error { err = filepath.WalkDir(rootPath, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { + statusPrefix := filepath.Base(filepath.Dir(p)) + s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) + defer s.Failed() + chart, err := deployment.NewHelmChart(p) if err != nil { + s.Update("%s: Error while loading helm-chart.yaml: %v", statusPrefix, err) return err } - statusPrefix := filepath.Base(filepath.Dir(p)) - s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) - defer s.Failed() + creds := cmd.HelmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) + if chart.Config.CredentialsId != nil && creds == nil { + err := fmt.Errorf("%s: No credentials provided", statusPrefix) + s.FailedWithMessage(err.Error()) + return err + } + chart.SetCredentials(creds) newVersion, updated, err := chart.CheckUpdate() if err != nil { diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 93ba7e85a..7c42f2f1b 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -32,8 +32,9 @@ import ( ) type helmChart struct { - configFile string - Config *types.HelmChartConfig + configFile string + Config *types.HelmChartConfig + credentials *repo.Entry } func NewHelmChart(configFile string) (*helmChart, error) { @@ -115,15 +116,14 @@ func (c *helmChart) Pull(ctx context.Context) error { a.DestDir = targetDir a.Version = *c.Config.ChartVersion - s := c.getRepoSettings(a.Settings, *c.Config.Repo) - if s != nil { - a.Username = s.Username - a.Password = s.Password - a.CertFile = s.CertFile - a.CaFile = s.CAFile - a.KeyFile = s.KeyFile - a.InsecureSkipTLSverify = s.InsecureSkipTLSverify - a.PassCredentialsAll = s.PassCredentialsAll + if c.credentials != nil { + a.Username = c.credentials.Username + a.Password = c.credentials.Password + a.CertFile = c.credentials.CertFile + a.CaFile = c.credentials.CAFile + a.KeyFile = c.credentials.KeyFile + a.InsecureSkipTLSverify = c.credentials.InsecureSkipTLSverify + a.PassCredentialsAll = c.credentials.PassCredentialsAll } var out string @@ -156,7 +156,7 @@ func (c *helmChart) CheckUpdate() (string, bool, error) { var latestVersion string settings := cli.New() - e := c.getRepoSettings(settings, *c.Config.Repo) + e := c.credentials if e == nil { e = &repo.Entry{ URL: *c.Config.Repo, @@ -352,29 +352,8 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { return nil } -func (c *helmChart) getRepoSettings(env *cli.EnvSettings, repoUrl string) *repo.Entry { - f, err := repo.LoadFile(env.RepositoryConfig) - if err != nil { - return nil - } - - removeTrailingSlash := func(s string) string { - if len(s) == 0 { - return s - } - if s[len(s)-1] == '/' { - return s[:len(s)-1] - } - return s - } - repoUrl = removeTrailingSlash(repoUrl) - - for _, e := range f.Repositories { - if removeTrailingSlash(e.URL) == repoUrl { - return e - } - } - return nil +func (c *helmChart) SetCredentials(credentials *repo.Entry) { + c.credentials = credentials } func checkIfInstallable(ch *chart.Chart) error { diff --git a/pkg/types/helm_chart.go b/pkg/types/helm_chart.go index 88a960a12..3b78cd353 100644 --- a/pkg/types/helm_chart.go +++ b/pkg/types/helm_chart.go @@ -1,14 +1,15 @@ package types type HelmChartConfig2 struct { - Repo *string `yaml:"repo" validate:"required"` - ChartName *string `yaml:"chartName,omitempty"` - ChartVersion *string `yaml:"chartVersion" validate:"required"` - ReleaseName string `yaml:"releaseName" validate:"required"` - Namespace *string `yaml:"namespace,omitempty"` - Output string `yaml:"output" validate:"required"` - SkipCRDs bool `yaml:"skipCRDs,omitempty"` - SkipUpdate bool `yaml:"skipUpdate,omitempty"` + Repo *string `yaml:"repo" validate:"required"` + CredentialsId *string `yaml:"credentialsId,omitempty"` + ChartName *string `yaml:"chartName,omitempty"` + ChartVersion *string `yaml:"chartVersion" validate:"required"` + ReleaseName string `yaml:"releaseName" validate:"required"` + Namespace *string `yaml:"namespace,omitempty"` + Output string `yaml:"output" validate:"required"` + SkipCRDs bool `yaml:"skipCRDs,omitempty"` + SkipUpdate bool `yaml:"skipUpdate,omitempty"` } type HelmChartConfig struct { From 660ccf8e295bd707a5134636afd99f5f0c5d64e0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 09:30:45 +0200 Subject: [PATCH 0211/2268] fix: Don't let cobra print errors (rely on status reporting instead) --- cmd/kluctl/commands/root.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 56f91a2d6..f157d961e 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -184,6 +184,7 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif rootCmd.Version = version.GetVersion() rootCmd.SilenceUsage = true + rootCmd.SilenceErrors = true rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { err := copyViperValuesToCobraCmd(cmd) From 60f85b3e057d4401f96c984404fc271b958e2f9b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 10:25:18 +0200 Subject: [PATCH 0212/2268] docs: Replace .yml with .yaml in help strings --- cmd/kluctl/args/images.go | 2 +- cmd/kluctl/args/project.go | 12 ++++++------ cmd/kluctl/commands/cmd_delete.go | 2 +- cmd/kluctl/commands/cmd_helm_pull.go | 2 +- cmd/kluctl/commands/cmd_helm_update.go | 2 +- cmd/kluctl/commands/cmd_prune.go | 2 +- cmd/kluctl/commands/root.go | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index f11fba0a4..13aaf244d 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -9,7 +9,7 @@ import ( type ImageFlags struct { FixedImage []string `group:"images" short:"F" help:"Pin an image to a given version. Expects '--fixed-image=image<:namespace:deployment:container>=result'"` - FixedImagesFile existingFileType `group:"images" help:"Use .yml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format" exts:"yml,yaml"` + FixedImagesFile existingFileType `group:"images" help:"Use .yaml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format" exts:"yml,yaml"` UpdateImages bool `group:"images" short:"u" help:"This causes kluctl to prefer the latest image found in registries, based on the 'latest_image' filters provided to 'images.get_image(...)' calls. Use this flag if you want to update to the latest versions/tags of all images. '-u' takes precedence over '--fixed-image/--fixed-images-file', meaning that the latest images are used even if an older image is given via fixed images."` OfflineImages bool `group:"images" help:"Omit contacting image registries and do not query for latest image tags."` } diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index ab9da298f..c7479b869 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -6,11 +6,11 @@ type ProjectFlags struct { ProjectUrl string `group:"project" short:"p" help:"Git url of the kluctl project. If not specified, the current directory will be used instead of a remote Git project"` ProjectRef string `group:"project" short:"b" help:"Git ref of the kluctl project. Only used when --project-url was given."` - ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yml config file. Defaults to $PROJECT/.kluctl.yml" exts:"yml,yaml"` - LocalClusters existingDirType `group:"project" help:"Local clusters directory. Overrides the project from .kluctl.yml"` - LocalDeployment existingDirType `group:"project" help:"Local deployment directory. Overrides the project from .kluctl.yml"` - LocalSealedSecrets existingDirType `group:"project" help:"Local sealed-secrets directory. Overrides the project from .kluctl.yml" ` - FromArchive existingPathType `group:"project" help:"Load project (.kluctl.yml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." exts:"tar.gz,tgz"` + ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` + LocalClusters existingDirType `group:"project" help:"Local clusters directory. Overrides the project from .kluctl.yaml"` + LocalDeployment existingDirType `group:"project" help:"Local deployment directory. Overrides the project from .kluctl.yaml"` + LocalSealedSecrets existingDirType `group:"project" help:"Local sealed-secrets directory. Overrides the project from .kluctl.yaml" ` + FromArchive existingPathType `group:"project" help:"Load project (.kluctl.yaml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." exts:"tar.gz,tgz"` FromArchiveMetadata existingFileType `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." exts:"yml,yaml"` OutputMetadata pathType `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` Cluster string `group:"project" help:"Specify/Override cluster"` @@ -24,5 +24,5 @@ type ArgsFlags struct { } type TargetFlags struct { - Target string `group:"project" short:"t" help:"Target name to run command for. Target must exist in .kluctl.yml."` + Target string `group:"project" short:"t" help:"Target name to run command for. Target must exist in .kluctl.yaml."` } diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 2cd7607fa..4bb602290 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -28,7 +28,7 @@ type deleteCmd struct { } func (cmd *deleteCmd) Help() string { - return `Objects are located based on 'commonLabels'', configured in 'deployment.yml' + return `Objects are located based on 'commonLabels', configured in 'deployment.yaml' WARNING: This command will also delete objects which are not part of your deployment project (anymore). It really only decides based on the 'deleteByLabel' labels and does NOT diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index d727ee594..90d8c9fc8 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -17,7 +17,7 @@ type helmPullCmd struct { func (cmd *helmPullCmd) Help() string { return `The Helm charts are stored under the sub-directory 'charts/' next to the -'helm-chart.yml'. These Helm charts are meant to be added to version control so that +'helm-chart.yaml'. These Helm charts are meant to be added to version control so that pulling is only needed when really required (e.g. when the chart version changes).` } diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index fb6b35972..55c30b8e8 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -15,7 +15,7 @@ type helmUpdateCmd struct { args.HelmCredentials LocalDeployment string `group:"project" help:"Local deployment directory. Defaults to current directory"` - Upgrade bool `group:"misc" help:"Write new versions into helm-chart.yml and perform helm-pull afterwards"` + Upgrade bool `group:"misc" help:"Write new versions into helm-chart.yaml and perform helm-pull afterwards"` Commit bool `group:"misc" help:"Create a git commit for every updated chart"` } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 602d3f3a2..14d4d8790 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -21,7 +21,7 @@ type pruneCmd struct { func (cmd *pruneCmd) Help() string { return `"Searching works by: - 1. Search the cluster for all objects match 'commonLabels', as configured in 'deployment.yml'' + 1. Search the cluster for all objects match 'commonLabels', as configured in 'deployment.yaml' 2. Render the local target and list all objects. 3. Remove all objects from the list of 1. that are part of the list in 2.` } diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index f157d961e..9c8e70711 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -48,8 +48,8 @@ type cli struct { Deploy deployCmd `cmd:"" help:"Deploys a target to the corresponding cluster"` Diff diffCmd `cmd:"" help:"Perform a diff between the locally rendered target and the already deployed target"` Downscale downscaleCmd `cmd:"" help:"Downscale all deployments"` - HelmPull helmPullCmd `cmd:"" help:"Recursively searches for 'helm-chart.yml' files and pulls the specified Helm charts"` - HelmUpdate helmUpdateCmd `cmd:"" help:"Recursively searches for 'helm-chart.yml'' files and checks for new available versions"` + HelmPull helmPullCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and pulls the specified Helm charts"` + HelmUpdate helmUpdateCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and checks for new available versions"` ListImages listImagesCmd `cmd:"" help:"Renders the target and outputs all images used via 'images.get_image(...)"` ListTargets listTargetsCmd `cmd:"" help:"Outputs a yaml list with all target, including dynamic targets"` PokeImages pokeImagesCmd `cmd:"" help:"Replace all images in target"` @@ -99,7 +99,7 @@ func (c *cli) checkNewVersion() { return } - versionCheckPath := filepath.Join(utils.GetTmpBaseDir(), "version_check.yml") + versionCheckPath := filepath.Join(utils.GetTmpBaseDir(), "version_check.yaml") var versionCheckState VersionCheckState err := yaml.ReadYamlFile(versionCheckPath, &versionCheckState) if err == nil { From 912ca491b55fe7637fa8827fad6f591feaa5ff74 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 10:25:50 +0200 Subject: [PATCH 0213/2268] feat: Make helm-chart.yaml output field optional Fixes: #33 --- pkg/deployment/helm_chart.go | 6 +++++- pkg/types/helm_chart.go | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 7c42f2f1b..6d48b661c 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -79,7 +79,11 @@ func (c *helmChart) GetChartDir() (string, error) { func (c *helmChart) GetOutputPath() (string, error) { dir := filepath.Dir(c.configFile) - return securejoin.SecureJoin(dir, c.Config.Output) + output := "helm-rendered.yaml" + if c.Config.Output != nil { + output = *c.Config.Output + } + return securejoin.SecureJoin(dir, output) } func (c *helmChart) buildHelmConfig() (*action.Configuration, error) { diff --git a/pkg/types/helm_chart.go b/pkg/types/helm_chart.go index 3b78cd353..d5e9e4445 100644 --- a/pkg/types/helm_chart.go +++ b/pkg/types/helm_chart.go @@ -7,7 +7,7 @@ type HelmChartConfig2 struct { ChartVersion *string `yaml:"chartVersion" validate:"required"` ReleaseName string `yaml:"releaseName" validate:"required"` Namespace *string `yaml:"namespace,omitempty"` - Output string `yaml:"output" validate:"required"` + Output *string `yaml:"output,omitempty"` SkipCRDs bool `yaml:"skipCRDs,omitempty"` SkipUpdate bool `yaml:"skipUpdate,omitempty"` } From 545187732c6197346f84f7852f8ad6b1f6ec366c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 10:42:20 +0200 Subject: [PATCH 0214/2268] feat: Show error when output file from helm-chart.yaml is not included --- pkg/deployment/deployment_item.go | 45 +++++++++++++++++++++++++------ pkg/deployment/helm_chart.go | 12 ++++++--- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 82038c71b..783084673 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -175,6 +175,22 @@ func (di *DeploymentItem) renderHelmCharts(k *k8s.K8sCluster, wp *utils.WorkerPo if err != nil { return err } + + ky, err := di.readKustoimizationYaml() + if err == nil && ky != nil { + resources, _, _ := ky.GetNestedStringList("resources") + found := false + for _, r := range resources { + if r == chart.GetOutputPath() { + found = true + break + } + } + if !found { + return fmt.Errorf("%s/kustomization.yaml does not include the rendered helm chart: %s", di.relRenderedDir, chart.GetOutputPath()) + } + } + return chart.Render(di.Project.ctx, k) }) return nil @@ -285,20 +301,37 @@ func (di *DeploymentItem) checkInclusionForDelete() bool { return di.Inclusion.CheckIncluded(values, di.Config.SkipDeleteIfTags) } -func (di *DeploymentItem) prepareKustomizationYaml() error { +func (di *DeploymentItem) readKustoimizationYaml() (*uo.UnstructuredObject, error) { if di.dir == nil { - return nil + return nil, nil } kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.renderedDir, "kustomization.yml")) if !utils.IsFile(kustomizeYamlPath) { - return nil + return nil, nil } ky, err := uo.FromFile(kustomizeYamlPath) + if err != nil { + return nil, err + } + + return ky, err +} + +func (di *DeploymentItem) writeKustomizationYaml(ky *uo.UnstructuredObject) error { + kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.renderedDir, "kustomization.yml")) + return yaml.WriteYamlFile(kustomizeYamlPath, ky) +} + +func (di *DeploymentItem) prepareKustomizationYaml() error { + ky, err := di.readKustoimizationYaml() if err != nil { return err } + if ky == nil { + return nil + } overrideNamespace := di.Project.getOverrideNamespace() if overrideNamespace != nil { @@ -315,11 +348,7 @@ func (di *DeploymentItem) prepareKustomizationYaml() error { di.WaitReadiness = utils.ParseBoolOrFalse(ky.GetK8sAnnotation("kluctl.io/wait-readiness")) // Save modified kustomize.yml - err = yaml.WriteYamlFile(kustomizeYamlPath, ky) - if err != nil { - return err - } - return nil + return di.writeKustomizationYaml(ky) } func (di *DeploymentItem) buildKustomize() error { diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 6d48b661c..b41313276 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -77,13 +77,17 @@ func (c *helmChart) GetChartDir() (string, error) { return securejoin.SecureJoin(targetDir, chartName) } -func (c *helmChart) GetOutputPath() (string, error) { - dir := filepath.Dir(c.configFile) +func (c *helmChart) GetOutputPath() string { output := "helm-rendered.yaml" if c.Config.Output != nil { output = *c.Config.Output } - return securejoin.SecureJoin(dir, output) + return output +} + +func (c *helmChart) GetFullOutputPath() (string, error) { + dir := filepath.Dir(c.configFile) + return securejoin.SecureJoin(dir, c.GetOutputPath()) } func (c *helmChart) buildHelmConfig() (*action.Configuration, error) { @@ -215,7 +219,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { if err != nil { return err } - outputPath, err := c.GetOutputPath() + outputPath, err := c.GetFullOutputPath() if err != nil { return err } From 76a47b0fc19dcef3c5d5ca4fc077aef69feaa1ed Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 10:42:47 +0200 Subject: [PATCH 0215/2268] fix: Show cluster config warning only once --- pkg/kluctl_project/project.go | 3 +++ pkg/kluctl_project/target_context.go | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 98a460568..bb59ce9b2 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -6,6 +6,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/jinja2" types2 "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" "regexp" ) @@ -31,6 +32,8 @@ type LoadedKluctlProject struct { J2 *jinja2.Jinja2 GRC *git.MirroredGitRepoCollection + + warnOnce utils.OnceByKey } func (c *LoadedKluctlProject) GetMetadata() *types2.ProjectMetadata { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 400d9a118..d42151d32 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -191,7 +191,9 @@ func (p *LoadedKluctlProject) LoadClusterConfig(clusterName *string, contextName var clusterConfig *types.ClusterConfig if clusterName != nil { - status.Warning(p.ctx, "Cluster configurations have been deprecated and support for them will be removed in a future kluctl release.") + p.warnOnce.Do("cluster-config", func() { + status.Warning(p.ctx, "Cluster configurations have been deprecated and support for them will be removed in a future kluctl release.") + }) clusterConfig, err = types.LoadClusterConfig(p.ClustersDir, *clusterName) if err != nil { From 22be586f40e10538f96f82601da232faf327f7a6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 10:51:44 +0200 Subject: [PATCH 0216/2268] build: Upgrade kustomize to 4.5.5 --- go.mod | 7 ++++--- go.sum | 11 ++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 8ba0c8880..81f4d83f7 100644 --- a/go.mod +++ b/go.mod @@ -48,8 +48,8 @@ require ( k8s.io/client-go v0.24.0-rc.1 k8s.io/klog/v2 v2.60.1 sigs.k8s.io/kind v0.12.0 - sigs.k8s.io/kustomize/api v0.11.4 - sigs.k8s.io/kustomize/kyaml v0.13.6 + sigs.k8s.io/kustomize/api v0.11.5 + sigs.k8s.io/kustomize/kyaml v0.13.7 sigs.k8s.io/structured-merge-diff/v4 v4.2.1 ) @@ -112,6 +112,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect @@ -190,7 +191,7 @@ require ( k8s.io/apiserver v0.23.6 // indirect k8s.io/cli-runtime v0.24.0-rc.1 // indirect k8s.io/component-base v0.24.0-rc.1 // indirect - k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect + k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 // indirect k8s.io/kubectl v0.23.5 // indirect k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect oras.land/oras-go v1.1.1 // indirect diff --git a/go.sum b/go.sum index fe9ab20ed..f79fe67a1 100644 --- a/go.sum +++ b/go.sum @@ -2025,8 +2025,9 @@ k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 h1:nqYOUleKLC/0P1zbU29F5q6aoezM6MOAVz+iyfQbZ5M= +k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= k8s.io/kubectl v0.23.5 h1:DmDULqCaF4qstj0Im143XmncvqWtJxHzK8IrW2BzlU0= k8s.io/kubectl v0.23.5/go.mod h1:lLgw7cVY8xbd7o637vOXPca/w6HC205KsPCRDYRCxwE= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= @@ -2053,13 +2054,13 @@ sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz sigs.k8s.io/kind v0.12.0 h1:LFynXwQkH1MrWI8pM1FQty0oUwEKjU5EkMaVZaPld8E= sigs.k8s.io/kind v0.12.0/go.mod h1:EcgDSBVxz8Bvm19fx8xkioFrf9dC30fMJdOTXBSGNoM= sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8= -sigs.k8s.io/kustomize/api v0.11.4 h1:/0Mr3kfBBNcNPOW5Qwk/3eb8zkswCwnqQxxKtmrTkRo= -sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= +sigs.k8s.io/kustomize/api v0.11.5 h1:vLDp++YAX7iy2y2CVPJNy9pk9CY8XaUKgHkjbVtnWag= +sigs.k8s.io/kustomize/api v0.11.5/go.mod h1:2UDpxS6AonWXow2ZbySd4AjUxmdXLeTlvGBC46uSiq8= sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ= sigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io= sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E= -sigs.k8s.io/kustomize/kyaml v0.13.6 h1:eF+wsn4J7GOAXlvajv6OknSunxpcOBQQqsnPxObtkGs= -sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= +sigs.k8s.io/kustomize/kyaml v0.13.7 h1:/EZ/nPaLUzeJKF/BuJ4QCuMVJWiEVoI8iftOHY3g3tk= +sigs.k8s.io/kustomize/kyaml v0.13.7/go.mod h1:6K+IUOuir3Y7nucPRAjw9yth04KSWBnP5pqUTGwj/qU= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= From 3eb682a11f9bc81d1cfac8d86b9b2a9a888ed323 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 11:01:11 +0200 Subject: [PATCH 0217/2268] build: Update all dependencies --- go.mod | 94 +++--- go.sum | 1027 +++++++++++++++++++++++--------------------------------- 2 files changed, 467 insertions(+), 654 deletions(-) diff --git a/go.mod b/go.mod index 81f4d83f7..f9447d54d 100644 --- a/go.mod +++ b/go.mod @@ -5,68 +5,60 @@ go 1.18 require ( github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e github.com/Masterminds/semver/v3 v3.1.1 - github.com/aws/aws-sdk-go v1.44.1 + github.com/aws/aws-sdk-go v1.44.19 github.com/bitnami-labs/sealed-secrets v0.17.5 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/evanphx/json-patch v5.6.0+incompatible github.com/gammazero/workerpool v1.1.2 github.com/go-git/go-git/v5 v5.4.2 - github.com/go-playground/validator/v10 v10.10.1 + github.com/go-playground/validator/v10 v10.11.0 github.com/gobwas/glob v0.2.3 github.com/goccy/go-yaml v1.9.5 github.com/golang-jwt/jwt/v4 v4.4.1 - github.com/google/go-containerregistry v0.8.0 + github.com/google/go-containerregistry v0.9.0 github.com/hexops/gotextdiff v1.0.3 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/klauspost/compress v1.15.2 + github.com/klauspost/compress v1.15.4 github.com/mattn/go-isatty v0.0.14 github.com/mitchellh/go-ps v1.0.0 github.com/ohler55/ojg v1.14.0 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 - github.com/rogpeppe/go-internal v1.8.0 + github.com/rogpeppe/go-internal v1.8.1 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.10.0 + github.com/spf13/viper v1.11.0 github.com/stretchr/testify v1.7.1 github.com/vbauerster/mpb/v7 v7.4.1 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.1 - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 + golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 + golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 golang.org/x/text v0.3.7 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b - helm.sh/helm/v3 v3.8.2 - k8s.io/api v0.24.0-rc.1 - k8s.io/apiextensions-apiserver v0.23.6 - k8s.io/apimachinery v0.24.0-rc.1 - k8s.io/client-go v0.24.0-rc.1 + gopkg.in/yaml.v3 v3.0.0 + helm.sh/helm/v3 v3.9.0 + k8s.io/api v0.24.1-rc.0 + k8s.io/apiextensions-apiserver v0.24.0 + k8s.io/apimachinery v0.24.1-rc.0 + k8s.io/client-go v0.24.1-rc.0 k8s.io/klog/v2 v2.60.1 - sigs.k8s.io/kind v0.12.0 + sigs.k8s.io/kind v0.14.0 sigs.k8s.io/kustomize/api v0.11.5 sigs.k8s.io/kustomize/kyaml v0.13.7 sigs.k8s.io/structured-merge-diff/v4 v4.2.1 ) -replace ( - k8s.io/api => k8s.io/api v0.23.5 - k8s.io/apimachinery => k8s.io/apimachinery v0.23.5 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.23.5 - k8s.io/client-go => k8s.io/client-go v0.23.5 - k8s.io/component-base => k8s.io/component-base v0.23.5 -) - require ( cloud.google.com/go/compute v1.6.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.27 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect @@ -74,9 +66,9 @@ require ( github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect - github.com/Masterminds/squirrel v1.5.2 // indirect + github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.3 // indirect @@ -85,19 +77,20 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect - github.com/containerd/containerd v1.6.3 // indirect + github.com/containerd/containerd v1.6.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.14+incompatible // indirect - github.com/docker/docker v20.10.14+incompatible // indirect + github.com/docker/cli v20.10.16+incompatible // indirect + github.com/docker/docker v20.10.16+incompatible // indirect github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.4.0 // indirect + github.com/emicklei/go-restful v2.9.5+incompatible // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/gammazero/deque v0.1.1 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect @@ -112,12 +105,11 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/gnostic v0.5.5 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect @@ -133,9 +125,9 @@ require ( github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.1 // indirect - github.com/lib/pq v1.10.5 // indirect + github.com/lib/pq v1.10.6 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/magiconair/properties v1.8.5 // indirect + github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect @@ -143,7 +135,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -152,12 +144,14 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect + github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.1 // indirect + github.com/prometheus/client_golang v1.12.2 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.34.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect @@ -166,8 +160,8 @@ require ( github.com/russross/blackfriday v1.6.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect - github.com/spf13/afero v1.6.0 // indirect - github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/afero v1.8.2 // indirect + github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect @@ -176,23 +170,23 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect + golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect - golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect + golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 // indirect - google.golang.org/grpc v1.46.0 // indirect + google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect + google.golang.org/grpc v1.46.2 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiserver v0.23.6 // indirect - k8s.io/cli-runtime v0.24.0-rc.1 // indirect - k8s.io/component-base v0.24.0-rc.1 // indirect - k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 // indirect - k8s.io/kubectl v0.23.5 // indirect + k8s.io/apiserver v0.24.0 // indirect + k8s.io/cli-runtime v0.24.0 // indirect + k8s.io/component-base v0.24.0 // indirect + k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6 // indirect + k8s.io/kubectl v0.24.0 // indirect k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect oras.land/oras-go v1.1.1 // indirect sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect diff --git a/go.sum b/go.sum index f79fe67a1..851e2a68e 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,11 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= +4d63.com/gochecknoglobals v0.1.0/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= +bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= 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= @@ -13,10 +14,12 @@ cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6 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.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU= 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.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -27,7 +30,6 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= @@ -45,41 +47,39 @@ cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLq 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= +cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= 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/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w= +cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk= 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= +contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo= +github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY= github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= -github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= +github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= @@ -88,65 +88,45 @@ github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/ github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Masterminds/squirrel v1.5.2 h1:UiOEi2ZX4RCSkpiNDQN5kro/XIBpSRk9iTqdIRPzUXE= -github.com/Masterminds/squirrel v1.5.2/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Masterminds/vcs v1.13.3/go.mod h1:TiE7xuEjl1N4j016moRd6vezp6e6Lz23gypeXfzXeW8= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= +github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= github.com/Microsoft/hcsshim v0.9.2 h1:wB06W5aYFfUB3IvootYAY2WnOmIdgPGfqSI6tufQNnY= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 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/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 h1:cSHEbLj0GZeHM1mWG84qEnGFojNEQ83W7cwaPRjcwXU= -github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b h1:lcbBNuQhppsc7A5gjdHmdlqUqJfgGMylBdGyDs0j7G8= +github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -160,60 +140,55 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF 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/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= +github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.44.1 h1:w34ZmPT6K4NTd7Yap1P7SLXPTii0ABmBz3KEh4KJdKc= -github.com/aws/aws-sdk-go v1.44.1/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= +github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= +github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.44.19 h1:dhI6p4l6kisnA7gBAM8sP5YIk0bZ9HNAj7yrK7kcfdU= +github.com/aws/aws-sdk-go v1.44.19/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bitnami-labs/sealed-secrets v0.17.5 h1:v5ENZRSrgog3GnFr8fWfVtrUTPlZlNlbsjaro9mn6YY= github.com/bitnami-labs/sealed-secrets v0.17.5/go.mod h1:ZgGUqKixr/SRpsG8LVXnuneyZG7DOX/+eCRvXi8SVjo= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/blizzy78/varnamelen v0.3.0/go.mod h1:hbwRdBvoBqxk34XyQ6HA0UH3G0/1TKuv5AC4eaBT0Ec= +github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= -github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -222,20 +197,11 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= +github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU= 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/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= 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= @@ -246,129 +212,28 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= -github.com/containerd/containerd v1.6.3 h1:JfgUEIAH07xDWk6kqz0P3ArZt+KJ9YeihSC9uyFtSKg= -github.com/containerd/containerd v1.6.3/go.mod h1:gCVGrYRYFm2E8GmuUIbj/NGD7DLZQLzSJQazjVKDOig= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/stargz-snapshotter/estargz v0.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng= -github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/containerd/containerd v1.6.4 h1:SEDZBp10mhCp+hkO3Njz/YhGrI7ah3edNcUlRdUPOgg= +github.com/containerd/containerd v1.6.4/go.mod h1:oWOqbuJUZmOVafhA0lj2NAXbiO1u7F0K5l1bUgdyo94= +github.com/containerd/stargz-snapshotter/estargz v0.11.4 h1:LjrYUZpyOhiSaU7hHrdR82/RBoxfGWSaC0VeSSMXqnk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -376,62 +241,43 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= +github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY= -github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684/go.mod h1:UfCu3YXJJCI+IdnqGgYP82dk2+Joxmv+mUTVBES6wac= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.14+incompatible h1:dSBKJOVesDgHo7rbxlYjYsXe7gPzrTT+/cKQgpDAazg= -github.com/docker/cli v20.10.14+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/cli v20.10.16+incompatible h1:aLQ8XowgKpR3/IysPj8qZQJBVQ+Qws61icFuZl6iKYs= +github.com/docker/cli v20.10.16+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.14+incompatible h1:+T9/PRYWNDo5SZl5qS1r9Mo/0Q8AwxKKPtu9S1yxM0w= -github.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ= +github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -444,11 +290,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m 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/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= +github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= @@ -464,30 +310,33 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= github.com/gammazero/deque v0.1.1 h1:xRVkDuSvDmFuMGf3IquHuRc2jlL0+v/WpFCWaauzwbE= github.com/gammazero/deque v0.1.1/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/qj87g= github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= @@ -505,7 +354,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4= github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 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= @@ -516,30 +364,19 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.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.1/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.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 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.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= 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/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= @@ -553,41 +390,43 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= 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.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig= -github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= +github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= +github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= +github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= +github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astequal v1.0.1/go.mod h1:4oGA3EZXTVItV/ipGiOx7NWkY5veFfcsOJVS2YxltLw= +github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= +github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= +github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= +github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= +github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= +github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= +github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= +github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= -github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= -github.com/gobuffalo/packr/v2 v2.8.1/go.mod h1:c/PLlOuTU+p3SybaJATW3H6lX/iK7xEz5OeMf+NnJpg= github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0= github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -615,6 +454,7 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -634,17 +474,29 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= +github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= +github.com/golangci/golangci-lint v1.43.0/go.mod h1:VIFlUqidx5ggxDfQagdvd9E67UjMXtTHBkBQ7sHoC5Q= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= +github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= +github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= -github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 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/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= +github.com/google/cel-go v0.10.1/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= +github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +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= @@ -660,9 +512,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/go-containerregistry v0.8.0 h1:mtR24eN6rapCN+shds82qFEIWWmg64NPMuyCNT7/Ogc= -github.com/google/go-containerregistry v0.8.0/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI= +github.com/google/go-containerregistry v0.9.0 h1:5Ths7RjxyFV0huKChQTgY6fLzvHhZMpLTFNja8U0/0w= +github.com/google/go-containerregistry v0.9.0/go.mod h1:9eq4BnSufyT1kHNffX+vSXVonaJ7yaIOulrKZejMxnQ= 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= @@ -677,9 +528,11 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf 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-20200507031123-427632fa3b1c/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-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -688,10 +541,11 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= +github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/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= @@ -700,21 +554,31 @@ github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pf github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= +github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw= +github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= +github.com/gostaticanalysis/analysisutil v0.4.1/go.mod h1:18U/DLpRgIUd459wGxVHE0fRgmo1UgHDcbw7F5idXu0= +github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= +github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI= +github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= +github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= +github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= +github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= +github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= +github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -722,37 +586,32 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJr github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -762,58 +621,55 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/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.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= +github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOvVRsHiKkeGCT6tYBNWyDVuzj9wAaBb5R9qamfw= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.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= @@ -821,9 +677,11 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= 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/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -832,12 +690,12 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.2 h1:3WH+AG7s2+T8o3nrM/8u2rdqUEcQhmga7smjrT41nAw= -github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.4 h1:1kn4/7MepF/CHmYub99/nNX8az0IJjfSOU/jbnTVfqQ= +github.com/klauspost/compress v1.15.4/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -850,52 +708,64 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn 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/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 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/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U= +github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= +github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ= -github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= +github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/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/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= +github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/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 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -908,25 +778,28 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= +github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= +github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= +github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= @@ -947,25 +820,18 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -975,18 +841,28 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN 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/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= +github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= +github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= +github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nishanths/exhaustive v0.2.3/go.mod h1:bhIX678Nx8inLM9PbpvK1yv6oGtoP8BfaIeMzgBNKvc= +github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ= +github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -994,129 +870,103 @@ github.com/ohler55/ojg v1.14.0 h1:DyHomsCwofNswmKj7BLMdx51xnKbXxgIo1rVWCaBcNk= github.com/ohler55/ojg v1.14.0/go.mod h1:3+GH+0PggMKocQtbZCrFifal3yRpHiBT4QUkxFJI6e8= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+czc/J8SlhPKLOtVLMQc+xDCFBT73ZStMsRhSsUhsSg= +github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 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.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= -github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= 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-0.20171018195549-f15c970de5b7/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 v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 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.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= +github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= +github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= +github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= +github.com/quasilyte/go-ruleguard v0.3.13/go.mod h1:Ul8wwdqR6kBVOCt2dipDBkE+T6vAV/iixkrKuRTN1oQ= +github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= +github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= @@ -1124,11 +974,12 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 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.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc/go.mod h1:HFLT6i9iR4QBOF5rdCyjddC9t59ArqWJV2xx+jwcCMo= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rubenv/sql-migrate v1.1.1 h1:haR5Hn8hbW9/SpAICrXoZqXnywS7Q5WijwkQENPeNWY= github.com/rubenv/sql-migrate v1.1.1/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -1136,25 +987,24 @@ github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3V github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg= +github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= +github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= +github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= +github.com/shirou/gopsutil/v3 v3.21.10/go.mod h1:t75NhzCZ/dYyPQjyQmrAYP6c8+LCdFANeBMdLPCNnew= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -1162,52 +1012,52 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= +github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= +github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +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 v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.10.0 h1:mXH0UwHS4D2HwWZa75im4xIQynLfblmWV7qcWpfv0yk= -github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= +github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44= +github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk= +github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1218,39 +1068,39 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= +github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= +github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= +github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= +github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= +github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/tomarrell/wrapcheck/v2 v2.4.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= +github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= +github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= +github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= -github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= github.com/vbauerster/mpb/v7 v7.4.1 h1:NhLMWQ3gNg2KJR8oeA9lO8Xvq+eNPmixDmB6JEQOUdA= github.com/vbauerster/mpb/v7 v7.4.1/go.mod h1:Ygg2mV9Vj9sQBWqsK2m2pidcf9H3s6bNKtqd3/M4gBo= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= @@ -1259,14 +1109,18 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMc github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yeya24/promlinter v0.1.0/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= 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= @@ -1275,29 +1129,26 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q= go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= 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= @@ -1308,68 +1159,61 @@ go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd h1:Uo/x0Ir5vQJ+683GXB9Ug+4fcjsbp7z7Ul8UaZbhsRM= go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 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= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/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-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/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-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= +golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -1380,6 +1224,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 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/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= 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= @@ -1406,11 +1251,10 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1421,17 +1265,15 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/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= @@ -1439,6 +1281,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-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-20200421231249-e086a090c8fd/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= @@ -1447,7 +1290,6 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ 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-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1459,24 +1301,20 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 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= @@ -1493,7 +1331,6 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/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.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= @@ -1503,14 +1340,16 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ 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-20190412183630-56d357773e84/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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1518,6 +1357,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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= @@ -1525,17 +1365,10 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1545,55 +1378,41 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/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-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/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-20200217220822-9197077df867/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-20200420163511-1957bb5e6d1f/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-20200519105757-fe76b779f299/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-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/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-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1602,7 +1421,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1613,19 +1431,16 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc 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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1634,11 +1449,10 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1658,32 +1472,40 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/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.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/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-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/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-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/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-20190614205625-5aca471b1d59/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-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/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-20190916130336-e45ffcd953cc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1691,9 +1513,11 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20200117220505-0cba7a3a9ee9/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= @@ -1702,47 +1526,72 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK 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-20200308013534-11ec41452d41/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-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 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-20200616133436-c1934b75d054/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-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 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-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210104081019-d8d6ddbec6ee/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.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= +golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= 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.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= +golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= 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= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 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.10.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= @@ -1769,9 +1618,7 @@ google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6 google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= @@ -1782,26 +1629,27 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl 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.2/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/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/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-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 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-20190927181202-20e1ac93f88c/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-20200117163144-32f20d992d24/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= @@ -1816,8 +1664,9 @@ google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfG 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-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 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= @@ -1825,11 +1674,12 @@ google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/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-20201110150050-8816d57aaa9a/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-20210222152913-aa3ee6e6a81c/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-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1850,11 +1700,7 @@ google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= @@ -1870,12 +1716,11 @@ google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 h1:G1IeWbjrqEq9ChWxEuRPJu6laA67+XgTFHVSAvepr38= -google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -1885,6 +1730,7 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 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.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 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= @@ -1902,12 +1748,11 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1924,31 +1769,28 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= +gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -1959,21 +1801,21 @@ 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.6/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 h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -helm.sh/helm/v3 v3.8.2 h1:HDhe2nKek976VLMPZlIgJbNqwcqvHYBp1qy+sXQ4jiY= -helm.sh/helm/v3 v3.8.2/go.mod h1:NxtE2KObf2PrzDl6SIamPFPKyAqWi10iWuvKlQn/Yao= +helm.sh/helm/v3 v3.9.0 h1:qDSWViuF6SzZX5s5AB/NVRGWmdao7T5j4S4ebIkMGag= +helm.sh/helm/v3 v3.9.0/go.mod h1:fzZfyslcPAWwSdkXrXlpKexFeE2Dei8N27FFQWt+PN0= 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= @@ -1981,90 +1823,67 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA= -k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= -k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ= -k8s.io/apiextensions-apiserver v0.23.6 h1:v58cQ6Z0/GK1IXYr+oW0fnYl52o9LTY0WgoWvI8uv5Q= -k8s.io/apiextensions-apiserver v0.23.6/go.mod h1:YVh17Mphv183THQJA5spNFp9XfoidFyL3WoDgZxQIZU= -k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0= -k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw= -k8s.io/apiserver v0.23.6 h1:p94LiXcsSnpSDIl4cv98liBuFKcaygSCNopFNfMg/Ac= -k8s.io/apiserver v0.23.6/go.mod h1:5PU32F82tfErXPmf7FXhd/UcuLfh97tGepjKUgJ2atg= -k8s.io/cli-runtime v0.23.5 h1:Z7XUpGoJZYZB2uNjQfJjMbyDKyVkoBGye62Ap0sWQHY= -k8s.io/cli-runtime v0.23.5/go.mod h1:oY6QDF2qo9xndSq32tqcmRp2UyXssdGrLfjAVymgbx4= -k8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8= -k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= -k8s.io/code-generator v0.23.6/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= -k8s.io/component-base v0.23.5 h1:8qgP5R6jG1BBSXmRYW+dsmitIrpk8F/fPEvgDenMCCE= -k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0= -k8s.io/component-helpers v0.23.5/go.mod h1:5riXJgjTIs+ZB8xnf5M2anZ8iQuq37a0B/0BgoPQuSM= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= +honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= +k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I= +k8s.io/api v0.24.1-rc.0 h1:qCZcUZ/YVXVVacShyNgyEHSUGxrWwIlDGAU1XpyMhYs= +k8s.io/api v0.24.1-rc.0/go.mod h1:IXhQwEOq8wx1Hmtm/YCNjyQ3XaeIUH0NP5Zsc0aF5Js= +k8s.io/apiextensions-apiserver v0.24.0 h1:JfgFqbA8gKJ/uDT++feAqk9jBIwNnL9YGdQvaI9DLtY= +k8s.io/apiextensions-apiserver v0.24.0/go.mod h1:iuVe4aEpe6827lvO6yWQVxiPSpPoSKVjkq+MIdg84cM= +k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.24.1-rc.0 h1:mjcDse2fEbUbiSZYU83NxdLUXlCSprcMlQVgNWn6tGk= +k8s.io/apimachinery v0.24.1-rc.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apiserver v0.24.0 h1:GR7kGsjOMfilRvlG3Stxv/3uz/ryvJ/aZXc5pqdsNV0= +k8s.io/apiserver v0.24.0/go.mod h1:WFx2yiOMawnogNToVvUYT9nn1jaIkMKj41ZYCVycsBA= +k8s.io/cli-runtime v0.24.0 h1:ot3Qf49T852uEyNApABO1UHHpFIckKK/NqpheZYN2gM= +k8s.io/cli-runtime v0.24.0/go.mod h1:9XxoZDsEkRFUThnwqNviqzljtT/LdHtNWvcNFrAXl0A= +k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw= +k8s.io/client-go v0.24.1-rc.0 h1:FoTsvkV8oz2FTORlpAR6TgRQMcdzGVIGQkjc7VrwZ40= +k8s.io/client-go v0.24.1-rc.0/go.mod h1:6VdmspONsQ+gV2AsYugsRaVSYOEUm3Trd16aQJgJahU= +k8s.io/code-generator v0.24.0/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= +k8s.io/component-base v0.24.0 h1:h5jieHZQoHrY/lHG+HyrSbJeyfuitheBvqvKwKHVC0g= +k8s.io/component-base v0.24.0/go.mod h1:Dgazgon0i7KYUsS8krG8muGiMVtUZxG037l1MKyXgrA= +k8s.io/component-helpers v0.24.0/go.mod h1:Q2SlLm4h6g6lPTC9GMMfzdywfLSvJT2f1hOnnjaWD8c= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= -k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 h1:nqYOUleKLC/0P1zbU29F5q6aoezM6MOAVz+iyfQbZ5M= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= -k8s.io/kubectl v0.23.5 h1:DmDULqCaF4qstj0Im143XmncvqWtJxHzK8IrW2BzlU0= -k8s.io/kubectl v0.23.5/go.mod h1:lLgw7cVY8xbd7o637vOXPca/w6HC205KsPCRDYRCxwE= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/metrics v0.23.5/go.mod h1:WNAtV2a5BYbmDS8+7jSqYYV6E3efuGTpIwJ8PTD1wgs= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6 h1:nBQrWPlrNIiw0BsX6a6MKr1itkm0ZS0Nl97kNLitFfI= +k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= +k8s.io/kubectl v0.24.0 h1:nA+WtMLVdXUs4wLogGd1mPTAesnLdBpCVgCmz3I7dXo= +k8s.io/kubectl v0.24.0/go.mod h1:pdXkmCyHiRTqjYfyUJiXtbVNURhv0/Q1TyRhy2d5ic0= +k8s.io/metrics v0.24.0/go.mod h1:jrLlFGdKl3X+szubOXPG0Lf2aVxuV3QJcbsgVRAM6fI= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= +mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= oras.land/oras-go v1.1.1 h1:gI00ftziRivKXaw1BdMeEoIA4uBgga33iVlOsEwefFs= oras.land/oras-go v1.1.1/go.mod h1:n2TE1ummt9MUyprGhT+Q7kGZUF4kVUpYysPFxeV2IpQ= 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.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/kind v0.12.0 h1:LFynXwQkH1MrWI8pM1FQty0oUwEKjU5EkMaVZaPld8E= -sigs.k8s.io/kind v0.12.0/go.mod h1:EcgDSBVxz8Bvm19fx8xkioFrf9dC30fMJdOTXBSGNoM= -sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8= +sigs.k8s.io/kind v0.14.0 h1:cNmI3jGBvp7UegEGbC5we8plDtCUmaNRL+bod7JoSCE= +sigs.k8s.io/kind v0.14.0/go.mod h1:UrFRPHG+2a5j0Q7qiR4gtJ4rEyn8TuMQwuOPf+m4oHg= +sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= sigs.k8s.io/kustomize/api v0.11.5 h1:vLDp++YAX7iy2y2CVPJNy9pk9CY8XaUKgHkjbVtnWag= sigs.k8s.io/kustomize/api v0.11.5/go.mod h1:2UDpxS6AonWXow2ZbySd4AjUxmdXLeTlvGBC46uSiq8= -sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ= -sigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io= -sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E= +sigs.k8s.io/kustomize/cmd/config v0.10.6/go.mod h1:/S4A4nUANUa4bZJ/Edt7ZQTyKOY9WCER0uBS1SW2Rco= +sigs.k8s.io/kustomize/kustomize/v4 v4.5.4/go.mod h1:Zo/Xc5FKD6sHl0lilbrieeGeZHVYCA4BzxeAaLI05Bg= +sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= sigs.k8s.io/kustomize/kyaml v0.13.7 h1:/EZ/nPaLUzeJKF/BuJ4QCuMVJWiEVoI8iftOHY3g3tk= sigs.k8s.io/kustomize/kyaml v0.13.7/go.mod h1:6K+IUOuir3Y7nucPRAjw9yth04KSWBnP5pqUTGwj/qU= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= From 630e7db136b0b02914f9ea94fd4fe8bdbc6dd6d4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 13:41:26 +0200 Subject: [PATCH 0218/2268] fix: Fix false positive error about not included rendered helm resource We must take sub directories into account when checking for inclusion of rendered helm charts. --- pkg/deployment/deployment_item.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 783084673..c8a5d92a2 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -171,12 +171,17 @@ func (di *DeploymentItem) renderHelmCharts(k *k8s.K8sCluster, wp *utils.WorkerPo } wp.Submit(func() error { + subDir, err := filepath.Rel(di.renderedDir, filepath.Dir(p)) + if err != nil { + return err + } + chart, err := NewHelmChart(p) if err != nil { return err } - ky, err := di.readKustoimizationYaml() + ky, err := di.readKustoimizationYaml(subDir) if err == nil && ky != nil { resources, _, _ := ky.GetNestedStringList("resources") found := false @@ -301,12 +306,12 @@ func (di *DeploymentItem) checkInclusionForDelete() bool { return di.Inclusion.CheckIncluded(values, di.Config.SkipDeleteIfTags) } -func (di *DeploymentItem) readKustoimizationYaml() (*uo.UnstructuredObject, error) { +func (di *DeploymentItem) readKustoimizationYaml(subDir string) (*uo.UnstructuredObject, error) { if di.dir == nil { return nil, nil } - kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.renderedDir, "kustomization.yml")) + kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.renderedDir, subDir, "kustomization.yml")) if !utils.IsFile(kustomizeYamlPath) { return nil, nil } @@ -325,7 +330,7 @@ func (di *DeploymentItem) writeKustomizationYaml(ky *uo.UnstructuredObject) erro } func (di *DeploymentItem) prepareKustomizationYaml() error { - ky, err := di.readKustoimizationYaml() + ky, err := di.readKustoimizationYaml("") if err != nil { return err } From d1904f53b19d84ebf387d13d9fb6feb44f99dca6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 13:43:01 +0200 Subject: [PATCH 0219/2268] chore: Fix typo in readKustomizationYaml --- pkg/deployment/deployment_item.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index c8a5d92a2..65d303bfd 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -181,7 +181,7 @@ func (di *DeploymentItem) renderHelmCharts(k *k8s.K8sCluster, wp *utils.WorkerPo return err } - ky, err := di.readKustoimizationYaml(subDir) + ky, err := di.readKustomizationYaml(subDir) if err == nil && ky != nil { resources, _, _ := ky.GetNestedStringList("resources") found := false @@ -306,7 +306,7 @@ func (di *DeploymentItem) checkInclusionForDelete() bool { return di.Inclusion.CheckIncluded(values, di.Config.SkipDeleteIfTags) } -func (di *DeploymentItem) readKustoimizationYaml(subDir string) (*uo.UnstructuredObject, error) { +func (di *DeploymentItem) readKustomizationYaml(subDir string) (*uo.UnstructuredObject, error) { if di.dir == nil { return nil, nil } @@ -330,7 +330,7 @@ func (di *DeploymentItem) writeKustomizationYaml(ky *uo.UnstructuredObject) erro } func (di *DeploymentItem) prepareKustomizationYaml() error { - ky, err := di.readKustoimizationYaml("") + ky, err := di.readKustomizationYaml("") if err != nil { return err } From f079878f0a97b510a18f4c2c8a22d5015c03112c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 16:59:04 +0200 Subject: [PATCH 0220/2268] fix: Get rid of pathType The implementation was broken and it actually doesn't make sense to have it. --- cmd/kluctl/args/cobra_types.go | 21 --------------------- cmd/kluctl/args/misc.go | 2 +- cmd/kluctl/args/project.go | 2 +- cmd/kluctl/commands/cmd_render.go | 2 +- cmd/kluctl/commands/utils.go | 4 ++-- 5 files changed, 5 insertions(+), 26 deletions(-) diff --git a/cmd/kluctl/args/cobra_types.go b/cmd/kluctl/args/cobra_types.go index 7989383a1..c6907ea51 100644 --- a/cmd/kluctl/args/cobra_types.go +++ b/cmd/kluctl/args/cobra_types.go @@ -64,24 +64,3 @@ func (s *existingDirType) Type() string { } func (s *existingDirType) String() string { return string(*s) } - -type pathType string - -func (s *pathType) Set(val string) error { - if val != "-" { - val = utils.ExpandPath(val) - } - if !utils.Exists(val) { - return fmt.Errorf("%s does not exist", val) - } - if !utils.IsDirectory(val) { - return fmt.Errorf("%s exists but is not a directory", val) - } - *s = pathType(val) - return nil -} -func (s *pathType) Type() string { - return "path" -} - -func (s *pathType) String() string { return string(*s) } diff --git a/cmd/kluctl/args/misc.go b/cmd/kluctl/args/misc.go index 86ef05ae5..196f52872 100644 --- a/cmd/kluctl/args/misc.go +++ b/cmd/kluctl/args/misc.go @@ -44,5 +44,5 @@ type OutputFlags struct { } type RenderOutputDirFlags struct { - RenderOutputDir pathType `group:"misc" help:"Specifies the target directory to render the project into. If omitted, a temporary directory is used."` + RenderOutputDir string `group:"misc" help:"Specifies the target directory to render the project into. If omitted, a temporary directory is used."` } diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index c7479b869..f1b35c3fb 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -12,7 +12,7 @@ type ProjectFlags struct { LocalSealedSecrets existingDirType `group:"project" help:"Local sealed-secrets directory. Overrides the project from .kluctl.yaml" ` FromArchive existingPathType `group:"project" help:"Load project (.kluctl.yaml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." exts:"tar.gz,tgz"` FromArchiveMetadata existingFileType `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." exts:"yml,yaml"` - OutputMetadata pathType `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` + OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` Cluster string `group:"project" help:"Specify/Override cluster"` Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index c22b242af..3aabf8cf0 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -26,7 +26,7 @@ func (cmd *renderCmd) Run() error { if err != nil { return err } - _ = cmd.RenderOutputDir.Set(p) + cmd.RenderOutputDir = p } ptArgs := projectTargetCommandArgs{ diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 8dab17d0d..388396f30 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -87,7 +87,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b if err != nil { return err } - err = ioutil.WriteFile(projectFlags.OutputMetadata.String(), b, 0o640) + err = ioutil.WriteFile(projectFlags.OutputMetadata, b, 0o640) if err != nil { return err } @@ -148,7 +148,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm return err } - renderOutputDir := args.renderOutputDirFlags.RenderOutputDir.String() + renderOutputDir := args.renderOutputDirFlags.RenderOutputDir if renderOutputDir == "" { tmpDir, err := ioutil.TempDir(p.TmpDir, "rendered") if err != nil { From f0f94803256d63453627d2b6e42dae8a83671838 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 23 May 2022 17:07:25 +0200 Subject: [PATCH 0221/2268] refactor: Move loading of individual include into own function --- pkg/deployment/deployment_project.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 6ac7de718..17e5e306c 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -162,24 +162,32 @@ func (p *DeploymentProject) loadIncludes(k *k8s.K8sCluster) error { incDir := filepath.Join(p.dir, *inc.Include) - varsCtx := p.VarsCtx.Copy() - err := p.loadVarsList(varsCtx, inc.Vars) + newProject, err := p.loadLocalInclude(k, incDir, inc.Vars) if err != nil { return err } - newProject, err := NewDeploymentProject(p.ctx, k, p.varsLoader, varsCtx, incDir, - p.SealedSecretsDir, p) - if err != nil { - return err - } newProject.parentProjectInclude = inc - p.includes[i] = newProject } return nil } +func (p *DeploymentProject) loadLocalInclude(k *k8s.K8sCluster, incDir string, vars []*types.VarsSource) (*DeploymentProject, error) { + varsCtx := p.VarsCtx.Copy() + err := p.loadVarsList(varsCtx, vars) + if err != nil { + return nil, err + } + + newProject, err := NewDeploymentProject(p.ctx, k, p.varsLoader, varsCtx, incDir, p.SealedSecretsDir, p) + if err != nil { + return nil, err + } + + return newProject, nil +} + func (p *DeploymentProject) getSealedSecretsDir() string { root := p.getRootProject() if root.Config.SealedSecrets == nil || root.Config.SealedSecrets.OutputPattern == nil { From 7b4713fda338d747702f2264a520f88ebb77ebd6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 10:07:01 +0200 Subject: [PATCH 0222/2268] refactor: Introduce SharedContext for deployment projects and collections --- cmd/kluctl/commands/cmd_delete.go | 4 +-- cmd/kluctl/commands/cmd_deploy.go | 2 +- cmd/kluctl/commands/cmd_diff.go | 2 +- cmd/kluctl/commands/cmd_downscale.go | 2 +- cmd/kluctl/commands/cmd_poke_images.go | 2 +- cmd/kluctl/commands/cmd_prune.go | 4 +-- cmd/kluctl/commands/cmd_render.go | 2 +- cmd/kluctl/commands/cmd_seal.go | 8 +++--- cmd/kluctl/commands/cmd_validate.go | 2 +- cmd/kluctl/commands/completion.go | 2 +- cmd/kluctl/commands/utils.go | 2 +- pkg/deployment/commands/seal.go | 20 +++++++------ pkg/deployment/deployment_collection.go | 35 +++++++++++------------ pkg/deployment/deployment_item.go | 25 +++++++++------- pkg/deployment/deployment_project.go | 38 ++++++++++--------------- pkg/deployment/shared_context.go | 17 +++++++++++ pkg/kluctl_project/target_context.go | 20 +++++++++---- 17 files changed, 104 insertions(+), 83 deletions(-) create mode 100644 pkg/deployment/shared_context.go diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 4bb602290..4911d9479 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -55,11 +55,11 @@ func (cmd *deleteCmd) Run() error { cmd2.OverrideDeleteByLabels = deleteByLabels - objects, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) + objects, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) if err != nil { return err } - result, err := confirmedDeleteObjects(ctx.targetCtx.K, objects, cmd.DryRun, cmd.Yes) + result, err := confirmedDeleteObjects(ctx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 1e514ca9b..a2b6d70a9 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -63,7 +63,7 @@ func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { cb = nil } - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K, cb) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K, cb) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 7b2efec67..bc6fcc211 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -43,7 +43,7 @@ func (cmd *diffCmd) Run() error { cmd2.IgnoreTags = cmd.IgnoreTags cmd2.IgnoreLabels = cmd.IgnoreLabels cmd2.IgnoreAnnotations = cmd.IgnoreAnnotations - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_downscale.go b/cmd/kluctl/commands/cmd_downscale.go index d3a0ffc10..304b5cb1c 100644 --- a/cmd/kluctl/commands/cmd_downscale.go +++ b/cmd/kluctl/commands/cmd_downscale.go @@ -44,7 +44,7 @@ func (cmd *downscaleCmd) Run() error { cmd2 := commands.NewDownscaleCommand(ctx.targetCtx.DeploymentCollection) - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index c7121e515..67e7c5d0a 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -44,7 +44,7 @@ func (cmd *pokeImagesCmd) Run() error { cmd2 := commands.NewPokeImagesCommand(ctx.targetCtx.DeploymentCollection) - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 14d4d8790..d9c25852a 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -43,11 +43,11 @@ func (cmd *pruneCmd) Run() error { func (cmd *pruneCmd) runCmdPrune(ctx *commandCtx) error { cmd2 := commands.NewPruneCommand(ctx.targetCtx.DeploymentCollection) - objects, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) + objects, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) if err != nil { return err } - result, err := confirmedDeleteObjects(ctx.targetCtx.K, objects, cmd.DryRun, cmd.Yes) + result, err := confirmedDeleteObjects(ctx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 3aabf8cf0..190760e8d 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -37,7 +37,7 @@ func (cmd *renderCmd) Run() error { renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - status.Info(ctx.ctx, "Rendered into %s", ctx.targetCtx.DeploymentCollection.RenderDir) + status.Info(ctx.ctx, "Rendered into %s", ctx.targetCtx.SharedContext.RenderDir) return nil }) } diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 6749b559d..531fb13b4 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -44,7 +44,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L // pass forSeal=True so that .sealme files are rendered as well return withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { - err := ctx.targetCtx.DeploymentCollection.RenderDeployments(ctx.targetCtx.K) + err := ctx.targetCtx.DeploymentCollection.RenderDeployments() if err != nil { return doFail(err) } @@ -60,7 +60,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L } } if p.Config.SecretsConfig == nil || p.Config.SecretsConfig.SealedSecrets == nil || p.Config.SecretsConfig.SealedSecrets.Bootstrap == nil || *p.Config.SecretsConfig.SealedSecrets.Bootstrap { - err = seal.BootstrapSealedSecrets(ctx.ctx, ctx.targetCtx.K, sealedSecretsNamespace) + err = seal.BootstrapSealedSecrets(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace) if err != nil { return doFail(err) } @@ -70,7 +70,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L if err != nil { return doFail(err) } - sealer, err := seal.NewSealer(ctx.ctx, ctx.targetCtx.K, sealedSecretsNamespace, sealedSecretsControllerName, clusterConfig.Cluster, cmd.ForceReseal) + sealer, err := seal.NewSealer(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace, sealedSecretsControllerName, clusterConfig.Cluster, cmd.ForceReseal) if err != nil { return doFail(err) } @@ -82,7 +82,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L outputPattern = *ctx.targetCtx.DeploymentProject.Config.SealedSecrets.OutputPattern } - cmd2 := commands.NewSealCommand(ctx.targetCtx.DeploymentCollection, outputPattern) + cmd2 := commands.NewSealCommand(ctx.targetCtx.DeploymentCollection, outputPattern, ctx.targetCtx.SharedContext.RenderDir, ctx.targetCtx.SharedContext.SealedSecretsDir) err = cmd2.Run(sealer) if err != nil { diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index 308f1abfb..b34d90a0d 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -39,7 +39,7 @@ func (cmd *validateCmd) Run() error { startTime := time.Now() cmd2 := commands.NewValidateCommand(ctx.ctx, ctx.targetCtx.DeploymentCollection) for true { - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.K) + result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) if err != nil { return err } diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index a541ab565..5b94c0964 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -204,7 +204,7 @@ func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, a wg.Add(1) go func() { _ = withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { - err := ctx.targetCtx.DeploymentCollection.Prepare(nil) + err := ctx.targetCtx.DeploymentCollection.Prepare() if err != nil { status.Error(cliCtx, err.Error()) } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 388396f30..12c89660b 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -172,7 +172,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm } if !args.forSeal && !args.forCompletion { - err = targetCtx.DeploymentCollection.Prepare(targetCtx.K) + err = targetCtx.DeploymentCollection.Prepare() if err != nil { return err } diff --git a/pkg/deployment/commands/seal.go b/pkg/deployment/commands/seal.go index 0397133ca..c8c3e37a5 100644 --- a/pkg/deployment/commands/seal.go +++ b/pkg/deployment/commands/seal.go @@ -11,29 +11,33 @@ import ( ) type SealCommand struct { - c *deployment.DeploymentCollection - outputPattern string + c *deployment.DeploymentCollection + outputPattern string + renderDir string + sealedSecretsDir string } -func NewSealCommand(c *deployment.DeploymentCollection, outputPattern string) *SealCommand { +func NewSealCommand(c *deployment.DeploymentCollection, outputPattern string, renderDir string, sealedSecretsDir string) *SealCommand { return &SealCommand{ - c: c, - outputPattern: outputPattern, + c: c, + outputPattern: outputPattern, + renderDir: renderDir, + sealedSecretsDir: sealedSecretsDir, } } func (cmd *SealCommand) Run(sealer *seal.Sealer) error { - err := filepath.WalkDir(cmd.c.RenderDir, func(p string, d fs.DirEntry, err error) error { + err := filepath.WalkDir(cmd.renderDir, func(p string, d fs.DirEntry, err error) error { if !strings.HasSuffix(p, deployment.SealmeExt) { return nil } - relPath, err := filepath.Rel(cmd.c.RenderDir, p) + relPath, err := filepath.Rel(cmd.renderDir, p) if err != nil { return err } relTargetFile := filepath.Join(filepath.Dir(relPath), cmd.outputPattern, filepath.Base(p)) - targetFile, err := securejoin.SecureJoin(cmd.c.Project.SealedSecretsDir, relTargetFile) + targetFile, err := securejoin.SecureJoin(cmd.sealedSecretsDir, relTargetFile) if err != nil { return err } diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 03883a86c..d25503c77 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -3,7 +3,6 @@ package deployment import ( "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" @@ -14,25 +13,23 @@ import ( ) type DeploymentCollection struct { - ctx context.Context + ctx SharedContext Project *DeploymentProject Images *Images Inclusion *utils.Inclusion - RenderDir string forSeal bool Deployments []*DeploymentItem mutex sync.Mutex } -func NewDeploymentCollection(ctx context.Context, project *DeploymentProject, images *Images, inclusion *utils.Inclusion, renderDir string, forSeal bool) (*DeploymentCollection, error) { +func NewDeploymentCollection(ctx SharedContext, project *DeploymentProject, images *Images, inclusion *utils.Inclusion, forSeal bool) (*DeploymentCollection, error) { dc := &DeploymentCollection{ ctx: ctx, Project: project, Images: images, Inclusion: inclusion, - RenderDir: renderDir, forSeal: forSeal, } @@ -49,7 +46,7 @@ func (c *DeploymentCollection) createBarrierDummy(project *DeploymentProject) *D tmpDiConfig := &types.DeploymentItemConfig{ Barrier: true, } - di, err := NewDeploymentItem(project, c, tmpDiConfig, nil, 0) + di, err := NewDeploymentItem(c.ctx, project, c, tmpDiConfig, nil, 0) if err != nil { panic(err) } @@ -97,7 +94,7 @@ func (c *DeploymentCollection) collectDeployments(project *DeploymentProject, in } } else { index, dir2 := findDeploymentItemIndex(project, diConfig.Path, indexes) - di, err := NewDeploymentItem(project, c, diConfig, dir2, index) + di, err := NewDeploymentItem(c.ctx, project, c, diConfig, dir2, index) if err != nil { return nil, err } @@ -108,8 +105,8 @@ func (c *DeploymentCollection) collectDeployments(project *DeploymentProject, in return ret, nil } -func (c *DeploymentCollection) RenderDeployments(k *k8s.K8sCluster) error { - s := status.Start(c.ctx, "Rendering templates") +func (c *DeploymentCollection) RenderDeployments() error { + s := status.Start(c.ctx.Ctx, "Rendering templates") defer s.Failed() wp := utils.NewDebuggerAwareWorkerPool(16) @@ -127,11 +124,11 @@ func (c *DeploymentCollection) RenderDeployments(k *k8s.K8sCluster) error { } s.Success() - s = status.Start(c.ctx, "Rendering Helm Charts") + s = status.Start(c.ctx.Ctx, "Rendering Helm Charts") defer s.Failed() for _, d := range c.Deployments { - err := d.renderHelmCharts(k, wp) + err := d.renderHelmCharts(wp) if err != nil { return err } @@ -159,7 +156,7 @@ func (c *DeploymentCollection) resolveSealedSecrets() error { return nil } -func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { +func (c *DeploymentCollection) buildKustomizeObjects() error { var wg sync.WaitGroup var errs []error var mutex sync.Mutex @@ -171,7 +168,7 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { mutex.Unlock() } - s := status.Start(c.ctx, "Building kustomize objects") + s := status.Start(c.ctx.Ctx, "Building kustomize objects") for _, d_ := range c.Deployments { d := d_ @@ -192,7 +189,7 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { } s.Success() - s = status.Start(c.ctx, "Postprocessing objects") + s = status.Start(c.ctx.Ctx, "Postprocessing objects") for _, d_ := range c.Deployments { d := d_ @@ -202,7 +199,7 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { if err != nil { handleError(fmt.Errorf("loading objects failed: %w", err)) } else { - err = d.postprocessCRDs(k) + err = d.postprocessCRDs() if err != nil { handleError(fmt.Errorf("postprocessing CRDs failed: %w", err)) } @@ -220,7 +217,7 @@ func (c *DeploymentCollection) buildKustomizeObjects(k *k8s.K8sCluster) error { _ = sem.Acquire(context.Background(), 1) defer sem.Release(1) - err := d.postprocessObjects(k, c.Images) + err := d.postprocessObjects(c.Images) if err != nil { mutex.Lock() errs = append(errs, fmt.Errorf("postprocessing kustomize objects for %s failed: %w", *d.dir, err)) @@ -259,8 +256,8 @@ func (c *DeploymentCollection) LocalObjectRefs() []k8s2.ObjectRef { return ret } -func (c *DeploymentCollection) Prepare(k *k8s.K8sCluster) error { - err := c.RenderDeployments(k) +func (c *DeploymentCollection) Prepare() error { + err := c.RenderDeployments() if err != nil { return err } @@ -268,7 +265,7 @@ func (c *DeploymentCollection) Prepare(k *k8s.K8sCluster) error { if err != nil { return err } - err = c.buildKustomizeObjects(k) + err = c.buildKustomizeObjects() if err != nil { return err } diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 65d303bfd..b07ff59a9 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -21,6 +21,8 @@ import ( const SealmeExt = ".sealme" type DeploymentItem struct { + ctx SharedContext + Project *DeploymentProject Inclusion *utils.Inclusion Config *types.DeploymentItemConfig @@ -42,8 +44,9 @@ type DeploymentItem struct { renderedYamlPath string } -func NewDeploymentItem(project *DeploymentProject, collection *DeploymentCollection, config *types.DeploymentItemConfig, dir *string, index int) (*DeploymentItem, error) { +func NewDeploymentItem(ctx SharedContext, project *DeploymentProject, collection *DeploymentCollection, config *types.DeploymentItemConfig, dir *string, index int) (*DeploymentItem, error) { di := &DeploymentItem{ + ctx: ctx, Project: project, Inclusion: collection.Inclusion, Config: config, @@ -80,7 +83,7 @@ func NewDeploymentItem(project *DeploymentProject, collection *DeploymentCollect di.relRenderedDir = fmt.Sprintf("%s-%d", di.relRenderedDir, di.index) } - di.renderedDir = filepath.Join(collection.RenderDir, di.relRenderedDir) + di.renderedDir = filepath.Join(collection.ctx.RenderDir, di.relRenderedDir) di.renderedYamlPath = filepath.Join(di.renderedDir, ".rendered.yml") } return di, nil @@ -160,7 +163,7 @@ func (di *DeploymentItem) render(forSeal bool, wp *utils.WorkerPoolWithErrors) e return nil } -func (di *DeploymentItem) renderHelmCharts(k *k8s.K8sCluster, wp *utils.WorkerPoolWithErrors) error { +func (di *DeploymentItem) renderHelmCharts(wp *utils.WorkerPoolWithErrors) error { if di.dir == nil { return nil } @@ -196,7 +199,7 @@ func (di *DeploymentItem) renderHelmCharts(k *k8s.K8sCluster, wp *utils.WorkerPo } } - return chart.Render(di.Project.ctx, k) + return chart.Render(di.ctx.Ctx, di.ctx.K) }) return nil }) @@ -212,7 +215,7 @@ func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { } sealedSecretsDir := di.Project.getSealedSecretsDir() - baseSourcePath := di.Project.SealedSecretsDir + baseSourcePath := di.Project.ctx.SealedSecretsDir renderedDir := filepath.Join(di.renderedDir, subdir) @@ -419,7 +422,7 @@ var crdGV = schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourc // postprocessCRDs will update api resources from freshly deployed CRDs // value even if the CRD is not deployed yet. -func (di *DeploymentItem) postprocessCRDs(k *k8s.K8sCluster) error { +func (di *DeploymentItem) postprocessCRDs() error { if di.dir == nil { return nil } @@ -430,7 +433,7 @@ func (di *DeploymentItem) postprocessCRDs(k *k8s.K8sCluster) error { continue } - err := k.Resources.UpdateResourcesFromCRD(o) + err := di.ctx.K.Resources.UpdateResourcesFromCRD(o) if err != nil { return err } @@ -438,7 +441,7 @@ func (di *DeploymentItem) postprocessCRDs(k *k8s.K8sCluster) error { return nil } -func (di *DeploymentItem) postprocessObjects(k *k8s.K8sCluster, images *Images) error { +func (di *DeploymentItem) postprocessObjects(images *Images) error { if di.dir == nil { return nil } @@ -451,8 +454,8 @@ func (di *DeploymentItem) postprocessObjects(k *k8s.K8sCluster, images *Images) commonAnnotations := di.getCommonAnnotations() _ = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { - if k != nil { - k.Resources.FixNamespace(o, "default") + if di.ctx.K != nil { + di.ctx.K.Resources.FixNamespace(o, "default") } // Set common labels/annotations @@ -460,7 +463,7 @@ func (di *DeploymentItem) postprocessObjects(k *k8s.K8sCluster, images *Images) o.SetK8sAnnotations(uo.CopyMergeStrMap(o.GetK8sAnnotations(), commonAnnotations)) // Resolve image placeholders - err := images.ResolvePlaceholders(k, o, di.relRenderedDir, di.Tags.ListKeys()) + err := images.ResolvePlaceholders(di.ctx.K, o, di.relRenderedDir, di.Tags.ListKeys()) if err != nil { errList = append(errList, err) } diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 17e5e306c..96985f625 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -1,9 +1,7 @@ package deployment import ( - "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -14,16 +12,12 @@ import ( ) type DeploymentProject struct { - ctx context.Context + ctx SharedContext - varsLoader *vars.VarsLoader - VarsCtx *vars.VarsCtx + VarsCtx *vars.VarsCtx dir string - SealedSecretsDir string - DefaultSealedSecretsOutputPattern string - Config types.DeploymentProjectConfig includes map[int]*DeploymentProject @@ -32,15 +26,13 @@ type DeploymentProject struct { parentProjectInclude *types.DeploymentItemConfig } -func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsLoader *vars.VarsLoader, varsCtx *vars.VarsCtx, dir string, sealedSecretsDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { +func NewDeploymentProject(ctx SharedContext, varsCtx *vars.VarsCtx, dir string, parentProject *DeploymentProject) (*DeploymentProject, error) { dp := &DeploymentProject{ - ctx: ctx, - varsLoader: varsLoader, - VarsCtx: varsCtx.Copy(), - dir: dir, - SealedSecretsDir: sealedSecretsDir, - parentProject: parentProject, - includes: map[int]*DeploymentProject{}, + ctx: ctx, + VarsCtx: varsCtx.Copy(), + dir: dir, + parentProject: parentProject, + includes: map[int]*DeploymentProject{}, } if !utils.IsDirectory(dir) { @@ -52,7 +44,7 @@ func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsLoader *va return nil, fmt.Errorf("failed to load deployment config for %s: %w", dir, err) } - err = dp.loadIncludes(k) + err = dp.loadIncludes() if err != nil { return nil, fmt.Errorf("failed to load includes for %s: %w", dir, err) } @@ -61,7 +53,7 @@ func NewDeploymentProject(ctx context.Context, k *k8s.K8sCluster, varsLoader *va } func (p *DeploymentProject) loadVarsList(varsCtx *vars.VarsCtx, varsList []*types.VarsSource) error { - return p.varsLoader.LoadVarsList(varsCtx, varsList, p.getRenderSearchDirs(), "") + return p.ctx.VarsLoader.LoadVarsList(varsCtx, varsList, p.getRenderSearchDirs(), "") } func (p *DeploymentProject) loadConfig() error { @@ -154,7 +146,7 @@ func (p *DeploymentProject) checkDeploymentDirs() error { return nil } -func (p *DeploymentProject) loadIncludes(k *k8s.K8sCluster) error { +func (p *DeploymentProject) loadIncludes() error { for i, inc := range p.Config.Deployments { if inc.Include == nil { continue @@ -162,7 +154,7 @@ func (p *DeploymentProject) loadIncludes(k *k8s.K8sCluster) error { incDir := filepath.Join(p.dir, *inc.Include) - newProject, err := p.loadLocalInclude(k, incDir, inc.Vars) + newProject, err := p.loadLocalInclude(incDir, inc.Vars) if err != nil { return err } @@ -173,14 +165,14 @@ func (p *DeploymentProject) loadIncludes(k *k8s.K8sCluster) error { return nil } -func (p *DeploymentProject) loadLocalInclude(k *k8s.K8sCluster, incDir string, vars []*types.VarsSource) (*DeploymentProject, error) { +func (p *DeploymentProject) loadLocalInclude(incDir string, vars []*types.VarsSource) (*DeploymentProject, error) { varsCtx := p.VarsCtx.Copy() err := p.loadVarsList(varsCtx, vars) if err != nil { return nil, err } - newProject, err := NewDeploymentProject(p.ctx, k, p.varsLoader, varsCtx, incDir, p.SealedSecretsDir, p) + newProject, err := NewDeploymentProject(p.ctx, varsCtx, incDir, p) if err != nil { return nil, err } @@ -191,7 +183,7 @@ func (p *DeploymentProject) loadLocalInclude(k *k8s.K8sCluster, incDir string, v func (p *DeploymentProject) getSealedSecretsDir() string { root := p.getRootProject() if root.Config.SealedSecrets == nil || root.Config.SealedSecrets.OutputPattern == nil { - return root.DefaultSealedSecretsOutputPattern + return p.ctx.DefaultSealedSecretsOutputPattern } return *root.Config.SealedSecrets.OutputPattern } diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go new file mode 100644 index 000000000..9ee8dd233 --- /dev/null +++ b/pkg/deployment/shared_context.go @@ -0,0 +1,17 @@ +package deployment + +import ( + "context" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/vars" +) + +type SharedContext struct { + Ctx context.Context + K *k8s.K8sCluster + VarsLoader *vars.VarsLoader + + RenderDir string + SealedSecretsDir string + DefaultSealedSecretsOutputPattern string +} diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index d42151d32..79fccfc7d 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -17,10 +17,11 @@ import ( ) type TargetContext struct { + SharedContext deployment.SharedContext + KluctlProject *LoadedKluctlProject Target *types.Target ClusterContext string - K *k8s.K8sCluster DeploymentProject *deployment.DeploymentProject DeploymentCollection *deployment.DeploymentCollection } @@ -73,23 +74,30 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s } } - d, err := deployment.NewDeploymentProject(ctx, k, varsLoader, varsCtx, deploymentDir, p.sealedSecretsDir, nil) + dctx := deployment.SharedContext{ + Ctx: ctx, + K: k, + VarsLoader: varsLoader, + RenderDir: renderOutputDir, + SealedSecretsDir: p.sealedSecretsDir, + DefaultSealedSecretsOutputPattern: targetName, + } + + d, err := deployment.NewDeploymentProject(dctx, varsCtx, deploymentDir, nil) if err != nil { return nil, err } - d.DefaultSealedSecretsOutputPattern = targetName - - c, err := deployment.NewDeploymentCollection(ctx, d, images, inclusion, renderOutputDir, forSeal) + c, err := deployment.NewDeploymentCollection(dctx, d, images, inclusion, forSeal) if err != nil { return nil, err } targetCtx := &TargetContext{ + SharedContext: dctx, KluctlProject: p, Target: target, ClusterContext: clusterContext, - K: k, DeploymentProject: d, DeploymentCollection: c, } From a3efdc0e703384adc665918e966f646ac7313d67 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 10:15:41 +0200 Subject: [PATCH 0223/2268] refactor: Store rootDir, relDir and absDir in DeploymentProject --- pkg/deployment/deployment_collection.go | 2 +- pkg/deployment/deployment_item.go | 20 ++----- pkg/deployment/deployment_project.go | 75 ++++++++++++------------- pkg/kluctl_project/target_context.go | 2 +- 4 files changed, 43 insertions(+), 56 deletions(-) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index d25503c77..04b5c77a0 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -59,7 +59,7 @@ func findDeploymentItemIndex(project *DeploymentProject, pth *string, indexes ma } var dir2 *string index := 0 - dir := filepath.Join(project.dir, *pth) + dir := filepath.Join(project.absDir, *pth) absDir, err := filepath.Abs(dir) if err != nil { // we pre-checked directories, so this should not happen diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index b07ff59a9..457998d63 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -36,7 +36,6 @@ type DeploymentItem struct { Objects []*uo.UnstructuredObject Tags *utils.OrderedMap - relProjectDir string RelToRootItemDir string RelToProjectItemDir string relRenderedDir string @@ -60,20 +59,13 @@ func NewDeploymentItem(ctx SharedContext, project *DeploymentProject, collection di.Tags = di.Project.getTags() di.Tags.SetMultiple(di.Config.Tags, true) - rootProject := di.Project.getRootProject() - - di.relProjectDir, err = filepath.Rel(rootProject.dir, di.Project.dir) - if err != nil { - return nil, err - } - if di.dir != nil { - di.RelToRootItemDir, err = filepath.Rel(rootProject.dir, *di.dir) + di.RelToRootItemDir, err = filepath.Rel(di.Project.rootDir, *di.dir) if err != nil { return nil, err } - di.RelToProjectItemDir, err = filepath.Rel(di.relProjectDir, di.RelToRootItemDir) + di.RelToProjectItemDir, err = filepath.Rel(di.Project.relDir, di.RelToRootItemDir) if err != nil { return nil, err } @@ -115,8 +107,6 @@ func (di *DeploymentItem) render(forSeal bool, wp *utils.WorkerPoolWithErrors) e return nil } - rootDir := di.Project.getRootProject().dir - err := os.MkdirAll(di.renderedDir, 0o700) if err != nil { return err @@ -157,7 +147,7 @@ func (di *DeploymentItem) render(forSeal bool, wp *utils.WorkerPoolWithErrors) e } wp.Submit(func() error { - return varsCtx.RenderDirectory(rootDir, di.Project.getRenderSearchDirs(), di.relProjectDir, excludePatterns, di.RelToProjectItemDir, di.renderedDir) + return varsCtx.RenderDirectory(di.Project.rootDir, di.Project.getRenderSearchDirs(), di.Project.relDir, excludePatterns, di.RelToProjectItemDir, di.renderedDir) }) return nil @@ -220,7 +210,7 @@ func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { renderedDir := filepath.Join(di.renderedDir, subdir) // ensure we're not leaving the project - _, err := securejoin.SecureJoin(di.Project.getRootProject().dir, subdir) + _, err := securejoin.SecureJoin(di.Project.rootDir, subdir) if err != nil { return err } @@ -251,7 +241,7 @@ func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { } fname := filepath.Base(p) - baseError := fmt.Sprintf("failed to resolve SealedSecret %s", filepath.Clean(filepath.Join(di.Project.dir, resource))) + baseError := fmt.Sprintf("failed to resolve SealedSecret %s", filepath.Clean(filepath.Join(di.Project.absDir, resource))) // ensure we're not leaving the .sealed-secrets dir sourcePath, err := securejoin.SecureJoin(baseSourcePath, filepath.Join(di.relRenderedDir, subdir, relDir, sealedSecretsDir, fname)) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 96985f625..e125d54b9 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -2,6 +2,7 @@ package deployment import ( "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -16,7 +17,9 @@ type DeploymentProject struct { VarsCtx *vars.VarsCtx - dir string + rootDir string + relDir string + absDir string Config types.DeploymentProjectConfig @@ -26,20 +29,28 @@ type DeploymentProject struct { parentProjectInclude *types.DeploymentItemConfig } -func NewDeploymentProject(ctx SharedContext, varsCtx *vars.VarsCtx, dir string, parentProject *DeploymentProject) (*DeploymentProject, error) { +func NewDeploymentProject(ctx SharedContext, varsCtx *vars.VarsCtx, rootDir string, relDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { dp := &DeploymentProject{ ctx: ctx, VarsCtx: varsCtx.Copy(), - dir: dir, + rootDir: rootDir, + relDir: relDir, parentProject: parentProject, includes: map[int]*DeploymentProject{}, } + dir, err := securejoin.SecureJoin(dp.rootDir, dp.relDir) + if err != nil { + return nil, err + } + if !utils.IsDirectory(dir) { return nil, fmt.Errorf("%s does not exist or is not a directory", dir) } - err := dp.loadConfig() + dp.absDir = dir + + err = dp.loadConfig() if err != nil { return nil, fmt.Errorf("failed to load deployment config for %s: %w", dir, err) } @@ -57,15 +68,15 @@ func (p *DeploymentProject) loadVarsList(varsCtx *vars.VarsCtx, varsList []*type } func (p *DeploymentProject) loadConfig() error { - configPath := filepath.Join(p.dir, "deployment.yml") + configPath := filepath.Join(p.absDir, "deployment.yml") if !yaml.Exists(configPath) { - if yaml.Exists(filepath.Join(p.dir, "kustomization.yml")) { - return fmt.Errorf("deployment.yml not found but folder %s contains a kustomization.yml", p.dir) + if yaml.Exists(filepath.Join(p.absDir, "kustomization.yml")) { + return fmt.Errorf("deployment.yml not found but folder %s contains a kustomization.yml", p.absDir) } - return fmt.Errorf("%s not found", p.dir) + return fmt.Errorf("%s not found", configPath) } - err := p.VarsCtx.RenderYamlFile(yaml.FixNameExt(p.dir, "deployment.yml"), p.getRenderSearchDirs(), &p.Config) + err := p.VarsCtx.RenderYamlFile(yaml.FixNameExt(p.absDir, "deployment.yml"), p.getRenderSearchDirs(), &p.Config) if err != nil { return fmt.Errorf("failed to load deployment.yml: %w", err) } @@ -104,41 +115,28 @@ func (p *DeploymentProject) loadConfig() error { } func (p *DeploymentProject) checkDeploymentDirs() error { - rootProject := p.getRootProject() for _, di := range p.Config.Deployments { - if di.Path == nil && di.Include == nil { + if di.Path == nil { continue } - var pth string - if di.Path != nil { - pth = *di.Path - } else { - pth = *di.Include - } - - diDir := filepath.Join(p.dir, pth) - diDir, err := filepath.Abs(diDir) + diDir, err := securejoin.SecureJoin(p.absDir, *di.Path) if err != nil { return err } - if !strings.HasPrefix(diDir, rootProject.dir) { - return fmt.Errorf("path/include is not part of root deployment project: %s", pth) + if !strings.HasPrefix(diDir, p.rootDir) { + return fmt.Errorf("path/include is not part of the deployment project: %s", *di.Path) } if !utils.Exists(diDir) { - return fmt.Errorf("deployment directory does not exist: %s", pth) + return fmt.Errorf("deployment directory does not exist: %s", *di.Path) } if !utils.IsDirectory(diDir) { - return fmt.Errorf("deployment path is not a directory: %s", pth) + return fmt.Errorf("deployment path is not a directory: %s", *di.Path) } - if di.Path != nil { - pth = yaml.FixPathExt(filepath.Join(diDir, "kustomization.yml")) - } else { - pth = yaml.FixPathExt(filepath.Join(diDir, "deployment.yml")) - } + pth := yaml.FixPathExt(filepath.Join(diDir, "kustomization.yml")) if !utils.IsFile(pth) { return fmt.Errorf("%s not found or not a file", pth) } @@ -152,9 +150,7 @@ func (p *DeploymentProject) loadIncludes() error { continue } - incDir := filepath.Join(p.dir, *inc.Include) - - newProject, err := p.loadLocalInclude(incDir, inc.Vars) + newProject, err := p.loadLocalInclude(p.rootDir, filepath.Join(p.relDir, *inc.Include), inc.Vars) if err != nil { return err } @@ -165,14 +161,14 @@ func (p *DeploymentProject) loadIncludes() error { return nil } -func (p *DeploymentProject) loadLocalInclude(incDir string, vars []*types.VarsSource) (*DeploymentProject, error) { +func (p *DeploymentProject) loadLocalInclude(rootDir string, incDir string, vars []*types.VarsSource) (*DeploymentProject, error) { varsCtx := p.VarsCtx.Copy() err := p.loadVarsList(varsCtx, vars) if err != nil { return nil, err } - newProject, err := NewDeploymentProject(p.ctx, varsCtx, incDir, p) + newProject, err := NewDeploymentProject(p.ctx, varsCtx, rootDir, incDir, p) if err != nil { return nil, err } @@ -181,11 +177,12 @@ func (p *DeploymentProject) loadLocalInclude(incDir string, vars []*types.VarsSo } func (p *DeploymentProject) getSealedSecretsDir() string { - root := p.getRootProject() - if root.Config.SealedSecrets == nil || root.Config.SealedSecrets.OutputPattern == nil { - return p.ctx.DefaultSealedSecretsOutputPattern + for _, x := range p.getParents() { + if x.p.Config.SealedSecrets != nil && x.p.Config.SealedSecrets.OutputPattern != nil { + return *x.p.Config.SealedSecrets.OutputPattern + } } - return *root.Config.SealedSecrets.OutputPattern + return p.ctx.DefaultSealedSecretsOutputPattern } func (p *DeploymentProject) getRootProject() *DeploymentProject { @@ -229,7 +226,7 @@ func (p *DeploymentProject) getChildren(recursive bool, includeSelf bool) []*Dep func (p *DeploymentProject) getRenderSearchDirs() []string { var ret []string for _, d := range p.getParents() { - ret = append(ret, d.p.dir) + ret = append(ret, d.p.absDir) } return ret } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 79fccfc7d..c04bdd73b 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -83,7 +83,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s DefaultSealedSecretsOutputPattern: targetName, } - d, err := deployment.NewDeploymentProject(dctx, varsCtx, deploymentDir, nil) + d, err := deployment.NewDeploymentProject(dctx, varsCtx, deploymentDir, ".", nil) if err != nil { return nil, err } From ad0be5ca8b5253f95593e5e834fa1e685ae904f4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 10:41:54 +0200 Subject: [PATCH 0224/2268] refactor: Rename getSealedSecretsDir to getRenderedOutputPattern --- pkg/deployment/deployment_item.go | 2 +- pkg/deployment/deployment_project.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 457998d63..8f2afbb25 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -204,7 +204,7 @@ func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { return nil } - sealedSecretsDir := di.Project.getSealedSecretsDir() + sealedSecretsDir := di.Project.getRenderedOutputPattern() baseSourcePath := di.Project.ctx.SealedSecretsDir renderedDir := filepath.Join(di.renderedDir, subdir) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index e125d54b9..92a022fac 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -176,7 +176,7 @@ func (p *DeploymentProject) loadLocalInclude(rootDir string, incDir string, vars return newProject, nil } -func (p *DeploymentProject) getSealedSecretsDir() string { +func (p *DeploymentProject) getRenderedOutputPattern() string { for _, x := range p.getParents() { if x.p.Config.SealedSecrets != nil && x.p.Config.SealedSecrets.OutputPattern != nil { return *x.p.Config.SealedSecrets.OutputPattern From d158b94fb48cc98c360acd060a8bc0abaa99a399 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 11:19:49 +0200 Subject: [PATCH 0225/2268] feat: Support including deployment projects from git --- pkg/deployment/deployment_collection.go | 2 +- pkg/deployment/deployment_project.go | 25 ++++++++--- pkg/deployment/shared_context.go | 2 + pkg/git/repo_collection.go | 58 ++++++++++++++++++++++++- pkg/kluctl_project/target_context.go | 1 + pkg/types/deployment.go | 15 ++++++- 6 files changed, 91 insertions(+), 12 deletions(-) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 04b5c77a0..698dba2bf 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -79,7 +79,7 @@ func (c *DeploymentCollection) collectDeployments(project *DeploymentProject, in var ret []*DeploymentItem for i, diConfig := range project.Config.Deployments { - if diConfig.Include != nil { + if diConfig.Include != nil || diConfig.Git != nil { includedProject, ok := project.includes[i] if !ok { panic(fmt.Sprintf("Did not find find index %d in project.includes", i)) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 92a022fac..f95deb25d 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -146,15 +146,26 @@ func (p *DeploymentProject) checkDeploymentDirs() error { func (p *DeploymentProject) loadIncludes() error { for i, inc := range p.Config.Deployments { - if inc.Include == nil { + var err error + var newProject *DeploymentProject + + if inc.Include != nil { + newProject, err = p.loadLocalInclude(p.rootDir, filepath.Join(p.relDir, *inc.Include), inc.Vars) + if err != nil { + return err + } + } else if inc.Git != nil { + cloneDir, err := p.ctx.GRC.GetClonedDir(inc.Git.Url, inc.Git.Ref, true, true, true) + if err != nil { + return err + } + newProject, err = p.loadLocalInclude(cloneDir, inc.Git.SubDir, inc.Vars) + if err != nil { + return err + } + } else { continue } - - newProject, err := p.loadLocalInclude(p.rootDir, filepath.Join(p.relDir, *inc.Include), inc.Vars) - if err != nil { - return err - } - newProject.parentProjectInclude = inc p.includes[i] = newProject } diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index 9ee8dd233..59d0ebb07 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -2,6 +2,7 @@ package deployment import ( "context" + "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/vars" ) @@ -9,6 +10,7 @@ import ( type SharedContext struct { Ctx context.Context K *k8s.K8sCluster + GRC *git.MirroredGitRepoCollection VarsLoader *vars.VarsLoader RenderDir string diff --git a/pkg/git/repo_collection.go b/pkg/git/repo_collection.go index 0269bf69f..dd31d890f 100644 --- a/pkg/git/repo_collection.go +++ b/pkg/git/repo_collection.go @@ -5,6 +5,11 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/utils" + "io/ioutil" + "os" + "path" + "path/filepath" "sync" "time" ) @@ -19,6 +24,7 @@ type MirroredGitRepoCollection struct { type entry struct { mr *MirroredGitRepo + clonedDirs map[string]string updateMutex sync.Mutex } @@ -36,6 +42,10 @@ func (g *MirroredGitRepoCollection) Clear() { defer g.mutex.Unlock() for _, e := range g.repos { + for _, path := range e.clonedDirs { + _ = os.RemoveAll(path) + } + if e.mr.IsLocked() { _ = e.mr.Unlock() } @@ -44,7 +54,7 @@ func (g *MirroredGitRepoCollection) Clear() { g.repos = map[string]*entry{} } -func (g *MirroredGitRepoCollection) GetMirroredGitRepo(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*MirroredGitRepo, error) { +func (g *MirroredGitRepoCollection) getEntry(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*entry, error) { e, err := func() (*entry, error) { g.mutex.Lock() defer g.mutex.Unlock() @@ -59,7 +69,8 @@ func (g *MirroredGitRepoCollection) GetMirroredGitRepo(url git_url.GitUrl, allow return nil, err } e = &entry{ - mr: mr, + mr: mr, + clonedDirs: map[string]string{}, } g.repos[url.NormalizedRepoKey()] = e @@ -90,5 +101,48 @@ func (g *MirroredGitRepoCollection) GetMirroredGitRepo(url git_url.GitUrl, allow } } + return e, nil +} + +func (g *MirroredGitRepoCollection) GetMirroredGitRepo(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*MirroredGitRepo, error) { + e, err := g.getEntry(url, allowCreate, lockRepo, update) + if err != nil { + return nil, err + } return e.mr, nil } + +func (g *MirroredGitRepoCollection) GetClonedDir(url git_url.GitUrl, ref string, allowCreate bool, lockRepo bool, update bool) (string, error) { + e, err := g.getEntry(url, allowCreate, lockRepo, update) + if err != nil { + return "", err + } + + e.updateMutex.Lock() + defer e.updateMutex.Unlock() + + p, ok := e.clonedDirs[ref] + if ok { + return p, nil + } + + tmpDir := filepath.Join(utils.GetTmpBaseDir(), "git-cloned") + err = os.MkdirAll(tmpDir, 0700) + if err != nil { + return "", err + } + + repoName := path.Base(url.Normalize().Path) + p, err = ioutil.TempDir(tmpDir, repoName+"-"+ref+"-") + if err != nil { + return "", err + } + + err = e.mr.CloneProject(ref, p) + if err != nil { + return "", err + } + + e.clonedDirs[ref] = p + return p, nil +} diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index c04bdd73b..c0db4b7e9 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -77,6 +77,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s dctx := deployment.SharedContext{ Ctx: ctx, K: k, + GRC: p.GRC, VarsLoader: varsLoader, RenderDir: renderOutputDir, SealedSecretsDir: p.sealedSecretsDir, diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index b9db6bb96..6f2ccd6fa 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -8,6 +8,7 @@ import ( type DeploymentItemConfig struct { Path *string `yaml:"path,omitempty"` Include *string `yaml:"include,omitempty"` + Git *GitProject `yaml:"git,omitempty"` Tags []string `yaml:"tags,omitempty"` Barrier bool `yaml:"barrier,omitempty"` WaitReadiness bool `yaml:"waitReadiness,omitempty"` @@ -20,8 +21,18 @@ type DeploymentItemConfig struct { func ValidateDeploymentItemConfig(sl validator.StructLevel) { s := sl.Current().Interface().(DeploymentItemConfig) - if s.Path != nil && s.Include != nil { - sl.ReportError(s, "path", "Path", "path and include can not be set at the same time", "") + cnt := 0 + if s.Path != nil { + cnt += 1 + } + if s.Include != nil { + cnt += 1 + } + if s.Git != nil { + cnt += 1 + } + if cnt > 1 { + sl.ReportError(s, "self", "self", "only one of path, include and git can be set at the same time", "") } if s.Path == nil && s.WaitReadiness { sl.ReportError(s, "waitReadiness", "WaitReadiness", "only kustomize deployments are allowed to have waitReadiness set", "") From 8b2b600504c54f0c054ae1ad3f1823d850ec12e8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 11:41:19 +0200 Subject: [PATCH 0226/2268] fix: Introduce Source object in deployment projects and render per source id This fixes issues where multiple included git projects would render into the same directory. --- cmd/kluctl/commands/completion.go | 2 +- pkg/deployment/deployment_item.go | 18 ++++++++--------- pkg/deployment/deployment_project.go | 26 ++++++++++++++----------- pkg/deployment/source.go | 29 ++++++++++++++++++++++++++++ pkg/kluctl_project/target_context.go | 2 +- pkg/utils/fs.go | 4 ++++ 6 files changed, 59 insertions(+), 22 deletions(-) create mode 100644 pkg/deployment/source.go diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 5b94c0964..3c8ae9e37 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -154,7 +154,7 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd defer mutex.Unlock() for _, di := range ctx.targetCtx.DeploymentCollection.Deployments { tags.Merge(di.Tags) - deploymentItemDirs.Set(di.RelToRootItemDir, true) + deploymentItemDirs.Set(di.RelToSourceItemDir, true) } return nil }) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 8f2afbb25..f0e89f5ff 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -36,7 +36,7 @@ type DeploymentItem struct { Objects []*uo.UnstructuredObject Tags *utils.OrderedMap - RelToRootItemDir string + RelToSourceItemDir string RelToProjectItemDir string relRenderedDir string renderedDir string @@ -60,22 +60,22 @@ func NewDeploymentItem(ctx SharedContext, project *DeploymentProject, collection di.Tags.SetMultiple(di.Config.Tags, true) if di.dir != nil { - di.RelToRootItemDir, err = filepath.Rel(di.Project.rootDir, *di.dir) + di.RelToSourceItemDir, err = filepath.Rel(di.Project.source.dir, *di.dir) if err != nil { return nil, err } - di.RelToProjectItemDir, err = filepath.Rel(di.Project.relDir, di.RelToRootItemDir) + di.RelToProjectItemDir, err = filepath.Rel(di.Project.relDir, di.RelToSourceItemDir) if err != nil { return nil, err } - di.relRenderedDir = di.RelToRootItemDir + di.relRenderedDir = di.RelToSourceItemDir if di.index != 0 { di.relRenderedDir = fmt.Sprintf("%s-%d", di.relRenderedDir, di.index) } - di.renderedDir = filepath.Join(collection.ctx.RenderDir, di.relRenderedDir) + di.renderedDir = filepath.Join(collection.ctx.RenderDir, di.Project.source.id, di.relRenderedDir) di.renderedYamlPath = filepath.Join(di.renderedDir, ".rendered.yml") } return di, nil @@ -94,7 +94,7 @@ func (di *DeploymentItem) getCommonLabels() map[string]string { func (di *DeploymentItem) getCommonAnnotations() map[string]string { // TODO change it to kluctl.io/deployment_dir a := map[string]string{ - "kluctl.io/kustomize_dir": filepath.ToSlash(di.RelToRootItemDir), + "kluctl.io/kustomize_dir": filepath.ToSlash(di.RelToSourceItemDir), } if di.Config.SkipDeleteIfTags { a["kluctl.io/skip-delete-if-tags"] = "true" @@ -147,7 +147,7 @@ func (di *DeploymentItem) render(forSeal bool, wp *utils.WorkerPoolWithErrors) e } wp.Submit(func() error { - return varsCtx.RenderDirectory(di.Project.rootDir, di.Project.getRenderSearchDirs(), di.Project.relDir, excludePatterns, di.RelToProjectItemDir, di.renderedDir) + return varsCtx.RenderDirectory(di.Project.source.dir, di.Project.getRenderSearchDirs(), di.Project.relDir, excludePatterns, di.RelToProjectItemDir, di.renderedDir) }) return nil @@ -210,7 +210,7 @@ func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { renderedDir := filepath.Join(di.renderedDir, subdir) // ensure we're not leaving the project - _, err := securejoin.SecureJoin(di.Project.rootDir, subdir) + err := utils.CheckSubInDir(di.Project.source.dir, subdir) if err != nil { return err } @@ -271,7 +271,7 @@ func (di *DeploymentItem) buildInclusionEntries() []utils.InclusionEntry { values = append(values, utils.InclusionEntry{Type: "tag", Value: t}) } if di.dir != nil { - dir := filepath.ToSlash(di.RelToRootItemDir) + dir := filepath.ToSlash(di.RelToSourceItemDir) values = append(values, utils.InclusionEntry{Type: "deploymentItemDir", Value: dir}) } return values diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index f95deb25d..b814ad864 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -17,9 +17,9 @@ type DeploymentProject struct { VarsCtx *vars.VarsCtx - rootDir string - relDir string - absDir string + source Source + relDir string + absDir string Config types.DeploymentProjectConfig @@ -29,17 +29,17 @@ type DeploymentProject struct { parentProjectInclude *types.DeploymentItemConfig } -func NewDeploymentProject(ctx SharedContext, varsCtx *vars.VarsCtx, rootDir string, relDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { +func NewDeploymentProject(ctx SharedContext, varsCtx *vars.VarsCtx, source Source, relDir string, parentProject *DeploymentProject) (*DeploymentProject, error) { dp := &DeploymentProject{ ctx: ctx, VarsCtx: varsCtx.Copy(), - rootDir: rootDir, + source: source, relDir: relDir, parentProject: parentProject, includes: map[int]*DeploymentProject{}, } - dir, err := securejoin.SecureJoin(dp.rootDir, dp.relDir) + dir, err := securejoin.SecureJoin(dp.source.dir, dp.relDir) if err != nil { return nil, err } @@ -125,7 +125,7 @@ func (p *DeploymentProject) checkDeploymentDirs() error { return err } - if !strings.HasPrefix(diDir, p.rootDir) { + if !strings.HasPrefix(diDir, p.source.dir) { return fmt.Errorf("path/include is not part of the deployment project: %s", *di.Path) } @@ -150,7 +150,7 @@ func (p *DeploymentProject) loadIncludes() error { var newProject *DeploymentProject if inc.Include != nil { - newProject, err = p.loadLocalInclude(p.rootDir, filepath.Join(p.relDir, *inc.Include), inc.Vars) + newProject, err = p.loadLocalInclude(p.source, filepath.Join(p.relDir, *inc.Include), inc.Vars) if err != nil { return err } @@ -159,7 +159,7 @@ func (p *DeploymentProject) loadIncludes() error { if err != nil { return err } - newProject, err = p.loadLocalInclude(cloneDir, inc.Git.SubDir, inc.Vars) + newProject, err = p.loadLocalInclude(NewSource(cloneDir), inc.Git.SubDir, inc.Vars) if err != nil { return err } @@ -172,14 +172,14 @@ func (p *DeploymentProject) loadIncludes() error { return nil } -func (p *DeploymentProject) loadLocalInclude(rootDir string, incDir string, vars []*types.VarsSource) (*DeploymentProject, error) { +func (p *DeploymentProject) loadLocalInclude(source Source, incDir string, vars []*types.VarsSource) (*DeploymentProject, error) { varsCtx := p.VarsCtx.Copy() err := p.loadVarsList(varsCtx, vars) if err != nil { return nil, err } - newProject, err := NewDeploymentProject(p.ctx, varsCtx, rootDir, incDir, p) + newProject, err := NewDeploymentProject(p.ctx, varsCtx, source, incDir, p) if err != nil { return nil, err } @@ -237,6 +237,10 @@ func (p *DeploymentProject) getChildren(recursive bool, includeSelf bool) []*Dep func (p *DeploymentProject) getRenderSearchDirs() []string { var ret []string for _, d := range p.getParents() { + if d.p.source != p.source { + // only allow searching inside same source project + continue + } ret = append(ret, d.p.absDir) } return ret diff --git a/pkg/deployment/source.go b/pkg/deployment/source.go new file mode 100644 index 000000000..0792ea8b3 --- /dev/null +++ b/pkg/deployment/source.go @@ -0,0 +1,29 @@ +package deployment + +import ( + "fmt" + "sync" +) + +type Source struct { + id string + dir string +} + +func NewSource(dir string) Source { + return Source{ + id: getNextSourceId(), + dir: dir, + } +} + +var nextSourceId int +var nextSourceIdMutex sync.Mutex + +func getNextSourceId() string { + nextSourceIdMutex.Lock() + defer nextSourceIdMutex.Unlock() + id := fmt.Sprintf("%d", nextSourceId) + nextSourceId += 1 + return id +} diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index c0db4b7e9..b8a103e7d 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -84,7 +84,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s DefaultSealedSecretsOutputPattern: targetName, } - d, err := deployment.NewDeploymentProject(dctx, varsCtx, deploymentDir, ".", nil) + d, err := deployment.NewDeploymentProject(dctx, varsCtx, deployment.NewSource(deploymentDir), ".", nil) if err != nil { return nil, err } diff --git a/pkg/utils/fs.go b/pkg/utils/fs.go index ac567cdb8..0b25e1e4b 100644 --- a/pkg/utils/fs.go +++ b/pkg/utils/fs.go @@ -57,6 +57,10 @@ func CheckInDir(root string, path string) error { return nil } +func CheckSubInDir(root string, subDir string) error { + return CheckInDir(root, filepath.Join(root, subDir)) +} + func Touch(path string) error { f, err := os.Create(path) if err != nil { From cef51fae878e40d2d2e74b66e3a57a8d1ce93009 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 11:47:37 +0200 Subject: [PATCH 0227/2268] feat: Deprecate external projects and warn about future removal --- cmd/kluctl/args/project.go | 8 ++++---- pkg/kluctl_project/project_load.go | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index f1b35c3fb..c9f9de378 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -7,13 +7,13 @@ type ProjectFlags struct { ProjectRef string `group:"project" short:"b" help:"Git ref of the kluctl project. Only used when --project-url was given."` ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` - LocalClusters existingDirType `group:"project" help:"Local clusters directory. Overrides the project from .kluctl.yaml"` - LocalDeployment existingDirType `group:"project" help:"Local deployment directory. Overrides the project from .kluctl.yaml"` - LocalSealedSecrets existingDirType `group:"project" help:"Local sealed-secrets directory. Overrides the project from .kluctl.yaml" ` + LocalClusters existingDirType `group:"project" help:"DEPRECATED. Local clusters directory. Overrides the project from .kluctl.yaml"` + LocalDeployment existingDirType `group:"project" help:"DEPRECATED. Local deployment directory. Overrides the project from .kluctl.yaml"` + LocalSealedSecrets existingDirType `group:"project" help:"DEPRECATED. Local sealed-secrets directory. Overrides the project from .kluctl.yaml" ` FromArchive existingPathType `group:"project" help:"Load project (.kluctl.yaml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." exts:"tar.gz,tgz"` FromArchiveMetadata existingFileType `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." exts:"yml,yaml"` OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` - Cluster string `group:"project" help:"Specify/Override cluster"` + Cluster string `group:"project" help:"DEPRECATED. Specify/Override cluster"` Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index f241a485d..bbab0983b 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -131,6 +131,11 @@ func (c *LoadedKluctlProject) loadExternalProject(ep *types2.ExternalProject, de } if ep.Project != nil { + c.warnOnce.Do("external-projects", func() { + status.Warning(c.ctx, "External projects are deprecated and support for them will be removed in the future. "+ + "Use Git variable sources as replacement for cluster configs and Git includes as replacement for external deployment projects.") + }) + // pointing to an actual external project, so let's try to clone it return c.loadGitProject(ep.Project, defaultGitSubDir, true) } @@ -192,6 +197,16 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { s := status.Start(c.ctx, "Loading kluctl project") defer s.Failed() + if c.loadArgs.LocalClusters != "" { + status.Warning(c.ctx, "--local-clusters is deprecated and will be removed in an upcoming version. Use variables loaded from git instead.") + } + if c.loadArgs.LocalDeployment != "" { + status.Warning(c.ctx, "--local-deployment is deprecated and will be removed in an upcoming version. Use git includes instead.") + } + if c.loadArgs.LocalSealedSecrets != "" { + status.Warning(c.ctx, "--local-sealed-secrets is deprecated and will be removed in an upcoming version.") + } + deploymentInfo, err := c.loadExternalProject(c.Config.Deployment, "", c.loadArgs.LocalDeployment) if err != nil { return err @@ -202,10 +217,8 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { } var clustersInfos []gitProjectInfo if c.loadArgs.LocalClusters != "" { - status.Warning(c.ctx, "--local-clusters is deprecated and will be removed in an upcoming version. Use variables loaded from git instead.") clustersInfos = append(clustersInfos, c.localProject(c.loadArgs.LocalClusters)) } else if len(c.Config.Clusters.Projects) != 0 { - status.Warning(c.ctx, "'clusters' is deprecated and will be removed in an upcoming version. Use variables loaded from git instead.") for _, ep := range c.Config.Clusters.Projects { info, err := c.loadExternalProject(&ep, "clusters", "") if err != nil { From 2e821a272c149dea34461f1130ed2ac89412dc87 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 11:54:10 +0200 Subject: [PATCH 0228/2268] fix: Mark --output-archive as required flag --- cmd/kluctl/commands/cmd_archive.go | 2 +- cmd/kluctl/commands/cobra_utils.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/cmd_archive.go b/cmd/kluctl/commands/cmd_archive.go index d5c22af07..4e8fe72bc 100644 --- a/cmd/kluctl/commands/cmd_archive.go +++ b/cmd/kluctl/commands/cmd_archive.go @@ -9,7 +9,7 @@ import ( type archiveCmd struct { args.ProjectFlags - OutputArchive string `group:"misc" help:"Path to .tgz to write project to." type:"path"` + OutputArchive string `group:"misc" help:"Path to .tgz to write project to." type:"path" required:"true"` } func (cmd *archiveCmd) Help() string { diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index 493b15605..b8fae0c44 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -143,6 +143,7 @@ func (c *rootCommand) buildCobraArg(cg *commandAndGroups, f reflect.StructField, help := f.Tag.Get("help") shortFlag := f.Tag.Get("short") defaultValue := f.Tag.Get("default") + required := f.Tag.Get("required") == "true" group := f.Tag.Get("group") if group != "" { @@ -208,6 +209,10 @@ func (c *rootCommand) buildCobraArg(cg *commandAndGroups, f reflect.StructField, return fmt.Errorf("unknown type %s", f.Type.Name()) } + if required { + _ = cg.cmd.MarkPersistentFlagRequired(name) + } + return nil } From ba98984e8c9dd9fd7bfb9d85acfe3cf8c1237702 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 12:16:53 +0200 Subject: [PATCH 0229/2268] refactor: Use GRC when cloning kluctl project related repos --- pkg/deployment/deployment_project.go | 2 +- pkg/git/repo_collection.go | 19 +++++++---- pkg/kluctl_project/git.go | 49 ---------------------------- pkg/kluctl_project/project_load.go | 49 +++++----------------------- pkg/kluctl_project/targets.go | 4 ++- 5 files changed, 25 insertions(+), 98 deletions(-) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index b814ad864..93290a708 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -155,7 +155,7 @@ func (p *DeploymentProject) loadIncludes() error { return err } } else if inc.Git != nil { - cloneDir, err := p.ctx.GRC.GetClonedDir(inc.Git.Url, inc.Git.Ref, true, true, true) + cloneDir, _, err := p.ctx.GRC.GetClonedDir(inc.Git.Url, inc.Git.Ref, true, true, true) if err != nil { return err } diff --git a/pkg/git/repo_collection.go b/pkg/git/repo_collection.go index dd31d890f..095b7ffb8 100644 --- a/pkg/git/repo_collection.go +++ b/pkg/git/repo_collection.go @@ -112,10 +112,10 @@ func (g *MirroredGitRepoCollection) GetMirroredGitRepo(url git_url.GitUrl, allow return e.mr, nil } -func (g *MirroredGitRepoCollection) GetClonedDir(url git_url.GitUrl, ref string, allowCreate bool, lockRepo bool, update bool) (string, error) { +func (g *MirroredGitRepoCollection) GetClonedDir(url git_url.GitUrl, ref string, allowCreate bool, lockRepo bool, update bool) (string, GitRepoInfo, error) { e, err := g.getEntry(url, allowCreate, lockRepo, update) if err != nil { - return "", err + return "", GitRepoInfo{}, err } e.updateMutex.Lock() @@ -123,26 +123,31 @@ func (g *MirroredGitRepoCollection) GetClonedDir(url git_url.GitUrl, ref string, p, ok := e.clonedDirs[ref] if ok { - return p, nil + return p, GitRepoInfo{}, nil } tmpDir := filepath.Join(utils.GetTmpBaseDir(), "git-cloned") err = os.MkdirAll(tmpDir, 0700) if err != nil { - return "", err + return "", GitRepoInfo{}, err } repoName := path.Base(url.Normalize().Path) p, err = ioutil.TempDir(tmpDir, repoName+"-"+ref+"-") if err != nil { - return "", err + return "", GitRepoInfo{}, err } err = e.mr.CloneProject(ref, p) if err != nil { - return "", err + return "", GitRepoInfo{}, err + } + + repoInfo, err := GetGitRepoInfo(p) + if err != nil { + return "", GitRepoInfo{}, err } e.clonedDirs[ref] = p - return p, nil + return p, repoInfo, nil } diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 3d3a2584a..11c409846 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -2,15 +2,10 @@ package kluctl_project import ( "fmt" - "github.com/kluctl/kluctl/v2/pkg/git" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" types2 "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils" - "os" - "path/filepath" "reflect" "sort" - "strings" "sync" ) @@ -81,50 +76,6 @@ func (c *LoadedKluctlProject) updateGitCaches() error { return firstError } -func (c *LoadedKluctlProject) cloneGitProject(gitProject *types2.GitProject, targetDir string) (info git.GitRepoInfo, err error) { - err = os.MkdirAll(filepath.Join(c.TmpDir, "git"), 0o700) - if err != nil { - return - } - - mr, err := c.GRC.GetMirroredGitRepo(gitProject.Url, true, true, true) - if err != nil { - return git.GitRepoInfo{}, err - } - - err = mr.CloneProject(gitProject.Ref, targetDir) - if err != nil { - return - } - - info, err = git.GetGitRepoInfo(targetDir) - return -} - -func (c *LoadedKluctlProject) buildCloneDir(u git_url.GitUrl, ref string) (string, error) { - baseDir := c.TmpDir - if c.archiveDir != "" { - baseDir = c.archiveDir - } - baseDir = filepath.Join(baseDir, "git") - - if ref == "" { - ref = "HEAD" - } - ref = strings.ReplaceAll(ref, "/", "-") - ref = strings.ReplaceAll(ref, "\\", "-") - urlPath := filepath.FromSlash(u.Path) - baseName := filepath.Base(urlPath) - urlHash := utils.Sha256String(fmt.Sprintf("%s:%s", u.Host, u.Path))[:16] - cloneDir := filepath.Join(fmt.Sprintf("%s-%s", baseName, urlHash), ref) - cloneDir = filepath.Join(baseDir, cloneDir) - err := utils.CheckInDir(baseDir, cloneDir) - if err != nil { - return "", err - } - return cloneDir, nil -} - func (c *LoadedKluctlProject) addInvolvedRepo(u git_url.GitUrl, refPattern string, refs map[string]string) { repoKey := u.NormalizedRepoKey() irs, _ := c.involvedRepos[repoKey] diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index bbab0983b..9a0f231cd 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -59,51 +59,20 @@ func (c *LoadedKluctlProject) localProject(dir string) gitProjectInfo { } func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defaultSubDir string, doAddInvolvedRepo bool) (ret gitProjectInfo, err error) { - cloneDir, err := c.buildCloneDir(gitProject.Url, gitProject.Ref) + cloneDir, ri, err := c.GRC.GetClonedDir(gitProject.Url, gitProject.Ref, c.loadArgs.AllowGitClone, true, true) if err != nil { return } - if c.archiveDir != "" { - var md types2.GitRepoMetadata - err = yaml.ReadYamlFile(filepath.Join(cloneDir, ".git-metadata.yaml"), &md) - if err != nil { - return - } - ret.url = gitProject.Url - ret.ref = gitProject.Ref - ret.commit = md.Commit - ret.repoRoot = cloneDir - } else { - if !c.loadArgs.AllowGitClone { - err = fmt.Errorf("tried to load an external project from git, which is not allowed") - return - } - - var ri git.GitRepoInfo - ri, err = c.cloneGitProject(gitProject, cloneDir) - if err != nil { - return - } + ret.url = gitProject.Url + ret.ref = ri.CheckedOutRef + ret.commit = ri.CheckedOutCommit + ret.repoRoot = cloneDir - var md types2.GitRepoMetadata - md.Ref = ri.CheckedOutRef - md.Commit = ri.CheckedOutCommit - err = yaml.WriteYamlFile(filepath.Join(cloneDir, ".git-metadata.yaml"), &md) - if err != nil { - return gitProjectInfo{}, err - } - - ret.url = gitProject.Url - ret.ref = ri.CheckedOutRef - ret.commit = ri.CheckedOutCommit - ret.repoRoot = cloneDir - - if doAddInvolvedRepo { - c.addInvolvedRepo(ret.url, ret.ref, map[string]string{ - ret.ref: ret.commit, - }) - } + if doAddInvolvedRepo { + c.addInvolvedRepo(ret.url, ret.ref, map[string]string{ + ret.ref: ret.commit, + }) } subDir := gitProject.SubDir diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index d8a20bcef..b760c01d1 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -178,7 +178,7 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Ta continue } - cloneDir, err := c.buildCloneDir(baseTarget.TargetConfig.Project.Url, refShortName) + cloneDir, _, err := c.GRC.GetClonedDir(baseTarget.TargetConfig.Project.Url, refShortName, false, false, false) if err != nil { return nil, err } @@ -236,7 +236,9 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTarge if _, ok := uniqueClones[targetInfo.dir]; ok { continue } + mutex.Lock() uniqueClones[targetInfo.dir] = nil + mutex.Unlock() wp.Submit(func() error { gitProject := *targetInfo.gitProject From 99ed27feed0c6b94c616371f74a6e164be528ddf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 13:24:04 +0200 Subject: [PATCH 0230/2268] feat: Get rid of involvedRepos from archive metadata --- pkg/kluctl_project/git.go | 25 ------------------ pkg/kluctl_project/load.go | 3 --- pkg/kluctl_project/project.go | 5 +--- pkg/kluctl_project/project_archive.go | 1 - pkg/kluctl_project/project_load.go | 12 +++------ pkg/kluctl_project/targets.go | 37 +-------------------------- pkg/types/metadata.go | 13 +--------- 7 files changed, 6 insertions(+), 90 deletions(-) diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 11c409846..c89e6405c 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -4,8 +4,6 @@ import ( "fmt" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" types2 "github.com/kluctl/kluctl/v2/pkg/types" - "reflect" - "sort" "sync" ) @@ -75,26 +73,3 @@ func (c *LoadedKluctlProject) updateGitCaches() error { waitGroup.Wait() return firstError } - -func (c *LoadedKluctlProject) addInvolvedRepo(u git_url.GitUrl, refPattern string, refs map[string]string) { - repoKey := u.NormalizedRepoKey() - irs, _ := c.involvedRepos[repoKey] - e := &types2.InvolvedRepo{ - RefPattern: refPattern, - Refs: refs, - } - found := false - for _, ir := range irs { - if reflect.DeepEqual(ir, e) { - found = true - break - } - } - if !found { - c.involvedRepos[repoKey] = append(c.involvedRepos[repoKey], *e) - s := c.involvedRepos[repoKey] - sort.SliceStable(s, func(i, j int) bool { - return s[i].RefPattern < s[j].RefPattern - }) - } -} diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 801d66f6d..609d15796 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/jinja2" - "github.com/kluctl/kluctl/v2/pkg/types" ) func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*LoadedKluctlProject, error) { @@ -14,8 +13,6 @@ func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir s TmpDir: tmpDir, J2: j2, GRC: args.GRC, - - involvedRepos: map[string][]types.InvolvedRepo{}, } if args.FromArchive != "" { diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index bb59ce9b2..a3b197dd7 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -25,8 +25,6 @@ type LoadedKluctlProject struct { ClustersDir string sealedSecretsDir string - involvedRepos map[string][]types2.InvolvedRepo - Config types2.KluctlProject DynamicTargets []*types2.DynamicTarget @@ -38,8 +36,7 @@ type LoadedKluctlProject struct { func (c *LoadedKluctlProject) GetMetadata() *types2.ProjectMetadata { md := &types2.ProjectMetadata{ - InvolvedRepos: c.involvedRepos, - Targets: c.DynamicTargets, + Targets: c.DynamicTargets, } return md } diff --git a/pkg/kluctl_project/project_archive.go b/pkg/kluctl_project/project_archive.go index d9e94f053..dd80a258e 100644 --- a/pkg/kluctl_project/project_archive.go +++ b/pkg/kluctl_project/project_archive.go @@ -57,7 +57,6 @@ func (c *LoadedKluctlProject) loadFromArchive() error { } c.archiveDir = dir - c.involvedRepos = pmd.InvolvedRepos c.DynamicTargets = pmd.Targets err = c.loadKluctlProject() diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 9a0f231cd..fba29c43c 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -58,7 +58,7 @@ func (c *LoadedKluctlProject) localProject(dir string) gitProjectInfo { } } -func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defaultSubDir string, doAddInvolvedRepo bool) (ret gitProjectInfo, err error) { +func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defaultSubDir string) (ret gitProjectInfo, err error) { cloneDir, ri, err := c.GRC.GetClonedDir(gitProject.Url, gitProject.Ref, c.loadArgs.AllowGitClone, true, true) if err != nil { return @@ -69,12 +69,6 @@ func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defa ret.commit = ri.CheckedOutCommit ret.repoRoot = cloneDir - if doAddInvolvedRepo { - c.addInvolvedRepo(ret.url, ret.ref, map[string]string{ - ret.ref: ret.commit, - }) - } - subDir := gitProject.SubDir if subDir == "" { subDir = defaultSubDir @@ -106,7 +100,7 @@ func (c *LoadedKluctlProject) loadExternalProject(ep *types2.ExternalProject, de }) // pointing to an actual external project, so let's try to clone it - return c.loadGitProject(ep.Project, defaultGitSubDir, true) + return c.loadGitProject(ep.Project, defaultGitSubDir) } // ExternalProject was provided but without an external repo url, so point into the kluctl project. @@ -134,7 +128,7 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { gi, err := c.loadGitProject(&types2.GitProject{ Url: *c.loadArgs.ProjectUrl, Ref: c.loadArgs.ProjectRef, - }, "", true) + }, "") if err != nil { return err } diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index b760c01d1..5a4e0b1f1 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -3,7 +3,6 @@ package kluctl_project import ( "fmt" securejoin "github.com/cyphar/filepath-securejoin" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -244,7 +243,7 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTarge gitProject := *targetInfo.gitProject gitProject.Ref = *targetInfo.ref - gi, err := c.loadGitProject(&gitProject, "", false) + gi, err := c.loadGitProject(&gitProject, "") mutex.Lock() defer mutex.Unlock() if err != nil { @@ -260,40 +259,6 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTarge return err } - refsByUrlAndPattern := make(map[string]map[string]map[string]string) - for _, targetInfo := range dynamicTargets { - if targetInfo.gitProject == nil { - continue - } - o, ok := uniqueClones[targetInfo.dir] - if !ok { - return fmt.Errorf("%s not in uniqueClones. This is probably a bug", targetInfo.dir) - } - err, ok := o.(error) - if ok { - return err - } - info := o.(*gitProjectInfo) - normalizedUrl := info.url.Normalize().String() - if _, ok := refsByUrlAndPattern[normalizedUrl]; !ok { - refsByUrlAndPattern[normalizedUrl] = make(map[string]map[string]string) - } - if _, ok := refsByUrlAndPattern[normalizedUrl][*targetInfo.refPattern]; !ok { - refsByUrlAndPattern[normalizedUrl][*targetInfo.refPattern] = make(map[string]string) - } - refsByUrlAndPattern[normalizedUrl][*targetInfo.refPattern][info.ref] = info.commit - } - - for url, refPatterns := range refsByUrlAndPattern { - for refPattern, refs := range refPatterns { - u, err := git_url.Parse(url) - if err != nil { - return err - } - c.addInvolvedRepo(*u, refPattern, refs) - } - } - return nil } diff --git a/pkg/types/metadata.go b/pkg/types/metadata.go index 0742a92ad..028510789 100644 --- a/pkg/types/metadata.go +++ b/pkg/types/metadata.go @@ -1,18 +1,7 @@ package types -type InvolvedRepo struct { - RefPattern string `yaml:"refPattern"` - Refs map[string]string `yaml:"refs"` -} - type ProjectMetadata struct { - InvolvedRepos map[string][]InvolvedRepo `yaml:"involvedRepos"` - Targets []*DynamicTarget `yaml:"targets"` -} - -type GitRepoMetadata struct { - Ref string `yaml:"ref"` - Commit string `yaml:"commit"` + Targets []*DynamicTarget `yaml:"targets"` } type ArchiveMetadata struct { From aeb26ecdc4088bdd74d31814adf405a143bb8224 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 14:10:24 +0200 Subject: [PATCH 0231/2268] refactor: Introduce RepoProvider interface --- cmd/kluctl/commands/utils.go | 9 +- pkg/deployment/deployment_project.go | 2 +- pkg/deployment/shared_context.go | 4 +- pkg/git/mirrored_repo.go | 8 +- pkg/git/repo_collection.go | 153 ---------------------- pkg/git/repoprovider/live.go | 184 +++++++++++++++++++++++++++ pkg/git/repoprovider/repoprovider.go | 18 +++ pkg/git/utils.go | 8 +- pkg/kluctl_project/git.go | 2 +- pkg/kluctl_project/load.go | 2 +- pkg/kluctl_project/project.go | 6 +- pkg/kluctl_project/project_load.go | 6 +- pkg/kluctl_project/target_context.go | 4 +- pkg/kluctl_project/targets.go | 18 +-- pkg/vars/vars_loader.go | 21 ++- pkg/vars/vars_loader_test.go | 8 +- 16 files changed, 249 insertions(+), 204 deletions(-) delete mode 100644 pkg/git/repo_collection.go create mode 100644 pkg/git/repoprovider/live.go create mode 100644 pkg/git/repoprovider/repoprovider.go diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 12c89660b..a752a7b85 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -8,6 +8,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/registries" @@ -58,8 +59,8 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b ctx, cancel := context.WithTimeout(cliCtx, projectFlags.Timeout) defer cancel() - grc := git.NewMirroredGitRepoCollection(ctx, auth.NewDefaultAuthProviders(), projectFlags.GitCacheUpdateInterval) - defer grc.Clear() + rp := repoprovider.NewLiveRepoProvider(ctx, auth.NewDefaultAuthProviders(), projectFlags.GitCacheUpdateInterval) + defer rp.Clear() loadArgs := kluctl_project.LoadKluctlProjectArgs{ RepoRoot: repoRoot, @@ -73,7 +74,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b FromArchive: projectFlags.FromArchive.String(), FromArchiveMetadata: projectFlags.FromArchiveMetadata.String(), AllowGitClone: projectFlags.FromArchive == "", - GRC: grc, + RP: rp, ClientConfigGetter: clientConfigGetter(forCompletion), } @@ -185,7 +186,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm } // we can assume that all git access is done at this point, so we can clear grc and thus unlock all repos - p.GRC.Clear() + p.RP.Clear() return cb(cmdCtx) } diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 93290a708..14b894813 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -155,7 +155,7 @@ func (p *DeploymentProject) loadIncludes() error { return err } } else if inc.Git != nil { - cloneDir, _, err := p.ctx.GRC.GetClonedDir(inc.Git.Url, inc.Git.Ref, true, true, true) + cloneDir, _, err := p.ctx.RP.GetClonedDir(inc.Git.Url, inc.Git.Ref) if err != nil { return err } diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index 59d0ebb07..a659a43e0 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -2,7 +2,7 @@ package deployment import ( "context" - "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/vars" ) @@ -10,7 +10,7 @@ import ( type SharedContext struct { Ctx context.Context K *k8s.K8sCluster - GRC *git.MirroredGitRepoCollection + RP repoprovider.RepoProvider VarsLoader *vars.VarsLoader RenderDir string diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 60abfe25e..83fc777e5 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -146,19 +146,19 @@ func (g *MirroredGitRepo) RemoteRefHashesMap() (map[string]string, error) { return refs, nil } -func (g *MirroredGitRepo) DefaultRef() *string { +func (g *MirroredGitRepo) DefaultRef() (string, error) { r, err := git.PlainOpen(g.mirrorDir) if err != nil { - return nil + return "", err } ref, err := r.Reference("HEAD", false) if err != nil { - return nil + return "", err } s := ref.Target().String() - return &s + return s, nil } func (g *MirroredGitRepo) buildRepositoryObject() (*git.Repository, error) { diff --git a/pkg/git/repo_collection.go b/pkg/git/repo_collection.go deleted file mode 100644 index 095b7ffb8..000000000 --- a/pkg/git/repo_collection.go +++ /dev/null @@ -1,153 +0,0 @@ -package git - -import ( - "context" - "fmt" - "github.com/kluctl/kluctl/v2/pkg/git/auth" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/utils" - "io/ioutil" - "os" - "path" - "path/filepath" - "sync" - "time" -) - -type MirroredGitRepoCollection struct { - ctx context.Context - authProviders *auth.GitAuthProviders - updateInterval time.Duration - repos map[string]*entry - mutex sync.Mutex -} - -type entry struct { - mr *MirroredGitRepo - clonedDirs map[string]string - updateMutex sync.Mutex -} - -func NewMirroredGitRepoCollection(ctx context.Context, authProviders *auth.GitAuthProviders, updateInterval time.Duration) *MirroredGitRepoCollection { - return &MirroredGitRepoCollection{ - ctx: ctx, - authProviders: authProviders, - updateInterval: updateInterval, - repos: map[string]*entry{}, - } -} - -func (g *MirroredGitRepoCollection) Clear() { - g.mutex.Lock() - defer g.mutex.Unlock() - - for _, e := range g.repos { - for _, path := range e.clonedDirs { - _ = os.RemoveAll(path) - } - - if e.mr.IsLocked() { - _ = e.mr.Unlock() - } - } - - g.repos = map[string]*entry{} -} - -func (g *MirroredGitRepoCollection) getEntry(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*entry, error) { - e, err := func() (*entry, error) { - g.mutex.Lock() - defer g.mutex.Unlock() - - e, ok := g.repos[url.NormalizedRepoKey()] - if !ok { - if !allowCreate { - return nil, fmt.Errorf("git repo %s not found", url.NormalizedRepoKey()) - } - mr, err := NewMirroredGitRepo(g.ctx, url) - if err != nil { - return nil, err - } - e = &entry{ - mr: mr, - clonedDirs: map[string]string{}, - } - g.repos[url.NormalizedRepoKey()] = e - - if lockRepo { - err = e.mr.Lock() - if err != nil { - return nil, err - } - } - } - return e, nil - }() - if err != nil { - return nil, err - } - - e.updateMutex.Lock() - defer e.updateMutex.Unlock() - - if update && !e.mr.HasUpdated() { - if time.Now().Sub(e.mr.LastUpdateTime()) <= g.updateInterval { - e.mr.SetUpdated(true) - } else { - err = e.mr.Update(g.authProviders) - if err != nil { - return nil, err - } - } - } - - return e, nil -} - -func (g *MirroredGitRepoCollection) GetMirroredGitRepo(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*MirroredGitRepo, error) { - e, err := g.getEntry(url, allowCreate, lockRepo, update) - if err != nil { - return nil, err - } - return e.mr, nil -} - -func (g *MirroredGitRepoCollection) GetClonedDir(url git_url.GitUrl, ref string, allowCreate bool, lockRepo bool, update bool) (string, GitRepoInfo, error) { - e, err := g.getEntry(url, allowCreate, lockRepo, update) - if err != nil { - return "", GitRepoInfo{}, err - } - - e.updateMutex.Lock() - defer e.updateMutex.Unlock() - - p, ok := e.clonedDirs[ref] - if ok { - return p, GitRepoInfo{}, nil - } - - tmpDir := filepath.Join(utils.GetTmpBaseDir(), "git-cloned") - err = os.MkdirAll(tmpDir, 0700) - if err != nil { - return "", GitRepoInfo{}, err - } - - repoName := path.Base(url.Normalize().Path) - p, err = ioutil.TempDir(tmpDir, repoName+"-"+ref+"-") - if err != nil { - return "", GitRepoInfo{}, err - } - - err = e.mr.CloneProject(ref, p) - if err != nil { - return "", GitRepoInfo{}, err - } - - repoInfo, err := GetGitRepoInfo(p) - if err != nil { - return "", GitRepoInfo{}, err - } - - e.clonedDirs[ref] = p - return p, repoInfo, nil -} diff --git a/pkg/git/repoprovider/live.go b/pkg/git/repoprovider/live.go new file mode 100644 index 000000000..f1a67b490 --- /dev/null +++ b/pkg/git/repoprovider/live.go @@ -0,0 +1,184 @@ +package repoprovider + +import ( + "context" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/git/auth" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/utils" + "io/ioutil" + "os" + "path" + "path/filepath" + "sync" + "time" +) + +type LiveRepoProvider struct { + ctx context.Context + authProviders *auth.GitAuthProviders + updateInterval time.Duration + repos map[string]*entry + mutex sync.Mutex +} + +type entry struct { + mr *git.MirroredGitRepo + clonedDirs map[string]clonedDir + updateMutex sync.Mutex +} + +type clonedDir struct { + dir string + info git.CheckoutInfo +} + +func NewLiveRepoProvider(ctx context.Context, authProviders *auth.GitAuthProviders, updateInterval time.Duration) RepoProvider { + return &LiveRepoProvider{ + ctx: ctx, + authProviders: authProviders, + updateInterval: updateInterval, + repos: map[string]*entry{}, + } +} + +func (rp *LiveRepoProvider) Clear() { + rp.mutex.Lock() + defer rp.mutex.Unlock() + + for _, e := range rp.repos { + for _, cd := range e.clonedDirs { + _ = os.RemoveAll(cd.dir) + } + + if e.mr.IsLocked() { + _ = e.mr.Unlock() + } + } + + rp.repos = map[string]*entry{} +} + +func (rp *LiveRepoProvider) getEntry(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*entry, error) { + e, err := func() (*entry, error) { + rp.mutex.Lock() + defer rp.mutex.Unlock() + + e, ok := rp.repos[url.NormalizedRepoKey()] + if !ok { + if !allowCreate { + return nil, fmt.Errorf("git repo %s not found", url.NormalizedRepoKey()) + } + mr, err := git.NewMirroredGitRepo(rp.ctx, url) + if err != nil { + return nil, err + } + e = &entry{ + mr: mr, + clonedDirs: map[string]clonedDir{}, + } + rp.repos[url.NormalizedRepoKey()] = e + + if lockRepo { + err = e.mr.Lock() + if err != nil { + return nil, err + } + } + } + return e, nil + }() + if err != nil { + return nil, err + } + + e.updateMutex.Lock() + defer e.updateMutex.Unlock() + + if update && !e.mr.HasUpdated() { + if time.Now().Sub(e.mr.LastUpdateTime()) <= rp.updateInterval { + e.mr.SetUpdated(true) + } else { + err = e.mr.Update(rp.authProviders) + if err != nil { + return nil, err + } + } + } + + return e, nil +} + +func (rp *LiveRepoProvider) GetRepoInfo(url git_url.GitUrl) (RepoInfo, error) { + e, err := rp.getEntry(url, true, true, true) + if err != nil { + return RepoInfo{}, err + } + + remoteRefs, err := e.mr.RemoteRefHashesMap() + if err != nil { + return RepoInfo{}, err + } + + defaultRef, err := e.mr.DefaultRef() + if err != nil { + return RepoInfo{}, err + } + + info := RepoInfo{ + Url: url, + RemoteRefs: remoteRefs, + DefaultRef: defaultRef, + } + + return info, nil +} + +func (rp *LiveRepoProvider) GetClonedDir(url git_url.GitUrl, ref string) (string, git.CheckoutInfo, error) { + e, err := rp.getEntry(url, true, true, true) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + e.updateMutex.Lock() + defer e.updateMutex.Unlock() + + cd, ok := e.clonedDirs[ref] + if ok { + return cd.dir, cd.info, nil + } + + tmpDir := filepath.Join(utils.GetTmpBaseDir(), "git-cloned") + err = os.MkdirAll(tmpDir, 0700) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + repoName := path.Base(url.Normalize().Path) + "-" + if ref == "" { + repoName += "HEAD-" + } else { + repoName += ref + "-" + } + p, err := ioutil.TempDir(tmpDir, repoName) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + err = e.mr.CloneProject(ref, p) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + repoInfo, err := git.GetCheckoutInfo(p) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + e.clonedDirs[ref] = clonedDir{ + dir: p, + info: repoInfo, + } + return p, repoInfo, nil +} diff --git a/pkg/git/repoprovider/repoprovider.go b/pkg/git/repoprovider/repoprovider.go new file mode 100644 index 000000000..77495df96 --- /dev/null +++ b/pkg/git/repoprovider/repoprovider.go @@ -0,0 +1,18 @@ +package repoprovider + +import ( + "github.com/kluctl/kluctl/v2/pkg/git" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" +) + +type RepoInfo struct { + Url git_url.GitUrl `yaml:"url"` + RemoteRefs map[string]string `yaml:"remoteRefs"` + DefaultRef string `yaml:"defaultRef"` +} + +type RepoProvider interface { + GetRepoInfo(url git_url.GitUrl) (RepoInfo, error) + GetClonedDir(url git_url.GitUrl, ref string) (string, git.CheckoutInfo, error) + Clear() +} diff --git a/pkg/git/utils.go b/pkg/git/utils.go index d3826b03d..0c0bb72d0 100644 --- a/pkg/git/utils.go +++ b/pkg/git/utils.go @@ -7,12 +7,12 @@ import ( "path/filepath" ) -type GitRepoInfo struct { - CheckedOutRef string - CheckedOutCommit string +type CheckoutInfo struct { + CheckedOutRef string `yaml:"checkedOutRef"` + CheckedOutCommit string `yaml:"checkedOutCommit"` } -func GetGitRepoInfo(path string) (ri GitRepoInfo, err error) { +func GetCheckoutInfo(path string) (ri CheckoutInfo, err error) { r, err := git.PlainOpen(path) if err != nil { return diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index c89e6405c..31e5678b8 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -25,7 +25,7 @@ func (c *LoadedKluctlProject) updateGitCaches() error { go func() { defer waitGroup.Done() - _, err := c.GRC.GetMirroredGitRepo(u, true, true, true) + _, err := c.RP.GetRepoInfo(u) if err != nil { doError(fmt.Errorf("failed to update git project %v: %v", u.String(), err)) } diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 609d15796..8ea01b500 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -12,7 +12,7 @@ func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir s loadArgs: args, TmpDir: tmpDir, J2: j2, - GRC: args.GRC, + RP: args.RP, } if args.FromArchive != "" { diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index a3b197dd7..d19f515e8 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -3,7 +3,7 @@ package kluctl_project import ( "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" "github.com/kluctl/kluctl/v2/pkg/jinja2" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -28,8 +28,8 @@ type LoadedKluctlProject struct { Config types2.KluctlProject DynamicTargets []*types2.DynamicTarget - J2 *jinja2.Jinja2 - GRC *git.MirroredGitRepoCollection + J2 *jinja2.Jinja2 + RP repoprovider.RepoProvider warnOnce utils.OnceByKey } diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index fba29c43c..77cba9795 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -2,8 +2,8 @@ package kluctl_project import ( "fmt" - "github.com/kluctl/kluctl/v2/pkg/git" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" "github.com/kluctl/kluctl/v2/pkg/status" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -28,7 +28,7 @@ type LoadKluctlProjectArgs struct { FromArchiveMetadata string AllowGitClone bool - GRC *git.MirroredGitRepoCollection + RP repoprovider.RepoProvider ClientConfigGetter func(context *string) (*rest.Config, *api.Config, error) } @@ -59,7 +59,7 @@ func (c *LoadedKluctlProject) localProject(dir string) gitProjectInfo { } func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defaultSubDir string) (ret gitProjectInfo, err error) { - cloneDir, ri, err := c.GRC.GetClonedDir(gitProject.Url, gitProject.Ref, c.loadArgs.AllowGitClone, true, true) + cloneDir, ri, err := c.RP.GetClonedDir(gitProject.Url, gitProject.Ref) if err != nil { return } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index b8a103e7d..9237562fe 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -65,7 +65,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s s.Success() } - varsLoader := vars.NewVarsLoader(ctx, k, p.GRC, aws.NewClientFactory()) + varsLoader := vars.NewVarsLoader(ctx, k, p.RP, aws.NewClientFactory()) if forSeal { err = p.loadSecrets(target, varsCtx, varsLoader) @@ -77,7 +77,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s dctx := deployment.SharedContext{ Ctx: ctx, K: k, - GRC: p.GRC, + RP: p.RP, VarsLoader: varsLoader, RenderDir: renderOutputDir, SealedSecretsDir: p.sealedSecretsDir, diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 5a4e0b1f1..828e4ec8c 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -133,7 +133,7 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsSimple(baseTarget *types.Targ } func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { - mr, err := c.GRC.GetMirroredGitRepo(baseTarget.TargetConfig.Project.Url, true, true, true) + repoInfo, err := c.RP.GetRepoInfo(baseTarget.TargetConfig.Project.Url) if err != nil { return nil, err } @@ -145,21 +145,17 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Ta targetConfigRef := baseTarget.TargetConfig.Ref refPattern := baseTarget.TargetConfig.RefPattern - defaultBranch := mr.DefaultRef() - if defaultBranch == nil { + defaultBranch := repoInfo.DefaultRef + if defaultBranch == "" { return nil, fmt.Errorf("git project %v seems to have no default branch", baseTarget.TargetConfig.Project.Url.String()) } if baseTarget.TargetConfig.Ref == nil && baseTarget.TargetConfig.RefPattern == nil { // use default branch of repo - targetConfigRef = defaultBranch - } - - refs, err := mr.RemoteRefHashesMap() - if err != nil { - return nil, err + targetConfigRef = &defaultBranch } + refs := repoInfo.RemoteRefs if targetConfigRef != nil { if _, ok := refs[fmt.Sprintf("refs/heads/%s", *targetConfigRef)]; !ok { return nil, fmt.Errorf("git project %s has no ref %s", baseTarget.TargetConfig.Project.Url.String(), *targetConfigRef) @@ -177,7 +173,7 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Ta continue } - cloneDir, _, err := c.GRC.GetClonedDir(baseTarget.TargetConfig.Project.Url, refShortName, false, false, false) + cloneDir, _, err := c.RP.GetClonedDir(baseTarget.TargetConfig.Project.Url, refShortName) if err != nil { return nil, err } @@ -188,7 +184,7 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Ta gitProject: baseTarget.TargetConfig.Project, ref: &refShortName, refPattern: refPattern, - defaultBranch: *defaultBranch, + defaultBranch: defaultBranch, }) } return dynamicTargets, nil diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 6e1310bee..94a221422 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -4,12 +4,12 @@ import ( "context" "encoding/base64" "fmt" - "github.com/kluctl/kluctl/v2/pkg/git" + securejoin "github.com/cyphar/filepath-securejoin" + "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars/aws" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -25,17 +25,17 @@ type usernamePassword struct { type VarsLoader struct { ctx context.Context k *k8s.K8sCluster - grc *git.MirroredGitRepoCollection + rp repoprovider.RepoProvider aws aws.AwsClientFactory credentialsCache map[string]usernamePassword } -func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, grc *git.MirroredGitRepoCollection, aws aws.AwsClientFactory) *VarsLoader { +func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, rp repoprovider.RepoProvider, aws aws.AwsClientFactory) *VarsLoader { return &VarsLoader{ ctx: ctx, k: k, - grc: grc, + rp: rp, aws: aws, credentialsCache: map[string]usernamePassword{}, } @@ -140,23 +140,22 @@ func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsS } func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, rootKey string) error { - mr, err := v.grc.GetMirroredGitRepo(gitFile.Url, true, true, true) + clonedDir, _, err := v.rp.GetClonedDir(gitFile.Url, gitFile.Ref) if err != nil { return fmt.Errorf("failed to load vars from git repository %s: %w", gitFile.Url.String(), err) } - tmpDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "git-vars") + path, err := securejoin.SecureJoin(clonedDir, gitFile.Path) if err != nil { return err } - defer os.RemoveAll(tmpDir) - file, err := mr.ReadFile(gitFile.Ref, gitFile.Path) + f, err := ioutil.ReadFile(path) if err != nil { - return fmt.Errorf("failed to load vars from git repository %s and path %s: %w", gitFile.Url.String(), gitFile.Path, err) + return err } - return v.loadFromString(varsCtx, string(file), rootKey) + return v.loadFromString(varsCtx, string(f), rootKey) } func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, ref k8s2.ObjectRef, key string, rootKey string, base64Decode bool) error { diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 763b6c1ae..da4f11531 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -5,9 +5,9 @@ import ( git2 "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" - "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -37,8 +37,8 @@ func newTestDir(t *testing.T) string { return tmp } -func newGRC(t *testing.T) *git.MirroredGitRepoCollection { - grc := git.NewMirroredGitRepoCollection(context.TODO(), auth.NewDefaultAuthProviders(), 0) +func newRP(t *testing.T) repoprovider.RepoProvider { + grc := repoprovider.NewMirroredGitRepoCollection(context.TODO(), auth.NewDefaultAuthProviders(), 0) t.Cleanup(func() { grc.Clear() }) @@ -50,7 +50,7 @@ func testVarsLoader(t *testing.T, test func(vl *VarsLoader, vc *VarsCtx, aws *aw if err != nil { t.Fatal(err) } - grc := newGRC(t) + grc := newRP(t) fakeAws := aws.NewFakeClientFactory() vl := NewVarsLoader(context.TODO(), k, grc, fakeAws) From 990da6d27843e98609ce5476d19a90ae8c3f6ab5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 16:40:00 +0200 Subject: [PATCH 0232/2268] feat: Remove archive support This turned out to be incompatible to git variables loading and git includes. --- cmd/kluctl/args/project.go | 14 ++- cmd/kluctl/commands/cmd_archive.go | 23 ----- cmd/kluctl/commands/root.go | 1 - cmd/kluctl/commands/utils.go | 29 +++--- pkg/kluctl_project/load.go | 29 ++---- pkg/kluctl_project/project.go | 3 +- pkg/kluctl_project/project_archive.go | 127 -------------------------- pkg/kluctl_project/project_load.go | 36 +++----- pkg/types/metadata.go | 5 - pkg/vars/vars_loader_test.go | 2 +- 10 files changed, 45 insertions(+), 224 deletions(-) delete mode 100644 cmd/kluctl/commands/cmd_archive.go delete mode 100644 pkg/kluctl_project/project_archive.go diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index c9f9de378..2639966fb 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -6,14 +6,12 @@ type ProjectFlags struct { ProjectUrl string `group:"project" short:"p" help:"Git url of the kluctl project. If not specified, the current directory will be used instead of a remote Git project"` ProjectRef string `group:"project" short:"b" help:"Git ref of the kluctl project. Only used when --project-url was given."` - ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` - LocalClusters existingDirType `group:"project" help:"DEPRECATED. Local clusters directory. Overrides the project from .kluctl.yaml"` - LocalDeployment existingDirType `group:"project" help:"DEPRECATED. Local deployment directory. Overrides the project from .kluctl.yaml"` - LocalSealedSecrets existingDirType `group:"project" help:"DEPRECATED. Local sealed-secrets directory. Overrides the project from .kluctl.yaml" ` - FromArchive existingPathType `group:"project" help:"Load project (.kluctl.yaml, cluster, ...) from archive. Given path can either be an archive file or a directory with the extracted contents." exts:"tar.gz,tgz"` - FromArchiveMetadata existingFileType `group:"project" help:"Specify where to load metadata (targets, ...) from. If not specified, metadata is assumed to be part of the archive." exts:"yml,yaml"` - OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to. When used with the 'archive' command, it will also cause the archive to not include the metadata.yaml file."` - Cluster string `group:"project" help:"DEPRECATED. Specify/Override cluster"` + ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` + LocalClusters existingDirType `group:"project" help:"DEPRECATED. Local clusters directory. Overrides the project from .kluctl.yaml"` + LocalDeployment existingDirType `group:"project" help:"DEPRECATED. Local deployment directory. Overrides the project from .kluctl.yaml"` + LocalSealedSecrets existingDirType `group:"project" help:"DEPRECATED. Local sealed-secrets directory. Overrides the project from .kluctl.yaml" ` + OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to."` + Cluster string `group:"project" help:"DEPRECATED. Specify/Override cluster"` Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` diff --git a/cmd/kluctl/commands/cmd_archive.go b/cmd/kluctl/commands/cmd_archive.go deleted file mode 100644 index 4e8fe72bc..000000000 --- a/cmd/kluctl/commands/cmd_archive.go +++ /dev/null @@ -1,23 +0,0 @@ -package commands - -import ( - "context" - "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/kluctl_project" -) - -type archiveCmd struct { - args.ProjectFlags - - OutputArchive string `group:"misc" help:"Path to .tgz to write project to." type:"path" required:"true"` -} - -func (cmd *archiveCmd) Help() string { - return `This archive can then be used with '--from-archive'` -} - -func (cmd *archiveCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { - return p.WriteArchive(cmd.OutputArchive, cmd.ProjectFlags.OutputMetadata == "") - }) -} diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 9c8e70711..f71cd8595 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -42,7 +42,6 @@ type cli struct { Debug bool `group:"global" help:"Enable debug logging"` NoUpdateCheck bool `group:"global" help:"Disable update check on startup"` - Archive archiveCmd `cmd:"" help:"Write project and all related components into single tgz"` CheckImageUpdates checkImageUpdatesCmd `cmd:"" help:"Render deployment and check if any images have new tags available"` Delete deleteCmd `cmd:"" help:"Delete a target (or parts of it) from the corresponding cluster"` Deploy deployCmd `cmd:"" help:"Deploys a target to the corresponding cluster"` diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index a752a7b85..5ff04f003 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -52,7 +52,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b } repoRoot, err := git.DetectGitRepositoryRoot(cwd) - if err != nil && projectFlags.FromArchive == "" { + if err != nil { status.Warning(cliCtx, "Failed to detect git project root. This might cause follow-up errors") } @@ -63,25 +63,26 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b defer rp.Clear() loadArgs := kluctl_project.LoadKluctlProjectArgs{ - RepoRoot: repoRoot, - ProjectDir: cwd, - ProjectUrl: url, - ProjectRef: projectFlags.ProjectRef, - ProjectConfig: projectFlags.ProjectConfig.String(), - LocalClusters: projectFlags.LocalClusters.String(), - LocalDeployment: projectFlags.LocalDeployment.String(), - LocalSealedSecrets: projectFlags.LocalSealedSecrets.String(), - FromArchive: projectFlags.FromArchive.String(), - FromArchiveMetadata: projectFlags.FromArchiveMetadata.String(), - AllowGitClone: projectFlags.FromArchive == "", - RP: rp, - ClientConfigGetter: clientConfigGetter(forCompletion), + RepoRoot: repoRoot, + ProjectDir: cwd, + ProjectUrl: url, + ProjectRef: projectFlags.ProjectRef, + ProjectConfig: projectFlags.ProjectConfig.String(), + LocalClusters: projectFlags.LocalClusters.String(), + LocalDeployment: projectFlags.LocalDeployment.String(), + LocalSealedSecrets: projectFlags.LocalSealedSecrets.String(), + RP: rp, + ClientConfigGetter: clientConfigGetter(forCompletion), } p, err := kluctl_project.LoadKluctlProject(ctx, loadArgs, tmpDir, j2) + if p != nil && p.RP != nil { + defer p.RP.Clear() + } if err != nil { return err } + if projectFlags.OutputMetadata != "" { md := p.GetMetadata() b, err := yaml.WriteYamlBytes(md) diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 8ea01b500..62c4f140d 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -2,11 +2,11 @@ package kluctl_project import ( "context" - "fmt" "github.com/kluctl/kluctl/v2/pkg/jinja2" ) func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*LoadedKluctlProject, error) { + p := &LoadedKluctlProject{ ctx: ctx, loadArgs: args, @@ -15,24 +15,13 @@ func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir s RP: args.RP, } - if args.FromArchive != "" { - if args.ProjectUrl != nil || args.ProjectRef != "" || args.ProjectConfig != "" || args.LocalClusters != "" || args.LocalDeployment != "" || args.LocalSealedSecrets != "" { - return nil, fmt.Errorf("--from-archive can not be combined with any other project related option") - } - err := p.loadFromArchive() - if err != nil { - return nil, err - } - return p, nil - } else { - err := p.loadKluctlProject() - if err != nil { - return nil, err - } - err = p.loadTargets() - if err != nil { - return nil, err - } - return p, nil + err := p.loadKluctlProject() + if err != nil { + return nil, err + } + err = p.loadTargets() + if err != nil { + return nil, err } + return p, nil } diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index d19f515e8..ee616243a 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -15,8 +15,7 @@ type LoadedKluctlProject struct { loadArgs LoadKluctlProjectArgs - TmpDir string - archiveDir string + TmpDir string projectRootDir string ProjectDir string diff --git a/pkg/kluctl_project/project_archive.go b/pkg/kluctl_project/project_archive.go deleted file mode 100644 index dd80a258e..000000000 --- a/pkg/kluctl_project/project_archive.go +++ /dev/null @@ -1,127 +0,0 @@ -package kluctl_project - -import ( - "archive/tar" - "compress/gzip" - "fmt" - types2 "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/yaml" - "os" - "path/filepath" - "strings" - "time" -) - -func (c *LoadedKluctlProject) loadFromArchive() error { - var dir string - if utils.IsFile(c.loadArgs.FromArchive) { - dir = filepath.Join(c.TmpDir, "archive") - err := utils.ExtractTarGzFile(c.loadArgs.FromArchive, dir) - if err != nil { - return fmt.Errorf("failed to extract archive %v: %w", c.loadArgs.FromArchive, err) - } - } else { - dir = c.loadArgs.FromArchive - } - - var pmdPath string - if c.loadArgs.FromArchiveMetadata != "" { - pmdPath = c.loadArgs.FromArchiveMetadata - } else { - pmdPath = yaml.FixPathExt(filepath.Join(dir, "project-metadata.yml")) - } - - var pmd types2.ProjectMetadata - err := yaml.ReadYamlFile(pmdPath, &pmd) - if err != nil { - return err - } - - var amd types2.ArchiveMetadata - err = yaml.ReadYamlFile(yaml.FixPathExt(filepath.Join(dir, "archive-metadata.yml")), &amd) - if err != nil { - return err - } - - c.loadArgs.RepoRoot = filepath.Join(dir, amd.ProjectRootDir) - c.loadArgs.ProjectDir = filepath.Join(dir, amd.ProjectRootDir, amd.ProjectSubDir) - - err = utils.CheckInDir(dir, c.loadArgs.RepoRoot) - if err != nil { - return err - } - err = utils.CheckInDir(dir, c.loadArgs.ProjectDir) - if err != nil { - return err - } - - c.archiveDir = dir - c.DynamicTargets = pmd.Targets - - err = c.loadKluctlProject() - if err != nil { - return err - } - return nil -} - -func (c *LoadedKluctlProject) WriteArchive(archivePath string, embedProjectMetadata bool) error { - f, err := os.Create(archivePath) - if err != nil { - return err - } - defer f.Close() - gz := gzip.NewWriter(f) - defer gz.Close() - tw := tar.NewWriter(gz) - defer tw.Close() - - filter := func(h *tar.Header, size int64) (*tar.Header, error) { - if strings.HasSuffix(strings.ToLower(h.Name), ".git") { - return nil, nil - } - h.Uid = 0 - h.Gid = 0 - h.Uname = "" - h.Gname = "" - h.ModTime = time.Time{} - h.ChangeTime = time.Time{} - h.AccessTime = time.Time{} - return h, nil - } - - if embedProjectMetadata { - err = yaml.WriteYamlToTar(tw, c.GetMetadata(), "project-metadata.yaml") - if err != nil { - return nil - } - } - - amd := types2.ArchiveMetadata{ - ProjectRootDir: "kluctl-project", - } - - amd.ProjectSubDir, err = filepath.Rel(c.projectRootDir, c.ProjectDir) - if err != nil { - return err - } - - err = yaml.WriteYamlToTar(tw, &amd, "archive-metadata.yaml") - if err != nil { - return nil - } - - if err = utils.AddToTar(tw, c.projectRootDir, "kluctl-project", filter); err != nil { - return err - } - gitReposBaseDir := filepath.Join(c.TmpDir, "git") - if utils.Exists(gitReposBaseDir) { - err = utils.AddToTar(tw, gitReposBaseDir, "git", filter) - if err != nil { - return err - } - } - - return nil -} diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 77cba9795..a409b3de7 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -16,19 +16,16 @@ import ( ) type LoadKluctlProjectArgs struct { - RepoRoot string - ProjectDir string - ProjectUrl *git_url.GitUrl - ProjectRef string - ProjectConfig string - LocalClusters string - LocalDeployment string - LocalSealedSecrets string - FromArchive string - FromArchiveMetadata string - - AllowGitClone bool - RP repoprovider.RepoProvider + RepoRoot string + ProjectDir string + ProjectUrl *git_url.GitUrl + ProjectRef string + ProjectConfig string + LocalClusters string + LocalDeployment string + LocalSealedSecrets string + + RP repoprovider.RepoProvider ClientConfigGetter func(context *string) (*rest.Config, *api.Config, error) } @@ -145,16 +142,9 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { } } - if c.loadArgs.AllowGitClone { - err = os.MkdirAll(filepath.Join(c.TmpDir, "git"), 0o755) - if err != nil { - return err - } - - err = c.updateGitCaches() - if err != nil { - return err - } + err = c.updateGitCaches() + if err != nil { + return err } s := status.Start(c.ctx, "Loading kluctl project") diff --git a/pkg/types/metadata.go b/pkg/types/metadata.go index 028510789..f734de0ad 100644 --- a/pkg/types/metadata.go +++ b/pkg/types/metadata.go @@ -3,8 +3,3 @@ package types type ProjectMetadata struct { Targets []*DynamicTarget `yaml:"targets"` } - -type ArchiveMetadata struct { - ProjectRootDir string `yaml:"projectRootDir"` - ProjectSubDir string `yaml:"projectSubDir"` -} diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index da4f11531..03faf654e 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -38,7 +38,7 @@ func newTestDir(t *testing.T) string { } func newRP(t *testing.T) repoprovider.RepoProvider { - grc := repoprovider.NewMirroredGitRepoCollection(context.TODO(), auth.NewDefaultAuthProviders(), 0) + grc := repoprovider.NewLiveRepoProvider(context.TODO(), auth.NewDefaultAuthProviders(), 0) t.Cleanup(func() { grc.Clear() }) From 501b6289d4283b3791db2aa1181aefc602f6b064 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 16:40:18 +0200 Subject: [PATCH 0233/2268] fix: Fix locking of cloneDynamicTargets mutx --- pkg/kluctl_project/targets.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 828e4ec8c..827982764 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -228,10 +228,11 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTarge if targetInfo.gitProject == nil { continue } + mutex.Lock() if _, ok := uniqueClones[targetInfo.dir]; ok { + mutex.Unlock() continue } - mutex.Lock() uniqueClones[targetInfo.dir] = nil mutex.Unlock() From 8e181c3a0b0fe385a18513b45c2707af071f0b68 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 24 May 2022 16:42:22 +0200 Subject: [PATCH 0234/2268] fix: Fix handling of default refs in LiveRepoProvider --- pkg/git/mirrored_repo.go | 4 ++++ pkg/git/repoprovider/live.go | 26 +++++++++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 83fc777e5..81525865e 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -55,6 +55,10 @@ func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl) (*MirroredGitRepo return o, nil } +func (g *MirroredGitRepo) Url() git_url.GitUrl { + return g.url +} + func (g *MirroredGitRepo) HasUpdated() bool { return g.hasUpdated } diff --git a/pkg/git/repoprovider/live.go b/pkg/git/repoprovider/live.go index f1a67b490..9d877b6ae 100644 --- a/pkg/git/repoprovider/live.go +++ b/pkg/git/repoprovider/live.go @@ -110,12 +110,7 @@ func (rp *LiveRepoProvider) getEntry(url git_url.GitUrl, allowCreate bool, lockR return e, nil } -func (rp *LiveRepoProvider) GetRepoInfo(url git_url.GitUrl) (RepoInfo, error) { - e, err := rp.getEntry(url, true, true, true) - if err != nil { - return RepoInfo{}, err - } - +func (e *entry) getRepoInfo() (RepoInfo, error) { remoteRefs, err := e.mr.RemoteRefHashesMap() if err != nil { return RepoInfo{}, err @@ -127,7 +122,7 @@ func (rp *LiveRepoProvider) GetRepoInfo(url git_url.GitUrl) (RepoInfo, error) { } info := RepoInfo{ - Url: url, + Url: e.mr.Url(), RemoteRefs: remoteRefs, DefaultRef: defaultRef, } @@ -135,12 +130,29 @@ func (rp *LiveRepoProvider) GetRepoInfo(url git_url.GitUrl) (RepoInfo, error) { return info, nil } +func (rp *LiveRepoProvider) GetRepoInfo(url git_url.GitUrl) (RepoInfo, error) { + e, err := rp.getEntry(url, true, true, true) + if err != nil { + return RepoInfo{}, err + } + + return e.getRepoInfo() +} + func (rp *LiveRepoProvider) GetClonedDir(url git_url.GitUrl, ref string) (string, git.CheckoutInfo, error) { e, err := rp.getEntry(url, true, true, true) if err != nil { return "", git.CheckoutInfo{}, err } + if ref == "" { + ref, err = e.mr.DefaultRef() + if err != nil { + return "", git.CheckoutInfo{}, err + } + ref = path.Base(ref) + } + e.updateMutex.Lock() defer e.updateMutex.Unlock() From c9d9b57954492b4cff5e5d57c35ceed9c8f0bd11 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 27 May 2022 19:16:33 +0200 Subject: [PATCH 0235/2268] feat: Only load cluster and client config when necessary This commit will also remove the cluster name from the secrets hash calculation, which will cause resealing of all secrets. --- cmd/kluctl/commands/cmd_seal.go | 6 +-- pkg/kluctl_project/target_context.go | 60 +++++++++------------------- pkg/kluctl_project/targets.go | 12 +++--- pkg/seal/sealer.go | 19 ++++----- 4 files changed, 35 insertions(+), 62 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 531fb13b4..65fe92997 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -66,11 +66,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L } } - clusterConfig, _, err := p.LoadClusterConfig(ctx.targetCtx.Target.Cluster, ctx.targetCtx.Target.Context) - if err != nil { - return doFail(err) - } - sealer, err := seal.NewSealer(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace, sealedSecretsControllerName, clusterConfig.Cluster, cmd.ForceReseal) + sealer, err := seal.NewSealer(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace, sealedSecretsControllerName, cmd.ForceReseal) if err != nil { return doFail(err) } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 9237562fe..2e4ac84bb 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -12,7 +12,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/vars/aws" "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd/api" "path/filepath" ) @@ -117,15 +116,21 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin contextName = target.Context } - clusterConfig, clientConfig, err := p.LoadClusterConfig(clusterName, contextName) + clientConfig, restConfig, err := p.loadArgs.ClientConfigGetter(contextName) if err != nil { return doError(err) } varsCtx := vars.NewVarsCtx(p.J2) - err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) - if err != nil { - return doError(err) + if clusterName != nil { + clusterConfig, err := p.LoadClusterConfig(*clusterName) + if err != nil { + return doError(err) + } + err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) + if err != nil { + return doError(err) + } } targetVars, err := uo.FromStruct(target) if err != nil { @@ -162,7 +167,7 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin varsCtx.UpdateChild("args", allArgs) - return varsCtx, clientConfig, clusterConfig.Cluster.Context, nil + return varsCtx, clientConfig, restConfig.CurrentContext, nil } func (p *LoadedKluctlProject) findSecretsEntry(name string) (*types.SecretSet, error) { @@ -195,45 +200,18 @@ func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.Va return nil } -func (p *LoadedKluctlProject) LoadClusterConfig(clusterName *string, contextName *string) (*types.ClusterConfig, *rest.Config, error) { +func (p *LoadedKluctlProject) LoadClusterConfig(clusterName string) (*types.ClusterConfig, error) { var err error var clusterConfig *types.ClusterConfig - if clusterName != nil { - p.warnOnce.Do("cluster-config", func() { - status.Warning(p.ctx, "Cluster configurations have been deprecated and support for them will be removed in a future kluctl release.") - }) - - clusterConfig, err = types.LoadClusterConfig(p.ClustersDir, *clusterName) - if err != nil { - return nil, nil, err - } - } + p.warnOnce.Do("cluster-config", func() { + status.Warning(p.ctx, "Cluster configurations have been deprecated and support for them will be removed in a future kluctl release.") + }) - var clientConfig *rest.Config - if clusterConfig != nil { - clientConfig, _, err = p.loadArgs.ClientConfigGetter(&clusterConfig.Cluster.Context) - if err != nil { - return nil, nil, err - } - } else { - var rawConfig *api.Config - clientConfig, rawConfig, err = p.loadArgs.ClientConfigGetter(contextName) - if err != nil { - return nil, nil, err - } - ctx, ok := rawConfig.Contexts[rawConfig.CurrentContext] - if !ok { - return nil, nil, fmt.Errorf("context %s not found", rawConfig.CurrentContext) - } - clusterConfig = &types.ClusterConfig{ - Cluster: &types.ClusterConfig2{ - Name: ctx.Cluster, - Context: rawConfig.CurrentContext, - Vars: uo.New(), - }, - } + clusterConfig, err = types.LoadClusterConfig(p.ClustersDir, clusterName) + if err != nil { + return nil, err } - return clusterConfig, clientConfig, nil + return clusterConfig, nil } diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 827982764..7911a2811 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -88,11 +88,13 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, return nil, err } - cc, _, err := c.LoadClusterConfig(target.Cluster, target.Context) - if err == nil { - err = varsCtx.UpdateChildFromStruct("cluster", cc.Cluster) - if err != nil { - return nil, err + if target.Cluster != nil { + cc, err := c.LoadClusterConfig(*target.Cluster) + if err == nil { + err = varsCtx.UpdateChildFromStruct("cluster", cc.Cluster) + if err != nil { + return nil, err + } } } diff --git a/pkg/seal/sealer.go b/pkg/seal/sealer.go index 93d1af66f..80309e6a7 100644 --- a/pkg/seal/sealer.go +++ b/pkg/seal/sealer.go @@ -10,7 +10,6 @@ import ( "github.com/bitnami-labs/sealed-secrets/pkg/crypto" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -28,18 +27,16 @@ const hashAnnotation = "kluctl.io/sealedsecret-hashes" const clusterIdAnnotation = "kluctl.io/sealedsecret-cluster-id" type Sealer struct { - ctx context.Context - clusterConfig *types.ClusterConfig2 - forceReseal bool - cert *rsa.PublicKey - clusterId string + ctx context.Context + forceReseal bool + cert *rsa.PublicKey + clusterId string } -func NewSealer(ctx context.Context, k *k8s.K8sCluster, sealedSecretsNamespace string, sealedSecretsControllerName string, clusterConfig *types.ClusterConfig2, forceReseal bool) (*Sealer, error) { +func NewSealer(ctx context.Context, k *k8s.K8sCluster, sealedSecretsNamespace string, sealedSecretsControllerName string, forceReseal bool) (*Sealer, error) { s := &Sealer{ - ctx: ctx, - clusterConfig: clusterConfig, - forceReseal: forceReseal, + ctx: ctx, + forceReseal: forceReseal, } cert, err := fetchCert(ctx, k, sealedSecretsNamespace, sealedSecretsControllerName) if err != nil { @@ -93,7 +90,7 @@ func (s *Sealer) doHash(key string, secret []byte, secretName string, secretName if secretNamespace == "" { secretNamespace = "*" } - salt := fmt.Sprintf("%s-%s-%s-%s", s.clusterConfig.Name, secretName, secretNamespace, key) + salt := fmt.Sprintf("%s-%s-%s", secretName, secretNamespace, key) if scope != "strict" { salt += "-" + scope } From f686192104dba5dbdaa6e9b495f58333271278ed Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 27 May 2022 19:18:22 +0200 Subject: [PATCH 0236/2268] fix: Reuse sealed secrets gathering code in sealing and resolving phases Before this, the seal command used a different logic to find sealed secrets then the resolving code, causing some issues. --- pkg/deployment/commands/seal.go | 38 ++++--- pkg/deployment/deployment_collection.go | 2 +- pkg/deployment/deployment_item.go | 130 ++++++++++++++++-------- 3 files changed, 107 insertions(+), 63 deletions(-) diff --git a/pkg/deployment/commands/seal.go b/pkg/deployment/commands/seal.go index c8c3e37a5..bd9eccc2e 100644 --- a/pkg/deployment/commands/seal.go +++ b/pkg/deployment/commands/seal.go @@ -2,12 +2,9 @@ package commands import ( "fmt" - securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/seal" - "io/fs" "path/filepath" - "strings" ) type SealCommand struct { @@ -27,26 +24,25 @@ func NewSealCommand(c *deployment.DeploymentCollection, outputPattern string, re } func (cmd *SealCommand) Run(sealer *seal.Sealer) error { - err := filepath.WalkDir(cmd.renderDir, func(p string, d fs.DirEntry, err error) error { - if !strings.HasSuffix(p, deployment.SealmeExt) { - return nil - } - - relPath, err := filepath.Rel(cmd.renderDir, p) - if err != nil { - return err - } - relTargetFile := filepath.Join(filepath.Dir(relPath), cmd.outputPattern, filepath.Base(p)) - targetFile, err := securejoin.SecureJoin(cmd.sealedSecretsDir, relTargetFile) + for _, di := range cmd.c.Deployments { + sealedSecrets, err := di.ListSealedSecrets("") if err != nil { return err } - targetFile = targetFile[:len(targetFile)-len(deployment.SealmeExt)] - err = sealer.SealFile(p, targetFile) - if err != nil { - return fmt.Errorf("failed sealing %s: %w", filepath.Base(p), err) + + for _, relPath := range sealedSecrets { + sealmeFile := filepath.Join(di.RenderedDir, relPath+deployment.SealmeExt) + targetFile, err := di.BuildSealedSecretPath(relPath) + if err != nil { + return err + } + + err = sealer.SealFile(sealmeFile, targetFile) + if err != nil { + return fmt.Errorf("failed sealing %s: %w", filepath.Base(relPath), err) + } } - return nil - }) - return err + } + + return nil } diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 698dba2bf..9b1a99789 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -148,7 +148,7 @@ func (c *DeploymentCollection) resolveSealedSecrets() error { } for _, d := range c.Deployments { - err := d.resolveSealedSecrets("") + err := d.resolveSealedSecrets() if err != nil { return err } diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index f0e89f5ff..b9e36c547 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -2,7 +2,6 @@ package deployment import ( "fmt" - securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -38,8 +37,8 @@ type DeploymentItem struct { RelToSourceItemDir string RelToProjectItemDir string - relRenderedDir string - renderedDir string + RelRenderedDir string + RenderedDir string renderedYamlPath string } @@ -70,13 +69,13 @@ func NewDeploymentItem(ctx SharedContext, project *DeploymentProject, collection return nil, err } - di.relRenderedDir = di.RelToSourceItemDir + di.RelRenderedDir = di.RelToSourceItemDir if di.index != 0 { - di.relRenderedDir = fmt.Sprintf("%s-%d", di.relRenderedDir, di.index) + di.RelRenderedDir = fmt.Sprintf("%s-%d", di.RelRenderedDir, di.index) } - di.renderedDir = filepath.Join(collection.ctx.RenderDir, di.Project.source.id, di.relRenderedDir) - di.renderedYamlPath = filepath.Join(di.renderedDir, ".rendered.yml") + di.RenderedDir = filepath.Join(collection.ctx.RenderDir, di.Project.source.id, di.RelRenderedDir) + di.renderedYamlPath = filepath.Join(di.RenderedDir, ".rendered.yml") } return di, nil } @@ -107,7 +106,7 @@ func (di *DeploymentItem) render(forSeal bool, wp *utils.WorkerPoolWithErrors) e return nil } - err := os.MkdirAll(di.renderedDir, 0o700) + err := os.MkdirAll(di.RenderedDir, 0o700) if err != nil { return err } @@ -147,7 +146,7 @@ func (di *DeploymentItem) render(forSeal bool, wp *utils.WorkerPoolWithErrors) e } wp.Submit(func() error { - return varsCtx.RenderDirectory(di.Project.source.dir, di.Project.getRenderSearchDirs(), di.Project.relDir, excludePatterns, di.RelToProjectItemDir, di.renderedDir) + return varsCtx.RenderDirectory(di.Project.source.dir, di.Project.getRenderSearchDirs(), di.Project.relDir, excludePatterns, di.RelToProjectItemDir, di.RenderedDir) }) return nil @@ -158,13 +157,13 @@ func (di *DeploymentItem) renderHelmCharts(wp *utils.WorkerPoolWithErrors) error return nil } - err := filepath.Walk(di.renderedDir, func(p string, info fs.FileInfo, err error) error { + err := filepath.Walk(di.RenderedDir, func(p string, info fs.FileInfo, err error) error { if !strings.HasSuffix(p, "helm-chart.yml") && !strings.HasSuffix(p, "helm-chart.yaml") { return nil } wp.Submit(func() error { - subDir, err := filepath.Rel(di.renderedDir, filepath.Dir(p)) + subDir, err := filepath.Rel(di.RenderedDir, filepath.Dir(p)) if err != nil { return err } @@ -185,7 +184,7 @@ func (di *DeploymentItem) renderHelmCharts(wp *utils.WorkerPoolWithErrors) error } } if !found { - return fmt.Errorf("%s/kustomization.yaml does not include the rendered helm chart: %s", di.relRenderedDir, chart.GetOutputPath()) + return fmt.Errorf("%s/kustomization.yaml does not include the rendered helm chart: %s", di.RelRenderedDir, chart.GetOutputPath()) } } @@ -199,57 +198,106 @@ func (di *DeploymentItem) renderHelmCharts(wp *utils.WorkerPoolWithErrors) error return nil } -func (di *DeploymentItem) resolveSealedSecrets(subdir string) error { +func (di *DeploymentItem) ListSealedSecrets(subdir string) ([]string, error) { + var ret []string + if di.dir == nil { - return nil + return nil, nil } - sealedSecretsDir := di.Project.getRenderedOutputPattern() - baseSourcePath := di.Project.ctx.SealedSecretsDir - - renderedDir := filepath.Join(di.renderedDir, subdir) + renderedDir := filepath.Join(di.RenderedDir, subdir) // ensure we're not leaving the project err := utils.CheckSubInDir(di.Project.source.dir, subdir) if err != nil { - return err + return nil, err } y, err := uo.FromFile(yaml.FixPathExt(filepath.Join(renderedDir, "kustomization.yml"))) if err != nil { - return err + return nil, err } l, _, err := y.GetNestedStringList("resources") if err != nil { - return err + return nil, err } for _, resource := range l { - p := filepath.Join(renderedDir, resource) - if utils.IsDirectory(p) { - err = di.resolveSealedSecrets(filepath.Join(subdir, resource)) - if err != nil { - return err - } + p := filepath.Clean(filepath.Join(renderedDir, resource)) + + isDir := utils.IsDirectory(p) + isSealedSecret := !utils.Exists(p) && utils.Exists(p+SealmeExt) + if !isDir && !isSealedSecret { continue } - if utils.Exists(p) || !utils.Exists(p+SealmeExt) { - continue + + relPath, err := filepath.Rel(di.RenderedDir, p) + if err != nil { + return nil, err } - relDir, err := filepath.Rel(renderedDir, filepath.Dir(p)) + + relPath = filepath.Clean(relPath) + + // ensure we're not leaving the project + err = utils.CheckSubInDir(di.Project.source.dir, relPath) if err != nil { - return err + return nil, err + } + + if isDir { + ret2, err := di.ListSealedSecrets(relPath) + if err != nil { + return nil, err + } + ret = append(ret, ret2...) + } else { + ret = append(ret, relPath) } - fname := filepath.Base(p) + } + return ret, nil +} - baseError := fmt.Sprintf("failed to resolve SealedSecret %s", filepath.Clean(filepath.Join(di.Project.absDir, resource))) +func (di *DeploymentItem) BuildSealedSecretPath(relPath string) (string, error) { + sealedSecretsDir := di.Project.getRenderedOutputPattern() + baseSourcePath := di.Project.ctx.SealedSecretsDir - // ensure we're not leaving the .sealed-secrets dir - sourcePath, err := securejoin.SecureJoin(baseSourcePath, filepath.Join(di.relRenderedDir, subdir, relDir, sealedSecretsDir, fname)) + relDir := filepath.Dir(relPath) + fname := filepath.Base(relPath) + + // ensure we're not leaving the .sealed-secrets dir + sourcePath := filepath.Join(baseSourcePath, di.RelRenderedDir, relDir, sealedSecretsDir, fname) + err := utils.CheckInDir(baseSourcePath, sourcePath) + if err != nil { + return "", err + } + sourcePath = filepath.Clean(sourcePath) + return sourcePath, nil +} + +func (di *DeploymentItem) resolveSealedSecrets() error { + if di.dir == nil { + return nil + } + + sealedSecrets, err := di.ListSealedSecrets("") + if err != nil { + return err + } + + for _, relPath := range sealedSecrets { + relDir := filepath.Dir(relPath) if err != nil { return err } - sourcePath = filepath.Clean(sourcePath) - targetPath := filepath.Join(renderedDir, relDir, fname) + fname := filepath.Base(relPath) + + baseError := fmt.Sprintf("failed to resolve SealedSecret %s", relPath) + + sourcePath, err := di.BuildSealedSecretPath(relPath) + if err != nil { + return fmt.Errorf("%s: %w", baseError, err) + } + + targetPath := filepath.Join(di.RenderedDir, relDir, fname) if !utils.IsFile(sourcePath) { return fmt.Errorf("%s. %s not found. You might need to seal it first", baseError, sourcePath) } @@ -304,7 +352,7 @@ func (di *DeploymentItem) readKustomizationYaml(subDir string) (*uo.Unstructured return nil, nil } - kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.renderedDir, subDir, "kustomization.yml")) + kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.RenderedDir, subDir, "kustomization.yml")) if !utils.IsFile(kustomizeYamlPath) { return nil, nil } @@ -318,7 +366,7 @@ func (di *DeploymentItem) readKustomizationYaml(subDir string) (*uo.Unstructured } func (di *DeploymentItem) writeKustomizationYaml(ky *uo.UnstructuredObject) error { - kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.renderedDir, "kustomization.yml")) + kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.RenderedDir, "kustomization.yml")) return yaml.WriteYamlFile(kustomizeYamlPath, ky) } @@ -365,7 +413,7 @@ func (di *DeploymentItem) buildKustomize() error { k := krusty.MakeKustomizer(ko) fsys := filesys.MakeFsOnDisk() - rm, err := k.Run(fsys, di.renderedDir) + rm, err := k.Run(fsys, di.RenderedDir) if err != nil { return err } @@ -453,7 +501,7 @@ func (di *DeploymentItem) postprocessObjects(images *Images) error { o.SetK8sAnnotations(uo.CopyMergeStrMap(o.GetK8sAnnotations(), commonAnnotations)) // Resolve image placeholders - err := images.ResolvePlaceholders(di.ctx.K, o, di.relRenderedDir, di.Tags.ListKeys()) + err := images.ResolvePlaceholders(di.ctx.K, o, di.RelRenderedDir, di.Tags.ListKeys()) if err != nil { errList = append(errList, err) } From eda52d10077003f4ae046c00cc08740327c289e5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 30 May 2022 08:58:35 +0200 Subject: [PATCH 0237/2268] fix: Fix zombie jinja2 processes We missed calling Wait() on the process. --- pkg/jinja2/jinja2_renderer.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/jinja2/jinja2_renderer.go b/pkg/jinja2/jinja2_renderer.go index f52869396..f29ec2312 100644 --- a/pkg/jinja2/jinja2_renderer.go +++ b/pkg/jinja2/jinja2_renderer.go @@ -13,6 +13,7 @@ import ( "os/exec" "path/filepath" "strings" + "time" ) type pythonJinja2Renderer struct { @@ -75,7 +76,11 @@ func (j *pythonJinja2Renderer) Close() { } if j.cmd != nil { if j.cmd.Process != nil { - _ = j.cmd.Process.Kill() + timer := time.AfterFunc(5*time.Second, func() { + _ = j.cmd.Process.Kill() + }) + _ = j.cmd.Wait() + timer.Stop() } j.cmd = nil } From bef1faf818ef9a7513d4256777151c59862dd8f8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 30 May 2022 10:58:13 +0200 Subject: [PATCH 0238/2268] fix: Allow to pass nil DeploymentCollection to DeleteCommand --- pkg/deployment/commands/delete.go | 8 +++++++- pkg/utils/inclusion.go | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index d794d355e..d4cb32fd8 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -6,6 +6,7 @@ import ( utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" ) type DeleteCommand struct { @@ -31,10 +32,15 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.Ob labels = cmd.c.Project.GetCommonLabels() } + var inclusion *utils.Inclusion + if cmd.c != nil { + inclusion = cmd.c.Inclusion + } + err := ru.UpdateRemoteObjects(k, labels, nil) if err != nil { return nil, err } - return utils2.FindObjectsForDelete(k, ru.GetFilteredRemoteObjects(cmd.c.Inclusion), cmd.c.Inclusion.HasType("tags"), nil) + return utils2.FindObjectsForDelete(k, ru.GetFilteredRemoteObjects(inclusion), inclusion.HasType("tags"), nil) } diff --git a/pkg/utils/inclusion.go b/pkg/utils/inclusion.go index 63990cf7c..5300f269f 100644 --- a/pkg/utils/inclusion.go +++ b/pkg/utils/inclusion.go @@ -26,6 +26,9 @@ func (inc *Inclusion) AddExclude(typ string, value string) { } func (inc *Inclusion) HasType(typ string) bool { + if inc == nil { + return false + } for e, _ := range inc.includes { if e.Type == typ { return true @@ -49,6 +52,9 @@ func (inc *Inclusion) checkList(l []InclusionEntry, m map[InclusionEntry]bool) b } func (inc *Inclusion) CheckIncluded(l []InclusionEntry, excludeIfNotIncluded bool) bool { + if inc == nil { + return true + } if len(inc.includes) == 0 && len(inc.excludes) == 0 { return true } From aa47679c2306e732c2aed34d89311e6eedc569cc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 30 May 2022 11:20:55 +0200 Subject: [PATCH 0239/2268] fix: Remove WaitForOpenapiInitDone as it is not needed anymore Improvements in kustomize resulted in quite some speedup in openapi loading, so this is not needed anymore. --- pkg/deployment/deployment_item.go | 2 -- pkg/k8s/fake_client_factory.go | 3 --- pkg/utils/init_kyaml.go | 30 ------------------------------ 3 files changed, 35 deletions(-) delete mode 100644 pkg/utils/init_kyaml.go diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index b9e36c547..b7ca38e17 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -407,8 +407,6 @@ func (di *DeploymentItem) buildKustomize() error { return err } - utils.WaitForOpenapiInitDone() - ko := krusty.MakeDefaultOptions() k := krusty.MakeKustomizer(ko) diff --git a/pkg/k8s/fake_client_factory.go b/pkg/k8s/fake_client_factory.go index 8af606ad5..8bc1169f2 100644 --- a/pkg/k8s/fake_client_factory.go +++ b/pkg/k8s/fake_client_factory.go @@ -1,7 +1,6 @@ package k8s import ( - "github.com/kluctl/kluctl/v2/pkg/utils" v1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -65,8 +64,6 @@ func NewFakeClientFactory(objects ...runtime.Object) ClientFactory { } func ConvertSchemeToAPIResources(s *runtime.Scheme) []*metav1.APIResourceList { - utils.WaitForOpenapiInitDone() - m := map[schema.GroupVersion][]metav1.APIResource{} for gvk, _ := range s.AllKnownTypes() { diff --git a/pkg/utils/init_kyaml.go b/pkg/utils/init_kyaml.go deleted file mode 100644 index a25bddbb0..000000000 --- a/pkg/utils/init_kyaml.go +++ /dev/null @@ -1,30 +0,0 @@ -package utils - -import ( - "sigs.k8s.io/kustomize/kyaml/openapi" - yaml2 "sigs.k8s.io/kustomize/kyaml/yaml" - "sync" -) - -var openapiInitDoneMutex sync.Mutex -var openapiInitDoneOnce sync.Once - -func WaitForOpenapiInitDone() { - openapiInitDoneOnce.Do(func() { - openapiInitDoneMutex.Lock() - openapiInitDoneMutex.Unlock() - }) -} - -func init() { - openapiInitDoneMutex.Lock() - go func() { - // we do a single call to IsNamespaceScoped to enforce openapi schema initialization - // this is required here to ensure that it is later not done in parallel which would cause race conditions - openapi.IsNamespaceScoped(yaml2.TypeMeta{ - APIVersion: "", - Kind: "ConfigMap", - }) - openapiInitDoneMutex.Unlock() - }() -} From 7270131a4dda803244a8b913c91a8256229c8d42 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 30 May 2022 11:21:40 +0200 Subject: [PATCH 0240/2268] fix: Don't write yamls to disk in buildKustomize And also avoid loading this yaml back into memory. We can simply use the in-memory result of the kustomize run. --- pkg/deployment/deployment_collection.go | 9 ++----- pkg/deployment/deployment_item.go | 31 +++---------------------- 2 files changed, 5 insertions(+), 35 deletions(-) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 9b1a99789..be086ebb2 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -195,14 +195,9 @@ func (c *DeploymentCollection) buildKustomizeObjects() error { wg.Add(1) go func() { - err := d.loadObjects() + err := d.postprocessCRDs() if err != nil { - handleError(fmt.Errorf("loading objects failed: %w", err)) - } else { - err = d.postprocessCRDs() - if err != nil { - handleError(fmt.Errorf("postprocessing CRDs failed: %w", err)) - } + handleError(fmt.Errorf("postprocessing CRDs failed: %w", err)) } wg.Done() }() diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index b7ca38e17..7afc98470 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -416,41 +416,16 @@ func (di *DeploymentItem) buildKustomize() error { return err } - var yamls []interface{} + di.Objects = nil for _, r := range rm.Resources() { y, err := r.Map() if err != nil { return err } - yamls = append(yamls, y) + o := uo.FromMap(y) + di.Objects = append(di.Objects, o) } - err = yaml.WriteYamlAllFile(di.renderedYamlPath, yamls) - if err != nil { - return err - } - - return nil -} - -func (di *DeploymentItem) loadObjects() error { - if di.dir == nil { - return nil - } - - objects, err := yaml.ReadYamlAllFile(di.renderedYamlPath) - if err != nil { - return err - } - - di.Objects = []*uo.UnstructuredObject{} - for _, o := range objects { - m, ok := o.(map[string]interface{}) - if !ok { - return fmt.Errorf("object is not a map") - } - di.Objects = append(di.Objects, uo.FromMap(m)) - } return nil } From 6509923813653c7645ad0924849f9a2140ffb7f7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 30 May 2022 11:42:07 +0200 Subject: [PATCH 0241/2268] fix: Use secureBuildKustomization from Flux kustomize-controller This fixes multiple issues at once, see the comment of the function for details. --- go.mod | 1 + go.sum | 4 +- pkg/deployment/deployment_item.go | 22 +++++------ pkg/utils/kustomize.go | 62 +++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 pkg/utils/kustomize.go diff --git a/go.mod b/go.mod index f9447d54d..e0968119e 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/evanphx/json-patch v5.6.0+incompatible + github.com/fluxcd/pkg/kustomize v0.5.1 github.com/gammazero/workerpool v1.1.2 github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.0 diff --git a/go.sum b/go.sum index 851e2a68e..ba0bc3d06 100644 --- a/go.sum +++ b/go.sum @@ -314,6 +314,8 @@ github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4 github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/fluxcd/pkg/kustomize v0.5.1 h1:151Ih34ltxN2z1e2mA5AvQONyE6phc4es57oVK3+plU= +github.com/fluxcd/pkg/kustomize v0.5.1/go.mod h1:58MFITy24bIbGI6cC3JkV/YpFQj648sVvgs0K1kraJw= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -885,7 +887,7 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+czc/J8SlhPKLOtVLMQc+xDCFBT73ZStMsRhSsUhsSg= diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 7afc98470..51957e455 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -12,8 +12,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "os" "path/filepath" - "sigs.k8s.io/kustomize/api/filesys" - "sigs.k8s.io/kustomize/api/krusty" "strings" ) @@ -35,11 +33,12 @@ type DeploymentItem struct { Objects []*uo.UnstructuredObject Tags *utils.OrderedMap - RelToSourceItemDir string - RelToProjectItemDir string - RelRenderedDir string - RenderedDir string - renderedYamlPath string + RenderedSourceRootDir string + RelToSourceItemDir string + RelToProjectItemDir string + RelRenderedDir string + RenderedDir string + renderedYamlPath string } func NewDeploymentItem(ctx SharedContext, project *DeploymentProject, collection *DeploymentCollection, config *types.DeploymentItemConfig, dir *string, index int) (*DeploymentItem, error) { @@ -74,7 +73,8 @@ func NewDeploymentItem(ctx SharedContext, project *DeploymentProject, collection di.RelRenderedDir = fmt.Sprintf("%s-%d", di.RelRenderedDir, di.index) } - di.RenderedDir = filepath.Join(collection.ctx.RenderDir, di.Project.source.id, di.RelRenderedDir) + di.RenderedSourceRootDir = filepath.Join(collection.ctx.RenderDir, di.Project.source.id) + di.RenderedDir = filepath.Join(di.RenderedSourceRootDir, di.RelRenderedDir) di.renderedYamlPath = filepath.Join(di.RenderedDir, ".rendered.yml") } return di, nil @@ -407,11 +407,7 @@ func (di *DeploymentItem) buildKustomize() error { return err } - ko := krusty.MakeDefaultOptions() - k := krusty.MakeKustomizer(ko) - - fsys := filesys.MakeFsOnDisk() - rm, err := k.Run(fsys, di.RenderedDir) + rm, err := utils.SecureBuildKustomization(di.RenderedSourceRootDir, di.RenderedDir, true) if err != nil { return err } diff --git a/pkg/utils/kustomize.go b/pkg/utils/kustomize.go new file mode 100644 index 000000000..21fbe4fe2 --- /dev/null +++ b/pkg/utils/kustomize.go @@ -0,0 +1,62 @@ +/* +Code in this file is copied from https://github.com/fluxcd/kustomize-controller/blob/main/controllers/kustomization_generator.go +*/ + +package utils + +import ( + "fmt" + securefs "github.com/fluxcd/pkg/kustomize/filesys" + "sigs.k8s.io/kustomize/api/filesys" + "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/api/resmap" + kustypes "sigs.k8s.io/kustomize/api/types" + "sync" +) + +// TODO: remove mutex when kustomize fixes the concurrent map read/write panic +var kustomizeBuildMutex sync.Mutex + +// SecureBuildKustomization wraps krusty.MakeKustomizer with the following settings: +// - secure on-disk FS denying operations outside root +// - load files from outside the kustomization dir path +// (but not outside root) +// - disable plugins except for the builtin ones +func SecureBuildKustomization(root, dirPath string, allowRemoteBases bool) (_ resmap.ResMap, err error) { + var fs filesys.FileSystem + + // Create secure FS for root with or without remote base support + if allowRemoteBases { + fs, err = securefs.MakeFsOnDiskSecureBuild(root) + if err != nil { + return nil, err + } + } else { + fs, err = securefs.MakeFsOnDiskSecure(root) + if err != nil { + return nil, err + } + } + + // Temporary workaround for concurrent map read and map write bug + // https://github.com/kubernetes-sigs/kustomize/issues/3659 + kustomizeBuildMutex.Lock() + defer kustomizeBuildMutex.Unlock() + + // Kustomize tends to panic in unpredicted ways due to (accidental) + // invalid object data; recover when this happens to ensure continuity of + // operations + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("recovered from kustomize build panic: %v", r) + } + }() + + buildOptions := &krusty.Options{ + LoadRestrictions: kustypes.LoadRestrictionsNone, + PluginConfig: kustypes.DisabledPluginConfig(), + } + + k := krusty.MakeKustomizer(buildOptions) + return k.Run(fs, dirPath) +} From 8ef86b647c22473c839f1751cdd7390a707e366b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 30 May 2022 11:44:33 +0200 Subject: [PATCH 0242/2268] ci: Fix branches pattern in Github workflow --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1ad4ff67a..0f3ffed05 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,7 +3,7 @@ name: tests on: push: branches: - - '*' + - '**' jobs: build: From 47cb42b0659a107f3cd66d52485bc1dcc3142c34 Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Mon, 30 May 2022 13:35:32 +0200 Subject: [PATCH 0243/2268] Feat/add seald secret vault support (token auth) (#28) * feat: reformat new vars style * feat: modify vault support * fix: Run go mod tidy Co-authored-by: Alexander Block --- go.mod | 28 +++++++++++++ go.sum | 88 +++++++++++++++++++++++++++++++++++++++ pkg/types/vars_source.go | 6 +++ pkg/vars/vars_loader.go | 11 +++++ pkg/vars/vault/secrets.go | 31 ++++++++++++++ 5 files changed, 164 insertions(+) create mode 100644 pkg/vars/vault/secrets.go diff --git a/go.mod b/go.mod index e0968119e..3c936f079 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/goccy/go-yaml v1.9.5 github.com/golang-jwt/jwt/v4 v4.4.1 github.com/google/go-containerregistry v0.9.0 + github.com/hashicorp/vault/api v1.6.0 github.com/hexops/gotextdiff v1.0.3 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 @@ -74,8 +75,11 @@ require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/alessio/shellescape v1.4.1 // indirect + github.com/armon/go-metrics v0.3.10 // indirect + github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect github.com/containerd/containerd v1.6.4 // indirect @@ -105,6 +109,7 @@ require ( github.com/go-playground/universal-translator v0.18.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.5.8 // indirect @@ -114,7 +119,24 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/go-retryablehttp v0.6.6 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/go-version v1.2.1 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/vault/sdk v0.5.0 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -135,6 +157,7 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -146,11 +169,13 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oklog/run v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.2 // indirect github.com/prometheus/client_model v0.2.0 // indirect @@ -159,6 +184,7 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/rubenv/sql-migrate v1.1.1 // indirect github.com/russross/blackfriday v1.6.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/afero v1.8.2 // indirect @@ -171,6 +197,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect + go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect @@ -181,6 +208,7 @@ require ( google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.4 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiserver v0.24.0 // indirect diff --git a/go.sum b/go.sum index ba0bc3d06..c0ed01965 100644 --- a/go.sum +++ b/go.sum @@ -93,6 +93,7 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= @@ -152,7 +153,11 @@ github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= @@ -188,6 +193,8 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= @@ -202,6 +209,8 @@ github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3/ 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/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= 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= @@ -299,6 +308,7 @@ github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 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.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= 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/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= @@ -310,6 +320,8 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -319,6 +331,8 @@ github.com/fluxcd/pkg/kustomize v0.5.1/go.mod h1:58MFITy24bIbGI6cC3JkV/YpFQj648s github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= +github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -338,6 +352,7 @@ github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49P github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -360,6 +375,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 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-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= 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= @@ -402,6 +418,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= @@ -476,6 +494,8 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= @@ -600,23 +620,61 @@ github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= +github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 h1:MBgwAFPUbfuI0+tmDU/aeM1MARvdbqWmiieXIalKqDE= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= +github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -627,6 +685,12 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/vault/api v1.6.0 h1:B8UUYod1y1OoiGHq9GtpiqSnGOUEWHaA26AY8RQEDY4= +github.com/hashicorp/vault/api v1.6.0/go.mod h1:h1K70EO2DgnBaTz5IsL6D5ERsNt5Pce93ueVS2+t0Xc= +github.com/hashicorp/vault/sdk v0.5.0 h1:EED7p0OCU3OY5SAqJwSANofY1YKMytm+jDHDQ2EzGVQ= +github.com/hashicorp/vault/sdk v0.5.0/go.mod h1:UJZHlfwj7qUJG8g22CuxUgkdJouFrBNvBHCyx8XAPdo= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -650,6 +714,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= +github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= @@ -672,6 +738,7 @@ github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOv github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.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= @@ -813,6 +880,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -870,6 +939,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/ohler55/ojg v1.14.0 h1:DyHomsCwofNswmKj7BLMdx51xnKbXxgIo1rVWCaBcNk= github.com/ohler55/ojg v1.14.0/go.mod h1:3+GH+0PggMKocQtbZCrFifal3yRpHiBT4QUkxFJI6e8= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -899,6 +970,8 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6 github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= @@ -911,6 +984,8 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 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= @@ -931,6 +1006,7 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= @@ -945,6 +1021,7 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= @@ -954,6 +1031,7 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 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.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= @@ -992,6 +1070,9 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg= github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= @@ -1085,6 +1166,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1 github.com/tomarrell/wrapcheck/v2 v2.4.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= @@ -1178,6 +1260,8 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= @@ -1254,6 +1338,7 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1750,6 +1835,7 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= @@ -1793,6 +1879,8 @@ gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index 84e15f393..006b9d2f4 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -37,6 +37,11 @@ type VarsSourceAwsSecretsManager struct { Profile *string `yaml:"profile,omitempty"` } +type VarsSourceVault struct { + Server string `yaml:"server" validate:"required"` + Key string `yaml:"key" validate:"required"` +} + type VarsSource struct { Values *uo.UnstructuredObject `yaml:"values,omitempty"` File *string `yaml:"file,omitempty"` @@ -47,6 +52,7 @@ type VarsSource struct { SystemEnvVars *uo.UnstructuredObject `yaml:"systemEnvVars,omitempty"` Http *VarsSourceHttp `yaml:"http,omitempty"` AwsSecretsManager *VarsSourceAwsSecretsManager `yaml:"awsSecretsManager,omitempty"` + Vault *VarsSourceVault `yaml:"vault,omitempty"` } func ValidateVarsSource(sl validator.StructLevel) { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 94a221422..2af7b09af 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -12,6 +12,7 @@ import ( k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars/aws" + "github.com/kluctl/kluctl/v2/pkg/vars/vault" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" "os" @@ -81,6 +82,8 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear return v.loadHttp(varsCtx, &source, rootKey) } else if source.AwsSecretsManager != nil { return v.loadAwsSecretsManager(varsCtx, &source, rootKey) + } else if source.Vault != nil { + return v.loadVault(varsCtx, &source, rootKey) } return fmt.Errorf("invalid vars source") } @@ -139,6 +142,14 @@ func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsS return v.loadFromString(varsCtx, secret, rootKey) } +func (v *VarsLoader) loadVault(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { + secret, err := vault.GetSecret(source.Vault.Server, source.Vault.Key) + if err != nil { + return err + } + return v.loadFromString(varsCtx, secret, rootKey) +} + func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, rootKey string) error { clonedDir, _, err := v.rp.GetClonedDir(gitFile.Url, gitFile.Ref) if err != nil { diff --git a/pkg/vars/vault/secrets.go b/pkg/vars/vault/secrets.go new file mode 100644 index 000000000..0a05e6579 --- /dev/null +++ b/pkg/vars/vault/secrets.go @@ -0,0 +1,31 @@ +package vault + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/hashicorp/vault/api" +) + +var httpClient = &http.Client{ + Timeout: 15 * time.Second, +} + +func GetSecret(server string, key string) (string, error) { + client, err := api.NewClient(&api.Config{Address: server, HttpClient: httpClient}) + if err != nil { + return "", fmt.Errorf("failed to create vault %s client", server) + } + secret, err := client.Logical().Read(key) + if err != nil { + return "", fmt.Errorf("connection to vault %s failed", server) + } + if secret == nil || secret.Data == nil { + return "", fmt.Errorf("the specified vault secret was not found") + } + data, _ := secret.Data["data"].(map[string]interface{}) + jsonData, _ := json.Marshal(data) + return string(jsonData), nil +} From 5d5f316f43424b44692666735f4a48e9e4a1159b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 30 May 2022 15:58:44 +0200 Subject: [PATCH 0244/2268] fix: Move prompts handling into status handler This allows to have prompts while there are still status lines being displayed. Fixes: #37 --- cmd/kluctl/commands/cmd_delete.go | 9 ++-- cmd/kluctl/commands/cmd_deploy.go | 10 ++-- cmd/kluctl/commands/cmd_downscale.go | 4 +- cmd/kluctl/commands/cmd_poke_images.go | 4 +- cmd/kluctl/commands/cmd_prune.go | 2 +- pkg/git/auth/list_auth_provider.go | 2 +- pkg/git/auth/ssh_auth_provider.go | 7 +-- pkg/git/auth/ssh_known_hosts.go | 14 ++--- pkg/status/noop.go | 6 +++ pkg/{utils/prompts.go => status/promts.go} | 61 +++++++--------------- pkg/status/simple_status_handler.go | 27 ++++++++++ pkg/status/status.go | 7 +++ pkg/status/status_handler.go | 22 ++++++++ pkg/utils/term/read_line.go | 48 +++++++++++++++++ pkg/utils/term/term_unix.go | 38 ++++++++++++++ pkg/utils/term/term_unix_bsd.go | 13 +++++ pkg/utils/term/term_unix_other.go | 13 +++++ pkg/utils/term/term_windows.go | 37 +++++++++++++ pkg/vars/vars_loader_http.go | 4 +- 19 files changed, 257 insertions(+), 71 deletions(-) rename pkg/{utils/prompts.go => status/promts.go} (51%) create mode 100644 pkg/utils/term/read_line.go create mode 100644 pkg/utils/term/term_unix.go create mode 100644 pkg/utils/term/term_unix_bsd.go create mode 100644 pkg/utils/term/term_unix_other.go create mode 100644 pkg/utils/term/term_windows.go diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 4911d9479..368ccd554 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -1,15 +1,16 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - utils2 "github.com/kluctl/kluctl/v2/pkg/utils" "os" ) @@ -59,7 +60,7 @@ func (cmd *deleteCmd) Run() error { if err != nil { return err } - result, err := confirmedDeleteObjects(ctx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) + result, err := confirmedDeleteObjects(ctx.ctx, ctx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) if err != nil { return err } @@ -74,14 +75,14 @@ func (cmd *deleteCmd) Run() error { }) } -func confirmedDeleteObjects(k *k8s.K8sCluster, refs []k8s2.ObjectRef, dryRun bool, forceYes bool) (*types.CommandResult, error) { +func confirmedDeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, dryRun bool, forceYes bool) (*types.CommandResult, error) { if len(refs) != 0 { _, _ = os.Stderr.WriteString("The following objects will be deleted:\n") for _, ref := range refs { _, _ = os.Stderr.WriteString(fmt.Sprintf(" %s\n", ref.String())) } if !forceYes && !dryRun { - if !utils2.AskForConfirmation(fmt.Sprintf("Do you really want to delete %d objects?", len(refs))) { + if !status.AskForConfirmation(ctx, fmt.Sprintf("Do you really want to delete %d objects?", len(refs))) { return nil, fmt.Errorf("aborted") } } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index a2b6d70a9..8f2d5a8d4 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -4,9 +4,8 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils" - "time" ) type deployCmd struct { @@ -78,9 +77,6 @@ func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { } func (cmd *deployCmd) diffResultCb(diffResult *types.CommandResult) error { - // workaround to ensure that progress has been completely written/updated - time.Sleep(130 * time.Millisecond) - err := outputCommandResult(nil, diffResult) if err != nil { return err @@ -89,11 +85,11 @@ func (cmd *deployCmd) diffResultCb(diffResult *types.CommandResult) error { return nil } if len(diffResult.Errors) != 0 { - if !utils.AskForConfirmation("\nThe diff resulted in errors, do you still want to proceed?") { + if !status.AskForConfirmation(cliCtx, "The diff resulted in errors, do you still want to proceed?") { return fmt.Errorf("aborted") } } else { - if !utils.AskForConfirmation("\nThe diff succeeded, do you want to proceed?") { + if !status.AskForConfirmation(cliCtx, "The diff succeeded, do you want to proceed?") { return fmt.Errorf("aborted") } } diff --git a/cmd/kluctl/commands/cmd_downscale.go b/cmd/kluctl/commands/cmd_downscale.go index 304b5cb1c..ac2d0a610 100644 --- a/cmd/kluctl/commands/cmd_downscale.go +++ b/cmd/kluctl/commands/cmd_downscale.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" - "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/status" ) type downscaleCmd struct { @@ -37,7 +37,7 @@ func (cmd *downscaleCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { - if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to downscale on context/cluster %s?", ctx.targetCtx.ClusterContext)) { + if !status.AskForConfirmation(cliCtx, fmt.Sprintf("Do you really want to downscale on context/cluster %s?", ctx.targetCtx.ClusterContext)) { return fmt.Errorf("aborted") } } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 67e7c5d0a..e08e152c8 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" - "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/status" ) type pokeImagesCmd struct { @@ -37,7 +37,7 @@ func (cmd *pokeImagesCmd) Run() error { } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { - if !utils.AskForConfirmation(fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", ctx.targetCtx.ClusterContext)) { + if !status.AskForConfirmation(cliCtx, fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", ctx.targetCtx.ClusterContext)) { return fmt.Errorf("aborted") } } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index d9c25852a..3610aed88 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -47,7 +47,7 @@ func (cmd *pruneCmd) runCmdPrune(ctx *commandCtx) error { if err != nil { return err } - result, err := confirmedDeleteObjects(ctx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) + result, err := confirmedDeleteObjects(ctx.ctx, ctx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) if err != nil { return err } diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index 241a8d5b2..4faa19b70 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -67,7 +67,7 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) if err != nil { status.Trace(ctx, "Failed to parse private key: %v", err) } else { - a.HostKeyCallback = buildVerifyHostCallback(e.KnownHosts) + a.HostKeyCallback = buildVerifyHostCallback(ctx, e.KnownHosts) return AuthMethodAndCA{ AuthMethod: a, } diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index 686e90b47..0ec025aa1 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -43,7 +43,7 @@ func (a *sshDefaultIdentityAndAgent) ClientConfig() (*ssh.ClientConfig, error) { User: a.user, Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Signers)}, } - cc.HostKeyCallback = buildVerifyHostCallback(nil) + cc.HostKeyCallback = buildVerifyHostCallback(a.ctx, nil) return cc, nil } @@ -148,13 +148,14 @@ func (k *deferredPassphraseKey) getPassphrase() ([]byte, error) { return passphrase, nil } - passphraseStr, err := utils.AskForPassword(fmt.Sprintf("Enter passphrase for key '%s'", k.path)) + passphraseStr, err := status.AskForPassword(k.ctx, fmt.Sprintf("Enter passphrase for key '%s'", k.path)) if err != nil { k.a.passphrases[k.path] = nil return nil, err } + passphrase = []byte(passphraseStr) k.a.passphrases[k.path] = passphrase - return []byte(passphraseStr), nil + return passphrase, nil } func (k *deferredPassphraseKey) parse() { diff --git a/pkg/git/auth/ssh_known_hosts.go b/pkg/git/auth/ssh_known_hosts.go index e5d522d6e..ec5df2966 100644 --- a/pkg/git/auth/ssh_known_hosts.go +++ b/pkg/git/auth/ssh_known_hosts.go @@ -1,8 +1,10 @@ package auth import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/git/auth/goph" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/crypto/ssh" "net" @@ -14,13 +16,13 @@ import ( var askHostMutex sync.Mutex -func buildVerifyHostCallback(knownHosts []byte) func(hostname string, remote net.Addr, key ssh.PublicKey) error { +func buildVerifyHostCallback(ctx context.Context, knownHosts []byte) func(hostname string, remote net.Addr, key ssh.PublicKey) error { return func(hostname string, remote net.Addr, key ssh.PublicKey) error { - return verifyHost(hostname, remote, key, knownHosts) + return verifyHost(ctx, hostname, remote, key, knownHosts) } } -func verifyHost(host string, remote net.Addr, key ssh.PublicKey, knownHosts []byte) error { +func verifyHost(ctx context.Context, host string, remote net.Addr, key ssh.PublicKey, knownHosts []byte) error { // Ensure only one prompt happens at a time askHostMutex.Lock() defer askHostMutex.Unlock() @@ -85,14 +87,14 @@ func verifyHost(host string, remote net.Addr, key ssh.PublicKey, knownHosts []by return fmt.Errorf("host not found and SSH_KNOWN_HOSTS has been set") } - if !askIsHostTrusted(host, key) { + if !askIsHostTrusted(ctx, host, key) { return fmt.Errorf("aborted") } return goph.AddKnownHost(host, remote, key, "") } -func askIsHostTrusted(host string, key ssh.PublicKey) bool { +func askIsHostTrusted(ctx context.Context, host string, key ssh.PublicKey) bool { prompt := fmt.Sprintf("Unknown Host: %s\nFingerprint: %s\nWould you like to add it? ", host, ssh.FingerprintSHA256(key)) - return utils.AskForConfirmation(prompt) + return status.AskForConfirmation(ctx, prompt) } diff --git a/pkg/status/noop.go b/pkg/status/noop.go index 1f30341a0..24c24ed5c 100644 --- a/pkg/status/noop.go +++ b/pkg/status/noop.go @@ -1,5 +1,7 @@ package status +import "fmt" + type NoopStatusHandler struct { } @@ -34,6 +36,10 @@ func (n NoopStatusHandler) PlainText(text string) { func (n NoopStatusHandler) InfoFallback(message string) { } +func (n NoopStatusHandler) Prompt(password bool, message string) (string, error) { + return "", fmt.Errorf("Prompt not implemented in NoopStatusHandler") +} + var _ StatusHandler = &NoopStatusHandler{} func (n NoopStatusLine) SetTotal(total int) { diff --git a/pkg/utils/prompts.go b/pkg/status/promts.go similarity index 51% rename from pkg/utils/prompts.go rename to pkg/status/promts.go index f95098796..8274e8c40 100644 --- a/pkg/utils/prompts.go +++ b/pkg/status/promts.go @@ -1,98 +1,73 @@ -package utils +package status import ( - "bufio" + "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/mattn/go-isatty" - "golang.org/x/term" "os" "strings" - "syscall" ) -func doWarn(f string, args ...any) { - _, _ = fmt.Fprintf(os.Stderr, f, args...) -} - // AskForConfirmation uses Scanln to parse user input. A user must type in "yes" or "no" and // then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", and "Yes" all count as // confirmations. If the input is not recognized, it will ask again. The function does not return // until it gets a valid response from the user. Typically, you should use fmt to print out a question // before calling askForConfirmation. E.g. fmt.Println("WARNING: Are you sure? (yes/no)") -func AskForConfirmation(prompt string) bool { +func AskForConfirmation(ctx context.Context, prompt string) bool { if !isatty.IsTerminal(os.Stderr.Fd()) { - doWarn("Not a terminal, suppressed prompt: %s", prompt) + Warning(ctx, "Not a terminal, suppressed prompt: %s", prompt) return false } - _, err := os.Stderr.WriteString(prompt + " (y/N) ") - if err != nil { - panic(err) - } - - var response string - _, err = fmt.Scanln(&response) + response, err := Prompt(ctx, false, prompt+" (y/N) ") if err != nil { return false } okayResponses := []string{"y", "Y", "yes", "Yes", "YES"} nokayResponses := []string{"n", "N", "no", "No", "NO"} - if FindStrInSlice(okayResponses, response) != -1 { + if utils.FindStrInSlice(okayResponses, response) != -1 { return true - } else if FindStrInSlice(nokayResponses, response) != -1 || response == "" { + } else if utils.FindStrInSlice(nokayResponses, response) != -1 || response == "" { return false } else { - fmt.Println("Please type yes or no and then press enter:") - return AskForConfirmation(prompt) + Warning(ctx, "Please type yes or no and then press enter!") + return AskForConfirmation(ctx, prompt) } } -func AskForPassword(prompt string) (string, error) { +func AskForPassword(ctx context.Context, prompt string) (string, error) { if !isatty.IsTerminal(os.Stderr.Fd()) { err := fmt.Errorf("not a terminal, suppressed credentials prompt: %s", prompt) - doWarn(err.Error()) - return "", err - } - - _, err := fmt.Fprintf(os.Stderr, "%s: ", prompt) - if err != nil { + Warning(ctx, err.Error()) return "", err } - bytePassword, err := term.ReadPassword(int(syscall.Stdin)) - _, _ = fmt.Fprintf(os.Stderr, "\n") + password, err := Prompt(ctx, true, fmt.Sprintf("%s: ", prompt)) if err != nil { return "", err } - password := string(bytePassword) return strings.TrimSpace(password), nil } -func AskForCredentials(prompt string) (string, string, error) { +func AskForCredentials(ctx context.Context, prompt string) (string, string, error) { if !isatty.IsTerminal(os.Stderr.Fd()) { err := fmt.Errorf("not a terminal, suppressed credentials prompt: %s", prompt) - doWarn(err.Error()) + Warning(ctx, err.Error()) return "", "", err } - reader := bufio.NewReader(os.Stdin) + Info(ctx, prompt) - _, err := fmt.Fprintf(os.Stderr, prompt+"\n") + username, err := Prompt(ctx, false, "Enter Username: ") if err != nil { return "", "", err } - fmt.Fprint(os.Stderr, "Enter Username: ") - username, err := reader.ReadString('\n') + password, err := Prompt(ctx, true, "Enter Password: ") if err != nil { return "", "", err } - - password, err := AskForPassword("Enter Password") - if err != nil { - return "", "", err - } - return strings.TrimSpace(username), strings.TrimSpace(password), nil } diff --git a/pkg/status/simple_status_handler.go b/pkg/status/simple_status_handler.go index 21a55a6ba..03c2509c8 100644 --- a/pkg/status/simple_status_handler.go +++ b/pkg/status/simple_status_handler.go @@ -1,5 +1,12 @@ package status +import ( + "fmt" + "golang.org/x/term" + "os" + "syscall" +) + type simpleStatusHandler struct { cb func(message string) trace bool @@ -55,6 +62,26 @@ func (s *simpleStatusHandler) InfoFallback(message string) { s.Info(message) } +func (s *simpleStatusHandler) Prompt(password bool, message string) (string, error) { + s.cb(message) + + if password { + bytePassword, err := term.ReadPassword(int(syscall.Stdin)) + _, _ = fmt.Fprintf(os.Stderr, "\n") + if err != nil { + return "", err + } + return string(bytePassword), nil + } else { + var response string + _, err := fmt.Scanln(&response) + if err != nil { + return "", err + } + return response, nil + } +} + func (sl *simpleStatusLine) SetTotal(total int) { } diff --git a/pkg/status/status.go b/pkg/status/status.go index f1f60395c..52faa225a 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -48,6 +48,8 @@ type StatusHandler interface { PlainText(text string) InfoFallback(message string) + + Prompt(password bool, message string) (string, error) } type contextKey struct{} @@ -233,3 +235,8 @@ func Error(ctx context.Context, status string, args ...any) { slh := FromContext(ctx) slh.Error(fmt.Sprintf(status, args...)) } + +func Prompt(ctx context.Context, password bool, message string, args ...any) (string, error) { + slh := FromContext(ctx) + return slh.Prompt(password, fmt.Sprintf(message, args...)) +} diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index c0205333a..f5f437acb 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -3,13 +3,17 @@ package status import ( "context" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/term" "github.com/vbauerster/mpb/v7" "github.com/vbauerster/mpb/v7/decor" "io" "math" + "strings" + "syscall" ) type MultiLineStatusHandler struct { + ctx context.Context out io.Writer progress *mpb.Progress stop chan any @@ -119,6 +123,24 @@ func (s *MultiLineStatusHandler) PlainText(text string) { s.Info(text) } +func (s *MultiLineStatusHandler) Prompt(password bool, message string) (string, error) { + o := withColor("yellow", "?") + sl := s.startStatus(1, message, math.MinInt, o) + defer sl.end(o) + + doUpdate := func(ret []byte) { + if password { + sl.Update(message + strings.Repeat("*", len(ret))) + } else { + sl.Update(message + string(ret)) + } + } + + ret, err := term.ReadLineNoEcho(int(syscall.Stdin), doUpdate) + + return string(ret), err +} + func (sl *statusLine) SetTotal(total int) { sl.total = total sl.bar.SetTotal(int64(total), false) diff --git a/pkg/utils/term/read_line.go b/pkg/utils/term/read_line.go new file mode 100644 index 000000000..910b039e7 --- /dev/null +++ b/pkg/utils/term/read_line.go @@ -0,0 +1,48 @@ +package term + +import ( + "io" + "runtime" +) + +func ReadLineNoEcho(fd int, cb func(ret []byte)) ([]byte, error) { + return readLineNoEcho(fd, cb) +} + +func readLine(reader io.Reader, cb func(ret []byte)) ([]byte, error) { + var buf [1]byte + var ret []byte + + for { + n, err := reader.Read(buf[:]) + if n > 0 { + switch buf[0] { + case '', '\b': + if len(ret) > 0 { + ret = ret[:len(ret)-1] + } + cb(ret) + case '\n': + if runtime.GOOS != "windows" { + return ret, nil + } + // otherwise ignore \n + case '\r': + if runtime.GOOS == "windows" { + return ret, nil + } + // otherwise ignore \r + default: + ret = append(ret, buf[0]) + cb(ret) + } + continue + } + if err != nil { + if err == io.EOF && len(ret) > 0 { + return ret, nil + } + return ret, err + } + } +} diff --git a/pkg/utils/term/term_unix.go b/pkg/utils/term/term_unix.go new file mode 100644 index 000000000..399785a6a --- /dev/null +++ b/pkg/utils/term/term_unix.go @@ -0,0 +1,38 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos + +package term + +import ( + "golang.org/x/sys/unix" +) + +// passwordReader is an io.Reader that reads from a specific file descriptor. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return unix.Read(int(r), buf) +} + +func readLineNoEcho(fd int, cb func(ret []byte)) ([]byte, error) { + termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + if err != nil { + return nil, err + } + + newState := *termios + newState.Lflag &^= unix.ECHO | unix.ICANON + newState.Lflag |= unix.ISIG + newState.Iflag |= unix.ICRNL + if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil { + return nil, err + } + + defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios) + + return readLine(passwordReader(fd), cb) +} diff --git a/pkg/utils/term/term_unix_bsd.go b/pkg/utils/term/term_unix_bsd.go new file mode 100644 index 000000000..853b3d698 --- /dev/null +++ b/pkg/utils/term/term_unix_bsd.go @@ -0,0 +1,13 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || dragonfly || freebsd || netbsd || openbsd +// +build darwin dragonfly freebsd netbsd openbsd + +package term + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TIOCGETA +const ioctlWriteTermios = unix.TIOCSETA diff --git a/pkg/utils/term/term_unix_other.go b/pkg/utils/term/term_unix_other.go new file mode 100644 index 000000000..1e8955c93 --- /dev/null +++ b/pkg/utils/term/term_unix_other.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || linux || solaris || zos +// +build aix linux solaris zos + +package term + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS +const ioctlWriteTermios = unix.TCSETS diff --git a/pkg/utils/term/term_windows.go b/pkg/utils/term/term_windows.go new file mode 100644 index 000000000..82ea4fa81 --- /dev/null +++ b/pkg/utils/term/term_windows.go @@ -0,0 +1,37 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package term + +import ( + "os" + + "golang.org/x/sys/windows" +) + +func readLineNoEcho(fd int, cb func(ret []byte)) ([]byte, error) { + var st uint32 + if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { + return nil, err + } + old := st + + st &^= (windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT) + st |= (windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_PROCESSED_INPUT) + if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil { + return nil, err + } + + defer windows.SetConsoleMode(windows.Handle(fd), old) + + var h windows.Handle + p, _ := windows.GetCurrentProcess() + if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil { + return nil, err + } + + f := os.NewFile(uintptr(h), "stdin") + defer f.Close() + return readLine(f, cb) +} diff --git a/pkg/vars/vars_loader_http.go b/pkg/vars/vars_loader_http.go index 18cd60695..379e1730e 100644 --- a/pkg/vars/vars_loader_http.go +++ b/pkg/vars/vars_loader_http.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/Azure/go-ntlmssp" "github.com/docker/distribution/registry/client/auth/challenge" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "io" @@ -85,7 +85,7 @@ func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, rootKe credsKey := fmt.Sprintf("%s|%s", source.Http.Url.Host, strings.Join(realms, "+")) creds, ok := v.credentialsCache[credsKey] if !ok { - username, password, err := utils.AskForCredentials(fmt.Sprintf("Please enter credentials for host '%s'", source.Http.Url.Host)) + username, password, err := status.AskForCredentials(v.ctx, fmt.Sprintf("Please enter credentials for host '%s'", source.Http.Url.Host)) if err != nil { return err } From 6f45f60298eefe3a7d7d7c88c59aa460b262b046 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 30 May 2022 15:59:23 +0200 Subject: [PATCH 0245/2268] fix: Flush status lines before printing results --- cmd/kluctl/commands/command_result.go | 7 +++++++ pkg/status/noop.go | 3 +++ pkg/status/simple_status_handler.go | 3 +++ pkg/status/status.go | 6 ++++++ pkg/status/status_handler.go | 24 ++++++++++++++++-------- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index bd2e8cd59..668e21ec2 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -3,6 +3,7 @@ package commands import ( "bytes" "fmt" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -193,18 +194,24 @@ func outputHelper(output []string, cb func(format string) (string, error)) error } func outputCommandResult(output []string, cr *types.CommandResult) error { + status.Flush(cliCtx) + return outputHelper(output, func(format string) (string, error) { return formatCommandResult(cr, format) }) } func outputValidateResult(output []string, vr *types.ValidateResult) error { + status.Flush(cliCtx) + return outputHelper(output, func(format string) (string, error) { return formatValidateResult(vr, format) }) } func outputYamlResult(output []string, result interface{}, multiDoc bool) error { + status.Flush(cliCtx) + if len(output) == 0 { output = []string{"-"} } diff --git a/pkg/status/noop.go b/pkg/status/noop.go index 24c24ed5c..c1ff08a9e 100644 --- a/pkg/status/noop.go +++ b/pkg/status/noop.go @@ -14,6 +14,9 @@ func (n NoopStatusHandler) SetTrace(trace bool) { func (n NoopStatusHandler) Stop() { } +func (n NoopStatusHandler) Flush() { +} + func (n NoopStatusHandler) StartStatus(total int, message string) StatusLine { return &NoopStatusLine{} } diff --git a/pkg/status/simple_status_handler.go b/pkg/status/simple_status_handler.go index 03c2509c8..2f7cc9772 100644 --- a/pkg/status/simple_status_handler.go +++ b/pkg/status/simple_status_handler.go @@ -29,6 +29,9 @@ func (s *simpleStatusHandler) SetTrace(trace bool) { func (s *simpleStatusHandler) Stop() { } +func (s *simpleStatusHandler) Flush() { +} + func (s *simpleStatusHandler) StartStatus(total int, message string) StatusLine { if message != "" { s.Info(message) diff --git a/pkg/status/status.go b/pkg/status/status.go index 52faa225a..0e0514e41 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -39,6 +39,7 @@ type StatusLine interface { type StatusHandler interface { SetTrace(trace bool) Stop() + Flush() StartStatus(total int, message string) StatusLine Info(message string) @@ -240,3 +241,8 @@ func Prompt(ctx context.Context, password bool, message string, args ...any) (st slh := FromContext(ctx) return slh.Prompt(password, fmt.Sprintf(message, args...)) } + +func Flush(ctx context.Context) { + slh := FromContext(ctx) + slh.Flush() +} diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index f5f437acb..d385afb76 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -16,7 +16,6 @@ type MultiLineStatusHandler struct { ctx context.Context out io.Writer progress *mpb.Progress - stop chan any trace bool } @@ -32,25 +31,34 @@ type statusLine struct { func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, trace bool) *MultiLineStatusHandler { sh := &MultiLineStatusHandler{ + ctx: ctx, out: out, trace: trace, - stop: make(chan any), } - sh.progress = mpb.NewWithContext( - ctx, - mpb.WithWidth(utils.GetTermWidth()), - mpb.WithOutput(out), - mpb.PopCompletedMode(), - ) + sh.start() return sh } +func (s *MultiLineStatusHandler) Flush() { + s.Stop() + s.start() +} + func (s *MultiLineStatusHandler) SetTrace(trace bool) { s.trace = trace } +func (s *MultiLineStatusHandler) start() { + s.progress = mpb.NewWithContext( + s.ctx, + mpb.WithWidth(utils.GetTermWidth()), + mpb.WithOutput(s.out), + mpb.PopCompletedMode(), + ) +} + func (s *MultiLineStatusHandler) Stop() { s.progress.Wait() } From f1869534f24843ab97dde650d202151a59356e41 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 08:36:37 +0200 Subject: [PATCH 0246/2268] tests: Stop using cluster config in most tests Except for external_projecs_tests as these test only deprecated functionality. --- e2e/deploy_test.go | 13 +++++++---- e2e/external_projects_test.go | 6 ++--- e2e/hooks_test.go | 8 +++---- e2e/inclusion_test.go | 8 +++---- e2e/project.go | 43 +++++++++++++++++++++++------------ 5 files changed, 46 insertions(+), 32 deletions(-) diff --git a/e2e/deploy_test.go b/e2e/deploy_test.go index 64b57dbd4..918559e2f 100644 --- a/e2e/deploy_test.go +++ b/e2e/deploy_test.go @@ -1,18 +1,21 @@ package e2e -import "testing" +import ( + "testing" +) func TestCommandDeploySimple(t *testing.T) { t.Parallel() + + k := defaultKindCluster + p := &testProject{} - p.init(t, "simple") + p.init(t, k, "simple") defer p.cleanup() - k := defaultKindCluster recreateNamespace(t, k, p.projectName) - p.updateKindCluster(k, nil) - p.updateTarget("test", k.Name, nil) + p.updateTarget("test", nil) addConfigMapDeployment(p, "cm", nil, resourceOpts{ name: "cm", diff --git a/e2e/external_projects_test.go b/e2e/external_projects_test.go index 87a972634..11b104d82 100644 --- a/e2e/external_projects_test.go +++ b/e2e/external_projects_test.go @@ -10,7 +10,7 @@ import ( func doTestProject(t *testing.T, namespace string, p *testProject) { k := defaultKindCluster - p.init(t, fmt.Sprintf("project-%s", namespace)) + p.init(t, k, fmt.Sprintf("project-%s", namespace)) defer p.cleanup() recreateNamespace(t, k, namespace) @@ -18,7 +18,7 @@ func doTestProject(t *testing.T, namespace string, p *testProject) { p.updateKindCluster(k, uo.FromMap(map[string]interface{}{ "cluster_var": "cluster_value1", })) - p.updateTarget("test", k.Name, uo.FromMap(map[string]interface{}{ + p.updateTargetDeprecated("test", k.Name, uo.FromMap(map[string]interface{}{ "target_var": "target_value1", })) addBusyboxDeployment(p, "busybox", resourceOpts{name: "busybox", namespace: namespace}) @@ -47,7 +47,7 @@ func doTestProject(t *testing.T, namespace string, p *testProject) { assertNestedFieldEquals(t, o, "cluster_value2", "data", "cluster_var") assertNestedFieldEquals(t, o, "target_value1", "data", "target_var") - p.updateTarget("test", k.Name, uo.FromMap(map[string]interface{}{ + p.updateTargetDeprecated("test", k.Name, uo.FromMap(map[string]interface{}{ "target_var": "target_value2", })) p.KluctlMust("deploy", "--yes", "-t", "test") diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 7e6b4557e..161dd8285 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -109,20 +109,18 @@ func assertHookResultNotCMName(t *testing.T, p *testProject, k *KindCluster, sec func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) (*testProject, *KindCluster) { isDone := false namespace := fmt.Sprintf("hook-%s", name) + k := defaultKindCluster p := &testProject{} - p.init(t, namespace) + p.init(t, k, namespace) defer func() { if !isDone { p.cleanup() } }() - k := defaultKindCluster - recreateNamespace(t, k, namespace) - p.updateKindCluster(k, nil) - p.updateTarget("test", k.Name, nil) + p.updateTarget("test", nil) addHookDeployment(p, "hook", resourceOpts{name: "hook", namespace: namespace}, false, hook, hookDeletionPolicy) addConfigMap(p, "hook", resourceOpts{name: "cm1", namespace: namespace}) diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index c7502a361..55e7be2ff 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -10,20 +10,18 @@ import ( func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bool) (*testProject, *KindCluster) { isDone := false + k := defaultKindCluster p := &testProject{} - p.init(t, namespace) + p.init(t, k, namespace) defer func() { if !isDone { p.cleanup() } }() - k := defaultKindCluster - recreateNamespace(t, k, p.projectName) - p.updateKindCluster(k, nil) - p.updateTarget("test", k.Name, nil) + p.updateTarget("test", nil) addConfigMapDeployment(p, "cm1", nil, resourceOpts{name: "cm1", namespace: p.projectName}) addConfigMapDeployment(p, "cm2", nil, resourceOpts{name: "cm2", namespace: p.projectName}) diff --git a/e2e/project.go b/e2e/project.go index fa5f18e8c..9713265bd 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/go-git/go-git/v5" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "os" @@ -29,13 +28,15 @@ type testProject struct { localDeployment *string localSealedSecrets *string + k *KindCluster kubeconfigs []string gitServer *test_utils.GitServer } -func (p *testProject) init(t *testing.T, projectName string) { +func (p *testProject) init(t *testing.T, k *KindCluster, projectName string) { p.t = t + p.k = k p.gitServer = test_utils.NewGitServer(t) p.projectName = projectName @@ -68,6 +69,8 @@ func (p *testProject) init(t *testing.T, projectName string) { p.updateDeploymentYaml(".", func(c *uo.UnstructuredObject) error { return nil }) + + p.kubeconfigs = append(p.kubeconfigs, k.Kubeconfig()) } func (p *testProject) cleanup() { @@ -145,9 +148,6 @@ func (p *testProject) updateCluster(name string, context string, vars *uo.Unstru } func (p *testProject) updateKindCluster(k *KindCluster, vars *uo.UnstructuredObject) { - if utils.FindStrInSlice(p.kubeconfigs, k.Kubeconfig()) == -1 { - p.kubeconfigs = append(p.kubeconfigs, k.Kubeconfig()) - } context, err := k.Kubectl("config", "current-context") if err != nil { p.t.Fatal(err) @@ -156,26 +156,41 @@ func (p *testProject) updateKindCluster(k *KindCluster, vars *uo.UnstructuredObj p.updateCluster(k.Name, context, vars) } -func (p *testProject) updateTarget(name string, cluster string, args *uo.UnstructuredObject) { +func (p *testProject) updateTargetDeprecated(name string, cluster string, args *uo.UnstructuredObject) { + p.updateTarget(name, func(target *uo.UnstructuredObject) { + if args != nil { + target.MergeChild("args", args) + } + // compatibility + _ = target.SetNestedField(cluster, "cluster") + }) +} + +func (p *testProject) updateTarget(name string, cb func(target *uo.UnstructuredObject)) { + if cb == nil { + cb = func(target *uo.UnstructuredObject) {} + } + p.updateKluctlYaml(func(o *uo.UnstructuredObject) error { targets, _, _ := o.GetNestedObjectList("targets") var newTargets []*uo.UnstructuredObject + found := false for _, t := range targets { n, _, _ := t.GetNestedString("name") if n == name { - continue + cb(t) + found = true } newTargets = append(newTargets, t) } - n := uo.FromMap(map[string]interface{}{ - "name": name, - "cluster": cluster, - }) - if args != nil { - n.MergeChild("args", args) + if !found { + n := uo.FromMap(map[string]interface{}{ + "name": name, + }) + cb(n) + newTargets = append(newTargets, n) } - newTargets = append(newTargets, n) _ = o.SetNestedObjectList(newTargets, "targets") return nil }) From 51d767ea5eb20d4e4fa0fa6d891dd37b2c65af9f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 08:45:31 +0200 Subject: [PATCH 0247/2268] ci: Make KIND_KUBECONFIG more configurable --- .github/workflows/tests.yml | 7 ++++--- hack/start-kind-cluster.sh | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0f3ffed05..327211e28 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -246,11 +246,12 @@ jobs: else PORT=10002 fi - KIND_CLUSTER_NAME=$(echo "kluctl-${{ runner.os }}" | awk '{{ print tolower($1)}}') + KIND_CLUSTER_NAME=$(echo "kluctl-${{ runner.os }}" | awk '{{ print tolower($1) }}') + KIND_KUBECONFIG=$(pwd)/kind-kubeconfig echo "KIND_CLUSTER_NAME=$KIND_CLUSTER_NAME" >> $GITHUB_ENV - echo "KIND_KUBECONFIG=$(pwd)/kind-kubeconfig" >> $GITHUB_ENV + echo "KIND_KUBECONFIG=$KIND_KUBECONFIG" >> $GITHUB_ENV - ./hack/start-kind-cluster.sh "$KIND_CLUSTER_NAME" "$DOCKER_IP" "$PORT" + ./hack/start-kind-cluster.sh "$KIND_CLUSTER_NAME" "$DOCKER_IP" "$PORT" "$KIND_KUBECONFIG" - name: Download artifacts uses: actions/download-artifact@v2 - name: Run e2e tests diff --git a/hack/start-kind-cluster.sh b/hack/start-kind-cluster.sh index 3c7e737ee..315d6887a 100755 --- a/hack/start-kind-cluster.sh +++ b/hack/start-kind-cluster.sh @@ -5,8 +5,9 @@ set -e NAME=$1 IP=$2 PORT=$3 +export KUBECONFIG=$4 -cat << EOF > kind-cluster.yml +cat << EOF > kind-cluster-$NAME.yml kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 networking: @@ -15,8 +16,7 @@ networking: EOF rm -f $(pwd)/kind-kubeconfig -export KUBECONFIG=$KIND_KUBECONFIG -kind create cluster --config kind-cluster.yml --name $NAME +kind create cluster --config kind-cluster-$NAME.yml --name $NAME # Rewrite cluster info to point to docker host # This also fully disables TLS verification @@ -24,5 +24,5 @@ kubectl config view -ojson --raw \ | jq ".clusters[0].cluster.\"insecure-skip-tls-verify\"=true" \ | jq "del(.clusters[0].cluster.\"certificate-authority-data\")" \ | jq ".clusters[0].cluster.server=\"https://$IP:$PORT\"" \ -> kind-kubeconfig2 -mv kind-kubeconfig2 kind-kubeconfig +> $KUBECONFIG-tmp +mv $KUBECONFIG-tmp $KUBECONFIG From ac1c0b278e8a6e8f302c08d12705301e4b250ce6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 08:53:13 +0200 Subject: [PATCH 0248/2268] ci: Start 2 kind clusters in parallel Needed for context tests. --- .github/workflows/tests.yml | 31 +++++++++++++++++++++++-------- e2e/deploy_test.go | 2 +- e2e/external_projects_test.go | 2 +- e2e/hooks_test.go | 2 +- e2e/inclusion_test.go | 2 +- e2e/kind_cluster.go | 32 ++++++++++++++++++++++++++------ e2e/utils.go | 3 ++- 7 files changed, 55 insertions(+), 19 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 327211e28..70690c075 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -240,18 +240,33 @@ jobs: shell: bash run: | if [ "${{ runner.os }}" == "Linux" ]; then - PORT=10000 + PORT1=10000 + PORT2=20000 elif [ "${{ runner.os }}" == "Windows" ]; then - PORT=10001 + PORT1=10001 + PORT2=20001 else - PORT=10002 + PORT1=10002 + PORT2=20002 fi - KIND_CLUSTER_NAME=$(echo "kluctl-${{ runner.os }}" | awk '{{ print tolower($1) }}') - KIND_KUBECONFIG=$(pwd)/kind-kubeconfig - echo "KIND_CLUSTER_NAME=$KIND_CLUSTER_NAME" >> $GITHUB_ENV - echo "KIND_KUBECONFIG=$KIND_KUBECONFIG" >> $GITHUB_ENV + KIND_CLUSTER_NAME_BASE=$(echo "kluctl-${{ runner.os }}" | awk '{{ print tolower($1) }}') - ./hack/start-kind-cluster.sh "$KIND_CLUSTER_NAME" "$DOCKER_IP" "$PORT" "$KIND_KUBECONFIG" + KIND_CLUSTER_NAME1=$KIND_CLUSTER_NAME_BASE-1 + KIND_KUBECONFIG1=$(pwd)/kind-kubeconfig-1 + echo "KIND_CLUSTER_NAME1=$KIND_CLUSTER_NAME1" >> $GITHUB_ENV + echo "KIND_KUBECONFIG1=$KIND_KUBECONFIG1" >> $GITHUB_ENV + ./hack/start-kind-cluster.sh "$KIND_CLUSTER_NAME1" "$DOCKER_IP" "$PORT1" "$KIND_KUBECONFIG1" & + PID1=$! + + KIND_CLUSTER_NAME2=$KIND_CLUSTER_NAME_BASE-2 + KIND_KUBECONFIG2=$(pwd)/kind-kubeconfig-2 + echo "KIND_CLUSTER_NAME2=$KIND_CLUSTER_NAME2" >> $GITHUB_ENV + echo "KIND_KUBECONFIG2=$KIND_KUBECONFIG2" >> $GITHUB_ENV + ./hack/start-kind-cluster.sh "$KIND_CLUSTER_NAME2" "$DOCKER_IP" "$PORT2" "$KIND_KUBECONFIG2" & + PID2=$1 + + wait $PID1 + wait $PID2 - name: Download artifacts uses: actions/download-artifact@v2 - name: Run e2e tests diff --git a/e2e/deploy_test.go b/e2e/deploy_test.go index 918559e2f..f8d451032 100644 --- a/e2e/deploy_test.go +++ b/e2e/deploy_test.go @@ -7,7 +7,7 @@ import ( func TestCommandDeploySimple(t *testing.T) { t.Parallel() - k := defaultKindCluster + k := defaultKindCluster1 p := &testProject{} p.init(t, k, "simple") diff --git a/e2e/external_projects_test.go b/e2e/external_projects_test.go index 11b104d82..eefd91b17 100644 --- a/e2e/external_projects_test.go +++ b/e2e/external_projects_test.go @@ -8,7 +8,7 @@ import ( ) func doTestProject(t *testing.T, namespace string, p *testProject) { - k := defaultKindCluster + k := defaultKindCluster1 p.init(t, k, fmt.Sprintf("project-%s", namespace)) defer p.cleanup() diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 161dd8285..5c2af1dae 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -109,7 +109,7 @@ func assertHookResultNotCMName(t *testing.T, p *testProject, k *KindCluster, sec func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) (*testProject, *KindCluster) { isDone := false namespace := fmt.Sprintf("hook-%s", name) - k := defaultKindCluster + k := defaultKindCluster1 p := &testProject{} p.init(t, k, namespace) defer func() { diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 55e7be2ff..21a3028eb 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -10,7 +10,7 @@ import ( func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bool) (*testProject, *KindCluster) { isDone := false - k := defaultKindCluster + k := defaultKindCluster1 p := &testProject{} p.init(t, k, namespace) defer func() { diff --git a/e2e/kind_cluster.go b/e2e/kind_cluster.go index f420001a2..1096058ea 100644 --- a/e2e/kind_cluster.go +++ b/e2e/kind_cluster.go @@ -14,6 +14,7 @@ import ( "path/filepath" "sigs.k8s.io/kind/pkg/cluster" kindcmd "sigs.k8s.io/kind/pkg/cmd" + "sync" "testing" "time" ) @@ -148,16 +149,35 @@ func createKindCluster(name string, kubeconfig string) *KindCluster { return k } -func createDefaultKindCluster() *KindCluster { - kindClusterName := os.Getenv("KIND_CLUSTER_NAME") - kindKubeconfig := os.Getenv("KIND_KUBECONFIG") +func createDefaultKindCluster(num int) *KindCluster { + kindClusterName := os.Getenv(fmt.Sprintf("KIND_CLUSTER_NAME%d", num)) + kindKubeconfig := os.Getenv(fmt.Sprintf("KIND_KUBECONFIG%d", num)) if kindClusterName == "" { - kindClusterName = "kluctl-e2e" + kindClusterName = fmt.Sprintf("kluctl-e2e-%d", num) } if kindKubeconfig == "" { - kindKubeconfig = filepath.Join(utils.GetTmpBaseDir(), "kluctl-e2e-kubeconfig.yml") + kindKubeconfig = filepath.Join(utils.GetTmpBaseDir(), fmt.Sprintf("kluctl-e2e-kubeconfig-%d.yml", num)) } return createKindCluster(kindClusterName, kindKubeconfig) } -var defaultKindCluster = createDefaultKindCluster() +func createDefaultKindClusters() (*KindCluster, *KindCluster) { + var k1, k2 *KindCluster + + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + k1 = createDefaultKindCluster(1) + }() + go func() { + defer wg.Done() + k2 = createDefaultKindCluster(2) + }() + wg.Wait() + return k1, k2 +} + +var ( + defaultKindCluster1, defaultKindCluster2 = createDefaultKindClusters() +) diff --git a/e2e/utils.go b/e2e/utils.go index 97df8657c..79a80696d 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -134,5 +134,6 @@ func runHelper(t *testing.T, cmd *exec.Cmd) (string, string, error) { } func init() { - deleteTestNamespaces(defaultKindCluster) + deleteTestNamespaces(defaultKindCluster1) + deleteTestNamespaces(defaultKindCluster2) } From 064d176408de474e226d55fc0637e6a78042eece Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 09:49:31 +0200 Subject: [PATCH 0249/2268] tests: Use merged kubeconfig instead of multiple --- e2e/project.go | 51 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/e2e/project.go b/e2e/project.go index 9713265bd..c5326febf 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -3,9 +3,12 @@ package e2e import ( "fmt" "github.com/go-git/go-git/v5" + "github.com/imdario/mergo" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "os" "os/exec" "path/filepath" @@ -28,15 +31,13 @@ type testProject struct { localDeployment *string localSealedSecrets *string - k *KindCluster - kubeconfigs []string + mergedKubeconfig string gitServer *test_utils.GitServer } func (p *testProject) init(t *testing.T, k *KindCluster, projectName string) { p.t = t - p.k = k p.gitServer = test_utils.NewGitServer(t) p.projectName = projectName @@ -70,7 +71,13 @@ func (p *testProject) init(t *testing.T, k *KindCluster, projectName string) { return nil }) - p.kubeconfigs = append(p.kubeconfigs, k.Kubeconfig()) + tmpFile, err := os.CreateTemp("", projectName+"-kubeconfig-") + if err != nil { + t.Fatal(err) + } + _ = tmpFile.Close() + p.mergedKubeconfig = tmpFile.Name() + p.mergeKubeconfig(k) } func (p *testProject) cleanup() { @@ -78,6 +85,38 @@ func (p *testProject) cleanup() { p.gitServer.Cleanup() p.gitServer = nil } + if p.mergedKubeconfig != "" { + _ = os.Remove(p.mergedKubeconfig) + p.mergedKubeconfig = "" + } +} + +func (p *testProject) mergeKubeconfig(k *KindCluster) { + p.updateMergedKubeconfig(func(config *clientcmdapi.Config) { + nkcfg, err := clientcmd.LoadFromFile(k.kubeconfig) + if err != nil { + p.t.Fatal(err) + } + + err = mergo.Merge(config, nkcfg) + if err != nil { + p.t.Fatal(err) + } + }) +} + +func (p *testProject) updateMergedKubeconfig(cb func(config *clientcmdapi.Config)) { + mkcfg, err := clientcmd.LoadFromFile(p.mergedKubeconfig) + if err != nil { + p.t.Fatal(err) + } + + cb(mkcfg) + + err = clientcmd.WriteToFile(*mkcfg, p.mergedKubeconfig) + if err != nil { + p.t.Fatal(err) + } } func (p *testProject) updateKluctlYaml(update func(o *uo.UnstructuredObject) error) { @@ -377,13 +416,11 @@ func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { args = append(args, "--local-sealed-secrets", *p.localSealedSecrets) } - sep := ":" if runtime.GOOS == "windows" { - sep = ";" args = append(args, "--debug") } env := os.Environ() - env = append(env, fmt.Sprintf("KUBECONFIG=%s", strings.Join(p.kubeconfigs, sep))) + env = append(env, fmt.Sprintf("KUBECONFIG=%s", p.mergedKubeconfig)) p.t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) From aca1058146d3323b33a8e8c2094813ed6f4ecda8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 09:57:20 +0200 Subject: [PATCH 0250/2268] tests: Add context tests --- e2e/contexts_test.go | 128 +++++++++++++++++++++++++++++++++++++++++++ e2e/kind_cluster.go | 2 + 2 files changed, 130 insertions(+) create mode 100644 e2e/contexts_test.go diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go new file mode 100644 index 000000000..b55c5ab6d --- /dev/null +++ b/e2e/contexts_test.go @@ -0,0 +1,128 @@ +package e2e + +import ( + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "k8s.io/client-go/tools/clientcmd/api" + "sync" + "testing" +) + +func prepareContextTest(t *testing.T, name string) *testProject { + p := &testProject{} + p.init(t, defaultKindCluster1, name) + p.mergeKubeconfig(defaultKindCluster2) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + recreateNamespace(t, defaultKindCluster1, p.projectName) + }() + go func() { + defer wg.Done() + recreateNamespace(t, defaultKindCluster2, p.projectName) + }() + wg.Wait() + + addConfigMapDeployment(p, "cm", nil, resourceOpts{ + name: "cm", + namespace: p.projectName, + }) + + return p +} + +func TestContextCurrent(t *testing.T) { + t.Parallel() + + p := prepareContextTest(t, "context-current") + defer p.cleanup() + + p.updateTarget("test1", func(target *uo.UnstructuredObject) { + // no context set, assume the current one is used + }) + + p.KluctlMust("deploy", "--yes", "-t", "test1") + assertResourceExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + + p.updateMergedKubeconfig(func(config *api.Config) { + config.CurrentContext = defaultKindCluster2.Context + }) + + p.KluctlMust("deploy", "--yes", "-t", "test1") + assertResourceExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") +} + +func TestContext1(t *testing.T) { + t.Parallel() + + p := prepareContextTest(t, "context-1") + defer p.cleanup() + + p.updateTarget("test1", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(defaultKindCluster1.Context, "context") + }) + + p.KluctlMust("deploy", "--yes", "-t", "test1") + assertResourceExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") +} + +func TestContext2(t *testing.T) { + t.Parallel() + + p := prepareContextTest(t, "context-2") + defer p.cleanup() + + p.updateTarget("test1", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(defaultKindCluster2.Context, "context") + }) + + p.KluctlMust("deploy", "--yes", "-t", "test1") + assertResourceExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") +} + +func TestContext1And2(t *testing.T) { + t.Parallel() + + p := prepareContextTest(t, "context-1-and-2") + defer p.cleanup() + + p.updateTarget("test1", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(defaultKindCluster1.Context, "context") + }) + p.updateTarget("test2", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(defaultKindCluster2.Context, "context") + }) + + p.KluctlMust("deploy", "--yes", "-t", "test1") + assertResourceExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + + p.KluctlMust("deploy", "--yes", "-t", "test2") + assertResourceExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") +} + +func TestContextSwitch(t *testing.T) { + t.Parallel() + + p := prepareContextTest(t, "context-switch") + defer p.cleanup() + + p.updateTarget("test1", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(defaultKindCluster1.Context, "context") + }) + + p.KluctlMust("deploy", "--yes", "-t", "test1") + assertResourceExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + + p.updateTarget("test1", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(defaultKindCluster2.Context, "context") + }) + + p.KluctlMust("deploy", "--yes", "-t", "test1") + assertResourceExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") +} diff --git a/e2e/kind_cluster.go b/e2e/kind_cluster.go index 1096058ea..574b55aaa 100644 --- a/e2e/kind_cluster.go +++ b/e2e/kind_cluster.go @@ -21,6 +21,7 @@ import ( type KindCluster struct { Name string + Context string kubeconfig string config *rest.Config } @@ -30,6 +31,7 @@ func CreateKindCluster(name, kubeconfigPath string) (*KindCluster, error) { c := &KindCluster{ Name: name, + Context: fmt.Sprintf("kind-%s", name), kubeconfig: kubeconfigPath, } From 2fcc67d4e7eba465858d4677f6ded7f9b1854b2a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 15:00:07 +0200 Subject: [PATCH 0251/2268] fix: Add some tracing to debug test hangs --- cmd/kluctl/commands/cmd_deploy.go | 3 +++ pkg/kluctl_project/load.go | 3 +++ pkg/kluctl_project/targets.go | 3 +++ 3 files changed, 9 insertions(+) diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 8f2d5a8d4..0e57f6e75 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -49,6 +49,9 @@ func (cmd *deployCmd) Run() error { } func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { + status.Trace(ctx.ctx, "enter runCmdDeploy") + defer status.Trace(ctx.ctx, "leave runCmdDeploy") + cmd2 := commands.NewDeployCommand(ctx.targetCtx.DeploymentCollection) cmd2.ForceApply = cmd.ForceApply cmd2.ReplaceOnError = cmd.ReplaceOnError diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 62c4f140d..78cde9970 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -3,9 +3,12 @@ package kluctl_project import ( "context" "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/status" ) func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*LoadedKluctlProject, error) { + status.Trace(ctx, "enter LoadKluctlProject") + defer status.Trace(ctx, "leave LoadKluctlProject") p := &LoadedKluctlProject{ ctx: ctx, diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 7911a2811..acd861f13 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -26,6 +26,9 @@ type dynamicTargetInfo struct { } func (c *LoadedKluctlProject) loadTargets() error { + status.Trace(c.ctx, "Loading targets") + defer status.Trace(c.ctx, "Done loading targets") + targetNames := make(map[string]bool) c.DynamicTargets = nil From 167590dbb80baeeb01b46bd5751c8ffd07da9bf3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 15:01:07 +0200 Subject: [PATCH 0252/2268] fix: Fix loading of secrets from file The rootKey must be honored. --- pkg/vars/vars_loader.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 2af7b09af..f6f2dfc76 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -97,12 +97,15 @@ func (v *VarsLoader) mergeVars(varsCtx *VarsCtx, newVars *uo.UnstructuredObject, } func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, searchDirs []string, rootKey string) error { - var newVars uo.UnstructuredObject - err := varsCtx.RenderYamlFile(path, searchDirs, &newVars) + newVars := uo.New() + err := varsCtx.RenderYamlFile(path, searchDirs, newVars) if err != nil { return fmt.Errorf("failed to load vars from %s: %w", path, err) } - v.mergeVars(varsCtx, &newVars, rootKey) + if rootKey != "" { + newVars, _, err = newVars.GetNestedObject(rootKey) + } + v.mergeVars(varsCtx, newVars, rootKey) return nil } From 6bc83856316f97c174ef5dbabcf92b2179a268cf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 15:01:39 +0200 Subject: [PATCH 0253/2268] tests: Add sealed secrets tests --- e2e/hooks_test.go | 2 +- e2e/project.go | 37 ++- e2e/seal_test.go | 346 +++++++++++++++++++++++++ e2e/test_resources/README.md | 4 + e2e/test_resources/resources.go | 6 + e2e/test_resources/sealed-secrets.yaml | 307 ++++++++++++++++++++++ e2e/utils_resources.go | 20 +- 7 files changed, 706 insertions(+), 16 deletions(-) create mode 100644 e2e/seal_test.go create mode 100644 e2e/test_resources/README.md create mode 100644 e2e/test_resources/resources.go create mode 100644 e2e/test_resources/sealed-secrets.yaml diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 5c2af1dae..f1e1f60b1 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -49,7 +49,7 @@ func addConfigMap(p *testProject, dir string, opts resourceOpts) { mergeMetadata(o, opts) o.SetNestedField(map[string]interface{}{}, "data") p.addKustomizeResources(dir, []kustomizeResource{ - {fmt.Sprintf("%s.yml", opts.name), o}, + {fmt.Sprintf("%s.yml", opts.name), "", o}, }) } diff --git a/e2e/project.go b/e2e/project.go index c5326febf..6f73c2197 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -206,31 +206,39 @@ func (p *testProject) updateTargetDeprecated(name string, cluster string, args * } func (p *testProject) updateTarget(name string, cb func(target *uo.UnstructuredObject)) { + p.updateNamedListItem(uo.KeyPath{"targets"}, name, cb) +} + +func (p *testProject) updateSecretSet(name string, cb func(secretSet *uo.UnstructuredObject)) { + p.updateNamedListItem(uo.KeyPath{"secretsConfig", "secretSets"}, name, cb) +} + +func (p *testProject) updateNamedListItem(path uo.KeyPath, name string, cb func(item *uo.UnstructuredObject)) { if cb == nil { cb = func(target *uo.UnstructuredObject) {} } p.updateKluctlYaml(func(o *uo.UnstructuredObject) error { - targets, _, _ := o.GetNestedObjectList("targets") - var newTargets []*uo.UnstructuredObject + l, _, _ := o.GetNestedObjectList(path...) + var newList []*uo.UnstructuredObject found := false - for _, t := range targets { - n, _, _ := t.GetNestedString("name") + for _, item := range l { + n, _, _ := item.GetNestedString("name") if n == name { - cb(t) + cb(item) found = true } - newTargets = append(newTargets, t) + newList = append(newList, item) } if !found { n := uo.FromMap(map[string]interface{}{ "name": name, }) cb(n) - newTargets = append(newTargets, n) + newList = append(newList, n) } - _ = o.SetNestedObjectList(newTargets, "targets") + _ = o.SetNestedObjectList(newList, path...) return nil }) } @@ -330,8 +338,9 @@ func (p *testProject) convertInterfaceToList(x interface{}) []interface{} { } type kustomizeResource struct { - name string - content interface{} + name string + fileName string + content interface{} } func (p *testProject) addKustomizeResources(dir string, resources []kustomizeResource) { @@ -340,11 +349,15 @@ func (p *testProject) addKustomizeResources(dir string, resources []kustomizeRes for _, r := range resources { l = append(l, r.name) x := p.convertInterfaceToList(r.content) - err := yaml.WriteYamlAllFile(filepath.Join(p.gitServer.LocalRepoDir(p.getDeploymentRepo()), dir, r.name), x) + fileName := r.fileName + if fileName == "" { + fileName = r.name + } + err := yaml.WriteYamlAllFile(filepath.Join(p.gitServer.LocalRepoDir(p.getDeploymentRepo()), dir, fileName), x) if err != nil { return err } - _, err = wt.Add(filepath.Join(dir, r.name)) + _, err = wt.Add(filepath.Join(dir, fileName)) if err != nil { return err } diff --git a/e2e/seal_test.go b/e2e/seal_test.go new file mode 100644 index 000000000..00ad70147 --- /dev/null +++ b/e2e/seal_test.go @@ -0,0 +1,346 @@ +package e2e + +import ( + "encoding/base64" + "fmt" + "github.com/kluctl/kluctl/v2/e2e/test_resources" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/stretchr/testify/assert" + "os" + "path/filepath" + "testing" + "time" +) + +func installSealedSecretsOperator(t *testing.T, k *KindCluster) { + tmpFile, _ := os.CreateTemp("", "") + tmpFile.Close() + defer os.Remove(tmpFile.Name()) + + _ = utils.FsCopyFile(test_resources.Yamls, "sealed-secrets.yaml", tmpFile.Name()) + + _, err := k.Kubectl("apply", "-f", tmpFile.Name()) + if err != nil { + panic(err) + } + + waitForReadiness(t, k, "kube-system", "deployment/sealed-secrets-controller", 5*time.Minute) +} + +func deleteSealedSecretsOperator(t *testing.T, k *KindCluster) { + _, _ = defaultKindCluster1.Kubectl("-n", "kube-system", "delete", "deployment", "sealed-secrets-controller", "--wait") + _, _ = defaultKindCluster1.Kubectl("-n", "kube-system", "delete", "secret", "-l", "sealedsecrets.bitnami.com/sealed-secrets-key", "--wait") +} + +func prepareSealTest(t *testing.T, k *KindCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject) *testProject { + p := &testProject{} + p.init(t, k, fmt.Sprintf("seal-%s", namespace)) + + recreateNamespace(t, k, namespace) + + addSecretsSet(p, "test", varsSources) + addSecretsSetToTarget(p, "test-target", "test") + + addSecretDeployment(p, "secret-deployment", secrets, true, resourceOpts{name: "secret", namespace: namespace}) + + return p +} + +func addSecretsSet(p *testProject, name string, varsSources []*uo.UnstructuredObject) { + p.updateSecretSet(name, func(secretSet *uo.UnstructuredObject) { + _ = secretSet.SetNestedField(varsSources, "vars") + }) +} + +func addSecretsSetToTarget(p *testProject, targetName string, secretSetName string) { + p.updateTarget(targetName, func(target *uo.UnstructuredObject) { + l, _, _ := target.GetNestedList("sealingConfig", "secretSets") + l = append(l, secretSetName) + _ = target.SetNestedField(l, "sealingConfig", "secretSets") + }) +} + +func assertDecryptedSecrets(t *testing.T, k *KindCluster, namespace string, secretName string, expectedSecrets map[string]string) { + s := k.KubectlYamlMust(t, "-n", namespace, "get", "secret", secretName) + + for key, value := range expectedSecrets { + x, _, _ := s.GetNestedString("data", key) + decoded, _ := base64.StdEncoding.DecodeString(x) + assert.Equal(t, value, string(decoded)) + } +} + +func TestSeal_WithOperator(t *testing.T) { + k := defaultKindCluster1 + namespace := "seal-with-operator" + + deleteSealedSecretsOperator(t, k) + installSealedSecretsOperator(t, k) + + p := prepareSealTest(t, k, namespace, + map[string]string{ + "s1": "{{ secrets.s1 }}", + "s2": "{{ secrets.s2 }}", + }, + []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s1": "v1", + "s2": "v2", + }, + }), + }, + ) + defer p.cleanup() + + p.KluctlMust("seal", "-t", "test-target") + + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + + p.KluctlMust("deploy", "--yes", "-t", "test-target") + + waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) + assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + "s1": "v1", + "s2": "v2", + }) +} + +func TestSeal_WithBootstrap(t *testing.T) { + k := defaultKindCluster2 + namespace := "seal-with-bootstrap" + + deleteSealedSecretsOperator(t, k) + + p := prepareSealTest(t, k, namespace, + map[string]string{ + "s1": "{{ secrets.s1 }}", + "s2": "{{ secrets.s2 }}", + }, + []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s1": "v1", + "s2": "v2", + }, + }), + }, + ) + defer p.cleanup() + + p.KluctlMust("seal", "-t", "test-target") + + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + + installSealedSecretsOperator(t, k) + + p.KluctlMust("deploy", "--yes", "-t", "test-target") + + waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) + assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + "s1": "v1", + "s2": "v2", + }) +} + +func TestSeal_MultipleVarSources(t *testing.T) { + t.Parallel() + + k := defaultKindCluster1 + namespace := "seal-multiple-vs" + + installSealedSecretsOperator(t, k) + + p := prepareSealTest(t, k, namespace, + map[string]string{ + "s1": "{{ secrets.s1 }}", + "s2": "{{ secrets.s2 }}", + }, + []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s1": "v1", + }, + }), + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s2": "v2", + }, + }), + }, + ) + defer p.cleanup() + + p.KluctlMust("seal", "-t", "test-target") + + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + + p.KluctlMust("deploy", "--yes", "-t", "test-target") + + waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) + assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + "s1": "v1", + "s2": "v2", + }) +} + +func TestSeal_MultipleSecretSets(t *testing.T) { + t.Parallel() + + k := defaultKindCluster1 + namespace := "seal-multiple-ss" + + installSealedSecretsOperator(t, k) + + p := prepareSealTest(t, k, namespace, + map[string]string{ + "s1": "{{ secrets.s1 }}", + "s2": "{{ secrets.s2 }}", + }, + []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s1": "v1", + }, + }), + }, + ) + defer p.cleanup() + + addSecretsSet(p, "test2", []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s2": "v2", + }, + }), + }) + addSecretsSetToTarget(p, "test-target", "test2") + + p.KluctlMust("seal", "-t", "test-target") + + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + + p.KluctlMust("deploy", "--yes", "-t", "test-target") + + waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) + assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + "s1": "v1", + "s2": "v2", + }) +} + +func TestSeal_MultipleTargets(t *testing.T) { + k := defaultKindCluster1 + namespace := "seal-multiple-targets" + + installSealedSecretsOperator(t, k) + installSealedSecretsOperator(t, defaultKindCluster2) + + p := prepareSealTest(t, k, namespace, + map[string]string{ + "s1": "{{ secrets.s1 }}", + "s2": "{{ secrets.s2 }}", + }, + []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s1": "v1", + }, + }), + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s2": "v2", + }, + }), + }, + ) + defer p.cleanup() + + addSecretsSet(p, "test2", []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s1": "v3", + "s2": "v4", + }, + }), + }) + addSecretsSetToTarget(p, "test-target2", "test2") + + p.mergeKubeconfig(defaultKindCluster2) + recreateNamespace(t, defaultKindCluster2, namespace) + p.updateTarget("test-target", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(defaultKindCluster1.Context, "context") + }) + p.updateTarget("test-target2", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(defaultKindCluster2.Context, "context") + }) + + p.KluctlMust("seal", "-t", "test-target") + p.KluctlMust("seal", "-t", "test-target2") + + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target2/secret-secret.yml")) + + p.KluctlMust("deploy", "--yes", "-t", "test-target") + p.KluctlMust("deploy", "--yes", "-t", "test-target2") + + waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) + assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + "s1": "v1", + "s2": "v2", + }) + waitForReadiness(t, defaultKindCluster2, namespace, "secret/secret", 1*time.Minute) + assertDecryptedSecrets(t, defaultKindCluster2, namespace, "secret", map[string]string{ + "s1": "v3", + "s2": "v4", + }) +} + +func TestSeal_File(t *testing.T) { + k := defaultKindCluster1 + namespace := "seal-file" + + installSealedSecretsOperator(t, k) + + p := prepareSealTest(t, k, namespace, + map[string]string{ + "s1": "{{ secrets.s1 }}", + "s2": "{{ secrets.s2 }}", + }, + []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "file": utils.StrPtr("secret-values.yaml"), + }), + }, + ) + defer p.cleanup() + + p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), "secret-values.yaml", func(o *uo.UnstructuredObject) error { + *o = *uo.FromMap(map[string]interface{}{ + "secrets": map[string]interface{}{ + "s1": "v1", + "s2": "v2", + }, + }) + return nil + }, "") + + p.KluctlMust("seal", "-t", "test-target") + + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + + p.KluctlMust("deploy", "--yes", "-t", "test-target") + + waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) + assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + "s1": "v1", + "s2": "v2", + }) +} diff --git a/e2e/test_resources/README.md b/e2e/test_resources/README.md new file mode 100644 index 000000000..75d21866f --- /dev/null +++ b/e2e/test_resources/README.md @@ -0,0 +1,4 @@ +# sealed-secrets.yaml + +helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets +helm template sealed-secrets-controller sealed-secrets/sealed-secrets -n kube-system --include-crds > sealed-secrets.yaml diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go new file mode 100644 index 000000000..0ec6d270c --- /dev/null +++ b/e2e/test_resources/resources.go @@ -0,0 +1,6 @@ +package test_resources + +import "embed" + +//go:embed *.yaml +var Yamls embed.FS diff --git a/e2e/test_resources/sealed-secrets.yaml b/e2e/test_resources/sealed-secrets.yaml new file mode 100644 index 000000000..b45788937 --- /dev/null +++ b/e2e/test_resources/sealed-secrets.yaml @@ -0,0 +1,307 @@ +--- +# Source: crds/sealedsecret-crd.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: sealedsecrets.bitnami.com +spec: + group: bitnami.com + names: + kind: SealedSecret + listKind: SealedSecretList + plural: sealedsecrets + singular: sealedsecret + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + x-kubernetes-preserve-unknown-fields: true + status: + x-kubernetes-preserve-unknown-fields: true + +--- +# Source: sealed-secrets/templates/service-account.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: sealed-secrets-controller + namespace: kube-system + labels: + app.kubernetes.io/name: sealed-secrets + helm.sh/chart: sealed-secrets-2.1.8 + app.kubernetes.io/instance: sealed-secrets-controller + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: v0.17.5 +--- +# Source: sealed-secrets/templates/cluster-role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: secrets-unsealer + labels: + app.kubernetes.io/name: sealed-secrets + helm.sh/chart: sealed-secrets-2.1.8 + app.kubernetes.io/instance: sealed-secrets-controller + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: v0.17.5 +rules: + - apiGroups: + - bitnami.com + resources: + - sealedsecrets + verbs: + - get + - list + - watch + - apiGroups: + - bitnami.com + resources: + - sealedsecrets/status + verbs: + - update + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - create + - update + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +# Source: sealed-secrets/templates/cluster-role-binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: sealed-secrets-controller + labels: + app.kubernetes.io/name: sealed-secrets + helm.sh/chart: sealed-secrets-2.1.8 + app.kubernetes.io/instance: sealed-secrets-controller + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: v0.17.5 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: secrets-unsealer +subjects: + - apiGroup: "" + kind: ServiceAccount + name: sealed-secrets-controller + namespace: kube-system +--- +# Source: sealed-secrets/templates/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: sealed-secrets-controller-key-admin + namespace: kube-system + labels: + app.kubernetes.io/name: sealed-secrets + helm.sh/chart: sealed-secrets-2.1.8 + app.kubernetes.io/instance: sealed-secrets-controller + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: v0.17.5 +rules: + - apiGroups: + - "" + resourceNames: + - sealed-secrets-key + resources: + - secrets + verbs: + - get + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - list +--- +# Source: sealed-secrets/templates/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: sealed-secrets-controller-service-proxier + namespace: kube-system + labels: + app.kubernetes.io/name: sealed-secrets + helm.sh/chart: sealed-secrets-2.1.8 + app.kubernetes.io/instance: sealed-secrets-controller + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: v0.17.5 +rules: + - apiGroups: + - "" + resourceNames: + - sealed-secrets-controller + resources: + - services + verbs: + - get + - apiGroups: + - "" + resourceNames: + - 'http:sealed-secrets-controller:' + - 'http:sealed-secrets-controller:http' + - sealed-secrets-controller + resources: + - services/proxy + verbs: + - create + - get +--- +# Source: sealed-secrets/templates/role-binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: sealed-secrets-controller-key-admin + namespace: kube-system + labels: + app.kubernetes.io/name: sealed-secrets + helm.sh/chart: sealed-secrets-2.1.8 + app.kubernetes.io/instance: sealed-secrets-controller + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: v0.17.5 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: sealed-secrets-controller-key-admin +subjects: + - apiGroup: "" + kind: ServiceAccount + name: sealed-secrets-controller + namespace: kube-system +--- +# Source: sealed-secrets/templates/role-binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: sealed-secrets-controller-service-proxier + namespace: kube-system + labels: + app.kubernetes.io/name: sealed-secrets + helm.sh/chart: sealed-secrets-2.1.8 + app.kubernetes.io/instance: sealed-secrets-controller + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: v0.17.5 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: sealed-secrets-controller-service-proxier +subjects: +- apiGroup: rbac.authorization.k8s.io + kind: Group + name: system:authenticated +--- +# Source: sealed-secrets/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: sealed-secrets-controller + namespace: kube-system + labels: + app.kubernetes.io/name: sealed-secrets + helm.sh/chart: sealed-secrets-2.1.8 + app.kubernetes.io/instance: sealed-secrets-controller + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: v0.17.5 +spec: + type: ClusterIP + ports: + - name: http + port: 8080 + targetPort: http + nodePort: null + selector: + app.kubernetes.io/name: sealed-secrets + app.kubernetes.io/instance: sealed-secrets-controller +--- +# Source: sealed-secrets/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sealed-secrets-controller + namespace: kube-system + labels: + app.kubernetes.io/name: sealed-secrets + helm.sh/chart: sealed-secrets-2.1.8 + app.kubernetes.io/instance: sealed-secrets-controller + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: v0.17.5 +spec: + selector: + matchLabels: + app.kubernetes.io/name: sealed-secrets + app.kubernetes.io/instance: sealed-secrets-controller + template: + metadata: + labels: + app.kubernetes.io/name: sealed-secrets + app.kubernetes.io/instance: sealed-secrets-controller + spec: + securityContext: + fsGroup: 65534 + serviceAccountName: sealed-secrets-controller + containers: + - name: controller + command: + - controller + args: + - --update-status + - --key-prefix + - "sealed-secrets-key" + image: docker.io/bitnami/sealed-secrets-controller:v0.17.5 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + name: http + livenessProbe: + failureThreshold: 3 + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + httpGet: + path: /healthz + port: http + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + httpGet: + path: /healthz + port: http + resources: + limits: {} + requests: {} + securityContext: + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1001 + volumeMounts: + - mountPath: /tmp + name: tmp + volumes: + - name: tmp + emptyDir: {} diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 255e07476..0dc063e7f 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -2,6 +2,7 @@ package e2e import ( "bytes" + "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "text/template" ) @@ -56,7 +57,20 @@ func addConfigMapDeployment(p *testProject, dir string, data map[string]string, o.SetNestedField(data, "data") } p.addKustomizeDeployment(dir, []kustomizeResource{ - {"configmap.yml", o}, + {fmt.Sprintf("configmap-%s.yml", opts.name), "", o}, + }, opts.tags) +} + +func addSecretDeployment(p *testProject, dir string, data map[string]string, sealedSecret bool, opts resourceOpts) { + o := uo.New() + o.SetK8sGVKs("", "v1", "Secret") + mergeMetadata(o, opts) + if data != nil { + o.SetNestedField(data, "stringData") + } + fname := fmt.Sprintf("secret-%s.yml", opts.name) + p.addKustomizeDeployment(dir, []kustomizeResource{ + {fname, fname + ".sealme", o}, }, opts.tags) } @@ -71,8 +85,8 @@ func addDeploymentHelper(p *testProject, dir string, o *uo.UnstructuredObject, o mergeMetadata(o, opts) resources := []kustomizeResource{ - {"rbac.yml", rbac}, - {"deploy.yml", o}, + {"rbac.yml", "", rbac}, + {"deploy.yml", "", o}, } p.addKustomizeDeployment(dir, resources, opts.tags) From b1962eb938ebbec9497191f8b2d2483d9d8e4656 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 15:39:01 +0200 Subject: [PATCH 0254/2268] tests: Ignore NotFound errors when waiting for readiness --- e2e/utils.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/e2e/utils.go b/e2e/utils.go index 79a80696d..f7f3d5f7c 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -32,7 +32,11 @@ func waitForReadiness(t *testing.T, k *KindCluster, namespace string, resource s for time.Now().Sub(startTime) < timeout { y, err := k.KubectlYaml("-n", namespace, "get", resource) if err != nil { - t.Fatal(err) + if ee, ok := err.(*exec.ExitError); !ok || strings.Index(string(ee.Stderr), "NotFound") == -1 { + t.Fatal(err) + } + time.Sleep(1 * time.Second) + continue } v := validation.ValidateObject(nil, y, true) From 9bb46dd7726cc75130ee7ee5349bf8d67170b535 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 16:08:27 +0200 Subject: [PATCH 0255/2268] tests: Move KindCluster into test-utils package --- e2e/default_clusters.go | 56 ++++++++++++++++++++ e2e/external_projects_test.go | 1 + e2e/hooks_test.go | 15 +++--- e2e/inclusion_test.go | 5 +- e2e/project.go | 6 +-- e2e/seal_test.go | 9 ++-- e2e/utils.go | 18 +++---- {e2e => internal/test-utils}/kind_cluster.go | 47 +--------------- 8 files changed, 84 insertions(+), 73 deletions(-) create mode 100644 e2e/default_clusters.go rename {e2e => internal/test-utils}/kind_cluster.go (75%) diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go new file mode 100644 index 000000000..664cf31cb --- /dev/null +++ b/e2e/default_clusters.go @@ -0,0 +1,56 @@ +package e2e + +import ( + "fmt" + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils" + "os" + "path/filepath" + "sync" +) + +func createKindCluster(name string, kubeconfig string) *test_utils.KindCluster { + k, err := test_utils.CreateKindCluster(name, kubeconfig) + if err != nil { + panic(err) + } + return k +} + +func createDefaultKindCluster(num int) *test_utils.KindCluster { + kindClusterName := os.Getenv(fmt.Sprintf("KIND_CLUSTER_NAME%d", num)) + kindKubeconfig := os.Getenv(fmt.Sprintf("KIND_KUBECONFIG%d", num)) + if kindClusterName == "" { + kindClusterName = fmt.Sprintf("kluctl-e2e-%d", num) + } + if kindKubeconfig == "" { + kindKubeconfig = filepath.Join(utils.GetTmpBaseDir(), fmt.Sprintf("kluctl-e2e-kubeconfig-%d.yml", num)) + } + return createKindCluster(kindClusterName, kindKubeconfig) +} + +func createDefaultKindClusters() (*test_utils.KindCluster, *test_utils.KindCluster) { + var k1, k2 *test_utils.KindCluster + + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + k1 = createDefaultKindCluster(1) + }() + go func() { + defer wg.Done() + k2 = createDefaultKindCluster(2) + }() + wg.Wait() + return k1, k2 +} + +var ( + defaultKindCluster1, defaultKindCluster2 = createDefaultKindClusters() +) + +func init() { + deleteTestNamespaces(defaultKindCluster1) + deleteTestNamespaces(defaultKindCluster2) +} diff --git a/e2e/external_projects_test.go b/e2e/external_projects_test.go index eefd91b17..bb2f2094e 100644 --- a/e2e/external_projects_test.go +++ b/e2e/external_projects_test.go @@ -2,6 +2,7 @@ package e2e import ( "fmt" + "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "testing" "time" diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index f1e1f60b1..18149608c 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -3,6 +3,7 @@ package e2e import ( "encoding/base64" "fmt" + "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "testing" ) @@ -53,7 +54,7 @@ func addConfigMap(p *testProject, dir string, opts resourceOpts) { }) } -func getHookResult(t *testing.T, p *testProject, k *KindCluster, secretName string) *uo.UnstructuredObject { +func getHookResult(t *testing.T, p *testProject, k *test_utils.KindCluster, secretName string) *uo.UnstructuredObject { o := k.KubectlYamlMust(t, "-n", p.projectName, "get", "secret", secretName) s, ok, err := o.GetNestedString("data", "result") if err != nil { @@ -73,7 +74,7 @@ func getHookResult(t *testing.T, p *testProject, k *KindCluster, secretName stri return r } -func getHookResultCMNames(t *testing.T, p *testProject, k *KindCluster, second bool) []string { +func getHookResultCMNames(t *testing.T, p *testProject, k *test_utils.KindCluster, second bool) []string { secretName := "hook-result" if second { secretName = "hook-result2" @@ -87,7 +88,7 @@ func getHookResultCMNames(t *testing.T, p *testProject, k *KindCluster, second b return names } -func assertHookResultCMName(t *testing.T, p *testProject, k *KindCluster, second bool, cmName string) { +func assertHookResultCMName(t *testing.T, p *testProject, k *test_utils.KindCluster, second bool, cmName string) { names := getHookResultCMNames(t, p, k, second) for _, x := range names { if x == cmName { @@ -97,7 +98,7 @@ func assertHookResultCMName(t *testing.T, p *testProject, k *KindCluster, second t.Fatalf("%s not found in hook result", cmName) } -func assertHookResultNotCMName(t *testing.T, p *testProject, k *KindCluster, second bool, cmName string) { +func assertHookResultNotCMName(t *testing.T, p *testProject, k *test_utils.KindCluster, second bool, cmName string) { names := getHookResultCMNames(t, p, k, second) for _, x := range names { if x == cmName { @@ -106,7 +107,7 @@ func assertHookResultNotCMName(t *testing.T, p *testProject, k *KindCluster, sec } } -func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) (*testProject, *KindCluster) { +func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) (*testProject, *test_utils.KindCluster) { isDone := false namespace := fmt.Sprintf("hook-%s", name) k := defaultKindCluster1 @@ -129,13 +130,13 @@ func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletion return p, k } -func ensureHookExecuted(t *testing.T, p *testProject, k *KindCluster) { +func ensureHookExecuted(t *testing.T, p *testProject, k *test_utils.KindCluster) { _, _ = k.Kubectl("delete", "-n", p.projectName, "secret", "hook-result", "hook-result2") p.KluctlMust("deploy", "--yes", "-t", "test") assertResourceExists(t, k, p.projectName, "ConfigMap/cm1") } -func ensureHookNotExecuted(t *testing.T, p *testProject, k *KindCluster) { +func ensureHookNotExecuted(t *testing.T, p *testProject, k *test_utils.KindCluster) { _, _ = k.Kubectl("delete", "-n", p.projectName, "secret", "hook-result", "hook-result2") p.KluctlMust("deploy", "--yes", "-t", "test") assertResourceNotExists(t, k, p.projectName, "Secret/hook-result") diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 21a3028eb..0395bff9f 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -2,12 +2,13 @@ package e2e import ( "fmt" + "github.com/kluctl/kluctl/v2/internal/test-utils" "path/filepath" "reflect" "testing" ) -func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bool) (*testProject, *KindCluster) { +func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bool) (*testProject, *test_utils.KindCluster) { isDone := false k := defaultKindCluster1 @@ -48,7 +49,7 @@ func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bo return p, k } -func assertExistsHelper(t *testing.T, p *testProject, k *KindCluster, shouldExists map[string]bool, add []string, remove []string) { +func assertExistsHelper(t *testing.T, p *testProject, k *test_utils.KindCluster, shouldExists map[string]bool, add []string, remove []string) { for _, x := range add { shouldExists[x] = true } diff --git a/e2e/project.go b/e2e/project.go index 6f73c2197..3fb63a6fa 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -36,7 +36,7 @@ type testProject struct { gitServer *test_utils.GitServer } -func (p *testProject) init(t *testing.T, k *KindCluster, projectName string) { +func (p *testProject) init(t *testing.T, k *test_utils.KindCluster, projectName string) { p.t = t p.gitServer = test_utils.NewGitServer(t) p.projectName = projectName @@ -91,7 +91,7 @@ func (p *testProject) cleanup() { } } -func (p *testProject) mergeKubeconfig(k *KindCluster) { +func (p *testProject) mergeKubeconfig(k *test_utils.KindCluster) { p.updateMergedKubeconfig(func(config *clientcmdapi.Config) { nkcfg, err := clientcmd.LoadFromFile(k.kubeconfig) if err != nil { @@ -186,7 +186,7 @@ func (p *testProject) updateCluster(name string, context string, vars *uo.Unstru }, fmt.Sprintf("add/update cluster %s", name)) } -func (p *testProject) updateKindCluster(k *KindCluster, vars *uo.UnstructuredObject) { +func (p *testProject) updateKindCluster(k *test_utils.KindCluster, vars *uo.UnstructuredObject) { context, err := k.Kubectl("config", "current-context") if err != nil { p.t.Fatal(err) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 00ad70147..b28cc12e7 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "fmt" "github.com/kluctl/kluctl/v2/e2e/test_resources" + "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" @@ -13,7 +14,7 @@ import ( "time" ) -func installSealedSecretsOperator(t *testing.T, k *KindCluster) { +func installSealedSecretsOperator(t *testing.T, k *test_utils.KindCluster) { tmpFile, _ := os.CreateTemp("", "") tmpFile.Close() defer os.Remove(tmpFile.Name()) @@ -28,12 +29,12 @@ func installSealedSecretsOperator(t *testing.T, k *KindCluster) { waitForReadiness(t, k, "kube-system", "deployment/sealed-secrets-controller", 5*time.Minute) } -func deleteSealedSecretsOperator(t *testing.T, k *KindCluster) { +func deleteSealedSecretsOperator(t *testing.T, k *test_utils.KindCluster) { _, _ = defaultKindCluster1.Kubectl("-n", "kube-system", "delete", "deployment", "sealed-secrets-controller", "--wait") _, _ = defaultKindCluster1.Kubectl("-n", "kube-system", "delete", "secret", "-l", "sealedsecrets.bitnami.com/sealed-secrets-key", "--wait") } -func prepareSealTest(t *testing.T, k *KindCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject) *testProject { +func prepareSealTest(t *testing.T, k *test_utils.KindCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject) *testProject { p := &testProject{} p.init(t, k, fmt.Sprintf("seal-%s", namespace)) @@ -61,7 +62,7 @@ func addSecretsSetToTarget(p *testProject, targetName string, secretSetName stri }) } -func assertDecryptedSecrets(t *testing.T, k *KindCluster, namespace string, secretName string, expectedSecrets map[string]string) { +func assertDecryptedSecrets(t *testing.T, k *test_utils.KindCluster, namespace string, secretName string, expectedSecrets map[string]string) { s := k.KubectlYamlMust(t, "-n", namespace, "get", "secret", secretName) for key, value := range expectedSecrets { diff --git a/e2e/utils.go b/e2e/utils.go index f7f3d5f7c..274f5946c 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "fmt" + "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" log "github.com/sirupsen/logrus" @@ -15,17 +16,17 @@ import ( "time" ) -func recreateNamespace(t *testing.T, k *KindCluster, namespace string) { +func recreateNamespace(t *testing.T, k *test_utils.KindCluster, namespace string) { _, _ = k.Kubectl("delete", "ns", namespace) k.KubectlMust(t, "create", "ns", namespace) k.KubectlMust(t, "label", "ns", namespace, "kluctl-e2e=true") } -func deleteTestNamespaces(k *KindCluster) { +func deleteTestNamespaces(k *test_utils.KindCluster) { _, _ = k.Kubectl("delete", "ns", "-l", "kubectl-e2e=true") } -func waitForReadiness(t *testing.T, k *KindCluster, namespace string, resource string, timeout time.Duration) bool { +func waitForReadiness(t *testing.T, k *test_utils.KindCluster, namespace string, resource string, timeout time.Duration) bool { t.Logf("Waiting for readiness: %s/%s", namespace, resource) startTime := time.Now() @@ -59,13 +60,13 @@ func waitForReadiness(t *testing.T, k *KindCluster, namespace string, resource s return false } -func assertReadiness(t *testing.T, k *KindCluster, namespace string, resource string, timeout time.Duration) { +func assertReadiness(t *testing.T, k *test_utils.KindCluster, namespace string, resource string, timeout time.Duration) { if !waitForReadiness(t, k, namespace, resource, timeout) { t.Errorf("%s/%s did not get ready in time", namespace, resource) } } -func assertResourceExists(t *testing.T, k *KindCluster, namespace string, resource string) *uo.UnstructuredObject { +func assertResourceExists(t *testing.T, k *test_utils.KindCluster, namespace string, resource string) *uo.UnstructuredObject { var args []string if namespace != "" { args = append(args, "-n", namespace) @@ -74,7 +75,7 @@ func assertResourceExists(t *testing.T, k *KindCluster, namespace string, resour return k.KubectlYamlMust(t, args...) } -func assertResourceNotExists(t *testing.T, k *KindCluster, namespace string, resource string) { +func assertResourceNotExists(t *testing.T, k *test_utils.KindCluster, namespace string, resource string) { var args []string if namespace != "" { args = append(args, "-n", namespace) @@ -136,8 +137,3 @@ func runHelper(t *testing.T, cmd *exec.Cmd) (string, string, error) { err = cmd.Run() return stdoutBuf.String(), stderrBuf.String(), err } - -func init() { - deleteTestNamespaces(defaultKindCluster1) - deleteTestNamespaces(defaultKindCluster2) -} diff --git a/e2e/kind_cluster.go b/internal/test-utils/kind_cluster.go similarity index 75% rename from e2e/kind_cluster.go rename to internal/test-utils/kind_cluster.go index 574b55aaa..d527f0173 100644 --- a/e2e/kind_cluster.go +++ b/internal/test-utils/kind_cluster.go @@ -1,20 +1,16 @@ -package e2e +package test_utils import ( "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/pkg/errors" - log "github.com/sirupsen/logrus" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "os" "os/exec" - "path/filepath" "sigs.k8s.io/kind/pkg/cluster" kindcmd "sigs.k8s.io/kind/pkg/cmd" - "sync" "testing" "time" ) @@ -142,44 +138,3 @@ func kindCreate(name, kubeconfig string) error { } } } - -func createKindCluster(name string, kubeconfig string) *KindCluster { - k, err := CreateKindCluster(name, kubeconfig) - if err != nil { - log.Fatal(err) - } - return k -} - -func createDefaultKindCluster(num int) *KindCluster { - kindClusterName := os.Getenv(fmt.Sprintf("KIND_CLUSTER_NAME%d", num)) - kindKubeconfig := os.Getenv(fmt.Sprintf("KIND_KUBECONFIG%d", num)) - if kindClusterName == "" { - kindClusterName = fmt.Sprintf("kluctl-e2e-%d", num) - } - if kindKubeconfig == "" { - kindKubeconfig = filepath.Join(utils.GetTmpBaseDir(), fmt.Sprintf("kluctl-e2e-kubeconfig-%d.yml", num)) - } - return createKindCluster(kindClusterName, kindKubeconfig) -} - -func createDefaultKindClusters() (*KindCluster, *KindCluster) { - var k1, k2 *KindCluster - - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() - k1 = createDefaultKindCluster(1) - }() - go func() { - defer wg.Done() - k2 = createDefaultKindCluster(2) - }() - wg.Wait() - return k1, k2 -} - -var ( - defaultKindCluster1, defaultKindCluster2 = createDefaultKindClusters() -) From 9ea226e761915421f31d621ca77ff1279e1a481f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 1 Jun 2022 12:32:05 +0200 Subject: [PATCH 0256/2268] tests: Move starting of kind clusters into go code And allow to specify extra ports to be exposed. --- .github/workflows/tests.yml | 38 +++++------ e2e/default_clusters.go | 68 +++++++++++++------- e2e/external_projects_test.go | 1 - e2e/project.go | 2 +- hack/start-kind-cluster.sh | 28 --------- internal/test-utils/kind_cluster.go | 97 +++++++++++++++++++---------- 6 files changed, 125 insertions(+), 109 deletions(-) delete mode 100755 hack/start-kind-cluster.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 70690c075..b966ab2c8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -240,33 +240,27 @@ jobs: shell: bash run: | if [ "${{ runner.os }}" == "Linux" ]; then - PORT1=10000 - PORT2=20000 + echo "KIND_API_PORT1=10000" >> $GITHUB_ENV + echo "KIND_API_PORT2=20000" >> $GITHUB_ENV + echo "KIND_EXTRA_PORTS_OFFSET1=30000" >> $GITHUB_ENV + echo "KIND_EXTRA_PORTS_OFFSET2=31000" >> $GITHUB_ENV elif [ "${{ runner.os }}" == "Windows" ]; then - PORT1=10001 - PORT2=20001 + echo "KIND_API_PORT1=10001" >> $GITHUB_ENV + echo "KIND_API_PORT2=20001" >> $GITHUB_ENV + echo "KIND_EXTRA_PORTS_OFFSET1=30100" >> $GITHUB_ENV + echo "KIND_EXTRA_PORTS_OFFSET2=31100" >> $GITHUB_ENV else - PORT1=10002 - PORT2=20002 + echo "KIND_API_PORT1=10002" >> $GITHUB_ENV + echo "KIND_API_PORT2=20002" >> $GITHUB_ENV + echo "KIND_EXTRA_PORTS_OFFSET1=30200" >> $GITHUB_ENV + echo "KIND_EXTRA_PORTS_OFFSET2=31200" >> $GITHUB_ENV fi KIND_CLUSTER_NAME_BASE=$(echo "kluctl-${{ runner.os }}" | awk '{{ print tolower($1) }}') - KIND_CLUSTER_NAME1=$KIND_CLUSTER_NAME_BASE-1 - KIND_KUBECONFIG1=$(pwd)/kind-kubeconfig-1 - echo "KIND_CLUSTER_NAME1=$KIND_CLUSTER_NAME1" >> $GITHUB_ENV - echo "KIND_KUBECONFIG1=$KIND_KUBECONFIG1" >> $GITHUB_ENV - ./hack/start-kind-cluster.sh "$KIND_CLUSTER_NAME1" "$DOCKER_IP" "$PORT1" "$KIND_KUBECONFIG1" & - PID1=$! - - KIND_CLUSTER_NAME2=$KIND_CLUSTER_NAME_BASE-2 - KIND_KUBECONFIG2=$(pwd)/kind-kubeconfig-2 - echo "KIND_CLUSTER_NAME2=$KIND_CLUSTER_NAME2" >> $GITHUB_ENV - echo "KIND_KUBECONFIG2=$KIND_KUBECONFIG2" >> $GITHUB_ENV - ./hack/start-kind-cluster.sh "$KIND_CLUSTER_NAME2" "$DOCKER_IP" "$PORT2" "$KIND_KUBECONFIG2" & - PID2=$1 - - wait $PID1 - wait $PID2 + echo "KIND_API_HOST1=$DOCKER_IP" >> $GITHUB_ENV + echo "KIND_API_HOST2=$DOCKER_IP" >> $GITHUB_ENV + echo "KIND_CLUSTER_NAME1=$KIND_CLUSTER_NAME_BASE-1" >> $GITHUB_ENV + echo "KIND_CLUSTER_NAME2=$KIND_CLUSTER_NAME_BASE-2" >> $GITHUB_ENV - name: Download artifacts uses: actions/download-artifact@v2 - name: Run e2e tests diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 664cf31cb..466df5b4f 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -6,51 +6,71 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "os" "path/filepath" + "strconv" "sync" ) -func createKindCluster(name string, kubeconfig string) *test_utils.KindCluster { - k, err := test_utils.CreateKindCluster(name, kubeconfig) - if err != nil { - panic(err) - } - return k -} - -func createDefaultKindCluster(num int) *test_utils.KindCluster { +func createDefaultKindCluster(num int) (*test_utils.KindCluster, int) { kindClusterName := os.Getenv(fmt.Sprintf("KIND_CLUSTER_NAME%d", num)) + kindApiHost := os.Getenv(fmt.Sprintf("KIND_API_HOST%d", num)) + kindApiPort := os.Getenv(fmt.Sprintf("KIND_API_PORT%d", num)) + kindExtraPortsOffset := os.Getenv(fmt.Sprintf("KIND_EXTRA_PORTS_OFFSET%d", num)) kindKubeconfig := os.Getenv(fmt.Sprintf("KIND_KUBECONFIG%d", num)) if kindClusterName == "" { kindClusterName = fmt.Sprintf("kluctl-e2e-%d", num) } + if kindApiHost == "" { + kindApiHost = "localhost" + } + if kindExtraPortsOffset == "" { + kindExtraPortsOffset = fmt.Sprintf("%d", 30000+num*1000) + } if kindKubeconfig == "" { kindKubeconfig = filepath.Join(utils.GetTmpBaseDir(), fmt.Sprintf("kluctl-e2e-kubeconfig-%d.yml", num)) } - return createKindCluster(kindClusterName, kindKubeconfig) + + var err error + var kindApiPortInt, kindExtraPortsOffsetInt int64 + + if kindApiPort != "" { + kindApiPortInt, err = strconv.ParseInt(kindApiPort, 0, 32) + if err != nil { + panic(err) + } + } + kindExtraPortsOffsetInt, err = strconv.ParseInt(kindExtraPortsOffset, 0, 32) + if err != nil { + panic(err) + } + + vaultPort := int(kindExtraPortsOffsetInt) + 0 + + kindExtraPorts := map[int]int{ + vaultPort: 30000, + } + + k, err := test_utils.CreateKindCluster(kindClusterName, kindApiHost, int(kindApiPortInt), kindExtraPorts, kindKubeconfig) + if err != nil { + panic(err) + } + return k, vaultPort } -func createDefaultKindClusters() (*test_utils.KindCluster, *test_utils.KindCluster) { - var k1, k2 *test_utils.KindCluster +var defaultKindCluster1, defaultKindCluster2 *test_utils.KindCluster +var defaultKindCluster1VaultPort, defaultKindCluster2VaultPort int +func init() { var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() - k1 = createDefaultKindCluster(1) + defaultKindCluster1, defaultKindCluster1VaultPort = createDefaultKindCluster(1) + deleteTestNamespaces(defaultKindCluster1) }() go func() { defer wg.Done() - k2 = createDefaultKindCluster(2) + defaultKindCluster2, defaultKindCluster2VaultPort = createDefaultKindCluster(2) + deleteTestNamespaces(defaultKindCluster2) }() wg.Wait() - return k1, k2 -} - -var ( - defaultKindCluster1, defaultKindCluster2 = createDefaultKindClusters() -) - -func init() { - deleteTestNamespaces(defaultKindCluster1) - deleteTestNamespaces(defaultKindCluster2) } diff --git a/e2e/external_projects_test.go b/e2e/external_projects_test.go index bb2f2094e..eefd91b17 100644 --- a/e2e/external_projects_test.go +++ b/e2e/external_projects_test.go @@ -2,7 +2,6 @@ package e2e import ( "fmt" - "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "testing" "time" diff --git a/e2e/project.go b/e2e/project.go index 3fb63a6fa..09367ff6c 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -93,7 +93,7 @@ func (p *testProject) cleanup() { func (p *testProject) mergeKubeconfig(k *test_utils.KindCluster) { p.updateMergedKubeconfig(func(config *clientcmdapi.Config) { - nkcfg, err := clientcmd.LoadFromFile(k.kubeconfig) + nkcfg, err := clientcmd.LoadFromFile(k.Kubeconfig) if err != nil { p.t.Fatal(err) } diff --git a/hack/start-kind-cluster.sh b/hack/start-kind-cluster.sh deleted file mode 100755 index 315d6887a..000000000 --- a/hack/start-kind-cluster.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -set -e - -NAME=$1 -IP=$2 -PORT=$3 -export KUBECONFIG=$4 - -cat << EOF > kind-cluster-$NAME.yml -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -networking: - apiServerAddress: "0.0.0.0" - apiServerPort: $PORT -EOF - -rm -f $(pwd)/kind-kubeconfig -kind create cluster --config kind-cluster-$NAME.yml --name $NAME - -# Rewrite cluster info to point to docker host -# This also fully disables TLS verification -kubectl config view -ojson --raw \ - | jq ".clusters[0].cluster.\"insecure-skip-tls-verify\"=true" \ - | jq "del(.clusters[0].cluster.\"certificate-authority-data\")" \ - | jq ".clusters[0].cluster.server=\"https://$IP:$PORT\"" \ -> $KUBECONFIG-tmp -mv $KUBECONFIG-tmp $KUBECONFIG diff --git a/internal/test-utils/kind_cluster.go b/internal/test-utils/kind_cluster.go index d527f0173..eaa7c8dd5 100644 --- a/internal/test-utils/kind_cluster.go +++ b/internal/test-utils/kind_cluster.go @@ -4,11 +4,12 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - "github.com/pkg/errors" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "net/url" "os" "os/exec" + "sigs.k8s.io/kind/pkg/apis/config/v1alpha4" "sigs.k8s.io/kind/pkg/cluster" kindcmd "sigs.k8s.io/kind/pkg/cmd" "testing" @@ -18,17 +19,17 @@ import ( type KindCluster struct { Name string Context string - kubeconfig string + Kubeconfig string config *rest.Config } -func CreateKindCluster(name, kubeconfigPath string) (*KindCluster, error) { +func CreateKindCluster(name, apiServerHost string, apiServerPort int, extraPorts map[int]int, kubeconfigPath string) (*KindCluster, error) { provider := cluster.NewProvider(cluster.ProviderWithLogger(kindcmd.NewLogger())) c := &KindCluster{ Name: name, Context: fmt.Sprintf("kind-%s", name), - kubeconfig: kubeconfigPath, + Kubeconfig: kubeconfigPath, } n, err := provider.ListNodes(name) @@ -36,7 +37,7 @@ func CreateKindCluster(name, kubeconfigPath string) (*KindCluster, error) { return nil, err } if len(n) == 0 { - if err := kindCreate(name, kubeconfigPath); err != nil { + if err := kindCreate(name, apiServerHost, apiServerPort, extraPorts, kubeconfigPath); err != nil { return nil, err } } @@ -47,19 +48,14 @@ func CreateKindCluster(name, kubeconfigPath string) (*KindCluster, error) { // Delete removes the cluster from kind. The cluster may not be deleted straight away - this only issues a delete command func (c *KindCluster) Delete() error { provider := cluster.NewProvider(cluster.ProviderWithLogger(kindcmd.NewLogger())) - return provider.Delete(c.Name, c.kubeconfig) -} - -// Kubeconfig returns the path to the cluster kubeconfig -func (c *KindCluster) Kubeconfig() string { - return c.kubeconfig + return provider.Delete(c.Name, c.Kubeconfig) } // RESTConfig returns K8s client config to pass to clientset objects func (c *KindCluster) RESTConfig() *rest.Config { if c.config == nil { var err error - c.config, err = clientcmd.BuildConfigFromFlags("", c.Kubeconfig()) + c.config, err = clientcmd.BuildConfigFromFlags("", c.Kubeconfig) if err != nil { panic(err) } @@ -70,7 +66,7 @@ func (c *KindCluster) RESTConfig() *rest.Config { func (c *KindCluster) Kubectl(args ...string) (string, error) { cmd := exec.Command("kubectl", args...) cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, fmt.Sprintf("KUBECONFIG=%s", c.kubeconfig)) + cmd.Env = append(cmd.Env, fmt.Sprintf("KUBECONFIG=%s", c.Kubeconfig)) stdout, err := cmd.Output() return string(stdout), err @@ -112,29 +108,64 @@ func (c *KindCluster) KubectlYamlMust(t *testing.T, args ...string) *uo.Unstruct } // kindCreate creates the kind cluster. It will retry up to 10 times if cluster creation fails. -func kindCreate(name, kubeconfig string) error { +func kindCreate(name string, apiServerHost string, apiServerPort int, extraPorts map[int]int, kubeconfig string) error { fmt.Printf("🌧️ Creating kind cluster %s...\n", name) provider := cluster.NewProvider(cluster.ProviderWithLogger(kindcmd.NewLogger())) - attempts := 0 - maxAttempts := 10 - for { - err := provider.Create( - name, - cluster.CreateWithNodeImage(""), - cluster.CreateWithRetain(false), - cluster.CreateWithWaitForReady(time.Duration(0)), - cluster.CreateWithKubeconfigPath(kubeconfig), - cluster.CreateWithDisplayUsage(false), - ) - if err == nil { - return nil - } + config := v1alpha4.Cluster{ + Name: name, + Nodes: []v1alpha4.Node{{ + Role: "control-plane", + }}, + Networking: v1alpha4.Networking{ + APIServerAddress: "0.0.0.0", + APIServerPort: int32(apiServerPort), + }, + } + for hostPort, containerPort := range extraPorts { + config.Nodes[0].ExtraPortMappings = append(config.Nodes[0].ExtraPortMappings, v1alpha4.PortMapping{ + ContainerPort: int32(containerPort), + HostPort: int32(hostPort), + ListenAddress: "0.0.0.0", + Protocol: "TCP", + }) + } - fmt.Printf("Error bringing up cluster, will retry (attempt %d): %v", attempts, err) - attempts++ - if attempts >= maxAttempts { - return errors.Wrapf(err, "Error bringing up cluster, exceeded max attempts (%d)", attempts) - } + err := provider.Create( + name, + cluster.CreateWithV1Alpha4Config(&config), + cluster.CreateWithNodeImage(""), + cluster.CreateWithRetain(false), + cluster.CreateWithWaitForReady(time.Duration(0)), + cluster.CreateWithKubeconfigPath(kubeconfig), + cluster.CreateWithDisplayUsage(false), + ) + if err != nil { + return err + } + + kcfg, err := clientcmd.LoadFromFile(kubeconfig) + if err != nil { + return err } + + c := kcfg.Clusters[fmt.Sprintf("kind-%s", name)] + + u, err := url.Parse(c.Server) + if err != nil { + return err + } + + // override api server host and disable TLS verification + // this is needed to make it work with remote docker hosts + c.InsecureSkipTLSVerify = true + c.CertificateAuthorityData = nil + c.Server = fmt.Sprintf("https://%s:%s", apiServerHost, u.Port()) + + err = clientcmd.WriteToFile(*kcfg, kubeconfig) + if err != nil { + return err + } + + return nil } From 548cbef7b529ddbc0a55ba6dbe30016fc0d960ee Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 1 Jun 2022 12:32:42 +0200 Subject: [PATCH 0257/2268] feat: Rename vault vars fields to align with go client lib --- pkg/types/vars_source.go | 4 ++-- pkg/vars/vars_loader.go | 2 +- pkg/vars/vault/secrets.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index 006b9d2f4..68ba14731 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -38,8 +38,8 @@ type VarsSourceAwsSecretsManager struct { } type VarsSourceVault struct { - Server string `yaml:"server" validate:"required"` - Key string `yaml:"key" validate:"required"` + Address string `yaml:"address" validate:"required"` + Path string `yaml:"path" validate:"required"` } type VarsSource struct { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index f6f2dfc76..bdb586bd8 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -146,7 +146,7 @@ func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsS } func (v *VarsLoader) loadVault(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { - secret, err := vault.GetSecret(source.Vault.Server, source.Vault.Key) + secret, err := vault.GetSecret(source.Vault.Address, source.Vault.Path) if err != nil { return err } diff --git a/pkg/vars/vault/secrets.go b/pkg/vars/vault/secrets.go index 0a05e6579..49f04618f 100644 --- a/pkg/vars/vault/secrets.go +++ b/pkg/vars/vault/secrets.go @@ -13,14 +13,14 @@ var httpClient = &http.Client{ Timeout: 15 * time.Second, } -func GetSecret(server string, key string) (string, error) { +func GetSecret(server string, path string) (string, error) { client, err := api.NewClient(&api.Config{Address: server, HttpClient: httpClient}) if err != nil { return "", fmt.Errorf("failed to create vault %s client", server) } - secret, err := client.Logical().Read(key) + secret, err := client.Logical().Read(path) if err != nil { - return "", fmt.Errorf("connection to vault %s failed", server) + return "", fmt.Errorf("reading from vault failed: %v", err) } if secret == nil || secret.Data == nil { return "", fmt.Errorf("the specified vault secret was not found") From a3ffe50a8675a62d37afff82682c8aa624fef425 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 31 May 2022 16:11:28 +0200 Subject: [PATCH 0258/2268] tests: Add vault sealing tests --- e2e/project.go | 2 + e2e/seal_test.go | 140 ++++++++++++---- e2e/test_resources/README.md | 7 +- e2e/test_resources/resources.go | 32 +++- e2e/test_resources/vault-values.yaml | 10 ++ e2e/test_resources/vault.yaml | 235 +++++++++++++++++++++++++++ 6 files changed, 396 insertions(+), 30 deletions(-) create mode 100644 e2e/test_resources/vault-values.yaml create mode 100644 e2e/test_resources/vault.yaml diff --git a/e2e/project.go b/e2e/project.go index 09367ff6c..d80c0c3e4 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -20,6 +20,7 @@ import ( type testProject struct { t *testing.T + extraEnv []string projectName string kluctlProjectExternal bool @@ -433,6 +434,7 @@ func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { args = append(args, "--debug") } env := os.Environ() + env = append(env, p.extraEnv...) env = append(env, fmt.Sprintf("KUBECONFIG=%s", p.mergedKubeconfig)) p.t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index b28cc12e7..c04c67361 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -8,30 +8,51 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" - "os" + "io/ioutil" + "net/http" + "net/url" "path/filepath" + "strings" + "sync" "testing" "time" ) -func installSealedSecretsOperator(t *testing.T, k *test_utils.KindCluster) { - tmpFile, _ := os.CreateTemp("", "") - tmpFile.Close() - defer os.Remove(tmpFile.Name()) +func installSealedSecretsOperator(k *test_utils.KindCluster) { + test_resources.ApplyYaml("sealed-secrets.yaml", k) +} - _ = utils.FsCopyFile(test_resources.Yamls, "sealed-secrets.yaml", tmpFile.Name()) +func waitForSealedSecretsOperator(t *testing.T, k *test_utils.KindCluster) { + waitForReadiness(t, k, "kube-system", "deployment/sealed-secrets-controller", 5*time.Minute) +} - _, err := k.Kubectl("apply", "-f", tmpFile.Name()) - if err != nil { - panic(err) - } +func deleteSealedSecretsOperator(k *test_utils.KindCluster) { + _, _ = k.Kubectl("-n", "kube-system", "delete", "deployment", "sealed-secrets-controller", "--wait") + _, _ = k.Kubectl("-n", "kube-system", "delete", "secret", "-l", "sealedsecrets.bitnami.com/sealed-secrets-key", "--wait") +} - waitForReadiness(t, k, "kube-system", "deployment/sealed-secrets-controller", 5*time.Minute) +func installVault(k *test_utils.KindCluster) { + _, _ = k.Kubectl("create", "ns", "vault") + test_resources.ApplyYaml("vault.yaml", k) } -func deleteSealedSecretsOperator(t *testing.T, k *test_utils.KindCluster) { - _, _ = defaultKindCluster1.Kubectl("-n", "kube-system", "delete", "deployment", "sealed-secrets-controller", "--wait") - _, _ = defaultKindCluster1.Kubectl("-n", "kube-system", "delete", "secret", "-l", "sealedsecrets.bitnami.com/sealed-secrets-key", "--wait") +func waitForVault(t *testing.T, k *test_utils.KindCluster) { + waitForReadiness(t, k, "vault", "statefulset/vault", 5*time.Minute) +} + +func init() { + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + installSealedSecretsOperator(defaultKindCluster1) + installVault(defaultKindCluster1) + }() + go func() { + defer wg.Done() + installSealedSecretsOperator(defaultKindCluster2) + }() + wg.Wait() } func prepareSealTest(t *testing.T, k *test_utils.KindCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject) *testProject { @@ -76,8 +97,7 @@ func TestSeal_WithOperator(t *testing.T) { k := defaultKindCluster1 namespace := "seal-with-operator" - deleteSealedSecretsOperator(t, k) - installSealedSecretsOperator(t, k) + waitForSealedSecretsOperator(t, k) p := prepareSealTest(t, k, namespace, map[string]string{ @@ -113,7 +133,10 @@ func TestSeal_WithBootstrap(t *testing.T) { k := defaultKindCluster2 namespace := "seal-with-bootstrap" - deleteSealedSecretsOperator(t, k) + // we still wait for it to be ready before we then delete it + // this way it's pre-pulled and pre-warmed when we later start it + waitForSealedSecretsOperator(t, k) + deleteSealedSecretsOperator(k) p := prepareSealTest(t, k, namespace, map[string]string{ @@ -136,7 +159,8 @@ func TestSeal_WithBootstrap(t *testing.T) { sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) - installSealedSecretsOperator(t, k) + installSealedSecretsOperator(k) + waitForSealedSecretsOperator(t, k) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -153,7 +177,7 @@ func TestSeal_MultipleVarSources(t *testing.T) { k := defaultKindCluster1 namespace := "seal-multiple-vs" - installSealedSecretsOperator(t, k) + waitForSealedSecretsOperator(t, k) p := prepareSealTest(t, k, namespace, map[string]string{ @@ -195,7 +219,7 @@ func TestSeal_MultipleSecretSets(t *testing.T) { k := defaultKindCluster1 namespace := "seal-multiple-ss" - installSealedSecretsOperator(t, k) + waitForSealedSecretsOperator(t, k) p := prepareSealTest(t, k, namespace, map[string]string{ @@ -239,8 +263,8 @@ func TestSeal_MultipleTargets(t *testing.T) { k := defaultKindCluster1 namespace := "seal-multiple-targets" - installSealedSecretsOperator(t, k) - installSealedSecretsOperator(t, defaultKindCluster2) + waitForSealedSecretsOperator(t, k) + waitForSealedSecretsOperator(t, defaultKindCluster2) p := prepareSealTest(t, k, namespace, map[string]string{ @@ -253,11 +277,6 @@ func TestSeal_MultipleTargets(t *testing.T) { "s1": "v1", }, }), - uo.FromMap(map[string]interface{}{ - "values": map[string]interface{}{ - "s2": "v2", - }, - }), }, ) defer p.cleanup() @@ -304,10 +323,12 @@ func TestSeal_MultipleTargets(t *testing.T) { } func TestSeal_File(t *testing.T) { + t.Parallel() + k := defaultKindCluster1 namespace := "seal-file" - installSealedSecretsOperator(t, k) + waitForSealedSecretsOperator(t, k) p := prepareSealTest(t, k, namespace, map[string]string{ @@ -345,3 +366,66 @@ func TestSeal_File(t *testing.T) { "s2": "v2", }) } + +func TestSeal_Vault(t *testing.T) { + t.Parallel() + + k := defaultKindCluster1 + namespace := "seal-vault" + + waitForSealedSecretsOperator(t, k) + waitForVault(t, k) + + u, err := url.Parse(defaultKindCluster1.RESTConfig().Host) + if err != nil { + t.Fatal(err) + } + + vaultUrl := fmt.Sprintf("http://%s:%d", u.Hostname(), defaultKindCluster1VaultPort) + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/secret/data/secret", vaultUrl), strings.NewReader(`{"data": {"secrets":{"s1":"v1","s2":"v2"}}}`)) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Vault-Token", "root") + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + body, _ := ioutil.ReadAll(resp.Body) + t.Fatalf("vault response status %d, body=%s", resp.StatusCode, string(body)) + } + + p := prepareSealTest(t, k, namespace, + map[string]string{ + "s1": "{{ secrets.s1 }}", + "s2": "{{ secrets.s2 }}", + }, + []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "vault": map[string]interface{}{ + "address": vaultUrl, + "path": "secret/data/secret", + }, + }), + }, + ) + defer p.cleanup() + + p.extraEnv = append(p.extraEnv, "VAULT_TOKEN=root") + p.KluctlMust("seal", "-t", "test-target") + + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + + p.KluctlMust("deploy", "--yes", "-t", "test-target") + + waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) + assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + "s1": "v1", + "s2": "v2", + }) +} diff --git a/e2e/test_resources/README.md b/e2e/test_resources/README.md index 75d21866f..e3655fad6 100644 --- a/e2e/test_resources/README.md +++ b/e2e/test_resources/README.md @@ -1,4 +1,9 @@ # sealed-secrets.yaml helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets -helm template sealed-secrets-controller sealed-secrets/sealed-secrets -n kube-system --include-crds > sealed-secrets.yaml +helm template sealed-secrets-controller sealed-secrets/sealed-secrets -n kube-system --include-crds --skip-tests > sealed-secrets.yaml + +# vault.yaml + +helm repo add hashicorp https://helm.releases.hashicorp.com +helm template vault hashicorp/vault -n vault -f vault-values.yaml --include-crds --skip-tests > vault.yaml diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go index 0ec6d270c..2645c7488 100644 --- a/e2e/test_resources/resources.go +++ b/e2e/test_resources/resources.go @@ -1,6 +1,36 @@ package test_resources -import "embed" +import ( + "embed" + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils" + "os" +) //go:embed *.yaml var Yamls embed.FS + +func GetYamlTmpFile(name string) string { + tmpFile, err := os.CreateTemp("", "") + if err != nil { + panic(err) + } + tmpFile.Close() + + err = utils.FsCopyFile(Yamls, name, tmpFile.Name()) + if err != nil { + panic(err) + } + + return tmpFile.Name() +} + +func ApplyYaml(name string, k *test_utils.KindCluster) { + tmpFile := GetYamlTmpFile(name) + defer os.Remove(tmpFile) + + _, err := k.Kubectl("apply", "-f", tmpFile) + if err != nil { + panic(err) + } +} \ No newline at end of file diff --git a/e2e/test_resources/vault-values.yaml b/e2e/test_resources/vault-values.yaml new file mode 100644 index 000000000..1934c072a --- /dev/null +++ b/e2e/test_resources/vault-values.yaml @@ -0,0 +1,10 @@ +server: + # Must be RollingUpdate as otherwise it's reported as ready much too early + updateStrategyType: RollingUpdate + dev: + enabled: true + service: + type: NodePort + nodePort: 30000 +injector: + enabled: false diff --git a/e2e/test_resources/vault.yaml b/e2e/test_resources/vault.yaml new file mode 100644 index 000000000..13e65b084 --- /dev/null +++ b/e2e/test_resources/vault.yaml @@ -0,0 +1,235 @@ +--- +# Source: vault/templates/server-serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: vault + namespace: vault + labels: + helm.sh/chart: vault-0.20.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: vault + app.kubernetes.io/managed-by: Helm +--- +# Source: vault/templates/server-clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: vault-server-binding + labels: + helm.sh/chart: vault-0.20.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: vault + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: vault + namespace: vault +--- +# Source: vault/templates/server-headless-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: vault-internal + namespace: vault + labels: + helm.sh/chart: vault-0.20.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: vault + app.kubernetes.io/managed-by: Helm + annotations: + +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: "http" + port: 8200 + targetPort: 8200 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: vault + component: server +--- +# Source: vault/templates/server-service.yaml +# Service for Vault cluster +apiVersion: v1 +kind: Service +metadata: + name: vault + namespace: vault + labels: + helm.sh/chart: vault-0.20.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: vault + app.kubernetes.io/managed-by: Helm + annotations: + +spec: + type: NodePort + externalTrafficPolicy: Cluster + # We want the servers to become available even if they're not ready + # since this DNS is also used for join operations. + publishNotReadyAddresses: true + ports: + - name: http + port: 8200 + targetPort: 8200 + nodePort: 30000 + - name: https-internal + port: 8201 + targetPort: 8201 + selector: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: vault + component: server +--- +# Source: vault/templates/server-statefulset.yaml +# StatefulSet to run the actual vault server cluster. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: vault + namespace: vault + labels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: vault + app.kubernetes.io/managed-by: Helm +spec: + serviceName: vault-internal + podManagementPolicy: Parallel + replicas: 1 + updateStrategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: vault + app.kubernetes.io/instance: vault + component: server + template: + metadata: + labels: + helm.sh/chart: vault-0.20.1 + app.kubernetes.io/name: vault + app.kubernetes.io/instance: vault + component: server + spec: + + + + + terminationGracePeriodSeconds: 10 + serviceAccountName: vault + + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + fsGroup: 1000 + volumes: + + - name: home + emptyDir: {} + containers: + - name: vault + + image: hashicorp/vault:1.10.3 + imagePullPolicy: IfNotPresent + command: + - "/bin/sh" + - "-ec" + args: + - | + /usr/local/bin/docker-entrypoint.sh vault server -dev + + securityContext: + allowPrivilegeEscalation: false + env: + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: VAULT_K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: VAULT_ADDR + value: "http://127.0.0.1:8200" + - name: VAULT_API_ADDR + value: "http://$(POD_IP):8200" + - name: SKIP_CHOWN + value: "true" + - name: SKIP_SETCAP + value: "true" + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: VAULT_CLUSTER_ADDR + value: "https://$(HOSTNAME).vault-internal:8201" + - name: HOME + value: "/home/vault" + + - name: VAULT_DEV_ROOT_TOKEN_ID + value: root + - name: VAULT_DEV_LISTEN_ADDRESS + value: "[::]:8200" + + + + volumeMounts: + + + + - name: home + mountPath: /home/vault + ports: + - containerPort: 8200 + name: http + - containerPort: 8201 + name: https-internal + - containerPort: 8202 + name: http-rep + readinessProbe: + # Check status; unsealed vault servers return 0 + # The exit code reflects the seal status: + # 0 - unsealed + # 1 - error + # 2 - sealed + exec: + command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + lifecycle: + # Vault container doesn't receive SIGTERM from Kubernetes + # and after the grace period ends, Kube sends SIGKILL. This + # causes issues with graceful shutdowns such as deregistering itself + # from Consul (zombie services). + preStop: + exec: + command: [ + "/bin/sh", "-c", + # Adding a sleep here to give the pod eviction a + # chance to propagate, so requests will not be made + # to this pod while it's terminating + "sleep 5 && kill -SIGTERM $(pidof vault)", + ] From e9ef495487ccc3c48d42f46888c77aae756b3c87 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 1 Jun 2022 13:07:31 +0200 Subject: [PATCH 0259/2268] tests: Fully delete all sealed-secrets resources in deleteSealedSecretsOperator --- e2e/seal_test.go | 3 ++- e2e/test_resources/resources.go | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index c04c67361..d9a6bdce1 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -27,8 +27,9 @@ func waitForSealedSecretsOperator(t *testing.T, k *test_utils.KindCluster) { } func deleteSealedSecretsOperator(k *test_utils.KindCluster) { - _, _ = k.Kubectl("-n", "kube-system", "delete", "deployment", "sealed-secrets-controller", "--wait") + test_resources.DeleteYaml("sealed-secrets.yaml", k) _, _ = k.Kubectl("-n", "kube-system", "delete", "secret", "-l", "sealedsecrets.bitnami.com/sealed-secrets-key", "--wait") + _, _ = k.Kubectl("-n", "kube-system", "delete", "configmap", "sealed-secrets-key-kluctl-bootstrap", "--wait") } func installVault(k *test_utils.KindCluster) { diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go index 2645c7488..7644d1f09 100644 --- a/e2e/test_resources/resources.go +++ b/e2e/test_resources/resources.go @@ -33,4 +33,14 @@ func ApplyYaml(name string, k *test_utils.KindCluster) { if err != nil { panic(err) } -} \ No newline at end of file +} + +func DeleteYaml(name string, k *test_utils.KindCluster) { + tmpFile := GetYamlTmpFile(name) + defer os.Remove(tmpFile) + + _, err := k.Kubectl("delete", "-f", tmpFile) + if err != nil { + panic(err) + } +} From 377503949fc7465bff8c6e2c8189173116d5054a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 1 Jun 2022 13:07:41 +0200 Subject: [PATCH 0260/2268] tests: Fix TestSeal_MultipleTargets --- e2e/seal_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index d9a6bdce1..28e6b976f 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -276,6 +276,7 @@ func TestSeal_MultipleTargets(t *testing.T) { uo.FromMap(map[string]interface{}{ "values": map[string]interface{}{ "s1": "v1", + "s2": "v2", }, }), }, From c0f409eabf1368150ac484c43f6cb7189aba17bf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 1 Jun 2022 13:19:55 +0200 Subject: [PATCH 0261/2268] tests: Add some more debug info to kindCreate logging --- internal/test-utils/kind_cluster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/test-utils/kind_cluster.go b/internal/test-utils/kind_cluster.go index eaa7c8dd5..791f8753c 100644 --- a/internal/test-utils/kind_cluster.go +++ b/internal/test-utils/kind_cluster.go @@ -110,7 +110,7 @@ func (c *KindCluster) KubectlYamlMust(t *testing.T, args ...string) *uo.Unstruct // kindCreate creates the kind cluster. It will retry up to 10 times if cluster creation fails. func kindCreate(name string, apiServerHost string, apiServerPort int, extraPorts map[int]int, kubeconfig string) error { - fmt.Printf("🌧️ Creating kind cluster %s...\n", name) + fmt.Printf("🌧️ Creating kind cluster %s with apiServerHost=%s, apiServerPort=%d, extraPorts=%v...\n", name, apiServerHost, apiServerPort, extraPorts) provider := cluster.NewProvider(cluster.ProviderWithLogger(kindcmd.NewLogger())) config := v1alpha4.Cluster{ Name: name, From c0a77304296e05a7928269da0e0bf5f6624b181d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 1 Jun 2022 13:58:09 +0200 Subject: [PATCH 0262/2268] tests: Reintroduce retries when creating kind clusters --- .github/workflows/tests.yml | 2 +- internal/test-utils/kind_cluster.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b966ab2c8..88c84e18e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -236,7 +236,7 @@ jobs: run: | kubectl version || true kind version || true - - name: Start kind cluster + - name: Prepare kind cluster variables shell: bash run: | if [ "${{ runner.os }}" == "Linux" ]; then diff --git a/internal/test-utils/kind_cluster.go b/internal/test-utils/kind_cluster.go index 791f8753c..99ad404d7 100644 --- a/internal/test-utils/kind_cluster.go +++ b/internal/test-utils/kind_cluster.go @@ -109,7 +109,17 @@ func (c *KindCluster) KubectlYamlMust(t *testing.T, args ...string) *uo.Unstruct // kindCreate creates the kind cluster. It will retry up to 10 times if cluster creation fails. func kindCreate(name string, apiServerHost string, apiServerPort int, extraPorts map[int]int, kubeconfig string) error { + var err error + for i := 0; i < 10; i++ { + err = kindCreate2(name, apiServerHost, apiServerPort, extraPorts, kubeconfig) + if err == nil { + return nil + } + } + return err +} +func kindCreate2(name string, apiServerHost string, apiServerPort int, extraPorts map[int]int, kubeconfig string) error { fmt.Printf("🌧️ Creating kind cluster %s with apiServerHost=%s, apiServerPort=%d, extraPorts=%v...\n", name, apiServerHost, apiServerPort, extraPorts) provider := cluster.NewProvider(cluster.ProviderWithLogger(kindcmd.NewLogger())) config := v1alpha4.Cluster{ From c20d5917abc245164056051317992bb25372139e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 1 Jun 2022 18:54:15 +0200 Subject: [PATCH 0263/2268] ci: Increase inotify limits for docker host --- .github/workflows/tests.yml | 6 +++++- hack/setup-docker-port-forward.sh | 20 -------------------- 2 files changed, 5 insertions(+), 21 deletions(-) delete mode 100755 hack/setup-docker-port-forward.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 88c84e18e..e6a9124ca 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -85,11 +85,15 @@ jobs: - name: Checkout uses: actions/checkout@v2 - name: Install zerotier - if: runner.os == 'Linux' run: | sudo apt update sudo apt install -y gpg jq curl -s https://install.zerotier.com | sudo bash + - name: Setup inotify limits + run: | + # see https://kind.sigs.k8s.io/docs/user/known-issues/#pod-errors-due-to-too-many-open-files + sudo sysctl fs.inotify.max_user_watches=524288 + sudo sysctl fs.inotify.max_user_instances=512 - name: Stop docker run: | # Ensure docker is down and that the test jobs can wait for it to be available again after joining the network. diff --git a/hack/setup-docker-port-forward.sh b/hack/setup-docker-port-forward.sh deleted file mode 100755 index 886ed268f..000000000 --- a/hack/setup-docker-port-forward.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -set -e - -echo "Forwarding ports" - -# pre-create this to avoid races in the background ssh calls -mkdir -p $HOME/.ssh - -# docker -tail -F ssh-log-2375 & -nohup /usr/bin/ssh -i kluctl-ci.pem -o StrictHostKeyChecking=no -L2375:/run/docker.sock -N kluctl-ci@docker.ci.kluctl.io &> ssh-log-2375 & - -while ! curl http://localhost:2375 &> /dev/null; do - echo "Waiting for ports to get available..." - sleep 5 -done - -# keep ports alive -nohup bash -c "while true; do curl http://localhost:2375 &> /dev/null ; sleep 5; done" & From 058f619d71298e46cffe17f5633163e7de6b426e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Jun 2022 09:03:07 +0200 Subject: [PATCH 0264/2268] ci: Exclude "ci" commits from changelog --- .goreleaser.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 029ace461..2461d3dd0 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -61,6 +61,7 @@ changelog: - '^test.*:' - '^chore:' - '^build:' + - '^ci:' - '^refactor:' release: From 937967b840da9ccda9b215cd12ceaeee030edae2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Jun 2022 09:03:17 +0200 Subject: [PATCH 0265/2268] docs: fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6cd3ba354..c19f8f86f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Kluctl does not depend on external operators/controllers and allows to use the s as long as access to the kluctl project and clusters is available. This means, that you can use it from your local machine, from your CI/CD pipelines or any automation platform/system that allows to call custom tools. -Flux support is in alpha statium and available via the [flux-kluctl-controller](https://github.com/kluctl/flux-kluctl-controller). +Flux support is in alpha stadium and available via the [flux-kluctl-controller](https://github.com/kluctl/flux-kluctl-controller). ## Installation From 56fe43b968fe2e13f9cc5b5fee4be94eebe509ac Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Jun 2022 09:29:49 +0200 Subject: [PATCH 0266/2268] refactor: Stop using gammazero WorkerPool --- .../commands/cmd_check_image_updates.go | 16 ++-- pkg/deployment/deployment_collection.go | 47 +++++++---- pkg/deployment/deployment_item.go | 55 ++++++------ pkg/k8s/k8s_cluster.go | 51 +++++------ pkg/kluctl_project/targets.go | 16 ++-- pkg/utils/workerpool.go | 84 ------------------- 6 files changed, 94 insertions(+), 175 deletions(-) delete mode 100644 pkg/utils/workerpool.go diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index 1da6883a3..14be5ede7 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -37,11 +37,9 @@ func runCheckImageUpdates(ctx *commandCtx) error { rh := registries.NewRegistryHelper(ctx.ctx) - wg := utils.NewWorkerPoolWithErrors(8) - defer wg.StopWait(false) - imageTags := make(map[string]interface{}) var mutex sync.Mutex + var wg sync.WaitGroup for _, images := range renderedImages { for _, image := range images { @@ -51,7 +49,9 @@ func runCheckImageUpdates(ctx *commandCtx) error { } repo := s[0] if _, ok := imageTags[repo]; !ok { - wg.Submit(func() error { + wg.Add(1) + go func() { + defer wg.Done() tags, err := rh.ListImageTags(repo) mutex.Lock() defer mutex.Unlock() @@ -60,15 +60,11 @@ func runCheckImageUpdates(ctx *commandCtx) error { } else { imageTags[repo] = tags } - return nil - }) + }() } } } - err := wg.StopWait(false) - if err != nil { - return err - } + wg.Wait() prefixPattern := regexp.MustCompile("^([a-zA-Z]+[a-zA-Z-_.]*)") suffixPattern := regexp.MustCompile("([-][a-zA-Z]+[a-zA-Z-_.]*)$") diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index be086ebb2..0a379cffa 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -109,18 +109,26 @@ func (c *DeploymentCollection) RenderDeployments() error { s := status.Start(c.ctx.Ctx, "Rendering templates") defer s.Failed() - wp := utils.NewDebuggerAwareWorkerPool(16) - defer wp.StopWait(false) + var wg sync.WaitGroup + var mutex sync.Mutex + var errors []error for _, d := range c.Deployments { - err := d.render(c.forSeal, wp) - if err != nil { - return err - } + d := d + wg.Add(1) + go func() { + defer wg.Done() + err := d.render(c.forSeal) + if err != nil { + mutex.Lock() + errors = append(errors, err) + mutex.Unlock() + } + }() } - err := wp.StopWait(true) - if err != nil { - return err + wg.Wait() + if len(errors) != 0 { + return utils.NewErrorListOrNil(errors) } s.Success() @@ -128,14 +136,21 @@ func (c *DeploymentCollection) RenderDeployments() error { defer s.Failed() for _, d := range c.Deployments { - err := d.renderHelmCharts(wp) - if err != nil { - return err - } + d := d + wg.Add(1) + go func() { + defer wg.Done() + err := d.renderHelmCharts() + if err != nil { + mutex.Lock() + errors = append(errors, err) + mutex.Unlock() + } + }() } - err = wp.StopWait(false) - if err != nil { - return err + wg.Wait() + if len(errors) != 0 { + return utils.NewErrorListOrNil(errors) } s.Success() diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 51957e455..959830915 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -101,7 +101,7 @@ func (di *DeploymentItem) getCommonAnnotations() map[string]string { return a } -func (di *DeploymentItem) render(forSeal bool, wp *utils.WorkerPoolWithErrors) error { +func (di *DeploymentItem) render(forSeal bool) error { if di.dir == nil { return nil } @@ -145,14 +145,10 @@ func (di *DeploymentItem) render(forSeal bool, wp *utils.WorkerPoolWithErrors) e excludePatterns = append(excludePatterns, "**.sealme") } - wp.Submit(func() error { - return varsCtx.RenderDirectory(di.Project.source.dir, di.Project.getRenderSearchDirs(), di.Project.relDir, excludePatterns, di.RelToProjectItemDir, di.RenderedDir) - }) - - return nil + return varsCtx.RenderDirectory(di.Project.source.dir, di.Project.getRenderSearchDirs(), di.Project.relDir, excludePatterns, di.RelToProjectItemDir, di.RenderedDir) } -func (di *DeploymentItem) renderHelmCharts(wp *utils.WorkerPoolWithErrors) error { +func (di *DeploymentItem) renderHelmCharts() error { if di.dir == nil { return nil } @@ -162,35 +158,32 @@ func (di *DeploymentItem) renderHelmCharts(wp *utils.WorkerPoolWithErrors) error return nil } - wp.Submit(func() error { - subDir, err := filepath.Rel(di.RenderedDir, filepath.Dir(p)) - if err != nil { - return err - } + subDir, err := filepath.Rel(di.RenderedDir, filepath.Dir(p)) + if err != nil { + return err + } - chart, err := NewHelmChart(p) - if err != nil { - return err - } + chart, err := NewHelmChart(p) + if err != nil { + return err + } - ky, err := di.readKustomizationYaml(subDir) - if err == nil && ky != nil { - resources, _, _ := ky.GetNestedStringList("resources") - found := false - for _, r := range resources { - if r == chart.GetOutputPath() { - found = true - break - } - } - if !found { - return fmt.Errorf("%s/kustomization.yaml does not include the rendered helm chart: %s", di.RelRenderedDir, chart.GetOutputPath()) + ky, err := di.readKustomizationYaml(subDir) + if err == nil && ky != nil { + resources, _, _ := ky.GetNestedStringList("resources") + found := false + for _, r := range resources { + if r == chart.GetOutputPath() { + found = true + break } } + if !found { + return fmt.Errorf("%s/kustomization.yaml does not include the rendered helm chart: %s", di.RelRenderedDir, chart.GetOutputPath()) + } + } - return chart.Render(di.ctx.Ctx, di.ctx.K) - }) - return nil + return chart.Render(di.ctx.Ctx, di.ctx.K) }) if err != nil { return err diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 289e3d53a..32499284f 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -164,12 +164,11 @@ func (k *K8sCluster) ListObjectsMetadata(gvk schema.GroupVersionKind, namespace } func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map[string]string, onlyMetadata bool) ([]*uo.UnstructuredObject, map[schema.GroupVersionKind][]ApiWarning, error) { - wp := utils.NewWorkerPoolWithErrors(8) - defer wp.StopWait(false) - var ret []*uo.UnstructuredObject + var errs []error retApiWarnings := make(map[schema.GroupVersionKind][]ApiWarning) var mutex sync.Mutex + var wg sync.WaitGroup filter := func(ar *v1.APIResource) bool { foundVerb := false @@ -184,7 +183,10 @@ func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map for _, gvk := range k.Resources.GetFilteredPreferredGVKs(filter) { gvk := gvk - wp.Submit(func() error { + wg.Add(1) + go func() { + defer wg.Done() + var l []*uo.UnstructuredObject var apiWarnings []ApiWarning var err error @@ -193,23 +195,24 @@ func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map } else { l, apiWarnings, err = k.ListObjects(gvk, namespace, labels) } - if err != nil && !errors.IsNotFound(err) { - return err - } mutex.Lock() defer mutex.Unlock() + if err != nil && !errors.IsNotFound(err) { + errs = append(errs, err) + return + } ret = append(ret, l...) if len(apiWarnings) != 0 { retApiWarnings[gvk] = apiWarnings } - return nil - }) + }() } + wg.Wait() - err := wp.StopWait(false) - if err != nil { - return nil, retApiWarnings, err + if len(errs) != 0 { + return nil, retApiWarnings, utils.NewErrorListOrNil(errs) } + return ret, retApiWarnings, nil } @@ -228,16 +231,17 @@ func (k *K8sCluster) GetSingleObject(ref k8s.ObjectRef) (*uo.UnstructuredObject, } func (k *K8sCluster) GetObjectsByRefs(refs []k8s.ObjectRef) ([]*uo.UnstructuredObject, map[k8s.ObjectRef][]ApiWarning, error) { - wp := utils.NewWorkerPoolWithErrors(32) - defer wp.StopWait(false) - var ret []*uo.UnstructuredObject + var errs []error retApiWarnings := make(map[k8s.ObjectRef][]ApiWarning) var mutex sync.Mutex + var wg sync.WaitGroup for _, ref_ := range refs { ref := ref_ - wp.Submit(func() error { + wg.Add(1) + go func() { + defer wg.Done() o, apiWarnings, err := k.GetSingleObject(ref) mutex.Lock() defer mutex.Unlock() @@ -245,18 +249,17 @@ func (k *K8sCluster) GetObjectsByRefs(refs []k8s.ObjectRef) ([]*uo.UnstructuredO retApiWarnings[ref] = apiWarnings } if err != nil { - if errors.IsNotFound(err) || meta.IsNoMatchError(err) { - return nil + if !errors.IsNotFound(err) && !meta.IsNoMatchError(err) { + errs = append(errs, err) } - return err + return } ret = append(ret, o) - return nil - }) + }() } - err := wp.StopWait(false) - if err != nil { - return nil, retApiWarnings, err + wg.Wait() + if len(errs) != 0 { + return nil, retApiWarnings, utils.NewErrorListOrNil(errs) } return ret, retApiWarnings, nil diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index acd861f13..795424ad4 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -221,11 +221,9 @@ func (c *LoadedKluctlProject) matchRef(s string, pattern string) (bool, string, } func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTargetInfo) error { - wp := utils.NewDebuggerAwareWorkerPool(8) - defer wp.StopWait(false) - uniqueClones := make(map[string]interface{}) var mutex sync.Mutex + var wg sync.WaitGroup for _, targetInfo_ := range dynamicTargets { targetInfo := targetInfo_ @@ -241,7 +239,9 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTarge uniqueClones[targetInfo.dir] = nil mutex.Unlock() - wp.Submit(func() error { + wg.Add(1) + go func() { + defer wg.Done() gitProject := *targetInfo.gitProject gitProject.Ref = *targetInfo.ref @@ -253,13 +253,9 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTarge } else { uniqueClones[targetInfo.dir] = &gi } - return nil - }) - } - err := wp.StopWait(false) - if err != nil { - return err + }() } + wg.Wait() return nil } diff --git a/pkg/utils/workerpool.go b/pkg/utils/workerpool.go deleted file mode 100644 index fe71e32b9..000000000 --- a/pkg/utils/workerpool.go +++ /dev/null @@ -1,84 +0,0 @@ -package utils - -import ( - "github.com/gammazero/workerpool" - "os" - "strconv" - "sync" -) - -type WorkerPoolWithErrors struct { - maxWorkers int - pool *workerpool.WorkerPool - errors []error - results []interface{} - mutex sync.Mutex -} - -func NewWorkerPoolWithErrors(maxWorkers int) *WorkerPoolWithErrors { - return &WorkerPoolWithErrors{ - maxWorkers: maxWorkers, - pool: workerpool.New(maxWorkers), - } -} - -func NewDebuggerAwareWorkerPool(maxWorkers int) *WorkerPoolWithErrors { - if IsLaunchedByDebugger() { - ignoreDebuggerStr, _ := os.LookupEnv("KLUCTL_IGNORE_DEBUGGER") - ignoreDebugger, _ := strconv.ParseBool(ignoreDebuggerStr) - if !ignoreDebugger { - maxWorkers = 1 - } - } - return NewWorkerPoolWithErrors(maxWorkers) -} - -func (wp *WorkerPoolWithErrors) Submit(cb func() error) { - wp.SubmitWithResult(func() (interface{}, error) { - err := cb() - return nil, err - }) -} - -func (wp *WorkerPoolWithErrors) SubmitWithResult(cb func() (interface{}, error)) { - wp.pool.Submit(func() { - result, err := cb() - wp.mutex.Lock() - defer wp.mutex.Unlock() - if err != nil { - wp.errors = append(wp.errors, err) - } else if result != nil { - wp.results = append(wp.results, result) - } - }) -} - -func (wp *WorkerPoolWithErrors) StopWait(restart bool) error { - if wp.pool == nil { - return nil - } - wp.pool.StopWait() - if restart { - wp.pool = workerpool.New(wp.maxWorkers) - } else { - wp.pool = nil - } - - wp.mutex.Lock() - defer wp.mutex.Unlock() - - if len(wp.errors) == 0 { - return nil - } - err := NewErrorListOrNil(wp.errors) - wp.errors = nil - return err -} - -func (wp *WorkerPoolWithErrors) Errors() []error { - return wp.errors -} - -func (wp *WorkerPoolWithErrors) Results() []interface{} { - return wp.results -} From a32e7f4e8d584df6f1e6679bed6d93428d6feec6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Jun 2022 10:00:31 +0200 Subject: [PATCH 0267/2268] build: Run go get -u ./... --- go.mod | 94 +++++++++++++++-------------- go.sum | 183 ++++++++++++++++++++++++++++++--------------------------- 2 files changed, 144 insertions(+), 133 deletions(-) diff --git a/go.mod b/go.mod index 3c936f079..b4e6c807c 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,12 @@ go 1.18 require ( github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e github.com/Masterminds/semver/v3 v3.1.1 - github.com/aws/aws-sdk-go v1.44.19 - github.com/bitnami-labs/sealed-secrets v0.17.5 + github.com/aws/aws-sdk-go v1.44.28 + github.com/bitnami-labs/sealed-secrets v0.18.0 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/evanphx/json-patch v5.6.0+incompatible github.com/fluxcd/pkg/kustomize v0.5.1 - github.com/gammazero/workerpool v1.1.2 github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.0 github.com/gobwas/glob v0.2.3 @@ -20,34 +19,35 @@ require ( github.com/google/go-containerregistry v0.9.0 github.com/hashicorp/vault/api v1.6.0 github.com/hexops/gotextdiff v1.0.3 + github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/klauspost/compress v1.15.4 + github.com/klauspost/compress v1.15.6 github.com/mattn/go-isatty v0.0.14 github.com/mitchellh/go-ps v1.0.0 - github.com/ohler55/ojg v1.14.0 + github.com/ohler55/ojg v1.14.2 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.8.1 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.11.0 - github.com/stretchr/testify v1.7.1 - github.com/vbauerster/mpb/v7 v7.4.1 + github.com/spf13/viper v1.12.0 + github.com/stretchr/testify v1.7.2 + github.com/vbauerster/mpb/v7 v7.4.2 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.1 - golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 - golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e + golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a - golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 + golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 golang.org/x/text v0.3.7 - gopkg.in/yaml.v3 v3.0.0 + gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.9.0 - k8s.io/api v0.24.1-rc.0 - k8s.io/apiextensions-apiserver v0.24.0 - k8s.io/apimachinery v0.24.1-rc.0 - k8s.io/client-go v0.24.1-rc.0 + k8s.io/api v0.24.1 + k8s.io/apiextensions-apiserver v0.24.1 + k8s.io/apimachinery v0.24.1 + k8s.io/client-go v0.24.1 k8s.io/klog/v2 v2.60.1 sigs.k8s.io/kind v0.14.0 sigs.k8s.io/kustomize/api v0.11.5 @@ -75,28 +75,27 @@ require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/alessio/shellescape v1.4.1 // indirect - github.com/armon/go-metrics v0.3.10 // indirect + github.com/armon/go-metrics v0.4.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect - github.com/containerd/containerd v1.6.4 // indirect + github.com/containerd/containerd v1.6.6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.16+incompatible // indirect - github.com/docker/docker v20.10.16+incompatible // indirect + github.com/docker/cli v20.10.17+incompatible // indirect + github.com/docker/docker v20.10.17+incompatible // indirect github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/emicklei/go-restful v2.9.5+incompatible // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/gammazero/deque v0.1.1 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect @@ -121,24 +120,23 @@ require ( github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-hclog v1.2.1 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.3 // indirect - github.com/hashicorp/go-retryablehttp v0.6.6 // indirect + github.com/hashicorp/go-plugin v1.4.4 // indirect + github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 // indirect + github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/go-uuid v1.0.2 // indirect - github.com/hashicorp/go-version v1.2.1 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/go-version v1.5.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/vault/sdk v0.5.0 // indirect - github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect github.com/huandu/xstrings v1.3.2 // indirect - github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -157,7 +155,7 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -169,13 +167,13 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oklog/run v1.0.0 // indirect + github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pierrec/lz4 v2.5.2+incompatible // indirect + github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.2 // indirect github.com/prometheus/client_model v0.2.0 // indirect @@ -190,7 +188,7 @@ require ( github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/subosito/gotenv v1.2.0 // indirect + github.com/subosito/gotenv v1.4.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -198,26 +196,26 @@ require ( github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect - golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect + golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect + golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect - google.golang.org/grpc v1.46.2 // indirect + google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 // indirect + google.golang.org/grpc v1.47.0 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.4 // indirect - gopkg.in/square/go-jose.v2 v2.5.1 // indirect + gopkg.in/ini.v1 v1.66.6 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiserver v0.24.0 // indirect - k8s.io/cli-runtime v0.24.0 // indirect - k8s.io/component-base v0.24.0 // indirect - k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6 // indirect - k8s.io/kubectl v0.24.0 // indirect + k8s.io/apiserver v0.24.1 // indirect + k8s.io/cli-runtime v0.24.1 // indirect + k8s.io/component-base v0.24.1 // indirect + k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60 // indirect + k8s.io/kubectl v0.24.1 // indirect k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect oras.land/oras-go v1.1.1 // indirect - sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect + sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index c0ed01965..739e736a0 100644 --- a/go.sum +++ b/go.sum @@ -116,7 +116,7 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/hcsshim v0.9.2 h1:wB06W5aYFfUB3IvootYAY2WnOmIdgPGfqSI6tufQNnY= +github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -154,8 +154,8 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= -github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -169,8 +169,8 @@ github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9D github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.19 h1:dhI6p4l6kisnA7gBAM8sP5YIk0bZ9HNAj7yrK7kcfdU= -github.com/aws/aws-sdk-go v1.44.19/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.28 h1:h/OAqEqY18wq//v6h4GNPMmCkxuzSDrWuGyrvSiRqf4= +github.com/aws/aws-sdk-go v1.44.28/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= 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= @@ -178,8 +178,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.17.5 h1:v5ENZRSrgog3GnFr8fWfVtrUTPlZlNlbsjaro9mn6YY= -github.com/bitnami-labs/sealed-secrets v0.17.5/go.mod h1:ZgGUqKixr/SRpsG8LVXnuneyZG7DOX/+eCRvXi8SVjo= +github.com/bitnami-labs/sealed-secrets v0.18.0 h1:7LdfPRMyx9nGQW9204JM1F+F+s6xmaSBl8oNujlrCuc= +github.com/bitnami-labs/sealed-secrets v0.18.0/go.mod h1:uV8CUHJQVcDOY9cZ1FdC1rtybC4ath+VGH+/dUQvBu0= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= @@ -193,8 +193,9 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= -github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= +github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= @@ -226,8 +227,8 @@ github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= -github.com/containerd/containerd v1.6.4 h1:SEDZBp10mhCp+hkO3Njz/YhGrI7ah3edNcUlRdUPOgg= -github.com/containerd/containerd v1.6.4/go.mod h1:oWOqbuJUZmOVafhA0lj2NAXbiO1u7F0K5l1bUgdyo94= +github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= +github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= github.com/containerd/stargz-snapshotter/estargz v0.11.4 h1:LjrYUZpyOhiSaU7hHrdR82/RBoxfGWSaC0VeSSMXqnk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -264,12 +265,12 @@ github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27N github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY= -github.com/docker/cli v20.10.16+incompatible h1:aLQ8XowgKpR3/IysPj8qZQJBVQ+Qws61icFuZl6iKYs= -github.com/docker/cli v20.10.16+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= +github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ= -github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= +github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -286,8 +287,10 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.7.5-0.20220308211933-7c971ca4d0fd/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= @@ -342,11 +345,6 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= -github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= -github.com/gammazero/deque v0.1.1 h1:xRVkDuSvDmFuMGf3IquHuRc2jlL0+v/WpFCWaauzwbE= -github.com/gammazero/deque v0.1.1/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= -github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/qj87g= -github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -630,8 +628,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= -github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= +github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -641,20 +639,24 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ= +github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= +github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 h1:MBgwAFPUbfuI0+tmDU/aeM1MARvdbqWmiieXIalKqDE= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= @@ -666,11 +668,13 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E= +github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -689,8 +693,9 @@ github.com/hashicorp/vault/api v1.6.0 h1:B8UUYod1y1OoiGHq9GtpiqSnGOUEWHaA26AY8RQ github.com/hashicorp/vault/api v1.6.0/go.mod h1:h1K70EO2DgnBaTz5IsL6D5ERsNt5Pce93ueVS2+t0Xc= github.com/hashicorp/vault/sdk v0.5.0 h1:EED7p0OCU3OY5SAqJwSANofY1YKMytm+jDHDQ2EzGVQ= github.com/hashicorp/vault/sdk v0.5.0/go.mod h1:UJZHlfwj7qUJG8g22CuxUgkdJouFrBNvBHCyx8XAPdo= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= +github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -705,8 +710,9 @@ github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -763,8 +769,8 @@ github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.4 h1:1kn4/7MepF/CHmYub99/nNX8az0IJjfSOU/jbnTVfqQ= -github.com/klauspost/compress v1.15.4/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.6 h1:6D9PcO8QWu0JyaQ2zUMmu16T1T+zjjEpP91guRsvDfY= +github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -881,8 +887,9 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= @@ -937,10 +944,11 @@ github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/ohler55/ojg v1.14.0 h1:DyHomsCwofNswmKj7BLMdx51xnKbXxgIo1rVWCaBcNk= -github.com/ohler55/ojg v1.14.0/go.mod h1:3+GH+0PggMKocQtbZCrFifal3yRpHiBT4QUkxFJI6e8= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/ohler55/ojg v1.14.2 h1:EHdrwmDrOuTGpj1W2LrT/yKeUOkLMIk1cTYcm42Sjj0= +github.com/ohler55/ojg v1.14.2/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -984,8 +992,9 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= -github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 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= @@ -1131,8 +1140,8 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= -github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44= -github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk= +github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= +github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= 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= @@ -1147,10 +1156,12 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P 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 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs= +github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= @@ -1178,8 +1189,8 @@ github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= -github.com/vbauerster/mpb/v7 v7.4.1 h1:NhLMWQ3gNg2KJR8oeA9lO8Xvq+eNPmixDmB6JEQOUdA= -github.com/vbauerster/mpb/v7 v7.4.1/go.mod h1:Ygg2mV9Vj9sQBWqsK2m2pidcf9H3s6bNKtqd3/M4gBo= +github.com/vbauerster/mpb/v7 v7.4.2 h1:n917F4d8EWdUKc9c81wFkksyG6P6Mg7IETfKCE1Xqng= +github.com/vbauerster/mpb/v7 v7.4.2/go.mod h1:UmOiIUI8aPqWXIps0ciik3RKMdzx7+ooQpq+fBcXwBA= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -1298,8 +1309,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= -golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -1400,8 +1411,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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= @@ -1421,8 +1432,9 @@ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 h1:zwrSfklXn0gxyLRX/aR+q6cgHbV/ItVyzbPlbA+dkAw= +golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 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= @@ -1435,8 +1447,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= -golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1536,13 +1548,14 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= -golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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= @@ -1803,8 +1816,8 @@ google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 h1:qRu95HZ148xXw+XeZ3dvqe85PxH4X8+jIo0iRPKcEnM= +google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1838,9 +1851,9 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1874,13 +1887,14 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= +gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -1899,8 +1913,9 @@ 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 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= 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.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= @@ -1914,25 +1929,22 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt 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= honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= -k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I= -k8s.io/api v0.24.1-rc.0 h1:qCZcUZ/YVXVVacShyNgyEHSUGxrWwIlDGAU1XpyMhYs= -k8s.io/api v0.24.1-rc.0/go.mod h1:IXhQwEOq8wx1Hmtm/YCNjyQ3XaeIUH0NP5Zsc0aF5Js= -k8s.io/apiextensions-apiserver v0.24.0 h1:JfgFqbA8gKJ/uDT++feAqk9jBIwNnL9YGdQvaI9DLtY= -k8s.io/apiextensions-apiserver v0.24.0/go.mod h1:iuVe4aEpe6827lvO6yWQVxiPSpPoSKVjkq+MIdg84cM= -k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apimachinery v0.24.1-rc.0 h1:mjcDse2fEbUbiSZYU83NxdLUXlCSprcMlQVgNWn6tGk= -k8s.io/apimachinery v0.24.1-rc.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apiserver v0.24.0 h1:GR7kGsjOMfilRvlG3Stxv/3uz/ryvJ/aZXc5pqdsNV0= -k8s.io/apiserver v0.24.0/go.mod h1:WFx2yiOMawnogNToVvUYT9nn1jaIkMKj41ZYCVycsBA= -k8s.io/cli-runtime v0.24.0 h1:ot3Qf49T852uEyNApABO1UHHpFIckKK/NqpheZYN2gM= -k8s.io/cli-runtime v0.24.0/go.mod h1:9XxoZDsEkRFUThnwqNviqzljtT/LdHtNWvcNFrAXl0A= -k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw= -k8s.io/client-go v0.24.1-rc.0 h1:FoTsvkV8oz2FTORlpAR6TgRQMcdzGVIGQkjc7VrwZ40= -k8s.io/client-go v0.24.1-rc.0/go.mod h1:6VdmspONsQ+gV2AsYugsRaVSYOEUm3Trd16aQJgJahU= -k8s.io/code-generator v0.24.0/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= -k8s.io/component-base v0.24.0 h1:h5jieHZQoHrY/lHG+HyrSbJeyfuitheBvqvKwKHVC0g= -k8s.io/component-base v0.24.0/go.mod h1:Dgazgon0i7KYUsS8krG8muGiMVtUZxG037l1MKyXgrA= -k8s.io/component-helpers v0.24.0/go.mod h1:Q2SlLm4h6g6lPTC9GMMfzdywfLSvJT2f1hOnnjaWD8c= +k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY= +k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= +k8s.io/apiextensions-apiserver v0.24.1 h1:5yBh9+ueTq/kfnHQZa0MAo6uNcPrtxPMpNQgorBaKS0= +k8s.io/apiextensions-apiserver v0.24.1/go.mod h1:A6MHfaLDGfjOc/We2nM7uewD5Oa/FnEbZ6cD7g2ca4Q= +k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I= +k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apiserver v0.24.1 h1:LAA5UpPOeaREEtFAQRUQOI3eE5So/j5J3zeQJjeLdz4= +k8s.io/apiserver v0.24.1/go.mod h1:dQWNMx15S8NqJMp0gpYfssyvhYnkilc1LpExd/dkLh0= +k8s.io/cli-runtime v0.24.1 h1:IW6L8dRBq+pPTzvXcB+m/hOabzbqXy57Bqo4XxmW7DY= +k8s.io/cli-runtime v0.24.1/go.mod h1:14aVvCTqkA7dNXY51N/6hRY3GUjchyWDOwW84qmR3bs= +k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= +k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8= +k8s.io/code-generator v0.24.1/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= +k8s.io/component-base v0.24.1 h1:APv6W/YmfOWZfo+XJ1mZwep/f7g7Tpwvdbo9CQLDuts= +k8s.io/component-base v0.24.1/go.mod h1:DW5vQGYVCog8WYpNob3PMmmsY8A3L9QZNg4j/dV3s38= +k8s.io/component-helpers v0.24.1/go.mod h1:q5Z1pWV/QfX9ThuNeywxasiwkLw9KsR4Q9TAOdb/Y3s= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -1943,11 +1955,11 @@ k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= -k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6 h1:nBQrWPlrNIiw0BsX6a6MKr1itkm0ZS0Nl97kNLitFfI= -k8s.io/kube-openapi v0.0.0-20220413171646-5e7f5fdc6da6/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= -k8s.io/kubectl v0.24.0 h1:nA+WtMLVdXUs4wLogGd1mPTAesnLdBpCVgCmz3I7dXo= -k8s.io/kubectl v0.24.0/go.mod h1:pdXkmCyHiRTqjYfyUJiXtbVNURhv0/Q1TyRhy2d5ic0= -k8s.io/metrics v0.24.0/go.mod h1:jrLlFGdKl3X+szubOXPG0Lf2aVxuV3QJcbsgVRAM6fI= +k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60 h1:cE/M8rmDQgibspuSm+X1iW16ByTImtEaapgaHoVSLX4= +k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60/go.mod h1:ouUzE1U2mEv//HRoBwYLFE5pdqjIebvtX361vtEIlBI= +k8s.io/kubectl v0.24.1 h1:gxcjHrnwntV1c+G/BHWVv4Mtk8CQJ0WTraElLBG+ddk= +k8s.io/kubectl v0.24.1/go.mod h1:NzFqQ50B004fHYWOfhHTrAm4TY6oGF5FAAL13LEaeUI= +k8s.io/metrics v0.24.1/go.mod h1:vMs5xpcOyY9D+/XVwlaw8oUHYCo6JTGBCZfyXOOkAhE= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -1961,8 +1973,9 @@ 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.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= +sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 h1:2sgAQQcY0dEW2SsQwTXhQV4vO6+rSslYx8K3XmM5hqQ= +sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/kind v0.14.0 h1:cNmI3jGBvp7UegEGbC5we8plDtCUmaNRL+bod7JoSCE= sigs.k8s.io/kind v0.14.0/go.mod h1:UrFRPHG+2a5j0Q7qiR4gtJ4rEyn8TuMQwuOPf+m4oHg= sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= From 7d386057f2571ff337efc7f4a3d307abc83374c0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Jun 2022 11:17:58 +0200 Subject: [PATCH 0268/2268] ci: Use ubuntu-20.04 in CI --- .github/workflows/tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e6a9124ca..e0aaa6f29 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -78,7 +78,7 @@ jobs: docker-host: if: "!startsWith(github.ref, 'refs/tags/')" - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 needs: - build steps: @@ -137,13 +137,13 @@ jobs: strategy: matrix: include: - - os: ubuntu-18.04 + - os: ubuntu-20.04 binary-suffix: linux-amd64 - os: macos-10.15 binary-suffix: darwin-amd64 - os: windows-2019 binary-suffix: windows-amd64 - os: [ubuntu-18.04, macos-10.15, windows-2019] + os: [ubuntu-20.04, macos-10.15, windows-2019] fail-fast: false needs: - build From 5452ca251b98217bc74e2c675ff89def081863f6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Jun 2022 16:01:46 +0200 Subject: [PATCH 0269/2268] fix: Ignore ComponentStatus when listing all objects This removes the deprecate warning of ComponentStatus --- pkg/k8s/k8s_cluster.go | 6 ------ pkg/k8s/resources.go | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 32499284f..a810baa31 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -25,12 +25,6 @@ import ( "time" ) -var ( - deprecatedResources = map[schema.GroupKind]bool{ - {Group: "extensions", Kind: "Ingress"}: true, - } -) - type K8sCluster struct { ctx context.Context diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index e8a3d843e..a6a5aac81 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -17,6 +17,13 @@ import ( "sync" ) +var ( + deprecatedResources = map[schema.GroupKind]bool{ + {Group: "extensions", Kind: "Ingress"}: true, + {Group: "", Kind: "ComponentStatus"}: true, + } +) + type k8sResources struct { ctx context.Context discovery discovery.DiscoveryInterface From eb0a92ffab90656144338d52d477ae7df76e70d0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Jun 2022 16:44:40 +0200 Subject: [PATCH 0270/2268] fix: Use context name from cluster config --- pkg/kluctl_project/target_context.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 2e4ac84bb..b5ed711fd 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -110,18 +110,14 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin return nil, nil, "", err } + varsCtx := vars.NewVarsCtx(p.J2) + var contextName *string if clusterName == nil && target != nil { clusterName = target.Cluster contextName = target.Context } - clientConfig, restConfig, err := p.loadArgs.ClientConfigGetter(contextName) - if err != nil { - return doError(err) - } - - varsCtx := vars.NewVarsCtx(p.J2) if clusterName != nil { clusterConfig, err := p.LoadClusterConfig(*clusterName) if err != nil { @@ -131,7 +127,16 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin if err != nil { return doError(err) } + if contextName == nil { + contextName = &clusterConfig.Cluster.Context + } } + + clientConfig, restConfig, err := p.loadArgs.ClientConfigGetter(contextName) + if err != nil { + return doError(err) + } + targetVars, err := uo.FromStruct(target) if err != nil { return doError(err) From 70157973a597cfa5117a2ba74627a6634d809713 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Jun 2022 14:48:28 +0200 Subject: [PATCH 0271/2268] fix: Skip entries with no host when looking for KLUCTL_GIT_XXX variables --- pkg/git/auth/env_auth_provider.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index f05516e00..9d4be5eef 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -21,6 +21,9 @@ func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr Username: m["USERNAME"], Password: m["PASSWORD"], } + if e.Host == "" { + continue + } ssh_key_path, _ := m["SSH_KEY"] if ssh_key_path != "" { From 21d0fe5ed0f1c592c8dbb44a8f3fbf93f20cb254 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Jun 2022 14:49:14 +0200 Subject: [PATCH 0272/2268] fix: More tracing in ListAuthProvider and GitEnvAuthProvider --- pkg/git/auth/env_auth_provider.go | 7 +++++-- pkg/git/auth/list_auth_provider.go | 11 +++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index 9d4be5eef..2c1483811 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -26,11 +26,14 @@ func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr } ssh_key_path, _ := m["SSH_KEY"] + + status.Trace(ctx, "GitEnvAuthProvider: adding entry host=%s, pathPrefix=%s, username=%s, ssh_key=%s", e.Host, e.PathPrefix, e.Username, ssh_key_path) + if ssh_key_path != "" { ssh_key_path = utils.ExpandPath(ssh_key_path) b, err := ioutil.ReadFile(ssh_key_path) if err != nil { - status.Trace(ctx, "Failed to read key %s: %v", ssh_key_path, err) + status.Trace(ctx, "GitEnvAuthProvider: failed to read key %s: %v", ssh_key_path, err) } else { e.SshKey = b } @@ -40,7 +43,7 @@ func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr ca_bundle_path = utils.ExpandPath(ca_bundle_path) b, err := ioutil.ReadFile(ca_bundle_path) if err != nil { - status.Trace(ctx, "Failed to read ca bundle %s: %v", ca_bundle_path, err) + status.Trace(ctx, "GitEnvAuthProvider: failed to read ca bundle %s: %v", ca_bundle_path, err) } else { e.CABundle = b } diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index 4faa19b70..e1ce56525 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -30,7 +30,11 @@ func (a *ListAuthProvider) AddEntry(e AuthEntry) { } func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { + status.Trace(ctx, "ListAuthProvider: BuildAuth for %s", gitUrl.String()) + status.Trace(ctx, "ListAuthProvider: path=%s, username=%s, scheme=%s", gitUrl.Path, gitUrl.User.Username(), gitUrl.Scheme) for _, e := range a.entries { + status.Trace(ctx, "ListAuthProvider: try host=%s, pathPrefix=%s, username=%s", e.Host, e.PathPrefix, e.Username) + if e.Host != "*" && e.Host != gitUrl.Hostname() { continue } @@ -61,11 +65,13 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) if gitUrl.IsSsh() { if e.SshKey == nil { + status.Trace(ctx, "ListAuthProvider: empty ssh key is not accepted") continue } + status.Trace(ctx, "ListAuthProvider: using username+sshKey") a, err := ssh.NewPublicKeys(username, e.SshKey, "") if err != nil { - status.Trace(ctx, "Failed to parse private key: %v", err) + status.Trace(ctx, "ListAuthProvider: failed to parse private key: %v", err) } else { a.HostKeyCallback = buildVerifyHostCallback(ctx, e.KnownHosts) return AuthMethodAndCA{ @@ -74,9 +80,10 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) } } else { if e.Password == "" { - // empty password is not accepted + status.Trace(ctx, "ListAuthProvider: empty password is not accepted") continue } + status.Trace(ctx, "ListAuthProvider: using username+password") return AuthMethodAndCA{ AuthMethod: &http.BasicAuth{ Username: username, From 0a95b9ac42715ca1d5f63c6fdc82f1668c221cbe Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Jun 2022 15:00:50 +0200 Subject: [PATCH 0273/2268] fix: Ignore leading / when checking for git path prefix --- pkg/git/auth/list_auth_provider.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index e1ce56525..6ea7ba938 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -38,7 +38,11 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) if e.Host != "*" && e.Host != gitUrl.Hostname() { continue } - if !strings.HasPrefix(gitUrl.Path, e.PathPrefix) { + urlPath := gitUrl.Path + if strings.HasPrefix(urlPath, "/") { + urlPath = urlPath[1:] + } + if !strings.HasPrefix(urlPath, e.PathPrefix) { continue } if e.Username == "" { From 560adf2d466d934457424ec8c6bd8c5b59726815 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 10 Jun 2022 10:12:03 +0200 Subject: [PATCH 0274/2268] fix: Don't remove cloned dirs too early --- cmd/kluctl/commands/utils.go | 7 ++---- pkg/git/repoprovider/live.go | 35 ++++++++++++++++++++-------- pkg/git/repoprovider/repoprovider.go | 1 + 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 5ff04f003..4fdc5f1a9 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -76,9 +76,6 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b } p, err := kluctl_project.LoadKluctlProject(ctx, loadArgs, tmpDir, j2) - if p != nil && p.RP != nil { - defer p.RP.Clear() - } if err != nil { return err } @@ -186,8 +183,8 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm images: images, } - // we can assume that all git access is done at this point, so we can clear grc and thus unlock all repos - p.RP.Clear() + // we can assume that all git access is done at this point, so we can unlock all repos + p.RP.UnlockAll() return cb(cmdCtx) } diff --git a/pkg/git/repoprovider/live.go b/pkg/git/repoprovider/live.go index 9d877b6ae..bb0243f9a 100644 --- a/pkg/git/repoprovider/live.go +++ b/pkg/git/repoprovider/live.go @@ -20,7 +20,10 @@ type LiveRepoProvider struct { authProviders *auth.GitAuthProviders updateInterval time.Duration repos map[string]*entry - mutex sync.Mutex + reposMutex sync.Mutex + + cleanupDirs []string + cleeanupDirsMutex sync.Mutex } type entry struct { @@ -43,15 +46,11 @@ func NewLiveRepoProvider(ctx context.Context, authProviders *auth.GitAuthProvide } } -func (rp *LiveRepoProvider) Clear() { - rp.mutex.Lock() - defer rp.mutex.Unlock() +func (rp *LiveRepoProvider) UnlockAll() { + rp.reposMutex.Lock() + defer rp.reposMutex.Unlock() for _, e := range rp.repos { - for _, cd := range e.clonedDirs { - _ = os.RemoveAll(cd.dir) - } - if e.mr.IsLocked() { _ = e.mr.Unlock() } @@ -60,10 +59,22 @@ func (rp *LiveRepoProvider) Clear() { rp.repos = map[string]*entry{} } +func (rp *LiveRepoProvider) Clear() { + rp.UnlockAll() + + rp.cleeanupDirsMutex.Lock() + defer rp.cleeanupDirsMutex.Unlock() + + for _, p := range rp.cleanupDirs { + _ = os.RemoveAll(p) + } + rp.cleanupDirs = nil +} + func (rp *LiveRepoProvider) getEntry(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*entry, error) { e, err := func() (*entry, error) { - rp.mutex.Lock() - defer rp.mutex.Unlock() + rp.reposMutex.Lock() + defer rp.reposMutex.Unlock() e, ok := rp.repos[url.NormalizedRepoKey()] if !ok { @@ -178,6 +189,10 @@ func (rp *LiveRepoProvider) GetClonedDir(url git_url.GitUrl, ref string) (string return "", git.CheckoutInfo{}, err } + rp.cleeanupDirsMutex.Lock() + rp.cleanupDirs = append(rp.cleanupDirs, p) + rp.cleeanupDirsMutex.Unlock() + err = e.mr.CloneProject(ref, p) if err != nil { return "", git.CheckoutInfo{}, err diff --git a/pkg/git/repoprovider/repoprovider.go b/pkg/git/repoprovider/repoprovider.go index 77495df96..a6427551a 100644 --- a/pkg/git/repoprovider/repoprovider.go +++ b/pkg/git/repoprovider/repoprovider.go @@ -14,5 +14,6 @@ type RepoInfo struct { type RepoProvider interface { GetRepoInfo(url git_url.GitUrl) (RepoInfo, error) GetClonedDir(url git_url.GitUrl, ref string) (string, git.CheckoutInfo, error) + UnlockAll() Clear() } From 11064b7bef932073118db036b27bc49063ea8cef Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 13 Jun 2022 16:47:47 +0200 Subject: [PATCH 0275/2268] feat: Allow to select secrets/configmaps vars sources via labels --- pkg/types/vars_source.go | 18 ++++++++++++--- pkg/vars/vars_loader.go | 38 ++++++++++++++++++++++++------- pkg/vars/vars_loader_test.go | 43 ++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index 68ba14731..89ca64760 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -15,9 +15,20 @@ type VarsSourceGit struct { } type VarsSourceClusterConfigMapOrSecret struct { - Name string `yaml:"name" validate:"required"` - Namespace string `yaml:"namespace,omitempty"` - Key string `yaml:"key" validate:"required"` + Name string `yaml:"name,omitempty"` + Labels map[string]string `yaml:"labels,omitempty"` + Namespace string `yaml:"namespace" validate:"required"` + Key string `yaml:"key" validate:"required"` +} + +func ValidateVarsSourceClusterConfigMapOrSecret(sl validator.StructLevel) { + s := sl.Current().Interface().(VarsSourceClusterConfigMapOrSecret) + + if s.Name == "" && len(s.Labels) == 0 { + sl.ReportError(s, "self", "self", "either name or labels must be set", "") + } else if s.Name != "" && len(s.Labels) != 0 { + sl.ReportError(s, "self", "self", "only one of name or labels can be set", "") + } } type VarsSourceHttp struct { @@ -74,5 +85,6 @@ func ValidateVarsSource(sl validator.StructLevel) { } func init() { + yaml.Validator.RegisterStructValidation(ValidateVarsSourceClusterConfigMapOrSecret, VarsSourceClusterConfigMapOrSecret{}) yaml.Validator.RegisterStructValidation(ValidateVarsSource, VarsSource{}) } diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index bdb586bd8..73a0780b1 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -15,6 +15,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/vars/vault" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" + "k8s.io/apimachinery/pkg/runtime/schema" "os" ) @@ -71,11 +72,9 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear } else if source.Git != nil { return v.loadGit(varsCtx, source.Git, rootKey) } else if source.ClusterConfigMap != nil { - ref := k8s2.NewObjectRef("", "v1", "ConfigMap", source.ClusterConfigMap.Name, source.ClusterConfigMap.Namespace) - return v.loadFromK8sObject(varsCtx, ref, source.ClusterConfigMap.Key, rootKey, false) + return v.loadFromK8sObject(varsCtx, *source.ClusterConfigMap, "ConfigMap", source.ClusterConfigMap.Key, rootKey, false) } else if source.ClusterSecret != nil { - ref := k8s2.NewObjectRef("", "v1", "Secret", source.ClusterSecret.Name, source.ClusterSecret.Namespace) - return v.loadFromK8sObject(varsCtx, ref, source.ClusterSecret.Key, rootKey, true) + return v.loadFromK8sObject(varsCtx, *source.ClusterSecret, "Secret", source.ClusterSecret.Key, rootKey, true) } else if source.SystemEnvVars != nil { return v.loadSystemEnvs(varsCtx, &source, rootKey) } else if source.Http != nil { @@ -172,16 +171,39 @@ func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, roo return v.loadFromString(varsCtx, string(f), rootKey) } -func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, ref k8s2.ObjectRef, key string, rootKey string, base64Decode bool) error { +func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, key string, rootKey string, base64Decode bool) error { if v.k == nil { return nil } - o, _, err := v.k.GetSingleObject(ref) - if err != nil { - return err + var err error + var o *uo.UnstructuredObject + + if varsSource.Name != "" { + o, _, err = v.k.GetSingleObject(k8s2.NewObjectRef("", "v1", kind, varsSource.Name, varsSource.Namespace)) + if err != nil { + return err + } + } else { + objs, _, err := v.k.ListObjects(schema.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: kind, + }, varsSource.Namespace, varsSource.Labels) + if err != nil { + return err + } + if len(objs) == 0 { + return fmt.Errorf("no object found with labels %v", varsSource.Labels) + } + if len(objs) > 1 { + return fmt.Errorf("found more than one objects with labels %v", varsSource.Labels) + } + o = objs[0] } + ref := o.GetK8sRef() + f, found, err := o.GetNestedField("data", key) if err != nil { return err diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 03faf654e..1dbb14018 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -277,6 +277,49 @@ func TestVarsLoader_ClusterSecret(t *testing.T) { }, &secret) } +func TestVarsLoader_K8sObjectLabels(t *testing.T) { + cm1 := corev1.ConfigMap{ + ObjectMeta: v1.ObjectMeta{Name: "cm1", Namespace: "ns", Labels: map[string]string{"label1": "value1"}}, + Data: map[string]string{ + "vars": `{"test1": {"test2": 42}}`, + }, + } + cm2 := corev1.ConfigMap{ + ObjectMeta: v1.ObjectMeta{Name: "cm2", Namespace: "ns", Labels: map[string]string{"label2": "value2"}}, + Data: map[string]string{ + "vars": `{"test3": {"test4": 43}}`, + }, + } + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + ClusterConfigMap: &types.VarsSourceClusterConfigMapOrSecret{ + Labels: map[string]string{"label1": "value1"}, + Namespace: "ns", + Key: "vars", + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }, &cm1, &cm2) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + ClusterConfigMap: &types.VarsSourceClusterConfigMapOrSecret{ + Labels: map[string]string{"label2": "value2"}, + Namespace: "ns", + Key: "vars", + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test3", "test4") + assert.Equal(t, int64(43), v) + }, &cm1, &cm2) +} + func TestVarsLoader_SystemEnv(t *testing.T) { t.Setenv("TEST1", "42") t.Setenv("TEST2", "43") From 57a4eb2d4ca93c89f8435d810b5d6efb44339f70 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 14 Jun 2022 14:01:55 +0200 Subject: [PATCH 0276/2268] fix: Use simple status handler in case of --debug --- cmd/kluctl/commands/root.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index f71cd8595..c7854cc4a 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -70,15 +70,16 @@ var flagGroups = []groupInfo{ var cliCtx = context.Background() -func setupStatusHandler() { +func setupStatusHandler(debug bool) { var sh status.StatusHandler - if isatty.IsTerminal(os.Stderr.Fd()) { + if !debug && isatty.IsTerminal(os.Stderr.Fd()) { sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, false) } else { sh = status.NewSimpleStatusHandler(func(message string) { _, _ = fmt.Fprintf(os.Stderr, "%s\n", message) }, false) } + sh.SetTrace(debug) cliCtx = status.NewContext(cliCtx, sh) klog.LogToStderr(false) @@ -143,7 +144,7 @@ func (c *cli) checkNewVersion() { } func (c *cli) preRun() error { - status.FromContext(cliCtx).SetTrace(c.Debug) + setupStatusHandler(c.Debug) c.checkNewVersion() return nil } @@ -193,7 +194,6 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif return root.preRun() } - setupStatusHandler() initViper() err = rootCmd.ExecuteContext(cliCtx) From fbdd3856ef9f8f12c464b64d0ba26b1b1d28eedd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 14 Jun 2022 14:02:07 +0200 Subject: [PATCH 0277/2268] fix: Redirect stderr lines to status handler --- cmd/kluctl/commands/root.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index c7854cc4a..497f18dae 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -27,6 +27,7 @@ import ( "github.com/mattn/go-isatty" "github.com/spf13/cobra" "github.com/spf13/viper" + "io" "k8s.io/klog/v2" "log" "net/http" @@ -85,6 +86,18 @@ func setupStatusHandler(debug bool) { klog.LogToStderr(false) klog.SetOutput(status.NewLineRedirector(sh.Info)) log.SetOutput(status.NewLineRedirector(sh.Info)) + + pr, pw, err := os.Pipe() + if err != nil { + panic(err) + } + + go func() { + x := status.NewLineRedirector(sh.Info) + _, _ = io.Copy(x, pr) + }() + + os.Stderr = pw } type VersionCheckState struct { From a619f0dbea5b1b92ee8bb1e5b587d5faff513439 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 14 Jun 2022 14:03:13 +0200 Subject: [PATCH 0278/2268] fix: Fix check for existing dirs in checkDeploymentDirs This fixes an issue with relatively (e.g. ../../deployment) included sub deployments. --- pkg/deployment/deployment_project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 14b894813..36c005f62 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -120,7 +120,7 @@ func (p *DeploymentProject) checkDeploymentDirs() error { continue } - diDir, err := securejoin.SecureJoin(p.absDir, *di.Path) + diDir, err := securejoin.SecureJoin(p.source.dir, filepath.Join(p.relDir, *di.Path)) if err != nil { return err } From a796596b539bd7a465620aa3a1b548b292e7d906 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 15 Jun 2022 10:45:39 +0200 Subject: [PATCH 0279/2268] fix: Determine if stderr is a terminal before overriding os.Stderr --- cmd/kluctl/commands/root.go | 6 ++++-- pkg/status/noop.go | 4 ++++ pkg/status/promts.go | 15 ++++++++++++--- pkg/status/simple_status_handler.go | 16 +++++++++++----- pkg/status/status.go | 2 ++ pkg/status/status_handler.go | 22 ++++++++++++++-------- 6 files changed, 47 insertions(+), 18 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 497f18dae..e0329e7ff 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -72,13 +72,15 @@ var flagGroups = []groupInfo{ var cliCtx = context.Background() func setupStatusHandler(debug bool) { + // we must determine isTerminal before we override os.Stderr + isTerminal := isatty.IsTerminal(os.Stderr.Fd()) var sh status.StatusHandler if !debug && isatty.IsTerminal(os.Stderr.Fd()) { - sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, false) + sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, isTerminal, false) } else { sh = status.NewSimpleStatusHandler(func(message string) { _, _ = fmt.Fprintf(os.Stderr, "%s\n", message) - }, false) + }, isTerminal, false) } sh.SetTrace(debug) cliCtx = status.NewContext(cliCtx, sh) diff --git a/pkg/status/noop.go b/pkg/status/noop.go index c1ff08a9e..53f9f7a51 100644 --- a/pkg/status/noop.go +++ b/pkg/status/noop.go @@ -8,6 +8,10 @@ type NoopStatusHandler struct { type NoopStatusLine struct { } +func (n NoopStatusHandler) IsTerminal() bool { + return false +} + func (n NoopStatusHandler) SetTrace(trace bool) { } diff --git a/pkg/status/promts.go b/pkg/status/promts.go index 8274e8c40..3f8276a10 100644 --- a/pkg/status/promts.go +++ b/pkg/status/promts.go @@ -9,13 +9,22 @@ import ( "strings" ) +func isTerminal(ctx context.Context) bool { + sh := FromContext(ctx) + if sh != nil { + return sh.IsTerminal() + } else { + return isatty.IsTerminal(os.Stderr.Fd()) + } +} + // AskForConfirmation uses Scanln to parse user input. A user must type in "yes" or "no" and // then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", and "Yes" all count as // confirmations. If the input is not recognized, it will ask again. The function does not return // until it gets a valid response from the user. Typically, you should use fmt to print out a question // before calling askForConfirmation. E.g. fmt.Println("WARNING: Are you sure? (yes/no)") func AskForConfirmation(ctx context.Context, prompt string) bool { - if !isatty.IsTerminal(os.Stderr.Fd()) { + if !isTerminal(ctx) { Warning(ctx, "Not a terminal, suppressed prompt: %s", prompt) return false } @@ -37,7 +46,7 @@ func AskForConfirmation(ctx context.Context, prompt string) bool { } func AskForPassword(ctx context.Context, prompt string) (string, error) { - if !isatty.IsTerminal(os.Stderr.Fd()) { + if !isTerminal(ctx) { err := fmt.Errorf("not a terminal, suppressed credentials prompt: %s", prompt) Warning(ctx, err.Error()) return "", err @@ -52,7 +61,7 @@ func AskForPassword(ctx context.Context, prompt string) (string, error) { } func AskForCredentials(ctx context.Context, prompt string) (string, string, error) { - if !isatty.IsTerminal(os.Stderr.Fd()) { + if !isTerminal(ctx) { err := fmt.Errorf("not a terminal, suppressed credentials prompt: %s", prompt) Warning(ctx, err.Error()) return "", "", err diff --git a/pkg/status/simple_status_handler.go b/pkg/status/simple_status_handler.go index 2f7cc9772..cdddd374c 100644 --- a/pkg/status/simple_status_handler.go +++ b/pkg/status/simple_status_handler.go @@ -8,20 +8,26 @@ import ( ) type simpleStatusHandler struct { - cb func(message string) - trace bool + cb func(message string) + isTerminal bool + trace bool } type simpleStatusLine struct { } -func NewSimpleStatusHandler(cb func(message string), trace bool) StatusHandler { +func NewSimpleStatusHandler(cb func(message string), isTerminal bool, trace bool) StatusHandler { return &simpleStatusHandler{ - cb: cb, - trace: trace, + cb: cb, + isTerminal: isTerminal, + trace: trace, } } +func (s *simpleStatusHandler) IsTerminal() bool { + return s.isTerminal +} + func (s *simpleStatusHandler) SetTrace(trace bool) { s.trace = trace } diff --git a/pkg/status/status.go b/pkg/status/status.go index 0e0514e41..875e03391 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -37,6 +37,8 @@ type StatusLine interface { } type StatusHandler interface { + IsTerminal() bool + SetTrace(trace bool) Stop() Flush() diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index d385afb76..ad9491bad 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -13,10 +13,11 @@ import ( ) type MultiLineStatusHandler struct { - ctx context.Context - out io.Writer - progress *mpb.Progress - trace bool + ctx context.Context + out io.Writer + isTerminal bool + progress *mpb.Progress + trace bool } type statusLine struct { @@ -29,11 +30,12 @@ type statusLine struct { barOverride string } -func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, trace bool) *MultiLineStatusHandler { +func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, isTerminal bool, trace bool) *MultiLineStatusHandler { sh := &MultiLineStatusHandler{ - ctx: ctx, - out: out, - trace: trace, + ctx: ctx, + out: out, + isTerminal: isTerminal, + trace: trace, } sh.start() @@ -41,6 +43,10 @@ func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, trace bool) * return sh } +func (s *MultiLineStatusHandler) IsTerminal() bool { + return s.isTerminal +} + func (s *MultiLineStatusHandler) Flush() { s.Stop() s.start() From d00efb995c57808f1127d39de77c28818d482574 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 15 Jun 2022 11:06:41 +0200 Subject: [PATCH 0280/2268] tests: No need to deploy a sleeping busybox deployment Deploy a ConfigMap instead --- e2e/external_projects_test.go | 15 +++++++-------- e2e/utils_resources.go | 15 --------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/e2e/external_projects_test.go b/e2e/external_projects_test.go index eefd91b17..18dbe95ca 100644 --- a/e2e/external_projects_test.go +++ b/e2e/external_projects_test.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "testing" - "time" ) func doTestProject(t *testing.T, namespace string, p *testProject) { @@ -21,21 +20,21 @@ func doTestProject(t *testing.T, namespace string, p *testProject) { p.updateTargetDeprecated("test", k.Name, uo.FromMap(map[string]interface{}{ "target_var": "target_value1", })) - addBusyboxDeployment(p, "busybox", resourceOpts{name: "busybox", namespace: namespace}) + addConfigMapDeployment(p, "cm1", map[string]string{}, resourceOpts{name: "cm1", namespace: namespace}) p.KluctlMust("deploy", "--yes", "-t", "test") - assertReadiness(t, k, namespace, "Deployment/busybox", time.Minute*5) + assertResourceExists(t, k, namespace, "ConfigMap/cm1") cmData := map[string]string{ "cluster_var": "{{ cluster.cluster_var }}", "target_var": "{{ args.target_var }}", } - assertResourceNotExists(t, k, namespace, "ConfigMap/cm") - addConfigMapDeployment(p, "cm", cmData, resourceOpts{name: "cm", namespace: namespace}) + assertResourceNotExists(t, k, namespace, "ConfigMap/cm2") + addConfigMapDeployment(p, "cm2", cmData, resourceOpts{name: "cm2", namespace: namespace}) p.KluctlMust("deploy", "--yes", "-t", "test") - o := assertResourceExists(t, k, namespace, "ConfigMap/cm") + o := assertResourceExists(t, k, namespace, "ConfigMap/cm2") assertNestedFieldEquals(t, o, "cluster_value1", "data", "cluster_var") assertNestedFieldEquals(t, o, "target_value1", "data", "target_var") @@ -43,7 +42,7 @@ func doTestProject(t *testing.T, namespace string, p *testProject) { "cluster_var": "cluster_value2", })) p.KluctlMust("deploy", "--yes", "-t", "test") - o = assertResourceExists(t, k, namespace, "ConfigMap/cm") + o = assertResourceExists(t, k, namespace, "ConfigMap/cm2") assertNestedFieldEquals(t, o, "cluster_value2", "data", "cluster_var") assertNestedFieldEquals(t, o, "target_value1", "data", "target_var") @@ -51,7 +50,7 @@ func doTestProject(t *testing.T, namespace string, p *testProject) { "target_var": "target_value2", })) p.KluctlMust("deploy", "--yes", "-t", "test") - o = assertResourceExists(t, k, namespace, "ConfigMap/cm") + o = assertResourceExists(t, k, namespace, "ConfigMap/cm2") assertNestedFieldEquals(t, o, "cluster_value2", "data", "cluster_var") assertNestedFieldEquals(t, o, "target_value2", "data", "target_var") } diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 0dc063e7f..e6f9e4a20 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -92,17 +92,6 @@ func addDeploymentHelper(p *testProject, dir string, o *uo.UnstructuredObject, o p.addKustomizeDeployment(dir, resources, opts.tags) } -func addDeploymentDeployment(p *testProject, dir string, opts resourceOpts, image string, command []string, args []string) { - o := renderTemplateObjectHelper(deploymentTemplate, map[string]interface{}{ - "name": opts.name, - "namespace": opts.namespace, - "image": image, - }) - o[0].SetNestedField(command, "spec", "template", "spec", "containers", 0, "command") - o[0].SetNestedField(args, "spec", "template", "spec", "containers", 0, "args") - addDeploymentHelper(p, dir, o[0], opts) -} - func addJobDeployment(p *testProject, dir string, opts resourceOpts, image string, command []string, args []string) { o := renderTemplateObjectHelper(jobTemplate, map[string]interface{}{ "name": opts.name, @@ -114,10 +103,6 @@ func addJobDeployment(p *testProject, dir string, opts resourceOpts, image strin addDeploymentHelper(p, dir, o[0], opts) } -func addBusyboxDeployment(p *testProject, dir string, opts resourceOpts) { - addDeploymentDeployment(p, dir, opts, "busybox", []string{"sleep"}, []string{"1000"}) -} - const podRbacTemplate = ` apiVersion: v1 kind: ServiceAccount From 4d1e8b019d833e08f43b74ed78f3e16794841c42 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 16 Jun 2022 11:25:22 +0200 Subject: [PATCH 0281/2268] feat: Remove version field from DeleteObjectItemConfig --- pkg/deployment/utils/apply_utils.go | 2 +- pkg/types/deployment.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 46b246351..eb6cded06 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -403,7 +403,7 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { var toDelete []k8s2.ObjectRef for _, x := range d.Config.DeleteObjects { - for _, gvk := range a.k.Resources.GetFilteredGVKs(k8s.BuildGVKFilter(x.Group, x.Version, x.Kind)) { + for _, gvk := range a.k.Resources.GetFilteredGVKs(k8s.BuildGVKFilter(x.Group, nil, x.Kind)) { ref := k8s2.ObjectRef{ GVK: gvk, Name: x.Name, diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index 6f2ccd6fa..d331457f2 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -41,7 +41,6 @@ func ValidateDeploymentItemConfig(sl validator.StructLevel) { type DeleteObjectItemConfig struct { Group *string `yaml:"group,omitempty"` - Version *string `yaml:"version,omitempty"` Kind *string `yaml:"kind,omitempty"` Name string `yaml:"name" validate:"required"` Namespace string `yaml:"namespace,omitempty"` @@ -49,7 +48,7 @@ type DeleteObjectItemConfig struct { func ValidateDeleteObjectItemConfig(sl validator.StructLevel) { s := sl.Current().Interface().(DeleteObjectItemConfig) - if s.Group == nil && s.Version == nil && s.Kind == nil { + if s.Group == nil && s.Kind == nil { sl.ReportError(s, "self", "self", "at least one of group/version/kind must be set", "") } } From e2092eb29d922403642a8761f198662387874687 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 29 Jun 2022 14:24:50 +0200 Subject: [PATCH 0282/2268] fix: Call setupStatusHandler even for unknown commands --- cmd/kluctl/commands/root.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index e0329e7ff..435c6e67d 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -70,8 +70,11 @@ var flagGroups = []groupInfo{ } var cliCtx = context.Background() +var didSetupStatusHandler bool func setupStatusHandler(debug bool) { + didSetupStatusHandler = true + // we must determine isTerminal before we override os.Stderr isTerminal := isatty.IsTerminal(os.Stderr.Fd()) var sh status.StatusHandler @@ -212,6 +215,9 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif initViper() err = rootCmd.ExecuteContext(cliCtx) + if !didSetupStatusHandler { + setupStatusHandler(false) + } sh := status.FromContext(cliCtx) From 93e9978117bf738ab56b34640856dc1007d41528 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 1 Jul 2022 14:15:40 +0200 Subject: [PATCH 0283/2268] fix: Fix crash in poke-image in case a remote object does not exist yet --- pkg/deployment/commands/poke_images.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 03ae6dbc3..cde5280b7 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -77,7 +77,12 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type go func() { defer wg.Done() au := ad.NewApplyUtil(ctx, nil) - au.ReplaceObject(ref, ru.GetRemoteObject(ref), func(o *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { + remote := ru.GetRemoteObject(ref) + if remote == nil { + dew.AddWarning(ref, fmt.Errorf("remote object not found, skipped image replacement")) + return + } + au.ReplaceObject(ref, remote, func(o *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { return doPokeImage(containers, o) }) }() From ea50eff19dc2c63480f5da6fef3d974ef929e3a2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 1 Jul 2022 14:31:57 +0200 Subject: [PATCH 0284/2268] fix: Use local object for field manager path conversion This fixes a bug where a key based fieldpath element points to a different index in the local vs remote objects. --- pkg/diff/managed_fields.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index 8dd6ce197..deaf26f4e 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -175,21 +175,28 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr return nil, nil, fmt.Errorf("field path '%s' is ambiguous", cause.Field) } - p, found, err := convertToKeyList(remote, mf.pathes[0]) + localKeyPath, found, err := convertToKeyList(local, mf.pathes[0]) if err != nil { return nil, nil, err } if !found { - return nil, nil, fmt.Errorf("field '%s' not found in remote object", cause.Field) + return nil, nil, fmt.Errorf("field '%s' not found in local object", cause.Field) } - localValue, found, err := local.GetNestedField(p...) + + remoteKeyPath, found, err := convertToKeyList(remote, mf.pathes[0]) if err != nil { return nil, nil, err } if !found { - return nil, nil, fmt.Errorf("field '%s' not found in local object", cause.Field) + return nil, nil, fmt.Errorf("field '%s' not found in remote object", cause.Field) } - remoteValue, found, err := remote.GetNestedField(p...) + + localValue, found, err := local.GetNestedField(localKeyPath...) + if !found { + panic(fmt.Sprintf("field '%s' not found in local object...which can't be!", cause.Field)) + } + + remoteValue, found, err := remote.GetNestedField(remoteKeyPath...) if !found { panic(fmt.Sprintf("field '%s' not found in remote object...which can't be!", cause.Field)) } @@ -209,13 +216,16 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr break } } - if _, ok := forceApplyFields[p.ToJsonPath()]; ok { + if _, ok := forceApplyFields[localKeyPath.ToJsonPath()]; ok { + overwrite = true + } + if _, ok := forceApplyFields[remoteKeyPath.ToJsonPath()]; ok { overwrite = true } } if !overwrite { - j, err := uo.NewMyJsonPath(p.ToJsonPath()) + j, err := uo.NewMyJsonPath(localKeyPath.ToJsonPath()) if err != nil { return nil, nil, err } From 12c47e8ce6241e5521d4ae02365cd086ea5cee81 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 6 Jul 2022 13:53:57 +0200 Subject: [PATCH 0285/2268] fix: Add original message to conflict error messages --- pkg/diff/managed_fields.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index deaf26f4e..0e89651fe 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -169,10 +169,10 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr mf, ok := managersByFields[cause.Field] if !ok { - return nil, nil, fmt.Errorf("could not find matching field for path '%s'", cause.Field) + return nil, nil, fmt.Errorf("%s. Could not find matching field for path '%s'", cause.Message, cause.Field) } if len(mf.pathes) != 1 { - return nil, nil, fmt.Errorf("field path '%s' is ambiguous", cause.Field) + return nil, nil, fmt.Errorf("%s. Field path '%s' is ambiguous", cause.Message, cause.Field) } localKeyPath, found, err := convertToKeyList(local, mf.pathes[0]) @@ -180,7 +180,7 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr return nil, nil, err } if !found { - return nil, nil, fmt.Errorf("field '%s' not found in local object", cause.Field) + return nil, nil, fmt.Errorf("%s. Field '%s' not found in local object", cause.Message, cause.Field) } remoteKeyPath, found, err := convertToKeyList(remote, mf.pathes[0]) @@ -188,7 +188,7 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr return nil, nil, err } if !found { - return nil, nil, fmt.Errorf("field '%s' not found in remote object", cause.Field) + return nil, nil, fmt.Errorf("%s. Field '%s' not found in remote object", cause.Message, cause.Field) } localValue, found, err := local.GetNestedField(localKeyPath...) From ed7c5000e1339d92b01d2b9006e1bbf88690717e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 6 Jul 2022 14:34:57 +0200 Subject: [PATCH 0286/2268] fix: Stop using goccy/go-yaml and fully rely on yaml.v3 --- go.mod | 4 +--- go.sum | 8 -------- pkg/yaml/validator.go | 26 ++++++++++++++++++++++++- pkg/yaml/yaml.go | 45 ++++++++++++++++++++++++++++--------------- 4 files changed, 56 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index b4e6c807c..ca9a9d8ec 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.0 github.com/gobwas/glob v0.2.3 - github.com/goccy/go-yaml v1.9.5 github.com/golang-jwt/jwt/v4 v4.4.1 github.com/google/go-containerregistry v0.9.0 github.com/hashicorp/vault/api v1.6.0 @@ -25,6 +24,7 @@ require ( github.com/klauspost/compress v1.15.6 github.com/mattn/go-isatty v0.0.14 github.com/mitchellh/go-ps v1.0.0 + github.com/mitchellh/reflectwalk v1.0.2 github.com/ohler55/ojg v1.14.2 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 @@ -158,7 +158,6 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect @@ -199,7 +198,6 @@ require ( golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect - golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 // indirect google.golang.org/grpc v1.47.0 // indirect diff --git a/go.sum b/go.sum index 739e736a0..af4a31531 100644 --- a/go.sum +++ b/go.sum @@ -399,13 +399,10 @@ github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrK github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 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 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= 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 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= 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.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= @@ -438,8 +435,6 @@ github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XE github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0= -github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= @@ -796,7 +791,6 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= @@ -1685,8 +1679,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 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= diff --git a/pkg/yaml/validator.go b/pkg/yaml/validator.go index aa2398605..4c3265da2 100644 --- a/pkg/yaml/validator.go +++ b/pkg/yaml/validator.go @@ -1,7 +1,31 @@ package yaml -import "github.com/go-playground/validator/v10" +import ( + "github.com/go-playground/validator/v10" + "github.com/mitchellh/reflectwalk" + "reflect" +) var ( Validator = validator.New() ) + +type structValidationWalker struct { +} + +func (w *structValidationWalker) Struct(v reflect.Value) error { + v2 := v.Interface() + err := Validator.Struct(v2) + if err != nil { + return err + } + return nil +} + +func (w *structValidationWalker) StructField(reflect.StructField, reflect.Value) error { + return nil +} + +func ValidateStructs(s interface{}) error { + return reflectwalk.Walk(s, &structValidationWalker{}) +} diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 2c24f990a..295ce0a46 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/goccy/go-yaml" "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/text/encoding/unicode" "golang.org/x/text/transform" @@ -17,8 +16,32 @@ import ( "strings" ) -func newYamlDecoder(r io.Reader) *yaml.Decoder { - return yaml.NewDecoder(r, yaml.Strict(), yaml.Validator(Validator)) +type Decoder interface { + Decode(v interface{}) error +} + +type decoderWrapper struct { + d *yaml3.Decoder +} + +func (w *decoderWrapper) Decode(v interface{}) error { + err := w.d.Decode(v) + if err != nil { + return err + } + + err = ValidateStructs(v) + if err != nil { + return err + } + + return nil +} + +func newDecoder(r io.Reader, out any) Decoder { + d := yaml3.NewDecoder(r) + d.KnownFields(true) + return &decoderWrapper{d: d} } func newUnicodeReader(r io.Reader) io.Reader { @@ -51,16 +74,9 @@ func ReadYamlBytes(b []byte, o interface{}) error { func ReadYamlStream(r io.Reader, o interface{}) error { r = newUnicodeReader(r) - var err error - if _, ok := o.(*map[string]interface{}); ok { - // much faster - d := yaml3.NewDecoder(r) - err = d.Decode(o) - } else { - // we need proper working strict mode - d := newYamlDecoder(r) - err = d.Decode(o) - } + d := newDecoder(r, o) + + err := d.Decode(o) if err != nil && errors.Is(err, io.EOF) { return nil } @@ -88,8 +104,7 @@ func ReadYamlAllBytes(b []byte) ([]interface{}, error) { func ReadYamlAllStream(r io.Reader) ([]interface{}, error) { r = newUnicodeReader(r) - // yaml.v3 is much faster then go-yaml - d := yaml3.NewDecoder(r) + d := newDecoder(r, nil) var l []interface{} for true { From b3c0ee6b5fd5deac8722cfc8ef1d341fc9e92bdc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 6 Jul 2022 14:40:33 +0200 Subject: [PATCH 0287/2268] refactor: Move parsing of rendered manifests into own func --- pkg/deployment/helm_chart.go | 39 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index b41313276..87e78ddbf 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -299,24 +299,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { return err } - var parsed []*uo.UnstructuredObject - - doParse := func(s string) error { - m, err := yaml.ReadYamlAllString(s) - if err != nil { - return err - } - for _, x := range m { - x2, ok := x.(map[string]interface{}) - if !ok { - return fmt.Errorf("yaml object is not a map") - } - parsed = append(parsed, uo.FromMap(x2)) - } - return nil - } - - err = doParse(rel.Manifest) + parsed, err := c.parseRenderedManifests(rel.Manifest) if err != nil { return err } @@ -326,10 +309,11 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { if isTestHook(m) { continue } - err = doParse(m.Manifest) + parsedHooks, err := c.parseRenderedManifests(m.Manifest) if err != nil { return err } + parsed = append(parsed, parsedHooks...) } } @@ -360,6 +344,23 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { return nil } +func (c *helmChart) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, error) { + var parsed []*uo.UnstructuredObject + + m, err := yaml.ReadYamlAllString(s) + if err != nil { + return nil, err + } + for _, x := range m { + x2, ok := x.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("yaml object is not a map") + } + parsed = append(parsed, uo.FromMap(x2)) + } + return parsed, nil +} + func (c *helmChart) SetCredentials(credentials *repo.Entry) { c.credentials = credentials } From 52316f99d0f1e0b683f08e47d1dd93ef1f8e6e0c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 6 Jul 2022 14:57:08 +0200 Subject: [PATCH 0288/2268] fix: Ignore duplicate fields in manifests of rendered Helm charts --- pkg/deployment/helm_chart.go | 7 ++++++- pkg/yaml/yaml.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 87e78ddbf..a533ba8de 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -347,7 +347,12 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { func (c *helmChart) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, error) { var parsed []*uo.UnstructuredObject - m, err := yaml.ReadYamlAllString(s) + duplicatesRemoved, err := yaml.RemoveDuplicateFields(strings.NewReader(s)) + if err != nil { + return nil, err + } + + m, err := yaml.ReadYamlAllBytes(duplicatesRemoved) if err != nil { return nil, err } diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 295ce0a46..6d559b530 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -9,6 +9,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/text/encoding/unicode" "golang.org/x/text/transform" + yaml2 "gopkg.in/yaml.v2" yaml3 "gopkg.in/yaml.v3" "io" "os" @@ -213,6 +214,35 @@ func ConvertYamlToJson(b []byte) ([]byte, error) { return b, nil } +// RemoveDuplicateFields is a helper/hack to remove duplicate fields from yaml maps/structs. The yaml spec explicitly +// forbids duplicate keys, but yaml.v2 ignored those by default, leading to some tools (e.g. Helm) ignoring these. This +// forces us to also ignore/remove them in some cases. We do this by loading the yaml via yaml.v2 and then writing them +// back to a string which can then be parsed by yaml.v3 +// TODO Remove this helper method when https://github.com/go-yaml/yaml/issues/751 is implemented +func RemoveDuplicateFields(r io.Reader) ([]byte, error) { + buf := bytes.NewBuffer(nil) + d := yaml2.NewDecoder(r) + e := yaml2.NewEncoder(buf) + + for { + var o interface{} + err := d.Decode(&o) + if err != nil { + if errors.Is(err, io.EOF) { + break + } + return nil, err + } + if o != nil { + err = e.Encode(o) + if err != nil { + return nil, err + } + } + } + return buf.Bytes(), nil +} + func FixNameExt(dir string, name string) string { p := filepath.Join(dir, name) p = FixPathExt(p) From dbf9994d064955c4534907fd6d8f98a824bd3bf9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 8 Jul 2022 18:16:01 +0200 Subject: [PATCH 0289/2268] chore: Remove support for metadata listing This is not used anymore and thus can be removed. --- pkg/deployment/utils/remote_objects_utils.go | 2 +- pkg/k8s/client.go | 29 +------------- pkg/k8s/client_factory.go | 8 ---- pkg/k8s/fake_client_factory.go | 6 --- pkg/k8s/k8s_cluster.go | 41 +------------------- 5 files changed, 5 insertions(+), 81 deletions(-) diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index ea2c3339a..442acb1a6 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -36,7 +36,7 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st s := status.Start(u.ctx, "Getting remote objects by commonLabels") defer s.Failed() - allObjects, apiWarnings, err := k.ListAllObjects([]string{"get"}, "", labels, false) + allObjects, apiWarnings, err := k.ListAllObjects([]string{"get"}, "", labels) for gvk, aw := range apiWarnings { u.dew.AddApiWarnings(k8s2.ObjectRef{GVK: gvk}, aw) } diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index 76f01135e..829240fc5 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -6,7 +6,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/metadata" ) type k8sClients struct { @@ -17,9 +16,8 @@ type k8sClients struct { } type parallelClientEntry struct { - corev1 corev1.CoreV1Interface - dynamicClient dynamic.Interface - metadataClient metadata.Interface + corev1 corev1.CoreV1Interface + dynamicClient dynamic.Interface warnings []ApiWarning } @@ -61,11 +59,6 @@ func newK8sClients(ctx context.Context, clientFactory ClientFactory, count int) return nil, err } - p.metadataClient, err = clientFactory.MetadataClient(p) - if err != nil { - return nil, err - } - k.clientPool <- p } return k, nil @@ -104,16 +97,6 @@ func (k *k8sClients) withDynamicClientForGVR(gvr *schema.GroupVersionResource, n }) } -func (k *k8sClients) withMetadataClientForGVR(gvr *schema.GroupVersionResource, namespace string, cb func(r metadata.ResourceInterface) error) ([]ApiWarning, error) { - return k.withClientFromPool(func(p *parallelClientEntry) error { - if namespace != "" { - return cb(p.metadataClient.Resource(*gvr).Namespace(namespace)) - } else { - return cb(p.metadataClient.Resource(*gvr)) - } - }) -} - func (k *k8sClients) withDynamicClientForGVK(resources *k8sResources, gvk schema.GroupVersionKind, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { gvr, err := resources.GetGVRForGVK(gvk) if err != nil { @@ -121,11 +104,3 @@ func (k *k8sClients) withDynamicClientForGVK(resources *k8sResources, gvk schema } return k.withDynamicClientForGVR(gvr, namespace, cb) } - -func (k *k8sClients) withMetadataClientForGVK(resources *k8sResources, gvk schema.GroupVersionKind, namespace string, cb func(r metadata.ResourceInterface) error) ([]ApiWarning, error) { - gvr, err := resources.GetGVRForGVK(gvk) - if err != nil { - return nil, err - } - return k.withMetadataClientForGVR(gvr, namespace, cb) -} diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go index 0205c475c..6ab49a220 100644 --- a/pkg/k8s/client_factory.go +++ b/pkg/k8s/client_factory.go @@ -6,7 +6,6 @@ import ( "k8s.io/client-go/discovery/cached/disk" "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/metadata" "k8s.io/client-go/rest" "net/http" "net/url" @@ -22,7 +21,6 @@ type ClientFactory interface { DiscoveryClient() (discovery.DiscoveryInterface, error) CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1Interface, error) DynamicClient(wh rest.WarningHandler) (dynamic.Interface, error) - MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) } type realClientFactory struct { @@ -60,12 +58,6 @@ func (r *realClientFactory) DynamicClient(wh rest.WarningHandler) (dynamic.Inter return dynamic.NewForConfigAndClient(config, r.httpClient) } -func (r *realClientFactory) MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) { - config := rest.CopyConfig(r.config) - config.WarningHandler = wh - return metadata.NewForConfigAndClient(config, r.httpClient) -} - func (r *realClientFactory) CloseIdleConnections() { r.httpClient.CloseIdleConnections() } diff --git a/pkg/k8s/fake_client_factory.go b/pkg/k8s/fake_client_factory.go index 8bc1169f2..b01673f27 100644 --- a/pkg/k8s/fake_client_factory.go +++ b/pkg/k8s/fake_client_factory.go @@ -11,8 +11,6 @@ import ( fake_dynamic "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes/fake" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/metadata" - metadata_fake "k8s.io/client-go/metadata/fake" "k8s.io/client-go/rest" "sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/yaml" @@ -44,10 +42,6 @@ func (f *fakeClientFactory) DynamicClient(wh rest.WarningHandler) (dynamic.Inter return fake_dynamic.NewSimpleDynamicClient(f.scheme, f.objects...), nil } -func (f *fakeClientFactory) MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) { - return metadata_fake.NewSimpleMetadataClient(f.scheme, f.objects...), nil -} - func NewFakeClientFactory(objects ...runtime.Object) ClientFactory { scheme := runtime.NewScheme() _ = v1.AddToScheme(scheme) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index a810baa31..8b58788c9 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -2,7 +2,6 @@ package k8s import ( "context" - "encoding/json" "fmt" "github.com/Masterminds/semver/v3" "github.com/kluctl/kluctl/v2/pkg/status" @@ -18,7 +17,6 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/dynamic" - "k8s.io/client-go/metadata" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" "sync" @@ -129,35 +127,7 @@ func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, return result, apiWarnings, err } -func (k *K8sCluster) ListObjectsMetadata(gvk schema.GroupVersionKind, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { - var result []*uo.UnstructuredObject - - apiWarnings, err := k.clients.withMetadataClientForGVK(k.Resources, gvk, namespace, func(r metadata.ResourceInterface) error { - o := v1.ListOptions{ - LabelSelector: k.buildLabelSelector(labels), - } - x, err := r.List(k.ctx, o) - if err != nil { - return err - } - for _, o := range x.Items { - b, err := json.Marshal(o) - if err != nil { - panic(err) - } - u, err := uo.FromString(string(b)) - if err != nil { - panic(err) - } - u.SetK8sGVK(gvk) - result = append(result, u) - } - return nil - }) - return result, apiWarnings, err -} - -func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map[string]string, onlyMetadata bool) ([]*uo.UnstructuredObject, map[schema.GroupVersionKind][]ApiWarning, error) { +func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, map[schema.GroupVersionKind][]ApiWarning, error) { var ret []*uo.UnstructuredObject var errs []error retApiWarnings := make(map[schema.GroupVersionKind][]ApiWarning) @@ -181,14 +151,7 @@ func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map go func() { defer wg.Done() - var l []*uo.UnstructuredObject - var apiWarnings []ApiWarning - var err error - if onlyMetadata { - l, apiWarnings, err = k.ListObjectsMetadata(gvk, namespace, labels) - } else { - l, apiWarnings, err = k.ListObjects(gvk, namespace, labels) - } + l, apiWarnings, err := k.ListObjects(gvk, namespace, labels) mutex.Lock() defer mutex.Unlock() if err != nil && !errors.IsNotFound(err) { From 24336636a1dd23cca2a549652774738a824429a5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 14 Jul 2022 14:52:04 +0200 Subject: [PATCH 0290/2268] fix: Add rootDir to searchDirs when rendering directories --- pkg/jinja2/jinja2.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go index 83e176870..1bc115aa7 100644 --- a/pkg/jinja2/jinja2.go +++ b/pkg/jinja2/jinja2.go @@ -214,6 +214,8 @@ func (j *Jinja2) needsRender(path string, excludedPatterns []string) bool { } func (j *Jinja2) RenderDirectory(rootDir string, searchDirs []string, relSourceDir string, excludePatterns []string, subdir string, targetDir string, vars *uo.UnstructuredObject) error { + searchDirs = append([]string{rootDir}, searchDirs...) + walkDir, err := securejoin.SecureJoin(rootDir, filepath.Join(relSourceDir, subdir)) if err != nil { return err From 2a32c584b7809d20ce1cb518eb14c215160f7857 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 14 Jul 2022 15:00:04 +0200 Subject: [PATCH 0291/2268] chore: Upgrade python version to 3.10.5 --- pkg/python/generate/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/python/generate/main.go b/pkg/python/generate/main.go index dc68cc409..9cc24b145 100644 --- a/pkg/python/generate/main.go +++ b/pkg/python/generate/main.go @@ -15,8 +15,8 @@ import ( const ( pythonVersionBase = "3.10" - pythonVersionFull = "3.10.4" - pythonStandaloneVersion = "20220502" + pythonVersionFull = "3.10.5" + pythonStandaloneVersion = "20220630" ) var pythonDists = map[string]string{ From d240624113c42cb2c85a78e0fe9fbe8c4b33df07 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 14 Jul 2022 15:00:14 +0200 Subject: [PATCH 0292/2268] chore: Upgrade jinja2 and click --- pkg/jinja2/python_src/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/jinja2/python_src/requirements.txt b/pkg/jinja2/python_src/requirements.txt index 7bc8d801c..9ee2b287d 100644 --- a/pkg/jinja2/python_src/requirements.txt +++ b/pkg/jinja2/python_src/requirements.txt @@ -1,4 +1,4 @@ -jinja2===3.0.3 -click==8.0.3 +jinja2===3.1.2 +click==8.1.3 jsonpath-ng==1.5.3 pyyaml==6.0 From 3f7a6357e29afd6cdfa4457126da30bd2cc33ec6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 18 Jul 2022 14:15:27 +0200 Subject: [PATCH 0293/2268] fix: Only hold repo locks while actually pulling/cloning And don't hold locks for the whole lifetime of loaded projects. Instead, get a copy of the refs maps while the lock is held and afterwards use the commit ids for precise checkouts. --- cmd/kluctl/commands/utils.go | 7 +- pkg/deployment/deployment_project.go | 6 +- pkg/deployment/shared_context.go | 4 +- pkg/git/mirrored_repo.go | 8 +- pkg/git/poor_mans_clone.go | 32 +--- pkg/git/repocache/cache.go | 223 +++++++++++++++++++++++++++ pkg/git/repoprovider/live.go | 211 ------------------------- pkg/git/repoprovider/repoprovider.go | 19 --- pkg/kluctl_project/git.go | 2 +- pkg/kluctl_project/project.go | 4 +- pkg/kluctl_project/project_load.go | 11 +- pkg/kluctl_project/targets.go | 11 +- pkg/vars/vars_loader.go | 13 +- pkg/vars/vars_loader_test.go | 5 +- 14 files changed, 271 insertions(+), 285 deletions(-) create mode 100644 pkg/git/repocache/cache.go delete mode 100644 pkg/git/repoprovider/live.go delete mode 100644 pkg/git/repoprovider/repoprovider.go diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 4fdc5f1a9..8459ae908 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -8,7 +8,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" + "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/registries" @@ -59,7 +59,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b ctx, cancel := context.WithTimeout(cliCtx, projectFlags.Timeout) defer cancel() - rp := repoprovider.NewLiveRepoProvider(ctx, auth.NewDefaultAuthProviders(), projectFlags.GitCacheUpdateInterval) + rp := repocache.NewGitRepoCache(ctx, auth.NewDefaultAuthProviders(), projectFlags.GitCacheUpdateInterval) defer rp.Clear() loadArgs := kluctl_project.LoadKluctlProjectArgs{ @@ -183,9 +183,6 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm images: images, } - // we can assume that all git access is done at this point, so we can unlock all repos - p.RP.UnlockAll() - return cb(cmdCtx) } diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 36c005f62..a11c1e919 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -155,7 +155,11 @@ func (p *DeploymentProject) loadIncludes() error { return err } } else if inc.Git != nil { - cloneDir, _, err := p.ctx.RP.GetClonedDir(inc.Git.Url, inc.Git.Ref) + ge, err := p.ctx.RP.GetEntry(inc.Git.Url) + if err != nil { + return err + } + cloneDir, _, err := ge.GetClonedDir(inc.Git.Ref) if err != nil { return err } diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index a659a43e0..3d4e3ca50 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -2,7 +2,7 @@ package deployment import ( "context" - "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" + "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/vars" ) @@ -10,7 +10,7 @@ import ( type SharedContext struct { Ctx context.Context K *k8s.K8sCluster - RP repoprovider.RepoProvider + RP *repocache.GitRepoCache VarsLoader *vars.VarsLoader RenderDir string diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 81525865e..5a48414aa 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -343,16 +343,16 @@ func (g *MirroredGitRepo) Update(authProviders *auth2.GitAuthProviders) error { return nil } -func (g *MirroredGitRepo) CloneProject(ref string, targetDir string) error { +func (g *MirroredGitRepo) CloneProjectByCommit(commit string, targetDir string) error { if !g.IsLocked() || !g.hasUpdated { panic("tried to clone from a project that is not locked/updated") } - status.Trace(g.ctx, "Cloning git project: url='%s', ref='%s', target='%s'", g.url.String(), ref, targetDir) + status.Trace(g.ctx, "Cloning git project: url='%s', commit='%s', target='%s'", g.url.String(), commit, targetDir) - err := PoorMansClone(g.mirrorDir, targetDir, ref) + err := PoorMansClone(g.mirrorDir, targetDir, &git.CheckoutOptions{Hash: plumbing.NewHash(commit)}) if err != nil { - return fmt.Errorf("failed to clone %s from %s: %w", ref, g.url.String(), err) + return fmt.Errorf("failed to clone %s from %s: %w", commit, g.url.String(), err) } return nil } diff --git a/pkg/git/poor_mans_clone.go b/pkg/git/poor_mans_clone.go index 851b6ae04..d5216cb49 100644 --- a/pkg/git/poor_mans_clone.go +++ b/pkg/git/poor_mans_clone.go @@ -1,20 +1,17 @@ package git import ( - "fmt" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing" "github.com/kluctl/kluctl/v2/pkg/utils" "io/ioutil" "os" "path/filepath" "runtime" - "strings" ) -// PoorMansClone poor mans clone from a local repo, which does not rely on go-git using git-upload-pack -func PoorMansClone(sourceDir string, targetDir string, ref string) error { +// PoorMansCloneCommit poor mans clone from a local repo, which does not rely on go-git using git-upload-pack +func PoorMansClone(sourceDir string, targetDir string, coOptions *git.CheckoutOptions) error { err := os.MkdirAll(targetDir, 0o700) if err != nil { return err @@ -80,29 +77,8 @@ func PoorMansClone(sourceDir string, targetDir string, ref string) error { if err != nil { return err } - var ref2 plumbing.ReferenceName - if ref != "" { - if strings.HasPrefix(ref, "refs/heads") { - ref2 = plumbing.ReferenceName(ref) - } else { - if _, err := r.Reference(plumbing.NewBranchReferenceName(ref), true); err == nil { - ref2 = plumbing.NewBranchReferenceName(ref) - } else if _, err := r.Reference(plumbing.NewTagReferenceName(ref), true); err == nil { - ref2 = plumbing.NewTagReferenceName(ref) - } else { - return fmt.Errorf("ref %s not found", ref) - } - } - } else { - x, err := r.Reference("HEAD", true) - if err != nil { - return err - } - ref2 = x.Name() - } - err = wt.Checkout(&git.CheckoutOptions{ - Branch: ref2, - }) + + err = wt.Checkout(coOptions) if err != nil { return err } diff --git a/pkg/git/repocache/cache.go b/pkg/git/repocache/cache.go new file mode 100644 index 000000000..34d3ebcef --- /dev/null +++ b/pkg/git/repocache/cache.go @@ -0,0 +1,223 @@ +package repocache + +import ( + "context" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/git/auth" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/utils" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + "sync" + "time" +) + +type GitRepoCache struct { + ctx context.Context + authProviders *auth.GitAuthProviders + updateInterval time.Duration + repos map[string]*CacheEntry + reposMutex sync.Mutex + + cleanupDirs []string + cleeanupDirsMutex sync.Mutex +} + +type CacheEntry struct { + rp *GitRepoCache + mr *git.MirroredGitRepo + defaultRef string + refs map[string]string + + clonedDirs map[string]clonedDir + updateMutex sync.Mutex +} + +type RepoInfo struct { + Url git_url.GitUrl `yaml:"url"` + RemoteRefs map[string]string `yaml:"remoteRefs"` + DefaultRef string `yaml:"defaultRef"` +} + +type clonedDir struct { + dir string + info git.CheckoutInfo +} + +func NewGitRepoCache(ctx context.Context, authProviders *auth.GitAuthProviders, updateInterval time.Duration) *GitRepoCache { + return &GitRepoCache{ + ctx: ctx, + authProviders: authProviders, + updateInterval: updateInterval, + repos: map[string]*CacheEntry{}, + } +} + +func (rp *GitRepoCache) Clear() { + rp.cleeanupDirsMutex.Lock() + defer rp.cleeanupDirsMutex.Unlock() + + for _, p := range rp.cleanupDirs { + _ = os.RemoveAll(p) + } + rp.cleanupDirs = nil +} + +func (rp *GitRepoCache) GetEntry(url git_url.GitUrl) (*CacheEntry, error) { + rp.reposMutex.Lock() + defer rp.reposMutex.Unlock() + + e, ok := rp.repos[url.NormalizedRepoKey()] + if !ok { + mr, err := git.NewMirroredGitRepo(rp.ctx, url) + if err != nil { + return nil, err + } + e = &CacheEntry{ + rp: rp, + mr: mr, + clonedDirs: map[string]clonedDir{}, + } + rp.repos[url.NormalizedRepoKey()] = e + } + err := e.Update() + if err != nil { + return nil, err + } + return e, nil +} + +func (e *CacheEntry) Update() error { + e.updateMutex.Lock() + defer e.updateMutex.Unlock() + + err := e.mr.Lock() + if err != nil { + return err + } + defer e.mr.Unlock() + + if !e.mr.HasUpdated() { + if time.Now().Sub(e.mr.LastUpdateTime()) <= e.rp.updateInterval { + e.mr.SetUpdated(true) + } else { + err := e.mr.Update(e.rp.authProviders) + if err != nil { + return err + } + } + } + + e.refs, err = e.mr.RemoteRefHashesMap() + if err != nil { + return err + } + + e.defaultRef, err = e.mr.DefaultRef() + if err != nil { + return err + } + + return nil +} + +func (e *CacheEntry) GetRepoInfo() RepoInfo { + e.updateMutex.Lock() + defer e.updateMutex.Unlock() + + info := RepoInfo{ + Url: e.mr.Url(), + RemoteRefs: e.refs, + DefaultRef: e.defaultRef, + } + + return info +} + +func (e *CacheEntry) findCommit(ref string) (string, string, error) { + if strings.HasPrefix(ref, "refs/heads") { + c, ok := e.refs[ref] + if !ok { + return "", "", fmt.Errorf("ref %s not found", ref) + } + return ref, c, nil + } else { + ref2 := "refs/heads/" + ref + c, ok := e.refs[ref2] + if ok { + return ref2, c, nil + } + ref2 = "refs/tags/" + ref + c, ok = e.refs[ref2] + if ok { + return ref2, c, nil + } + return "", "", fmt.Errorf("ref %s not found", ref) + } +} + +func (e *CacheEntry) GetClonedDir(ref string) (string, git.CheckoutInfo, error) { + e.updateMutex.Lock() + defer e.updateMutex.Unlock() + + err := e.mr.Lock() + if err != nil { + return "", git.CheckoutInfo{}, err + } + defer e.mr.Unlock() + + if ref == "" { + ref = e.defaultRef + } + + ref2, commit, err := e.findCommit(ref) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + tmpDir := filepath.Join(utils.GetTmpBaseDir(), "git-cloned") + err = os.MkdirAll(tmpDir, 0700) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + url := e.mr.Url() + repoName := path.Base(url.Normalize().Path) + "-" + if ref == "" { + repoName += "HEAD-" + } else { + repoName += ref + "-" + } + repoName = strings.ReplaceAll(repoName, "/", "-") + + p, err := ioutil.TempDir(tmpDir, repoName) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + e.rp.cleeanupDirsMutex.Lock() + e.rp.cleanupDirs = append(e.rp.cleanupDirs, p) + e.rp.cleeanupDirsMutex.Unlock() + + err = e.mr.CloneProjectByCommit(commit, p) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + repoInfo, err := git.GetCheckoutInfo(p) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + repoInfo.CheckedOutRef = ref2 + + e.clonedDirs[ref] = clonedDir{ + dir: p, + info: repoInfo, + } + return p, repoInfo, nil +} diff --git a/pkg/git/repoprovider/live.go b/pkg/git/repoprovider/live.go deleted file mode 100644 index bb0243f9a..000000000 --- a/pkg/git/repoprovider/live.go +++ /dev/null @@ -1,211 +0,0 @@ -package repoprovider - -import ( - "context" - "fmt" - "github.com/kluctl/kluctl/v2/pkg/git" - "github.com/kluctl/kluctl/v2/pkg/git/auth" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/utils" - "io/ioutil" - "os" - "path" - "path/filepath" - "sync" - "time" -) - -type LiveRepoProvider struct { - ctx context.Context - authProviders *auth.GitAuthProviders - updateInterval time.Duration - repos map[string]*entry - reposMutex sync.Mutex - - cleanupDirs []string - cleeanupDirsMutex sync.Mutex -} - -type entry struct { - mr *git.MirroredGitRepo - clonedDirs map[string]clonedDir - updateMutex sync.Mutex -} - -type clonedDir struct { - dir string - info git.CheckoutInfo -} - -func NewLiveRepoProvider(ctx context.Context, authProviders *auth.GitAuthProviders, updateInterval time.Duration) RepoProvider { - return &LiveRepoProvider{ - ctx: ctx, - authProviders: authProviders, - updateInterval: updateInterval, - repos: map[string]*entry{}, - } -} - -func (rp *LiveRepoProvider) UnlockAll() { - rp.reposMutex.Lock() - defer rp.reposMutex.Unlock() - - for _, e := range rp.repos { - if e.mr.IsLocked() { - _ = e.mr.Unlock() - } - } - - rp.repos = map[string]*entry{} -} - -func (rp *LiveRepoProvider) Clear() { - rp.UnlockAll() - - rp.cleeanupDirsMutex.Lock() - defer rp.cleeanupDirsMutex.Unlock() - - for _, p := range rp.cleanupDirs { - _ = os.RemoveAll(p) - } - rp.cleanupDirs = nil -} - -func (rp *LiveRepoProvider) getEntry(url git_url.GitUrl, allowCreate bool, lockRepo bool, update bool) (*entry, error) { - e, err := func() (*entry, error) { - rp.reposMutex.Lock() - defer rp.reposMutex.Unlock() - - e, ok := rp.repos[url.NormalizedRepoKey()] - if !ok { - if !allowCreate { - return nil, fmt.Errorf("git repo %s not found", url.NormalizedRepoKey()) - } - mr, err := git.NewMirroredGitRepo(rp.ctx, url) - if err != nil { - return nil, err - } - e = &entry{ - mr: mr, - clonedDirs: map[string]clonedDir{}, - } - rp.repos[url.NormalizedRepoKey()] = e - - if lockRepo { - err = e.mr.Lock() - if err != nil { - return nil, err - } - } - } - return e, nil - }() - if err != nil { - return nil, err - } - - e.updateMutex.Lock() - defer e.updateMutex.Unlock() - - if update && !e.mr.HasUpdated() { - if time.Now().Sub(e.mr.LastUpdateTime()) <= rp.updateInterval { - e.mr.SetUpdated(true) - } else { - err = e.mr.Update(rp.authProviders) - if err != nil { - return nil, err - } - } - } - - return e, nil -} - -func (e *entry) getRepoInfo() (RepoInfo, error) { - remoteRefs, err := e.mr.RemoteRefHashesMap() - if err != nil { - return RepoInfo{}, err - } - - defaultRef, err := e.mr.DefaultRef() - if err != nil { - return RepoInfo{}, err - } - - info := RepoInfo{ - Url: e.mr.Url(), - RemoteRefs: remoteRefs, - DefaultRef: defaultRef, - } - - return info, nil -} - -func (rp *LiveRepoProvider) GetRepoInfo(url git_url.GitUrl) (RepoInfo, error) { - e, err := rp.getEntry(url, true, true, true) - if err != nil { - return RepoInfo{}, err - } - - return e.getRepoInfo() -} - -func (rp *LiveRepoProvider) GetClonedDir(url git_url.GitUrl, ref string) (string, git.CheckoutInfo, error) { - e, err := rp.getEntry(url, true, true, true) - if err != nil { - return "", git.CheckoutInfo{}, err - } - - if ref == "" { - ref, err = e.mr.DefaultRef() - if err != nil { - return "", git.CheckoutInfo{}, err - } - ref = path.Base(ref) - } - - e.updateMutex.Lock() - defer e.updateMutex.Unlock() - - cd, ok := e.clonedDirs[ref] - if ok { - return cd.dir, cd.info, nil - } - - tmpDir := filepath.Join(utils.GetTmpBaseDir(), "git-cloned") - err = os.MkdirAll(tmpDir, 0700) - if err != nil { - return "", git.CheckoutInfo{}, err - } - - repoName := path.Base(url.Normalize().Path) + "-" - if ref == "" { - repoName += "HEAD-" - } else { - repoName += ref + "-" - } - p, err := ioutil.TempDir(tmpDir, repoName) - if err != nil { - return "", git.CheckoutInfo{}, err - } - - rp.cleeanupDirsMutex.Lock() - rp.cleanupDirs = append(rp.cleanupDirs, p) - rp.cleeanupDirsMutex.Unlock() - - err = e.mr.CloneProject(ref, p) - if err != nil { - return "", git.CheckoutInfo{}, err - } - - repoInfo, err := git.GetCheckoutInfo(p) - if err != nil { - return "", git.CheckoutInfo{}, err - } - - e.clonedDirs[ref] = clonedDir{ - dir: p, - info: repoInfo, - } - return p, repoInfo, nil -} diff --git a/pkg/git/repoprovider/repoprovider.go b/pkg/git/repoprovider/repoprovider.go deleted file mode 100644 index a6427551a..000000000 --- a/pkg/git/repoprovider/repoprovider.go +++ /dev/null @@ -1,19 +0,0 @@ -package repoprovider - -import ( - "github.com/kluctl/kluctl/v2/pkg/git" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" -) - -type RepoInfo struct { - Url git_url.GitUrl `yaml:"url"` - RemoteRefs map[string]string `yaml:"remoteRefs"` - DefaultRef string `yaml:"defaultRef"` -} - -type RepoProvider interface { - GetRepoInfo(url git_url.GitUrl) (RepoInfo, error) - GetClonedDir(url git_url.GitUrl, ref string) (string, git.CheckoutInfo, error) - UnlockAll() - Clear() -} diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 31e5678b8..12e7253e5 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -25,7 +25,7 @@ func (c *LoadedKluctlProject) updateGitCaches() error { go func() { defer waitGroup.Done() - _, err := c.RP.GetRepoInfo(u) + _, err := c.RP.GetEntry(u) if err != nil { doError(fmt.Errorf("failed to update git project %v: %v", u.String(), err)) } diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index ee616243a..da067d70f 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -3,7 +3,7 @@ package kluctl_project import ( "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" + "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/jinja2" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -28,7 +28,7 @@ type LoadedKluctlProject struct { DynamicTargets []*types2.DynamicTarget J2 *jinja2.Jinja2 - RP repoprovider.RepoProvider + RP *repocache.GitRepoCache warnOnce utils.OnceByKey } diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index a409b3de7..3a2d1e730 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -3,7 +3,7 @@ package kluctl_project import ( "fmt" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" + "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/status" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -25,7 +25,7 @@ type LoadKluctlProjectArgs struct { LocalDeployment string LocalSealedSecrets string - RP repoprovider.RepoProvider + RP *repocache.GitRepoCache ClientConfigGetter func(context *string) (*rest.Config, *api.Config, error) } @@ -56,7 +56,12 @@ func (c *LoadedKluctlProject) localProject(dir string) gitProjectInfo { } func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defaultSubDir string) (ret gitProjectInfo, err error) { - cloneDir, ri, err := c.RP.GetClonedDir(gitProject.Url, gitProject.Ref) + ge, err := c.RP.GetEntry(gitProject.Url) + if err != nil { + return + } + + cloneDir, ri, err := ge.GetClonedDir(gitProject.Ref) if err != nil { return } diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 795424ad4..f51688a38 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -138,11 +138,13 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsSimple(baseTarget *types.Targ } func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { - repoInfo, err := c.RP.GetRepoInfo(baseTarget.TargetConfig.Project.Url) + ge, err := c.RP.GetEntry(baseTarget.TargetConfig.Project.Url) if err != nil { return nil, err } + repoInfo := ge.GetRepoInfo() + if baseTarget.TargetConfig.Ref != nil && baseTarget.TargetConfig.RefPattern != nil { return nil, fmt.Errorf("'refPattern' and 'ref' can't be specified together") } @@ -178,7 +180,12 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Ta continue } - cloneDir, _, err := c.RP.GetClonedDir(baseTarget.TargetConfig.Project.Url, refShortName) + ge, err := c.RP.GetEntry(baseTarget.TargetConfig.Project.Url) + if err != nil { + return nil, err + } + + cloneDir, _, err := ge.GetClonedDir(refShortName) if err != nil { return nil, err } diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 73a0780b1..022bd895c 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -5,7 +5,7 @@ import ( "encoding/base64" "fmt" securejoin "github.com/cyphar/filepath-securejoin" - "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" + "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" @@ -27,13 +27,13 @@ type usernamePassword struct { type VarsLoader struct { ctx context.Context k *k8s.K8sCluster - rp repoprovider.RepoProvider + rp *repocache.GitRepoCache aws aws.AwsClientFactory credentialsCache map[string]usernamePassword } -func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, rp repoprovider.RepoProvider, aws aws.AwsClientFactory) *VarsLoader { +func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, rp *repocache.GitRepoCache, aws aws.AwsClientFactory) *VarsLoader { return &VarsLoader{ ctx: ctx, k: k, @@ -153,7 +153,12 @@ func (v *VarsLoader) loadVault(varsCtx *VarsCtx, source *types.VarsSource, rootK } func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, rootKey string) error { - clonedDir, _, err := v.rp.GetClonedDir(gitFile.Url, gitFile.Ref) + ge, err := v.rp.GetEntry(gitFile.Url) + if err != nil { + return err + } + + clonedDir, _, err := ge.GetClonedDir(gitFile.Ref) if err != nil { return fmt.Errorf("failed to load vars from git repository %s: %w", gitFile.Url.String(), err) } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 1dbb14018..94b95ebeb 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -7,7 +7,6 @@ import ( test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/git/repoprovider" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -37,8 +36,8 @@ func newTestDir(t *testing.T) string { return tmp } -func newRP(t *testing.T) repoprovider.RepoProvider { - grc := repoprovider.NewLiveRepoProvider(context.TODO(), auth.NewDefaultAuthProviders(), 0) +func newRP(t *testing.T) repocache.RepoProvider { + grc := repocache.NewLiveRepoProvider(context.TODO(), auth.NewDefaultAuthProviders(), 0) t.Cleanup(func() { grc.Clear() }) From 8dc84490abecdbb0682be983124247f684843e59 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 18 Jul 2022 15:20:48 +0200 Subject: [PATCH 0294/2268] fix: Fix compilation errors for tests --- pkg/vars/vars_loader_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 94b95ebeb..ad7ee5be0 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -7,6 +7,7 @@ import ( test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -36,8 +37,8 @@ func newTestDir(t *testing.T) string { return tmp } -func newRP(t *testing.T) repocache.RepoProvider { - grc := repocache.NewLiveRepoProvider(context.TODO(), auth.NewDefaultAuthProviders(), 0) +func newRP(t *testing.T) *repocache.GitRepoCache { + grc := repocache.NewGitRepoCache(context.TODO(), auth.NewDefaultAuthProviders(), 0) t.Cleanup(func() { grc.Clear() }) From b5bcb45f5ec882b177e0190cf4829f236dff723c Mon Sep 17 00:00:00 2001 From: Christian Neufeld <74351567+CNeufeldCoder@users.noreply.github.com> Date: Wed, 20 Jul 2022 12:14:32 +0200 Subject: [PATCH 0295/2268] fix: Use original stderr file for simple status handler (#44) Co-authored-by: Christian Neufeld --- cmd/kluctl/commands/root.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 435c6e67d..06a9321e8 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -75,6 +75,8 @@ var didSetupStatusHandler bool func setupStatusHandler(debug bool) { didSetupStatusHandler = true + origStderr := os.Stderr + // we must determine isTerminal before we override os.Stderr isTerminal := isatty.IsTerminal(os.Stderr.Fd()) var sh status.StatusHandler @@ -82,7 +84,7 @@ func setupStatusHandler(debug bool) { sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, isTerminal, false) } else { sh = status.NewSimpleStatusHandler(func(message string) { - _, _ = fmt.Fprintf(os.Stderr, "%s\n", message) + _, _ = fmt.Fprintf(origStderr, "%s\n", message) }, isTerminal, false) } sh.SetTrace(debug) From c68720856223536505a5981a14b2fd750efb9808 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 20 Jul 2022 16:06:20 +0200 Subject: [PATCH 0296/2268] tests: Upgrade sealed-secrets operator used in tests --- e2e/test_resources/sealed-secrets.yaml | 38 +++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/e2e/test_resources/sealed-secrets.yaml b/e2e/test_resources/sealed-secrets.yaml index b45788937..8e2262447 100644 --- a/e2e/test_resources/sealed-secrets.yaml +++ b/e2e/test_resources/sealed-secrets.yaml @@ -37,10 +37,10 @@ metadata: namespace: kube-system labels: app.kubernetes.io/name: sealed-secrets - helm.sh/chart: sealed-secrets-2.1.8 + helm.sh/chart: sealed-secrets-2.4.0 app.kubernetes.io/instance: sealed-secrets-controller app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: v0.17.5 + app.kubernetes.io/version: v0.18.1 --- # Source: sealed-secrets/templates/cluster-role.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -49,10 +49,10 @@ metadata: name: secrets-unsealer labels: app.kubernetes.io/name: sealed-secrets - helm.sh/chart: sealed-secrets-2.1.8 + helm.sh/chart: sealed-secrets-2.4.0 app.kubernetes.io/instance: sealed-secrets-controller app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: v0.17.5 + app.kubernetes.io/version: v0.18.1 rules: - apiGroups: - bitnami.com @@ -93,10 +93,10 @@ metadata: name: sealed-secrets-controller labels: app.kubernetes.io/name: sealed-secrets - helm.sh/chart: sealed-secrets-2.1.8 + helm.sh/chart: sealed-secrets-2.4.0 app.kubernetes.io/instance: sealed-secrets-controller app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: v0.17.5 + app.kubernetes.io/version: v0.18.1 roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -115,10 +115,10 @@ metadata: namespace: kube-system labels: app.kubernetes.io/name: sealed-secrets - helm.sh/chart: sealed-secrets-2.1.8 + helm.sh/chart: sealed-secrets-2.4.0 app.kubernetes.io/instance: sealed-secrets-controller app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: v0.17.5 + app.kubernetes.io/version: v0.18.1 rules: - apiGroups: - "" @@ -144,10 +144,10 @@ metadata: namespace: kube-system labels: app.kubernetes.io/name: sealed-secrets - helm.sh/chart: sealed-secrets-2.1.8 + helm.sh/chart: sealed-secrets-2.4.0 app.kubernetes.io/instance: sealed-secrets-controller app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: v0.17.5 + app.kubernetes.io/version: v0.18.1 rules: - apiGroups: - "" @@ -177,10 +177,10 @@ metadata: namespace: kube-system labels: app.kubernetes.io/name: sealed-secrets - helm.sh/chart: sealed-secrets-2.1.8 + helm.sh/chart: sealed-secrets-2.4.0 app.kubernetes.io/instance: sealed-secrets-controller app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: v0.17.5 + app.kubernetes.io/version: v0.18.1 roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -199,10 +199,10 @@ metadata: namespace: kube-system labels: app.kubernetes.io/name: sealed-secrets - helm.sh/chart: sealed-secrets-2.1.8 + helm.sh/chart: sealed-secrets-2.4.0 app.kubernetes.io/instance: sealed-secrets-controller app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: v0.17.5 + app.kubernetes.io/version: v0.18.1 roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -220,10 +220,10 @@ metadata: namespace: kube-system labels: app.kubernetes.io/name: sealed-secrets - helm.sh/chart: sealed-secrets-2.1.8 + helm.sh/chart: sealed-secrets-2.4.0 app.kubernetes.io/instance: sealed-secrets-controller app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: v0.17.5 + app.kubernetes.io/version: v0.18.1 spec: type: ClusterIP ports: @@ -243,10 +243,10 @@ metadata: namespace: kube-system labels: app.kubernetes.io/name: sealed-secrets - helm.sh/chart: sealed-secrets-2.1.8 + helm.sh/chart: sealed-secrets-2.4.0 app.kubernetes.io/instance: sealed-secrets-controller app.kubernetes.io/managed-by: Helm - app.kubernetes.io/version: v0.17.5 + app.kubernetes.io/version: v0.18.1 spec: selector: matchLabels: @@ -269,7 +269,7 @@ spec: - --update-status - --key-prefix - "sealed-secrets-key" - image: docker.io/bitnami/sealed-secrets-controller:v0.17.5 + image: docker.io/bitnami/sealed-secrets-controller:v0.18.1 imagePullPolicy: IfNotPresent ports: - containerPort: 8080 From 8d605c4d1de642ee72f56903d00b20dce965dba1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 20 Jul 2022 19:46:14 +0200 Subject: [PATCH 0297/2268] tests: Always use --debug for e2e tests --- e2e/project.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/e2e/project.go b/e2e/project.go index d80c0c3e4..be0bd2823 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -13,7 +13,6 @@ import ( "os/exec" "path/filepath" "reflect" - "runtime" "strings" "testing" ) @@ -430,9 +429,8 @@ func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { args = append(args, "--local-sealed-secrets", *p.localSealedSecrets) } - if runtime.GOOS == "windows" { - args = append(args, "--debug") - } + args = append(args, "--debug") + env := os.Environ() env = append(env, p.extraEnv...) env = append(env, fmt.Sprintf("KUBECONFIG=%s", p.mergedKubeconfig)) From 16e3b6869c21a14fbeef0c626d5c17338374e0f1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 20 Jul 2022 19:46:55 +0200 Subject: [PATCH 0298/2268] chore: Write failing yaml to status handler when tracing is enabled --- pkg/deployment/utils/apply_utils.go | 7 +++++++ pkg/status/noop.go | 4 ++++ pkg/status/simple_status_handler.go | 4 ++++ pkg/status/status.go | 6 ++++++ pkg/status/status_handler.go | 4 ++++ 5 files changed, 25 insertions(+) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index eb6cded06..c241ff6ba 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -13,6 +13,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" + "github.com/kluctl/kluctl/v2/pkg/yaml" "golang.org/x/sync/semaphore" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -388,6 +389,12 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo case <-timeoutTimer.C: err := fmt.Errorf("timed out while waiting for readiness of %s", ref.String()) status.Warning(a.ctx, "%s (%ds elapsed)", err.Error(), elapsed) + if status.IsTraceEnabled(a.ctx) { + y, err := yaml.WriteYamlString(o) + if err == nil { + status.Trace(a.ctx, "yaml:\n"+y) + } + } a.HandleError(ref, err) return false case <-a.ctx.Done(): diff --git a/pkg/status/noop.go b/pkg/status/noop.go index 53f9f7a51..b33511b8b 100644 --- a/pkg/status/noop.go +++ b/pkg/status/noop.go @@ -12,6 +12,10 @@ func (n NoopStatusHandler) IsTerminal() bool { return false } +func (n NoopStatusHandler) IsTraceEnabled() bool { + return false +} + func (n NoopStatusHandler) SetTrace(trace bool) { } diff --git a/pkg/status/simple_status_handler.go b/pkg/status/simple_status_handler.go index cdddd374c..1a330c36b 100644 --- a/pkg/status/simple_status_handler.go +++ b/pkg/status/simple_status_handler.go @@ -28,6 +28,10 @@ func (s *simpleStatusHandler) IsTerminal() bool { return s.isTerminal } +func (s *simpleStatusHandler) IsTraceEnabled() bool { + return s.trace +} + func (s *simpleStatusHandler) SetTrace(trace bool) { s.trace = trace } diff --git a/pkg/status/status.go b/pkg/status/status.go index 875e03391..c49f64dfc 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -38,6 +38,7 @@ type StatusLine interface { type StatusHandler interface { IsTerminal() bool + IsTraceEnabled() bool SetTrace(trace bool) Stop() @@ -234,6 +235,11 @@ func Trace(ctx context.Context, status string, args ...any) { slh.Trace(fmt.Sprintf(status, args...)) } +func IsTraceEnabled(ctx context.Context) bool { + slh := FromContext(ctx) + return slh.IsTraceEnabled() +} + func Error(ctx context.Context, status string, args ...any) { slh := FromContext(ctx) slh.Error(fmt.Sprintf(status, args...)) diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index ad9491bad..7b5a85a82 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -47,6 +47,10 @@ func (s *MultiLineStatusHandler) IsTerminal() bool { return s.isTerminal } +func (s *MultiLineStatusHandler) IsTraceEnabled() bool { + return s.trace +} + func (s *MultiLineStatusHandler) Flush() { s.Stop() s.start() From d61a257271b7eea0ee7aa6829f09d24428245c6e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Jul 2022 12:11:42 +0200 Subject: [PATCH 0299/2268] fix: Fix race condition in ApplyDeploymentsUtil when creating new ApplyUtils --- pkg/deployment/utils/apply_utils.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index c241ff6ba..a14b1528a 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -65,7 +65,8 @@ type ApplyDeploymentsUtil struct { abortSignal atomic.Value - results []*ApplyUtil + resultsMutex sync.Mutex + results []*ApplyUtil } func NewApplyDeploymentsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnings, deployments []*deployment.DeploymentItem, ru *RemoteObjectUtils, k *k8s.K8sCluster, o *ApplyUtilOptions) *ApplyDeploymentsUtil { @@ -82,6 +83,9 @@ func NewApplyDeploymentsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnin } func (ad *ApplyDeploymentsUtil) NewApplyUtil(ctx context.Context, statusCtx *status.StatusContext) *ApplyUtil { + ad.resultsMutex.Lock() + defer ad.resultsMutex.Unlock() + ret := &ApplyUtil{ ctx: ctx, dew: ad.dew, @@ -651,6 +655,9 @@ func (a *ApplyUtil) ReplaceObject(ref k8s2.ObjectRef, firstVersion *uo.Unstructu } func (ad *ApplyDeploymentsUtil) GetAppliedObjects() []*types.RefAndObject { + ad.resultsMutex.Lock() + defer ad.resultsMutex.Unlock() + var ret []*types.RefAndObject for _, a := range ad.results { for _, o := range a.appliedObjects { @@ -672,6 +679,9 @@ func (ad *ApplyDeploymentsUtil) GetAppliedObjectsMap() map[k8s2.ObjectRef]*uo.Un } func (ad *ApplyDeploymentsUtil) GetAppliedHookObjects() []*types.RefAndObject { + ad.resultsMutex.Lock() + defer ad.resultsMutex.Unlock() + var ret []*types.RefAndObject for _, a := range ad.results { for _, o := range a.appliedHookObjects { @@ -685,6 +695,9 @@ func (ad *ApplyDeploymentsUtil) GetAppliedHookObjects() []*types.RefAndObject { } func (ad *ApplyDeploymentsUtil) GetDeletedObjects() []k8s2.ObjectRef { + ad.resultsMutex.Lock() + defer ad.resultsMutex.Unlock() + var ret []k8s2.ObjectRef m := make(map[k8s2.ObjectRef]bool) for _, a := range ad.results { From 7d5c42fbee21cdd8f99b2e15b0d9aad95444a42e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 25 Jul 2022 12:06:25 +0200 Subject: [PATCH 0300/2268] feat: Implement --cpu-profile --- cmd/kluctl/commands/root.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 06a9321e8..7e3928ac8 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -33,6 +33,7 @@ import ( "net/http" "os" "path/filepath" + "runtime/pprof" "strings" "time" ) @@ -43,6 +44,8 @@ type cli struct { Debug bool `group:"global" help:"Enable debug logging"` NoUpdateCheck bool `group:"global" help:"Disable update check on startup"` + CpuProfile string `group:"global" help:"Enable CPU profiling and write the result to the given path"` + CheckImageUpdates checkImageUpdatesCmd `cmd:"" help:"Render deployment and check if any images have new tags available"` Delete deleteCmd `cmd:"" help:"Delete a target (or parts of it) from the corresponding cluster"` Deploy deployCmd `cmd:"" help:"Deploys a target to the corresponding cluster"` @@ -107,6 +110,23 @@ func setupStatusHandler(debug bool) { os.Stderr = pw } +var cpuProfileFile *os.File + +func setupProfiling(cpuProfile string) error { + var err error + if cpuProfile != "" { + cpuProfileFile, err = os.Create(cpuProfile) + if err != nil { + return fmt.Errorf("failed to create cpu profile file: %w", err) + } + err = pprof.StartCPUProfile(cpuProfileFile) + if err != nil { + return fmt.Errorf("failed to start cpu profiling: %w", err) + } + } + return nil +} + type VersionCheckState struct { LastVersionCheck time.Time `yaml:"lastVersionCheck"` } @@ -164,6 +184,10 @@ func (c *cli) checkNewVersion() { } func (c *cli) preRun() error { + err := setupProfiling(c.CpuProfile) + if err != nil { + return err + } setupStatusHandler(c.Debug) c.checkNewVersion() return nil @@ -221,6 +245,12 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif setupStatusHandler(false) } + if cpuProfileFile != nil { + pprof.StopCPUProfile() + _ = cpuProfileFile.Close() + cpuProfileFile = nil + } + sh := status.FromContext(cliCtx) if err != nil { From 81d01d797f64f203eece52e65435ad85222a0d99 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 22 Jul 2022 15:12:36 +0200 Subject: [PATCH 0301/2268] refactor: Move loading of target-config.yml into own function --- pkg/kluctl_project/targets.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index f51688a38..eb09429e5 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -9,6 +9,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" + "os" "reflect" "regexp" "sort" @@ -267,6 +268,22 @@ func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTarge return nil } +func (c *LoadedKluctlProject) loadTargetConfigFile(targetInfo *dynamicTargetInfo) ([]byte, error) { + configFile := yaml.FixNameExt(targetInfo.dir, "target-config.yml") + if targetInfo.baseTarget.TargetConfig.File != nil { + configFile = *targetInfo.baseTarget.TargetConfig.File + } + configPath, err := securejoin.SecureJoin(targetInfo.dir, configFile) + if err != nil { + return nil, err + } + if !utils.IsFile(configPath) { + return nil, fmt.Errorf("no target config file with name %s found in target", configFile) + } + + return os.ReadFile(configPath) +} + func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) (*types.Target, error) { var target types.Target err := utils.DeepCopy(&target, targetInfo.baseTarget) @@ -277,20 +294,13 @@ func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) return &target, nil } - configFile := yaml.FixNameExt(targetInfo.dir, "target-config.yml") - if targetInfo.baseTarget.TargetConfig.File != nil { - configFile = *targetInfo.baseTarget.TargetConfig.File - } - configPath, err := securejoin.SecureJoin(targetInfo.dir, configFile) + configFile, err := c.loadTargetConfigFile(targetInfo) if err != nil { return nil, err } - if !utils.IsFile(configPath) { - return nil, fmt.Errorf("no target config file with name %s found in target", configFile) - } var targetConfig types.TargetConfig - err = yaml.ReadYamlFile(configPath, &targetConfig) + err = yaml.ReadYamlBytes(configFile, &targetConfig) if err != nil { return nil, err } From 6ebef19a621e02c6dc2a0d8d6235b46d3ba0057d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 25 Jul 2022 10:33:23 +0200 Subject: [PATCH 0302/2268] chore: Run go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ca9a9d8ec..54167b996 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 golang.org/x/text v0.3.7 + gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.9.0 k8s.io/api v0.24.1 @@ -206,7 +207,6 @@ require ( gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiserver v0.24.1 // indirect k8s.io/cli-runtime v0.24.1 // indirect k8s.io/component-base v0.24.1 // indirect From 10c2380832efb554315b73efca4b635a40ac3537 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 25 Jul 2022 11:35:51 +0200 Subject: [PATCH 0303/2268] fix: Speed up loading of projects with many dynamic targets by avoiding clones So far, kluctl has checked out all branches for all dynamic targets in parallel just to load the target-config.yaml from that branch. This commit changes this to only load the file object from the git tree. --- pkg/git/mirrored_repo.go | 28 +++--------- pkg/git/repocache/cache.go | 23 ++++++++++ pkg/kluctl_project/targets.go | 85 +++++++++++++++++------------------ 3 files changed, 68 insertions(+), 68 deletions(-) diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 5a48414aa..221113f6f 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -357,12 +357,12 @@ func (g *MirroredGitRepo) CloneProjectByCommit(commit string, targetDir string) return nil } -func (g *MirroredGitRepo) ReadFile(ref string, path string) ([]byte, error) { +func (g *MirroredGitRepo) GetGitTreeByCommit(commitHash string) (*object.Tree, error) { if !g.IsLocked() || !g.hasUpdated { panic("tried to read a file from a project that is not locked/updated") } - doError := func(err error) ([]byte, error) { + doError := func(err error) (*object.Tree, error) { return nil, fmt.Errorf("failed to read file from git repostory: %w", err) } @@ -371,16 +371,9 @@ func (g *MirroredGitRepo) ReadFile(ref string, path string) ([]byte, error) { return nil, err } - if ref == "" { - ref = "HEAD" - } + h := plumbing.NewHash(commitHash) - h, err := r.ResolveRevision(plumbing.Revision(ref)) - if err != nil { - return doError(err) - } - - commit, err := object.GetCommit(r.Storer, *h) + commit, err := object.GetCommit(r.Storer, h) if err != nil { return doError(err) } @@ -388,18 +381,7 @@ func (g *MirroredGitRepo) ReadFile(ref string, path string) ([]byte, error) { if err != nil { return doError(err) } - - f, err := tree.File(path) - if err != nil { - return doError(err) - } - reader, err := f.Reader() - if err != nil { - return doError(err) - } - defer reader.Close() - - return ioutil.ReadAll(reader) + return tree, nil } func buildMirrorRepoName(u git_url.GitUrl) string { diff --git a/pkg/git/repocache/cache.go b/pkg/git/repocache/cache.go index 34d3ebcef..3d34e2ec8 100644 --- a/pkg/git/repocache/cache.go +++ b/pkg/git/repocache/cache.go @@ -3,6 +3,7 @@ package repocache import ( "context" "fmt" + "github.com/go-git/go-git/v5/plumbing/object" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" @@ -221,3 +222,25 @@ func (e *CacheEntry) GetClonedDir(ref string) (string, git.CheckoutInfo, error) } return p, repoInfo, nil } + +func (e *CacheEntry) GetGitTree(ref string) (*object.Tree, error) { + e.updateMutex.Lock() + defer e.updateMutex.Unlock() + + err := e.mr.Lock() + if err != nil { + return nil, err + } + defer e.mr.Unlock() + + if ref == "" { + ref = e.defaultRef + } + + _, commit, err := e.findCommit(ref) + if err != nil { + return nil, err + } + + return e.mr.GetGitTreeByCommit(commit) +} diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index eb09429e5..8579a1d7c 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -3,23 +3,25 @@ package kluctl_project import ( "fmt" securejoin "github.com/cyphar/filepath-securejoin" + "github.com/go-git/go-git/v5/plumbing/object" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" + "io" "os" "reflect" "regexp" "sort" "strings" - "sync" ) type dynamicTargetInfo struct { baseTarget *types.Target dir string + gitTree *object.Tree gitProject *types.GitProject ref *string refPattern *string @@ -42,11 +44,6 @@ func (c *LoadedKluctlProject) loadTargets() error { targetInfos = append(targetInfos, l...) } - err := c.cloneDynamicTargets(targetInfos) - if err != nil { - return err - } - for _, targetInfo := range targetInfos { target, err := c.buildDynamicTarget(targetInfo) if err != nil { @@ -186,14 +183,11 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Ta return nil, err } - cloneDir, _, err := ge.GetClonedDir(refShortName) - if err != nil { - return nil, err - } + gitTree, err := ge.GetGitTree(refShortName) dynamicTargets = append(dynamicTargets, &dynamicTargetInfo{ baseTarget: baseTarget, - dir: cloneDir, + gitTree: gitTree, gitProject: baseTarget.TargetConfig.Project, ref: &refShortName, refPattern: refPattern, @@ -228,47 +222,40 @@ func (c *LoadedKluctlProject) matchRef(s string, pattern string) (bool, string, } } -func (c *LoadedKluctlProject) cloneDynamicTargets(dynamicTargets []*dynamicTargetInfo) error { - uniqueClones := make(map[string]interface{}) - var mutex sync.Mutex - var wg sync.WaitGroup +func (c *LoadedKluctlProject) loadTargetConfigFileFromGit(targetInfo *dynamicTargetInfo) ([]byte, error) { + existsFunc := func(path string) bool { + e, err := targetInfo.gitTree.FindEntry(path) + if e == nil || err != nil { + return false + } + return true + } - for _, targetInfo_ := range dynamicTargets { - targetInfo := targetInfo_ + var configFile string - if targetInfo.gitProject == nil { - continue - } - mutex.Lock() - if _, ok := uniqueClones[targetInfo.dir]; ok { - mutex.Unlock() - continue + if targetInfo.baseTarget.TargetConfig.File != nil { + configFile = *targetInfo.baseTarget.TargetConfig.File + } else { + configFile = "target-config.yml" + if !existsFunc(configFile) { + configFile = "target-config.yaml" } - uniqueClones[targetInfo.dir] = nil - mutex.Unlock() - - wg.Add(1) - go func() { - defer wg.Done() - gitProject := *targetInfo.gitProject - gitProject.Ref = *targetInfo.ref - - gi, err := c.loadGitProject(&gitProject, "") - mutex.Lock() - defer mutex.Unlock() - if err != nil { - uniqueClones[targetInfo.dir] = err - } else { - uniqueClones[targetInfo.dir] = &gi - } - }() } - wg.Wait() - return nil + f, err := targetInfo.gitTree.File(configFile) + if err != nil { + return nil, fmt.Errorf("failed to load target config: %w", err) + } + r, err := f.Reader() + if err != nil { + return nil, fmt.Errorf("failed to load target config: %w", err) + } + defer r.Close() + + return io.ReadAll(r) } -func (c *LoadedKluctlProject) loadTargetConfigFile(targetInfo *dynamicTargetInfo) ([]byte, error) { +func (c *LoadedKluctlProject) loadTargetConfigFileFromLocal(targetInfo *dynamicTargetInfo) ([]byte, error) { configFile := yaml.FixNameExt(targetInfo.dir, "target-config.yml") if targetInfo.baseTarget.TargetConfig.File != nil { configFile = *targetInfo.baseTarget.TargetConfig.File @@ -284,6 +271,14 @@ func (c *LoadedKluctlProject) loadTargetConfigFile(targetInfo *dynamicTargetInfo return os.ReadFile(configPath) } +func (c *LoadedKluctlProject) loadTargetConfigFile(targetInfo *dynamicTargetInfo) ([]byte, error) { + if targetInfo.gitTree != nil { + return c.loadTargetConfigFileFromGit(targetInfo) + } else { + return c.loadTargetConfigFileFromLocal(targetInfo) + } +} + func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) (*types.Target, error) { var target types.Target err := utils.DeepCopy(&target, targetInfo.baseTarget) From 931274540d593a6dd4534f3cdbcdd21e859a7f3e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 25 Jul 2022 11:36:12 +0200 Subject: [PATCH 0304/2268] tests: Add loadTargetConfigFile tests --- pkg/kluctl_project/targets_test.go | 65 ++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 pkg/kluctl_project/targets_test.go diff --git a/pkg/kluctl_project/targets_test.go b/pkg/kluctl_project/targets_test.go new file mode 100644 index 000000000..852a5f8cd --- /dev/null +++ b/pkg/kluctl_project/targets_test.go @@ -0,0 +1,65 @@ +package kluctl_project + +import ( + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/stretchr/testify/assert" + "os" + "path/filepath" + "testing" +) + +func TestLoadTargetConfigFileFromLocal(t *testing.T) { + c := LoadedKluctlProject{} + ti := &dynamicTargetInfo{ + baseTarget: &types.Target{ + TargetConfig: &types.ExternalTargetConfig{}, + }, + dir: t.TempDir(), + } + err := os.WriteFile(filepath.Join(ti.dir, "target-config.yml"), []byte("test"), 0600) + assert.NoError(t, err) + + data, err := c.loadTargetConfigFile(ti) + assert.NoError(t, err) + + assert.Equal(t, []byte("test"), data) +} + +func TestLoadTargetConfigFileFromGit(t *testing.T) { + dir := t.TempDir() + r, err := git.PlainInit(dir, false) + assert.NoError(t, err) + + wt, err := r.Worktree() + assert.NoError(t, err) + + err = os.WriteFile(filepath.Join(dir, "target-config.yml"), []byte("test"), 0600) + assert.NoError(t, err) + + _, err = wt.Add("target-config.yml") + assert.NoError(t, err) + + h, err := wt.Commit("test", &git.CommitOptions{}) + assert.NoError(t, err) + + commit, err := object.GetCommit(r.Storer, h) + assert.NoError(t, err) + + gitTree, err := commit.Tree() + assert.NoError(t, err) + + c := LoadedKluctlProject{} + ti := &dynamicTargetInfo{ + baseTarget: &types.Target{ + TargetConfig: &types.ExternalTargetConfig{}, + }, + gitTree: gitTree, + } + + data, err := c.loadTargetConfigFile(ti) + assert.NoError(t, err) + + assert.Equal(t, []byte("test"), data) +} From ecdf1035cf6e981774f5d5929b0e1dfed0f8a1f3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 25 Jul 2022 14:03:38 +0200 Subject: [PATCH 0305/2268] tests: Add LooseVersion tests for 'v' prefixes --- pkg/utils/versions/looseversion_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/utils/versions/looseversion_test.go b/pkg/utils/versions/looseversion_test.go index 5d2bab86f..b25520f8d 100644 --- a/pkg/utils/versions/looseversion_test.go +++ b/pkg/utils/versions/looseversion_test.go @@ -79,3 +79,14 @@ func TestLooseVersionNoNums(t *testing.T) { checkLess(t, "-1", "-2") checkLess(t, "-1.1", "-1.2") } + +func TestLooseVersionVPrefix(t *testing.T) { + checkLess(t, "v1.0", "v1.1") + checkLess(t, "v2.0", "v2.1") + checkLess(t, "v1.0", "v2.0") + checkLess(t, "1.0", "v2.0") + checkLess(t, "v1.0suffix", "v1.0") + checkLess(t, "v1.0-suffix", "v1.0") + checkLess(t, "v1.0suffix1", "v1.0suffix2") + checkLess(t, "v1.0-suffix1", "v1.0-suffix2") +} From 552b22dfebfcd87758716e46d7a8ffbf8eace162 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 25 Jul 2022 14:03:59 +0200 Subject: [PATCH 0306/2268] fix: Also allow 'v' prefixes in semantic versions --- pkg/utils/versions/looseversion.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/versions/looseversion.go b/pkg/utils/versions/looseversion.go index 11101ea03..0c6bbf529 100644 --- a/pkg/utils/versions/looseversion.go +++ b/pkg/utils/versions/looseversion.go @@ -8,7 +8,7 @@ import ( "strings" ) -var looseSemverRegex = regexp.MustCompile(`^(([0-9]+)(\.[0-9]+)*)?(.*)$`) +var looseSemverRegex = regexp.MustCompile(`^v?(([0-9]+)(\.[0-9]+)*)?(.*)$`) var suffixComponentRegex = regexp.MustCompile(`\d+|[a-zA-Z]+|\.`) // Allows to compare ints and strings. Strings are always considered less than ints. From 342b8bc4da85a9d585bf76c3098285bd7671a2cd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 25 Jul 2022 14:09:56 +0200 Subject: [PATCH 0307/2268] tests: Fix TestLoadTargetConfigFileFromGit test Missed adding a commit author, which was not failing locally due to global git configs. --- pkg/kluctl_project/targets_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/kluctl_project/targets_test.go b/pkg/kluctl_project/targets_test.go index 852a5f8cd..628a69d4b 100644 --- a/pkg/kluctl_project/targets_test.go +++ b/pkg/kluctl_project/targets_test.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "testing" + "time" ) func TestLoadTargetConfigFileFromLocal(t *testing.T) { @@ -41,7 +42,7 @@ func TestLoadTargetConfigFileFromGit(t *testing.T) { _, err = wt.Add("target-config.yml") assert.NoError(t, err) - h, err := wt.Commit("test", &git.CommitOptions{}) + h, err := wt.Commit("test", &git.CommitOptions{Author: &object.Signature{Name: "test", Email: "test@test.com", When: time.Now()}}) assert.NoError(t, err) commit, err := object.GetCommit(r.Storer, h) From 0a424702abe2e7b526da8cbdd21f1c6e69ba6747 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 26 Jul 2022 14:26:27 +0200 Subject: [PATCH 0308/2268] fix: Don't rely on yaml marshalling when rendering structs This should speed up rendering of structs as there is no need to marshal+unmarshal structs anymore. --- pkg/jinja2/jinja2.go | 55 ----------- pkg/jinja2/jinja2_renderer.go | 16 ++- pkg/jinja2/render_struct.go | 181 ++++++++++++++++++++++++++++++++++ pkg/kluctl_project/targets.go | 24 ++--- pkg/vars/vars_loader.go | 7 +- 5 files changed, 209 insertions(+), 74 deletions(-) create mode 100644 pkg/jinja2/render_struct.go diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go index 1bc115aa7..2d433ec1a 100644 --- a/pkg/jinja2/jinja2.go +++ b/pkg/jinja2/jinja2.go @@ -123,61 +123,6 @@ func (j *Jinja2) RenderFile(template string, searchDirs []string, vars *uo.Unstr return *jobs[0].Result, nil } -func (j *Jinja2) RenderStruct(dst interface{}, src interface{}, vars *uo.UnstructuredObject) error { - m, err := uo.FromStruct(src) - if err != nil { - return err - } - - type pk struct { - parent interface{} - key interface{} - } - - var jobs []*RenderJob - var fields []pk - err = m.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { - value := it.Value() - if s, ok := value.(string); ok { - jobs = append(jobs, &RenderJob{Template: s}) - fields = append(fields, pk{ - parent: it.Parent(), - key: it.Key(), - }) - } - return nil - }) - if err != nil { - return err - } - - err = j.RenderStrings(jobs, nil, vars) - if err != nil { - return err - } - - var errors []error - for i, j := range jobs { - if j.Error != nil { - errors = append(errors, j.Error) - } else { - err = uo.SetChild(fields[i].parent, fields[i].key, *j.Result) - if err != nil { - return err - } - } - } - if len(errors) != 0 { - return utils.NewErrorListOrNil(errors) - } - - err = m.ToStruct(dst) - if err != nil { - return err - } - return nil -} - func (j *Jinja2) getGlob(pattern string) (glob.Glob, error) { j.mutex.Lock() defer j.mutex.Unlock() diff --git a/pkg/jinja2/jinja2_renderer.go b/pkg/jinja2/jinja2_renderer.go index f29ec2312..8895f2366 100644 --- a/pkg/jinja2/jinja2_renderer.go +++ b/pkg/jinja2/jinja2_renderer.go @@ -86,9 +86,17 @@ func (j *pythonJinja2Renderer) Close() { } } -func (j *pythonJinja2Renderer) isMaybeTemplate(template string, searchDirs []string, isString bool) (bool, *string) { +func isMaybeTemplateString(template string) bool { + return strings.IndexRune(template, '{') != -1 +} + +func isMaybeTemplateBytes(template []byte) bool { + return bytes.IndexRune(template, '{') != -1 +} + +func isMaybeTemplate(template string, searchDirs []string, isString bool) (bool, *string) { if isString { - if strings.IndexRune(template, '{') == -1 { + if !isMaybeTemplateString(template) { return false, &template } } else { @@ -97,7 +105,7 @@ func (j *pythonJinja2Renderer) isMaybeTemplate(template string, searchDirs []str if err != nil { continue } - if bytes.IndexRune(b, '{') == -1 { + if !isMaybeTemplateBytes(b) { x := string(b) return false, &x } else { @@ -140,7 +148,7 @@ func (j *pythonJinja2Renderer) renderHelper(jobs []*RenderJob, searchDirs []stri jargs.Strict = strict for _, job := range jobs { - if ist, r := j.isMaybeTemplate(job.Template, searchDirs, isString); !ist { + if ist, r := isMaybeTemplate(job.Template, searchDirs, isString); !ist { job.Result = r continue } diff --git a/pkg/jinja2/render_struct.go b/pkg/jinja2/render_struct.go new file mode 100644 index 000000000..e2f06996b --- /dev/null +++ b/pkg/jinja2/render_struct.go @@ -0,0 +1,181 @@ +package jinja2 + +import ( + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "reflect" +) + +type structStringCollectorEntry struct { + s string + setter func(s string) +} + +type structStringCollector struct { + j *Jinja2 + strings []structStringCollectorEntry +} + +func (w *structStringCollector) extractTemplateString(v reflect.Value) (string, bool) { + if v.IsZero() { + return "", false + } + + t := v.Type() + if t.Kind() == reflect.Interface || t.Kind() == reflect.Pointer { + v = v.Elem() + t = v.Type() + } + if t.Kind() == reflect.String { + s := v.String() + if !isMaybeTemplateString(s) { + return "", false + } + return v.String(), true + } + return "", false +} + +func (w *structStringCollector) addString(s string, setter func(s string)) { + w.strings = append(w.strings, structStringCollectorEntry{ + s: s, + setter: setter, + }) +} + +func (w *structStringCollector) walkStruct(v reflect.Value) error { + t := v.Type() + l := t.NumField() + + for i := 0; i < l; i++ { + f := v.Field(i) + if s, ok := w.extractTemplateString(f); ok { + w.addString(s, func(s string) { + if f.Type().Kind() == reflect.Pointer { + f.Set(reflect.ValueOf(&s)) + } else { + f.Set(reflect.ValueOf(s)) + } + }) + } else { + err := w.walkValue(f) + if err != nil { + return err + } + } + } + return nil +} + +func (w *structStringCollector) walkList(v reflect.Value) error { + l := v.Len() + for i := 0; i < l; i++ { + e := v.Index(i) + if s, ok := w.extractTemplateString(e); ok { + w.addString(s, func(s string) { + if e.Type().Kind() == reflect.Pointer { + e.Set(reflect.ValueOf(&s)) + } else { + e.Set(reflect.ValueOf(s)) + } + }) + } else { + err := w.walkValue(e) + if err != nil { + return err + } + } + } + return nil +} + +func (w *structStringCollector) walkMap(v reflect.Value) error { + it := v.MapRange() + for it.Next() { + ik := it.Key() + iv := it.Value() + + if s, ok := w.extractTemplateString(iv); ok { + if isMaybeTemplateString(s) { + w.addString(s, func(s string) { + if iv.Type().Kind() == reflect.Pointer { + v.SetMapIndex(ik, reflect.ValueOf(&s)) + } else { + v.SetMapIndex(ik, reflect.ValueOf(s)) + } + }) + } + } else { + err := w.walkValue(iv) + if err != nil { + return err + } + } + } + return nil +} + +func (w *structStringCollector) walkValue(v reflect.Value) error { + if v.IsZero() { + return nil + } + + v = reflect.Indirect(v) + t := v.Type() + + switch t.Kind() { + case reflect.Interface, reflect.Pointer: + return w.walkValue(v.Elem()) + case reflect.Slice, reflect.Array: + return w.walkList(v) + case reflect.Struct: + return w.walkStruct(v) + case reflect.Map: + return w.walkMap(v) + } + return nil +} + +func (j *Jinja2) RenderStruct(o interface{}, vars *uo.UnstructuredObject) (bool, bool, error) { + w := &structStringCollector{j: j} + v := reflect.ValueOf(o) + err := w.walkValue(v) + if err != nil { + return false, false, err + } + + var jobs []*RenderJob + + for _, sv := range w.strings { + jobs = append(jobs, &RenderJob{Template: sv.s}) + } + + err = j.RenderStrings(jobs, nil, vars) + if err != nil { + return false, false, err + } + + changed := false + moreTemplates := false + + var errors []error + for i, sv := range w.strings { + job := jobs[i] + if job.Error != nil { + errors = append(errors, job.Error) + } else { + if sv.s != *job.Result { + sv.setter(*job.Result) + changed = true + if isMaybeTemplateString(*job.Result) { + moreTemplates = true + } + } + } + } + if len(errors) != 0 { + return false, false, utils.NewErrorListOrNil(errors) + } + + return changed, moreTemplates, nil +} diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 8579a1d7c..18ac0153a 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -12,7 +12,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" "io" "os" - "reflect" "regexp" "sort" "strings" @@ -55,7 +54,7 @@ func (c *LoadedKluctlProject) loadTargets() error { continue } - target, err = c.renderTarget(target) + err = c.renderTarget(target) if err != nil { return err } @@ -76,17 +75,16 @@ func (c *LoadedKluctlProject) loadTargets() error { return nil } -func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, error) { +func (c *LoadedKluctlProject) renderTarget(target *types.Target) error { // Try rendering the target multiple times, until all values can be rendered successfully. This allows the target // to reference itself in complex ways. We'll also try loading the cluster vars in each iteration. var errors []error - curTarget := target for i := 0; i < 10; i++ { varsCtx := vars.NewVarsCtx(c.J2) - err := varsCtx.UpdateChildFromStruct("target", curTarget) + err := varsCtx.UpdateChildFromStruct("target", target) if err != nil { - return nil, err + return err } if target.Cluster != nil { @@ -94,22 +92,20 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) (*types.Target, if err == nil { err = varsCtx.UpdateChildFromStruct("cluster", cc.Cluster) if err != nil { - return nil, err + return err } } } - var newTarget types.Target - err = c.J2.RenderStruct(&newTarget, curTarget, varsCtx.Vars) - if err == nil && reflect.DeepEqual(curTarget, &newTarget) { - return curTarget, nil + changed, moreTemplates, err := c.J2.RenderStruct(target, varsCtx.Vars) + if err == nil && (!changed || !moreTemplates) { + return nil } - curTarget = &newTarget } if len(errors) != 0 { - return nil, errors[0] + return errors[0] } - return curTarget, nil + return nil } func (c *LoadedKluctlProject) prepareDynamicTargets(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 022bd895c..5afb09788 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -10,6 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars/aws" "github.com/kluctl/kluctl/v2/pkg/vars/vault" @@ -55,8 +56,12 @@ func (v *VarsLoader) LoadVarsList(varsCtx *VarsCtx, varsList []*types.VarsSource func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, searchDirs []string, rootKey string) error { var source types.VarsSource + err := utils.DeepCopy(&source, sourceIn) + if err != nil { + return err + } - err := varsCtx.J2.RenderStruct(&source, sourceIn, varsCtx.Vars) + _, _, err = varsCtx.J2.RenderStruct(&source, varsCtx.Vars) if err != nil { return err } From 2182b5a7d14ee299ea60ad8a8d0c68ea030ec230 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 26 Jul 2022 16:44:43 +0200 Subject: [PATCH 0309/2268] fix: Don't rely on official managed fields parser Instead, parse managed fields based on the already parsed UnstructuredObject. This should speed up parsing of managed fields. --- pkg/deployment/utils/delete_utils.go | 3 +- pkg/diff/fieldpath_utils.go | 54 ++++++++++++++++++++++++++++ pkg/diff/managed_fields.go | 21 ++++++++--- pkg/utils/uo/k8s_fields.go | 6 ++-- 4 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 pkg/diff/fieldpath_utils.go diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index b79f93d5f..dabdf569e 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -96,7 +96,8 @@ func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, // check if kluctl is managing this object found := false for _, mf := range managedFields { - if mf.Manager == "kluctl" { + mgr, _, _ := mf.GetNestedString("manager") + if mgr == "kluctl" { found = true break } diff --git a/pkg/diff/fieldpath_utils.go b/pkg/diff/fieldpath_utils.go new file mode 100644 index 000000000..c8a2a3b54 --- /dev/null +++ b/pkg/diff/fieldpath_utils.go @@ -0,0 +1,54 @@ +package diff + +import ( + "fmt" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" +) + +// based on sigs.k8s.io/structured-merge-diff/v4/fieldpath/serialize.go:readIterV1 +func convertManagedFields(m map[string]any) (children *fieldpath.Set, isMember bool, retErr error) { + for key, v := range m { + if key == "." { + isMember = true + continue + } + pe, err := fieldpath.DeserializePathElement(key) + if err == fieldpath.ErrUnknownPathElementType { + // Ignore these-- a future version maybe knows what + // they are. We drop these completely rather than try + // to preserve things we don't understand. + continue + } else if err != nil { + retErr = fmt.Errorf("parsing key as path element: %w", err) + return + } + m2, ok := v.(map[string]any) + if !ok { + retErr = fmt.Errorf("value is not a map") + return + } + grandchildren, childIsMember, err := convertManagedFields(m2) + if err != nil { + retErr = err + return + } + if childIsMember { + if children == nil { + children = &fieldpath.Set{} + } + + children.Members.Insert(pe) + } + if grandchildren != nil { + if children == nil { + children = &fieldpath.Set{} + } + *children.Children.Descend(pe) = *grandchildren + } + } + if children == nil { + isMember = true + } + + return children, isMember, nil +} diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index 0e89651fe..1a5a36673 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -1,7 +1,6 @@ package diff import ( - "bytes" "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -112,11 +111,25 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr managersByFields := make(map[string]*managersByField) for _, mf := range managedFields { - fieldSet := fieldpath.NewSet() - err := fieldSet.FromJSON(bytes.NewReader(mf.FieldsV1.Raw)) + fields, ok, err := mf.GetNestedObject("fieldsV1") if err != nil { return nil, nil, err } + if !ok { + continue + } + fieldSet, _, err := convertManagedFields(fields.Object) + if err != nil { + return nil, nil, err + } + + mgr, ok, err := mf.GetNestedString("manager") + if err != nil { + return nil, nil, err + } + if !ok { + return nil, nil, fmt.Errorf("manager field is missing") + } fieldSet.Iterate(func(path fieldpath.Path) { path = path.Copy() @@ -135,7 +148,7 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr if !found { m.pathes = append(m.pathes, path) } - m.managers = append(m.managers, mf.Manager) + m.managers = append(m.managers, mgr) }) } diff --git a/pkg/utils/uo/k8s_fields.go b/pkg/utils/uo/k8s_fields.go index 69c295757..2758945b1 100644 --- a/pkg/utils/uo/k8s_fields.go +++ b/pkg/utils/uo/k8s_fields.go @@ -3,7 +3,6 @@ package uo import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/types/k8s" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "reflect" "regexp" @@ -208,8 +207,9 @@ func (uo *UnstructuredObject) GetK8sOwnerReferences() []*UnstructuredObject { return ret } -func (uo *UnstructuredObject) GetK8sManagedFields() []metav1.ManagedFieldsEntry { - return uo.ToUnstructured().GetManagedFields() +func (uo *UnstructuredObject) GetK8sManagedFields() []*UnstructuredObject { + ret, _, _ := uo.GetNestedObjectList("metadata", "managedFields") + return ret } func (uo *UnstructuredObject) GetK8sCreationTime() time.Time { From fd4bd0af59d101e001604ffcf5f7f1a1823de134 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Jul 2022 14:07:23 +0200 Subject: [PATCH 0310/2268] fix: Fix empty status lines --- pkg/deployment/utils/apply_utils.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index a14b1528a..baca9e10f 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -521,6 +521,9 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { if a.warningCount != 0 { finalStatus += fmt.Sprintf(" Encountered %d warnings.", a.warningCount) } + if finalStatus == "" { + finalStatus = "Nothing to apply." + } a.sctx.Update(strings.TrimSpace(finalStatus)) @@ -573,6 +576,7 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { sctx = status.StartWithOptions(a.ctx, status.WithTotal(-1), status.WithPrefix(*progressName), + status.WithStatus("Initializing"), ) } a2 := a.NewApplyUtil(a.ctx, sctx) From 89b2d3b080b0275372efc8d4a1142acdc9bfe022 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Jul 2022 14:15:56 +0200 Subject: [PATCH 0311/2268] fix: Fix validate command to properly report readiness --- pkg/deployment/commands/validate.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index d9a63720d..0647a3b51 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -27,6 +27,7 @@ func NewValidateCommand(ctx context.Context, c *deployment.DeploymentCollection) func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.ValidateResult, error) { var result types.ValidateResult + result.Ready = true cmd.dew.Init() @@ -56,6 +57,9 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types. continue } r := validation.ValidateObject(k, remoteObject, true) + if !r.Ready { + result.Ready = false + } result.Errors = append(result.Errors, r.Errors...) result.Warnings = append(result.Warnings, r.Warnings...) result.Results = append(result.Results, r.Results...) From 55530775ede773e6a3a23d15f110d43e7598345c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Jul 2022 14:36:31 +0200 Subject: [PATCH 0312/2268] ci: Upgrade macos images in Github actions --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e0aaa6f29..759044bff 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -139,11 +139,11 @@ jobs: include: - os: ubuntu-20.04 binary-suffix: linux-amd64 - - os: macos-10.15 + - os: macos-11 binary-suffix: darwin-amd64 - os: windows-2019 binary-suffix: windows-amd64 - os: [ubuntu-20.04, macos-10.15, windows-2019] + os: [ubuntu-20.04, macos-11, windows-2019] fail-fast: false needs: - build From 3d87d53383edf7bb344d150e3a340b7fa5614fd6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Jul 2022 13:36:43 +0200 Subject: [PATCH 0313/2268] fix: Properly differentiate between "insecure" and "tlsNoVerify" registries "insecure" means http instead of https. TLS verification is independent from this. This commit properly implements both. --- pkg/registries/registries.go | 89 ++++++++++++++++++++++++++---------- pkg/utils/env.go | 11 +++++ 2 files changed, 77 insertions(+), 23 deletions(-) diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 9f2171d83..24f169ccb 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -54,12 +54,13 @@ type RegistryHelper struct { } type AuthEntry struct { - Registry string - Username string - Password string - Auth string - CABundle []byte - Insecure bool + Registry string + Username string + Password string + Auth string + CABundle []byte + Insecure bool + SkipTlsVerify bool } func NewRegistryHelper(ctx context.Context) *RegistryHelper { @@ -86,8 +87,7 @@ func (rh *RegistryHelper) ListImageTags(image string) ([]string, error) { remote.WithContext(context.WithValue(rh.ctx, transportKey, t)), } - e := rh.findAuthEntry(repo.RegistryStr()) - if e != nil && e.Insecure { + if rh.isInsecureRegistry(repo.RegistryStr()) { nameOpts = append(nameOpts, name.Insecure) repo, err = name.NewRepository(image, nameOpts...) } @@ -110,23 +110,63 @@ func (rh *RegistryHelper) AddAuthEntry(e AuthEntry) { rh.authEntries = append(rh.authEntries, e) } -func (rh *RegistryHelper) ParseAuthEntriesFromEnv() error { - defaultTlsVerify := true - if x, ok := os.LookupEnv("KLUCTL_REGISTRY_DEFAULT_TLSVERIFY"); ok { - b, err := strconv.ParseBool(x) - if err != nil { - return fmt.Errorf("failed to parse KLUCTL_REGISTRY_DEFAULT_TLSVERIFY: %w", err) - } - defaultTlsVerify = b +func (rh *RegistryHelper) isDefaultInsecure() bool { + defaultInsecure, err := utils.ParseEnvBool("KLUCTL_REGISTRY_DEFAULT_INSECURE", false) + if err != nil { + status.Warning(rh.ctx, "Failed to parse KLUCTL_REGISTRY_DEFAULT_INSECURE: %w", err) + return false + } + return defaultInsecure +} + +func (rh *RegistryHelper) isDefaultSkipTlsVerify() bool { + defaultTlsVerify, err := utils.ParseEnvBool("KLUCTL_REGISTRY_DEFAULT_TLSVERIFY", true) + if err != nil { + status.Warning(rh.ctx, "Failed to parse KLUCTL_REGISTRY_DEFAULT_TLSVERIFY: %w", err) + return false } + return !defaultTlsVerify +} + +func (rh *RegistryHelper) isInsecureRegistry(registry string) bool { + e := rh.findAuthEntry(registry) + if e == nil { + return rh.isDefaultInsecure() + } + return e.Insecure +} + +func (rh *RegistryHelper) isSkipTlsVerify(registry string) bool { + e := rh.findAuthEntry(registry) + if e == nil { + return rh.isDefaultSkipTlsVerify() + } + return e.SkipTlsVerify +} + +func (rh *RegistryHelper) ParseAuthEntriesFromEnv() error { + defaultInsecure := rh.isDefaultInsecure() + defaultSkipTlsVerify := rh.isDefaultSkipTlsVerify() for _, m := range utils.ParseEnvConfigSets("KLUCTL_REGISTRY") { e := AuthEntry{ - Registry: m["HOST"], - Username: m["USERNAME"], - Password: m["PASSWORD"], - Auth: m["AUTH"], - Insecure: !defaultTlsVerify, + Registry: m["HOST"], + Username: m["USERNAME"], + Password: m["PASSWORD"], + Auth: m["AUTH"], + Insecure: defaultInsecure, + SkipTlsVerify: defaultSkipTlsVerify, + } + if e.Registry == "" { + continue + } + insecureStr, ok := m["INSECURE"] + if ok { + insecure, err := strconv.ParseBool(insecureStr) + if err != nil { + return fmt.Errorf("failed to parse INSECURE from env: %w", err) + } + e.Insecure = insecure } tlsverifyStr, ok := m["TLSVERIFY"] if ok { @@ -134,7 +174,7 @@ func (rh *RegistryHelper) ParseAuthEntriesFromEnv() error { if err != nil { return fmt.Errorf("failed to parse TLSVERIFY from env: %w", err) } - e.Insecure = !tlsverify + e.SkipTlsVerify = !tlsverify } ca_bundle_path := m["CA_BUNDLE"] @@ -178,16 +218,19 @@ func (rh *RegistryHelper) loadCA(registry string) (*x509.CertPool, error) { func (rh *RegistryHelper) buildTransport(registry string) (http.RoundTripper, error) { ret, err := rh.cachedTransports.Get(registry, func() (interface{}, error) { + skipTls := rh.isSkipTlsVerify(registry) + ca, err := rh.loadCA(registry) if err != nil { return nil, err } - if ca == nil { + if ca == nil && !skipTls { return remote.DefaultTransport, nil } t := remote.DefaultTransport.Clone() t.TLSClientConfig.RootCAs = ca + t.TLSClientConfig.InsecureSkipVerify = skipTls return t, nil }) if err != nil { diff --git a/pkg/utils/env.go b/pkg/utils/env.go index 5dbca1e69..66ae280f9 100644 --- a/pkg/utils/env.go +++ b/pkg/utils/env.go @@ -8,6 +8,17 @@ import ( "strings" ) +func ParseEnvBool(name string, def bool) (bool, error) { + if x, ok := os.LookupEnv(name); ok { + b, err := strconv.ParseBool(x) + if err != nil { + return def, err + } + return b, nil + } + return def, nil +} + func ParseEnvConfigSets(prefix string) map[int]map[string]string { ret := make(map[int]map[string]string) From ca65ba160f3d01ce62bc12f8e3e5880a7a5ce054 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Jul 2022 13:37:17 +0200 Subject: [PATCH 0314/2268] fix: Don't use empty credentials when env vars are only used to modify TLSVERIFY --- pkg/registries/registries.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 24f169ccb..d6c857aa5 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -247,11 +247,15 @@ func (rh *RegistryHelper) getTransport(req *http.Request) http.RoundTripper { func (rh *RegistryHelper) doResolve(resource authn.Resource) (authn.Authenticator, error) { e := rh.findAuthEntry(resource.RegistryStr()) if e != nil { - return authn.FromConfig(authn.AuthConfig{ - Username: e.Username, - Password: e.Password, - Auth: e.Auth, - }), nil + // only use credentials from entry if present, otherwise fallback to default keychain + // (which will also check ~/.docker/config.json) + if e.Username != "" || e.Auth != "" { + return authn.FromConfig(authn.AuthConfig{ + Username: e.Username, + Password: e.Password, + Auth: e.Auth, + }), nil + } } return authn.DefaultKeychain.Resolve(resource) From e7ec72b8d0891010386c9f09c99cd8bdf130fb59 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 29 Jul 2022 10:16:41 +0200 Subject: [PATCH 0315/2268] fix: Don's use %w when not supported --- pkg/registries/registries.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index d6c857aa5..f51f99913 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -113,7 +113,7 @@ func (rh *RegistryHelper) AddAuthEntry(e AuthEntry) { func (rh *RegistryHelper) isDefaultInsecure() bool { defaultInsecure, err := utils.ParseEnvBool("KLUCTL_REGISTRY_DEFAULT_INSECURE", false) if err != nil { - status.Warning(rh.ctx, "Failed to parse KLUCTL_REGISTRY_DEFAULT_INSECURE: %w", err) + status.Warning(rh.ctx, "Failed to parse KLUCTL_REGISTRY_DEFAULT_INSECURE: %s", err) return false } return defaultInsecure @@ -122,7 +122,7 @@ func (rh *RegistryHelper) isDefaultInsecure() bool { func (rh *RegistryHelper) isDefaultSkipTlsVerify() bool { defaultTlsVerify, err := utils.ParseEnvBool("KLUCTL_REGISTRY_DEFAULT_TLSVERIFY", true) if err != nil { - status.Warning(rh.ctx, "Failed to parse KLUCTL_REGISTRY_DEFAULT_TLSVERIFY: %w", err) + status.Warning(rh.ctx, "Failed to parse KLUCTL_REGISTRY_DEFAULT_TLSVERIFY: %s", err) return false } return !defaultTlsVerify From b7827f423bfc552eb3023c85c540185803829201 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 27 Jul 2022 13:37:40 +0200 Subject: [PATCH 0316/2268] refactor: Allow to access ClientConfig and public keys via AuthMethodAndCA --- pkg/git/auth/auth_provider.go | 4 ++++ pkg/git/auth/list_auth_provider.go | 5 +++++ pkg/git/auth/ssh_auth_provider.go | 9 +++++++++ 3 files changed, 18 insertions(+) diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 2a3f91757..314d2536a 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -4,11 +4,15 @@ import ( "context" "github.com/go-git/go-git/v5/plumbing/transport" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "golang.org/x/crypto/ssh" ) type AuthMethodAndCA struct { AuthMethod transport.AuthMethod CABundle []byte + + PublicKeys func() []ssh.PublicKey + ClientConfig func() (*ssh.ClientConfig, error) } type GitAuthProvider interface { diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index 6ea7ba938..6e92f345e 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -6,6 +6,7 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport/ssh" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/status" + ssh2 "golang.org/x/crypto/ssh" "strings" ) @@ -80,6 +81,10 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) a.HostKeyCallback = buildVerifyHostCallback(ctx, e.KnownHosts) return AuthMethodAndCA{ AuthMethod: a, + PublicKeys: func() []ssh2.PublicKey { + return []ssh2.PublicKey{a.Signer.PublicKey()} + }, + ClientConfig: a.ClientConfig, } } } else { diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index 0ec025aa1..d2de0669f 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -121,6 +121,15 @@ func (a *GitSshAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr return AuthMethodAndCA{ AuthMethod: auth, + PublicKeys: func() []ssh.PublicKey { + signers, _ := auth.Signers() + var ret []ssh.PublicKey + for _, s := range signers { + ret = append(ret, s.PublicKey()) + } + return ret + }, + ClientConfig: auth.ClientConfig, } } From 804cbb030ac5998b65a2dbec8e76729beb09b91e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 29 Jul 2022 12:25:14 +0200 Subject: [PATCH 0317/2268] feat: Introduce SSH connection pooling for listing of refs This speeds up updating of git repos from SSH when the git repos have no updates. For now, pooling is only used when listing remote refs, which gives enough info to be able to decide if a full fetch is required. --- cmd/kluctl/commands/utils.go | 5 +- pkg/git/list_refs.go | 99 ++++++++++++++++++++ pkg/git/mirrored_repo.go | 42 +++++---- pkg/git/repocache/cache.go | 9 +- pkg/git/ssh-pool/hostport.go | 45 +++++++++ pkg/git/ssh-pool/ssh_pool.go | 177 +++++++++++++++++++++++++++++++++++ 6 files changed, 354 insertions(+), 23 deletions(-) create mode 100644 pkg/git/list_refs.go create mode 100644 pkg/git/ssh-pool/hostport.go create mode 100644 pkg/git/ssh-pool/ssh_pool.go diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 8459ae908..3ab20e9be 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -9,6 +9,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/repocache" + ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/registries" @@ -59,7 +60,9 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b ctx, cancel := context.WithTimeout(cliCtx, projectFlags.Timeout) defer cancel() - rp := repocache.NewGitRepoCache(ctx, auth.NewDefaultAuthProviders(), projectFlags.GitCacheUpdateInterval) + sshPool := &ssh_pool.SshPool{} + + rp := repocache.NewGitRepoCache(ctx, sshPool, auth.NewDefaultAuthProviders(), projectFlags.GitCacheUpdateInterval) defer rp.Clear() loadArgs := kluctl_project.LoadKluctlProjectArgs{ diff --git a/pkg/git/list_refs.go b/pkg/git/list_refs.go new file mode 100644 index 000000000..2bc517fd6 --- /dev/null +++ b/pkg/git/list_refs.go @@ -0,0 +1,99 @@ +package git + +import ( + "bytes" + "fmt" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/protocol/packp" + auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" + "github.com/kluctl/kluctl/v2/pkg/status" + "strconv" +) + +// listRemoteRefsFastSsh will reuse existing ssh connections from a pool +func (g *MirroredGitRepo) listRemoteRefsFastSsh(r *git.Repository, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { + var portInt int64 = -1 + if g.url.Port() != "" { + var err error + portInt, err = strconv.ParseInt(g.url.Port(), 10, 32) + if err != nil { + return nil, err + } + } + + s, err := g.sshPool.GetSession(g.ctx, g.url.Hostname(), int(portInt), auth) + if err != nil { + return nil, err + } + defer s.Close() + + cmd := fmt.Sprintf("git-upload-pack %s", g.url.Path) + + stdout := bytes.NewBuffer(nil) + stderr := bytes.NewBuffer(nil) + stdin := bytes.NewBuffer([]byte("0000\n")) + + s.Session.Stdout = stdout + s.Session.Stderr = stderr + s.Session.Stdin = stdin + + err = s.Session.Run(cmd) + if err != nil { + return nil, fmt.Errorf("git-upload-pack failed: %w\nstderr=%s", err, stderr.String()) + } + + ar := packp.NewAdvRefs() + err = ar.Decode(stdout) + if err != nil { + return nil, err + } + + allRefs, err := ar.AllReferences() + if err != nil { + return nil, err + } + + refs, err := allRefs.IterReferences() + if err != nil { + return nil, err + } + + var resultRefs []*plumbing.Reference + err = refs.ForEach(func(ref *plumbing.Reference) error { + resultRefs = append(resultRefs, ref) + return nil + }) + if err != nil { + return nil, err + } + + return resultRefs, nil +} + +func (g *MirroredGitRepo) listRemoteRefsSlow(r *git.Repository, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { + remote, err := r.Remote("origin") + if err != nil { + return nil, err + } + + remoteRefs, err := remote.ListContext(g.ctx, &git.ListOptions{ + Auth: auth.AuthMethod, + CABundle: auth.CABundle, + }) + if err != nil { + return nil, err + } + return remoteRefs, nil +} + +func (g *MirroredGitRepo) listRemoteRefs(r *git.Repository, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { + if g.url.IsSsh() { + refs, err := g.listRemoteRefsFastSsh(r, auth) + if err == nil { + return refs, nil + } + status.Warning(g.ctx, "Fast listing of remote refs failed: %s", err.Error()) + } + return g.listRemoteRefsSlow(r, auth) +} diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 221113f6f..49e571530 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -9,6 +9,8 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + _ "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" + ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/rogpeppe/go-internal/lockedfile" @@ -25,6 +27,9 @@ var cacheBaseDir = filepath.Join(utils.GetTmpBaseDir(), "git-cache") type MirroredGitRepo struct { ctx context.Context + sshPool *ssh_pool.SshPool + authProviders *auth2.GitAuthProviders + url git_url.GitUrl mirrorDir string @@ -36,12 +41,14 @@ type MirroredGitRepo struct { mutex sync.Mutex } -func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl) (*MirroredGitRepo, error) { +func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl, sshPool *ssh_pool.SshPool, authProviders *auth2.GitAuthProviders) (*MirroredGitRepo, error) { mirrorRepoName := buildMirrorRepoName(u) o := &MirroredGitRepo{ - ctx: ctx, - url: u, - mirrorDir: filepath.Join(cacheBaseDir, mirrorRepoName), + ctx: ctx, + sshPool: sshPool, + authProviders: authProviders, + url: u, + mirrorDir: filepath.Join(cacheBaseDir, mirrorRepoName), } if !utils.IsDirectory(o.mirrorDir) { @@ -185,26 +192,19 @@ func (g *MirroredGitRepo) cleanupMirrorDir() error { return nil } -func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string, authProviders *auth2.GitAuthProviders) error { +func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string) error { r, err := git.PlainOpen(repoDir) if err != nil { return err } - auth := authProviders.BuildAuth(g.ctx, g.url) + auth := g.authProviders.BuildAuth(g.ctx, g.url) - remote, err := r.Remote("origin") + remoteRefs, err := g.listRemoteRefs(r, auth) if err != nil { return err } - remoteRefs, err := remote.ListContext(g.ctx, &git.ListOptions{ - Auth: auth.AuthMethod, - CABundle: auth.CABundle, - }) - if err != nil { - return err - } remoteRefsMap := make(map[plumbing.ReferenceName]*plumbing.Reference) for _, reference := range remoteRefs { remoteRefsMap[reference.Name()] = reference @@ -241,6 +241,10 @@ func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string, authPr } if changed { + remote, err := r.Remote("origin") + if err != nil { + return err + } err = remote.FetchContext(g.ctx, &git.FetchOptions{ Auth: auth.AuthMethod, CABundle: auth.CABundle, @@ -276,10 +280,10 @@ func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string, authPr return nil } -func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext, authProviders *auth2.GitAuthProviders) error { +func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext) error { initMarker := filepath.Join(g.mirrorDir, ".cache2.init") if utils.IsFile(initMarker) { - return g.update(s, g.mirrorDir, authProviders) + return g.update(s, g.mirrorDir) } err := g.cleanupMirrorDir() if err != nil { @@ -309,7 +313,7 @@ func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext, authProviders * return err } - err = g.update(s, tmpMirrorDir, authProviders) + err = g.update(s, tmpMirrorDir) if err != nil { return err } @@ -331,9 +335,9 @@ func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext, authProviders * return nil } -func (g *MirroredGitRepo) Update(authProviders *auth2.GitAuthProviders) error { +func (g *MirroredGitRepo) Update() error { s := status.Start(g.ctx, "Updating git cache for %s", g.url.String()) - err := g.cloneOrUpdate(s, authProviders) + err := g.cloneOrUpdate(s) if err != nil { s.FailedWithMessage(err.Error()) return err diff --git a/pkg/git/repocache/cache.go b/pkg/git/repocache/cache.go index 3d34e2ec8..76b4aa813 100644 --- a/pkg/git/repocache/cache.go +++ b/pkg/git/repocache/cache.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/utils" "io/ioutil" "os" @@ -20,6 +21,7 @@ import ( type GitRepoCache struct { ctx context.Context authProviders *auth.GitAuthProviders + sshPool *ssh_pool.SshPool updateInterval time.Duration repos map[string]*CacheEntry reposMutex sync.Mutex @@ -49,9 +51,10 @@ type clonedDir struct { info git.CheckoutInfo } -func NewGitRepoCache(ctx context.Context, authProviders *auth.GitAuthProviders, updateInterval time.Duration) *GitRepoCache { +func NewGitRepoCache(ctx context.Context, sshPool *ssh_pool.SshPool, authProviders *auth.GitAuthProviders, updateInterval time.Duration) *GitRepoCache { return &GitRepoCache{ ctx: ctx, + sshPool: sshPool, authProviders: authProviders, updateInterval: updateInterval, repos: map[string]*CacheEntry{}, @@ -74,7 +77,7 @@ func (rp *GitRepoCache) GetEntry(url git_url.GitUrl) (*CacheEntry, error) { e, ok := rp.repos[url.NormalizedRepoKey()] if !ok { - mr, err := git.NewMirroredGitRepo(rp.ctx, url) + mr, err := git.NewMirroredGitRepo(rp.ctx, url, rp.sshPool, rp.authProviders) if err != nil { return nil, err } @@ -106,7 +109,7 @@ func (e *CacheEntry) Update() error { if time.Now().Sub(e.mr.LastUpdateTime()) <= e.rp.updateInterval { e.mr.SetUpdated(true) } else { - err := e.mr.Update(e.rp.authProviders) + err := e.mr.Update() if err != nil { return err } diff --git a/pkg/git/ssh-pool/hostport.go b/pkg/git/ssh-pool/hostport.go new file mode 100644 index 000000000..4ca5c5cc7 --- /dev/null +++ b/pkg/git/ssh-pool/hostport.go @@ -0,0 +1,45 @@ +package ssh_pool + +import ( + "fmt" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "strconv" +) + +func getHostWithPort(host string, port int) string { + if addr, found := doGetHostWithPortFromSSHConfig(host, port); found { + return addr + } + + if port <= 0 { + port = ssh.DefaultPort + } + + return fmt.Sprintf("%s:%d", host, port) +} + +func doGetHostWithPortFromSSHConfig(host string, port int) (addr string, found bool) { + if ssh.DefaultSSHConfig == nil { + return + } + + configHost := ssh.DefaultSSHConfig.Get(host, "Hostname") + if configHost != "" { + host = configHost + found = true + } + + if !found { + return + } + + configPort := ssh.DefaultSSHConfig.Get(host, "Port") + if configPort != "" { + if i, err := strconv.Atoi(configPort); err == nil { + port = i + } + } + + addr = fmt.Sprintf("%s:%d", host, port) + return +} diff --git a/pkg/git/ssh-pool/ssh_pool.go b/pkg/git/ssh-pool/ssh_pool.go new file mode 100644 index 000000000..ccc6d46cd --- /dev/null +++ b/pkg/git/ssh-pool/ssh_pool.go @@ -0,0 +1,177 @@ +package ssh_pool + +import ( + "context" + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/git/auth" + "github.com/kluctl/kluctl/v2/pkg/status" + "golang.org/x/crypto/ssh" + "golang.org/x/net/proxy" + "sync" + "time" +) + +const maxAge = time.Minute * 1 + +type SshPool struct { + pool sync.Map + m sync.Mutex +} + +type poolEntry struct { + hash string + pc *poolClient + m sync.Mutex +} + +type poolClient struct { + time time.Time + sessionCount int + client *ssh.Client +} + +type PooledSession struct { + Session *ssh.Session + pe *poolEntry + pc *poolClient + hash string +} + +func (p *SshPool) GetSession(ctx context.Context, host string, port int, auth auth.AuthMethodAndCA) (*PooledSession, error) { + addr := getHostWithPort(host, port) + + h, err := p.buildHash(addr, auth) + if err != nil { + return nil, err + } + + pe1, _ := p.pool.LoadOrStore(h, &poolEntry{hash: h}) + pe, _ := pe1.(*poolEntry) + + pe.m.Lock() + defer pe.m.Unlock() + + if pe.pc != nil { + if time.Now().Sub(pe.pc.time) > maxAge { + if pe.pc.sessionCount == 0 { + _ = pe.pc.client.Close() + } + pe.pc = nil + } + } + + isNew := false + if pe.pc == nil { + isNew = true + client, err := p.newClient(ctx, addr, auth) + if err != nil { + p.pool.Delete(h) + return nil, err + } + pe.pc = &poolClient{ + time: time.Now(), + client: client, + } + } + + s, err := pe.pc.client.NewSession() + if err != nil { + origErr := err + _ = pe.pc.client.Close() + pe.pc = nil + if isNew { + // don't retry with a fresh connection + p.pool.Delete(h) + return nil, err + } else { + // retry with a fresh connection + client, err := p.newClient(ctx, addr, auth) + if err != nil { + p.pool.Delete(h) + return nil, err + } + status.Trace(ctx, "Successfully retries failed ssh connection. Old error: %s", origErr) + pe.pc = &poolClient{ + time: time.Now(), + client: client, + } + s, err = pe.pc.client.NewSession() + if err != nil { + // something is completely wrong here, forget about the client + p.pool.Delete(h) + return nil, err + } + } + } + + pe.pc.sessionCount += 1 + + ps := &PooledSession{ + hash: h, + pe: pe, + pc: pe.pc, + Session: s, + } + return ps, nil +} + +func (ps *PooledSession) Close() error { + err := ps.Session.Close() + + ps.pe.m.Lock() + defer ps.pe.m.Unlock() + + ps.pc.sessionCount-- + + if ps.pe.pc != ps.pc { + _ = ps.pc.client.Close() + } else if ps.pc.sessionCount == 0 && time.Now().Sub(ps.pc.time) > maxAge { + _ = ps.pc.client.Close() + ps.pe.pc = nil + } + + return err +} + +func (p *SshPool) newClient(ctx context.Context, addr string, auth auth.AuthMethodAndCA) (*ssh.Client, error) { + conn, err := proxy.Dial(ctx, "tcp", addr) + if err != nil { + return nil, err + } + + config, err := auth.ClientConfig() + if err != nil { + return nil, err + } + + c, chans, reqs, err := ssh.NewClientConn(conn, addr, config) + if err != nil { + return nil, err + } + return ssh.NewClient(c, chans, reqs), nil +} + +func (p *SshPool) buildHash(addr string, auth auth.AuthMethodAndCA) (string, error) { + if auth.PublicKeys == nil { + // this should not happen + return "", fmt.Errorf("auth has no PublicKeys") + } + + config, err := auth.ClientConfig() + if err != nil { + return "", err + } + + h := sha256.New() + _ = binary.Write(h, binary.LittleEndian, addr) + _ = binary.Write(h, binary.LittleEndian, config.User) + + for _, pk := range auth.PublicKeys() { + _ = binary.Write(h, binary.LittleEndian, pk.Marshal()) + } + + return hex.EncodeToString(h.Sum(nil)), nil +} From 3a075cfb0473ea001b302c21feb367e713307c03 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 29 Jul 2022 13:31:17 +0200 Subject: [PATCH 0318/2268] tests: Fix tests compilation --- pkg/git/ssh-pool/ssh_pool.go | 1 - pkg/vars/vars_loader_test.go | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/git/ssh-pool/ssh_pool.go b/pkg/git/ssh-pool/ssh_pool.go index ccc6d46cd..bbc84aa7c 100644 --- a/pkg/git/ssh-pool/ssh_pool.go +++ b/pkg/git/ssh-pool/ssh_pool.go @@ -18,7 +18,6 @@ const maxAge = time.Minute * 1 type SshPool struct { pool sync.Map - m sync.Mutex } type poolEntry struct { diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index ad7ee5be0..6cac07687 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -8,6 +8,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/repocache" + ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -38,7 +39,7 @@ func newTestDir(t *testing.T) string { } func newRP(t *testing.T) *repocache.GitRepoCache { - grc := repocache.NewGitRepoCache(context.TODO(), auth.NewDefaultAuthProviders(), 0) + grc := repocache.NewGitRepoCache(context.TODO(), &ssh_pool.SshPool{}, auth.NewDefaultAuthProviders(), 0) t.Cleanup(func() { grc.Clear() }) From 1ba1df71d0715a550e1aebf6e00d4b6bfa8943fa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 1 Aug 2022 09:14:27 +0200 Subject: [PATCH 0319/2268] fix: Don't try to decrypt passphrase protected keys when calculating hash Instead, use the encrypted key hash to calculate the auth entry hash. This prevents passphrase prompts for keys that are already unlocked via the ssh agent. --- pkg/git/auth/auth_provider.go | 2 +- pkg/git/auth/hash.go | 34 ++++++++++++++++++++++++++++++ pkg/git/auth/list_auth_provider.go | 5 ++--- pkg/git/auth/ssh_auth_provider.go | 26 +++++++++++++++++------ pkg/git/mirrored_repo.go | 2 +- pkg/git/ssh-pool/ssh_pool.go | 10 +++++---- 6 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 pkg/git/auth/hash.go diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 314d2536a..0eca67a60 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -11,7 +11,7 @@ type AuthMethodAndCA struct { AuthMethod transport.AuthMethod CABundle []byte - PublicKeys func() []ssh.PublicKey + Hash func() ([]byte, error) ClientConfig func() (*ssh.ClientConfig, error) } diff --git a/pkg/git/auth/hash.go b/pkg/git/auth/hash.go new file mode 100644 index 000000000..5c5ffdf82 --- /dev/null +++ b/pkg/git/auth/hash.go @@ -0,0 +1,34 @@ +package auth + +import ( + "crypto/sha256" + "encoding/binary" + "golang.org/x/crypto/ssh" +) + +type hashProvider interface { + Hash() ([]byte, error) +} + +func buildHash(s ssh.Signer) ([]byte, error) { + if hp, ok := s.(hashProvider); ok { + return hp.Hash() + } + h := sha256.New() + _ = binary.Write(h, binary.LittleEndian, "pk") + _ = binary.Write(h, binary.LittleEndian, s.PublicKey().Marshal()) + return h.Sum(nil), nil +} + +func buildHashForList(signers []ssh.Signer) ([]byte, error) { + h := sha256.New() + _ = binary.Write(h, binary.LittleEndian, "signers") + for _, s := range signers { + h2, err := buildHash(s) + if err != nil { + return nil, err + } + _ = binary.Write(h, binary.LittleEndian, h2) + } + return h.Sum(nil), nil +} diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index 6e92f345e..532734c1d 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -6,7 +6,6 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport/ssh" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/status" - ssh2 "golang.org/x/crypto/ssh" "strings" ) @@ -81,8 +80,8 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) a.HostKeyCallback = buildVerifyHostCallback(ctx, e.KnownHosts) return AuthMethodAndCA{ AuthMethod: a, - PublicKeys: func() []ssh2.PublicKey { - return []ssh2.PublicKey{a.Signer.PublicKey()} + Hash: func() ([]byte, error) { + return buildHash(a.Signer) }, ClientConfig: a.ClientConfig, } diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index d2de0669f..9c347192b 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -2,6 +2,8 @@ package auth import ( "context" + "crypto/sha256" + "encoding/binary" "fmt" "github.com/kevinburke/ssh_config" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" @@ -121,13 +123,12 @@ func (a *GitSshAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr return AuthMethodAndCA{ AuthMethod: auth, - PublicKeys: func() []ssh.PublicKey { - signers, _ := auth.Signers() - var ret []ssh.PublicKey - for _, s := range signers { - ret = append(ret, s.PublicKey()) + Hash: func() ([]byte, error) { + signers, err := auth.Signers() + if err != nil { + return nil, err } - return ret + return buildHashForList(signers) }, ClientConfig: auth.ClientConfig, } @@ -211,6 +212,19 @@ func (k *dummyPublicKey) Verify(data []byte, sig *ssh.Signature) error { return fmt.Errorf("this is a dummy key") } +func (k *deferredPassphraseKey) Hash() ([]byte, error) { + pemBytes, err := ioutil.ReadFile(k.path) + if err != nil { + return nil, err + } + + h := sha256.New() + _ = binary.Write(h, binary.LittleEndian, "dpk") + _ = binary.Write(h, binary.LittleEndian, pemBytes) + + return h.Sum(nil), nil +} + func (k *deferredPassphraseKey) PublicKey() ssh.PublicKey { k.mutex.Lock() defer k.mutex.Unlock() diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 49e571530..3c3a271c1 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -222,7 +222,7 @@ func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string) error }) var toDelete []*plumbing.Reference - changed := false + changed := true for name, ref := range remoteRefsMap { if name.String() != "HEAD" && !strings.HasPrefix(name.String(), "refs/heads/") && !strings.HasPrefix(name.String(), "refs/tags/") { // we only fetch branches and tags diff --git a/pkg/git/ssh-pool/ssh_pool.go b/pkg/git/ssh-pool/ssh_pool.go index bbc84aa7c..fb60c220c 100644 --- a/pkg/git/ssh-pool/ssh_pool.go +++ b/pkg/git/ssh-pool/ssh_pool.go @@ -154,9 +154,9 @@ func (p *SshPool) newClient(ctx context.Context, addr string, auth auth.AuthMeth } func (p *SshPool) buildHash(addr string, auth auth.AuthMethodAndCA) (string, error) { - if auth.PublicKeys == nil { + if auth.Hash == nil { // this should not happen - return "", fmt.Errorf("auth has no PublicKeys") + return "", fmt.Errorf("auth has no Hash") } config, err := auth.ClientConfig() @@ -168,9 +168,11 @@ func (p *SshPool) buildHash(addr string, auth auth.AuthMethodAndCA) (string, err _ = binary.Write(h, binary.LittleEndian, addr) _ = binary.Write(h, binary.LittleEndian, config.User) - for _, pk := range auth.PublicKeys() { - _ = binary.Write(h, binary.LittleEndian, pk.Marshal()) + h2, err := auth.Hash() + if err != nil { + return "", err } + _ = binary.Write(h, binary.LittleEndian, h2) return hex.EncodeToString(h.Sum(nil)), nil } From da21d297560d653fb2d055935bbc7e5ddfd039c1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 3 Aug 2022 09:19:38 +0200 Subject: [PATCH 0320/2268] chore: Run go get -u ./... --- go.mod | 113 ++++++++++++++++++++--------------------- go.sum | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+), 56 deletions(-) diff --git a/go.mod b/go.mod index 54167b996..2e5ea6a5c 100644 --- a/go.mod +++ b/go.mod @@ -3,75 +3,76 @@ module github.com/kluctl/kluctl/v2 go 1.18 require ( - github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e + github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 - github.com/aws/aws-sdk-go v1.44.28 - github.com/bitnami-labs/sealed-secrets v0.18.0 + github.com/aws/aws-sdk-go v1.44.68 + github.com/bitnami-labs/sealed-secrets v0.18.1 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/evanphx/json-patch v5.6.0+incompatible - github.com/fluxcd/pkg/kustomize v0.5.1 + github.com/fluxcd/pkg/kustomize v0.5.3 github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.0 github.com/gobwas/glob v0.2.3 - github.com/golang-jwt/jwt/v4 v4.4.1 - github.com/google/go-containerregistry v0.9.0 - github.com/hashicorp/vault/api v1.6.0 + github.com/golang-jwt/jwt/v4 v4.4.2 + github.com/google/go-containerregistry v0.11.0 + github.com/hashicorp/vault/api v1.7.2 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/klauspost/compress v1.15.6 + github.com/klauspost/compress v1.15.9 github.com/mattn/go-isatty v0.0.14 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.14.2 + github.com/ohler55/ojg v1.14.3 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.8.1 - github.com/sirupsen/logrus v1.8.1 - github.com/spf13/cobra v1.4.0 + github.com/sirupsen/logrus v1.9.0 + github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.12.0 github.com/stretchr/testify v1.7.2 github.com/vbauerster/mpb/v7 v7.4.2 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.1 - golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e - golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a - golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa + golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 + golang.org/x/sys v0.0.0-20220731174439-a90be440212d + golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 golang.org/x/text v0.3.7 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.9.0 - k8s.io/api v0.24.1 - k8s.io/apiextensions-apiserver v0.24.1 - k8s.io/apimachinery v0.24.1 - k8s.io/client-go v0.24.1 - k8s.io/klog/v2 v2.60.1 + helm.sh/helm/v3 v3.9.2 + k8s.io/api v0.24.3 + k8s.io/apiextensions-apiserver v0.24.3 + k8s.io/apimachinery v0.24.3 + k8s.io/client-go v0.24.3 + k8s.io/klog/v2 v2.70.1 sigs.k8s.io/kind v0.14.0 - sigs.k8s.io/kustomize/api v0.11.5 - sigs.k8s.io/kustomize/kyaml v0.13.7 - sigs.k8s.io/structured-merge-diff/v4 v4.2.1 + sigs.k8s.io/kustomize/api v0.12.1 + sigs.k8s.io/kustomize/kyaml v0.13.9 + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) require ( - cloud.google.com/go/compute v1.6.1 // indirect + cloud.google.com/go/compute v1.7.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.27 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect + github.com/Azure/go-autorest/autorest v0.11.28 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/BurntSushi/toml v1.1.0 // indirect + github.com/BurntSushi/toml v1.2.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b // indirect + github.com/ProtonMail/go-crypto v0.0.0-20220730123233-d6ffb7692adf // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.3 // indirect @@ -83,6 +84,7 @@ require ( github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect + github.com/cloudflare/circl v1.2.0 // indirect github.com/containerd/containerd v1.6.6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v20.10.17+incompatible // indirect @@ -91,7 +93,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect @@ -110,7 +112,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/btree v1.0.1 // indirect + github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -121,7 +123,7 @@ require ( github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.2.1 // indirect + github.com/hashicorp/go-hclog v1.2.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.4.4 // indirect @@ -132,11 +134,11 @@ require ( github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.5.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/vault/sdk v0.5.0 // indirect - github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect + github.com/hashicorp/vault/sdk v0.5.3 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect @@ -171,21 +173,21 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.2 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.2 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.34.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/rubenv/sql-migrate v1.1.1 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + github.com/rivo/uniseg v0.3.1 // indirect + github.com/rubenv/sql-migrate v1.1.2 // indirect github.com/russross/blackfriday v1.6.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect - github.com/spf13/afero v1.8.2 // indirect + github.com/spf13/afero v1.9.2 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.0 // indirect @@ -194,26 +196,25 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect - go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect + go.starlark.net v0.0.0-20220714194419-4cadf0a12139 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect - golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 // indirect - golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect + golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c // indirect + golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 // indirect - google.golang.org/grpc v1.47.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 // indirect + google.golang.org/grpc v1.48.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiserver v0.24.1 // indirect - k8s.io/cli-runtime v0.24.1 // indirect - k8s.io/component-base v0.24.1 // indirect - k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60 // indirect - k8s.io/kubectl v0.24.1 // indirect - k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect - oras.land/oras-go v1.1.1 // indirect - sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 // indirect + k8s.io/apiserver v0.24.3 // indirect + k8s.io/cli-runtime v0.24.3 // indirect + k8s.io/component-base v0.24.3 // indirect + k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect + k8s.io/kubectl v0.24.3 // indirect + k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + oras.land/oras-go v1.2.0 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index af4a31531..a2dc07990 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,7 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= 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= @@ -44,10 +45,13 @@ cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6m cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/compute v1.6.1 h1:2sMmt8prCn7DPaG4Pmh0N3Inmc8cT8ae5k1M6VJ9Wqc= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= 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= @@ -60,6 +64,7 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl 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= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo= @@ -71,10 +76,14 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= +github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= +github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= +github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= @@ -86,11 +95,15 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ= github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -102,10 +115,12 @@ github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy86 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= @@ -124,6 +139,8 @@ github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmU github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b h1:lcbBNuQhppsc7A5gjdHmdlqUqJfgGMylBdGyDs0j7G8= github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/ProtonMail/go-crypto v0.0.0-20220730123233-d6ffb7692adf h1:aFFtnGZ6/2Qlvx80yxA2fFSYDQWTFjtKozQKB36A3/A= +github.com/ProtonMail/go-crypto v0.0.0-20220730123233-d6ffb7692adf/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= @@ -171,6 +188,8 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.44.28 h1:h/OAqEqY18wq//v6h4GNPMmCkxuzSDrWuGyrvSiRqf4= github.com/aws/aws-sdk-go v1.44.28/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.68 h1:7zNr5+HLG0TMq+ZcZ8KhT4eT2KyL7v+u7/jANKEIinM= +github.com/aws/aws-sdk-go v1.44.68/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= 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= @@ -180,6 +199,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitnami-labs/sealed-secrets v0.18.0 h1:7LdfPRMyx9nGQW9204JM1F+F+s6xmaSBl8oNujlrCuc= github.com/bitnami-labs/sealed-secrets v0.18.0/go.mod h1:uV8CUHJQVcDOY9cZ1FdC1rtybC4ath+VGH+/dUQvBu0= +github.com/bitnami-labs/sealed-secrets v0.18.1 h1:xXzi0Z6lArTykRGqCOC2rN3zN648zjQtkCzAVjJj9OY= +github.com/bitnami-labs/sealed-secrets v0.18.1/go.mod h1:pOMGS1imRiIPLm7OpdD/s/OfgQmLkKxTqWruSEiQqCM= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= @@ -193,12 +214,15 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -213,6 +237,9 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.2.0 h1:NheeISPSUcYftKlfrLuOo4T62FkmD4t4jviLfFFYaec= +github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8M+INXlMk= 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= @@ -230,6 +257,7 @@ github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBd github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= github.com/containerd/stargz-snapshotter/estargz v0.11.4 h1:LjrYUZpyOhiSaU7hHrdR82/RBoxfGWSaC0VeSSMXqnk= +github.com/containerd/stargz-snapshotter/estargz v0.12.0 h1:idtwRTLjk2erqiYhPWy2L844By8NRFYEwYHcXhoIWPM= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -247,6 +275,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= @@ -265,6 +294,7 @@ github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27N github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY= +github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= @@ -291,6 +321,8 @@ github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT github.com/emicklei/go-restful/v3 v3.7.5-0.20220308211933-7c971ca4d0fd/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +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/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= @@ -331,6 +363,8 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fluxcd/pkg/kustomize v0.5.1 h1:151Ih34ltxN2z1e2mA5AvQONyE6phc4es57oVK3+plU= github.com/fluxcd/pkg/kustomize v0.5.1/go.mod h1:58MFITy24bIbGI6cC3JkV/YpFQj648sVvgs0K1kraJw= +github.com/fluxcd/pkg/kustomize v0.5.3 h1:WpxNOV/Yklp0p7Qv85VwBegq9fABuLR9qSWaAVa3+yc= +github.com/fluxcd/pkg/kustomize v0.5.3/go.mod h1:zy1FLxkEDADUykCnrXqq6rVN48t3uMhAb3ao+zv0rFE= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -448,6 +482,8 @@ github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 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= @@ -505,6 +541,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z 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/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.10.1/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= @@ -529,6 +567,8 @@ github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.9.0 h1:5Ths7RjxyFV0huKChQTgY6fLzvHhZMpLTFNja8U0/0w= github.com/google/go-containerregistry v0.9.0/go.mod h1:9eq4BnSufyT1kHNffX+vSXVonaJ7yaIOulrKZejMxnQ= +github.com/google/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= +github.com/google/go-containerregistry v0.11.0/go.mod h1:BBaYtsHPHA42uEgAvd/NejvAfPSlz281sJWqupjSxfk= 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= @@ -563,13 +603,16 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= 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/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -625,6 +668,8 @@ github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39 github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= +github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -670,6 +715,8 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E= github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -686,11 +733,17 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/vault/api v1.6.0 h1:B8UUYod1y1OoiGHq9GtpiqSnGOUEWHaA26AY8RQEDY4= github.com/hashicorp/vault/api v1.6.0/go.mod h1:h1K70EO2DgnBaTz5IsL6D5ERsNt5Pce93ueVS2+t0Xc= +github.com/hashicorp/vault/api v1.7.2 h1:kawHE7s/4xwrdKbkmwQi0wYaIeUhk5ueek7ljuezCVQ= +github.com/hashicorp/vault/api v1.7.2/go.mod h1:xbfA+1AvxFseDzxxdWaL0uO99n1+tndus4GCrtouy0M= github.com/hashicorp/vault/sdk v0.5.0 h1:EED7p0OCU3OY5SAqJwSANofY1YKMytm+jDHDQ2EzGVQ= github.com/hashicorp/vault/sdk v0.5.0/go.mod h1:UJZHlfwj7qUJG8g22CuxUgkdJouFrBNvBHCyx8XAPdo= +github.com/hashicorp/vault/sdk v0.5.3 h1:PWY8sq/9pRrK9vUIy75qCH2Jd8oeENAgkaa/qbhzFrs= +github.com/hashicorp/vault/sdk v0.5.3/go.mod h1:DoGraE9kKGNcVgPmTuX357Fm6WAx1Okvde8Vp3dPDoU= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -766,6 +819,8 @@ github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.6 h1:6D9PcO8QWu0JyaQ2zUMmu16T1T+zjjEpP91guRsvDfY= github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -940,6 +995,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/ohler55/ojg v1.14.2 h1:EHdrwmDrOuTGpj1W2LrT/yKeUOkLMIk1cTYcm42Sjj0= github.com/ohler55/ojg v1.14.2/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= +github.com/ohler55/ojg v1.14.3 h1:kaKNsntZ0PuoXPXCY4kjPDbHOLXqokor6Deq/oVxAR0= +github.com/ohler55/ojg v1.14.3/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= @@ -982,10 +1039,13 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= +github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -1030,6 +1090,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1039,6 +1101,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/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/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= @@ -1054,6 +1118,8 @@ github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk= +github.com/rivo/uniseg v0.3.1/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1065,6 +1131,8 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4 github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rubenv/sql-migrate v1.1.1 h1:haR5Hn8hbW9/SpAICrXoZqXnywS7Q5WijwkQENPeNWY= github.com/rubenv/sql-migrate v1.1.1/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= +github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36tZeQ= +github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= @@ -1098,6 +1166,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1111,6 +1181,8 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1122,6 +1194,8 @@ github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSW github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -1261,6 +1335,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd h1:Uo/x0Ir5vQJ+683GXB9Ug+4fcjsbp7z7Ul8UaZbhsRM= go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= +go.starlark.net v0.0.0-20220714194419-4cadf0a12139 h1:zMemyQYZSyEdPaUFixYICrXf/0Rfnil7+jiQRf5IBZ0= +go.starlark.net v0.0.0-20220714194419-4cadf0a12139/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1303,8 +1379,11 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -1405,8 +1484,11 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b h1:3ogNYyK4oIQdIKzTu68hQrr4iuVxF3AxKl9Aj/eDrw0= +golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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= @@ -1429,6 +1511,9 @@ golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 h1:zwrSfklXn0gxyLRX/aR+q6cgHbV/ItVyzbPlbA+dkAw= golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY= +golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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= @@ -1443,6 +1528,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1531,6 +1618,7 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1540,16 +1628,24 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/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-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= +golang.org/x/sys v0.0.0-20220731174439-a90be440212d/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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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= @@ -1569,6 +1665,8 @@ golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= +golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1679,6 +1777,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 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= @@ -1717,6 +1817,9 @@ google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/S google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= 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= @@ -1775,6 +1878,7 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -1808,8 +1912,16 @@ google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 h1:qRu95HZ148xXw+XeZ3dvqe85PxH4X8+jIo0iRPKcEnM= google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 h1:QntLWYqZeuBtJkth3m/6DLznnI0AHJr+AgJXvVh/izw= +google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1843,9 +1955,12 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1862,6 +1977,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/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= @@ -1913,6 +2030,8 @@ gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= helm.sh/helm/v3 v3.9.0 h1:qDSWViuF6SzZX5s5AB/NVRGWmdao7T5j4S4ebIkMGag= helm.sh/helm/v3 v3.9.0/go.mod h1:fzZfyslcPAWwSdkXrXlpKexFeE2Dei8N27FFQWt+PN0= +helm.sh/helm/v3 v3.9.2 h1:bx7kdhr5VAhYoWv9bIdT1C6qWR+/7SIoPCwLx22l78g= +helm.sh/helm/v3 v3.9.2/go.mod h1:y/dJc/0Lzcn40jgd85KQXnufhFF7sr4v6L/vYMLRaRM= 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= @@ -1923,44 +2042,72 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY= k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= +k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY= +k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI= k8s.io/apiextensions-apiserver v0.24.1 h1:5yBh9+ueTq/kfnHQZa0MAo6uNcPrtxPMpNQgorBaKS0= k8s.io/apiextensions-apiserver v0.24.1/go.mod h1:A6MHfaLDGfjOc/We2nM7uewD5Oa/FnEbZ6cD7g2ca4Q= +k8s.io/apiextensions-apiserver v0.24.3 h1:kyx+Tmro1qEsTUr07ZGQOfvTsF61yn+AxnxytBWq8As= +k8s.io/apiextensions-apiserver v0.24.3/go.mod h1:cL0xkmUefpYM4f6IuOau+6NMFEIh6/7wXe/O4vPVJ8A= k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I= k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= +k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apiserver v0.24.1 h1:LAA5UpPOeaREEtFAQRUQOI3eE5So/j5J3zeQJjeLdz4= k8s.io/apiserver v0.24.1/go.mod h1:dQWNMx15S8NqJMp0gpYfssyvhYnkilc1LpExd/dkLh0= +k8s.io/apiserver v0.24.3 h1:J8CKjUaZopT0hSgxjzUyp3T1GK78iixxOuFpEC0MI3k= +k8s.io/apiserver v0.24.3/go.mod h1:aXfwtIn4U27B7lYs5f2BKgz6DRbgWy+HJeYReN1jLJ8= k8s.io/cli-runtime v0.24.1 h1:IW6L8dRBq+pPTzvXcB+m/hOabzbqXy57Bqo4XxmW7DY= k8s.io/cli-runtime v0.24.1/go.mod h1:14aVvCTqkA7dNXY51N/6hRY3GUjchyWDOwW84qmR3bs= +k8s.io/cli-runtime v0.24.3 h1:O9YvUHrDSCQUPlsqVmaqDrueqjpJ7IO6Yas9B6xGSoo= +k8s.io/cli-runtime v0.24.3/go.mod h1:In84wauoMOqa7JDvDSXGbf8lTNlr70fOGpYlYfJtSqA= k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8= +k8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY= +k8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw= k8s.io/code-generator v0.24.1/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= +k8s.io/code-generator v0.24.3/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= k8s.io/component-base v0.24.1 h1:APv6W/YmfOWZfo+XJ1mZwep/f7g7Tpwvdbo9CQLDuts= k8s.io/component-base v0.24.1/go.mod h1:DW5vQGYVCog8WYpNob3PMmmsY8A3L9QZNg4j/dV3s38= +k8s.io/component-base v0.24.3 h1:u99WjuHYCRJjS1xeLOx72DdRaghuDnuMgueiGMFy1ec= +k8s.io/component-base v0.24.3/go.mod h1:bqom2IWN9Lj+vwAkPNOv2TflsP1PeVDIwIN0lRthxYY= k8s.io/component-helpers v0.24.1/go.mod h1:q5Z1pWV/QfX9ThuNeywxasiwkLw9KsR4Q9TAOdb/Y3s= +k8s.io/component-helpers v0.24.3/go.mod h1:/1WNW8TfBOijQ1ED2uCHb4wtXYWDVNMqUll8h36iNVo= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= +k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60 h1:cE/M8rmDQgibspuSm+X1iW16ByTImtEaapgaHoVSLX4= k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60/go.mod h1:ouUzE1U2mEv//HRoBwYLFE5pdqjIebvtX361vtEIlBI= +k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 h1:yEQKdMCjzAOvGeiTwG4hO/hNVNtDOuUFvMUZ0OlaIzs= +k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8/go.mod h1:mbJ+NSUoAhuR14N0S63bPkh8MGVSo3VYSGZtH/mfMe0= k8s.io/kubectl v0.24.1 h1:gxcjHrnwntV1c+G/BHWVv4Mtk8CQJ0WTraElLBG+ddk= k8s.io/kubectl v0.24.1/go.mod h1:NzFqQ50B004fHYWOfhHTrAm4TY6oGF5FAAL13LEaeUI= +k8s.io/kubectl v0.24.3 h1:PqY8ho/S/KuE2/hCC3Iee7X+lOtARYo0LQsNzvV/edE= +k8s.io/kubectl v0.24.3/go.mod h1:PYLcvw96sC1NLbxZEDbdlOEd6/C76VIWjGmWV5QjSk0= k8s.io/metrics v0.24.1/go.mod h1:vMs5xpcOyY9D+/XVwlaw8oUHYCo6JTGBCZfyXOOkAhE= +k8s.io/metrics v0.24.3/go.mod h1:p1M0lhMySWfhISkSd3HEj8xIgrVnJTK3PPhFq2rA3To= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= oras.land/oras-go v1.1.1 h1:gI00ftziRivKXaw1BdMeEoIA4uBgga33iVlOsEwefFs= oras.land/oras-go v1.1.1/go.mod h1:n2TE1ummt9MUyprGhT+Q7kGZUF4kVUpYysPFxeV2IpQ= +oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4= +oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= 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= @@ -1968,19 +2115,27 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lR sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 h1:2sgAQQcY0dEW2SsQwTXhQV4vO6+rSslYx8K3XmM5hqQ= sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kind v0.14.0 h1:cNmI3jGBvp7UegEGbC5we8plDtCUmaNRL+bod7JoSCE= sigs.k8s.io/kind v0.14.0/go.mod h1:UrFRPHG+2a5j0Q7qiR4gtJ4rEyn8TuMQwuOPf+m4oHg= sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= sigs.k8s.io/kustomize/api v0.11.5 h1:vLDp++YAX7iy2y2CVPJNy9pk9CY8XaUKgHkjbVtnWag= sigs.k8s.io/kustomize/api v0.11.5/go.mod h1:2UDpxS6AonWXow2ZbySd4AjUxmdXLeTlvGBC46uSiq8= +sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= +sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= sigs.k8s.io/kustomize/cmd/config v0.10.6/go.mod h1:/S4A4nUANUa4bZJ/Edt7ZQTyKOY9WCER0uBS1SW2Rco= sigs.k8s.io/kustomize/kustomize/v4 v4.5.4/go.mod h1:Zo/Xc5FKD6sHl0lilbrieeGeZHVYCA4BzxeAaLI05Bg= sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= sigs.k8s.io/kustomize/kyaml v0.13.7 h1:/EZ/nPaLUzeJKF/BuJ4QCuMVJWiEVoI8iftOHY3g3tk= sigs.k8s.io/kustomize/kyaml v0.13.7/go.mod h1:6K+IUOuir3Y7nucPRAjw9yth04KSWBnP5pqUTGwj/qU= +sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= +sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +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.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= From 9d20bb4a5ee6a82f021b327e801f12fb2033cc97 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 18 Aug 2022 17:43:15 +0200 Subject: [PATCH 0321/2268] refactor: Pull in python dependencies from kluctl-python-deps project Instead of requiring generation of static python archives. This also removes the need for vendoring and generation. --- .github/workflows/tests.yml | 6 - .gitignore | 2 - .goreleaser.yaml | 1 - Makefile | 17 +- go.mod | 3 +- go.sum | 144 +-------------- pkg/jinja2/embed/python_src.dummy | 0 pkg/jinja2/generate/main.go | 55 ------ pkg/jinja2/jinja2_renderer.go | 2 +- pkg/jinja2/python_src/dummy.go | 1 - pkg/jinja2/python_src/requirements.txt | 4 - pkg/jinja2/source.go | 75 ++++++-- pkg/jinja2/tools.go | 9 - pkg/python/.gitignore | 2 - pkg/python/cmd.go | 24 --- pkg/python/embed.go | 40 ---- pkg/python/embed/python-darwin-amd64.dummy | 0 pkg/python/embed/python-darwin-arm64.dummy | 0 pkg/python/embed/python-linux-amd64.dummy | 0 pkg/python/embed/python-linux-arm64.dummy | 0 pkg/python/embed/python-windows-amd64.dummy | 0 pkg/python/embed_darwin_amd64.go | 6 - pkg/python/embed_darwin_arm64.go | 6 - pkg/python/embed_linux_amd64.go | 6 - pkg/python/embed_linux_arm64.go | 6 - pkg/python/embed_windows_amd64.go | 6 - pkg/python/generate/main.go | 195 -------------------- pkg/python/tools.go | 8 - pkg/utils/embed_util/extract.go | 134 -------------- pkg/utils/embed_util/packer/packer.go | 147 --------------- pkg/utils/tar.go | 190 ------------------- 31 files changed, 73 insertions(+), 1016 deletions(-) delete mode 100644 pkg/jinja2/embed/python_src.dummy delete mode 100644 pkg/jinja2/generate/main.go delete mode 100644 pkg/jinja2/python_src/dummy.go delete mode 100644 pkg/jinja2/python_src/requirements.txt delete mode 100644 pkg/jinja2/tools.go delete mode 100644 pkg/python/.gitignore delete mode 100644 pkg/python/cmd.go delete mode 100644 pkg/python/embed.go delete mode 100644 pkg/python/embed/python-darwin-amd64.dummy delete mode 100644 pkg/python/embed/python-darwin-arm64.dummy delete mode 100644 pkg/python/embed/python-linux-amd64.dummy delete mode 100644 pkg/python/embed/python-linux-arm64.dummy delete mode 100644 pkg/python/embed/python-windows-amd64.dummy delete mode 100644 pkg/python/embed_darwin_amd64.go delete mode 100644 pkg/python/embed_darwin_arm64.go delete mode 100644 pkg/python/embed_linux_amd64.go delete mode 100644 pkg/python/embed_linux_arm64.go delete mode 100644 pkg/python/embed_windows_amd64.go delete mode 100644 pkg/python/generate/main.go delete mode 100644 pkg/python/tools.go delete mode 100644 pkg/utils/embed_util/extract.go delete mode 100644 pkg/utils/embed_util/packer/packer.go delete mode 100644 pkg/utils/tar.go diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 759044bff..509758756 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,12 +28,6 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - name: Go Mod Vendor - run: | - go mod vendor - - name: Go Generate - run: | - go generate ./... - name: Run unit tests run: | go test ./cmd/... ./pkg/... -v diff --git a/.gitignore b/.gitignore index 3158e3eb0..8079ea478 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,6 @@ .secrets.yml .sealed-secrets -/vendor -/download-python /kluctl /kluctl.exe /e2e.test* diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 2461d3dd0..9570d3b69 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,7 +1,6 @@ before: hooks: - go mod tidy - - go generate ./... builds: - <<: &build_defaults binary: kluctl diff --git a/Makefile b/Makefile index 6372eb9d5..631cc08ee 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ WHITE := $(shell tput -Txterm setaf 7) CYAN := $(shell tput -Txterm setaf 6) RESET := $(shell tput -Txterm sgr0) -.PHONY: all test build vendor check-kubectl check-helm check-kind +.PHONY: all test build check-kubectl check-helm check-kind all: help @@ -30,11 +30,11 @@ check-kind: ## Checks if kind is installed kind version ## Build: -build: vendor generate build-go ## Run the complete build pipeline +build: build-go ## Run the complete build pipeline build-go: ## Build your project and put the output binary in ./bin/ mkdir -p ./bin - CGO_ENBALED=0 GO111MODULE=on $(GOCMD) build -mod vendor -o ./bin/$(BINARY_NAME) + CGO_ENBALED=0 GO111MODULE=on $(GOCMD) build -o ./bin/$(BINARY_NAME) clean: ## Remove build related file rm -fr ./bin @@ -42,13 +42,6 @@ clean: ## Remove build related file rm -fr ./reports rm -fr ./download-python -vendor: ## Copy of all packages needed to support builds and tests in the vendor directory - $(GOCMD) mod vendor - -generate: ## Generating Python and Jinja2 support - $(GOCMD) generate ./... - $(GOCMD) generate -tags linux,darwin,windows,amd64,arm64 ./pkg/python - ## Test: test: test-unit test-e2e ## Runs the complete test suite @@ -61,10 +54,10 @@ ifeq ($(EXPORT_RESULT), true) GO111MODULE=off $(GOCMD) get -u github.com/jstemmer/go-junit-report $(eval OUTPUT_OPTIONS = | tee /dev/tty | go-junit-report -set-exit-code > reports/test-unit/junit-report.xml) endif - $(GOTEST) -v -race $(shell go list ./... | grep -v /e2e/ | grep -v /vendor/) $(OUTPUT_OPTIONS) + $(GOTEST) -v -race $(shell go list ./... | grep -v /e2e/) $(OUTPUT_OPTIONS) coverage-unit: ## Run the unit tests of the project and export the coverage - $(GOTEST) -cover -covermode=count -coverprofile=reports/coverage-unit/profile.cov $(shell go list ./... | grep -v /e2e/ | grep -v /vendor/) + $(GOTEST) -cover -covermode=count -coverprofile=reports/coverage-unit/profile.cov $(shell go list ./... | grep -v /e2e/) $(GOCMD) tool cover -func profile.cov ifeq ($(EXPORT_RESULT), true) mkdir -p reports/coverage-unit diff --git a/go.mod b/go.mod index 2e5ea6a5c..4364baad0 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/klauspost/compress v1.15.9 + github.com/kluctl/kluctl-python-deps v0.0.0-20220818145819-a2811c3f89fd github.com/mattn/go-isatty v0.0.14 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 @@ -146,6 +146,7 @@ require ( github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.15.9 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.1 // indirect diff --git a/go.sum b/go.sum index a2dc07990..c55806e56 100644 --- a/go.sum +++ b/go.sum @@ -43,7 +43,6 @@ cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTB cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1 h1:2sMmt8prCn7DPaG4Pmh0N3Inmc8cT8ae5k1M6VJ9Wqc= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= @@ -74,14 +73,10 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg6 github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= -github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= -github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= @@ -93,15 +88,11 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ= -github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -115,12 +106,10 @@ github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy86 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= @@ -137,8 +126,6 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b h1:lcbBNuQhppsc7A5gjdHmdlqUqJfgGMylBdGyDs0j7G8= -github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220730123233-d6ffb7692adf h1:aFFtnGZ6/2Qlvx80yxA2fFSYDQWTFjtKozQKB36A3/A= github.com/ProtonMail/go-crypto v0.0.0-20220730123233-d6ffb7692adf/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -170,7 +157,6 @@ github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -186,8 +172,6 @@ github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9D github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.28 h1:h/OAqEqY18wq//v6h4GNPMmCkxuzSDrWuGyrvSiRqf4= -github.com/aws/aws-sdk-go v1.44.28/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.68 h1:7zNr5+HLG0TMq+ZcZ8KhT4eT2KyL7v+u7/jANKEIinM= github.com/aws/aws-sdk-go v1.44.68/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= @@ -197,8 +181,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.18.0 h1:7LdfPRMyx9nGQW9204JM1F+F+s6xmaSBl8oNujlrCuc= -github.com/bitnami-labs/sealed-secrets v0.18.0/go.mod h1:uV8CUHJQVcDOY9cZ1FdC1rtybC4ath+VGH+/dUQvBu0= github.com/bitnami-labs/sealed-secrets v0.18.1 h1:xXzi0Z6lArTykRGqCOC2rN3zN648zjQtkCzAVjJj9OY= github.com/bitnami-labs/sealed-secrets v0.18.1/go.mod h1:pOMGS1imRiIPLm7OpdD/s/OfgQmLkKxTqWruSEiQqCM= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= @@ -216,13 +198,11 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXe github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -256,7 +236,6 @@ github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u9 github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= -github.com/containerd/stargz-snapshotter/estargz v0.11.4 h1:LjrYUZpyOhiSaU7hHrdR82/RBoxfGWSaC0VeSSMXqnk= github.com/containerd/stargz-snapshotter/estargz v0.12.0 h1:idtwRTLjk2erqiYhPWy2L844By8NRFYEwYHcXhoIWPM= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -293,7 +272,6 @@ github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCF github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -318,9 +296,6 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7fo github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.7.5-0.20220308211933-7c971ca4d0fd/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 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/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= @@ -343,7 +318,6 @@ github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 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.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= 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/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= @@ -356,20 +330,15 @@ github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGE github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fluxcd/pkg/kustomize v0.5.1 h1:151Ih34ltxN2z1e2mA5AvQONyE6phc4es57oVK3+plU= -github.com/fluxcd/pkg/kustomize v0.5.1/go.mod h1:58MFITy24bIbGI6cC3JkV/YpFQj648sVvgs0K1kraJw= github.com/fluxcd/pkg/kustomize v0.5.3 h1:WpxNOV/Yklp0p7Qv85VwBegq9fABuLR9qSWaAVa3+yc= github.com/fluxcd/pkg/kustomize v0.5.3/go.mod h1:zy1FLxkEDADUykCnrXqq6rVN48t3uMhAb3ao+zv0rFE= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= -github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -384,7 +353,6 @@ github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49P github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -407,7 +375,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 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-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= 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= @@ -448,7 +415,6 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= -github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= @@ -480,8 +446,6 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= -github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= @@ -539,7 +503,6 @@ github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA// github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 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/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= @@ -565,8 +528,6 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.9.0 h1:5Ths7RjxyFV0huKChQTgY6fLzvHhZMpLTFNja8U0/0w= -github.com/google/go-containerregistry v0.9.0/go.mod h1:9eq4BnSufyT1kHNffX+vSXVonaJ7yaIOulrKZejMxnQ= github.com/google/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= github.com/google/go-containerregistry v0.11.0/go.mod h1:BBaYtsHPHA42uEgAvd/NejvAfPSlz281sJWqupjSxfk= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -664,57 +625,40 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= -github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ= github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ= github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E= -github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -731,17 +675,10 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/vault/api v1.6.0 h1:B8UUYod1y1OoiGHq9GtpiqSnGOUEWHaA26AY8RQEDY4= -github.com/hashicorp/vault/api v1.6.0/go.mod h1:h1K70EO2DgnBaTz5IsL6D5ERsNt5Pce93ueVS2+t0Xc= github.com/hashicorp/vault/api v1.7.2 h1:kawHE7s/4xwrdKbkmwQi0wYaIeUhk5ueek7ljuezCVQ= github.com/hashicorp/vault/api v1.7.2/go.mod h1:xbfA+1AvxFseDzxxdWaL0uO99n1+tndus4GCrtouy0M= -github.com/hashicorp/vault/sdk v0.5.0 h1:EED7p0OCU3OY5SAqJwSANofY1YKMytm+jDHDQ2EzGVQ= -github.com/hashicorp/vault/sdk v0.5.0/go.mod h1:UJZHlfwj7qUJG8g22CuxUgkdJouFrBNvBHCyx8XAPdo= github.com/hashicorp/vault/sdk v0.5.3 h1:PWY8sq/9pRrK9vUIy75qCH2Jd8oeENAgkaa/qbhzFrs= github.com/hashicorp/vault/sdk v0.5.3/go.mod h1:DoGraE9kKGNcVgPmTuX357Fm6WAx1Okvde8Vp3dPDoU= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= -github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -768,7 +705,6 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= -github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= @@ -817,10 +753,10 @@ github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.6 h1:6D9PcO8QWu0JyaQ2zUMmu16T1T+zjjEpP91guRsvDfY= -github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/kluctl/kluctl-python-deps v0.0.0-20220818145819-a2811c3f89fd h1:CTUYp06sH7d38kfHY+tysa9jhQuo1C9ssSlVR1hFuDA= +github.com/kluctl/kluctl-python-deps v0.0.0-20220818145819-a2811c3f89fd/go.mod h1:w4cTz1av5JFX9bMTB7Vm2qtmm5+eU+ncvB1/oyjVwOw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -935,7 +871,6 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= @@ -993,11 +928,8 @@ github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/ohler55/ojg v1.14.2 h1:EHdrwmDrOuTGpj1W2LrT/yKeUOkLMIk1cTYcm42Sjj0= -github.com/ohler55/ojg v1.14.2/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= github.com/ohler55/ojg v1.14.3 h1:kaKNsntZ0PuoXPXCY4kjPDbHOLXqokor6Deq/oVxAR0= github.com/ohler55/ojg v1.14.3/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -1013,6 +945,7 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -1037,16 +970,12 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko 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.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= -github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -1088,8 +1017,6 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b 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.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= -github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -1099,7 +1026,6 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 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.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/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= @@ -1116,7 +1042,6 @@ github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mo github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk= github.com/rivo/uniseg v0.3.1/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -1129,8 +1054,6 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rubenv/sql-migrate v1.1.1 h1:haR5Hn8hbW9/SpAICrXoZqXnywS7Q5WijwkQENPeNWY= -github.com/rubenv/sql-migrate v1.1.1/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36tZeQ= github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -1164,7 +1087,6 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -1179,8 +1101,6 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1192,7 +1112,6 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= @@ -1224,7 +1143,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P 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.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -1333,8 +1251,6 @@ go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4 go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd h1:Uo/x0Ir5vQJ+683GXB9Ug+4fcjsbp7z7Ul8UaZbhsRM= -go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.starlark.net v0.0.0-20220714194419-4cadf0a12139 h1:zMemyQYZSyEdPaUFixYICrXf/0Rfnil7+jiQRf5IBZ0= go.starlark.net v0.0.0-20220714194419-4cadf0a12139/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1380,8 +1296,6 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1422,7 +1336,6 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1485,7 +1398,6 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b h1:3ogNYyK4oIQdIKzTu68hQrr4iuVxF3AxKl9Aj/eDrw0= golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -1509,8 +1421,6 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 h1:zwrSfklXn0gxyLRX/aR+q6cgHbV/ItVyzbPlbA+dkAw= -golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY= golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= @@ -1526,7 +1436,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1633,7 +1542,6 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1642,8 +1550,6 @@ golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBc 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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1663,8 +1569,6 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= -golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1916,8 +1820,6 @@ google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 h1:qRu95HZ148xXw+XeZ3dvqe85PxH4X8+jIo0iRPKcEnM= -google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To= google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 h1:QntLWYqZeuBtJkth3m/6DLznnI0AHJr+AgJXvVh/izw= @@ -1952,12 +1854,10 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= @@ -1975,7 +1875,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= @@ -2001,7 +1900,6 @@ gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -2028,8 +1926,6 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -helm.sh/helm/v3 v3.9.0 h1:qDSWViuF6SzZX5s5AB/NVRGWmdao7T5j4S4ebIkMGag= -helm.sh/helm/v3 v3.9.0/go.mod h1:fzZfyslcPAWwSdkXrXlpKexFeE2Dei8N27FFQWt+PN0= helm.sh/helm/v3 v3.9.2 h1:bx7kdhr5VAhYoWv9bIdT1C6qWR+/7SIoPCwLx22l78g= helm.sh/helm/v3 v3.9.2/go.mod h1:y/dJc/0Lzcn40jgd85KQXnufhFF7sr4v6L/vYMLRaRM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2040,63 +1936,38 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt 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= honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= -k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY= -k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY= k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI= -k8s.io/apiextensions-apiserver v0.24.1 h1:5yBh9+ueTq/kfnHQZa0MAo6uNcPrtxPMpNQgorBaKS0= -k8s.io/apiextensions-apiserver v0.24.1/go.mod h1:A6MHfaLDGfjOc/We2nM7uewD5Oa/FnEbZ6cD7g2ca4Q= k8s.io/apiextensions-apiserver v0.24.3 h1:kyx+Tmro1qEsTUr07ZGQOfvTsF61yn+AxnxytBWq8As= k8s.io/apiextensions-apiserver v0.24.3/go.mod h1:cL0xkmUefpYM4f6IuOau+6NMFEIh6/7wXe/O4vPVJ8A= -k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I= -k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apiserver v0.24.1 h1:LAA5UpPOeaREEtFAQRUQOI3eE5So/j5J3zeQJjeLdz4= -k8s.io/apiserver v0.24.1/go.mod h1:dQWNMx15S8NqJMp0gpYfssyvhYnkilc1LpExd/dkLh0= k8s.io/apiserver v0.24.3 h1:J8CKjUaZopT0hSgxjzUyp3T1GK78iixxOuFpEC0MI3k= k8s.io/apiserver v0.24.3/go.mod h1:aXfwtIn4U27B7lYs5f2BKgz6DRbgWy+HJeYReN1jLJ8= -k8s.io/cli-runtime v0.24.1 h1:IW6L8dRBq+pPTzvXcB+m/hOabzbqXy57Bqo4XxmW7DY= -k8s.io/cli-runtime v0.24.1/go.mod h1:14aVvCTqkA7dNXY51N/6hRY3GUjchyWDOwW84qmR3bs= k8s.io/cli-runtime v0.24.3 h1:O9YvUHrDSCQUPlsqVmaqDrueqjpJ7IO6Yas9B6xGSoo= k8s.io/cli-runtime v0.24.3/go.mod h1:In84wauoMOqa7JDvDSXGbf8lTNlr70fOGpYlYfJtSqA= -k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= -k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8= k8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY= k8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw= -k8s.io/code-generator v0.24.1/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= k8s.io/code-generator v0.24.3/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= -k8s.io/component-base v0.24.1 h1:APv6W/YmfOWZfo+XJ1mZwep/f7g7Tpwvdbo9CQLDuts= -k8s.io/component-base v0.24.1/go.mod h1:DW5vQGYVCog8WYpNob3PMmmsY8A3L9QZNg4j/dV3s38= k8s.io/component-base v0.24.3 h1:u99WjuHYCRJjS1xeLOx72DdRaghuDnuMgueiGMFy1ec= k8s.io/component-base v0.24.3/go.mod h1:bqom2IWN9Lj+vwAkPNOv2TflsP1PeVDIwIN0lRthxYY= -k8s.io/component-helpers v0.24.1/go.mod h1:q5Z1pWV/QfX9ThuNeywxasiwkLw9KsR4Q9TAOdb/Y3s= k8s.io/component-helpers v0.24.3/go.mod h1:/1WNW8TfBOijQ1ED2uCHb4wtXYWDVNMqUll8h36iNVo= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= -k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60 h1:cE/M8rmDQgibspuSm+X1iW16ByTImtEaapgaHoVSLX4= -k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60/go.mod h1:ouUzE1U2mEv//HRoBwYLFE5pdqjIebvtX361vtEIlBI= k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 h1:yEQKdMCjzAOvGeiTwG4hO/hNVNtDOuUFvMUZ0OlaIzs= k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8/go.mod h1:mbJ+NSUoAhuR14N0S63bPkh8MGVSo3VYSGZtH/mfMe0= -k8s.io/kubectl v0.24.1 h1:gxcjHrnwntV1c+G/BHWVv4Mtk8CQJ0WTraElLBG+ddk= -k8s.io/kubectl v0.24.1/go.mod h1:NzFqQ50B004fHYWOfhHTrAm4TY6oGF5FAAL13LEaeUI= k8s.io/kubectl v0.24.3 h1:PqY8ho/S/KuE2/hCC3Iee7X+lOtARYo0LQsNzvV/edE= k8s.io/kubectl v0.24.3/go.mod h1:PYLcvw96sC1NLbxZEDbdlOEd6/C76VIWjGmWV5QjSk0= -k8s.io/metrics v0.24.1/go.mod h1:vMs5xpcOyY9D+/XVwlaw8oUHYCo6JTGBCZfyXOOkAhE= k8s.io/metrics v0.24.3/go.mod h1:p1M0lhMySWfhISkSd3HEj8xIgrVnJTK3PPhFq2rA3To= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -2104,8 +1975,6 @@ mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= -oras.land/oras-go v1.1.1 h1:gI00ftziRivKXaw1BdMeEoIA4uBgga33iVlOsEwefFs= -oras.land/oras-go v1.1.1/go.mod h1:n2TE1ummt9MUyprGhT+Q7kGZUF4kVUpYysPFxeV2IpQ= oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4= oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -2113,26 +1982,19 @@ 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.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 h1:2sgAQQcY0dEW2SsQwTXhQV4vO6+rSslYx8K3XmM5hqQ= -sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kind v0.14.0 h1:cNmI3jGBvp7UegEGbC5we8plDtCUmaNRL+bod7JoSCE= sigs.k8s.io/kind v0.14.0/go.mod h1:UrFRPHG+2a5j0Q7qiR4gtJ4rEyn8TuMQwuOPf+m4oHg= sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= -sigs.k8s.io/kustomize/api v0.11.5 h1:vLDp++YAX7iy2y2CVPJNy9pk9CY8XaUKgHkjbVtnWag= -sigs.k8s.io/kustomize/api v0.11.5/go.mod h1:2UDpxS6AonWXow2ZbySd4AjUxmdXLeTlvGBC46uSiq8= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= sigs.k8s.io/kustomize/cmd/config v0.10.6/go.mod h1:/S4A4nUANUa4bZJ/Edt7ZQTyKOY9WCER0uBS1SW2Rco= sigs.k8s.io/kustomize/kustomize/v4 v4.5.4/go.mod h1:Zo/Xc5FKD6sHl0lilbrieeGeZHVYCA4BzxeAaLI05Bg= sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= -sigs.k8s.io/kustomize/kyaml v0.13.7 h1:/EZ/nPaLUzeJKF/BuJ4QCuMVJWiEVoI8iftOHY3g3tk= -sigs.k8s.io/kustomize/kyaml v0.13.7/go.mod h1:6K+IUOuir3Y7nucPRAjw9yth04KSWBnP5pqUTGwj/qU= sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= 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= diff --git a/pkg/jinja2/embed/python_src.dummy b/pkg/jinja2/embed/python_src.dummy deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/jinja2/generate/main.go b/pkg/jinja2/generate/main.go deleted file mode 100644 index 9da3d4f4e..000000000 --- a/pkg/jinja2/generate/main.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "github.com/kluctl/kluctl/v2/pkg/utils/embed_util/packer" - "os" - "os/exec" - "path/filepath" - "strings" -) - -func main() { - pipWheel() -} - -func pipWheel() { - _ = os.RemoveAll("python_src/wheel") - _ = os.MkdirAll("python_src/wheel", 0o700) - - cmd := exec.Command("pip3", "wheel", "-r", "../requirements.txt") - cmd.Dir = "python_src/wheel" - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - panic(err) - } - - wheels, err := os.ReadDir("python_src/wheel") - if err != nil { - panic(err) - } - for _, w := range wheels { - if !strings.HasSuffix(w.Name(), ".whl") { - continue - } - - cmd = exec.Command("unzip", w.Name()) - cmd.Dir = "python_src/wheel" - - err = cmd.Run() - if err != nil { - panic(err) - } - - err = os.Remove(filepath.Join("python_src/wheel", w.Name())) - if err != nil { - panic(err) - } - } - - err = packer.Pack("embed/python_src.tar.gz", "python_src", "*") - if err != nil { - panic(err) - } -} diff --git a/pkg/jinja2/jinja2_renderer.go b/pkg/jinja2/jinja2_renderer.go index 8895f2366..e5b64ef73 100644 --- a/pkg/jinja2/jinja2_renderer.go +++ b/pkg/jinja2/jinja2_renderer.go @@ -5,7 +5,7 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/kluctl/kluctl/v2/pkg/python" + "github.com/kluctl/kluctl-python-deps/pkg/python" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "io" "io/ioutil" diff --git a/pkg/jinja2/python_src/dummy.go b/pkg/jinja2/python_src/dummy.go deleted file mode 100644 index 60d276519..000000000 --- a/pkg/jinja2/python_src/dummy.go +++ /dev/null @@ -1 +0,0 @@ -package python_src diff --git a/pkg/jinja2/python_src/requirements.txt b/pkg/jinja2/python_src/requirements.txt deleted file mode 100644 index 9ee2b287d..000000000 --- a/pkg/jinja2/python_src/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -jinja2===3.1.2 -click==8.1.3 -jsonpath-ng==1.5.3 -pyyaml==6.0 diff --git a/pkg/jinja2/source.go b/pkg/jinja2/source.go index d9ab49bb4..b80377261 100644 --- a/pkg/jinja2/source.go +++ b/pkg/jinja2/source.go @@ -1,16 +1,22 @@ package jinja2 import ( + "crypto/sha256" "embed" + "encoding/binary" + "encoding/hex" + "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/embed_util" + "github.com/rogpeppe/go-internal/lockedfile" + "io/fs" + "os" "path/filepath" ) -//go:generate go run ./generate +//go:embed python_src +var _pythonSrc embed.FS +var pythonSrc, _ = fs.Sub(_pythonSrc, "python_src") -//go:embed embed/python_src.* -var pythonSrc embed.FS var pythonSrcExtracted string func init() { @@ -22,23 +28,66 @@ func init() { } func extractSource() (string, error) { - tgz, err := pythonSrc.Open("embed/python_src.tar.gz") + hash := calcEmbeddedHash(pythonSrc) + + targetPath := filepath.Join(utils.GetTmpBaseDir(), fmt.Sprintf("jinja2-%s", hash[:16])) + + lock, err := lockedfile.Create(targetPath + ".lock") if err != nil { return "", err } - defer tgz.Close() + defer lock.Close() - fileList, err := pythonSrc.Open("embed/python_src.tar.gz.files") + err = fs.WalkDir(pythonSrc, ".", func(path string, d fs.DirEntry, err error) error { + if d == nil || d.IsDir() { + return nil + } + data, err := fs.ReadFile(pythonSrc, path) + if err != nil { + return err + } + targetPath2 := filepath.Join(targetPath, path) + err = os.MkdirAll(filepath.Dir(targetPath2), 0o755) + if err != nil { + return err + } + + err = os.WriteFile(targetPath2+".tmp", data, 0o644) + if err != nil { + return err + } + err = os.Rename(targetPath2+".tmp", targetPath2) + if err != nil { + return err + } + return nil + }) if err != nil { return "", err } - defer fileList.Close() - libPath := filepath.Join(utils.GetTmpBaseDir(), "jinja2-src") - libPath, err = embed_util.ExtractTarToTmp(tgz, fileList, libPath) + return targetPath, nil +} + +func calcEmbeddedHash(fs1 fs.FS) string { + h := sha256.New() + err := fs.WalkDir(fs1, ".", func(path string, d fs.DirEntry, err error) error { + _ = binary.Write(h, binary.LittleEndian, path) + if d.IsDir() { + _ = binary.Write(h, binary.LittleEndian, "dir") + } else { + _ = binary.Write(h, binary.LittleEndian, "regular") + data, err := fs.ReadFile(fs1, path) + if err != nil { + panic(err) + } + _ = binary.Write(h, binary.LittleEndian, len(data)) + _ = binary.Write(h, binary.LittleEndian, data) + } + return nil + }) if err != nil { - return "", err + panic(err) } - - return libPath, nil + return hex.EncodeToString(h.Sum(nil)) } diff --git a/pkg/jinja2/tools.go b/pkg/jinja2/tools.go deleted file mode 100644 index 839cd9103..000000000 --- a/pkg/jinja2/tools.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build tools -// +build tools - -package tools - -import ( - _ "github.com/kluctl/kluctl/v2/pkg/jinja2/generate" - _ "github.com/kluctl/kluctl/v2/pkg/jinja2/python_src" -) diff --git a/pkg/python/.gitignore b/pkg/python/.gitignore deleted file mode 100644 index 01ec3b178..000000000 --- a/pkg/python/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.tar.gz -*.files diff --git a/pkg/python/cmd.go b/pkg/python/cmd.go deleted file mode 100644 index eea32c554..000000000 --- a/pkg/python/cmd.go +++ /dev/null @@ -1,24 +0,0 @@ -package python - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "runtime" -) - -func PythonCmd(args []string) *exec.Cmd { - var exePath string - if runtime.GOOS == "windows" { - exePath = filepath.Join(embeddedPythonPath, "python.exe") - } else { - exePath = filepath.Join(embeddedPythonPath, "bin/python3") - } - - cmd := exec.Command(exePath, args...) - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, fmt.Sprintf("PYTHONHOME=%s", embeddedPythonPath)) - - return cmd -} diff --git a/pkg/python/embed.go b/pkg/python/embed.go deleted file mode 100644 index 500e68ed8..000000000 --- a/pkg/python/embed.go +++ /dev/null @@ -1,40 +0,0 @@ -package python - -import ( - "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/embed_util" - "path/filepath" - "runtime" -) - -//go:generate go run ./generate - -var embeddedPythonPath string - -func init() { - embeddedPythonPath = decompressPython() -} - -func decompressPython() string { - tarName := fmt.Sprintf("embed/python-%s-%s.tar.gz", runtime.GOOS, runtime.GOARCH) - tgz, err := pythonLib.Open(tarName) - if err != nil { - panic(err) - } - defer tgz.Close() - - fileList, err := pythonLib.Open(tarName + ".files") - if err != nil { - panic(err) - } - defer fileList.Close() - - path := filepath.Join(utils.GetTmpBaseDir(), fmt.Sprintf("python-%s", runtime.GOOS)) - path, err = embed_util.ExtractTarToTmp(tgz, fileList, path) - if err != nil { - panic(err) - } - - return path -} diff --git a/pkg/python/embed/python-darwin-amd64.dummy b/pkg/python/embed/python-darwin-amd64.dummy deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/python/embed/python-darwin-arm64.dummy b/pkg/python/embed/python-darwin-arm64.dummy deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/python/embed/python-linux-amd64.dummy b/pkg/python/embed/python-linux-amd64.dummy deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/python/embed/python-linux-arm64.dummy b/pkg/python/embed/python-linux-arm64.dummy deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/python/embed/python-windows-amd64.dummy b/pkg/python/embed/python-windows-amd64.dummy deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/python/embed_darwin_amd64.go b/pkg/python/embed_darwin_amd64.go deleted file mode 100644 index 110c5af57..000000000 --- a/pkg/python/embed_darwin_amd64.go +++ /dev/null @@ -1,6 +0,0 @@ -package python - -import "embed" - -//go:embed embed/python-darwin-amd64.* -var pythonLib embed.FS diff --git a/pkg/python/embed_darwin_arm64.go b/pkg/python/embed_darwin_arm64.go deleted file mode 100644 index f14b56a5f..000000000 --- a/pkg/python/embed_darwin_arm64.go +++ /dev/null @@ -1,6 +0,0 @@ -package python - -import "embed" - -//go:embed embed/python-darwin-arm64.* -var pythonLib embed.FS diff --git a/pkg/python/embed_linux_amd64.go b/pkg/python/embed_linux_amd64.go deleted file mode 100644 index 99838aee9..000000000 --- a/pkg/python/embed_linux_amd64.go +++ /dev/null @@ -1,6 +0,0 @@ -package python - -import "embed" - -//go:embed embed/python-linux-amd64.* -var pythonLib embed.FS diff --git a/pkg/python/embed_linux_arm64.go b/pkg/python/embed_linux_arm64.go deleted file mode 100644 index 158c5087e..000000000 --- a/pkg/python/embed_linux_arm64.go +++ /dev/null @@ -1,6 +0,0 @@ -package python - -import "embed" - -//go:embed embed/python-linux-arm64.* -var pythonLib embed.FS diff --git a/pkg/python/embed_windows_amd64.go b/pkg/python/embed_windows_amd64.go deleted file mode 100644 index 65e5e2c9c..000000000 --- a/pkg/python/embed_windows_amd64.go +++ /dev/null @@ -1,6 +0,0 @@ -package python - -import "embed" - -//go:embed embed/python-windows-amd64.* -var pythonLib embed.FS diff --git a/pkg/python/generate/main.go b/pkg/python/generate/main.go deleted file mode 100644 index 9cc24b145..000000000 --- a/pkg/python/generate/main.go +++ /dev/null @@ -1,195 +0,0 @@ -package main - -import ( - "fmt" - "github.com/klauspost/compress/zstd" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/embed_util/packer" - log "github.com/sirupsen/logrus" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "sync" -) - -const ( - pythonVersionBase = "3.10" - pythonVersionFull = "3.10.5" - pythonStandaloneVersion = "20220630" -) - -var pythonDists = map[string]string{ - "linux": "unknown-linux-gnu-lto-full", - "darwin": "apple-darwin-lto-full", - "windows": "pc-windows-msvc-shared-pgo-full", -} - -var archMapping = map[string]string{ - "amd64": "x86_64", - "386": "i686", - "arm64": "aarch64", -} - -var removeLibs = []string{ - "site-packages", - "venv", - "ensurepip", - "idlelib", - "distutils", - "pydoc_data", - "asyncio", - "email", - "tkinter", - "lib2to3", - "xml", - "multiprocessing", - "unittest", -} - -var downloadLock sync.Mutex - -func main() { - var wg sync.WaitGroup - - nixPatterns := []string{ - "bin", - "lib/*.so*", - "lib/*.dylib", - "lib/python3.*", - } - winPatterns := []string{ - "Lib", - "DLLs", - "*.dll", - "*.exe", - } - type job struct { - os string - arch string - packSubDir string - out string - patterns []string - } - jobs := []job{ - {"linux", "amd64", "python/install", "embed/python-linux-amd64.tar.gz", nixPatterns}, - {"linux", "arm64", "python/install", "embed/python-linux-arm64.tar.gz", nixPatterns}, - {"darwin", "amd64", "python/install", "embed/python-darwin-amd64.tar.gz", nixPatterns}, - {"darwin", "arm64", "python/install", "embed/python-darwin-arm64.tar.gz", nixPatterns}, - {"windows", "amd64", "python/install", "embed/python-windows-amd64.tar.gz", winPatterns}, - } - for _, j := range jobs { - j := j - wg.Add(1) - go func() { - downloadAndPack(j.os, j.arch, j.packSubDir, j.out, j.patterns) - wg.Done() - }() - } - wg.Wait() -} - -func downloadAndPack(osName string, arch string, packSubdir string, out string, patterns []string) { - dist, ok := pythonDists[osName] - if !ok { - log.Panicf("no dist for %s", osName) - } - - downloadPath := download(osName, arch, dist) - archiveBytes, _ := ioutil.ReadFile(downloadPath) - hash := utils.Sha256Bytes(archiveBytes) - - extractPath := downloadPath + ".extracted" - if utils.Exists(filepath.Join(extractPath, hash)) { - log.Infof("skipping extract of %s", extractPath) - pack(out, filepath.Join(extractPath, packSubdir), patterns) - return - } - - _ = os.RemoveAll(extractPath) - extract(downloadPath, extractPath) - - for _, lib := range removeLibs { - _ = os.RemoveAll(filepath.Join(extractPath, "python", "install", "lib", fmt.Sprintf("python%s", pythonVersionBase), lib)) - _ = os.RemoveAll(filepath.Join(extractPath, "python", "install", "Lib", lib)) - } - - _ = utils.Touch(filepath.Join(extractPath, hash)) - - pack(out, filepath.Join(extractPath, packSubdir), patterns) -} - -func pack(out string, dir string, patterns []string) { - err := packer.Pack(out, dir, patterns...) - if err != nil { - log.Panic(err) - } -} - -func download(osName, arch, dist string) string { - downloadLock.Lock() - defer downloadLock.Unlock() - - pythonArch, ok := archMapping[arch] - if !ok { - log.Errorf("arch %s not supported", arch) - os.Exit(1) - } - fname := fmt.Sprintf("cpython-%s+%s-%s-%s.tar.zst", pythonVersionFull, pythonStandaloneVersion, pythonArch, dist) - downloadPath := filepath.Join(utils.GetTmpBaseDir(), fname) - downloadUrl := fmt.Sprintf("https://github.com/indygreg/python-build-standalone/releases/download/%s/%s", pythonStandaloneVersion, fname) - - if _, err := os.Stat(downloadPath); err == nil { - log.Infof("skipping download of %s", downloadUrl) - return downloadPath - } - - log.Infof("downloading %s", downloadUrl) - - r, err := http.Get(downloadUrl) - if err != nil { - log.Errorf("download failed: %v", err) - os.Exit(1) - } - if r.StatusCode == http.StatusNotFound { - log.Errorf("404 not found") - os.Exit(1) - } - defer r.Body.Close() - - fileData, err := ioutil.ReadAll(r.Body) - - err = ioutil.WriteFile(downloadPath, fileData, 0o640) - if err != nil { - log.Errorf("writing file failed: %v", err) - os.Remove(downloadPath) - os.Exit(1) - } - - return downloadPath -} - -func extract(archivePath string, targetPath string) string { - f, err := os.Open(archivePath) - if err != nil { - log.Errorf("opening file failed: %v", err) - os.Exit(1) - } - defer f.Close() - - z, err := zstd.NewReader(f) - if err != nil { - log.Errorf("decompression failed: %v", err) - os.Exit(1) - } - defer z.Close() - - log.Infof("decompressing %s", archivePath) - err = utils.ExtractTarStream(z, targetPath) - if err != nil { - log.Errorf("decompression failed: %v", err) - os.Exit(1) - } - - return targetPath -} diff --git a/pkg/python/tools.go b/pkg/python/tools.go deleted file mode 100644 index 48d6d8a70..000000000 --- a/pkg/python/tools.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build tools -// +build tools - -package tools - -import ( - _ "github.com/kluctl/kluctl/v2/pkg/python/generate" -) diff --git a/pkg/utils/embed_util/extract.go b/pkg/utils/embed_util/extract.go deleted file mode 100644 index e3c5dc0d4..000000000 --- a/pkg/utils/embed_util/extract.go +++ /dev/null @@ -1,134 +0,0 @@ -package embed_util - -import ( - "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/rogpeppe/go-internal/lockedfile" - "io" - "io/fs" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" -) - -func ExtractTarToTmp(r io.Reader, fileListR io.Reader, targetPrefix string) (string, error) { - fileList, err := ioutil.ReadAll(fileListR) - if err != nil { - return "", err - } - - targetPath := fmt.Sprintf("%s-%s", targetPrefix, utils.Sha256Bytes(fileList)[:16]) - - fl, err := lockedfile.Create(targetPath + ".lock") - if err != nil { - return "", err - } - defer fl.Close() - - needsExtract, expectedTarGzHash, err := checkExtractNeeded(targetPath, string(fileList)) - if err != nil { - return "", err - } - if !needsExtract { - return targetPath, nil - } - - err = os.RemoveAll(targetPath) - if err != nil && !os.IsNotExist(err) { - return "", err - } - - err = os.MkdirAll(targetPath, 0o700) - if err != nil { - return "", err - } - - err = utils.ExtractTarGzStream(r, targetPath) - if err != nil { - return "", err - } - - err = ioutil.WriteFile(filepath.Join(targetPath, ".tar-gz-hash"), []byte(expectedTarGzHash), 0o600) - if err != nil { - return "", err - } - - return targetPath, nil -} - -func checkExtractNeeded(targetPath string, fileListStr string) (bool, string, error) { - expectedHash, tarFilesMap, err := ReadFileList(fileListStr) - if err != nil { - return false, "", err - } - - if !utils.Exists(targetPath) { - return true, expectedHash, nil - } - - existingHash, err := ioutil.ReadFile(filepath.Join(targetPath, ".tar-gz-hash")) - if err != nil { - return true, expectedHash, nil - } - - if strings.TrimSpace(expectedHash) != strings.TrimSpace(string(existingHash)) { - return true, expectedHash, nil - } - - existingFiles, err := BuildFileList(targetPath) - if err != nil { - return false, "", err - } - - for fname, size := range tarFilesMap { - if s, ok := existingFiles[fname]; !ok || s != size { - return true, expectedHash, nil - } - } - return false, expectedHash, nil -} - -func ReadFileList(fileListStr string) (string, map[string]int64, error) { - fileList := strings.Split(fileListStr, "\n") - expectedHash := fileList[0] - fileList = fileList[1:] - - tarFilesMap := make(map[string]int64) - for _, l := range fileList { - s := strings.SplitN(l, ":", 2) - fname := strings.TrimSpace(s[0]) - sh := strings.SplitN(strings.TrimSpace(s[1]), " ", 2) - size, err := strconv.ParseInt(strings.TrimSpace(sh[0]), 10, 64) - if err != nil { - return expectedHash, tarFilesMap, err - } - tarFilesMap[fname] = size - } - return expectedHash, tarFilesMap, nil -} - -func BuildFileList(targetPath string) (map[string]int64, error) { - existingFiles := make(map[string]int64) - err := filepath.Walk(targetPath, func(path string, info fs.FileInfo, err error) error { - if !info.Mode().IsRegular() && info.Mode().Type() != fs.ModeSymlink && info.Mode().Type() != fs.ModeDir { - return nil - } - relPath, err := filepath.Rel(targetPath, path) - if err != nil { - return err - } - relPath = filepath.ToSlash(relPath) - if info.IsDir() { - existingFiles[relPath] = 0 - } else { - existingFiles[relPath] = info.Size() - } - return nil - }) - if err != nil { - return nil, err - } - return existingFiles, nil -} diff --git a/pkg/utils/embed_util/packer/packer.go b/pkg/utils/embed_util/packer/packer.go deleted file mode 100644 index fd6fdd077..000000000 --- a/pkg/utils/embed_util/packer/packer.go +++ /dev/null @@ -1,147 +0,0 @@ -package packer - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "crypto/sha256" - "encoding/hex" - "fmt" - "github.com/gobwas/glob" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/embed_util" - log "github.com/sirupsen/logrus" - "io/fs" - "io/ioutil" - "path/filepath" - "reflect" - "strings" -) - -func Pack(out string, dir string, patterns ...string) error { - fileList, err := findFiles(dir, patterns) - if err != nil { - return err - } - - if utils.Exists(out) && utils.Exists(out+".files") { - existingFileListStr, err := ioutil.ReadFile(out + ".files") - if err == nil { - _, existingFileList, err := embed_util.ReadFileList(string(existingFileListStr)) - if err == nil { - if reflect.DeepEqual(existingFileList, fileList) { - log.Infof("Skipping packing of %s", out) - return nil - } - } - } - } - - log.Infof("writing tar %s with %d files", out, len(fileList)) - tgz, err := writeTar(dir, fileList) - if err != nil { - return err - } - - hash := sha256.Sum256(tgz) - err = ioutil.WriteFile(out, tgz, 0o600) - if err != nil { - return err - } - - var fileList2 []string - for f, l := range fileList { - fileList2 = append(fileList2, fmt.Sprintf("%s: %d", f, l)) - } - - fileListStr := strings.Join(fileList2, "\n") - fileListStr = hex.EncodeToString(hash[:]) + "\n" + fileListStr - err = ioutil.WriteFile(out+".files", []byte(fileListStr), 0o600) - return err -} - -func findFiles(dir string, patterns []string) (map[string]int64, error) { - var globs []glob.Glob - for _, p := range patterns { - globs = append(globs, glob.MustCompile(p, '/')) - } - - var rootNames []string - err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { - rel, err := filepath.Rel(dir, path) - if err != nil { - return err - } - match := false - for _, p := range globs { - if p.Match(filepath.ToSlash(rel)) { - match = true - break - } - } - if match { - rootNames = append(rootNames, rel) - } - return nil - }) - if err != nil { - return nil, err - } - - excludes := []glob.Glob{ - glob.MustCompile("__pycache__"), - glob.MustCompile("**/__pycache__"), - glob.MustCompile("**.a"), - glob.MustCompile("**.pdb"), - glob.MustCompile("**.pyc"), - } - - fileList := make(map[string]int64) - for _, d := range rootNames { - err = filepath.Walk(filepath.Join(dir, d), func(path string, info fs.FileInfo, err error) error { - if !info.Mode().IsRegular() && info.Mode().Type() != fs.ModeSymlink { - return nil - } - - relPath, err := filepath.Rel(dir, path) - if err != nil { - return err - } - for _, e := range excludes { - if e.Match(relPath) { - return nil - } - } - fileList[relPath] = info.Size() - return nil - }) - if err != nil { - return nil, err - } - } - return fileList, err -} - -func writeTar(dir string, fileList map[string]int64) ([]byte, error) { - b := bytes.NewBuffer(nil) - gz := gzip.NewWriter(b) - t := tar.NewWriter(gz) - - for f, _ := range fileList { - err := utils.AddToTar(t, filepath.Join(dir, f), f, nil) - if err != nil { - return nil, err - } - } - - err := t.Close() - if err != nil { - return nil, err - } - err = gz.Close() - if err != nil { - return nil, err - } - - return b.Bytes(), nil -} diff --git a/pkg/utils/tar.go b/pkg/utils/tar.go deleted file mode 100644 index 0425dc6e6..000000000 --- a/pkg/utils/tar.go +++ /dev/null @@ -1,190 +0,0 @@ -package utils - -import ( - "archive/tar" - "compress/gzip" - "fmt" - securejoin "github.com/cyphar/filepath-securejoin" - "io" - "io/fs" - "io/ioutil" - "os" - "path/filepath" -) - -func ExtractTarGzFile(tarGzPath string, targetPath string) error { - f, err := os.Open(tarGzPath) - if err != nil { - return fmt.Errorf("archive %v could not be opened: %w", tarGzPath, err) - } - defer f.Close() - - err = ExtractTarGzStream(f, targetPath) - if err != nil { - return fmt.Errorf("archive %v could not be extracted: %w", tarGzPath, err) - } - return nil -} - -func ExtractTarGzStream(r io.Reader, targetPath string) error { - gz, err := gzip.NewReader(r) - if err != nil { - return err - } - defer gz.Close() - - return ExtractTarStream(gz, targetPath) -} - -func ExtractTarStream(r io.Reader, targetPath string) error { - tarReader := tar.NewReader(r) - for true { - header, err := tarReader.Next() - if err == io.EOF { - break - } - - if err != nil { - return fmt.Errorf("ExtractTarStream: Next() failed: %w", err) - } - - header.Name = filepath.FromSlash(header.Name) - - p, err := securejoin.SecureJoin(targetPath, header.Name) - if err != nil { - return err - } - err = os.MkdirAll(filepath.Dir(p), 0755) - if err != nil { - return err - } - - switch header.Typeflag { - case tar.TypeDir: - if err := os.MkdirAll(p, 0755); err != nil { - return fmt.Errorf("ExtractTarStream: Mkdir() failed: %w", err) - } - case tar.TypeReg: - outFile, err := os.Create(p) - if err != nil { - return fmt.Errorf("ExtractTarStream: Create() failed: %w", err) - } - _, err = io.Copy(outFile, tarReader) - _ = outFile.Close() - if err != nil { - return fmt.Errorf("ExtractTarStream: Copy() failed: %w", err) - } - err = os.Chmod(p, header.FileInfo().Mode()) - if err != nil { - return fmt.Errorf("ExtractTarStream: Chmod() failed: %w", err) - } - case tar.TypeSymlink: - if err := os.Symlink(header.Linkname, p); err != nil { - return fmt.Errorf("ExtractTarStream: Symlink() failed: %w", err) - } - default: - return fmt.Errorf("ExtractTarStream: uknown type %v in %v", header.Typeflag, header.Name) - } - } - return nil -} - -func AddToTar(tw *tar.Writer, pth string, name string, filter func(h *tar.Header, size int64) (*tar.Header, error)) error { - fi, err := os.Lstat(pth) - if err != nil { - return err - } - - var linkName string - if fi.Mode().Type() == fs.ModeSymlink { - x, err := os.Readlink(pth) - if err != nil { - return err - } - linkName = x - } - - h, err := tar.FileInfoHeader(fi, linkName) - if err != nil { - return err - } - h.Name = filepath.ToSlash(name) - - if filter != nil { - s := fi.Size() - if fi.IsDir() { - s = 0 - } - h, err = filter(h, s) - if err != nil { - return err - } - if h == nil { - return nil - } - } - - err = tw.WriteHeader(h) - if err != nil { - return err - } - - if fi.Mode().Type() == fs.ModeSymlink { - return nil - } - - if fi.Mode().IsDir() { - des, err := os.ReadDir(pth) - if err != nil { - return err - } - for _, d := range des { - err = AddToTar(tw, filepath.Join(pth, d.Name()), filepath.Join(name, d.Name()), filter) - if err != nil { - return err - } - } - return nil - } else if fi.Mode().IsRegular() { - f, err := os.Open(pth) - if err != nil { - return err - } - defer f.Close() - _, err = io.Copy(tw, f) - if err != nil { - return err - } - return nil - } else { - return fmt.Errorf("unsupported file type/mode %s", fi.Mode().String()) - } -} - -func HashTarEntry(dir string, name string) (string, error) { - p := filepath.Join(dir, filepath.FromSlash(name)) - st, err := os.Lstat(p) - if err != nil { - return "", err - } - var hashData []byte - if st.Mode().Type() == fs.ModeDir { - hashData = []byte(filepath.ToSlash(name)) - } else if st.Mode().Type() == fs.ModeSymlink { - l, err := os.Readlink(p) - if err != nil { - return "", err - } - hashData = []byte(l) - } else if st.Mode().IsRegular() { - var err error - hashData, err = ioutil.ReadFile(p) - if err != nil { - return "", err - } - } else { - return "", fmt.Errorf("unknown type %s", st.Mode().Type()) - } - hashStr := Sha256Bytes(hashData) - return hashStr, nil -} From 7904b73ca9ef92d1bf941fbb11627ea365c17012 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 18 Aug 2022 17:46:24 +0200 Subject: [PATCH 0322/2268] chore: Upgrade to go 1.19 --- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 2 +- go.mod | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 11588e3a3..9c7982a03 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.18.1 + go-version: 1.19 - name: Set up Python uses: actions/setup-python@v2 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 509758756..26e089c85 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-go@v2 with: - go-version: '1.18.1' + go-version: '1.19' - name: Set up Python uses: actions/setup-python@v2 with: diff --git a/go.mod b/go.mod index 4364baad0..deb6c1c5e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/kluctl/kluctl/v2 -go 1.18 +go 1.19 require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e From 6524f91adee683f02a01e536e9c18a6957523b77 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 Aug 2022 08:59:15 +0200 Subject: [PATCH 0323/2268] chore: Run go get -u ./... --- go.mod | 74 +++++++++--------- go.sum | 239 ++++++++++++++++++++------------------------------------- 2 files changed, 119 insertions(+), 194 deletions(-) diff --git a/go.mod b/go.mod index deb6c1c5e..df2bf20cd 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 - github.com/aws/aws-sdk-go v1.44.68 + github.com/aws/aws-sdk-go v1.44.80 github.com/bitnami-labs/sealed-secrets v0.18.1 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible @@ -21,11 +21,11 @@ require ( github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/kluctl/kluctl-python-deps v0.0.0-20220818145819-a2811c3f89fd - github.com/mattn/go-isatty v0.0.14 + github.com/kluctl/kluctl-python-deps v0.0.0-20220819065415-624185ed2c0a + github.com/mattn/go-isatty v0.0.16 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.14.3 + github.com/ohler55/ojg v1.14.4 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.8.1 @@ -33,23 +33,23 @@ require ( github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.12.0 - github.com/stretchr/testify v1.7.2 + github.com/stretchr/testify v1.8.0 github.com/vbauerster/mpb/v7 v7.4.2 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.1 - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa - golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 - golang.org/x/sys v0.0.0-20220731174439-a90be440212d + golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 + golang.org/x/net v0.0.0-20220812174116-3211cb980234 + golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde + golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 golang.org/x/text v0.3.7 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.9.2 - k8s.io/api v0.24.3 - k8s.io/apiextensions-apiserver v0.24.3 - k8s.io/apimachinery v0.24.3 - k8s.io/client-go v0.24.3 + helm.sh/helm/v3 v3.9.3 + k8s.io/api v0.24.4 + k8s.io/apiextensions-apiserver v0.24.4 + k8s.io/apimachinery v0.24.4 + k8s.io/client-go v0.24.4 k8s.io/klog/v2 v2.70.1 sigs.k8s.io/kind v0.14.0 sigs.k8s.io/kustomize/api v0.12.1 @@ -58,7 +58,7 @@ require ( ) require ( - cloud.google.com/go/compute v1.7.0 // indirect + cloud.google.com/go/compute v1.9.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.28 // indirect @@ -72,7 +72,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20220730123233-d6ffb7692adf // indirect + github.com/ProtonMail/go-crypto v0.0.0-20220812175011-7fcef0dbe794 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.3 // indirect @@ -85,7 +85,7 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect github.com/cloudflare/circl v1.2.0 // indirect - github.com/containerd/containerd v1.6.6 // indirect + github.com/containerd/containerd v1.6.8 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v20.10.17+incompatible // indirect github.com/docker/docker v20.10.17+incompatible // indirect @@ -106,7 +106,7 @@ require ( github.com/go-logr/logr v1.2.3 // 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/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -126,11 +126,11 @@ require ( github.com/hashicorp/go-hclog v1.2.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.4 // indirect + github.com/hashicorp/go-plugin v1.4.5 // indirect github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect @@ -140,7 +140,7 @@ require ( github.com/hashicorp/vault/sdk v0.5.3 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect @@ -154,7 +154,7 @@ require ( github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -164,7 +164,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect + github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect @@ -174,15 +174,15 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.3 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.2 // indirect + github.com/prometheus/client_golang v1.13.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/rivo/uniseg v0.3.1 // indirect + github.com/rivo/uniseg v0.3.4 // indirect github.com/rubenv/sql-migrate v1.1.2 // indirect github.com/russross/blackfriday v1.6.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect @@ -197,24 +197,24 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect - go.starlark.net v0.0.0-20220714194419-4cadf0a12139 // indirect - go.uber.org/atomic v1.9.0 // indirect - golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c // indirect + go.starlark.net v0.0.0-20220817180228-f738f5508c12 // indirect + go.uber.org/atomic v1.10.0 // indirect + golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 // indirect golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 // indirect + google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1 // indirect google.golang.org/grpc v1.48.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.6 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiserver v0.24.3 // indirect - k8s.io/cli-runtime v0.24.3 // indirect - k8s.io/component-base v0.24.3 // indirect - k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect - k8s.io/kubectl v0.24.3 // indirect - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + k8s.io/apiserver v0.24.4 // indirect + k8s.io/cli-runtime v0.24.4 // indirect + k8s.io/component-base v0.24.4 // indirect + k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea // indirect + k8s.io/kubectl v0.24.4 // indirect + k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 // indirect oras.land/oras-go v1.2.0 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index c55806e56..ed9d54a25 100644 --- a/go.sum +++ b/go.sum @@ -28,29 +28,18 @@ cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSU cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= 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 v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.9.0 h1:ED/FP4xv8GJw63v556/ASNc1CeeLUO2Bs8nzaHchkHg= +cloud.google.com/go/compute v1.9.0/go.mod h1:lWv1h/zUWTm/LozzfTJhBSkd6ShQq8la8VeeuOEGxfY= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= 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= @@ -63,7 +52,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl 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= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo= @@ -120,14 +108,14 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo= +github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 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/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20220730123233-d6ffb7692adf h1:aFFtnGZ6/2Qlvx80yxA2fFSYDQWTFjtKozQKB36A3/A= -github.com/ProtonMail/go-crypto v0.0.0-20220730123233-d6ffb7692adf/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= +github.com/ProtonMail/go-crypto v0.0.0-20220812175011-7fcef0dbe794 h1:efPD6snIrIBAfmZhcm7GQ72VHlzsQ/3OrghnnGEpJBM= +github.com/ProtonMail/go-crypto v0.0.0-20220812175011-7fcef0dbe794/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= @@ -172,8 +160,8 @@ github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9D github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.68 h1:7zNr5+HLG0TMq+ZcZ8KhT4eT2KyL7v+u7/jANKEIinM= -github.com/aws/aws-sdk-go v1.44.68/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.80 h1:jEXGecSgPdvM5KnyDsSgFhZSm7WwaTp4h544Im4SfhI= +github.com/aws/aws-sdk-go v1.44.80/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= 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= @@ -225,7 +213,6 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht 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-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -234,8 +221,8 @@ github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= -github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= -github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= +github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs= +github.com/containerd/containerd v1.6.8/go.mod h1:By6p5KqPK0/7/CgO/A6t/Gz+CUYUu2zf1hUaaymVXB0= github.com/containerd/stargz-snapshotter/estargz v0.12.0 h1:idtwRTLjk2erqiYhPWy2L844By8NRFYEwYHcXhoIWPM= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -308,7 +295,6 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y 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/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -396,8 +382,8 @@ github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZ 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.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -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-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -525,7 +511,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= @@ -564,16 +549,10 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= 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/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -635,8 +614,8 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ= -github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= +github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= @@ -645,8 +624,8 @@ github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5O github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ= github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= @@ -698,8 +677,9 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -755,8 +735,8 @@ github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/kluctl/kluctl-python-deps v0.0.0-20220818145819-a2811c3f89fd h1:CTUYp06sH7d38kfHY+tysa9jhQuo1C9ssSlVR1hFuDA= -github.com/kluctl/kluctl-python-deps v0.0.0-20220818145819-a2811c3f89fd/go.mod h1:w4cTz1av5JFX9bMTB7Vm2qtmm5+eU+ncvB1/oyjVwOw= +github.com/kluctl/kluctl-python-deps v0.0.0-20220819065415-624185ed2c0a h1:2W9TlV8b6sqjIKhbp9bgKjusdF3HgQucFNmwGNb1N+Y= +github.com/kluctl/kluctl-python-deps v0.0.0-20220819065415-624185ed2c0a/go.mod h1:w4cTz1av5JFX9bMTB7Vm2qtmm5+eU+ncvB1/oyjVwOw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -826,16 +806,18 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope 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 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= 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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -894,8 +876,9 @@ github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQ github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= 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= @@ -928,8 +911,8 @@ github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/ohler55/ojg v1.14.3 h1:kaKNsntZ0PuoXPXCY4kjPDbHOLXqokor6Deq/oVxAR0= -github.com/ohler55/ojg v1.14.3/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= +github.com/ohler55/ojg v1.14.4 h1:L2ds8AlB5t/QbqSfhRwvagJzQ7pgmdrefMIypQs0Xik= +github.com/ohler55/ojg v1.14.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -970,8 +953,8 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko 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.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= -github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= +github.com/pelletier/go-toml/v2 v2.0.3 h1:h9JoA60e1dVEOpp0PFwJSmt1Htu057NUq9/bUwaO61s= +github.com/pelletier/go-toml/v2 v2.0.3/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= @@ -1002,8 +985,8 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= +github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= 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= @@ -1043,8 +1026,8 @@ github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:r github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk= -github.com/rivo/uniseg v0.3.1/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= +github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1133,8 +1116,9 @@ github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRk 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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1143,8 +1127,10 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P 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.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs= github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= @@ -1251,14 +1237,14 @@ go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4 go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.starlark.net v0.0.0-20220714194419-4cadf0a12139 h1:zMemyQYZSyEdPaUFixYICrXf/0Rfnil7+jiQRf5IBZ0= -go.starlark.net v0.0.0-20220714194419-4cadf0a12139/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= +go.starlark.net v0.0.0-20220817180228-f738f5508c12 h1:xOBJXWGEDwU5xSDxH6macxO11Us0AH2fTa9rmsbbF7g= +go.starlark.net v0.0.0-20220817180228-f738f5508c12/go.mod h1:VZcBMdr3cT3PnBoWunTabuSEXwVAH+ZJ5zxfs3AdASk= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= @@ -1296,8 +1282,9 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c= +golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -1395,12 +1382,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b h1:3ogNYyK4oIQdIKzTu68hQrr4iuVxF3AxKl9Aj/eDrw0= -golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= +golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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= @@ -1419,11 +1402,8 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/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.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY= -golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw= +golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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= @@ -1436,9 +1416,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1523,30 +1502,23 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/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-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= -golang.org/x/sys v0.0.0-20220731174439-a90be440212d/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-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= +golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1680,9 +1652,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 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= @@ -1711,19 +1680,7 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= 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= @@ -1782,7 +1739,6 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -1797,33 +1753,9 @@ google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwy google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 h1:QntLWYqZeuBtJkth3m/6DLznnI0AHJr+AgJXvVh/izw= -google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1 h1:C2UVWqrgLYKrT5nh5oU6hLRm1AeEklCK5eloQA1NtFY= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1853,12 +1785,6 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -1875,7 +1801,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -1895,8 +1820,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= -gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -1926,8 +1851,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -helm.sh/helm/v3 v3.9.2 h1:bx7kdhr5VAhYoWv9bIdT1C6qWR+/7SIoPCwLx22l78g= -helm.sh/helm/v3 v3.9.2/go.mod h1:y/dJc/0Lzcn40jgd85KQXnufhFF7sr4v6L/vYMLRaRM= +helm.sh/helm/v3 v3.9.3 h1:etd4Qc45/bnIkBofZIRwrAzYuG3bNWR1EdMN4fsfzoE= +helm.sh/helm/v3 v3.9.3/go.mod h1:3eaWAIqzvlRSD06gR9MMwmp2KBKwlu9av1/1BZpjeWY= 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= @@ -1936,22 +1861,22 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt 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= honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= -k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY= -k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI= -k8s.io/apiextensions-apiserver v0.24.3 h1:kyx+Tmro1qEsTUr07ZGQOfvTsF61yn+AxnxytBWq8As= -k8s.io/apiextensions-apiserver v0.24.3/go.mod h1:cL0xkmUefpYM4f6IuOau+6NMFEIh6/7wXe/O4vPVJ8A= -k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= -k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apiserver v0.24.3 h1:J8CKjUaZopT0hSgxjzUyp3T1GK78iixxOuFpEC0MI3k= -k8s.io/apiserver v0.24.3/go.mod h1:aXfwtIn4U27B7lYs5f2BKgz6DRbgWy+HJeYReN1jLJ8= -k8s.io/cli-runtime v0.24.3 h1:O9YvUHrDSCQUPlsqVmaqDrueqjpJ7IO6Yas9B6xGSoo= -k8s.io/cli-runtime v0.24.3/go.mod h1:In84wauoMOqa7JDvDSXGbf8lTNlr70fOGpYlYfJtSqA= -k8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY= -k8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw= -k8s.io/code-generator v0.24.3/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= -k8s.io/component-base v0.24.3 h1:u99WjuHYCRJjS1xeLOx72DdRaghuDnuMgueiGMFy1ec= -k8s.io/component-base v0.24.3/go.mod h1:bqom2IWN9Lj+vwAkPNOv2TflsP1PeVDIwIN0lRthxYY= -k8s.io/component-helpers v0.24.3/go.mod h1:/1WNW8TfBOijQ1ED2uCHb4wtXYWDVNMqUll8h36iNVo= +k8s.io/api v0.24.4 h1:I5Y645gJ8zWKawyr78lVfDQkZrAViSbeRXsPZWTxmXk= +k8s.io/api v0.24.4/go.mod h1:42pVfA0NRxrtJhZQOvRSyZcJihzAdU59WBtTjYcB0/M= +k8s.io/apiextensions-apiserver v0.24.4 h1:w53Pm4zu8fCt9WfiRgS2YI6LE6I4NJ5aUi78GElD3K8= +k8s.io/apiextensions-apiserver v0.24.4/go.mod h1:iDK+Xb4jsPNnRGj5jU/WqqjLvt8363M7cKixKe1C9+U= +k8s.io/apimachinery v0.24.4 h1:S0Ur3J/PbivTcL43EdSdPhqCqKla2NIuneNwZcTDeGQ= +k8s.io/apimachinery v0.24.4/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apiserver v0.24.4 h1:ei+OunC83pVEiagBeZhTnRZvkclHgpzs/rrm7aSBDYs= +k8s.io/apiserver v0.24.4/go.mod h1:mAuC3pZVc0IDXLx7lUHoisBOtBa1SobfLW/CI3klXQE= +k8s.io/cli-runtime v0.24.4 h1:YCSf0dZp+pYXVR/8aZQ6MEBSiicv8rLyVsGBEbRnwfY= +k8s.io/cli-runtime v0.24.4/go.mod h1:RF+cSLYXkPV3WyvPrX2qeRLEUJY38INWx6jLKVLFCxM= +k8s.io/client-go v0.24.4 h1:hIAIJZIPyaw46AkxwyR0FRfM/pRxpUNTd3ysYu9vyRg= +k8s.io/client-go v0.24.4/go.mod h1:+AxlPWw/H6f+EJhRSjIeALaJT4tbeB/8g9BNvXGPd0Y= +k8s.io/code-generator v0.24.4/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= +k8s.io/component-base v0.24.4 h1:WEGRp06GBYVwxp5JdiRaJ1zkdOhrqucxRv/8IrABLG0= +k8s.io/component-base v0.24.4/go.mod h1:sWxkgcMfbYHadw0OJ0N+vIscd14/nqSIM2veCdg843o= +k8s.io/component-helpers v0.24.4/go.mod h1:xAHlOKU8rAjLgXWJEsueWLR1LDMThbaPf2YvgKpSyQ8= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -1962,15 +1887,15 @@ k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 h1:yEQKdMCjzAOvGeiTwG4hO/hNVNtDOuUFvMUZ0OlaIzs= -k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8/go.mod h1:mbJ+NSUoAhuR14N0S63bPkh8MGVSo3VYSGZtH/mfMe0= -k8s.io/kubectl v0.24.3 h1:PqY8ho/S/KuE2/hCC3Iee7X+lOtARYo0LQsNzvV/edE= -k8s.io/kubectl v0.24.3/go.mod h1:PYLcvw96sC1NLbxZEDbdlOEd6/C76VIWjGmWV5QjSk0= -k8s.io/metrics v0.24.3/go.mod h1:p1M0lhMySWfhISkSd3HEj8xIgrVnJTK3PPhFq2rA3To= +k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea h1:3QOH5+2fGsY8e1qf+GIFpg+zw/JGNrgyZRQR7/m6uWg= +k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/kubectl v0.24.4 h1:fPEBkAV3/cu3BQVIUCXNngCCY62AlZ+2rkRVHcmJPn0= +k8s.io/kubectl v0.24.4/go.mod h1:AVyJzxUwA5UMGTDyKGL6nd6RRW36FbmAdtIDMhrZtW0= +k8s.io/metrics v0.24.4/go.mod h1:7D8Xm3DGZoJaiCS8+QA2EzdMuDlq0Y8SiOPUB/1BaGU= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 h1:XmRqFcQlCy/lKRZ39j+RVpokYNroHPqV3mcBRfnhT5o= +k8s.io/utils v0.0.0-20220812165043-ad590609e2e5/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= From 09406c6ff1e06f903adae86586b8bfc3481d7587 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 Aug 2022 10:20:16 +0200 Subject: [PATCH 0324/2268] fix: Use distinct tmp dir per user This avoids conflicts when used in parallel and also avoids any possible security issues in regard to temporary files. --- pkg/utils/utils.go | 55 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 4bcd0bf70..f704bf0f6 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -3,24 +3,71 @@ package utils import ( "crypto/sha256" "encoding/hex" + "fmt" "github.com/jinzhu/copier" + "github.com/kluctl/kluctl-python-deps/pkg/utils" + "io/fs" "os" + "os/user" "path/filepath" "strconv" "sync" ) var createTmpBaseDirOnce sync.Once +var tmpBaseDir string func GetTmpBaseDir() string { - dir := filepath.Join(os.TempDir(), "kluctl-workdir") createTmpBaseDirOnce.Do(func() { - err := os.MkdirAll(dir, 0o700) + createTmpBaseDir() + }) + return tmpBaseDir +} + +func createTmpBaseDir() { + dir := filepath.Join(os.TempDir(), "kluctl-workdir") + + ensureDir(dir, 0o777, true) + + // every user gets its own tmp dir + u, err := user.Current() + if err != nil { + panic(err) + } + + dir = filepath.Join(dir, u.Uid) + ensureDir(dir, 0o700, true) + + tmpBaseDir = dir +} + +func ensureDir(path string, perm fs.FileMode, allowCreate bool) { + if utils.Exists(path) { + st, err := os.Lstat(path) if err != nil { panic(err) } - }) - return dir + if !st.IsDir() { + panic(fmt.Sprintf("%s is not a directory", path)) + } + if st.Mode().Perm() != perm { + err = os.Chmod(path, perm) + if err != nil { + panic(err) + } + } + } else if !allowCreate { + panic(fmt.Sprintf("failed to ensure directory %s", path)) + } else { + err := os.Mkdir(path, perm) + if err != nil { + if os.IsExist(err) { + ensureDir(path, perm, false) + } else { + panic(err) + } + } + } } func Sha256String(data string) string { From ecb5e10341a1125723ebdd00d206312922fee021 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 29 Aug 2022 12:11:40 +0200 Subject: [PATCH 0325/2268] fix: Properly marshal objects with yaml tags when passing vars to jinja2 --- pkg/jinja2/jinja2_renderer.go | 3 ++- pkg/yaml/yaml.go | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/pkg/jinja2/jinja2_renderer.go b/pkg/jinja2/jinja2_renderer.go index e5b64ef73..687669507 100644 --- a/pkg/jinja2/jinja2_renderer.go +++ b/pkg/jinja2/jinja2_renderer.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/kluctl/kluctl-python-deps/pkg/python" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" "io" "io/ioutil" "os" @@ -130,7 +131,7 @@ type jinja2Result struct { } func (j *pythonJinja2Renderer) renderHelper(jobs []*RenderJob, searchDirs []string, vars *uo.UnstructuredObject, isString bool, strict bool) error { - varsStr, err := json.Marshal(vars.Object) + varsStr, err := yaml.WriteJsonString(vars) if err != nil { return err } diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 6d559b530..89610c94a 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -201,6 +201,19 @@ func WriteYamlToTar(tw *tar.Writer, o interface{}, name string) error { return nil } +func WriteJsonString(o interface{}) (string, error) { + x, err := WriteYamlBytes(o) + if err != nil { + return "", err + } + + x, err = ConvertYamlToJson(x) + if err != nil { + return "", err + } + return string(x), nil +} + func ConvertYamlToJson(b []byte) ([]byte, error) { var x interface{} err := ReadYamlBytes(b, &x) From d60d77dd11e36195f16ebb97b4295e1f64a73527 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 29 Aug 2022 12:12:03 +0200 Subject: [PATCH 0326/2268] fix: Only check for replicas if replicas != 0 --- pkg/validation/validation.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 6e631470f..7a3ecb5d2 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -281,10 +281,13 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError } } case schema.GroupKind{Group: "apps", Kind: "Deployment"}: - readyReplicas := getStatusFieldInt("readyReplicas", reactNotReady, true, 0) - replicas := getStatusFieldInt("replicas", reactNotReady, true, 0) - if readyReplicas < replicas { - addNotReady(fmt.Sprintf("readyReplicas (%d) is less then replicas (%d)", readyReplicas, replicas)) + specReplicas, ok, _ := o.GetNestedInt("spec", "replicas") + if ok && specReplicas != 0 { + readyReplicas := getStatusFieldInt("readyReplicas", reactNotReady, true, 0) + replicas := getStatusFieldInt("replicas", reactNotReady, true, 0) + if readyReplicas < replicas { + addNotReady(fmt.Sprintf("readyReplicas (%d) is less then replicas (%d)", readyReplicas, replicas)) + } } case schema.GroupKind{Group: "", Kind: "PersistentVolumeClaim"}: phase := getStatusFieldStr("phase", reactNotReady, true, "") From 215585b3528929a0ce55059c1c7ca3b71ea9ea61 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 29 Aug 2022 14:41:07 +0200 Subject: [PATCH 0327/2268] refactor: Export ListRemoteRefs helper functions --- pkg/git/list_refs.go | 43 +++++++++++++++++++++++----------------- pkg/git/mirrored_repo.go | 16 ++++++++------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/pkg/git/list_refs.go b/pkg/git/list_refs.go index 2bc517fd6..d529c952c 100644 --- a/pkg/git/list_refs.go +++ b/pkg/git/list_refs.go @@ -2,33 +2,38 @@ package git import ( "bytes" + "context" "fmt" "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/protocol/packp" + "github.com/go-git/go-git/v5/storage/memory" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/status" "strconv" ) -// listRemoteRefsFastSsh will reuse existing ssh connections from a pool -func (g *MirroredGitRepo) listRemoteRefsFastSsh(r *git.Repository, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { - var portInt int64 = -1 - if g.url.Port() != "" { +// ListRemoteRefsFastSsh will reuse existing ssh connections from a pool +func ListRemoteRefsFastSsh(ctx context.Context, url git_url.GitUrl, sshPool *ssh_pool.SshPool, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { + var portInt int64 = 22 + if url.Port() != "" { var err error - portInt, err = strconv.ParseInt(g.url.Port(), 10, 32) + portInt, err = strconv.ParseInt(url.Port(), 10, 32) if err != nil { return nil, err } } - s, err := g.sshPool.GetSession(g.ctx, g.url.Hostname(), int(portInt), auth) + s, err := sshPool.GetSession(ctx, url.Hostname(), int(portInt), auth) if err != nil { return nil, err } defer s.Close() - cmd := fmt.Sprintf("git-upload-pack %s", g.url.Path) + cmd := fmt.Sprintf("git-upload-pack %s", url.Path) stdout := bytes.NewBuffer(nil) stderr := bytes.NewBuffer(nil) @@ -71,13 +76,15 @@ func (g *MirroredGitRepo) listRemoteRefsFastSsh(r *git.Repository, auth auth2.Au return resultRefs, nil } -func (g *MirroredGitRepo) listRemoteRefsSlow(r *git.Repository, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { - remote, err := r.Remote("origin") - if err != nil { - return nil, err - } +func ListRemoteRefsSlow(ctx context.Context, url git_url.GitUrl, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { + storage := memory.NewStorage() + remote := git.NewRemote(storage, &config.RemoteConfig{ + Name: "origin", + URLs: []string{url.String()}, + Fetch: defaultFetch, + }) - remoteRefs, err := remote.ListContext(g.ctx, &git.ListOptions{ + remoteRefs, err := remote.ListContext(ctx, &git.ListOptions{ Auth: auth.AuthMethod, CABundle: auth.CABundle, }) @@ -87,13 +94,13 @@ func (g *MirroredGitRepo) listRemoteRefsSlow(r *git.Repository, auth auth2.AuthM return remoteRefs, nil } -func (g *MirroredGitRepo) listRemoteRefs(r *git.Repository, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { - if g.url.IsSsh() { - refs, err := g.listRemoteRefsFastSsh(r, auth) +func ListRemoteRefs(ctx context.Context, url git_url.GitUrl, sshPool *ssh_pool.SshPool, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { + if url.IsSsh() { + refs, err := ListRemoteRefsFastSsh(ctx, url, sshPool, auth) if err == nil { return refs, nil } - status.Warning(g.ctx, "Fast listing of remote refs failed: %s", err.Error()) + status.Warning(ctx, "Fast listing of remote refs failed: %s", err.Error()) } - return g.listRemoteRefsSlow(r, auth) + return ListRemoteRefsSlow(ctx, url, auth) } diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 3c3a271c1..00eb928c6 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -24,6 +24,11 @@ import ( var cacheBaseDir = filepath.Join(utils.GetTmpBaseDir(), "git-cache") +var defaultFetch = []config.RefSpec{ + "+refs/heads/*:refs/heads/*", + "+refs/tags/*:refs/tags/*", +} + type MirroredGitRepo struct { ctx context.Context @@ -200,7 +205,7 @@ func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string) error auth := g.authProviders.BuildAuth(g.ctx, g.url) - remoteRefs, err := g.listRemoteRefs(r, auth) + remoteRefs, err := ListRemoteRefs(g.ctx, g.url, g.sshPool, auth) if err != nil { return err } @@ -302,12 +307,9 @@ func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext) error { } _, err = repo.CreateRemote(&config.RemoteConfig{ - Name: "origin", - URLs: []string{g.url.String()}, - Fetch: []config.RefSpec{ - "+refs/heads/*:refs/heads/*", - "+refs/tags/*:refs/tags/*", - }, + Name: "origin", + URLs: []string{g.url.String()}, + Fetch: defaultFetch, }) if err != nil { return err From 317da3aeac29300e5b14543a13f24efc3b043528 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 1 Sep 2022 15:18:02 +0200 Subject: [PATCH 0328/2268] refactor: Allow to trim/lstrip blocks in Jinja2 --- pkg/jinja2/jinja2.go | 19 ++++++++++++++++++- pkg/jinja2/jinja2_renderer.go | 8 ++++++++ pkg/jinja2/python_src/jinja2_renderer.py | 6 +++++- pkg/jinja2/python_src/main.py | 4 ++-- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go index 2d433ec1a..62df03c34 100644 --- a/pkg/jinja2/jinja2.go +++ b/pkg/jinja2/jinja2.go @@ -35,11 +35,25 @@ type Jinja2Error struct { error string } +type Jinja2Opt func(j2 *pythonJinja2Renderer) + +func WithTrimBlocks(trimBlocks bool) Jinja2Opt { + return func(j2 *pythonJinja2Renderer) { + j2.trimBlocks = trimBlocks + } +} + +func WithLStripBlocks(lstripBlocks bool) Jinja2Opt { + return func(j2 *pythonJinja2Renderer) { + j2.lstripBlocks = lstripBlocks + } +} + func (m *Jinja2Error) Error() string { return m.error } -func NewJinja2() (*Jinja2, error) { +func NewJinja2(opts ...Jinja2Opt) (*Jinja2, error) { var wg sync.WaitGroup var mutex sync.Mutex var err error @@ -61,6 +75,9 @@ func NewJinja2() (*Jinja2, error) { err = err2 return } + for _, o := range opts { + o(pj) + } j.pj <- pj }() } diff --git a/pkg/jinja2/jinja2_renderer.go b/pkg/jinja2/jinja2_renderer.go index 687669507..bcbd36edb 100644 --- a/pkg/jinja2/jinja2_renderer.go +++ b/pkg/jinja2/jinja2_renderer.go @@ -23,6 +23,9 @@ type pythonJinja2Renderer struct { stdout io.ReadCloser stdoutReader *bufio.Reader + + trimBlocks bool + lstripBlocks bool } func newPythonJinja2Renderer() (*pythonJinja2Renderer, error) { @@ -123,6 +126,9 @@ type jinja2Args struct { SearchDirs []string `json:"searchDirs"` Vars string `json:"vars"` Strict bool `json:"strict"` + + TrimBlocks bool `json:"trimBlocks"` + LStripBlocks bool `json:"lstripBlocks"` } type jinja2Result struct { @@ -147,6 +153,8 @@ func (j *pythonJinja2Renderer) renderHelper(jobs []*RenderJob, searchDirs []stri jargs.Vars = string(varsStr) jargs.SearchDirs = searchDirs jargs.Strict = strict + jargs.TrimBlocks = j.trimBlocks + jargs.LStripBlocks = j.lstripBlocks for _, job := range jobs { if ist, r := isMaybeTemplate(job.Template, searchDirs, isString); !ist { diff --git a/pkg/jinja2/python_src/jinja2_renderer.py b/pkg/jinja2/python_src/jinja2_renderer.py index 52e868b30..fbc1a1364 100644 --- a/pkg/jinja2/python_src/jinja2_renderer.py +++ b/pkg/jinja2/python_src/jinja2_renderer.py @@ -27,6 +27,9 @@ def _return_self(self, other): __pow__ = __rpow__ = _return_self class Jinja2Renderer: + def __init__(self, trim_blocks=False, lstrip_blocks=False): + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks def get_image_wrapper(self, image, latest_version=None): if latest_version is None: @@ -74,7 +77,8 @@ def build_env(self, vars_str, search_dirs, strict): environment = KluctlJinja2Environment(loader=FileSystemLoader(search_dirs), undefined=StrictUndefined if strict else NullUndefined, cache_size=10000, - auto_reload=False) + auto_reload=False, + trim_blocks=self.trim_blocks, lstrip_blocks=self.lstrip_blocks) merge_dict(environment.globals, vars, clone=False) add_jinja2_filters(environment) diff --git a/pkg/jinja2/python_src/main.py b/pkg/jinja2/python_src/main.py index 4f35ec9b7..15916d2c3 100644 --- a/pkg/jinja2/python_src/main.py +++ b/pkg/jinja2/python_src/main.py @@ -5,14 +5,14 @@ def main(): - r = Jinja2Renderer() - while True: args = sys.stdin.readline() if not args: break args = json.loads(args) + r = Jinja2Renderer(args.get("trimBlocks", False), args.get("lstripBlocks", False)) + if args["cmd"] == "render-strings": result = r.RenderStrings(args["templates"], args["searchDirs"] or [], args["vars"], args["strict"]) elif args["cmd"] == "render-files": From a0713fbde6841fb692b953189c44de8a7503a726 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 1 Sep 2022 16:26:00 +0200 Subject: [PATCH 0329/2268] chore: Run go get -u ./... --- go.mod | 58 ++++++++++++++++++++++++++-------------------------- go.sum | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index df2bf20cd..eb85f2f1c 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,12 @@ go 1.19 require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 - github.com/aws/aws-sdk-go v1.44.80 - github.com/bitnami-labs/sealed-secrets v0.18.1 + github.com/aws/aws-sdk-go v1.44.89 + github.com/bitnami-labs/sealed-secrets v0.18.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/evanphx/json-patch v5.6.0+incompatible - github.com/fluxcd/pkg/kustomize v0.5.3 + github.com/fluxcd/pkg/kustomize v0.7.0 github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.0 github.com/gobwas/glob v0.2.3 @@ -21,35 +21,35 @@ require ( github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/kluctl/kluctl-python-deps v0.0.0-20220819065415-624185ed2c0a + github.com/kluctl/kluctl-python-deps v0.0.0-20220819072433-e8107cc5ae41 github.com/mattn/go-isatty v0.0.16 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 github.com/ohler55/ojg v1.14.4 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 - github.com/rogpeppe/go-internal v1.8.1 + github.com/rogpeppe/go-internal v1.9.0 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.12.0 github.com/stretchr/testify v1.8.0 - github.com/vbauerster/mpb/v7 v7.4.2 + github.com/vbauerster/mpb/v7 v7.5.2 github.com/whilp/git-urls v1.0.0 - github.com/xanzy/ssh-agent v0.3.1 - golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 - golang.org/x/net v0.0.0-20220812174116-3211cb980234 + github.com/xanzy/ssh-agent v0.3.2 + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 + golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde - golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 + golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 golang.org/x/text v0.3.7 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.9.3 - k8s.io/api v0.24.4 - k8s.io/apiextensions-apiserver v0.24.4 - k8s.io/apimachinery v0.24.4 - k8s.io/client-go v0.24.4 + helm.sh/helm/v3 v3.9.4 + k8s.io/api v0.25.0 + k8s.io/apiextensions-apiserver v0.25.0 + k8s.io/apimachinery v0.25.0 + k8s.io/client-go v0.25.0 k8s.io/klog/v2 v2.70.1 sigs.k8s.io/kind v0.14.0 sigs.k8s.io/kustomize/api v0.12.1 @@ -72,7 +72,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20220812175011-7fcef0dbe794 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.3 // indirect @@ -83,7 +83,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.2.0 // indirect github.com/containerd/containerd v1.6.8 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -92,7 +92,7 @@ require ( github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect - github.com/docker/go-units v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect @@ -123,7 +123,7 @@ require ( github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.2.2 // indirect + github.com/hashicorp/go-hclog v1.3.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.4.5 // indirect @@ -174,7 +174,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.3 // indirect + github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -191,7 +191,7 @@ require ( github.com/spf13/afero v1.9.2 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/subosito/gotenv v1.4.0 // indirect + github.com/subosito/gotenv v1.4.1 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -199,22 +199,22 @@ require ( github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20220817180228-f738f5508c12 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 // indirect + golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 // indirect golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1 // indirect - google.golang.org/grpc v1.48.0 // indirect + google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf // indirect + google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiserver v0.24.4 // indirect - k8s.io/cli-runtime v0.24.4 // indirect - k8s.io/component-base v0.24.4 // indirect + k8s.io/apiserver v0.25.0 // indirect + k8s.io/cli-runtime v0.25.0 // indirect + k8s.io/component-base v0.25.0 // indirect k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea // indirect - k8s.io/kubectl v0.24.4 // indirect - k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 // indirect + k8s.io/kubectl v0.25.0 // indirect + k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 // indirect oras.land/oras-go v1.2.0 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index ed9d54a25..301f77c4e 100644 --- a/go.sum +++ b/go.sum @@ -94,10 +94,12 @@ github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy86 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= @@ -116,6 +118,8 @@ github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmU github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220812175011-7fcef0dbe794 h1:efPD6snIrIBAfmZhcm7GQ72VHlzsQ/3OrghnnGEpJBM= github.com/ProtonMail/go-crypto v0.0.0-20220812175011-7fcef0dbe794/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= +github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErIPzRrnogAXYwSoU7txA977LjDGrbkewJbg= +github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= @@ -162,6 +166,8 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.44.80 h1:jEXGecSgPdvM5KnyDsSgFhZSm7WwaTp4h544Im4SfhI= github.com/aws/aws-sdk-go v1.44.80/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.89 h1:Xf5Pp9GsNSMRinAuWNiQd0vusXXb3IgYbNlxldhWS2Q= +github.com/aws/aws-sdk-go v1.44.89/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= 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= @@ -171,6 +177,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitnami-labs/sealed-secrets v0.18.1 h1:xXzi0Z6lArTykRGqCOC2rN3zN648zjQtkCzAVjJj9OY= github.com/bitnami-labs/sealed-secrets v0.18.1/go.mod h1:pOMGS1imRiIPLm7OpdD/s/OfgQmLkKxTqWruSEiQqCM= +github.com/bitnami-labs/sealed-secrets v0.18.2 h1:CfZD0JmhAPOQ7YqxrxZ90/+BjqTheWztBOYploqO6nc= +github.com/bitnami-labs/sealed-secrets v0.18.2/go.mod h1:wlhaZ+SI5aJkpq9CyyP0pVLJEj4LPXLzLHb2TnID/Rc= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= @@ -191,12 +199,15 @@ github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4r github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -275,6 +286,8 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -322,6 +335,8 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fluxcd/pkg/kustomize v0.5.3 h1:WpxNOV/Yklp0p7Qv85VwBegq9fABuLR9qSWaAVa3+yc= github.com/fluxcd/pkg/kustomize v0.5.3/go.mod h1:zy1FLxkEDADUykCnrXqq6rVN48t3uMhAb3ao+zv0rFE= +github.com/fluxcd/pkg/kustomize v0.7.0 h1:604rlpRZTWaOfzDZ1W93aHaFh9kn8/UMX/wzsjwIUQY= +github.com/fluxcd/pkg/kustomize v0.7.0/go.mod h1:zJY3Z0+SX+zs+/A1F6fCT0JvUce265XnrpTtHnujXPo= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -606,6 +621,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.3.0 h1:G0ACM8Z2WilWgPv3Vdzwm3V0BQu/kSmrkVtpe1fy9do= +github.com/hashicorp/go-hclog v1.3.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -737,6 +754,8 @@ github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQan github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/kluctl/kluctl-python-deps v0.0.0-20220819065415-624185ed2c0a h1:2W9TlV8b6sqjIKhbp9bgKjusdF3HgQucFNmwGNb1N+Y= github.com/kluctl/kluctl-python-deps v0.0.0-20220819065415-624185ed2c0a/go.mod h1:w4cTz1av5JFX9bMTB7Vm2qtmm5+eU+ncvB1/oyjVwOw= +github.com/kluctl/kluctl-python-deps v0.0.0-20220819072433-e8107cc5ae41 h1:sMQz3y4XtYS4YjAil3B8pxuhjPEObt0xMPMwmPZC9O0= +github.com/kluctl/kluctl-python-deps v0.0.0-20220819072433-e8107cc5ae41/go.mod h1:w4cTz1av5JFX9bMTB7Vm2qtmm5+eU+ncvB1/oyjVwOw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -934,6 +953,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+czc/J8SlhPKLOtVLMQc+xDCFBT73ZStMsRhSsUhsSg= @@ -955,6 +975,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.3 h1:h9JoA60e1dVEOpp0PFwJSmt1Htu057NUq9/bUwaO61s= github.com/pelletier/go-toml/v2 v2.0.3/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= @@ -1036,6 +1058,8 @@ github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36tZeQ= github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= @@ -1134,6 +1158,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs= github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= +github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= +github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= @@ -1163,6 +1189,8 @@ github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7Fw github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= github.com/vbauerster/mpb/v7 v7.4.2 h1:n917F4d8EWdUKc9c81wFkksyG6P6Mg7IETfKCE1Xqng= github.com/vbauerster/mpb/v7 v7.4.2/go.mod h1:UmOiIUI8aPqWXIps0ciik3RKMdzx7+ooQpq+fBcXwBA= +github.com/vbauerster/mpb/v7 v7.5.2 h1:Ph3JvpBcoIwzIG1QwbUq97KQifrTRbKcMXN9rN5BYAs= +github.com/vbauerster/mpb/v7 v7.5.2/go.mod h1:UmOiIUI8aPqWXIps0ciik3RKMdzx7+ooQpq+fBcXwBA= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -1171,6 +1199,8 @@ github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgw github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= +github.com/xanzy/ssh-agent v0.3.2 h1:eKj4SX2Fe7mui28ZgnFW5fmTz1EIr7ugo5s6wDxdHBM= +github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1282,9 +1312,12 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c= golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -1384,6 +1417,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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= @@ -1404,6 +1439,8 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw= golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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= @@ -1519,6 +1556,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1756,6 +1795,8 @@ google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1 h1:C2UVWqrgLYKrT5nh5oU6hLRm1AeEklCK5eloQA1NtFY= google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf h1:Q5xNKbTSFwkuaaGaR7CMcXEM5sy19KYdUU8iF8/iRC0= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1787,6 +1828,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1853,6 +1896,8 @@ gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= helm.sh/helm/v3 v3.9.3 h1:etd4Qc45/bnIkBofZIRwrAzYuG3bNWR1EdMN4fsfzoE= helm.sh/helm/v3 v3.9.3/go.mod h1:3eaWAIqzvlRSD06gR9MMwmp2KBKwlu9av1/1BZpjeWY= +helm.sh/helm/v3 v3.9.4 h1:TCI1QhJUeLVOdccfdw+vnSEO3Td6gNqibptB04QtExY= +helm.sh/helm/v3 v3.9.4/go.mod h1:3eaWAIqzvlRSD06gR9MMwmp2KBKwlu9av1/1BZpjeWY= 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= @@ -1863,23 +1908,38 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= k8s.io/api v0.24.4 h1:I5Y645gJ8zWKawyr78lVfDQkZrAViSbeRXsPZWTxmXk= k8s.io/api v0.24.4/go.mod h1:42pVfA0NRxrtJhZQOvRSyZcJihzAdU59WBtTjYcB0/M= +k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= +k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= k8s.io/apiextensions-apiserver v0.24.4 h1:w53Pm4zu8fCt9WfiRgS2YI6LE6I4NJ5aUi78GElD3K8= k8s.io/apiextensions-apiserver v0.24.4/go.mod h1:iDK+Xb4jsPNnRGj5jU/WqqjLvt8363M7cKixKe1C9+U= +k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= +k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= k8s.io/apimachinery v0.24.4 h1:S0Ur3J/PbivTcL43EdSdPhqCqKla2NIuneNwZcTDeGQ= k8s.io/apimachinery v0.24.4/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= +k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= k8s.io/apiserver v0.24.4 h1:ei+OunC83pVEiagBeZhTnRZvkclHgpzs/rrm7aSBDYs= k8s.io/apiserver v0.24.4/go.mod h1:mAuC3pZVc0IDXLx7lUHoisBOtBa1SobfLW/CI3klXQE= +k8s.io/apiserver v0.25.0 h1:8kl2ifbNffD440MyvHtPaIz1mw4mGKVgWqM0nL+oyu4= +k8s.io/apiserver v0.25.0/go.mod h1:BKwsE+PTC+aZK+6OJQDPr0v6uS91/HWxX7evElAH6xo= k8s.io/cli-runtime v0.24.4 h1:YCSf0dZp+pYXVR/8aZQ6MEBSiicv8rLyVsGBEbRnwfY= k8s.io/cli-runtime v0.24.4/go.mod h1:RF+cSLYXkPV3WyvPrX2qeRLEUJY38INWx6jLKVLFCxM= +k8s.io/cli-runtime v0.25.0 h1:XBnTc2Fi+w818jcJGzhiJKQuXl8479sZ4FhtV5hVJ1Q= +k8s.io/cli-runtime v0.25.0/go.mod h1:bHOI5ZZInRHhbq12OdUiYZQN8ml8aKZLwQgt9QlLINw= k8s.io/client-go v0.24.4 h1:hIAIJZIPyaw46AkxwyR0FRfM/pRxpUNTd3ysYu9vyRg= k8s.io/client-go v0.24.4/go.mod h1:+AxlPWw/H6f+EJhRSjIeALaJT4tbeB/8g9BNvXGPd0Y= +k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= +k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= k8s.io/code-generator v0.24.4/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= k8s.io/component-base v0.24.4 h1:WEGRp06GBYVwxp5JdiRaJ1zkdOhrqucxRv/8IrABLG0= k8s.io/component-base v0.24.4/go.mod h1:sWxkgcMfbYHadw0OJ0N+vIscd14/nqSIM2veCdg843o= +k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= +k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= k8s.io/component-helpers v0.24.4/go.mod h1:xAHlOKU8rAjLgXWJEsueWLR1LDMThbaPf2YvgKpSyQ8= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= @@ -1891,11 +1951,15 @@ k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea h1:3QOH5+2fGsY8e1qf+GIFpg k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/kubectl v0.24.4 h1:fPEBkAV3/cu3BQVIUCXNngCCY62AlZ+2rkRVHcmJPn0= k8s.io/kubectl v0.24.4/go.mod h1:AVyJzxUwA5UMGTDyKGL6nd6RRW36FbmAdtIDMhrZtW0= +k8s.io/kubectl v0.25.0 h1:/Wn1cFqo8ik3iee1EvpxYre3bkWsGLXzLQI6uCCAkQc= +k8s.io/kubectl v0.25.0/go.mod h1:n16ULWsOl2jmQpzt2o7Dud1t4o0+Y186ICb4O+GwKAU= k8s.io/metrics v0.24.4/go.mod h1:7D8Xm3DGZoJaiCS8+QA2EzdMuDlq0Y8SiOPUB/1BaGU= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 h1:XmRqFcQlCy/lKRZ39j+RVpokYNroHPqV3mcBRfnhT5o= k8s.io/utils v0.0.0-20220812165043-ad590609e2e5/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= +k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= From d0e639c4ec6f6882d018b86d2ca501d61110bedf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Sep 2022 11:26:28 +0200 Subject: [PATCH 0330/2268] fix: Fix crash when secrets file has no "secrets" root --- pkg/vars/vars_loader.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 5afb09788..49ffe3e9b 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -108,6 +108,12 @@ func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, searchDirs []string } if rootKey != "" { newVars, _, err = newVars.GetNestedObject(rootKey) + if err != nil { + return err + } + if newVars == nil { + return fmt.Errorf("vars from %s have no '%s' root", path, rootKey) + } } v.mergeVars(varsCtx, newVars, rootKey) return nil @@ -146,7 +152,7 @@ func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsS if err != nil { return err } - return v.loadFromString(varsCtx, secret, rootKey) + return v.loadFromString(varsCtx, secret, "awsSecretsManager", rootKey) } func (v *VarsLoader) loadVault(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { @@ -154,7 +160,7 @@ func (v *VarsLoader) loadVault(varsCtx *VarsCtx, source *types.VarsSource, rootK if err != nil { return err } - return v.loadFromString(varsCtx, secret, rootKey) + return v.loadFromString(varsCtx, secret, "vault", rootKey) } func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, rootKey string) error { @@ -178,7 +184,7 @@ func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, roo return err } - return v.loadFromString(varsCtx, string(f), rootKey) + return v.loadFromString(varsCtx, string(f), "git", rootKey) } func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, key string, rootKey string, base64Decode bool) error { @@ -237,14 +243,14 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo } } - err = v.loadFromString(varsCtx, value, rootKey) + err = v.loadFromString(varsCtx, value, "k8s", rootKey) if err != nil { return fmt.Errorf("failed to load vars from kubernetes object %s and key %s: %w", ref.String(), key, err) } return nil } -func (v *VarsLoader) loadFromString(varsCtx *VarsCtx, s string, rootKey string) error { +func (v *VarsLoader) loadFromString(varsCtx *VarsCtx, s string, secretType string, rootKey string) error { newVars := uo.New() err := v.renderYamlString(varsCtx, s, newVars) if err != nil { @@ -253,6 +259,12 @@ func (v *VarsLoader) loadFromString(varsCtx *VarsCtx, s string, rootKey string) if rootKey != "" { newVars, _, err = newVars.GetNestedObject(rootKey) + if err != nil { + return err + } + if newVars == nil { + return fmt.Errorf("%s secret has no '%s' root", secretType, rootKey) + } } v.mergeVars(varsCtx, newVars, rootKey) From 9bf311f63a29d16825b96a2f7d1b518398159ec5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Sep 2022 11:32:39 +0200 Subject: [PATCH 0331/2268] chore: Run go mod tidy --- go.sum | 227 --------------------------------------------------------- 1 file changed, 227 deletions(-) diff --git a/go.sum b/go.sum index 301f77c4e..d09bfb382 100644 --- a/go.sum +++ b/go.sum @@ -60,10 +60,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= @@ -87,19 +85,16 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= @@ -107,21 +102,14 @@ github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvd github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -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/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20220812175011-7fcef0dbe794 h1:efPD6snIrIBAfmZhcm7GQ72VHlzsQ/3OrghnnGEpJBM= -github.com/ProtonMail/go-crypto v0.0.0-20220812175011-7fcef0dbe794/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErIPzRrnogAXYwSoU7txA977LjDGrbkewJbg= github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= @@ -144,7 +132,6 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -156,7 +143,6 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= @@ -164,25 +150,17 @@ github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9D github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.80 h1:jEXGecSgPdvM5KnyDsSgFhZSm7WwaTp4h544Im4SfhI= -github.com/aws/aws-sdk-go v1.44.80/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.89 h1:Xf5Pp9GsNSMRinAuWNiQd0vusXXb3IgYbNlxldhWS2Q= github.com/aws/aws-sdk-go v1.44.89/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.18.1 h1:xXzi0Z6lArTykRGqCOC2rN3zN648zjQtkCzAVjJj9OY= -github.com/bitnami-labs/sealed-secrets v0.18.1/go.mod h1:pOMGS1imRiIPLm7OpdD/s/OfgQmLkKxTqWruSEiQqCM= github.com/bitnami-labs/sealed-secrets v0.18.2 h1:CfZD0JmhAPOQ7YqxrxZ90/+BjqTheWztBOYploqO6nc= github.com/bitnami-labs/sealed-secrets v0.18.2/go.mod h1:wlhaZ+SI5aJkpq9CyyP0pVLJEj4LPXLzLHb2TnID/Rc= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blizzy78/varnamelen v0.3.0/go.mod h1:hbwRdBvoBqxk34XyQ6HA0UH3G0/1TKuv5AC4eaBT0Ec= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= @@ -197,15 +175,10 @@ github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= @@ -222,28 +195,17 @@ github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8 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-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs= github.com/containerd/containerd v1.6.8/go.mod h1:By6p5KqPK0/7/CgO/A6t/Gz+CUYUu2zf1hUaaymVXB0= github.com/containerd/stargz-snapshotter/estargz v0.12.0 h1:idtwRTLjk2erqiYhPWy2L844By8NRFYEwYHcXhoIWPM= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -265,11 +227,9 @@ github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2 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/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -284,8 +244,6 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= @@ -293,9 +251,6 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/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/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= @@ -308,21 +263,16 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y 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.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 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/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= -github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -331,15 +281,10 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fluxcd/pkg/kustomize v0.5.3 h1:WpxNOV/Yklp0p7Qv85VwBegq9fABuLR9qSWaAVa3+yc= -github.com/fluxcd/pkg/kustomize v0.5.3/go.mod h1:zy1FLxkEDADUykCnrXqq6rVN48t3uMhAb3ao+zv0rFE= github.com/fluxcd/pkg/kustomize v0.7.0 h1:604rlpRZTWaOfzDZ1W93aHaFh9kn8/UMX/wzsjwIUQY= github.com/fluxcd/pkg/kustomize v0.7.0/go.mod h1:zJY3Z0+SX+zs+/A1F6fCT0JvUce265XnrpTtHnujXPo= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -347,15 +292,11 @@ github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5 github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= -github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= @@ -381,22 +322,17 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -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.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 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.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= 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.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= @@ -451,14 +387,11 @@ github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQA github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 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/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/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= @@ -500,18 +433,13 @@ github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZ github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= -github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 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/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.10.1/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= -github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= 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= @@ -531,7 +459,6 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= github.com/google/go-containerregistry v0.11.0/go.mod h1:BBaYtsHPHA42uEgAvd/NejvAfPSlz281sJWqupjSxfk= 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= @@ -567,7 +494,6 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -594,15 +520,11 @@ github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -619,8 +541,6 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= -github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.3.0 h1:G0ACM8Z2WilWgPv3Vdzwm3V0BQu/kSmrkVtpe1fy9do= github.com/hashicorp/go-hclog v1.3.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -688,7 +608,6 @@ github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq 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.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -718,7 +637,6 @@ github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOvVRsHiKkeGCT6tYBNWyDVuzj9wAaBb5R9qamfw= @@ -752,8 +670,6 @@ github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/kluctl/kluctl-python-deps v0.0.0-20220819065415-624185ed2c0a h1:2W9TlV8b6sqjIKhbp9bgKjusdF3HgQucFNmwGNb1N+Y= -github.com/kluctl/kluctl-python-deps v0.0.0-20220819065415-624185ed2c0a/go.mod h1:w4cTz1av5JFX9bMTB7Vm2qtmm5+eU+ncvB1/oyjVwOw= github.com/kluctl/kluctl-python-deps v0.0.0-20220819072433-e8107cc5ae41 h1:sMQz3y4XtYS4YjAil3B8pxuhjPEObt0xMPMwmPZC9O0= github.com/kluctl/kluctl-python-deps v0.0.0-20220819072433-e8107cc5ae41/go.mod h1:w4cTz1av5JFX9bMTB7Vm2qtmm5+eU+ncvB1/oyjVwOw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -794,18 +710,14 @@ github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/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/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= @@ -841,7 +753,6 @@ github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mN github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -895,7 +806,6 @@ github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQ github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -913,14 +823,12 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -928,31 +836,24 @@ github.com/nishanths/exhaustive v0.2.3/go.mod h1:bhIX678Nx8inLM9PbpvK1yv6oGtoP8B github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ= github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/ohler55/ojg v1.14.4 h1:L2ds8AlB5t/QbqSfhRwvagJzQ7pgmdrefMIypQs0Xik= github.com/ohler55/ojg v1.14.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -973,8 +874,6 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko 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.3 h1:h9JoA60e1dVEOpp0PFwJSmt1Htu057NUq9/bUwaO61s= -github.com/pelletier/go-toml/v2 v2.0.3/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -998,9 +897,7 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= @@ -1014,8 +911,6 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= @@ -1025,7 +920,6 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -1034,7 +928,6 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/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/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= @@ -1056,8 +949,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= @@ -1101,7 +992,6 @@ github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5N github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1117,7 +1007,6 @@ 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 v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= @@ -1125,13 +1014,11 @@ github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= @@ -1140,7 +1027,6 @@ github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRk 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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1156,8 +1042,6 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs= -github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= @@ -1171,7 +1055,6 @@ github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcy github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tomarrell/wrapcheck/v2 v2.4.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= @@ -1187,8 +1070,6 @@ github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= -github.com/vbauerster/mpb/v7 v7.4.2 h1:n917F4d8EWdUKc9c81wFkksyG6P6Mg7IETfKCE1Xqng= -github.com/vbauerster/mpb/v7 v7.4.2/go.mod h1:UmOiIUI8aPqWXIps0ciik3RKMdzx7+ooQpq+fBcXwBA= github.com/vbauerster/mpb/v7 v7.5.2 h1:Ph3JvpBcoIwzIG1QwbUq97KQifrTRbKcMXN9rN5BYAs= github.com/vbauerster/mpb/v7 v7.5.2/go.mod h1:UmOiIUI8aPqWXIps0ciik3RKMdzx7+ooQpq+fBcXwBA= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= @@ -1197,8 +1078,6 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= -github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= -github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= github.com/xanzy/ssh-agent v0.3.2 h1:eKj4SX2Fe7mui28ZgnFW5fmTz1EIr7ugo5s6wDxdHBM= github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1209,7 +1088,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1: github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= @@ -1224,27 +1102,17 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -1254,19 +1122,7 @@ 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.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.starlark.net v0.0.0-20220817180228-f738f5508c12 h1:xOBJXWGEDwU5xSDxH6macxO11Us0AH2fTa9rmsbbF7g= go.starlark.net v0.0.0-20220817180228-f738f5508c12/go.mod h1:VZcBMdr3cT3PnBoWunTabuSEXwVAH+ZJ5zxfs3AdASk= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1275,7 +1131,6 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= @@ -1284,7 +1139,6 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 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= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1300,22 +1154,17 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/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.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c= -golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1355,7 +1204,6 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1375,7 +1223,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1397,7 +1244,6 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R 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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/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-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -1410,13 +1256,9 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1435,10 +1277,7 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/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.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw= -golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1480,7 +1319,6 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1502,13 +1340,11 @@ golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200519105757-fe76b779f299/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-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/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= @@ -1538,24 +1374,19 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= -golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1578,8 +1409,6 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb 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-20200416051211-89c76fbcd5d1/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.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1641,7 +1470,6 @@ golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 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= @@ -1686,7 +1514,6 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= 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= @@ -1766,8 +1593,6 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/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= @@ -1791,10 +1616,7 @@ google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1 h1:C2UVWqrgLYKrT5nh5oU6hLRm1AeEklCK5eloQA1NtFY= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf h1:Q5xNKbTSFwkuaaGaR7CMcXEM5sy19KYdUU8iF8/iRC0= google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1826,8 +1648,6 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -1860,17 +1680,13 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= @@ -1893,9 +1709,6 @@ 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.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -helm.sh/helm/v3 v3.9.3 h1:etd4Qc45/bnIkBofZIRwrAzYuG3bNWR1EdMN4fsfzoE= -helm.sh/helm/v3 v3.9.3/go.mod h1:3eaWAIqzvlRSD06gR9MMwmp2KBKwlu9av1/1BZpjeWY= helm.sh/helm/v3 v3.9.4 h1:TCI1QhJUeLVOdccfdw+vnSEO3Td6gNqibptB04QtExY= helm.sh/helm/v3 v3.9.4/go.mod h1:3eaWAIqzvlRSD06gR9MMwmp2KBKwlu9av1/1BZpjeWY= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1906,58 +1719,27 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt 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= honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= -k8s.io/api v0.24.4 h1:I5Y645gJ8zWKawyr78lVfDQkZrAViSbeRXsPZWTxmXk= -k8s.io/api v0.24.4/go.mod h1:42pVfA0NRxrtJhZQOvRSyZcJihzAdU59WBtTjYcB0/M= k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= -k8s.io/apiextensions-apiserver v0.24.4 h1:w53Pm4zu8fCt9WfiRgS2YI6LE6I4NJ5aUi78GElD3K8= -k8s.io/apiextensions-apiserver v0.24.4/go.mod h1:iDK+Xb4jsPNnRGj5jU/WqqjLvt8363M7cKixKe1C9+U= k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= -k8s.io/apimachinery v0.24.4 h1:S0Ur3J/PbivTcL43EdSdPhqCqKla2NIuneNwZcTDeGQ= -k8s.io/apimachinery v0.24.4/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= -k8s.io/apiserver v0.24.4 h1:ei+OunC83pVEiagBeZhTnRZvkclHgpzs/rrm7aSBDYs= -k8s.io/apiserver v0.24.4/go.mod h1:mAuC3pZVc0IDXLx7lUHoisBOtBa1SobfLW/CI3klXQE= k8s.io/apiserver v0.25.0 h1:8kl2ifbNffD440MyvHtPaIz1mw4mGKVgWqM0nL+oyu4= k8s.io/apiserver v0.25.0/go.mod h1:BKwsE+PTC+aZK+6OJQDPr0v6uS91/HWxX7evElAH6xo= -k8s.io/cli-runtime v0.24.4 h1:YCSf0dZp+pYXVR/8aZQ6MEBSiicv8rLyVsGBEbRnwfY= -k8s.io/cli-runtime v0.24.4/go.mod h1:RF+cSLYXkPV3WyvPrX2qeRLEUJY38INWx6jLKVLFCxM= k8s.io/cli-runtime v0.25.0 h1:XBnTc2Fi+w818jcJGzhiJKQuXl8479sZ4FhtV5hVJ1Q= k8s.io/cli-runtime v0.25.0/go.mod h1:bHOI5ZZInRHhbq12OdUiYZQN8ml8aKZLwQgt9QlLINw= -k8s.io/client-go v0.24.4 h1:hIAIJZIPyaw46AkxwyR0FRfM/pRxpUNTd3ysYu9vyRg= -k8s.io/client-go v0.24.4/go.mod h1:+AxlPWw/H6f+EJhRSjIeALaJT4tbeB/8g9BNvXGPd0Y= k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= -k8s.io/code-generator v0.24.4/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= -k8s.io/component-base v0.24.4 h1:WEGRp06GBYVwxp5JdiRaJ1zkdOhrqucxRv/8IrABLG0= -k8s.io/component-base v0.24.4/go.mod h1:sWxkgcMfbYHadw0OJ0N+vIscd14/nqSIM2veCdg843o= k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= -k8s.io/component-helpers v0.24.4/go.mod h1:xAHlOKU8rAjLgXWJEsueWLR1LDMThbaPf2YvgKpSyQ8= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea h1:3QOH5+2fGsY8e1qf+GIFpg+zw/JGNrgyZRQR7/m6uWg= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/kubectl v0.24.4 h1:fPEBkAV3/cu3BQVIUCXNngCCY62AlZ+2rkRVHcmJPn0= -k8s.io/kubectl v0.24.4/go.mod h1:AVyJzxUwA5UMGTDyKGL6nd6RRW36FbmAdtIDMhrZtW0= k8s.io/kubectl v0.25.0 h1:/Wn1cFqo8ik3iee1EvpxYre3bkWsGLXzLQI6uCCAkQc= k8s.io/kubectl v0.25.0/go.mod h1:n16ULWsOl2jmQpzt2o7Dud1t4o0+Y186ICb4O+GwKAU= -k8s.io/metrics v0.24.4/go.mod h1:7D8Xm3DGZoJaiCS8+QA2EzdMuDlq0Y8SiOPUB/1BaGU= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 h1:XmRqFcQlCy/lKRZ39j+RVpokYNroHPqV3mcBRfnhT5o= -k8s.io/utils v0.0.0-20220812165043-ad590609e2e5/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= @@ -1969,25 +1751,16 @@ oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= 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.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kind v0.14.0 h1:cNmI3jGBvp7UegEGbC5we8plDtCUmaNRL+bod7JoSCE= sigs.k8s.io/kind v0.14.0/go.mod h1:UrFRPHG+2a5j0Q7qiR4gtJ4rEyn8TuMQwuOPf+m4oHg= -sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= -sigs.k8s.io/kustomize/cmd/config v0.10.6/go.mod h1:/S4A4nUANUa4bZJ/Edt7ZQTyKOY9WCER0uBS1SW2Rco= -sigs.k8s.io/kustomize/kustomize/v4 v4.5.4/go.mod h1:Zo/Xc5FKD6sHl0lilbrieeGeZHVYCA4BzxeAaLI05Bg= -sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= 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.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -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= From fc0b6af6e72d705f219538b10e90392458dfd657 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Sep 2022 10:53:05 +0200 Subject: [PATCH 0332/2268] fix: Prefer fixed-images passed via command line over images configured anywhere else --- cmd/kluctl/commands/utils.go | 4 +--- pkg/deployment/images.go | 7 +++++++ pkg/kluctl_project/target_context.go | 4 +--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 3ab20e9be..ed0257f38 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -136,9 +136,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm if err != nil { return err } - for _, fi := range fixedImages { - images.AddFixedImage(fi) - } + images.PrependFixedImages(fixedImages) inclusion, err := args.inclusionFlags.ParseInclusionFromArgs() if err != nil { diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index f7ef098e7..8dc9c5f77 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -41,6 +41,13 @@ func (images *Images) AddFixedImage(fi types.FixedImage) { images.fixedImages = append(images.fixedImages, fi) } +func (images *Images) PrependFixedImages(fis []types.FixedImage) { + var newFixedImages []types.FixedImage + newFixedImages = append(newFixedImages, fis...) + newFixedImages = append(newFixedImages, images.fixedImages...) + images.fixedImages = newFixedImages +} + func (images *Images) SeenImages(simple bool) []types.FixedImage { var ret []types.FixedImage for _, fi := range images.seenImages { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index b5ed711fd..47d187a36 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -39,9 +39,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s } target = t.Target - for _, fi := range target.Images { - images.AddFixedImage(fi) - } + images.PrependFixedImages(target.Images) } varsCtx, clientConfig, clusterContext, err := p.buildVars(target, clusterName, args, forSeal) From 29ad3cfb633613fb36a299b4d444f69162824f5b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Sep 2022 13:24:39 +0200 Subject: [PATCH 0333/2268] fix: Don't write addresses into known_hosts to avoid issues with IPV6 parsing --- pkg/git/auth/goph/hosts.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pkg/git/auth/goph/hosts.go b/pkg/git/auth/goph/hosts.go index e2d9bec1e..10b8cdeb3 100644 --- a/pkg/git/auth/goph/hosts.go +++ b/pkg/git/auth/goph/hosts.go @@ -96,14 +96,7 @@ func AddKnownHost(host string, remote net.Addr, key ssh.PublicKey, knownFile str defer f.Close() - remoteNormalized := knownhosts.Normalize(remote.String()) - hostNormalized := knownhosts.Normalize(host) - addresses := []string{remoteNormalized} - - if hostNormalized != remoteNormalized { - addresses = append(addresses, hostNormalized) - } - + addresses := []string{host} _, err = f.WriteString(knownhosts.Line(addresses, key) + "\n") return err From 2889a9f2af1660edcc84c5dbb501dff0eb325ae3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Sep 2022 22:29:09 +0200 Subject: [PATCH 0334/2268] fix: Stop using mpb and instead rely on uilive --- go.mod | 4 +- go.sum | 8 +- pkg/status/multiline/multiline.go | 133 ++++++++++++++++++++++++++++++ pkg/status/multiline/spinner.go | 13 +++ pkg/status/status_handler.go | 102 ++++++++++++----------- pkg/status/utils.go | 2 +- 6 files changed, 203 insertions(+), 59 deletions(-) create mode 100644 pkg/status/multiline/multiline.go create mode 100644 pkg/status/multiline/spinner.go diff --git a/go.mod b/go.mod index eb85f2f1c..a17436d62 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/gobwas/glob v0.2.3 github.com/golang-jwt/jwt/v4 v4.4.2 github.com/google/go-containerregistry v0.11.0 + github.com/gosuri/uilive v0.0.4 github.com/hashicorp/vault/api v1.7.2 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 @@ -34,7 +35,6 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.12.0 github.com/stretchr/testify v1.8.0 - github.com/vbauerster/mpb/v7 v7.5.2 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.2 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 @@ -73,8 +73,6 @@ require ( github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 // indirect - github.com/VividCortex/ewma v1.2.0 // indirect - github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/alessio/shellescape v1.4.1 // indirect github.com/armon/go-metrics v0.4.0 // indirect diff --git a/go.sum b/go.sum index d09bfb382..a4812274e 100644 --- a/go.sum +++ b/go.sum @@ -112,10 +112,6 @@ github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErI github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= -github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= -github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -518,6 +514,8 @@ github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/g github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= +github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= +github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= @@ -1070,8 +1068,6 @@ github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= -github.com/vbauerster/mpb/v7 v7.5.2 h1:Ph3JvpBcoIwzIG1QwbUq97KQifrTRbKcMXN9rN5BYAs= -github.com/vbauerster/mpb/v7 v7.5.2/go.mod h1:UmOiIUI8aPqWXIps0ciik3RKMdzx7+ooQpq+fBcXwBA= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= diff --git a/pkg/status/multiline/multiline.go b/pkg/status/multiline/multiline.go new file mode 100644 index 000000000..f2a67889e --- /dev/null +++ b/pkg/status/multiline/multiline.go @@ -0,0 +1,133 @@ +package multiline + +import ( + "bytes" + "fmt" + "github.com/gosuri/uilive" + "io" + "sync" + "time" +) + +type MultiLinePrinter struct { + topLines []*Line + lines []*Line + + uil *uilive.Writer + lastWritten []byte + + ticker *time.Ticker + tdone chan bool + + mutex sync.Mutex +} + +type LineFunc func() string + +type Line struct { + ml *MultiLinePrinter + s LineFunc +} + +func (ml *MultiLinePrinter) Start(w io.Writer) { + ml.uil = uilive.New() + ml.uil.Out = w + ml.uil.Start() + + ml.ticker = time.NewTicker(100 * time.Millisecond) + ml.tdone = make(chan bool) + + go ml.loop() +} + +func (ml *MultiLinePrinter) Stop() { + ml.Flush() + ml.tdone <- true + <-ml.tdone + ml.uil.Stop() +} + +func (ml *MultiLinePrinter) loop() { + for { + select { + case <-ml.ticker.C: + if ml.ticker != nil { + ml.Flush() + } + case <-ml.tdone: + ml.mutex.Lock() + ml.ticker.Stop() + ml.ticker = nil + ml.mutex.Unlock() + close(ml.tdone) + return + } + } +} + +func (ml *MultiLinePrinter) Flush() { + ml.mutex.Lock() + defer ml.mutex.Unlock() + + hadTopLines := false + if len(ml.topLines) != 0 { + hadTopLines = true + topBuf := bytes.NewBuffer(nil) + for _, l := range ml.topLines { + _, _ = fmt.Fprintf(topBuf, "%s\n", l.s()) + } + + _, _ = ml.uil.Bypass().Write(topBuf.Bytes()) + ml.topLines = nil + } + + linesBuf := bytes.NewBuffer(nil) + for _, l := range ml.lines { + _, _ = fmt.Fprintf(linesBuf, "%s\n", l.s()) + } + newBytes := linesBuf.Bytes() + if hadTopLines || !bytes.Equal(newBytes, ml.lastWritten) { + _, _ = ml.uil.Write(newBytes) + ml.lastWritten = newBytes + } +} + +func (ml *MultiLinePrinter) NewTopLine(s LineFunc) { + ml.mutex.Lock() + defer ml.mutex.Unlock() + + l := &Line{ + ml: ml, + s: s, + } + ml.topLines = append(ml.topLines, l) +} + +func (ml *MultiLinePrinter) NewLine(s LineFunc) *Line { + ml.mutex.Lock() + defer ml.mutex.Unlock() + + l := &Line{ + ml: ml, + s: s, + } + ml.lines = append(ml.lines, l) + + return l +} + +func (l *Line) Remove(pushToTop bool) { + l.ml.mutex.Lock() + defer l.ml.mutex.Unlock() + + for i, l2 := range l.ml.lines { + if l2 == l { + l.ml.lines = append(l.ml.lines[:i], l.ml.lines[i+1:]...) + + if pushToTop { + l.ml.topLines = append(l.ml.topLines, l) + } + break + } + } +} diff --git a/pkg/status/multiline/spinner.go b/pkg/status/multiline/spinner.go new file mode 100644 index 000000000..b686973ab --- /dev/null +++ b/pkg/status/multiline/spinner.go @@ -0,0 +1,13 @@ +package multiline + +import ( + "time" +) + +var spinner = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} + +func Spinner() string { + factor := int64(100) + frame := int((time.Now().UnixMilli() / factor) % int64(len(spinner))) + return spinner[frame] +} diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index 7b5a85a82..34f2bd717 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -2,13 +2,12 @@ package status import ( "context" - "github.com/kluctl/kluctl/v2/pkg/utils" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/status/multiline" "github.com/kluctl/kluctl/v2/pkg/utils/term" - "github.com/vbauerster/mpb/v7" - "github.com/vbauerster/mpb/v7/decor" "io" - "math" "strings" + "sync" "syscall" ) @@ -16,18 +15,22 @@ type MultiLineStatusHandler struct { ctx context.Context out io.Writer isTerminal bool - progress *mpb.Progress trace bool + + ml *multiline.MultiLinePrinter } type statusLine struct { - slh *MultiLineStatusHandler + slh *MultiLineStatusHandler + l *multiline.Line + + current int total int - bar *mpb.Bar - filler mpb.BarFiller message string barOverride string + + mutex sync.Mutex } func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, isTerminal bool, trace bool) *MultiLineStatusHandler { @@ -52,8 +55,7 @@ func (s *MultiLineStatusHandler) IsTraceEnabled() bool { } func (s *MultiLineStatusHandler) Flush() { - s.Stop() - s.start() + s.ml.Flush() } func (s *MultiLineStatusHandler) SetTrace(trace bool) { @@ -61,60 +63,43 @@ func (s *MultiLineStatusHandler) SetTrace(trace bool) { } func (s *MultiLineStatusHandler) start() { - s.progress = mpb.NewWithContext( - s.ctx, - mpb.WithWidth(utils.GetTermWidth()), - mpb.WithOutput(s.out), - mpb.PopCompletedMode(), - ) + s.ml = &multiline.MultiLinePrinter{} + s.ml.Start(s.out) } func (s *MultiLineStatusHandler) Stop() { - s.progress.Wait() + s.ml.Stop() } func (s *MultiLineStatusHandler) StartStatus(total int, message string) StatusLine { - return s.startStatus(total, message, 0, "") + return s.startStatus(total, message, "") } -func (s *MultiLineStatusHandler) startStatus(total int, message string, priority int, barOverride string) *statusLine { +func (s *MultiLineStatusHandler) startStatus(total int, message string, barOverride string) *statusLine { sl := &statusLine{ slh: s, total: total, message: message, barOverride: barOverride, } - sl.filler = mpb.SpinnerStyle().PositionLeft().Build() - opts := []mpb.BarOption{ - mpb.BarWidth(1), - mpb.AppendDecorators(decor.Any(sl.DecorMessage, decor.WCSyncWidthR)), - } - if priority != 0 { - opts = append(opts, mpb.BarPriority(priority)) - } - - sl.bar = s.progress.Add(int64(total), sl, opts...) + sl.l = sl.slh.ml.NewLine(sl.lineFunc) return sl } -func (sl *statusLine) DecorMessage(s decor.Statistics) string { - return sl.message -} - -func (sl *statusLine) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { - if sl.barOverride != "" { - _, _ = io.WriteString(w, sl.barOverride) - return +func (s *MultiLineStatusHandler) printLine(message string, barOverride string, doFlush bool) { + s.ml.NewTopLine(func() string { + return fmt.Sprintf("%s %s", barOverride, message) + }) + if doFlush { + s.Flush() } - - sl.filler.Fill(w, reqWidth, stat) } func (s *MultiLineStatusHandler) Info(message string) { o := withColor("green", "ⓘ") - s.startStatus(1, message, math.MinInt, o).end(o) + s.printLine(message, o, true) } func (s *MultiLineStatusHandler) InfoFallback(message string) { @@ -123,12 +108,12 @@ func (s *MultiLineStatusHandler) InfoFallback(message string) { func (s *MultiLineStatusHandler) Warning(message string) { o := withColor("yellow", "⚠") - s.startStatus(1, message, math.MinInt, o).end(o) + s.printLine(message, o, true) } func (s *MultiLineStatusHandler) Error(message string) { o := withColor("red", "✗") - s.startStatus(1, message, math.MinInt, o).end(o) + s.printLine(message, o, true) } func (s *MultiLineStatusHandler) Trace(message string) { @@ -143,7 +128,7 @@ func (s *MultiLineStatusHandler) PlainText(text string) { func (s *MultiLineStatusHandler) Prompt(password bool, message string) (string, error) { o := withColor("yellow", "?") - sl := s.startStatus(1, message, math.MinInt, o) + sl := s.startStatus(1, message, o) defer sl.end(o) doUpdate := func(ret []byte) { @@ -159,28 +144,47 @@ func (s *MultiLineStatusHandler) Prompt(password bool, message string) (string, return string(ret), err } +func (sl *statusLine) lineFunc() string { + sl.mutex.Lock() + defer sl.mutex.Unlock() + + if sl.barOverride != "" { + return fmt.Sprintf("%s %s", sl.barOverride, sl.message) + } + + s := multiline.Spinner() + return fmt.Sprintf("%s %s", s, sl.message) +} + func (sl *statusLine) SetTotal(total int) { + sl.mutex.Lock() + defer sl.mutex.Unlock() sl.total = total - sl.bar.SetTotal(int64(total), false) - sl.bar.EnableTriggerComplete() } func (sl *statusLine) Increment() { - sl.bar.Increment() + sl.mutex.Lock() + defer sl.mutex.Unlock() + if sl.current < sl.total { + sl.current++ + } } func (sl *statusLine) Update(message string) { + sl.mutex.Lock() + defer sl.mutex.Unlock() sl.message = message } func (sl *statusLine) end(barOverride string) { sl.barOverride = barOverride - // make sure that the bar es rendered on top so that it can be properly popped - sl.bar.SetPriority(math.MinInt) - sl.bar.SetCurrent(int64(sl.total)) + sl.current = sl.total + sl.l.Remove(true) } func (sl *statusLine) End(result EndResult) { + sl.mutex.Lock() + defer sl.mutex.Unlock() switch result { case EndSuccess: sl.end(withColor("green", "✓")) diff --git a/pkg/status/utils.go b/pkg/status/utils.go index 0a2edfc82..21555d432 100644 --- a/pkg/status/utils.go +++ b/pkg/status/utils.go @@ -31,7 +31,7 @@ type replaceRReader struct { func (r *replaceRReader) Read(p []byte) (int, error) { written := 0 - for true { + for written < len(p) { b, err := r.reader.ReadByte() if err != nil { if err == io.EOF { From 1189a4598dbe2bd3e40ee74ed586109e63595b0e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Sep 2022 15:23:38 +0200 Subject: [PATCH 0335/2268] feat: Switch to go-jinja2 library --- cmd/kluctl/commands/utils.go | 6 +- go.mod | 5 +- go.sum | 5 +- pkg/deployment/deployment_item.go | 9 +- pkg/deployment/deployment_project.go | 3 +- pkg/jinja2/.gitignore | 2 - pkg/jinja2/jinja2.go | 255 ----------------------- pkg/jinja2/jinja2_renderer.go | 213 ------------------- pkg/jinja2/python_src/.gitignore | 4 - pkg/jinja2/python_src/.python-version | 1 - pkg/jinja2/python_src/dict_utils.py | 64 ------ pkg/jinja2/python_src/jinja2_renderer.py | 122 ----------- pkg/jinja2/python_src/jinja2_utils.py | 140 ------------- pkg/jinja2/python_src/jsonpath_utils.py | 100 --------- pkg/jinja2/python_src/main.py | 34 --- pkg/jinja2/python_src/yaml_utils.py | 51 ----- pkg/jinja2/render_struct.go | 181 ---------------- pkg/jinja2/source.go | 93 --------- pkg/kluctl_jinja2/embed.go | 8 + pkg/kluctl_jinja2/ext/__init__.py | 3 + pkg/kluctl_jinja2/ext/images_ext.py | 53 +++++ pkg/kluctl_jinja2/jinja2.go | 23 ++ pkg/kluctl_project/load.go | 2 +- pkg/kluctl_project/project.go | 2 +- pkg/kluctl_project/targets.go | 4 +- pkg/utils/uo/uo.go | 16 ++ pkg/utils/utils.go | 3 +- pkg/vars/vars.go | 37 +++- pkg/vars/vars_loader.go | 10 +- pkg/vars/vars_test.go | 5 +- 30 files changed, 168 insertions(+), 1286 deletions(-) delete mode 100644 pkg/jinja2/.gitignore delete mode 100644 pkg/jinja2/jinja2.go delete mode 100644 pkg/jinja2/jinja2_renderer.go delete mode 100644 pkg/jinja2/python_src/.gitignore delete mode 100644 pkg/jinja2/python_src/.python-version delete mode 100644 pkg/jinja2/python_src/dict_utils.py delete mode 100644 pkg/jinja2/python_src/jinja2_renderer.py delete mode 100644 pkg/jinja2/python_src/jinja2_utils.py delete mode 100644 pkg/jinja2/python_src/jsonpath_utils.py delete mode 100644 pkg/jinja2/python_src/main.py delete mode 100644 pkg/jinja2/python_src/yaml_utils.py delete mode 100644 pkg/jinja2/render_struct.go delete mode 100644 pkg/jinja2/source.go create mode 100644 pkg/kluctl_jinja2/embed.go create mode 100644 pkg/kluctl_jinja2/ext/__init__.py create mode 100644 pkg/kluctl_jinja2/ext/images_ext.py create mode 100644 pkg/kluctl_jinja2/jinja2.go diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index ed0257f38..9b50739d4 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -10,7 +10,7 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/repocache" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" - "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/registries" "github.com/kluctl/kluctl/v2/pkg/status" @@ -39,14 +39,12 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b } defer os.RemoveAll(tmpDir) - j2, err := jinja2.NewJinja2() + j2, err := kluctl_jinja2.NewKluctlJinja2(strictTemplates) if err != nil { return err } defer j2.Close() - j2.SetStrict(strictTemplates) - cwd, err := os.Getwd() if err != nil { return err diff --git a/go.mod b/go.mod index a17436d62..93aaf886c 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/fluxcd/pkg/kustomize v0.7.0 github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.0 - github.com/gobwas/glob v0.2.3 + github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 github.com/google/go-containerregistry v0.11.0 github.com/gosuri/uilive v0.0.4 @@ -22,7 +22,8 @@ require ( github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/kluctl/kluctl-python-deps v0.0.0-20220819072433-e8107cc5ae41 + github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 + github.com/kluctl/go-jinja2 v0.0.0-20220908201753-f33cd6224f2b github.com/mattn/go-isatty v0.0.16 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 diff --git a/go.sum b/go.sum index a4812274e..d4cf7fb99 100644 --- a/go.sum +++ b/go.sum @@ -668,8 +668,8 @@ github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/kluctl/kluctl-python-deps v0.0.0-20220819072433-e8107cc5ae41 h1:sMQz3y4XtYS4YjAil3B8pxuhjPEObt0xMPMwmPZC9O0= -github.com/kluctl/kluctl-python-deps v0.0.0-20220819072433-e8107cc5ae41/go.mod h1:w4cTz1av5JFX9bMTB7Vm2qtmm5+eU+ncvB1/oyjVwOw= +github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= +github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -1380,7 +1380,6 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 959830915..e97bb48d2 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -128,7 +128,7 @@ func (di *DeploymentItem) render(forSeal bool) error { } if yaml.Exists(filepath.Join(p, "helm-chart.yml")) { // never try to render helm charts - ep := filepath.Join(di.RelToProjectItemDir, relDir, "charts/**") + ep := filepath.Join(relDir, "charts/**") ep = filepath.ToSlash(ep) excludePatterns = append(excludePatterns, ep) return filepath.SkipDir @@ -145,7 +145,12 @@ func (di *DeploymentItem) render(forSeal bool) error { excludePatterns = append(excludePatterns, "**.sealme") } - return varsCtx.RenderDirectory(di.Project.source.dir, di.Project.getRenderSearchDirs(), di.Project.relDir, excludePatterns, di.RelToProjectItemDir, di.RenderedDir) + return varsCtx.RenderDirectory( + filepath.Join(di.Project.source.dir, di.RelToSourceItemDir), + di.RenderedDir, + excludePatterns, + di.Project.getRenderSearchDirs(), + ) } func (di *DeploymentItem) renderHelmCharts() error { diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index a11c1e919..ecd599c31 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -75,8 +75,9 @@ func (p *DeploymentProject) loadConfig() error { } return fmt.Errorf("%s not found", configPath) } + configPath = yaml.FixPathExt(configPath) - err := p.VarsCtx.RenderYamlFile(yaml.FixNameExt(p.absDir, "deployment.yml"), p.getRenderSearchDirs(), &p.Config) + err := p.VarsCtx.RenderYamlFile(configPath, p.getRenderSearchDirs(), &p.Config) if err != nil { return fmt.Errorf("failed to load deployment.yml: %w", err) } diff --git a/pkg/jinja2/.gitignore b/pkg/jinja2/.gitignore deleted file mode 100644 index c23b5c5b7..000000000 --- a/pkg/jinja2/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.tar.gz -*.files \ No newline at end of file diff --git a/pkg/jinja2/jinja2.go b/pkg/jinja2/jinja2.go deleted file mode 100644 index 62df03c34..000000000 --- a/pkg/jinja2/jinja2.go +++ /dev/null @@ -1,255 +0,0 @@ -package jinja2 - -import ( - "fmt" - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/gobwas/glob" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "io/fs" - "io/ioutil" - "os" - "path/filepath" - "strings" - "sync" -) - -var paralellism = 4 - -type Jinja2 struct { - pj chan *pythonJinja2Renderer - strict bool - globCache map[string]interface{} - mutex sync.Mutex -} - -type RenderJob struct { - Template string - Result *string - Error error - - target string -} - -type Jinja2Error struct { - error string -} - -type Jinja2Opt func(j2 *pythonJinja2Renderer) - -func WithTrimBlocks(trimBlocks bool) Jinja2Opt { - return func(j2 *pythonJinja2Renderer) { - j2.trimBlocks = trimBlocks - } -} - -func WithLStripBlocks(lstripBlocks bool) Jinja2Opt { - return func(j2 *pythonJinja2Renderer) { - j2.lstripBlocks = lstripBlocks - } -} - -func (m *Jinja2Error) Error() string { - return m.error -} - -func NewJinja2(opts ...Jinja2Opt) (*Jinja2, error) { - var wg sync.WaitGroup - var mutex sync.Mutex - var err error - - j := &Jinja2{ - pj: make(chan *pythonJinja2Renderer, paralellism), - strict: true, - globCache: map[string]interface{}{}, - } - - for i := 0; i < paralellism; i++ { - wg.Add(1) - go func() { - defer wg.Done() - pj, err2 := newPythonJinja2Renderer() - if err2 != nil { - mutex.Lock() - defer mutex.Unlock() - err = err2 - return - } - for _, o := range opts { - o(pj) - } - j.pj <- pj - }() - } - wg.Wait() - if err != nil { - return nil, err - } - - return j, nil -} - -func (j *Jinja2) Close() { - for i := 0; i < paralellism; i++ { - pj := <-j.pj - pj.Close() - } -} - -func (j *Jinja2) SetStrict(strict bool) { - j.strict = strict -} - -func (j *Jinja2) RenderStrings(jobs []*RenderJob, searchDirs []string, vars *uo.UnstructuredObject) error { - pj := <-j.pj - defer func() { j.pj <- pj }() - return pj.renderHelper(jobs, searchDirs, vars, true, j.strict) -} - -func (j *Jinja2) RenderFiles(jobs []*RenderJob, searchDirs []string, vars *uo.UnstructuredObject) error { - pj := <-j.pj - defer func() { j.pj <- pj }() - return pj.renderHelper(jobs, searchDirs, vars, false, j.strict) -} - -func (j *Jinja2) RenderString(template string, searchDirs []string, vars *uo.UnstructuredObject) (string, error) { - jobs := []*RenderJob{{ - Template: template, - }} - err := j.RenderStrings(jobs, searchDirs, vars) - if err != nil { - return "", err - } - if jobs[0].Error != nil { - return "", jobs[0].Error - } - return *jobs[0].Result, nil -} - -func (j *Jinja2) RenderFile(template string, searchDirs []string, vars *uo.UnstructuredObject) (string, error) { - jobs := []*RenderJob{{ - Template: template, - }} - err := j.RenderFiles(jobs, searchDirs, vars) - if err != nil { - return "", err - } - if jobs[0].Error != nil { - return "", jobs[0].Error - } - return *jobs[0].Result, nil -} - -func (j *Jinja2) getGlob(pattern string) (glob.Glob, error) { - j.mutex.Lock() - defer j.mutex.Unlock() - - g, ok := j.globCache[pattern] - if ok { - if g2, ok := g.(glob.Glob); ok { - return g2, nil - } else { - return nil, g2.(error) - } - } - g, err := glob.Compile(pattern, '/') - if err != nil { - j.globCache[pattern] = err - return nil, err - } - j.globCache[pattern] = g - return g.(glob.Glob), nil -} -func (j *Jinja2) needsRender(path string, excludedPatterns []string) bool { - path = filepath.ToSlash(path) - - for _, p := range excludedPatterns { - g, err := j.getGlob(p) - if err != nil { - return false - } - if g.Match(path) { - return false - } - } - return true -} - -func (j *Jinja2) RenderDirectory(rootDir string, searchDirs []string, relSourceDir string, excludePatterns []string, subdir string, targetDir string, vars *uo.UnstructuredObject) error { - searchDirs = append([]string{rootDir}, searchDirs...) - - walkDir, err := securejoin.SecureJoin(rootDir, filepath.Join(relSourceDir, subdir)) - if err != nil { - return err - } - - var jobs []*RenderJob - - err = filepath.WalkDir(walkDir, func(p string, d fs.DirEntry, err error) error { - relPath, err := filepath.Rel(walkDir, p) - if err != nil { - return err - } - if d.IsDir() { - err = os.MkdirAll(filepath.Join(targetDir, relPath), 0o700) - if err != nil { - return err - } - return nil - } - - sourcePath := filepath.Clean(filepath.Join(subdir, relPath)) - targetPath := filepath.Join(targetDir, relPath) - - if !j.needsRender(sourcePath, excludePatterns) { - return utils.CopyFile(p, targetPath) - } - - found := false - for _, searchDir := range searchDirs { - if utils.Exists(filepath.Join(searchDir, sourcePath)) { - sourcePath = filepath.Clean(filepath.Join(searchDir, sourcePath)) - sourcePath, err = filepath.Rel(rootDir, sourcePath) - if err != nil { - return err - } - found = true - } - } - if !found { - return fmt.Errorf("did not find %s in searchDirs", relPath) - } - - // jinja2 templates are using / even on Windows - sourcePath = strings.ReplaceAll(sourcePath, "\\", "/") - - job := &RenderJob{ - Template: sourcePath, - target: targetPath, - } - jobs = append(jobs, job) - return nil - }) - if err != nil { - return err - } - - err = j.RenderFiles(jobs, searchDirs, vars) - if err != nil { - return err - } - - var errors []error - for _, job := range jobs { - if job.Error != nil { - errors = append(errors, job.Error) - continue - } - - err = ioutil.WriteFile(job.target, []byte(*job.Result), 0o600) - if err != nil { - return err - } - } - return utils.NewErrorListOrNil(errors) -} diff --git a/pkg/jinja2/jinja2_renderer.go b/pkg/jinja2/jinja2_renderer.go deleted file mode 100644 index bcbd36edb..000000000 --- a/pkg/jinja2/jinja2_renderer.go +++ /dev/null @@ -1,213 +0,0 @@ -package jinja2 - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "github.com/kluctl/kluctl-python-deps/pkg/python" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strings" - "time" -) - -type pythonJinja2Renderer struct { - cmd *exec.Cmd - stdin io.WriteCloser - stdout io.ReadCloser - - stdoutReader *bufio.Reader - - trimBlocks bool - lstripBlocks bool -} - -func newPythonJinja2Renderer() (*pythonJinja2Renderer, error) { - isOk := false - j := &pythonJinja2Renderer{} - defer func() { - if !isOk { - j.Close() - } - }() - - args := []string{filepath.Join(pythonSrcExtracted, "main.py")} - j.cmd = python.PythonCmd(args) - j.cmd.Stderr = os.Stderr - j.cmd.Env = append(j.cmd.Env, fmt.Sprintf("PYTHONPATH=%s/wheel", pythonSrcExtracted)) - - stdout, err := j.cmd.StdoutPipe() - if err != nil { - return nil, err - } - j.stdout = stdout - - stdin, err := j.cmd.StdinPipe() - if err != nil { - return nil, err - } - j.stdin = stdin - - err = j.cmd.Start() - if err != nil { - return nil, err - } - - j.stdoutReader = bufio.NewReader(j.stdout) - - isOk = true - - return j, nil -} - -func (j *pythonJinja2Renderer) Close() { - if j.stdin != nil { - args := jinja2Args{Cmd: "exit"} - _ = json.NewEncoder(j.stdin).Encode(args) - - _ = j.stdin.Close() - j.stdin = nil - } - if j.stdout != nil { - _ = j.stdout.Close() - j.stdout = nil - } - if j.cmd != nil { - if j.cmd.Process != nil { - timer := time.AfterFunc(5*time.Second, func() { - _ = j.cmd.Process.Kill() - }) - _ = j.cmd.Wait() - timer.Stop() - } - j.cmd = nil - } -} - -func isMaybeTemplateString(template string) bool { - return strings.IndexRune(template, '{') != -1 -} - -func isMaybeTemplateBytes(template []byte) bool { - return bytes.IndexRune(template, '{') != -1 -} - -func isMaybeTemplate(template string, searchDirs []string, isString bool) (bool, *string) { - if isString { - if !isMaybeTemplateString(template) { - return false, &template - } - } else { - for _, s := range searchDirs { - b, err := ioutil.ReadFile(filepath.Join(s, template)) - if err != nil { - continue - } - if !isMaybeTemplateBytes(b) { - x := string(b) - return false, &x - } else { - return true, nil - } - } - } - return true, nil -} - -type jinja2Args struct { - Cmd string `json:"cmd"` - Templates []string `json:"templates"` - SearchDirs []string `json:"searchDirs"` - Vars string `json:"vars"` - Strict bool `json:"strict"` - - TrimBlocks bool `json:"trimBlocks"` - LStripBlocks bool `json:"lstripBlocks"` -} - -type jinja2Result struct { - Result *string `json:"result,omitempty"` - Error *string `json:"error,omitempty"` -} - -func (j *pythonJinja2Renderer) renderHelper(jobs []*RenderJob, searchDirs []string, vars *uo.UnstructuredObject, isString bool, strict bool) error { - varsStr, err := yaml.WriteJsonString(vars) - if err != nil { - return err - } - - var processedJobs []*RenderJob - - var jargs jinja2Args - if isString { - jargs.Cmd = "render-strings" - } else { - jargs.Cmd = "render-files" - } - jargs.Vars = string(varsStr) - jargs.SearchDirs = searchDirs - jargs.Strict = strict - jargs.TrimBlocks = j.trimBlocks - jargs.LStripBlocks = j.lstripBlocks - - for _, job := range jobs { - if ist, r := isMaybeTemplate(job.Template, searchDirs, isString); !ist { - job.Result = r - continue - } - processedJobs = append(processedJobs, job) - jargs.Templates = append(jargs.Templates, job.Template) - } - if len(processedJobs) == 0 { - return nil - } - - b, err := json.Marshal(jargs) - if err != nil { - j.Close() - return err - } - b = append(b, '\n') - - _, err = j.stdin.Write(b) - if err != nil { - j.Close() - return err - } - - line := bytes.NewBuffer(nil) - for true { - l, p, err := j.stdoutReader.ReadLine() - if err != nil { - return err - } - line.Write(l) - if !p { - break - } - } - var result []jinja2Result - err = json.Unmarshal(line.Bytes(), &result) - if err != nil { - return err - } - - for i, item := range result { - if item.Result != nil { - processedJobs[i].Result = item.Result - } else { - if item.Error == nil { - return fmt.Errorf("missing result and error from item at index %d", i) - } - processedJobs[i].Error = &Jinja2Error{*item.Error} - } - } - - return nil -} diff --git a/pkg/jinja2/python_src/.gitignore b/pkg/jinja2/python_src/.gitignore deleted file mode 100644 index c4095b70b..000000000 --- a/pkg/jinja2/python_src/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -__pycache__ - -/venv -/wheel diff --git a/pkg/jinja2/python_src/.python-version b/pkg/jinja2/python_src/.python-version deleted file mode 100644 index 30291cba2..000000000 --- a/pkg/jinja2/python_src/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.10.0 diff --git a/pkg/jinja2/python_src/dict_utils.py b/pkg/jinja2/python_src/dict_utils.py deleted file mode 100644 index f076faeea..000000000 --- a/pkg/jinja2/python_src/dict_utils.py +++ /dev/null @@ -1,64 +0,0 @@ -from jsonpath_utils import parse_json_path - - -def copy_primitive_value(v): - if isinstance(v, dict): - return copy_dict(v) - if is_iterable(v, False): - return [copy_primitive_value(x) for x in v] - return v - -def copy_dict(a): - ret = {} - for k, v in a.items(): - ret[k] = copy_primitive_value(v) - return ret - -def merge_dict(a, b, clone=True): - if clone: - a = copy_dict(a) - if a is None: - a = {} - if b is None: - b = {} - for key in b: - if key in a: - if isinstance(a[key], dict) and isinstance(b[key], dict): - merge_dict(a[key], b[key], clone=False) - else: - a[key] = b[key] - else: - a[key] = b[key] - return a - - -def get_dict_value(y, path, default=None): - p = parse_json_path(path) - f = p.find(y) - if len(f) > 1: - raise Exception("Only simple jsonpath supported in get_dict_value") - if len(f) == 0: - return default - return f[0].value - -def is_iterable(obj, str_and_bytes=True): - if isinstance(obj, list): - return True - if isinstance(obj, tuple): - return True - if isinstance(obj, dict): - return True - if isinstance(obj, str): - return str_and_bytes - if isinstance(obj, bytes): - return str_and_bytes - if isinstance(obj, int) or isinstance(obj, bool): - return False - if isinstance(obj, type): - return False - try: - iter(obj) - except Exception: - return False - else: - return True diff --git a/pkg/jinja2/python_src/jinja2_renderer.py b/pkg/jinja2/python_src/jinja2_renderer.py deleted file mode 100644 index fbc1a1364..000000000 --- a/pkg/jinja2/python_src/jinja2_renderer.py +++ /dev/null @@ -1,122 +0,0 @@ -import base64 -import json - -from jinja2 import StrictUndefined, FileSystemLoader, ChainableUndefined - -from dict_utils import merge_dict -from jinja2_utils import KluctlJinja2Environment, add_jinja2_filters, extract_template_error - - -begin_placeholder = "XXXXXbegin_get_image_" -end_placeholder = "_end_get_imageXXXXX" - - -class NullUndefined(ChainableUndefined): - def _return_self(self, other): - return self - - __add__ = __radd__ = __sub__ = __rsub__ = _return_self - __mul__ = __rmul__ = __div__ = __rdiv__ = _return_self - __truediv__ = __rtruediv__ = _return_self - __floordiv__ = __rfloordiv__ = _return_self - __mod__ = __rmod__ = _return_self - __pos__ = __neg__ = _return_self - __call__ = __getitem__ = _return_self - __lt__ = __le__ = __gt__ = __ge__ = _return_self - __int__ = __float__ = __complex__ = _return_self - __pow__ = __rpow__ = _return_self - -class Jinja2Renderer: - def __init__(self, trim_blocks=False, lstrip_blocks=False): - self.trim_blocks = trim_blocks - self.lstrip_blocks = lstrip_blocks - - def get_image_wrapper(self, image, latest_version=None): - if latest_version is None: - latest_version = "semver()" - placeholder = { - "image": image, - "latestVersion": str(latest_version), - } - j = json.dumps(placeholder) - j = base64.b64encode(j.encode("utf8")).decode("utf8") - j = begin_placeholder + j + end_placeholder - return j - - def build_images_vars(self): - def semver(allow_no_nums=False): - return "semver(allow_no_nums=%s)" % allow_no_nums - def prefix(s, suffix=None): - if suffix is None: - return "prefix(\"%s\")" % s - else: - return "prefix(\"%s\", suffix=\"%s\")" % (s, suffix) - def number(): - return "number()" - def regex(r): - return "regex(\"%s\")" % r - - vars = { - 'images': { - 'get_image': self.get_image_wrapper, - }, - 'version': { - 'semver': semver, - 'prefix': prefix, - 'number': number, - 'regex': regex, - }, - } - return vars - - def build_env(self, vars_str, search_dirs, strict): - vars = json.loads(vars_str) - image_vars = self.build_images_vars() - merge_dict(vars, image_vars, clone=False) - - environment = KluctlJinja2Environment(loader=FileSystemLoader(search_dirs), - undefined=StrictUndefined if strict else NullUndefined, - cache_size=10000, - auto_reload=False, - trim_blocks=self.trim_blocks, lstrip_blocks=self.lstrip_blocks) - merge_dict(environment.globals, vars, clone=False) - - add_jinja2_filters(environment) - return environment - - def render_helper(self, templates, search_dirs, vars, is_string, strict): - env = self.build_env(vars, search_dirs, strict) - - result = [] - - for i, t in enumerate(templates): - try: - if is_string: - t = env.from_string(t) - else: - t = env.get_template(t) - result.append({ - "result": t.render() - }) - except Exception as e: - result.append({ - "error": extract_template_error(e), - }) - - return result - - def RenderStrings(self, templates, search_dirs, vars, strict): - try: - return self.render_helper(templates, search_dirs, vars, True, strict) - except Exception as e: - return [{ - "error": str(e) - }] * len(templates) - - def RenderFiles(self, templates, search_dirs, vars, strict): - try: - return self.render_helper(templates, search_dirs, vars, False, strict) - except Exception as e: - return [{ - "error": str(e) - }] * len(templates) diff --git a/pkg/jinja2/python_src/jinja2_utils.py b/pkg/jinja2/python_src/jinja2_utils.py deleted file mode 100644 index 373afa280..000000000 --- a/pkg/jinja2/python_src/jinja2_utils.py +++ /dev/null @@ -1,140 +0,0 @@ -import base64 -import hashlib -import io -import json -import logging -import os -import sys -import traceback - -import jinja2 -from jinja2 import Environment, TemplateNotFound, TemplateError -from jinja2.runtime import Context - -from dict_utils import merge_dict, get_dict_value -from yaml_utils import yaml_dump, yaml_load - -logger = logging.getLogger(__name__) - -# This Jinja2 environment allows to load templates relative to the parent template. This means that for example -# '{% include "file.yml" %}' will try to include the template from a ./file.yml -class KluctlJinja2Environment(Environment): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.add_extension("jinja2.ext.loopcontrols") - - """Override join_path() to enable relative template paths.""" - """See https://stackoverflow.com/a/3655911/7132642""" - def join_path(self, template, parent): - p = os.path.join(os.path.dirname(parent), template) - p = os.path.normpath(p) - return p.replace('\\', '/') - -def b64encode(string): - return base64.b64encode(string.encode()).decode() - -def b64decode(string): - return base64.b64decode(string.encode()).decode() - -def to_yaml(obj): - return yaml_dump(obj) - -def to_json(obj): - return json.dumps(obj) - -def from_yaml(s): - return yaml_load(s) - -@jinja2.pass_context -def render(ctx, string): - t = ctx.environment.from_string(string) - return t.render(ctx.parent) - -def sha256(s): - if not isinstance(s, bytes): - s = s.encode("utf-8") - return hashlib.sha256(s).hexdigest() - -@jinja2.pass_context -def load_template(ctx, path, **kwargs): - dir = os.path.dirname(ctx.name) - full_path = os.path.join(dir, path) - try: - t = ctx.environment.get_template(full_path.replace('\\', '/')) - except TemplateNotFound: - t = ctx.environment.get_template(path.replace('\\', '/')) - vars = merge_dict(ctx.parent, kwargs) - return t.render(vars) - -class VarNotFoundException(Exception): - pass - -@jinja2.pass_context -def get_var(ctx, path, default): - if not isinstance(path, list): - path = [path] - for p in path: - r = get_dict_value(ctx.parent, p, VarNotFoundException()) - if isinstance(r, VarNotFoundException): - continue - return r - return default - -def update_dict(a, b): - merge_dict(a, b, False) - return "" - -def raise_helper(msg): - raise TemplateError(msg) - -def debug_print(msg): - logger.info("debug_print: %s" % str(msg)) - return "" - -@jinja2.pass_context -def load_sha256(ctx: Context, path, digest_len=None): - if "__calc_sha256__" in ctx: - return "__self_sha256__" - rendered = load_template(ctx, path, __calc_sha256__=True) - hash = hashlib.sha256(rendered.encode("utf-8")).hexdigest() - if digest_len is not None: - hash = hash[:digest_len] - return hash - - -def add_jinja2_filters(jinja2_env): - jinja2_env.filters['b64encode'] = b64encode - jinja2_env.filters['b64decode'] = b64decode - jinja2_env.filters['to_yaml'] = to_yaml - jinja2_env.filters['to_json'] = to_json - jinja2_env.filters['from_yaml'] = from_yaml - jinja2_env.filters['render'] = render - jinja2_env.filters['sha256'] = sha256 - jinja2_env.globals['load_template'] = load_template - jinja2_env.globals['get_var'] = get_var - jinja2_env.globals['merge_dict'] = merge_dict - jinja2_env.globals['update_dict'] = update_dict - jinja2_env.globals['raise'] = raise_helper - jinja2_env.globals['debug_print'] = debug_print - jinja2_env.globals['load_sha256'] = load_sha256 - -def extract_template_error(e): - try: - raise e - except TemplateNotFound as e2: - return "template %s not found" % str(e2) - except: - etype, value, tb = sys.exc_info() - extracted_tb = traceback.extract_tb(tb) - found_template = None - for i, s in reversed(list(enumerate(extracted_tb))): - if not s.filename.endswith(".py"): - found_template = i - break - f = io.StringIO() - if found_template is not None: - traceback.print_list([extracted_tb[found_template]], file=f) - print("%s: %s" % (type(e).__name__, str(e)), file=f) - else: - traceback.print_exception(etype, value, tb, file=f) - return f.getvalue() diff --git a/pkg/jinja2/python_src/jsonpath_utils.py b/pkg/jinja2/python_src/jsonpath_utils.py deleted file mode 100644 index 4d7f8d494..000000000 --- a/pkg/jinja2/python_src/jsonpath_utils.py +++ /dev/null @@ -1,100 +0,0 @@ -# Add better wildcard support to jsonpath lib -import fnmatch -import logging -import os - -import ply -from jsonpath_ng import auto_id_field, jsonpath, JSONPath -from jsonpath_ng.exceptions import JsonPathParserError -from jsonpath_ng.ext.parser import ExtentedJsonPathParser -from jsonpath_ng.parser import IteratorToTokenStream - - -logger = logging.getLogger(__name__) - -def ext_reified_fields(self, datum): - result = [] - for field in self.fields: - if "*" not in field: - result.append(field) - continue - try: - fields = [f for f in datum.value.keys() if fnmatch.fnmatch(f, field)] - if auto_id_field is not None: - fields.append(auto_id_field) - result += fields - except AttributeError: - pass - return tuple(result) -jsonpath.Fields.reified_fields = ext_reified_fields - -# This class pre-creates the yacc parser to speed up things -class MyJsonPathParser(ExtentedJsonPathParser): - ''' - Dummy doc-string to avoid exceptions - ''' - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - start_symbol = 'jsonpath' - - output_directory = os.path.dirname(__file__) - try: - module_name = os.path.splitext(os.path.split(__file__)[1])[0] - except: - module_name = __name__ - - parsing_table_module = '_'.join([module_name, start_symbol, 'parsetab']) - self.parser = ply.yacc.yacc(module=self, - debug=self.debug, - tabmodule=parsing_table_module, - outputdir=output_directory, - write_tables=0, - start=start_symbol, - errorlog=logger) - - def parse_token_stream(self, token_iterator, start_symbol='jsonpath'): - assert start_symbol == "jsonpath" - return self.parser.parse(lexer = IteratorToTokenStream(token_iterator)) - - -def convert_list_to_json_path(p): - p2 = "" - for x in p: - if isinstance(x, str): - if x.isalnum(): - if p2 != "": - p2 += "." - p2 += "%s" % x - else: - if p2 == "": - p2 = "$" - if '"' in x: - p2 = "%s['%s']" % (p2, x) - else: - p2 = '%s["%s"]' % (p2, x) - else: - if p2 == "": - p2 = "$" - p2 = "%s[%d]" % (p2, x) - return p2 - -json_path_cache = {} -json_path_parser = MyJsonPathParser() - -def parse_json_path(p) -> JSONPath: - if isinstance(p, list) or isinstance(p, tuple): - p = convert_list_to_json_path(p) - - if p in json_path_cache: - return json_path_cache[p] - - try: - pp = json_path_parser.parse(p) - except JsonPathParserError as e: - raise Exception("Invalid json path '%s'. Error=%s" % (p, str(e))) - - pp = json_path_cache.setdefault(p, pp) - - return pp diff --git a/pkg/jinja2/python_src/main.py b/pkg/jinja2/python_src/main.py deleted file mode 100644 index 15916d2c3..000000000 --- a/pkg/jinja2/python_src/main.py +++ /dev/null @@ -1,34 +0,0 @@ -import json -import sys - -from jinja2_renderer import Jinja2Renderer - - -def main(): - while True: - args = sys.stdin.readline() - if not args: - break - args = json.loads(args) - - r = Jinja2Renderer(args.get("trimBlocks", False), args.get("lstripBlocks", False)) - - if args["cmd"] == "render-strings": - result = r.RenderStrings(args["templates"], args["searchDirs"] or [], args["vars"], args["strict"]) - elif args["cmd"] == "render-files": - result = r.RenderFiles(args["templates"], args["searchDirs"] or [], args["vars"], args["strict"]) - elif args["cmd"] == "exit": - break - else: - raise Exception("invalid cmd") - - result = json.dumps(result) - - sys.stdout.write(result + "\n") - sys.stdout.flush() - -if __name__ == "__main__": - try: - main() - except KeyboardInterrupt as e: - pass diff --git a/pkg/jinja2/python_src/yaml_utils.py b/pkg/jinja2/python_src/yaml_utils.py deleted file mode 100644 index ce9d8ec8d..000000000 --- a/pkg/jinja2/python_src/yaml_utils.py +++ /dev/null @@ -1,51 +0,0 @@ -import yaml - -from yaml import SafeLoader as SafeLoader, SafeDumper as SafeDumper - -def construct_value(load, node): - if not isinstance(node, yaml.ScalarNode): - raise yaml.constructor.ConstructorError( - "while constructing a value", - node.start_mark, - "expected a scalar, but found %s" % node.id, node.start_mark - ) - yield str(node.value) - - -# See https://github.com/yaml/pyyaml/issues/89 -SafeLoader.add_constructor(u'tag:yaml.org,2002:value', construct_value) - - -def multiline_str_representer(dumper, data): - if len(data.splitlines()) > 1: # check for multiline string - return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|') - return dumper.represent_scalar('tag:yaml.org,2002:str', data) - -class MultilineStrDumper(SafeDumper): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.add_representer(str, multiline_str_representer) - -def yaml_load(s): - return yaml.load(s, Loader=SafeLoader) - -def yaml_load_all(s): - return list(yaml.load_all(s, Loader=SafeLoader)) - -def yaml_load_file(path, all=False): - with open(path) as f: - if all: - y = yaml_load_all(f) - else: - y = yaml_load(f) - return y - -def yaml_dump(y, stream=None): - return yaml.dump(y, stream=stream, Dumper=MultilineStrDumper, sort_keys=False) - -def yaml_dump_all(y, stream=None): - return yaml.dump_all(y, stream=stream, Dumper=MultilineStrDumper, sort_keys=False) - -def yaml_save_file(y, path): - with open(path, mode='w') as f: - yaml_dump(y, f) diff --git a/pkg/jinja2/render_struct.go b/pkg/jinja2/render_struct.go deleted file mode 100644 index e2f06996b..000000000 --- a/pkg/jinja2/render_struct.go +++ /dev/null @@ -1,181 +0,0 @@ -package jinja2 - -import ( - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "reflect" -) - -type structStringCollectorEntry struct { - s string - setter func(s string) -} - -type structStringCollector struct { - j *Jinja2 - strings []structStringCollectorEntry -} - -func (w *structStringCollector) extractTemplateString(v reflect.Value) (string, bool) { - if v.IsZero() { - return "", false - } - - t := v.Type() - if t.Kind() == reflect.Interface || t.Kind() == reflect.Pointer { - v = v.Elem() - t = v.Type() - } - if t.Kind() == reflect.String { - s := v.String() - if !isMaybeTemplateString(s) { - return "", false - } - return v.String(), true - } - return "", false -} - -func (w *structStringCollector) addString(s string, setter func(s string)) { - w.strings = append(w.strings, structStringCollectorEntry{ - s: s, - setter: setter, - }) -} - -func (w *structStringCollector) walkStruct(v reflect.Value) error { - t := v.Type() - l := t.NumField() - - for i := 0; i < l; i++ { - f := v.Field(i) - if s, ok := w.extractTemplateString(f); ok { - w.addString(s, func(s string) { - if f.Type().Kind() == reflect.Pointer { - f.Set(reflect.ValueOf(&s)) - } else { - f.Set(reflect.ValueOf(s)) - } - }) - } else { - err := w.walkValue(f) - if err != nil { - return err - } - } - } - return nil -} - -func (w *structStringCollector) walkList(v reflect.Value) error { - l := v.Len() - for i := 0; i < l; i++ { - e := v.Index(i) - if s, ok := w.extractTemplateString(e); ok { - w.addString(s, func(s string) { - if e.Type().Kind() == reflect.Pointer { - e.Set(reflect.ValueOf(&s)) - } else { - e.Set(reflect.ValueOf(s)) - } - }) - } else { - err := w.walkValue(e) - if err != nil { - return err - } - } - } - return nil -} - -func (w *structStringCollector) walkMap(v reflect.Value) error { - it := v.MapRange() - for it.Next() { - ik := it.Key() - iv := it.Value() - - if s, ok := w.extractTemplateString(iv); ok { - if isMaybeTemplateString(s) { - w.addString(s, func(s string) { - if iv.Type().Kind() == reflect.Pointer { - v.SetMapIndex(ik, reflect.ValueOf(&s)) - } else { - v.SetMapIndex(ik, reflect.ValueOf(s)) - } - }) - } - } else { - err := w.walkValue(iv) - if err != nil { - return err - } - } - } - return nil -} - -func (w *structStringCollector) walkValue(v reflect.Value) error { - if v.IsZero() { - return nil - } - - v = reflect.Indirect(v) - t := v.Type() - - switch t.Kind() { - case reflect.Interface, reflect.Pointer: - return w.walkValue(v.Elem()) - case reflect.Slice, reflect.Array: - return w.walkList(v) - case reflect.Struct: - return w.walkStruct(v) - case reflect.Map: - return w.walkMap(v) - } - return nil -} - -func (j *Jinja2) RenderStruct(o interface{}, vars *uo.UnstructuredObject) (bool, bool, error) { - w := &structStringCollector{j: j} - v := reflect.ValueOf(o) - err := w.walkValue(v) - if err != nil { - return false, false, err - } - - var jobs []*RenderJob - - for _, sv := range w.strings { - jobs = append(jobs, &RenderJob{Template: sv.s}) - } - - err = j.RenderStrings(jobs, nil, vars) - if err != nil { - return false, false, err - } - - changed := false - moreTemplates := false - - var errors []error - for i, sv := range w.strings { - job := jobs[i] - if job.Error != nil { - errors = append(errors, job.Error) - } else { - if sv.s != *job.Result { - sv.setter(*job.Result) - changed = true - if isMaybeTemplateString(*job.Result) { - moreTemplates = true - } - } - } - } - if len(errors) != 0 { - return false, false, utils.NewErrorListOrNil(errors) - } - - return changed, moreTemplates, nil -} diff --git a/pkg/jinja2/source.go b/pkg/jinja2/source.go deleted file mode 100644 index b80377261..000000000 --- a/pkg/jinja2/source.go +++ /dev/null @@ -1,93 +0,0 @@ -package jinja2 - -import ( - "crypto/sha256" - "embed" - "encoding/binary" - "encoding/hex" - "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/rogpeppe/go-internal/lockedfile" - "io/fs" - "os" - "path/filepath" -) - -//go:embed python_src -var _pythonSrc embed.FS -var pythonSrc, _ = fs.Sub(_pythonSrc, "python_src") - -var pythonSrcExtracted string - -func init() { - srcDir, err := extractSource() - if err != nil { - panic(err) - } - pythonSrcExtracted = srcDir -} - -func extractSource() (string, error) { - hash := calcEmbeddedHash(pythonSrc) - - targetPath := filepath.Join(utils.GetTmpBaseDir(), fmt.Sprintf("jinja2-%s", hash[:16])) - - lock, err := lockedfile.Create(targetPath + ".lock") - if err != nil { - return "", err - } - defer lock.Close() - - err = fs.WalkDir(pythonSrc, ".", func(path string, d fs.DirEntry, err error) error { - if d == nil || d.IsDir() { - return nil - } - data, err := fs.ReadFile(pythonSrc, path) - if err != nil { - return err - } - targetPath2 := filepath.Join(targetPath, path) - err = os.MkdirAll(filepath.Dir(targetPath2), 0o755) - if err != nil { - return err - } - - err = os.WriteFile(targetPath2+".tmp", data, 0o644) - if err != nil { - return err - } - err = os.Rename(targetPath2+".tmp", targetPath2) - if err != nil { - return err - } - return nil - }) - if err != nil { - return "", err - } - - return targetPath, nil -} - -func calcEmbeddedHash(fs1 fs.FS) string { - h := sha256.New() - err := fs.WalkDir(fs1, ".", func(path string, d fs.DirEntry, err error) error { - _ = binary.Write(h, binary.LittleEndian, path) - if d.IsDir() { - _ = binary.Write(h, binary.LittleEndian, "dir") - } else { - _ = binary.Write(h, binary.LittleEndian, "regular") - data, err := fs.ReadFile(fs1, path) - if err != nil { - panic(err) - } - _ = binary.Write(h, binary.LittleEndian, len(data)) - _ = binary.Write(h, binary.LittleEndian, data) - } - return nil - }) - if err != nil { - panic(err) - } - return hex.EncodeToString(h.Sum(nil)) -} diff --git a/pkg/kluctl_jinja2/embed.go b/pkg/kluctl_jinja2/embed.go new file mode 100644 index 000000000..a948f8133 --- /dev/null +++ b/pkg/kluctl_jinja2/embed.go @@ -0,0 +1,8 @@ +package kluctl_jinja2 + +import ( + "embed" +) + +//go:embed all:ext +var ExtSource embed.FS diff --git a/pkg/kluctl_jinja2/ext/__init__.py b/pkg/kluctl_jinja2/ext/__init__.py new file mode 100644 index 000000000..4e1189132 --- /dev/null +++ b/pkg/kluctl_jinja2/ext/__init__.py @@ -0,0 +1,3 @@ +from .images_ext import ImagesExtension + +images = ImagesExtension diff --git a/pkg/kluctl_jinja2/ext/images_ext.py b/pkg/kluctl_jinja2/ext/images_ext.py new file mode 100644 index 000000000..4c363b194 --- /dev/null +++ b/pkg/kluctl_jinja2/ext/images_ext.py @@ -0,0 +1,53 @@ +import base64 +import json + +from jinja2.ext import Extension + +begin_placeholder = "XXXXXbegin_get_image_" +end_placeholder = "_end_get_imageXXXXX" + +class ImagesExtension(Extension): + def __init__(self, environment): + super().__init__(environment) + environment.globals.update(self.build_images_vars()) + + def get_image_wrapper(self, image, latest_version=None): + if latest_version is None: + latest_version = "semver()" + placeholder = { + "image": image, + "latestVersion": str(latest_version), + } + j = json.dumps(placeholder) + j = base64.b64encode(j.encode("utf8")).decode("utf8") + j = begin_placeholder + j + end_placeholder + return j + + def build_images_vars(self): + def semver(allow_no_nums=False): + return "semver(allow_no_nums=%s)" % allow_no_nums + + def prefix(s, suffix=None): + if suffix is None: + return "prefix(\"%s\")" % s + else: + return "prefix(\"%s\", suffix=\"%s\")" % (s, suffix) + + def number(): + return "number()" + + def regex(r): + return "regex(\"%s\")" % r + + vars = { + 'images': { + 'get_image': self.get_image_wrapper, + }, + 'version': { + 'semver': semver, + 'prefix': prefix, + 'number': number, + 'regex': regex, + }, + } + return vars diff --git a/pkg/kluctl_jinja2/jinja2.go b/pkg/kluctl_jinja2/jinja2.go new file mode 100644 index 000000000..13b04154d --- /dev/null +++ b/pkg/kluctl_jinja2/jinja2.go @@ -0,0 +1,23 @@ +package kluctl_jinja2 + +import ( + "github.com/kluctl/go-embed-python/embed_util" + x "github.com/kluctl/go-jinja2" +) + +const parallelism = 4 + +func NewKluctlJinja2(strict bool) (*x.Jinja2, error) { + extSrc, err := embed_util.NewEmbeddedFiles(ExtSource, "kluctl-ext") + if err != nil { + return nil, err + } + + return x.NewJinja2("kluctl", + parallelism, + x.WithStrict(strict), + x.WithExtension("jinja2.ext.loopcontrols"), + x.WithExtension("go_jinja2.ext.kluctl"), + x.WithExtension("ext.images_ext.ImagesExtension"), + x.WithPythonPath(extSrc.GetExtractedPath())) +} diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 78cde9970..d5d8ec704 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -2,7 +2,7 @@ package kluctl_project import ( "context" - "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/status" ) diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index da067d70f..e0495d225 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -3,8 +3,8 @@ package kluctl_project import ( "context" "fmt" + "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/git/repocache" - "github.com/kluctl/kluctl/v2/pkg/jinja2" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "regexp" diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 18ac0153a..17f3f784c 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -97,8 +97,8 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) error { } } - changed, moreTemplates, err := c.J2.RenderStruct(target, varsCtx.Vars) - if err == nil && (!changed || !moreTemplates) { + changed, err := varsCtx.RenderStruct(target) + if err == nil && !changed { return nil } } diff --git a/pkg/utils/uo/uo.go b/pkg/utils/uo/uo.go index 0356a98ac..8630e1a15 100644 --- a/pkg/utils/uo/uo.go +++ b/pkg/utils/uo/uo.go @@ -63,6 +63,22 @@ func (uo *UnstructuredObject) ToStruct(out interface{}) error { return yaml.ReadYamlBytes(b, out) } +// ToMap will ensure that only plain go values are returned, meaning that all internal structs are converted +// to maps +func (uo *UnstructuredObject) ToMap() (map[string]any, error) { + b, err := yaml.WriteYamlBytes(uo.Object) + if err != nil { + return nil, err + } + + var ret map[string]any + err = yaml.ReadYamlBytes(b, &ret) + if err != nil { + return nil, err + } + return ret, nil +} + func FromString(s string) (*UnstructuredObject, error) { o := New() err := yaml.ReadYamlString(s, &o.Object) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index f704bf0f6..ae5ad7ca1 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "fmt" "github.com/jinzhu/copier" - "github.com/kluctl/kluctl-python-deps/pkg/utils" "io/fs" "os" "os/user" @@ -42,7 +41,7 @@ func createTmpBaseDir() { } func ensureDir(path string, perm fs.FileMode, allowCreate bool) { - if utils.Exists(path) { + if Exists(path) { st, err := os.Lstat(path) if err != nil { panic(err) diff --git a/pkg/vars/vars.go b/pkg/vars/vars.go index c29d4ba18..ecb960224 100644 --- a/pkg/vars/vars.go +++ b/pkg/vars/vars.go @@ -1,7 +1,7 @@ package vars import ( - "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" ) @@ -44,8 +44,33 @@ func (vc *VarsCtx) UpdateChildFromStruct(child string, o interface{}) error { return nil } +func (vc *VarsCtx) RenderString(t string) (string, error) { + globals, err := vc.Vars.ToMap() + if err != nil { + return "", err + } + return vc.J2.RenderString(t, + jinja2.WithGlobals(globals), + ) +} + +func (vc *VarsCtx) RenderStruct(o interface{}) (bool, error) { + globals, err := vc.Vars.ToMap() + if err != nil { + return false, err + } + return vc.J2.RenderStruct(o, jinja2.WithGlobals(globals)) +} + func (vc *VarsCtx) RenderYamlFile(p string, searchDirs []string, out interface{}) error { - ret, err := vc.J2.RenderFile(p, searchDirs, vc.Vars) + globals, err := vc.Vars.ToMap() + if err != nil { + return err + } + ret, err := vc.J2.RenderFile(p, + jinja2.WithSearchDirs(searchDirs), + jinja2.WithGlobals(globals), + ) if err != nil { return err } @@ -58,6 +83,10 @@ func (vc *VarsCtx) RenderYamlFile(p string, searchDirs []string, out interface{} return nil } -func (vc *VarsCtx) RenderDirectory(rootDir string, searchDirs []string, relSourceDir string, excludePatterns []string, subdir string, targetDir string) error { - return vc.J2.RenderDirectory(rootDir, searchDirs, relSourceDir, excludePatterns, subdir, targetDir, vc.Vars) +func (vc *VarsCtx) RenderDirectory(sourceDir string, targetDir string, excludePatterns []string, searchDirs []string) error { + globals, err := vc.Vars.ToMap() + if err != nil { + return err + } + return vc.J2.RenderDirectory(sourceDir, targetDir, excludePatterns, jinja2.WithGlobals(globals), jinja2.WithSearchDirs(searchDirs)) } diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 49ffe3e9b..338e1b446 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "fmt" securejoin "github.com/cyphar/filepath-securejoin" + "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" @@ -61,7 +62,12 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear return err } - _, _, err = varsCtx.J2.RenderStruct(&source, varsCtx.Vars) + globals, err := varsCtx.Vars.ToMap() + if err != nil { + return err + } + + _, err = varsCtx.J2.RenderStruct(&source, jinja2.WithGlobals(globals)) if err != nil { return err } @@ -272,7 +278,7 @@ func (v *VarsLoader) loadFromString(varsCtx *VarsCtx, s string, secretType strin } func (v *VarsLoader) renderYamlString(varsCtx *VarsCtx, s string, out interface{}) error { - ret, err := varsCtx.J2.RenderString(s, nil, varsCtx.Vars) + ret, err := varsCtx.RenderString(s) if err != nil { return err } diff --git a/pkg/vars/vars_test.go b/pkg/vars/vars_test.go index 66e7bb092..46c822e8b 100644 --- a/pkg/vars/vars_test.go +++ b/pkg/vars/vars_test.go @@ -1,14 +1,15 @@ package vars import ( - "github.com/kluctl/kluctl/v2/pkg/jinja2" + "github.com/kluctl/go-jinja2" + "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" "testing" ) func newJinja2Must(t *testing.T) *jinja2.Jinja2 { - j2, err := jinja2.NewJinja2() + j2, err := kluctl_jinja2.NewKluctlJinja2(true) if err != nil { t.Fatal(err) } From 6b9961a6d49d61f88ed57d670ddb53be405f4ea1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Sep 2022 23:31:52 +0200 Subject: [PATCH 0336/2268] fix: Run go mod tidy --- go.sum | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.sum b/go.sum index d4cf7fb99..09884bf43 100644 --- a/go.sum +++ b/go.sum @@ -670,6 +670,8 @@ github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQan github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= +github.com/kluctl/go-jinja2 v0.0.0-20220908201753-f33cd6224f2b h1:6IGYwbtc6VR5nyUckJvgtnIlrg7dzNUbkHtVvTfPVkY= +github.com/kluctl/go-jinja2 v0.0.0-20220908201753-f33cd6224f2b/go.mod h1:8deI9sssmQf1CiCnyRJoBgfIw4Hh3+WsHmmO9+xIIO0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= From 890e75da7d4ea2a42b9947f8a3d353c8e54b3e77 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Sep 2022 09:26:41 +0200 Subject: [PATCH 0337/2268] ci: Remove python from Github workflows --- .github/workflows/release.yml | 4 ---- .github/workflows/tests.yml | 4 ---- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c7982a03..bc3074b45 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,10 +24,6 @@ jobs: uses: actions/setup-go@v2 with: go-version: 1.19 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.10.2 - name: Setup QEMU uses: docker/setup-qemu-action@v1 - name: Setup Docker Buildx diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 26e089c85..fad114c1a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,10 +16,6 @@ jobs: - uses: actions/setup-go@v2 with: go-version: '1.19' - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.10.2 - uses: actions/cache@v2 with: path: | From 21c7a3d95ef4b919c11c117ef5eb31c442355c3a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Sep 2022 09:52:02 +0200 Subject: [PATCH 0338/2268] fix: Add deployment item dir to search dir --- pkg/deployment/deployment_item.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index e97bb48d2..a5709038c 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -145,11 +145,15 @@ func (di *DeploymentItem) render(forSeal bool) error { excludePatterns = append(excludePatterns, "**.sealme") } + searchDirs := di.Project.getRenderSearchDirs() + // also add deployment item dir to search dirs + searchDirs = append([]string{*di.dir}, searchDirs...) + return varsCtx.RenderDirectory( filepath.Join(di.Project.source.dir, di.RelToSourceItemDir), di.RenderedDir, excludePatterns, - di.Project.getRenderSearchDirs(), + searchDirs, ) } From bdba594f019420270977de4e48fdbb214f108beb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Sep 2022 09:52:12 +0200 Subject: [PATCH 0339/2268] fix: Fix deadloc in status handler --- pkg/status/status_handler.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index 34f2bd717..3f10a4763 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -177,14 +177,15 @@ func (sl *statusLine) Update(message string) { } func (sl *statusLine) end(barOverride string) { + sl.mutex.Lock() sl.barOverride = barOverride sl.current = sl.total + sl.mutex.Unlock() + sl.l.Remove(true) } func (sl *statusLine) End(result EndResult) { - sl.mutex.Lock() - defer sl.mutex.Unlock() switch result { case EndSuccess: sl.end(withColor("green", "✓")) From 9a386611f2c6520cdcb795a2d6565b576822f0b7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Sep 2022 10:59:16 +0200 Subject: [PATCH 0340/2268] feat: Remove Helm from docker image --- Dockerfile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index cd4912bac..1585cdfb3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,11 +4,6 @@ RUN apk add --no-cache ca-certificates curl ARG ARCH=linux-amd64 -ENV HELM_VERSION=v3.8.2 -RUN wget -O helm.tar.gz https://get.helm.sh/helm-$HELM_VERSION-$ARCH.tar.gz && \ - tar xzf helm.tar.gz && \ - mv $ARCH/helm / - # We must use a glibc based distro due to embedded python not supporting musl libc for aarch64 FROM debian:bullseye-slim COPY --from=builder /helm /usr/bin From ff96e257f53eef03c17fe9e5b10ea4fcfd05b0aa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 13 Sep 2022 15:34:56 +0200 Subject: [PATCH 0341/2268] chore: Add /vendor to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8079ea478..276ce77b0 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ /e2e.test* /bin /reports +/vendor dist/ From 90d639b6e6d525ae8b01d8e21497428a6f901b9b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Sep 2022 11:41:39 +0200 Subject: [PATCH 0342/2268] fix: Don't use user.Current() on Linux/Mac as it requires CGO/$USER --- pkg/utils/utils.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index ae5ad7ca1..b28b992eb 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -9,6 +9,7 @@ import ( "os" "os/user" "path/filepath" + "runtime" "strconv" "sync" ) @@ -29,12 +30,17 @@ func createTmpBaseDir() { ensureDir(dir, 0o777, true) // every user gets its own tmp dir - u, err := user.Current() - if err != nil { - panic(err) + if runtime.GOOS == "windows" { + u, err := user.Current() + if err != nil { + panic(err) + } + dir = filepath.Join(dir, u.Uid) + } else { + uid := os.Getuid() + dir = filepath.Join(dir, fmt.Sprintf("%d", uid)) } - dir = filepath.Join(dir, u.Uid) ensureDir(dir, 0o700, true) tmpBaseDir = dir From 7bb2a79621c55039a6507fbbb488da9d5d9fd43e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 19 Sep 2022 16:41:57 +0200 Subject: [PATCH 0343/2268] fix: Also treat --force-replace-on-error as --replace-on-error --- pkg/deployment/utils/apply_utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index baca9e10f..9b3ce4101 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -208,7 +208,7 @@ func (a *ApplyUtil) retryApplyForceReplace(x *uo.UnstructuredObject, hook bool, func (a *ApplyUtil) retryApplyWithReplace(x *uo.UnstructuredObject, hook bool, remoteObject *uo.UnstructuredObject, applyError error) { ref := x.GetK8sRef() - if !a.o.ReplaceOnError || remoteObject == nil { + if (!a.o.ReplaceOnError && !a.o.ForceReplaceOnError) || remoteObject == nil { a.HandleError(ref, applyError) return } From 80df7d116430ed9fbe258bd1d138ccdf0c1e2a28 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 19 Sep 2022 16:42:15 +0200 Subject: [PATCH 0344/2268] fix: Upgrade go-jinja2 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 93aaf886c..6faf03396 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 - github.com/kluctl/go-jinja2 v0.0.0-20220908201753-f33cd6224f2b + github.com/kluctl/go-jinja2 v0.0.0-20220915092153-31a9e083bdf2 github.com/mattn/go-isatty v0.0.16 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 diff --git a/go.sum b/go.sum index 09884bf43..9c5246c17 100644 --- a/go.sum +++ b/go.sum @@ -670,8 +670,8 @@ github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQan github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= -github.com/kluctl/go-jinja2 v0.0.0-20220908201753-f33cd6224f2b h1:6IGYwbtc6VR5nyUckJvgtnIlrg7dzNUbkHtVvTfPVkY= -github.com/kluctl/go-jinja2 v0.0.0-20220908201753-f33cd6224f2b/go.mod h1:8deI9sssmQf1CiCnyRJoBgfIw4Hh3+WsHmmO9+xIIO0= +github.com/kluctl/go-jinja2 v0.0.0-20220915092153-31a9e083bdf2 h1:qjiBSePbrBSWaJhvymlcrM3TW/PhbFoXvOfgKuQsDr0= +github.com/kluctl/go-jinja2 v0.0.0-20220915092153-31a9e083bdf2/go.mod h1:8deI9sssmQf1CiCnyRJoBgfIw4Hh3+WsHmmO9+xIIO0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= From 5f283c9ec542003255616be7e966916c18d2311d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Sep 2022 21:26:03 +0200 Subject: [PATCH 0345/2268] feat: Remove dynamicArgs pattern support --- pkg/kluctl_project/project.go | 14 -------------- pkg/types/kluctl_project.go | 3 +-- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index e0495d225..31e833e3a 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -7,7 +7,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git/repocache" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" - "regexp" ) type LoadedKluctlProject struct { @@ -70,18 +69,5 @@ func (c *LoadedKluctlProject) CheckDynamicArg(target *types2.Target, argName str return fmt.Errorf("dynamic argument %s is not allowed for target", argName) } - argPattern := ".*" - if dynArg.Pattern != nil { - argPattern = *dynArg.Pattern - } - argPattern = fmt.Sprintf("^%s$", argPattern) - - m, err := regexp.MatchString(argPattern, argValue) - if err != nil { - return err - } - if !m { - return fmt.Errorf("dynamic argument %s does not match required pattern '%s", argName, argPattern) - } return nil } diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 9ecb1fcdd..f5fb852ad 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -7,8 +7,7 @@ import ( ) type DynamicArg struct { - Name string `yaml:"name" validate:"required"` - Pattern *string `yaml:"pattern,omitempty"` + Name string `yaml:"name" validate:"required"` } type ExternalTargetConfig struct { From 834c11f258fedcdc6ce232240c5d67b81f6c196e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Sep 2022 23:11:06 +0200 Subject: [PATCH 0346/2268] fix: Don't treat final error as formatted string --- cmd/kluctl/commands/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 7e3928ac8..f7eea355e 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -254,7 +254,7 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif sh := status.FromContext(cliCtx) if err != nil { - status.Error(cliCtx, err.Error()) + status.Error(cliCtx, "%s", err.Error()) sh.Stop() os.Exit(1) } From e6c028ca3bcda3996df6409d5c1e37543a1f251a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Sep 2022 23:11:32 +0200 Subject: [PATCH 0347/2268] fix: Fix diff after downscale --- pkg/deployment/commands/downscale.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/deployment/commands/downscale.go b/pkg/deployment/commands/downscale.go index 82e89b2cc..184f08029 100644 --- a/pkg/deployment/commands/downscale.go +++ b/pkg/deployment/commands/downscale.go @@ -6,7 +6,6 @@ import ( utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" - k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "sync" ) @@ -34,8 +33,6 @@ func (cmd *DownscaleCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types ad := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, &utils2.ApplyUtilOptions{}) - appliedObjects := make(map[k8s2.ObjectRef]*uo.UnstructuredObject) - for _, d := range cmd.c.Deployments { if !d.CheckInclusionForDeploy() { continue @@ -62,7 +59,7 @@ func (cmd *DownscaleCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types } wg.Wait() - du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, appliedObjects) + du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, ad.GetAppliedObjectsMap()) du.Diff() return &types.CommandResult{ From 0c9fedec1fd44efce4a9ec08d24f85fc17660bfd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Sep 2022 23:12:13 +0200 Subject: [PATCH 0348/2268] feat: Treat -a values as yaml --- pkg/deployment/external_args.go | 11 ++++++++--- pkg/kluctl_project/target_context.go | 6 +++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index e3c6600ed..53e4a292d 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -28,16 +28,21 @@ func ParseArgs(argsList []string) (map[string]string, error) { return args, nil } -func ConvertArgsToVars(args map[string]string) *uo.UnstructuredObject { +func ConvertArgsToVars(args map[string]string) (*uo.UnstructuredObject, error) { vars := uo.New() for n, v := range args { var p []interface{} for _, x := range strings.Split(n, ".") { p = append(p, x) } - _ = vars.SetNestedField(v, p...) + var j any + err := yaml.ReadYamlString(v, &j) + if err != nil { + return nil, err + } + _ = vars.SetNestedField(j, p...) } - return vars + return vars, nil } func CheckRequiredDeployArgs(dir string, varsCtx *vars.VarsCtx, deployArgs *uo.UnstructuredObject) error { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 47d187a36..d8624f8ba 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -151,7 +151,11 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin } } } - allArgs.Merge(deployment.ConvertArgsToVars(args)) + convertedArgs, err := deployment.ConvertArgsToVars(args) + if err != nil { + return doError(err) + } + allArgs.Merge(convertedArgs) if target != nil { if target.Args != nil { allArgs.Merge(target.Args) From ad4535223b7b722b195bec6ab66feff32680b8b9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Sep 2022 23:12:48 +0200 Subject: [PATCH 0349/2268] feat: Allow children of dynamic args to be set via -a --- pkg/kluctl_project/project.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 31e833e3a..65dc137da 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git/repocache" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" + "strings" ) type LoadedKluctlProject struct { @@ -60,7 +61,7 @@ func (c *LoadedKluctlProject) FindDynamicTarget(name string) (*types2.DynamicTar func (c *LoadedKluctlProject) CheckDynamicArg(target *types2.Target, argName string, argValue string) error { var dynArg *types2.DynamicArg for _, x := range target.DynamicArgs { - if x.Name == argName { + if x.Name == argName || strings.HasPrefix(argName, x.Name+".") { dynArg = &x break } From 42e5a451dd52a10c9a017c3896bd82d0e75d4b93 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Sep 2022 23:13:53 +0200 Subject: [PATCH 0350/2268] feat: Allow nested defaults for args --- pkg/deployment/external_args.go | 20 ++++++++++++++------ pkg/kluctl_project/target_context.go | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index 53e4a292d..955b74b27 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -45,7 +45,7 @@ func ConvertArgsToVars(args map[string]string) (*uo.UnstructuredObject, error) { return vars, nil } -func CheckRequiredDeployArgs(dir string, varsCtx *vars.VarsCtx, deployArgs *uo.UnstructuredObject) error { +func LoadDeploymentArgs(dir string, varsCtx *vars.VarsCtx, deployArgs *uo.UnstructuredObject) error { // First try to load the config without templating to avoid getting errors while rendering because required // args were not set. Otherwise we won't be able to iterator through the 'args' array in the deployment.yml // when the rendering error is actually args related. @@ -69,6 +69,19 @@ func CheckRequiredDeployArgs(dir string, varsCtx *vars.VarsCtx, deployArgs *uo.U return nil } + // load defaults + defaults := uo.New() + for _, a := range conf.Args { + if a.Default != nil { + a2 := uo.FromMap(map[string]interface{}{ + a.Name: a.Default, + }) + defaults.Merge(a2) + } + } + defaults.Merge(deployArgs) + *deployArgs = *defaults + err = checkRequiredArgs(conf.Args, deployArgs) if err != nil { return err @@ -86,11 +99,6 @@ func checkRequiredArgs(argsDef []*types.DeploymentArg, args *uo.UnstructuredObje if !found { if a.Default == nil { return fmt.Errorf("required argument %s not set", a.Name) - } else { - err := args.SetNestedField(a.Default, p...) - if err != nil { - return err - } } } } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index d8624f8ba..023bf089a 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -167,7 +167,7 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin } } - err = deployment.CheckRequiredDeployArgs(p.DeploymentDir, varsCtx, allArgs) + err = deployment.LoadDeploymentArgs(p.DeploymentDir, varsCtx, allArgs) if err != nil { return doError(err) } From 97bc348f027b1739bb99cb69df56e2efcb02f72a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Sep 2022 23:14:26 +0200 Subject: [PATCH 0351/2268] feat: Implement kluctl.io/delete annotation --- pkg/deployment/utils/apply_utils.go | 16 +++++++++++++--- pkg/deployment/utils/hooks_util.go | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 9b3ce4101..58b32270f 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -412,7 +412,7 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo } func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { - var toDelete []k8s2.ObjectRef + toDelete := map[k8s2.ObjectRef]bool{} for _, x := range d.Config.DeleteObjects { for _, gvk := range a.k.Resources.GetFilteredGVKs(k8s.BuildGVKFilter(x.Group, nil, x.Kind)) { ref := k8s2.ObjectRef{ @@ -420,7 +420,12 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { Name: x.Name, Namespace: x.Namespace, } - toDelete = append(toDelete, ref) + toDelete[ref] = true + } + } + for _, x := range d.Objects { + if utils.ParseBoolOrFalse(x.GetK8sAnnotation("kluctl.io/delete")) { + toDelete[x.GetK8sRef()] = true } } @@ -438,6 +443,9 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { if h.GetHook(o) != nil { continue } + if _, ok := toDelete[o.GetK8sRef()]; ok { + continue + } applyObjects = append(applyObjects, o) } @@ -462,10 +470,12 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { if len(toDelete) != 0 { a.sctx.InfoFallback("Deleting %d objects", len(toDelete)) - for i, ref := range toDelete { + i := 0 + for ref := range toDelete { a.sctx.Update(fmt.Sprintf("Deleting object %s (%d of %d)", ref.String(), i+1, len(toDelete))) a.DeleteObject(ref, false) a.sctx.Increment() + i++ } } diff --git a/pkg/deployment/utils/hooks_util.go b/pkg/deployment/utils/hooks_util.go index b811962c7..9acbb6178 100644 --- a/pkg/deployment/utils/hooks_util.go +++ b/pkg/deployment/utils/hooks_util.go @@ -156,6 +156,10 @@ func (u *HooksUtil) GetHook(o *uo.UnstructuredObject) *hook { return ret } + if utils.ParseBoolOrFalse(o.GetK8sAnnotation("kluctl.io/delete")) { + return nil + } + hooks := getSet("kluctl.io/hook") for h := range hooks { if utils.FindStrInSlice(supportedKluctlHooks, h) == -1 { From b844b865c5db9634dd00e85e0ffada40f7bcdb3e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Sep 2022 23:41:10 +0200 Subject: [PATCH 0352/2268] fix: Don'tr treat string array arguments as CSV --- cmd/kluctl/commands/cobra_utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index b8fae0c44..1d2fa005a 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -171,7 +171,7 @@ func (c *rootCommand) buildCobraArg(cg *commandAndGroups, f reflect.StructField, if defaultValue != "" { return fmt.Errorf("default not supported for slices") } - cg.cmd.PersistentFlags().StringSliceVarP(v2.(*[]string), name, shortFlag, nil, help) + cg.cmd.PersistentFlags().StringArrayVarP(v2.(*[]string), name, shortFlag, nil, help) case *bool: parsedDefault := false if defaultValue != "" { From e96b775ee87bf305b53adfd01ecc31be7b4783a0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 08:56:03 +0200 Subject: [PATCH 0353/2268] refactor: Pass UnstructedObject into NewTargetContext for vars Instead of map[string]string. This allows arbitrary yaml values to be passed. --- cmd/kluctl/commands/utils.go | 6 +++++- pkg/kluctl_project/project.go | 2 +- pkg/kluctl_project/target_context.go | 15 ++++++--------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 9b50739d4..6a6efa30f 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -145,6 +145,10 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm if err != nil { return err } + optionArgs2, err := deployment.ConvertArgsToVars(optionArgs) + if err != nil { + return err + } renderOutputDir := args.renderOutputDirFlags.RenderOutputDir if renderOutputDir == "" { @@ -163,7 +167,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm targetCtx, err := p.NewTargetContext(ctx, args.targetFlags.Target, clusterName, args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, - optionArgs, args.forSeal, images, inclusion, + optionArgs2, args.forSeal, images, inclusion, renderOutputDir) if err != nil { return err diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 65dc137da..bca9ab1a3 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -58,7 +58,7 @@ func (c *LoadedKluctlProject) FindDynamicTarget(name string) (*types2.DynamicTar return nil, fmt.Errorf("target %s not existent in kluctl project config", name) } -func (c *LoadedKluctlProject) CheckDynamicArg(target *types2.Target, argName string, argValue string) error { +func (c *LoadedKluctlProject) CheckDynamicArg(target *types2.Target, argName string, argValue any) error { var dynArg *types2.DynamicArg for _, x := range target.DynamicArgs { if x.Name == argName || strings.HasPrefix(argName, x.Name+".") { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 023bf089a..37e87f56f 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -25,7 +25,7 @@ type TargetContext struct { DeploymentCollection *deployment.DeploymentCollection } -func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName string, clusterName *string, dryRun bool, args map[string]string, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { +func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName string, clusterName *string, dryRun bool, externalArgs *uo.UnstructuredObject, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { deploymentDir, err := filepath.Abs(p.DeploymentDir) if err != nil { return nil, err @@ -42,7 +42,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s images.PrependFixedImages(target.Images) } - varsCtx, clientConfig, clusterContext, err := p.buildVars(target, clusterName, args, forSeal) + varsCtx, clientConfig, clusterContext, err := p.buildVars(target, clusterName, externalArgs, forSeal) if err != nil { return nil, err } @@ -103,7 +103,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s return targetCtx, nil } -func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *string, args map[string]string, forSeal bool) (*vars.VarsCtx, *rest.Config, string, error) { +func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *string, externalArgs *uo.UnstructuredObject, forSeal bool) (*vars.VarsCtx, *rest.Config, string, error) { doError := func(err error) (*vars.VarsCtx, *rest.Config, string, error) { return nil, nil, "", err } @@ -144,18 +144,15 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin allArgs := uo.New() if target != nil { - for argName, argValue := range args { + for argName, argValue := range externalArgs.Object { err = p.CheckDynamicArg(target, argName, argValue) if err != nil { return doError(err) } } } - convertedArgs, err := deployment.ConvertArgsToVars(args) - if err != nil { - return doError(err) - } - allArgs.Merge(convertedArgs) + + allArgs.Merge(externalArgs) if target != nil { if target.Args != nil { allArgs.Merge(target.Args) From 0ec1e7c47c1e1ff78f93121a068eb2fceb9d15a3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 08:59:17 +0200 Subject: [PATCH 0354/2268] feat: Enable time extension for jinja2 templating --- go.mod | 2 +- go.sum | 4 ++-- pkg/kluctl_jinja2/jinja2.go | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6faf03396..c1423545f 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 - github.com/kluctl/go-jinja2 v0.0.0-20220915092153-31a9e083bdf2 + github.com/kluctl/go-jinja2 v0.0.0-20220922065727-cf67f5b7f26d github.com/mattn/go-isatty v0.0.16 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 diff --git a/go.sum b/go.sum index 9c5246c17..1c0792b44 100644 --- a/go.sum +++ b/go.sum @@ -670,8 +670,8 @@ github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQan github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= -github.com/kluctl/go-jinja2 v0.0.0-20220915092153-31a9e083bdf2 h1:qjiBSePbrBSWaJhvymlcrM3TW/PhbFoXvOfgKuQsDr0= -github.com/kluctl/go-jinja2 v0.0.0-20220915092153-31a9e083bdf2/go.mod h1:8deI9sssmQf1CiCnyRJoBgfIw4Hh3+WsHmmO9+xIIO0= +github.com/kluctl/go-jinja2 v0.0.0-20220922065727-cf67f5b7f26d h1:pfyMHiDIYskxhJFlh1u1Xi3qHKGzg8Vna5hsRm7Cz0I= +github.com/kluctl/go-jinja2 v0.0.0-20220922065727-cf67f5b7f26d/go.mod h1:8deI9sssmQf1CiCnyRJoBgfIw4Hh3+WsHmmO9+xIIO0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/pkg/kluctl_jinja2/jinja2.go b/pkg/kluctl_jinja2/jinja2.go index 13b04154d..8f81ebd5e 100644 --- a/pkg/kluctl_jinja2/jinja2.go +++ b/pkg/kluctl_jinja2/jinja2.go @@ -18,6 +18,7 @@ func NewKluctlJinja2(strict bool) (*x.Jinja2, error) { x.WithStrict(strict), x.WithExtension("jinja2.ext.loopcontrols"), x.WithExtension("go_jinja2.ext.kluctl"), + x.WithExtension("go_jinja2.ext.time"), x.WithExtension("ext.images_ext.ImagesExtension"), x.WithPythonPath(extSrc.GetExtractedPath())) } From 931a04fd5bf276974bf4e2bc47f7b21f6ffb49dd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 09:03:22 +0200 Subject: [PATCH 0355/2268] chore: Run go get -u ./... --- go.mod | 70 ++++++++++++++++++++++++++-------------------------- go.sum | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index c1423545f..b36010d39 100644 --- a/go.mod +++ b/go.mod @@ -5,19 +5,19 @@ go 1.19 require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 - github.com/aws/aws-sdk-go v1.44.89 + github.com/aws/aws-sdk-go v1.44.103 github.com/bitnami-labs/sealed-secrets v0.18.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/evanphx/json-patch v5.6.0+incompatible github.com/fluxcd/pkg/kustomize v0.7.0 github.com/go-git/go-git/v5 v5.4.2 - github.com/go-playground/validator/v10 v10.11.0 + github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 github.com/google/go-containerregistry v0.11.0 github.com/gosuri/uilive v0.0.4 - github.com/hashicorp/vault/api v1.7.2 + github.com/hashicorp/vault/api v1.8.0 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 @@ -34,32 +34,32 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.12.0 + github.com/spf13/viper v1.13.0 github.com/stretchr/testify v1.8.0 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.2 - golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 - golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b - golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde - golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 - golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 + golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 + golang.org/x/net v0.0.0-20220921203646-d300de134e69 + golang.org/x/sync v0.0.0-20220907140024-f12130a52804 + golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 + golang.org/x/term v0.0.0-20220919170432-7a66f970e087 golang.org/x/text v0.3.7 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.9.4 - k8s.io/api v0.25.0 - k8s.io/apiextensions-apiserver v0.25.0 - k8s.io/apimachinery v0.25.0 - k8s.io/client-go v0.25.0 - k8s.io/klog/v2 v2.70.1 - sigs.k8s.io/kind v0.14.0 + helm.sh/helm/v3 v3.10.0 + k8s.io/api v0.25.2 + k8s.io/apiextensions-apiserver v0.25.2 + k8s.io/apimachinery v0.25.2 + k8s.io/client-go v0.25.2 + k8s.io/klog/v2 v2.80.1 + sigs.k8s.io/kind v0.15.0 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) require ( - cloud.google.com/go/compute v1.9.0 // indirect + cloud.google.com/go/compute v1.10.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.28 // indirect @@ -76,7 +76,7 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/alessio/shellescape v1.4.1 // indirect - github.com/armon/go-metrics v0.4.0 // indirect + github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -86,8 +86,8 @@ require ( github.com/cloudflare/circl v1.2.0 // indirect github.com/containerd/containerd v1.6.8 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.17+incompatible // indirect - github.com/docker/docker v20.10.17+incompatible // indirect + github.com/docker/cli v20.10.18+incompatible // indirect + github.com/docker/docker v20.10.18+incompatible // indirect github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -113,7 +113,7 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect @@ -122,7 +122,7 @@ require ( github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.3.0 // indirect + github.com/hashicorp/go-hclog v1.3.1 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.4.5 // indirect @@ -136,7 +136,7 @@ require ( github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/vault/sdk v0.5.3 // indirect + github.com/hashicorp/vault/sdk v0.6.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect @@ -145,16 +145,16 @@ require ( github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.9 // indirect + github.com/klauspost/compress v1.15.10 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.1 // indirect - github.com/lib/pq v1.10.6 // indirect + github.com/lib/pq v1.10.7 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -181,8 +181,8 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/rivo/uniseg v0.3.4 // indirect - github.com/rubenv/sql-migrate v1.1.2 // indirect + github.com/rivo/uniseg v0.4.2 // indirect + github.com/rubenv/sql-migrate v1.2.0 // indirect github.com/russross/blackfriday v1.6.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect @@ -198,21 +198,21 @@ require ( github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20220817180228-f738f5508c12 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 // indirect - golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect + golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect + golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf // indirect + google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 // indirect google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiserver v0.25.0 // indirect - k8s.io/cli-runtime v0.25.0 // indirect - k8s.io/component-base v0.25.0 // indirect + k8s.io/apiserver v0.25.2 // indirect + k8s.io/cli-runtime v0.25.2 // indirect + k8s.io/component-base v0.25.2 // indirect k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea // indirect - k8s.io/kubectl v0.25.0 // indirect + k8s.io/kubectl v0.25.2 // indirect k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 // indirect oras.land/oras-go v1.2.0 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect diff --git a/go.sum b/go.sum index 1c0792b44..5851700e6 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v1.9.0 h1:ED/FP4xv8GJw63v556/ASNc1CeeLUO2Bs8nzaHchkHg= cloud.google.com/go/compute v1.9.0/go.mod h1:lWv1h/zUWTm/LozzfTJhBSkd6ShQq8la8VeeuOEGxfY= +cloud.google.com/go/compute v1.10.0 h1:aoLIYaA1fX3ywihqpBk2APQKOo20nXsp1GEZQbx5Jk4= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -91,11 +93,14 @@ github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy86 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= @@ -134,6 +139,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -148,6 +155,8 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.44.89 h1:Xf5Pp9GsNSMRinAuWNiQd0vusXXb3IgYbNlxldhWS2Q= github.com/aws/aws-sdk-go v1.44.89/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.103 h1:tbhBHKgiZSIUkG8FcHy3wYKpPVvp65Wn7ZiX0B8phpY= +github.com/aws/aws-sdk-go v1.44.103/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= 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= @@ -171,6 +180,7 @@ github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -229,10 +239,14 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.18+incompatible h1:f/GQLsVpo10VvToRay2IraVA1wHz9KktZyjev3SIVDU= +github.com/docker/cli v20.10.18+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= +github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -339,6 +353,8 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -452,6 +468,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= github.com/google/go-containerregistry v0.11.0/go.mod h1:BBaYtsHPHA42uEgAvd/NejvAfPSlz281sJWqupjSxfk= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -541,6 +559,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.3.0 h1:G0ACM8Z2WilWgPv3Vdzwm3V0BQu/kSmrkVtpe1fy9do= github.com/hashicorp/go-hclog v1.3.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo= +github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -591,8 +611,12 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/vault/api v1.7.2 h1:kawHE7s/4xwrdKbkmwQi0wYaIeUhk5ueek7ljuezCVQ= github.com/hashicorp/vault/api v1.7.2/go.mod h1:xbfA+1AvxFseDzxxdWaL0uO99n1+tndus4GCrtouy0M= +github.com/hashicorp/vault/api v1.8.0 h1:7765sW1XBt+qf4XKIYE4ebY9qc/yi9V2/egzGSUNMZU= +github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= github.com/hashicorp/vault/sdk v0.5.3 h1:PWY8sq/9pRrK9vUIy75qCH2Jd8oeENAgkaa/qbhzFrs= github.com/hashicorp/vault/sdk v0.5.3/go.mod h1:DoGraE9kKGNcVgPmTuX357Fm6WAx1Okvde8Vp3dPDoU= +github.com/hashicorp/vault/sdk v0.6.0 h1:6Z+In5DXHiUfZvIZdMx7e2loL1PPyDjA4bVh9ZTIAhs= +github.com/hashicorp/vault/sdk v0.6.0/go.mod h1:+DRpzoXIdMvKc88R4qxr+edwy/RvH5QK8itmxLiDHLc= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -668,6 +692,8 @@ github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo= +github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= github.com/kluctl/go-jinja2 v0.0.0-20220922065727-cf67f5b7f26d h1:pfyMHiDIYskxhJFlh1u1Xi3qHKGzg8Vna5hsRm7Cz0I= @@ -708,6 +734,8 @@ github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= @@ -756,10 +784,13 @@ github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= @@ -775,6 +806,7 @@ github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WT github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= +github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -855,6 +887,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+czc/J8SlhPKLOtVLMQc+xDCFBT73ZStMsRhSsUhsSg= @@ -943,6 +976,8 @@ github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= +github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -954,6 +989,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36tZeQ= github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= +github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU= +github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= @@ -1023,6 +1060,8 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= +github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= +github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= 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= @@ -1165,6 +1204,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY= +golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -1259,6 +1300,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps= +golang.org/x/net v0.0.0-20220921203646-d300de134e69/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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= @@ -1278,6 +1321,8 @@ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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= @@ -1292,6 +1337,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A= +golang.org/x/sync v0.0.0-20220907140024-f12130a52804/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1386,11 +1433,15 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w= +golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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= @@ -1408,6 +1459,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45 h1:yuLAip3bfURHClMG9VBdzPrQvCWjWiWUTBGV+/fCbUs= +golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1616,6 +1669,8 @@ google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf h1:Q5xNKbTSFwkuaaGaR7CMcXEM5sy19KYdUU8iF8/iRC0= google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 h1:K1zaaMdYBXRyX+cwFnxj7M6zwDyumLQMZ5xqwGvjreQ= +google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737/go.mod h1:2r/26NEF3bFmT3eC3aZreahSal0C3Shl8Gi6vyDYqOQ= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1708,6 +1763,8 @@ gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= helm.sh/helm/v3 v3.9.4 h1:TCI1QhJUeLVOdccfdw+vnSEO3Td6gNqibptB04QtExY= helm.sh/helm/v3 v3.9.4/go.mod h1:3eaWAIqzvlRSD06gR9MMwmp2KBKwlu9av1/1BZpjeWY= +helm.sh/helm/v3 v3.10.0 h1:y/MYONZ/bsld9kHwqgBX2uPggnUr5hahpjwt9/jrHlI= +helm.sh/helm/v3 v3.10.0/go.mod h1:paPw0hO5KVfrCMbi1M8+P8xdfBri3IiJiVKATZsFR94= 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= @@ -1718,25 +1775,44 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= +k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= +k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= +k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= +k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8= k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= +k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= +k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= k8s.io/apiserver v0.25.0 h1:8kl2ifbNffD440MyvHtPaIz1mw4mGKVgWqM0nL+oyu4= k8s.io/apiserver v0.25.0/go.mod h1:BKwsE+PTC+aZK+6OJQDPr0v6uS91/HWxX7evElAH6xo= +k8s.io/apiserver v0.25.2 h1:YePimobk187IMIdnmsMxsfIbC5p4eX3WSOrS9x6FEYw= +k8s.io/apiserver v0.25.2/go.mod h1:30r7xyQTREWCkG2uSjgjhQcKVvAAlqoD+YyrqR6Cn+I= k8s.io/cli-runtime v0.25.0 h1:XBnTc2Fi+w818jcJGzhiJKQuXl8479sZ4FhtV5hVJ1Q= k8s.io/cli-runtime v0.25.0/go.mod h1:bHOI5ZZInRHhbq12OdUiYZQN8ml8aKZLwQgt9QlLINw= +k8s.io/cli-runtime v0.25.2 h1:XOx+SKRjBpYMLY/J292BHTkmyDffl/qOx3YSuFZkTuc= +k8s.io/cli-runtime v0.25.2/go.mod h1:OQx3+/0st6x5YpkkJQlEWLC73V0wHsOFMC1/roxV8Oc= k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= +k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= +k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= +k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY= +k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea h1:3QOH5+2fGsY8e1qf+GIFpg+zw/JGNrgyZRQR7/m6uWg= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/kubectl v0.25.0 h1:/Wn1cFqo8ik3iee1EvpxYre3bkWsGLXzLQI6uCCAkQc= k8s.io/kubectl v0.25.0/go.mod h1:n16ULWsOl2jmQpzt2o7Dud1t4o0+Y186ICb4O+GwKAU= +k8s.io/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA= +k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg= k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= @@ -1752,6 +1828,8 @@ sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kind v0.14.0 h1:cNmI3jGBvp7UegEGbC5we8plDtCUmaNRL+bod7JoSCE= sigs.k8s.io/kind v0.14.0/go.mod h1:UrFRPHG+2a5j0Q7qiR4gtJ4rEyn8TuMQwuOPf+m4oHg= +sigs.k8s.io/kind v0.15.0 h1:Fskj234L4hjQlsScCgeYvCBIRt06cjLzc7+kbr1u8Tg= +sigs.k8s.io/kind v0.15.0/go.mod h1:cKTqagdRyUQmihhBOd+7p43DpOPRn9rHsUC08K1Jbsk= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= From 3704872db5d2c0ebd33dd717b6039fc0ff9180ed Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 09:10:24 +0200 Subject: [PATCH 0356/2268] feat: Remove downscale command --- .github/ISSUE_TEMPLATE/FEATURE.yml | 1 - cmd/kluctl/commands/cmd_downscale.go | 60 ----------------- cmd/kluctl/commands/root.go | 1 - pkg/deployment/commands/downscale.go | 73 -------------------- pkg/deployment/utils/downscale_utils.go | 88 ------------------------- 5 files changed, 223 deletions(-) delete mode 100644 cmd/kluctl/commands/cmd_downscale.go delete mode 100644 pkg/deployment/commands/downscale.go delete mode 100644 pkg/deployment/utils/downscale_utils.go diff --git a/.github/ISSUE_TEMPLATE/FEATURE.yml b/.github/ISSUE_TEMPLATE/FEATURE.yml index 4bdf4b33c..44c5f6875 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE.yml @@ -21,7 +21,6 @@ body: - label: "delete" - label: "deploy" - label: "diff" - - label: "downscale" - label: "helm-pull" - label: "helm-update" - label: "list-images" diff --git a/cmd/kluctl/commands/cmd_downscale.go b/cmd/kluctl/commands/cmd_downscale.go deleted file mode 100644 index ac2d0a610..000000000 --- a/cmd/kluctl/commands/cmd_downscale.go +++ /dev/null @@ -1,60 +0,0 @@ -package commands - -import ( - "fmt" - "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/deployment/commands" - "github.com/kluctl/kluctl/v2/pkg/status" -) - -type downscaleCmd struct { - args.ProjectFlags - args.TargetFlags - args.ArgsFlags - args.ImageFlags - args.InclusionFlags - args.YesFlags - args.DryRunFlags - args.OutputFormatFlags - args.RenderOutputDirFlags -} - -func (cmd *downscaleCmd) Help() string { - return `This command will downscale all Deployments, StatefulSets and CronJobs. -It is also possible to influence the behaviour with the help of annotations, as described in -the documentation.` -} - -func (cmd *downscaleCmd) Run() error { - ptArgs := projectTargetCommandArgs{ - projectFlags: cmd.ProjectFlags, - targetFlags: cmd.TargetFlags, - argsFlags: cmd.ArgsFlags, - imageFlags: cmd.ImageFlags, - inclusionFlags: cmd.InclusionFlags, - dryRunArgs: &cmd.DryRunFlags, - renderOutputDirFlags: cmd.RenderOutputDirFlags, - } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - if !cmd.Yes && !cmd.DryRun { - if !status.AskForConfirmation(cliCtx, fmt.Sprintf("Do you really want to downscale on context/cluster %s?", ctx.targetCtx.ClusterContext)) { - return fmt.Errorf("aborted") - } - } - - cmd2 := commands.NewDownscaleCommand(ctx.targetCtx.DeploymentCollection) - - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) - if err != nil { - return err - } - err = outputCommandResult(cmd.OutputFormat, result) - if err != nil { - return err - } - if len(result.Errors) != 0 { - return fmt.Errorf("command failed") - } - return nil - }) -} diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index f7eea355e..8c0f31a30 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -50,7 +50,6 @@ type cli struct { Delete deleteCmd `cmd:"" help:"Delete a target (or parts of it) from the corresponding cluster"` Deploy deployCmd `cmd:"" help:"Deploys a target to the corresponding cluster"` Diff diffCmd `cmd:"" help:"Perform a diff between the locally rendered target and the already deployed target"` - Downscale downscaleCmd `cmd:"" help:"Downscale all deployments"` HelmPull helmPullCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and pulls the specified Helm charts"` HelmUpdate helmUpdateCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and checks for new available versions"` ListImages listImagesCmd `cmd:"" help:"Renders the target and outputs all images used via 'images.get_image(...)"` diff --git a/pkg/deployment/commands/downscale.go b/pkg/deployment/commands/downscale.go deleted file mode 100644 index 184f08029..000000000 --- a/pkg/deployment/commands/downscale.go +++ /dev/null @@ -1,73 +0,0 @@ -package commands - -import ( - "context" - "github.com/kluctl/kluctl/v2/pkg/deployment" - utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" - "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "sync" -) - -type DownscaleCommand struct { - c *deployment.DeploymentCollection -} - -func NewDownscaleCommand(c *deployment.DeploymentCollection) *DownscaleCommand { - return &DownscaleCommand{ - c: c, - } -} - -func (cmd *DownscaleCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.CommandResult, error) { - var wg sync.WaitGroup - - dew := utils2.NewDeploymentErrorsAndWarnings() - - ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs()) - if err != nil { - return nil, err - } - - ad := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, &utils2.ApplyUtilOptions{}) - - for _, d := range cmd.c.Deployments { - if !d.CheckInclusionForDeploy() { - continue - } - au := ad.NewApplyUtil(ctx, nil) - for _, o := range d.Objects { - o := o - ref := o.GetK8sRef() - wg.Add(1) - if utils2.IsDownscaleDelete(o) { - go func() { - defer wg.Done() - au.DeleteObject(ref, false) - }() - } else { - go func() { - defer wg.Done() - au.ReplaceObject(ref, ru.GetRemoteObject(ref), func(remote *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { - return utils2.DownscaleObject(remote, o) - }) - }() - } - } - } - wg.Wait() - - du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, ad.GetAppliedObjectsMap()) - du.Diff() - - return &types.CommandResult{ - NewObjects: du.NewObjects, - ChangedObjects: du.ChangedObjects, - DeletedObjects: ad.GetDeletedObjects(), - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), - }, nil -} diff --git a/pkg/deployment/utils/downscale_utils.go b/pkg/deployment/utils/downscale_utils.go deleted file mode 100644 index aed46dd92..000000000 --- a/pkg/deployment/utils/downscale_utils.go +++ /dev/null @@ -1,88 +0,0 @@ -package utils - -import ( - "fmt" - jsonpatch "github.com/evanphx/json-patch" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" - "k8s.io/apimachinery/pkg/runtime/schema" - "regexp" - "strconv" -) - -var ( - downscaleAnnotationPatchRegex = regexp.MustCompile(`^kluctl.io/downscale-patch(-\d*)?$`) - downscaleAnnotationDelete = "kluctl.io/downscale-delete" - downscaleAnnotationIgnore = "kluctl.io/downscale-ignore" -) - -func IsDownscaleDelete(o *uo.UnstructuredObject) bool { - a, _ := o.GetK8sAnnotations()[downscaleAnnotationDelete] - b, _ := strconv.ParseBool(a) - return b -} - -func isDownscaleIgnore(o *uo.UnstructuredObject) bool { - a, _ := o.GetK8sAnnotations()[downscaleAnnotationIgnore] - b, _ := strconv.ParseBool(a) - return b -} - -func DownscaleObject(remote *uo.UnstructuredObject, local *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { - ret := remote - if isDownscaleIgnore(local) { - return ret, nil - } - var patch jsonpatch.Patch - for _, v := range local.GetK8sAnnotationsWithRegex(downscaleAnnotationPatchRegex) { - j, err := yaml.ConvertYamlToJson([]byte(v)) - if err != nil { - return nil, fmt.Errorf("invalid jsonpatch json/yaml: %w", err) - } - p, err := jsonpatch.DecodePatch(j) - if err != nil { - return nil, fmt.Errorf("invalid jsonpatch: %w", err) - } - patch = append(patch, p...) - } - - if len(patch) != 0 { - j, err := yaml.WriteYamlBytes(ret.Object) - if err != nil { - return nil, err - } - j, err = yaml.ConvertYamlToJson(j) - if err != nil { - return nil, err - } - j, err = patch.Apply(j) - if err != nil { - return nil, err - } - ret = &uo.UnstructuredObject{} - err = yaml.ReadYamlBytes(j, &ret.Object) - if err != nil { - return nil, err - } - } - - ref := remote.GetK8sRef() - switch ref.GVK.GroupKind() { - case schema.GroupKind{Group: "apps", Kind: "Deployment"}: - fallthrough - case schema.GroupKind{Group: "apps", Kind: "StatefulSet"}: - ret = ret.Clone() - err := ret.SetNestedField(0, "spec", "replicas") - if err != nil { - return nil, err - } - case schema.GroupKind{Group: "batch", Kind: "CronJob"}: - ret = ret.Clone() - err := ret.SetNestedField(true, "spec", "suspend") - if err != nil { - return nil, err - } - } - - return ret, nil -} From 2ee45fb78ec1a5dca3fd7cd62e8ee79be7455642 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 10:35:36 +0200 Subject: [PATCH 0357/2268] feat: Add --offline-kubernetes flag to render command --- cmd/kluctl/commands/cmd_render.go | 2 ++ cmd/kluctl/commands/utils.go | 8 +++++--- pkg/kluctl_project/target_context.go | 27 ++++++++++++++++++++------- pkg/vars/vars_loader.go | 2 +- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 190760e8d..2b45c9f9b 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -13,6 +13,7 @@ type renderCmd struct { args.ArgsFlags args.ImageFlags args.RenderOutputDirFlags + OfflineKubernetes bool `group:"misc" help:"Run render in offline mode, meaning that it will not try to connect the target cluster"` } func (cmd *renderCmd) Help() string { @@ -35,6 +36,7 @@ func (cmd *renderCmd) Run() error { argsFlags: cmd.ArgsFlags, imageFlags: cmd.ImageFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, + offlineKubernetes: cmd.OfflineKubernetes, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { status.Info(ctx.ctx, "Rendered into %s", ctx.targetCtx.SharedContext.RenderDir) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 6a6efa30f..968077a02 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -104,8 +104,9 @@ type projectTargetCommandArgs struct { dryRunArgs *args.DryRunFlags renderOutputDirFlags args.RenderOutputDirFlags - forSeal bool - forCompletion bool + forSeal bool + forCompletion bool + offlineKubernetes bool } type commandCtx struct { @@ -165,7 +166,8 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm clusterName = &args.projectFlags.Cluster } - targetCtx, err := p.NewTargetContext(ctx, args.targetFlags.Target, clusterName, + targetCtx, err := p.NewTargetContext(ctx, + args.targetFlags.Target, clusterName, args.offlineKubernetes, args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, optionArgs2, args.forSeal, images, inclusion, renderOutputDir) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 37e87f56f..f93c9e8ed 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -12,6 +12,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/vars/aws" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd/api" "path/filepath" ) @@ -25,7 +26,7 @@ type TargetContext struct { DeploymentCollection *deployment.DeploymentCollection } -func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName string, clusterName *string, dryRun bool, externalArgs *uo.UnstructuredObject, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { +func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName string, clusterName *string, offlineK8s bool, dryRun bool, externalArgs *uo.UnstructuredObject, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { deploymentDir, err := filepath.Abs(p.DeploymentDir) if err != nil { return nil, err @@ -42,7 +43,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s images.PrependFixedImages(target.Images) } - varsCtx, clientConfig, clusterContext, err := p.buildVars(target, clusterName, externalArgs, forSeal) + varsCtx, clientConfig, clusterContext, err := p.buildVars(target, clusterName, offlineK8s, externalArgs, forSeal) if err != nil { return nil, err } @@ -103,7 +104,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s return targetCtx, nil } -func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *string, externalArgs *uo.UnstructuredObject, forSeal bool) (*vars.VarsCtx, *rest.Config, string, error) { +func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *string, offlineK8s bool, externalArgs *uo.UnstructuredObject, forSeal bool) (*vars.VarsCtx, *rest.Config, string, error) { doError := func(err error) (*vars.VarsCtx, *rest.Config, string, error) { return nil, nil, "", err } @@ -130,9 +131,17 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin } } - clientConfig, restConfig, err := p.loadArgs.ClientConfigGetter(contextName) - if err != nil { - return doError(err) + var err error + var clientConfig *rest.Config + if !offlineK8s { + var restConfig *api.Config + clientConfig, restConfig, err = p.loadArgs.ClientConfigGetter(contextName) + if err != nil { + return doError(err) + } + if contextName == nil { + contextName = &restConfig.CurrentContext + } } targetVars, err := uo.FromStruct(target) @@ -171,7 +180,11 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin varsCtx.UpdateChild("args", allArgs) - return varsCtx, clientConfig, restConfig.CurrentContext, nil + var contextName2 string + if contextName != nil { + contextName2 = *contextName + } + return varsCtx, clientConfig, contextName2, nil } func (p *LoadedKluctlProject) findSecretsEntry(name string) (*types.SecretSet, error) { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 338e1b446..a479ec791 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -195,7 +195,7 @@ func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, roo func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, key string, rootKey string, base64Decode bool) error { if v.k == nil { - return nil + return fmt.Errorf("loading vars from cluster is disabled") } var err error From ce61477328ea2e1ce0076a9675d7dbfa00877451 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 10:35:49 +0200 Subject: [PATCH 0358/2268] feat: Add --print-all flag to render command --- cmd/kluctl/commands/cmd_render.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 2b45c9f9b..52327efdd 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -4,7 +4,9 @@ import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" + "os" ) type renderCmd struct { @@ -13,7 +15,9 @@ type renderCmd struct { args.ArgsFlags args.ImageFlags args.RenderOutputDirFlags + OfflineKubernetes bool `group:"misc" help:"Run render in offline mode, meaning that it will not try to connect the target cluster"` + PrintAll bool `group:"misc" help:"Write all rendered manifests to stdout"` } func (cmd *renderCmd) Help() string { @@ -22,12 +26,14 @@ a temporary directory or a specified directory.` } func (cmd *renderCmd) Run() error { + isTmp := false if cmd.RenderOutputDir == "" { p, err := ioutil.TempDir(utils.GetTmpBaseDir(), "rendered-") if err != nil { return err } cmd.RenderOutputDir = p + isTmp = true } ptArgs := projectTargetCommandArgs{ @@ -39,7 +45,21 @@ func (cmd *renderCmd) Run() error { offlineKubernetes: cmd.OfflineKubernetes, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - status.Info(ctx.ctx, "Rendered into %s", ctx.targetCtx.SharedContext.RenderDir) + if cmd.PrintAll { + var all []any + for _, d := range ctx.targetCtx.DeploymentCollection.Deployments { + for _, o := range d.Objects { + all = append(all, o) + } + } + if isTmp { + defer os.RemoveAll(cmd.RenderOutputDir) + } + status.Flush(ctx.ctx) + return yaml.WriteYamlAllStream(os.Stdout, all) + } else { + status.Info(ctx.ctx, "Rendered into %s", ctx.targetCtx.SharedContext.RenderDir) + } return nil }) } From ac91c0a5cdb90c40d0b674cea21e96b342e1e4d1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 10:47:30 +0200 Subject: [PATCH 0359/2268] feat: Remove cluster-id from sealed secrets This is required to make offline sealing possible. --- pkg/seal/sealer.go | 53 +--------------------------------------------- 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/pkg/seal/sealer.go b/pkg/seal/sealer.go index 80309e6a7..57e02491b 100644 --- a/pkg/seal/sealer.go +++ b/pkg/seal/sealer.go @@ -10,12 +10,10 @@ import ( "github.com/bitnami-labs/sealed-secrets/pkg/crypto" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" - k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "golang.org/x/crypto/scrypt" - "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" "os" "path/filepath" @@ -24,13 +22,11 @@ import ( ) const hashAnnotation = "kluctl.io/sealedsecret-hashes" -const clusterIdAnnotation = "kluctl.io/sealedsecret-cluster-id" type Sealer struct { ctx context.Context forceReseal bool cert *rsa.PublicKey - clusterId string } func NewSealer(ctx context.Context, k *k8s.K8sCluster, sealedSecretsNamespace string, sealedSecretsControllerName string, forceReseal bool) (*Sealer, error) { @@ -38,54 +34,16 @@ func NewSealer(ctx context.Context, k *k8s.K8sCluster, sealedSecretsNamespace st ctx: ctx, forceReseal: forceReseal, } + cert, err := fetchCert(ctx, k, sealedSecretsNamespace, sealedSecretsControllerName) if err != nil { return nil, err } s.cert = cert - clusterId, err := getClusterId(k) - if err != nil { - return nil, err - } - - s.clusterId = clusterId - return s, nil } -// We treat the hashed kube-root-ca.crt as cluster id for now. We also accept that it might change when keys -// get rotated. -func getClusterId(k *k8s.K8sCluster) (string, error) { - o, _, err := k.GetSingleObject(k8s2.ObjectRef{ - GVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}, - Name: "kube-root-ca.crt", - Namespace: "kube-system", - }) - if err != nil && !errors.IsNotFound(err) { - return "", fmt.Errorf("failed to retrieve kube-root-ca.crt: %w", err) - } - - var kubeRootCA string - if o != nil { - x, ok, err := o.GetNestedString("data", "ca.crt") - if err != nil { - return "", fmt.Errorf("failed to retrieve kube-root-ca.crt: %w", err) - } - if !ok { - return "", fmt.Errorf("failed to retrieve kube-root-ca.crt: ca.crt key is missing") - } - kubeRootCA = x - } else { - // fall-back to CA from kubeconfig - ca := k.GetCA() - if ca != nil { - kubeRootCA = string(ca) - } - } - return utils.Sha256String(kubeRootCA), nil -} - func (s *Sealer) doHash(key string, secret []byte, secretName string, secretNamespace string, scope string) string { if secretNamespace == "" { secretNamespace = "*" @@ -170,7 +128,6 @@ func (s *Sealer) SealFile(p string, targetFile string) error { var existingContent *uo.UnstructuredObject var existingHashes *uo.UnstructuredObject - var existingClusterId string if utils.Exists(targetFile) { existingContent, err = uo.FromFile(targetFile) @@ -178,10 +135,6 @@ func (s *Sealer) SealFile(p string, targetFile string) error { if a != nil { existingHashes, _ = uo.FromString(*a) } - a = existingContent.GetK8sAnnotation(clusterIdAnnotation) - if a != nil { - existingClusterId = *a - } } if existingHashes == nil { existingHashes = uo.New() @@ -243,9 +196,6 @@ func (s *Sealer) SealFile(p string, targetFile string) error { if s.forceReseal { resealAll = true status.Info(s.ctx, "Forcing reseal of secrets in %s", secretName) - } else if existingClusterId != s.clusterId { - resealAll = true - status.Info(s.ctx, "Target cluster for secret %s has changed, forcing reseal", secretName) } for k, v := range secrets { @@ -284,7 +234,6 @@ func (s *Sealer) SealFile(p string, targetFile string) error { return err } result.SetK8sAnnotation(hashAnnotation, resultSecretHashesStr) - result.SetK8sAnnotation(clusterIdAnnotation, s.clusterId) if reflect.DeepEqual(existingContent, result) { status.Info(s.ctx, "Skipped %s as it did not change", baseName) From 3f97ff132d744c11820b9ebb272fa3b491641c82 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 10:54:04 +0200 Subject: [PATCH 0360/2268] refactor: Move fetching of certs outside of NewSealer --- cmd/kluctl/commands/cmd_seal.go | 7 ++++++- pkg/seal/fetch_cert.go | 2 +- pkg/seal/sealer.go | 10 ++-------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 65fe92997..1cf922d40 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -66,7 +66,12 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L } } - sealer, err := seal.NewSealer(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace, sealedSecretsControllerName, cmd.ForceReseal) + cert, err := seal.FetchCert(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace, sealedSecretsControllerName) + if err != nil { + return doFail(err) + } + + sealer, err := seal.NewSealer(ctx.ctx, cert, cmd.ForceReseal) if err != nil { return doFail(err) } diff --git a/pkg/seal/fetch_cert.go b/pkg/seal/fetch_cert.go index 95b1c154a..5679079a0 100644 --- a/pkg/seal/fetch_cert.go +++ b/pkg/seal/fetch_cert.go @@ -13,7 +13,7 @@ import ( "k8s.io/client-go/util/cert" ) -func fetchCert(ctx context.Context, k *k8s.K8sCluster, namespace string, controllerName string) (*rsa.PublicKey, error) { +func FetchCert(ctx context.Context, k *k8s.K8sCluster, namespace string, controllerName string) (*rsa.PublicKey, error) { certData, err := openCertFromController(k, namespace, controllerName) if err != nil { if controllerName == "sealed-secrets-controller" { diff --git a/pkg/seal/sealer.go b/pkg/seal/sealer.go index 57e02491b..d6c31391e 100644 --- a/pkg/seal/sealer.go +++ b/pkg/seal/sealer.go @@ -8,7 +8,6 @@ import ( "encoding/hex" "fmt" "github.com/bitnami-labs/sealed-secrets/pkg/crypto" - "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -29,18 +28,13 @@ type Sealer struct { cert *rsa.PublicKey } -func NewSealer(ctx context.Context, k *k8s.K8sCluster, sealedSecretsNamespace string, sealedSecretsControllerName string, forceReseal bool) (*Sealer, error) { +func NewSealer(ctx context.Context, cert *rsa.PublicKey, forceReseal bool) (*Sealer, error) { s := &Sealer{ ctx: ctx, forceReseal: forceReseal, + cert: cert, } - cert, err := fetchCert(ctx, k, sealedSecretsNamespace, sealedSecretsControllerName) - if err != nil { - return nil, err - } - s.cert = cert - return s, nil } From 667ecaeee97d2097b1fa1700a6253eba0cf7c064 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 11:03:09 +0200 Subject: [PATCH 0361/2268] refactor: Simplify runCmdSealForTarget --- cmd/kluctl/commands/cmd_seal.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 1cf922d40..7aaaa489c 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -8,6 +8,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/seal" "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/types" ) type sealCmd struct { @@ -49,17 +50,24 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L return doFail(err) } + secretsConfig := p.Config.SecretsConfig + var sealedSecretsConfig *types.GlobalSealedSecretsConfig + if secretsConfig != nil { + sealedSecretsConfig = secretsConfig.SealedSecrets + } + sealedSecretsNamespace := "kube-system" sealedSecretsControllerName := "sealed-secrets-controller" - if p.Config.SecretsConfig != nil && p.Config.SecretsConfig.SealedSecrets != nil { - if p.Config.SecretsConfig.SealedSecrets.Namespace != nil { - sealedSecretsNamespace = *p.Config.SecretsConfig.SealedSecrets.Namespace + if sealedSecretsConfig != nil { + if sealedSecretsConfig.Namespace != nil { + sealedSecretsNamespace = *sealedSecretsConfig.Namespace } - if p.Config.SecretsConfig.SealedSecrets.ControllerName != nil { - sealedSecretsControllerName = *p.Config.SecretsConfig.SealedSecrets.ControllerName + if sealedSecretsConfig.ControllerName != nil { + sealedSecretsControllerName = *sealedSecretsConfig.ControllerName } } - if p.Config.SecretsConfig == nil || p.Config.SecretsConfig.SealedSecrets == nil || p.Config.SecretsConfig.SealedSecrets.Bootstrap == nil || *p.Config.SecretsConfig.SealedSecrets.Bootstrap { + + if sealedSecretsConfig == nil || sealedSecretsConfig.Bootstrap == nil || *sealedSecretsConfig.Bootstrap { err = seal.BootstrapSealedSecrets(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace) if err != nil { return doFail(err) From 1bf1c1ab31aed5f28af85c3982e7c4fa05c2a600 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 11:25:40 +0200 Subject: [PATCH 0362/2268] feat: Allow to specify certFile via target SealingConfig --- cmd/kluctl/commands/cmd_seal.go | 85 +++++++++++++++++++++++---------- pkg/seal/fetch_cert.go | 4 +- pkg/types/kluctl_project.go | 1 + 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 7aaaa489c..61ab95183 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -2,13 +2,16 @@ package commands import ( "context" + "crypto/rsa" "fmt" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/seal" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" + "os" ) type sealCmd struct { @@ -50,31 +53,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L return doFail(err) } - secretsConfig := p.Config.SecretsConfig - var sealedSecretsConfig *types.GlobalSealedSecretsConfig - if secretsConfig != nil { - sealedSecretsConfig = secretsConfig.SealedSecrets - } - - sealedSecretsNamespace := "kube-system" - sealedSecretsControllerName := "sealed-secrets-controller" - if sealedSecretsConfig != nil { - if sealedSecretsConfig.Namespace != nil { - sealedSecretsNamespace = *sealedSecretsConfig.Namespace - } - if sealedSecretsConfig.ControllerName != nil { - sealedSecretsControllerName = *sealedSecretsConfig.ControllerName - } - } - - if sealedSecretsConfig == nil || sealedSecretsConfig.Bootstrap == nil || *sealedSecretsConfig.Bootstrap { - err = seal.BootstrapSealedSecrets(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace) - if err != nil { - return doFail(err) - } - } - - cert, err := seal.FetchCert(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace, sealedSecretsControllerName) + cert, err := cmd.loadCert(ctx) if err != nil { return doFail(err) } @@ -102,6 +81,62 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L }) } +func (cmd *sealCmd) loadCert(ctx *commandCtx) (*rsa.PublicKey, error) { + sealingConfig := ctx.targetCtx.Target.SealingConfig + + var certFile string + + if sealingConfig != nil && sealingConfig.CertFile != nil { + path, err := securejoin.SecureJoin(ctx.targetCtx.KluctlProject.ProjectDir, *sealingConfig.CertFile) + if err != nil { + return nil, err + } + certFile = path + } + + if certFile != "" { + d, err := os.ReadFile(certFile) + if err != nil { + return nil, err + } + cert, err := seal.ParseCert(d) + if err != nil { + return nil, err + } + return cert, nil + } else { + secretsConfig := ctx.targetCtx.KluctlProject.Config.SecretsConfig + var sealedSecretsConfig *types.GlobalSealedSecretsConfig + if secretsConfig != nil { + sealedSecretsConfig = secretsConfig.SealedSecrets + } + + sealedSecretsNamespace := "kube-system" + sealedSecretsControllerName := "sealed-secrets-controller" + if sealedSecretsConfig != nil { + if sealedSecretsConfig.Namespace != nil { + sealedSecretsNamespace = *sealedSecretsConfig.Namespace + } + if sealedSecretsConfig.ControllerName != nil { + sealedSecretsControllerName = *sealedSecretsConfig.ControllerName + } + } + + if sealedSecretsConfig == nil || sealedSecretsConfig.Bootstrap == nil || *sealedSecretsConfig.Bootstrap { + err := seal.BootstrapSealedSecrets(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace) + if err != nil { + return nil, err + } + } + + cert, err := seal.FetchCert(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace, sealedSecretsControllerName) + if err != nil { + return nil, err + } + return cert, nil + } +} + func (cmd *sealCmd) Run() error { return withKluctlProjectFromArgs(cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { hadError := false diff --git a/pkg/seal/fetch_cert.go b/pkg/seal/fetch_cert.go index 5679079a0..900dc0369 100644 --- a/pkg/seal/fetch_cert.go +++ b/pkg/seal/fetch_cert.go @@ -34,7 +34,7 @@ func FetchCert(ctx context.Context, k *k8s.K8sCluster, namespace string, control } } - return parseKey(certData) + return ParseCert(certData) } func openCertFromBootstrap(k *k8s.K8sCluster, namespace string) ([]byte, error) { @@ -92,7 +92,7 @@ func getServicePortName(k *k8s.K8sCluster, namespace, serviceName string) (strin return n, nil } -func parseKey(data []byte) (*rsa.PublicKey, error) { +func ParseCert(data []byte) (*rsa.PublicKey, error) { certs, err := cert.ParseCertsPEM(data) if err != nil { return nil, err diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index f5fb852ad..3f7efdf59 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -25,6 +25,7 @@ type SealingConfig struct { DynamicSealing *bool `yaml:"dynamicSealing,omitempty"` Args *uo.UnstructuredObject `yaml:"args,omitempty"` SecretSets []string `yaml:"secretSets,omitempty"` + CertFile *string `yaml:"certFile,omitempty"` } type Target struct { From 864a0d694b84ce73af47801e82b01ad1e727501e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 11:26:07 +0200 Subject: [PATCH 0363/2268] feat: Allow to override certFile via command line --- cmd/kluctl/commands/cmd_seal.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 61ab95183..83ebbe68a 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -18,7 +18,8 @@ type sealCmd struct { args.ProjectFlags args.TargetFlags - ForceReseal bool `group:"misc" help:"Lets kluctl ignore secret hashes found in already sealed secrets and thus forces resealing of those."` + ForceReseal bool `group:"misc" help:"Lets kluctl ignore secret hashes found in already sealed secrets and thus forces resealing of those."` + CertFile string `group:"misc" help:"Use the given certificate for sealing instead of requesting it from the sealed-secrets controller"` } func (cmd *sealCmd) Help() string { @@ -94,6 +95,13 @@ func (cmd *sealCmd) loadCert(ctx *commandCtx) (*rsa.PublicKey, error) { certFile = path } + if cmd.CertFile != "" { + if certFile != "" { + status.Info(ctx.ctx, "Overriding certFile from target with certFile argument") + } + certFile = cmd.CertFile + } + if certFile != "" { d, err := os.ReadFile(certFile) if err != nil { From b25579cc82baf29c015bc63ef6e975298b3fec3e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 11:26:29 +0200 Subject: [PATCH 0364/2268] feat: Add --offline-kubernetes flag to seal command --- cmd/kluctl/commands/cmd_seal.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 83ebbe68a..6b3d9e9ff 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -18,8 +18,9 @@ type sealCmd struct { args.ProjectFlags args.TargetFlags - ForceReseal bool `group:"misc" help:"Lets kluctl ignore secret hashes found in already sealed secrets and thus forces resealing of those."` - CertFile string `group:"misc" help:"Use the given certificate for sealing instead of requesting it from the sealed-secrets controller"` + ForceReseal bool `group:"misc" help:"Lets kluctl ignore secret hashes found in already sealed secrets and thus forces resealing of those."` + CertFile string `group:"misc" help:"Use the given certificate for sealing instead of requesting it from the sealed-secrets controller"` + OfflineKubernetes bool `group:"misc" help:"Run seal in offline mode, meaning that it will not try to connect the target cluster"` } func (cmd *sealCmd) Help() string { @@ -41,9 +42,10 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L } ptArgs := projectTargetCommandArgs{ - projectFlags: cmd.ProjectFlags, - targetFlags: cmd.TargetFlags, - forSeal: true, + projectFlags: cmd.ProjectFlags, + targetFlags: cmd.TargetFlags, + forSeal: true, + offlineKubernetes: cmd.OfflineKubernetes, } ptArgs.targetFlags.Target = targetName @@ -113,6 +115,10 @@ func (cmd *sealCmd) loadCert(ctx *commandCtx) (*rsa.PublicKey, error) { } return cert, nil } else { + if ctx.targetCtx.SharedContext.K == nil { + return nil, fmt.Errorf("must specify certFile when sealing in offline mode") + } + secretsConfig := ctx.targetCtx.KluctlProject.Config.SecretsConfig var sealedSecretsConfig *types.GlobalSealedSecretsConfig if secretsConfig != nil { From 74ae3f7bb04ab18a9deb185911a79357e7f32750 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 11:43:27 +0200 Subject: [PATCH 0365/2268] feat: Write certificate hash to sealed secret and re-seal when it changes --- cmd/kluctl/commands/cmd_seal.go | 4 ++-- pkg/seal/fetch_cert.go | 13 ++++--------- pkg/seal/sealer.go | 34 ++++++++++++++++++++++++++++++--- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 6b3d9e9ff..636444179 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -2,7 +2,7 @@ package commands import ( "context" - "crypto/rsa" + "crypto/x509" "fmt" securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" @@ -84,7 +84,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L }) } -func (cmd *sealCmd) loadCert(ctx *commandCtx) (*rsa.PublicKey, error) { +func (cmd *sealCmd) loadCert(ctx *commandCtx) (*x509.Certificate, error) { sealingConfig := ctx.targetCtx.Target.SealingConfig var certFile string diff --git a/pkg/seal/fetch_cert.go b/pkg/seal/fetch_cert.go index 900dc0369..07fd5e7c0 100644 --- a/pkg/seal/fetch_cert.go +++ b/pkg/seal/fetch_cert.go @@ -2,7 +2,7 @@ package seal import ( "context" - "crypto/rsa" + "crypto/x509" "errors" "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -13,7 +13,7 @@ import ( "k8s.io/client-go/util/cert" ) -func FetchCert(ctx context.Context, k *k8s.K8sCluster, namespace string, controllerName string) (*rsa.PublicKey, error) { +func FetchCert(ctx context.Context, k *k8s.K8sCluster, namespace string, controllerName string) (*x509.Certificate, error) { certData, err := openCertFromController(k, namespace, controllerName) if err != nil { if controllerName == "sealed-secrets-controller" { @@ -92,7 +92,7 @@ func getServicePortName(k *k8s.K8sCluster, namespace, serviceName string) (strin return n, nil } -func ParseCert(data []byte) (*rsa.PublicKey, error) { +func ParseCert(data []byte) (*x509.Certificate, error) { certs, err := cert.ParseCertsPEM(data) if err != nil { return nil, err @@ -103,10 +103,5 @@ func ParseCert(data []byte) (*rsa.PublicKey, error) { return nil, errors.New("failed to read any certificates") } - cert, ok := certs[0].PublicKey.(*rsa.PublicKey) - if !ok { - return nil, fmt.Errorf("fxpected RSA public key but found %v", certs[0].PublicKey) - } - - return cert, nil + return certs[0], nil } diff --git a/pkg/seal/sealer.go b/pkg/seal/sealer.go index d6c31391e..268009cc6 100644 --- a/pkg/seal/sealer.go +++ b/pkg/seal/sealer.go @@ -4,6 +4,8 @@ import ( "context" "crypto/rand" "crypto/rsa" + "crypto/sha256" + "crypto/x509" "encoding/base64" "encoding/hex" "fmt" @@ -21,20 +23,36 @@ import ( ) const hashAnnotation = "kluctl.io/sealedsecret-hashes" +const certHashAnnotation = "kluctl.io/sealedsecret-cert-hash" type Sealer struct { ctx context.Context forceReseal bool - cert *rsa.PublicKey + cert *x509.Certificate + pubKey *rsa.PublicKey + certHash string } -func NewSealer(ctx context.Context, cert *rsa.PublicKey, forceReseal bool) (*Sealer, error) { +func NewSealer(ctx context.Context, cert *x509.Certificate, forceReseal bool) (*Sealer, error) { s := &Sealer{ ctx: ctx, forceReseal: forceReseal, cert: cert, } + pk, ok := cert.PublicKey.(*rsa.PublicKey) + if !ok { + return nil, fmt.Errorf("expected RSA public key but found %v", cert.PublicKey) + } + s.pubKey = pk + + pkBytes, err := x509.MarshalPKIXPublicKey(cert.PublicKey) + if err != nil { + return nil, err + } + h := sha256.Sum256(pkBytes) + s.certHash = hex.EncodeToString(h[:]) + return s, nil } @@ -69,7 +87,8 @@ func encryptionLabel(namespace string, name string, scope string) []byte { } func (s *Sealer) encryptSecret(secret []byte, secretName string, secretNamespace string, scope string) (string, error) { - b, err := crypto.HybridEncrypt(rand.Reader, s.cert, secret, encryptionLabel(secretNamespace, secretName, scope)) + // todo + b, err := crypto.HybridEncrypt(rand.Reader, s.pubKey, secret, encryptionLabel(secretNamespace, secretName, scope)) if err != nil { return "", err } @@ -122,6 +141,7 @@ func (s *Sealer) SealFile(p string, targetFile string) error { var existingContent *uo.UnstructuredObject var existingHashes *uo.UnstructuredObject + var existingCertHash string if utils.Exists(targetFile) { existingContent, err = uo.FromFile(targetFile) @@ -129,6 +149,10 @@ func (s *Sealer) SealFile(p string, targetFile string) error { if a != nil { existingHashes, _ = uo.FromString(*a) } + a = existingContent.GetK8sAnnotation(certHashAnnotation) + if a != nil { + existingCertHash = *a + } } if existingHashes == nil { existingHashes = uo.New() @@ -190,6 +214,9 @@ func (s *Sealer) SealFile(p string, targetFile string) error { if s.forceReseal { resealAll = true status.Info(s.ctx, "Forcing reseal of secrets in %s", secretName) + } else if existingCertHash != s.certHash { + resealAll = true + status.Info(s.ctx, "Cert for secret %s has changed, forcing reseal", secretName) } for k, v := range secrets { @@ -228,6 +255,7 @@ func (s *Sealer) SealFile(p string, targetFile string) error { return err } result.SetK8sAnnotation(hashAnnotation, resultSecretHashesStr) + result.SetK8sAnnotation(certHashAnnotation, s.certHash) if reflect.DeepEqual(existingContent, result) { status.Info(s.ctx, "Skipped %s as it did not change", baseName) From 738b2c834d3f768fd45203383369d6f2b3001791 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 11:49:20 +0200 Subject: [PATCH 0366/2268] chore: Run go mod tidy --- go.mod | 2 +- go.sum | 80 ++-------------------------------------------------------- 2 files changed, 3 insertions(+), 79 deletions(-) diff --git a/go.mod b/go.mod index b36010d39..2832f1384 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/bitnami-labs/sealed-secrets v0.18.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible - github.com/evanphx/json-patch v5.6.0+incompatible + github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fluxcd/pkg/kustomize v0.7.0 github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index 5851700e6..b2e3c6035 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,6 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf 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.9.0 h1:ED/FP4xv8GJw63v556/ASNc1CeeLUO2Bs8nzaHchkHg= -cloud.google.com/go/compute v1.9.0/go.mod h1:lWv1h/zUWTm/LozzfTJhBSkd6ShQq8la8VeeuOEGxfY= cloud.google.com/go/compute v1.10.0 h1:aoLIYaA1fX3ywihqpBk2APQKOo20nXsp1GEZQbx5Jk4= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -93,12 +91,10 @@ github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy86 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= @@ -137,8 +133,6 @@ github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= -github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -153,8 +147,6 @@ github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9D github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.89 h1:Xf5Pp9GsNSMRinAuWNiQd0vusXXb3IgYbNlxldhWS2Q= -github.com/aws/aws-sdk-go v1.44.89/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.103 h1:tbhBHKgiZSIUkG8FcHy3wYKpPVvp65Wn7ZiX0B8phpY= github.com/aws/aws-sdk-go v1.44.103/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -180,7 +172,6 @@ github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -237,14 +228,10 @@ github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCF github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= -github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= -github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.18+incompatible h1:f/GQLsVpo10VvToRay2IraVA1wHz9KktZyjev3SIVDU= github.com/docker/cli v20.10.18+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= @@ -351,8 +338,6 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= -github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= @@ -466,8 +451,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= @@ -557,8 +540,6 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.3.0 h1:G0ACM8Z2WilWgPv3Vdzwm3V0BQu/kSmrkVtpe1fy9do= -github.com/hashicorp/go-hclog v1.3.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo= github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -609,12 +590,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/vault/api v1.7.2 h1:kawHE7s/4xwrdKbkmwQi0wYaIeUhk5ueek7ljuezCVQ= -github.com/hashicorp/vault/api v1.7.2/go.mod h1:xbfA+1AvxFseDzxxdWaL0uO99n1+tndus4GCrtouy0M= github.com/hashicorp/vault/api v1.8.0 h1:7765sW1XBt+qf4XKIYE4ebY9qc/yi9V2/egzGSUNMZU= github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= -github.com/hashicorp/vault/sdk v0.5.3 h1:PWY8sq/9pRrK9vUIy75qCH2Jd8oeENAgkaa/qbhzFrs= -github.com/hashicorp/vault/sdk v0.5.3/go.mod h1:DoGraE9kKGNcVgPmTuX357Fm6WAx1Okvde8Vp3dPDoU= github.com/hashicorp/vault/sdk v0.6.0 h1:6Z+In5DXHiUfZvIZdMx7e2loL1PPyDjA4bVh9ZTIAhs= github.com/hashicorp/vault/sdk v0.6.0/go.mod h1:+DRpzoXIdMvKc88R4qxr+edwy/RvH5QK8itmxLiDHLc= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= @@ -690,8 +667,6 @@ github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo= github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= @@ -732,8 +707,6 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= -github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= @@ -782,14 +755,12 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -805,7 +776,6 @@ github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WT github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -882,11 +852,10 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -974,8 +943,6 @@ github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:r github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= -github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -987,8 +954,6 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36tZeQ= -github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU= github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -1058,8 +1023,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= @@ -1202,8 +1165,6 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY= golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1298,8 +1259,6 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps= golang.org/x/net v0.0.0-20220921203646-d300de134e69/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1319,8 +1278,6 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1335,8 +1292,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A= golang.org/x/sync v0.0.0-20220907140024-f12130a52804/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1431,15 +1386,11 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w= golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1457,8 +1408,6 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb 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-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45 h1:yuLAip3bfURHClMG9VBdzPrQvCWjWiWUTBGV+/fCbUs= golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1667,8 +1616,6 @@ google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwy google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf h1:Q5xNKbTSFwkuaaGaR7CMcXEM5sy19KYdUU8iF8/iRC0= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 h1:K1zaaMdYBXRyX+cwFnxj7M6zwDyumLQMZ5xqwGvjreQ= google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737/go.mod h1:2r/26NEF3bFmT3eC3aZreahSal0C3Shl8Gi6vyDYqOQ= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1761,8 +1708,6 @@ 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.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -helm.sh/helm/v3 v3.9.4 h1:TCI1QhJUeLVOdccfdw+vnSEO3Td6gNqibptB04QtExY= -helm.sh/helm/v3 v3.9.4/go.mod h1:3eaWAIqzvlRSD06gR9MMwmp2KBKwlu9av1/1BZpjeWY= helm.sh/helm/v3 v3.10.0 h1:y/MYONZ/bsld9kHwqgBX2uPggnUr5hahpjwt9/jrHlI= helm.sh/helm/v3 v3.10.0/go.mod h1:paPw0hO5KVfrCMbi1M8+P8xdfBri3IiJiVKATZsFR94= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1773,44 +1718,25 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt 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= honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= -k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= -k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8= -k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= -k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= -k8s.io/apiserver v0.25.0 h1:8kl2ifbNffD440MyvHtPaIz1mw4mGKVgWqM0nL+oyu4= -k8s.io/apiserver v0.25.0/go.mod h1:BKwsE+PTC+aZK+6OJQDPr0v6uS91/HWxX7evElAH6xo= k8s.io/apiserver v0.25.2 h1:YePimobk187IMIdnmsMxsfIbC5p4eX3WSOrS9x6FEYw= k8s.io/apiserver v0.25.2/go.mod h1:30r7xyQTREWCkG2uSjgjhQcKVvAAlqoD+YyrqR6Cn+I= -k8s.io/cli-runtime v0.25.0 h1:XBnTc2Fi+w818jcJGzhiJKQuXl8479sZ4FhtV5hVJ1Q= -k8s.io/cli-runtime v0.25.0/go.mod h1:bHOI5ZZInRHhbq12OdUiYZQN8ml8aKZLwQgt9QlLINw= k8s.io/cli-runtime v0.25.2 h1:XOx+SKRjBpYMLY/J292BHTkmyDffl/qOx3YSuFZkTuc= k8s.io/cli-runtime v0.25.2/go.mod h1:OQx3+/0st6x5YpkkJQlEWLC73V0wHsOFMC1/roxV8Oc= -k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= -k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= -k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= -k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY= k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea h1:3QOH5+2fGsY8e1qf+GIFpg+zw/JGNrgyZRQR7/m6uWg= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/kubectl v0.25.0 h1:/Wn1cFqo8ik3iee1EvpxYre3bkWsGLXzLQI6uCCAkQc= -k8s.io/kubectl v0.25.0/go.mod h1:n16ULWsOl2jmQpzt2o7Dud1t4o0+Y186ICb4O+GwKAU= k8s.io/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA= k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg= k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= @@ -1826,8 +1752,6 @@ 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/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kind v0.14.0 h1:cNmI3jGBvp7UegEGbC5we8plDtCUmaNRL+bod7JoSCE= -sigs.k8s.io/kind v0.14.0/go.mod h1:UrFRPHG+2a5j0Q7qiR4gtJ4rEyn8TuMQwuOPf+m4oHg= sigs.k8s.io/kind v0.15.0 h1:Fskj234L4hjQlsScCgeYvCBIRt06cjLzc7+kbr1u8Tg= sigs.k8s.io/kind v0.15.0/go.mod h1:cKTqagdRyUQmihhBOd+7p43DpOPRn9rHsUC08K1Jbsk= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= From 26ebb324ca8f9bfaf1fa1b9f78066d9d2ab82aa8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 12:25:17 +0200 Subject: [PATCH 0367/2268] fix: Upgrade go-jinja2 to fix relative includes of ../ files --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2832f1384..9aa4aa26d 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 - github.com/kluctl/go-jinja2 v0.0.0-20220922065727-cf67f5b7f26d + github.com/kluctl/go-jinja2 v0.0.0-20220922102415-424914b4142b github.com/mattn/go-isatty v0.0.16 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 diff --git a/go.sum b/go.sum index b2e3c6035..40c565407 100644 --- a/go.sum +++ b/go.sum @@ -671,8 +671,8 @@ github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkEN github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= -github.com/kluctl/go-jinja2 v0.0.0-20220922065727-cf67f5b7f26d h1:pfyMHiDIYskxhJFlh1u1Xi3qHKGzg8Vna5hsRm7Cz0I= -github.com/kluctl/go-jinja2 v0.0.0-20220922065727-cf67f5b7f26d/go.mod h1:8deI9sssmQf1CiCnyRJoBgfIw4Hh3+WsHmmO9+xIIO0= +github.com/kluctl/go-jinja2 v0.0.0-20220922102415-424914b4142b h1:4fBKK0PZ8e/DC4GqM6RrUSSE2J6SlVTKNPb8awYxb5g= +github.com/kluctl/go-jinja2 v0.0.0-20220922102415-424914b4142b/go.mod h1:8deI9sssmQf1CiCnyRJoBgfIw4Hh3+WsHmmO9+xIIO0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= From 52ca9acce842256386fccce6817fa1e3186723ed Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 13:43:21 +0200 Subject: [PATCH 0368/2268] chore: Remove archive command from issue template --- .github/ISSUE_TEMPLATE/FEATURE.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/FEATURE.yml b/.github/ISSUE_TEMPLATE/FEATURE.yml index 44c5f6875..79ca1bca3 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE.yml @@ -16,7 +16,6 @@ body: label: Command description: Which command will be affected by the feature request? (If you want to propose a new command, just leave all unchecked) options: - - label: "archive" - label: "check-image-updates" - label: "delete" - label: "deploy" From 8606733482c694fd5f3d706130d877ed9e93cc30 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 14:03:50 +0200 Subject: [PATCH 0369/2268] fix: Enable colored output in windows --- cmd/kluctl/commands/root.go | 12 ++++++--- pkg/status/status_handler.go | 49 ++++++++++++++++++++++++------------ pkg/status/utils.go | 13 ---------- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 8c0f31a30..3fabd1561 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -24,6 +24,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/version" "github.com/kluctl/kluctl/v2/pkg/yaml" + "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -43,6 +44,7 @@ const latestReleaseUrl = "https://api.github.com/repos/kluctl/kluctl/releases/la type cli struct { Debug bool `group:"global" help:"Enable debug logging"` NoUpdateCheck bool `group:"global" help:"Disable update check on startup"` + NoColor bool `group:"global" help:"Disable colored output"` CpuProfile string `group:"global" help:"Enable CPU profiling and write the result to the given path"` @@ -74,7 +76,7 @@ var flagGroups = []groupInfo{ var cliCtx = context.Background() var didSetupStatusHandler bool -func setupStatusHandler(debug bool) { +func setupStatusHandler(debug bool, noColor bool) { didSetupStatusHandler = true origStderr := os.Stderr @@ -83,7 +85,7 @@ func setupStatusHandler(debug bool) { isTerminal := isatty.IsTerminal(os.Stderr.Fd()) var sh status.StatusHandler if !debug && isatty.IsTerminal(os.Stderr.Fd()) { - sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, isTerminal, false) + sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, isTerminal, !noColor, false) } else { sh = status.NewSimpleStatusHandler(func(message string) { _, _ = fmt.Fprintf(origStderr, "%s\n", message) @@ -187,7 +189,7 @@ func (c *cli) preRun() error { if err != nil { return err } - setupStatusHandler(c.Debug) + setupStatusHandler(c.Debug, c.NoColor) c.checkNewVersion() return nil } @@ -214,6 +216,8 @@ func initViper() { } func Execute() { + colorable.EnableColorsStdout(nil) + root := cli{} rootCmd, err := buildRootCobraCmd(&root, "kluctl", "Deploy and manage complex deployments on Kubernetes", @@ -241,7 +245,7 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif err = rootCmd.ExecuteContext(cliCtx) if !didSetupStatusHandler { - setupStatusHandler(false) + setupStatusHandler(false, true) } if cpuProfileFile != nil { diff --git a/pkg/status/status_handler.go b/pkg/status/status_handler.go index 3f10a4763..a55075de4 100644 --- a/pkg/status/status_handler.go +++ b/pkg/status/status_handler.go @@ -12,10 +12,11 @@ import ( ) type MultiLineStatusHandler struct { - ctx context.Context - out io.Writer - isTerminal bool - trace bool + ctx context.Context + out io.Writer + isTerminal bool + enableColor bool + trace bool ml *multiline.MultiLinePrinter } @@ -33,12 +34,13 @@ type statusLine struct { mutex sync.Mutex } -func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, isTerminal bool, trace bool) *MultiLineStatusHandler { +func NewMultiLineStatusHandler(ctx context.Context, out io.Writer, isTerminal bool, enableColor bool, trace bool) *MultiLineStatusHandler { sh := &MultiLineStatusHandler{ - ctx: ctx, - out: out, - isTerminal: isTerminal, - trace: trace, + ctx: ctx, + out: out, + isTerminal: isTerminal, + enableColor: enableColor, + trace: trace, } sh.start() @@ -88,6 +90,21 @@ func (s *MultiLineStatusHandler) startStatus(total int, message string, barOverr return sl } +func (s *MultiLineStatusHandler) withColor(c string, txt string) string { + if !s.isTerminal || !s.enableColor { + return txt + } + switch c { + case "red": + c = "\x1b[31m" + case "green": + c = "\x1b[32m" + case "yellow": + c = "\x1b[33m" + } + return fmt.Sprintf("%s%s\x1b[0m", c, txt) +} + func (s *MultiLineStatusHandler) printLine(message string, barOverride string, doFlush bool) { s.ml.NewTopLine(func() string { return fmt.Sprintf("%s %s", barOverride, message) @@ -98,7 +115,7 @@ func (s *MultiLineStatusHandler) printLine(message string, barOverride string, d } func (s *MultiLineStatusHandler) Info(message string) { - o := withColor("green", "ⓘ") + o := s.withColor("green", "ⓘ") s.printLine(message, o, true) } @@ -107,12 +124,12 @@ func (s *MultiLineStatusHandler) InfoFallback(message string) { } func (s *MultiLineStatusHandler) Warning(message string) { - o := withColor("yellow", "⚠") + o := s.withColor("yellow", "⚠") s.printLine(message, o, true) } func (s *MultiLineStatusHandler) Error(message string) { - o := withColor("red", "✗") + o := s.withColor("red", "✗") s.printLine(message, o, true) } @@ -127,7 +144,7 @@ func (s *MultiLineStatusHandler) PlainText(text string) { } func (s *MultiLineStatusHandler) Prompt(password bool, message string) (string, error) { - o := withColor("yellow", "?") + o := s.withColor("yellow", "?") sl := s.startStatus(1, message, o) defer sl.end(o) @@ -188,10 +205,10 @@ func (sl *statusLine) end(barOverride string) { func (sl *statusLine) End(result EndResult) { switch result { case EndSuccess: - sl.end(withColor("green", "✓")) + sl.end(sl.slh.withColor("green", "✓")) case EndWarning: - sl.end(withColor("yellow", "⚠")) + sl.end(sl.slh.withColor("yellow", "⚠")) case EndError: - sl.end(withColor("red", "✗")) + sl.end(sl.slh.withColor("red", "✗")) } } diff --git a/pkg/status/utils.go b/pkg/status/utils.go index 21555d432..cc5002217 100644 --- a/pkg/status/utils.go +++ b/pkg/status/utils.go @@ -2,7 +2,6 @@ package status import ( "bufio" - "fmt" "io" ) @@ -64,15 +63,3 @@ func (r *replaceRReader) Read(p []byte) (int, error) { } return written, nil } - -func withColor(c string, s string) string { - switch c { - case "red": - c = "\x1b[31m" - case "green": - c = "\x1b[32m" - case "yellow": - c = "\x1b[33m" - } - return fmt.Sprintf("%s%s\x1b[0m", c, s) -} From 7bb7747095af82c18d8a1ded6c4a9b8d08a0e9e9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Sep 2022 21:17:57 +0200 Subject: [PATCH 0370/2268] chore: Run go fmt ./... --- cmd/kluctl/commands/root.go | 2 +- main.go | 2 +- pkg/utils/kustomize.go | 8 ++++---- pkg/utils/python_scanner/scanner.go | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 3fabd1561..d98200898 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/main.go b/main.go index fb256f4c4..f6ca27150 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/pkg/utils/kustomize.go b/pkg/utils/kustomize.go index 21fbe4fe2..44a92258a 100644 --- a/pkg/utils/kustomize.go +++ b/pkg/utils/kustomize.go @@ -18,10 +18,10 @@ import ( var kustomizeBuildMutex sync.Mutex // SecureBuildKustomization wraps krusty.MakeKustomizer with the following settings: -// - secure on-disk FS denying operations outside root -// - load files from outside the kustomization dir path -// (but not outside root) -// - disable plugins except for the builtin ones +// - secure on-disk FS denying operations outside root +// - load files from outside the kustomization dir path +// (but not outside root) +// - disable plugins except for the builtin ones func SecureBuildKustomization(root, dirPath string, allowRemoteBases bool) (_ resmap.ResMap, err error) { var fs filesys.FileSystem diff --git a/pkg/utils/python_scanner/scanner.go b/pkg/utils/python_scanner/scanner.go index c6f385484..c64b1e506 100644 --- a/pkg/utils/python_scanner/scanner.go +++ b/pkg/utils/python_scanner/scanner.go @@ -60,7 +60,6 @@ func (pos Position) String() string { // // Use GoTokens to configure the Scanner such that it accepts all Go // literal tokens including Go identifiers. Comments will be skipped. -// const ( ScanIdents = 1 << -Ident ScanInts = 1 << -Int From 0682af3ac4b6553d3a63ae92b142fff223f6b24f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 26 Sep 2022 12:26:43 +0200 Subject: [PATCH 0371/2268] feat: Pass k8s client into Helm install commands This is a preparation to allow lookup in Helm charts. It will however not work before https://github.com/helm/helm/pull/9426 or a comparable solution is merged. --- pkg/deployment/helm_chart.go | 10 ++++++---- pkg/k8s/client_factory.go | 6 +++++- pkg/k8s/fake_client_factory.go | 4 ++++ pkg/k8s/k8s_cluster.go | 29 +++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index a533ba8de..2d930c4b0 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -90,13 +90,15 @@ func (c *helmChart) GetFullOutputPath() (string, error) { return securejoin.SecureJoin(dir, c.GetOutputPath()) } -func (c *helmChart) buildHelmConfig() (*action.Configuration, error) { +func (c *helmChart) buildHelmConfig(k *k8s.K8sCluster) (*action.Configuration, error) { rc, err := registry.NewClient() if err != nil { return nil, err } + return &action.Configuration{ - RegistryClient: rc, + RESTClientGetter: k, + RegistryClient: rc, }, nil } @@ -114,7 +116,7 @@ func (c *helmChart) Pull(ctx context.Context) error { targetDir := filepath.Join(filepath.Dir(c.configFile), "charts") _ = os.RemoveAll(chartDir) - cfg, err := c.buildHelmConfig() + cfg, err := c.buildHelmConfig(nil) if err != nil { return err } @@ -232,7 +234,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { return err } } - cfg, err := c.buildHelmConfig() + cfg, err := c.buildHelmConfig(k) if err != nil { return err } diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go index 6ab49a220..8480a2540 100644 --- a/pkg/k8s/client_factory.go +++ b/pkg/k8s/client_factory.go @@ -14,6 +14,7 @@ import ( ) type ClientFactory interface { + RESTConfig() *rest.Config GetCA() []byte CloseIdleConnections() @@ -28,12 +29,15 @@ type realClientFactory struct { httpClient *http.Client } +func (r *realClientFactory) RESTConfig() *rest.Config { + return r.config +} + func (r *realClientFactory) GetCA() []byte { return r.config.CAData } func (r *realClientFactory) DiscoveryClient() (discovery.DiscoveryInterface, error) { - apiHost, err := url.Parse(r.config.Host) if err != nil { return nil, err diff --git a/pkg/k8s/fake_client_factory.go b/pkg/k8s/fake_client_factory.go index b01673f27..cee966194 100644 --- a/pkg/k8s/fake_client_factory.go +++ b/pkg/k8s/fake_client_factory.go @@ -23,6 +23,10 @@ type fakeClientFactory struct { scheme *runtime.Scheme } +func (f *fakeClientFactory) RESTConfig() *rest.Config { + return nil +} + func (f *fakeClientFactory) GetCA() []byte { return []byte{} } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 8b58788c9..de92465ac 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -16,9 +16,11 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/version" + "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" "sync" "time" ) @@ -449,3 +451,30 @@ func (k *K8sCluster) ProxyGet(scheme, namespace, name, port, path string, params } return ret.Stream(k.ctx) } + +func (k *K8sCluster) ToRESTConfig() (*rest.Config, error) { + return k.clientFactory.RESTConfig(), nil +} + +func (k *K8sCluster) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) { + d, err := k.clientFactory.DiscoveryClient() + if err != nil { + return nil, err + } + cd, ok := d.(discovery.CachedDiscoveryInterface) + if !ok { + return nil, fmt.Errorf("not a CachedDiscoveryInterface") + } + return cd, nil +} + +func (k *K8sCluster) ToRESTMapper() (meta.RESTMapper, error) { + discoveryClient, err := k.ToDiscoveryClient() + if err != nil { + return nil, err + } + + mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) + expander := restmapper.NewShortcutExpander(mapper, discoveryClient) + return expander, nil +} From 7e2b6b690403df4e386588c0b83e58735fd68a63 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 26 Sep 2022 13:10:45 +0200 Subject: [PATCH 0372/2268] fix: Add new-line between diff tables --- cmd/kluctl/commands/command_result.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index 668e21ec2..b5702f1c7 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -38,7 +38,10 @@ func formatCommandResultText(cr *types.CommandResult) string { prettyObjectRefs(buf, refs) buf.WriteString("\n") - for _, co := range cr.ChangedObjects { + for i, co := range cr.ChangedObjects { + if i != 0 { + buf.WriteString("\n") + } prettyChanges(buf, co.Ref, co.Changes) } } From b717b2bb6d7bcc710b7ed8abde75191b80dcda20 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 26 Sep 2022 14:00:08 +0200 Subject: [PATCH 0373/2268] fix: Use custom multiline implementation uilive had issues on windows and causes partially cleared lines. --- go.mod | 9 +- go.sum | 10 +-- pkg/status/multiline/clear_lines.go | 4 + pkg/status/multiline/clear_lines_posix.go | 15 ++++ pkg/status/multiline/clear_lines_windows.go | 94 +++++++++++++++++++++ pkg/status/multiline/multiline.go | 55 +++++++----- 6 files changed, 154 insertions(+), 33 deletions(-) create mode 100644 pkg/status/multiline/clear_lines.go create mode 100644 pkg/status/multiline/clear_lines_posix.go create mode 100644 pkg/status/multiline/clear_lines_windows.go diff --git a/go.mod b/go.mod index 9aa4aa26d..8b701789b 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,17 @@ go 1.19 require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/aws/aws-sdk-go v1.44.103 github.com/bitnami-labs/sealed-secrets v0.18.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible - github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fluxcd/pkg/kustomize v0.7.0 github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 github.com/google/go-containerregistry v0.11.0 - github.com/gosuri/uilive v0.0.4 github.com/hashicorp/vault/api v1.8.0 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 @@ -24,7 +23,9 @@ require ( github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 github.com/kluctl/go-jinja2 v0.0.0-20220922102415-424914b4142b + github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 + github.com/mattn/go-runewidth v0.0.9 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 github.com/ohler55/ojg v1.14.4 @@ -94,6 +95,7 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect @@ -153,8 +155,6 @@ require ( github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -181,7 +181,6 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/rivo/uniseg v0.4.2 // indirect github.com/rubenv/sql-migrate v1.2.0 // indirect github.com/russross/blackfriday v1.6.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect diff --git a/go.sum b/go.sum index 40c565407..4c724677b 100644 --- a/go.sum +++ b/go.sum @@ -113,6 +113,8 @@ github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErI github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -515,8 +517,6 @@ github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/g github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= -github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= -github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= @@ -754,9 +754,8 @@ github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mN github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -942,9 +941,6 @@ github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mo github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= -github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/pkg/status/multiline/clear_lines.go b/pkg/status/multiline/clear_lines.go new file mode 100644 index 000000000..0b0a41c7b --- /dev/null +++ b/pkg/status/multiline/clear_lines.go @@ -0,0 +1,4 @@ +package multiline + +// ESC is the ASCII code for escape character +const ESC = 27 diff --git a/pkg/status/multiline/clear_lines_posix.go b/pkg/status/multiline/clear_lines_posix.go new file mode 100644 index 000000000..0742f106d --- /dev/null +++ b/pkg/status/multiline/clear_lines_posix.go @@ -0,0 +1,15 @@ +//go:build !windows + +package multiline + +import ( + "fmt" + "strings" +) + +// clear the line and move the cursor up +var clear = fmt.Sprintf("%c[%dA%c[2K", ESC, 1, ESC) + +func (ml *MultiLinePrinter) clearLines(lines int) { + _, _ = fmt.Fprint(ml.w, strings.Repeat(clear, lines)) +} diff --git a/pkg/status/multiline/clear_lines_windows.go b/pkg/status/multiline/clear_lines_windows.go new file mode 100644 index 000000000..b58b42fa2 --- /dev/null +++ b/pkg/status/multiline/clear_lines_windows.go @@ -0,0 +1,94 @@ +//go:build windows + +package multiline + +import ( + "fmt" + "github.com/mattn/go-isatty" + "io" + "strings" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") +) + +// clear the line and move the cursor up +var clear = fmt.Sprintf("%c[%dA%c[2K\r", ESC, 0, ESC) + +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +// FdWriter is a writer with a file descriptor. +type FdWriter interface { + io.Writer + Fd() uintptr +} + +func (ml *MultiLinePrinter) clearLines(lines int) { + f, ok := ml.w.(FdWriter) + if ok && !isatty.IsTerminal(f.Fd()) { + ok = false + } + if !ok { + _, _ = fmt.Fprint(ml.w, strings.Repeat(clear, lines)) + return + } + fd := f.Fd() + var info windows.ConsoleScreenBufferInfo + if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { + return + } + + info.CursorPosition.Y -= int16(lines) + if info.CursorPosition.Y < 0 { + info.CursorPosition.Y = 0 + } + _, _, _ = procSetConsoleCursorPosition.Call( + uintptr(fd), + uintptr(uint32(uint16(info.CursorPosition.Y))<<16|uint32(uint16(info.CursorPosition.X))), + ) + + // clear the lines + cursor := &windows.Coord{ + X: info.Window.Left, + Y: info.CursorPosition.Y, + } + count := uint32(info.Size.X) * uint32(lines) + _, _, _ = procFillConsoleOutputCharacter.Call( + uintptr(fd), + uintptr(' '), + uintptr(count), + *(*uintptr)(unsafe.Pointer(cursor)), + uintptr(unsafe.Pointer(new(uint32))), + ) +} diff --git a/pkg/status/multiline/multiline.go b/pkg/status/multiline/multiline.go index f2a67889e..9ecce1d7c 100644 --- a/pkg/status/multiline/multiline.go +++ b/pkg/status/multiline/multiline.go @@ -1,9 +1,10 @@ package multiline import ( - "bytes" "fmt" - "github.com/gosuri/uilive" + "github.com/acarl005/stripansi" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/mattn/go-runewidth" "io" "sync" "time" @@ -13,8 +14,8 @@ type MultiLinePrinter struct { topLines []*Line lines []*Line - uil *uilive.Writer - lastWritten []byte + w io.Writer + prevLines []string ticker *time.Ticker tdone chan bool @@ -30,11 +31,9 @@ type Line struct { } func (ml *MultiLinePrinter) Start(w io.Writer) { - ml.uil = uilive.New() - ml.uil.Out = w - ml.uil.Start() + ml.w = w - ml.ticker = time.NewTicker(100 * time.Millisecond) + ml.ticker = time.NewTicker(1 * time.Millisecond) ml.tdone = make(chan bool) go ml.loop() @@ -44,7 +43,6 @@ func (ml *MultiLinePrinter) Stop() { ml.Flush() ml.tdone <- true <-ml.tdone - ml.uil.Stop() } func (ml *MultiLinePrinter) loop() { @@ -69,27 +67,42 @@ func (ml *MultiLinePrinter) Flush() { ml.mutex.Lock() defer ml.mutex.Unlock() - hadTopLines := false + tw := utils.GetTermWidth() + + // Count the number of lines that need to be cleared. We need to take wrapping into account as well + prevTotalLines := 0 + for _, line := range ml.prevLines { + prevTotalLines += ml.countConsoleLines(line, tw) + } + if prevTotalLines > 0 { + ml.clearLines(prevTotalLines) + } + if len(ml.topLines) != 0 { - hadTopLines = true - topBuf := bytes.NewBuffer(nil) for _, l := range ml.topLines { - _, _ = fmt.Fprintf(topBuf, "%s\n", l.s()) + _, _ = fmt.Fprintf(ml.w, "%s\n", l.s()) } - - _, _ = ml.uil.Bypass().Write(topBuf.Bytes()) ml.topLines = nil } - linesBuf := bytes.NewBuffer(nil) + ml.prevLines = nil for _, l := range ml.lines { - _, _ = fmt.Fprintf(linesBuf, "%s\n", l.s()) + s := l.s() + ml.prevLines = append(ml.prevLines, s) + _, _ = fmt.Fprintf(ml.w, "%s\n", s) } - newBytes := linesBuf.Bytes() - if hadTopLines || !bytes.Equal(newBytes, ml.lastWritten) { - _, _ = ml.uil.Write(newBytes) - ml.lastWritten = newBytes +} + +func (ml *MultiLinePrinter) countConsoleLines(s string, tw int) int { + s = stripansi.Strip(s) + w := runewidth.StringWidth(s) + cnt := 1 + for w > tw { + cnt++ + s = s[tw:] + w = runewidth.StringWidth(s) } + return cnt } func (ml *MultiLinePrinter) NewTopLine(s LineFunc) { From e190eec384f337ae26b7e4014f6778cc0206c723 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 26 Sep 2022 14:35:23 +0200 Subject: [PATCH 0374/2268] fix: Use custom GetSize/GetWidth implementation --- cmd/kluctl/commands/cobra_utils.go | 3 ++- pkg/status/multiline/multiline.go | 4 ++-- pkg/utils/prettytable.go | 20 ++------------------ pkg/utils/term/term_size.go | 22 ++++++++++++++++++++++ pkg/utils/term/term_size_unix.go | 14 ++++++++++++++ pkg/utils/term/term_size_windows.go | 21 +++++++++++++++++++++ 6 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 pkg/utils/term/term_size.go create mode 100644 pkg/utils/term/term_size_unix.go create mode 100644 pkg/utils/term/term_size_windows.go diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index 1d2fa005a..3f8bc6316 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -3,6 +3,7 @@ package commands import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/term" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -252,7 +253,7 @@ func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { func (c *rootCommand) helpFunc(cg *commandAndGroups) { cmd := cg.cmd - termWidth := utils.GetTermWidth() + termWidth := term.GetWidth() h := "Usage: " if cmd.Runnable() { diff --git a/pkg/status/multiline/multiline.go b/pkg/status/multiline/multiline.go index 9ecce1d7c..09bcd5b40 100644 --- a/pkg/status/multiline/multiline.go +++ b/pkg/status/multiline/multiline.go @@ -3,7 +3,7 @@ package multiline import ( "fmt" "github.com/acarl005/stripansi" - "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/term" "github.com/mattn/go-runewidth" "io" "sync" @@ -67,7 +67,7 @@ func (ml *MultiLinePrinter) Flush() { ml.mutex.Lock() defer ml.mutex.Unlock() - tw := utils.GetTermWidth() + tw := term.GetWidth() // Count the number of lines that need to be cleared. We need to take wrapping into account as well prevTotalLines := 0 diff --git a/pkg/utils/prettytable.go b/pkg/utils/prettytable.go index cb9858530..88db41614 100644 --- a/pkg/utils/prettytable.go +++ b/pkg/utils/prettytable.go @@ -2,10 +2,8 @@ package utils import ( "bytes" - "golang.org/x/crypto/ssh/terminal" - "os" + "github.com/kluctl/kluctl/v2/pkg/utils/term" "sort" - "strconv" "strings" ) @@ -25,20 +23,6 @@ func (t *PrettyTable) SortRows(col int) { }) } -func GetTermWidth() int { - if c, ok := os.LookupEnv("COLUMNS"); ok { - tw, err := strconv.ParseInt(c, 10, 32) - if err == nil { - return int(tw) - } - } - tw, _, err := terminal.GetSize(0) - if err != nil { - return 80 - } - return tw -} - func (t *PrettyTable) Render(limitWidths []int) string { cols := len(t.rows[0]) @@ -82,7 +66,7 @@ func (t *PrettyTable) Render(limitWidths []int) string { } if len(limitWidths) < cols { - tw := GetTermWidth() + tw := term.GetWidth() // last column should use all remaining space tw = tw - widthSum - (cols-1)*3 - 4 if tw <= 0 { diff --git a/pkg/utils/term/term_size.go b/pkg/utils/term/term_size.go new file mode 100644 index 000000000..706f09180 --- /dev/null +++ b/pkg/utils/term/term_size.go @@ -0,0 +1,22 @@ +package term + +import ( + "os" + "strconv" +) + +var origStdout = os.Stdout + +func GetWidth() int { + if c, ok := os.LookupEnv("COLUMNS"); ok { + tw, err := strconv.ParseInt(c, 10, 32) + if err == nil { + return int(tw) + } + } + w, _, err := GetSize(int(origStdout.Fd())) + if err != nil { + return 80 + } + return w +} diff --git a/pkg/utils/term/term_size_unix.go b/pkg/utils/term/term_size_unix.go new file mode 100644 index 000000000..f283e307c --- /dev/null +++ b/pkg/utils/term/term_size_unix.go @@ -0,0 +1,14 @@ +//go:build !windows + +package term + +import "golang.org/x/sys/unix" + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) + if err != nil { + return -1, -1, err + } + return int(ws.Col), int(ws.Row), nil +} diff --git a/pkg/utils/term/term_size_windows.go b/pkg/utils/term/term_size_windows.go new file mode 100644 index 000000000..51a9d1310 --- /dev/null +++ b/pkg/utils/term/term_size_windows.go @@ -0,0 +1,21 @@ +//go:build windows + +package term + +import ( + "golang.org/x/sys/windows" +) + +// GetSize returns the visible dimensions of the given terminal. +// +// These dimensions don't include any scrollback buffer height. +func GetSize(fd int) (width, height int, err error) { + var info windows.ConsoleScreenBufferInfo + if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { + return 0, 0, err + } + // terminal.GetSize from crypto/ssh adds "+ 1" to both width and height: + // https://go.googlesource.com/crypto/+/refs/heads/release-branch.go1.14/ssh/terminal/util_windows.go#75 + // but looks like this is a root cause of issue #66, so removing both "+ 1" have fixed it. + return int(info.Window.Right - info.Window.Left), int(info.Window.Bottom - info.Window.Top), nil +} From d1e5da6d4c74f7a1a08a2161aa091a72d663b2ac Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 26 Sep 2022 18:43:27 +0200 Subject: [PATCH 0375/2268] chore: Run go get -u ./... --- go.mod | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 8b701789b..81af6f3a8 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,11 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.103 - github.com/bitnami-labs/sealed-secrets v0.18.2 + github.com/aws/aws-sdk-go v1.44.105 + github.com/bitnami-labs/sealed-secrets v0.18.5 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible - github.com/fluxcd/pkg/kustomize v0.7.0 + github.com/fluxcd/pkg/kustomize v0.8.0 github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect @@ -25,7 +25,7 @@ require ( github.com/kluctl/go-jinja2 v0.0.0-20220922102415-424914b4142b github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 - github.com/mattn/go-runewidth v0.0.9 + github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 github.com/ohler55/ojg v1.14.4 @@ -39,9 +39,9 @@ require ( github.com/stretchr/testify v1.8.0 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.2 - golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 - golang.org/x/net v0.0.0-20220921203646-d300de134e69 - golang.org/x/sync v0.0.0-20220907140024-f12130a52804 + golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be + golang.org/x/net v0.0.0-20220923203811-8be639271d50 + golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 golang.org/x/term v0.0.0-20220919170432-7a66f970e087 golang.org/x/text v0.3.7 @@ -53,7 +53,7 @@ require ( k8s.io/apimachinery v0.25.2 k8s.io/client-go v0.25.2 k8s.io/klog/v2 v2.80.1 - sigs.k8s.io/kind v0.15.0 + sigs.k8s.io/kind v0.16.0 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 @@ -73,7 +73,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect - github.com/Microsoft/go-winio v0.5.2 // indirect + github.com/Microsoft/go-winio v0.6.0 // indirect github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/alessio/shellescape v1.4.1 // indirect @@ -89,7 +89,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v20.10.18+incompatible // indirect github.com/docker/docker v20.10.18+incompatible // indirect - github.com/docker/docker-credential-helpers v0.6.4 // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -147,7 +147,7 @@ require ( github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.10 // indirect + github.com/klauspost/compress v1.15.11 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.1 // indirect @@ -155,7 +155,7 @@ require ( github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect @@ -171,7 +171,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect + github.com/opencontainers/image-spec v1.1.0-rc1 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect @@ -181,6 +181,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect + github.com/rivo/uniseg v0.4.2 // indirect github.com/rubenv/sql-migrate v1.2.0 // indirect github.com/russross/blackfriday v1.6.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect @@ -195,12 +196,14 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect - go.starlark.net v0.0.0-20220817180228-f738f5508c12 // indirect + go.starlark.net v0.0.0-20220926145019-14b050677505 // indirect go.uber.org/atomic v1.10.0 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect - golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45 // indirect + golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect + golang.org/x/tools v0.1.12 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 // indirect + google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc // indirect google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -212,7 +215,7 @@ require ( k8s.io/component-base v0.25.2 // indirect k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea // indirect k8s.io/kubectl v0.25.2 // indirect - k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 // indirect + k8s.io/utils v0.0.0-20220922133306-665eaaec4324 // indirect oras.land/oras-go v1.2.0 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/yaml v1.3.0 // indirect From c6281fa167a3afdea9c8f906b8ac79ecaab7c9e4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 26 Sep 2022 18:48:20 +0200 Subject: [PATCH 0376/2268] chore: Run go mod tidy --- go.sum | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/go.sum b/go.sum index 4c724677b..46fc203bd 100644 --- a/go.sum +++ b/go.sum @@ -105,6 +105,8 @@ github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jB github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= @@ -151,6 +153,8 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.44.103 h1:tbhBHKgiZSIUkG8FcHy3wYKpPVvp65Wn7ZiX0B8phpY= github.com/aws/aws-sdk-go v1.44.103/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.105 h1:UUwoD1PRKIj3ltrDUYTDQj5fOTK3XsnqolLpRTMmSEM= +github.com/aws/aws-sdk-go v1.44.105/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= 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= @@ -158,6 +162,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitnami-labs/sealed-secrets v0.18.2 h1:CfZD0JmhAPOQ7YqxrxZ90/+BjqTheWztBOYploqO6nc= github.com/bitnami-labs/sealed-secrets v0.18.2/go.mod h1:wlhaZ+SI5aJkpq9CyyP0pVLJEj4LPXLzLHb2TnID/Rc= +github.com/bitnami-labs/sealed-secrets v0.18.5 h1:PT/lxfgWnP8l8ybpvTsHuJltY4A35LI8if19Ww9FbcU= +github.com/bitnami-labs/sealed-secrets v0.18.5/go.mod h1:nzCyKhzDRbNySRUZXR6tvui4s95LbuaNpBoGRvV9Nk8= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= github.com/blizzy78/varnamelen v0.3.0/go.mod h1:hbwRdBvoBqxk34XyQ6HA0UH3G0/1TKuv5AC4eaBT0Ec= @@ -174,6 +180,7 @@ github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -238,6 +245,8 @@ github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfem github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= @@ -283,6 +292,8 @@ github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8S github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fluxcd/pkg/kustomize v0.7.0 h1:604rlpRZTWaOfzDZ1W93aHaFh9kn8/UMX/wzsjwIUQY= github.com/fluxcd/pkg/kustomize v0.7.0/go.mod h1:zJY3Z0+SX+zs+/A1F6fCT0JvUce265XnrpTtHnujXPo= +github.com/fluxcd/pkg/kustomize v0.8.0 h1:8AdEvp6y38ISZzoi0H82Si5zkmLXClbeX10W7HevB00= +github.com/fluxcd/pkg/kustomize v0.8.0/go.mod h1:zGtCZF6V3hMWcf46SqrQc10fS9yUlKzi2UcFUeabDAE= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -620,6 +631,7 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= @@ -669,6 +681,8 @@ github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo= github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= github.com/kluctl/go-jinja2 v0.0.0-20220922102415-424914b4142b h1:4fBKK0PZ8e/DC4GqM6RrUSSE2J6SlVTKNPb8awYxb5g= @@ -729,6 +743,7 @@ github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2 github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= +github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= @@ -756,6 +771,8 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -765,6 +782,8 @@ github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpe github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= +github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0= @@ -860,6 +879,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+czc/J8SlhPKLOtVLMQc+xDCFBT73ZStMsRhSsUhsSg= github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q= +github.com/opencontainers/image-spec v1.1.0-rc1 h1:lfG+OTa7V8PD3PKvkocSG9KAcA9MANqJn53m31Fvwkc= +github.com/opencontainers/image-spec v1.1.0-rc1/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= @@ -941,6 +962,9 @@ github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mo github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= +github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1121,6 +1145,8 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20220817180228-f738f5508c12 h1:xOBJXWGEDwU5xSDxH6macxO11Us0AH2fTa9rmsbbF7g= go.starlark.net v0.0.0-20220817180228-f738f5508c12/go.mod h1:VZcBMdr3cT3PnBoWunTabuSEXwVAH+ZJ5zxfs3AdASk= +go.starlark.net v0.0.0-20220926145019-14b050677505 h1:W0MibAL5BiEenQR+F/EF/a4HJhgLngHVvm6jbtUW0PM= +go.starlark.net v0.0.0-20220926145019-14b050677505/go.mod h1:qsNirHv+Awo5xHuNyQ/0niov6kDxdBs+bqpVMBCW77k= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1163,6 +1189,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY= golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -1200,6 +1228,8 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1257,6 +1287,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps= golang.org/x/net v0.0.0-20220921203646-d300de134e69/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI= +golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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= @@ -1290,6 +1322,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A= golang.org/x/sync v0.0.0-20220907140024-f12130a52804/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 h1:ZrnxWX62AgTKOSagEqxvb3ffipvEDX2pl7E1TdqLqIc= +golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1387,6 +1421,7 @@ golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBc 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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w= golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1406,6 +1441,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45 h1:yuLAip3bfURHClMG9VBdzPrQvCWjWiWUTBGV+/fCbUs= golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1509,6 +1546,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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= @@ -1614,6 +1653,8 @@ google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 h1:K1zaaMdYBXRyX+cwFnxj7M6zwDyumLQMZ5xqwGvjreQ= google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737/go.mod h1:2r/26NEF3bFmT3eC3aZreahSal0C3Shl8Gi6vyDYqOQ= +google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc h1:saaNe2+SBQxandnzcD/qB1JEBQ2Pqew+KlFLLdA/XcM= +google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc/go.mod h1:yEEpwVWKMZZzo81NwRgyEJnA2fQvpXAYPVisv8EgDVs= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1728,6 +1769,7 @@ k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY= k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= @@ -1737,6 +1779,8 @@ k8s.io/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA= k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg= k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220922133306-665eaaec4324 h1:i+xdFemcSNuJvIfBlaYuXgRondKxK4z4prVPKzEaelI= +k8s.io/utils v0.0.0-20220922133306-665eaaec4324/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= @@ -1750,6 +1794,8 @@ sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kind v0.15.0 h1:Fskj234L4hjQlsScCgeYvCBIRt06cjLzc7+kbr1u8Tg= sigs.k8s.io/kind v0.15.0/go.mod h1:cKTqagdRyUQmihhBOd+7p43DpOPRn9rHsUC08K1Jbsk= +sigs.k8s.io/kind v0.16.0 h1:GFXyyxtPnHFKqXr3ZG8/X0+0K9sl69lejStlPn2WQyM= +sigs.k8s.io/kind v0.16.0/go.mod h1:cKTqagdRyUQmihhBOd+7p43DpOPRn9rHsUC08K1Jbsk= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= From 4ea1d40bb7dd5909ecb0b73011cafdfa73bd76f8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 27 Sep 2022 10:19:43 +0200 Subject: [PATCH 0377/2268] fix: Use 100ms for multiline updates --- pkg/status/multiline/multiline.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/status/multiline/multiline.go b/pkg/status/multiline/multiline.go index 09bcd5b40..399942828 100644 --- a/pkg/status/multiline/multiline.go +++ b/pkg/status/multiline/multiline.go @@ -33,7 +33,7 @@ type Line struct { func (ml *MultiLinePrinter) Start(w io.Writer) { ml.w = w - ml.ticker = time.NewTicker(1 * time.Millisecond) + ml.ticker = time.NewTicker(100 * time.Millisecond) ml.tdone = make(chan bool) go ml.loop() From d4b7cfd01b2b49d8501c1b3e855d35796a7d8113 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 27 Sep 2022 11:33:25 +0200 Subject: [PATCH 0378/2268] refactor: Generalize deprecation handling --- pkg/kluctl_project/project.go | 3 --- pkg/kluctl_project/project_load.go | 12 +++++------ pkg/kluctl_project/target_context.go | 6 ++---- pkg/status/status.go | 32 ++++++++++++++++++++++++---- pkg/vars/vars_loader.go | 2 +- 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index bca9ab1a3..ebb5b2e26 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -6,7 +6,6 @@ import ( "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/git/repocache" types2 "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils" "strings" ) @@ -29,8 +28,6 @@ type LoadedKluctlProject struct { J2 *jinja2.Jinja2 RP *repocache.GitRepoCache - - warnOnce utils.OnceByKey } func (c *LoadedKluctlProject) GetMetadata() *types2.ProjectMetadata { diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 3a2d1e730..738c3219c 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -96,10 +96,8 @@ func (c *LoadedKluctlProject) loadExternalProject(ep *types2.ExternalProject, de } if ep.Project != nil { - c.warnOnce.Do("external-projects", func() { - status.Warning(c.ctx, "External projects are deprecated and support for them will be removed in the future. "+ - "Use Git variable sources as replacement for cluster configs and Git includes as replacement for external deployment projects.") - }) + status.Deprecation(c.ctx, "external-projects", "External projects are deprecated and support for them will be removed in the future. "+ + "Use Git variable sources as replacement for cluster configs and Git includes as replacement for external deployment projects.") // pointing to an actual external project, so let's try to clone it return c.loadGitProject(ep.Project, defaultGitSubDir) @@ -156,13 +154,13 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { defer s.Failed() if c.loadArgs.LocalClusters != "" { - status.Warning(c.ctx, "--local-clusters is deprecated and will be removed in an upcoming version. Use variables loaded from git instead.") + status.Deprecation(c.ctx, "--local-clusters", "--local-clusters is deprecated and will be removed in an upcoming version. Use variables loaded from git instead.") } if c.loadArgs.LocalDeployment != "" { - status.Warning(c.ctx, "--local-deployment is deprecated and will be removed in an upcoming version. Use git includes instead.") + status.Deprecation(c.ctx, "--local-deployment", "--local-deployment is deprecated and will be removed in an upcoming version. Use git includes instead.") } if c.loadArgs.LocalSealedSecrets != "" { - status.Warning(c.ctx, "--local-sealed-secrets is deprecated and will be removed in an upcoming version.") + status.Deprecation(c.ctx, "--local-sealed-secrets", "--local-sealed-secrets is deprecated and will be removed in an upcoming version.") } deploymentInfo, err := c.loadExternalProject(c.Config.Deployment, "", c.loadArgs.LocalDeployment) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index f93c9e8ed..b2c9ad978 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -205,7 +205,7 @@ func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.Va return err } if len(secretEntry.Sources) != 0 { - status.Warning(p.ctx, "'sources' in secretSets is deprecated, use 'vars' instead") + status.Deprecation(p.ctx, "secrets-sets-sources", "'sources' in secretSets is deprecated, use 'vars' instead") err = varsLoader.LoadVarsList(varsCtx, secretEntry.Sources, searchDirs, "secrets") } else { err = varsLoader.LoadVarsList(varsCtx, secretEntry.Vars, searchDirs, "secrets") @@ -221,9 +221,7 @@ func (p *LoadedKluctlProject) LoadClusterConfig(clusterName string) (*types.Clus var err error var clusterConfig *types.ClusterConfig - p.warnOnce.Do("cluster-config", func() { - status.Warning(p.ctx, "Cluster configurations have been deprecated and support for them will be removed in a future kluctl release.") - }) + status.Deprecation(p.ctx, "cluster-config", "Cluster configurations have been deprecated and support for them will be removed in a future kluctl release.") clusterConfig, err = types.LoadClusterConfig(p.ClustersDir, clusterName) if err != nil { diff --git a/pkg/status/status.go b/pkg/status/status.go index c49f64dfc..8c8f158c3 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -3,6 +3,7 @@ package status import ( "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils" ) // StatusContext is used to report user-facing status/progress @@ -57,17 +58,33 @@ type StatusHandler interface { } type contextKey struct{} +type contextValue struct { + slh StatusHandler + deprecation utils.OnceByKey +} + +var noopContextValue = contextValue{ + slh: &NoopStatusHandler{}, +} func NewContext(ctx context.Context, slh StatusHandler) context.Context { - return context.WithValue(ctx, contextKey{}, slh) + return context.WithValue(ctx, contextKey{}, &contextValue{ + slh: slh, + }) } -func FromContext(ctx context.Context) StatusHandler { +func getContextValue(ctx context.Context) *contextValue { v := ctx.Value(contextKey{}) if v == nil { - return &NoopStatusHandler{} + return &noopContextValue } - return v.(StatusHandler) + cv := v.(*contextValue) + return cv +} + +func FromContext(ctx context.Context) StatusHandler { + v := getContextValue(ctx) + return v.slh } type Option func(s *StatusContext) @@ -250,6 +267,13 @@ func Prompt(ctx context.Context, password bool, message string, args ...any) (st return slh.Prompt(password, fmt.Sprintf(message, args...)) } +func Deprecation(ctx context.Context, key string, message string) { + cv := getContextValue(ctx) + cv.deprecation.Do(key, func() { + cv.slh.Warning(message) + }) +} + func Flush(ctx context.Context) { slh := FromContext(ctx) slh.Flush() diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index a479ec791..84986f0a1 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -76,7 +76,7 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear v.mergeVars(varsCtx, source.Values, rootKey) return nil } else if source.Path != nil { - status.Warning(v.ctx, "'path' is deprecated as vars source, use 'file' instead") + status.Deprecation(v.ctx, "vars-path", "'path' is deprecated as vars source, use 'file' instead") return v.loadFile(varsCtx, *source.Path, searchDirs, rootKey) } else if source.File != nil { return v.loadFile(varsCtx, *source.File, searchDirs, rootKey) From 1952338e127d2f06be04983a1ae214c969565b9d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 27 Sep 2022 11:33:56 +0200 Subject: [PATCH 0379/2268] feat: Upgrade go-jinja2 to add .templateignore support Also deprecate templateExcludes. --- go.mod | 2 +- go.sum | 517 +----------------------------- pkg/deployment/deployment_item.go | 4 + 3 files changed, 8 insertions(+), 515 deletions(-) diff --git a/go.mod b/go.mod index 81af6f3a8..cc78c4587 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 - github.com/kluctl/go-jinja2 v0.0.0-20220922102415-424914b4142b + github.com/kluctl/go-jinja2 v0.0.0-20220927093149-38ad307371ca github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 diff --git a/go.sum b/go.sum index 46fc203bd..1b9d4d57c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -4d63.com/gochecknoglobals v0.1.0/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= -bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= 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= @@ -14,7 +12,6 @@ cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6 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.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU= 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= @@ -23,11 +20,6 @@ cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPT cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= 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= @@ -39,23 +31,17 @@ cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOt 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= 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/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w= -cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk= 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= -contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo= -github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -77,25 +63,19 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= @@ -103,18 +83,15 @@ github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvd github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErIPzRrnogAXYwSoU7txA977LjDGrbkewJbg= github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= -github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= @@ -126,16 +103,10 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF 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/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= -github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= @@ -146,13 +117,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= -github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= -github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.103 h1:tbhBHKgiZSIUkG8FcHy3wYKpPVvp65Wn7ZiX0B8phpY= -github.com/aws/aws-sdk-go v1.44.103/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.105 h1:UUwoD1PRKIj3ltrDUYTDQj5fOTK3XsnqolLpRTMmSEM= github.com/aws/aws-sdk-go v1.44.105/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -160,35 +124,25 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.18.2 h1:CfZD0JmhAPOQ7YqxrxZ90/+BjqTheWztBOYploqO6nc= -github.com/bitnami-labs/sealed-secrets v0.18.2/go.mod h1:wlhaZ+SI5aJkpq9CyyP0pVLJEj4LPXLzLHb2TnID/Rc= github.com/bitnami-labs/sealed-secrets v0.18.5 h1:PT/lxfgWnP8l8ybpvTsHuJltY4A35LI8if19Ww9FbcU= github.com/bitnami-labs/sealed-secrets v0.18.5/go.mod h1:nzCyKhzDRbNySRUZXR6tvui4s95LbuaNpBoGRvV9Nk8= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= -github.com/blizzy78/varnamelen v0.3.0/go.mod h1:hbwRdBvoBqxk34XyQ6HA0UH3G0/1TKuv5AC4eaBT0Ec= -github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= -github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= -github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= -github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU= 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= @@ -202,40 +156,24 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX 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/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs= github.com/containerd/containerd v1.6.8/go.mod h1:By6p5KqPK0/7/CgO/A6t/Gz+CUYUu2zf1hUaaymVXB0= github.com/containerd/stargz-snapshotter/estargz v0.12.0 h1:idtwRTLjk2erqiYhPWy2L844By8NRFYEwYHcXhoIWPM= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc= -github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= github.com/docker/cli v20.10.18+incompatible h1:f/GQLsVpo10VvToRay2IraVA1wHz9KktZyjev3SIVDU= github.com/docker/cli v20.10.18+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -243,8 +181,6 @@ github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6 github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= -github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -256,8 +192,6 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= 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= @@ -271,10 +205,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y 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/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= -github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= 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= @@ -282,31 +213,21 @@ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fluxcd/pkg/kustomize v0.7.0 h1:604rlpRZTWaOfzDZ1W93aHaFh9kn8/UMX/wzsjwIUQY= -github.com/fluxcd/pkg/kustomize v0.7.0/go.mod h1:zJY3Z0+SX+zs+/A1F6fCT0JvUce265XnrpTtHnujXPo= github.com/fluxcd/pkg/kustomize v0.8.0 h1:8AdEvp6y38ISZzoi0H82Si5zkmLXClbeX10W7HevB00= github.com/fluxcd/pkg/kustomize v0.8.0/go.mod h1:zGtCZF6V3hMWcf46SqrQc10fS9yUlKzi2UcFUeabDAE= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= -github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= @@ -331,12 +252,9 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/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-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 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= @@ -353,27 +271,12 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.1/go.mod h1:4oGA3EZXTVItV/ipGiOx7NWkY5veFfcsOJVS2YxltLw= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= @@ -384,11 +287,7 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -397,7 +296,6 @@ github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQA github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-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= @@ -410,8 +308,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -430,26 +326,13 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.43.0/go.mod h1:VIFlUqidx5ggxDfQagdvd9E67UjMXtTHBkBQ7sHoC5Q= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 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.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= -github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= 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= @@ -463,7 +346,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/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/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= @@ -474,74 +356,40 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ 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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 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-20200507031123-427632fa3b1c/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-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= -github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/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/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= -github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw= -github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= -github.com/gostaticanalysis/analysisutil v0.4.1/go.mod h1:18U/DLpRgIUd459wGxVHE0fRgmo1UgHDcbw7F5idXu0= -github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= -github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI= -github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= -github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= -github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= -github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= -github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= -github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -550,7 +398,6 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo= github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -558,7 +405,6 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= @@ -584,7 +430,6 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -596,11 +441,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/vault/api v1.8.0 h1:7765sW1XBt+qf4XKIYE4ebY9qc/yi9V2/egzGSUNMZU= github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= github.com/hashicorp/vault/sdk v0.6.0 h1:6Z+In5DXHiUfZvIZdMx7e2loL1PPyDjA4bVh9ZTIAhs= @@ -609,16 +451,11 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/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.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= @@ -630,27 +467,17 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= -github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8= -github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= -github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOvVRsHiKkeGCT6tYBNWyDVuzj9wAaBb5R9qamfw= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -662,33 +489,22 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= 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/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo= -github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= -github.com/kluctl/go-jinja2 v0.0.0-20220922102415-424914b4142b h1:4fBKK0PZ8e/DC4GqM6RrUSSE2J6SlVTKNPb8awYxb5g= -github.com/kluctl/go-jinja2 v0.0.0-20220922102415-424914b4142b/go.mod h1:8deI9sssmQf1CiCnyRJoBgfIw4Hh3+WsHmmO9+xIIO0= +github.com/kluctl/go-jinja2 v0.0.0-20220927093149-38ad307371ca h1:OSl16ha01V8QHu/yKmsfKHx8r6ts/O3xHuT5SaaJ/1I= +github.com/kluctl/go-jinja2 v0.0.0-20220927093149-38ad307371ca/go.mod h1:kMEDb2dWTbpe6CT+XuGVPcnY1ph9IfHf4b43KN80qas= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -702,32 +518,18 @@ 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/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U= -github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= -github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -735,65 +537,37 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= -github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 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 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= -github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -814,11 +588,9 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -835,63 +607,30 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN 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/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= -github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= 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/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= -github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= -github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/exhaustive v0.2.3/go.mod h1:bhIX678Nx8inLM9PbpvK1yv6oGtoP8BfaIeMzgBNKvc= -github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ= -github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/ohler55/ojg v1.14.4 h1:L2ds8AlB5t/QbqSfhRwvagJzQ7pgmdrefMIypQs0Xik= github.com/ohler55/ojg v1.14.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+czc/J8SlhPKLOtVLMQc+xDCFBT73ZStMsRhSsUhsSg= -github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q= github.com/opencontainers/image-spec v1.1.0-rc1 h1:lfG+OTa7V8PD3PKvkocSG9KAcA9MANqJn53m31Fvwkc= github.com/opencontainers/image-spec v1.1.0-rc1/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 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= @@ -900,7 +639,6 @@ github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwb github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -911,12 +649,9 @@ 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 v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -950,57 +685,34 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/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/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= -github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= -github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= -github.com/quasilyte/go-ruleguard v0.3.13/go.mod h1:Ul8wwdqR6kBVOCt2dipDBkE+T6vAV/iixkrKuRTN1oQ= -github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= -github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= -github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU= github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg= -github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= -github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= -github.com/shirou/gopsutil/v3 v3.21.10/go.mod h1:t75NhzCZ/dYyPQjyQmrAYP6c8+LCdFANeBMdLPCNnew= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -1010,49 +722,32 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= -github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 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 v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= -github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= 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 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1066,33 +761,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= -github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= -github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= -github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= -github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= -github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= -github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tomarrell/wrapcheck/v2 v2.4.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY= -github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= -github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= -github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= -github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= @@ -1107,15 +777,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yeya24/promlinter v0.1.0/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= 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= @@ -1127,13 +790,9 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= 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= @@ -1143,52 +802,33 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.starlark.net v0.0.0-20220817180228-f738f5508c12 h1:xOBJXWGEDwU5xSDxH6macxO11Us0AH2fTa9rmsbbF7g= -go.starlark.net v0.0.0-20220817180228-f738f5508c12/go.mod h1:VZcBMdr3cT3PnBoWunTabuSEXwVAH+ZJ5zxfs3AdASk= go.starlark.net v0.0.0-20220926145019-14b050677505 h1:W0MibAL5BiEenQR+F/EF/a4HJhgLngHVvm6jbtUW0PM= go.starlark.net v0.0.0-20220926145019-14b050677505/go.mod h1:qsNirHv+Awo5xHuNyQ/0niov6kDxdBs+bqpVMBCW77k= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/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-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/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.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY= -golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1201,7 +841,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 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/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= 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= @@ -1227,16 +866,13 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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= @@ -1248,9 +884,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-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= @@ -1258,11 +891,9 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-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-20200421231249-e086a090c8fd/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-20200520004742-59133d7f0dd7/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= @@ -1277,16 +908,11 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps= -golang.org/x/net v0.0.0-20220921203646-d300de134e69/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI= golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1302,9 +928,6 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= @@ -1312,7 +935,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ 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-20190412183630-56d357773e84/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= @@ -1320,20 +942,14 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A= -golang.org/x/sync v0.0.0-20220907140024-f12130a52804/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 h1:ZrnxWX62AgTKOSagEqxvb3ffipvEDX2pl7E1TdqLqIc= golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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= @@ -1343,30 +959,22 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/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-20200124204421-9fbb57f87de9/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-20200420163511-1957bb5e6d1f/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= @@ -1379,7 +987,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210112080510-489259a85091/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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1394,21 +1001,14 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20210514084401-e8d321eab015/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-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1418,7 +1018,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1434,29 +1033,17 @@ 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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45 h1:yuLAip3bfURHClMG9VBdzPrQvCWjWiWUTBGV+/fCbUs= -golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/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-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/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-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/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= @@ -1466,26 +1053,17 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw 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-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/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-20190916130336-e45ffcd953cc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20200117220505-0cba7a3a9ee9/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= @@ -1495,56 +1073,24 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK 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-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-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-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 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-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210104081019-d8d6ddbec6ee/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.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= -golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= -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.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -1556,7 +1102,6 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt 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.10.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= @@ -1575,24 +1120,15 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 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.2/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-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/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= @@ -1600,7 +1136,6 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/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= @@ -1614,15 +1149,12 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG 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-20200423170343-7949de9c1215/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-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 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= @@ -1638,36 +1170,19 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 h1:K1zaaMdYBXRyX+cwFnxj7M6zwDyumLQMZ5xqwGvjreQ= -google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737/go.mod h1:2r/26NEF3bFmT3eC3aZreahSal0C3Shl8Gi6vyDYqOQ= google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc h1:saaNe2+SBQxandnzcD/qB1JEBQ2Pqew+KlFLLdA/XcM= google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc/go.mod h1:yEEpwVWKMZZzo81NwRgyEJnA2fQvpXAYPVisv8EgDVs= -google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= 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.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 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= @@ -1678,15 +1193,10 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 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.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1709,30 +1219,21 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/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.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.6/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= @@ -1754,7 +1255,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= @@ -1769,22 +1269,14 @@ k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY= k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea h1:3QOH5+2fGsY8e1qf+GIFpg+zw/JGNrgyZRQR7/m6uWg= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA= k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220922133306-665eaaec4324 h1:i+xdFemcSNuJvIfBlaYuXgRondKxK4z4prVPKzEaelI= k8s.io/utils v0.0.0-20220922133306-665eaaec4324/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4= oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -1792,8 +1284,6 @@ 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/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kind v0.15.0 h1:Fskj234L4hjQlsScCgeYvCBIRt06cjLzc7+kbr1u8Tg= -sigs.k8s.io/kind v0.15.0/go.mod h1:cKTqagdRyUQmihhBOd+7p43DpOPRn9rHsUC08K1Jbsk= sigs.k8s.io/kind v0.16.0 h1:GFXyyxtPnHFKqXr3ZG8/X0+0K9sl69lejStlPn2WQyM= sigs.k8s.io/kind v0.16.0/go.mod h1:cKTqagdRyUQmihhBOd+7p43DpOPRn9rHsUC08K1Jbsk= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= @@ -1802,6 +1292,5 @@ sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2 sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= 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.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index a5709038c..e0c30c28e 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -3,6 +3,7 @@ package deployment import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -119,6 +120,9 @@ func (di *DeploymentItem) render(forSeal bool) error { } var excludePatterns []string + if len(di.Project.Config.TemplateExcludes) != 0 { + status.Deprecation(di.ctx.Ctx, "template-excludes", "'templateExcludes' are deprecated, use .templateignore files instead.") + } excludePatterns = append(excludePatterns, di.Project.Config.TemplateExcludes...) err = filepath.WalkDir(*di.dir, func(p string, d fs.DirEntry, err error) error { if d.IsDir() { From 2ee42097e3045124cd96e98b5f46aa070c2ddedd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 27 Sep 2022 14:05:58 +0200 Subject: [PATCH 0380/2268] fix: Remove traces of Helm installation from Dockerfile --- Dockerfile | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1585cdfb3..d312e8c6f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,4 @@ -FROM alpine:3.15 as builder - -RUN apk add --no-cache ca-certificates curl - -ARG ARCH=linux-amd64 - # We must use a glibc based distro due to embedded python not supporting musl libc for aarch64 FROM debian:bullseye-slim -COPY --from=builder /helm /usr/bin COPY kluctl /usr/bin/ ENTRYPOINT ["/usr/bin/kluctl"] From f9d6b8a2f36f5004a525c6ff31bc77c5e152114d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 27 Sep 2022 16:08:36 +0200 Subject: [PATCH 0381/2268] docs: Enhance documentation for `-a` --- cmd/kluctl/args/project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 2639966fb..d2c933721 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -18,7 +18,7 @@ type ProjectFlags struct { } type ArgsFlags struct { - Arg []string `group:"project" short:"a" help:"Template argument in the form name=value"` + Arg []string `group:"project" short:"a" help:"Passes a template argument in the form of name=value. Nested args can be set with the '-a my.nested.arg=value' syntax. Values are interpreted as yaml values, meaning that 'true' and 'false' will lead to boolean values and numbers will be treated as numbers. Use quotes if you want these to be treated as strings."` } type TargetFlags struct { From 545bff744d53bcb0e2da15de57c3ed750a5ead2a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 27 Sep 2022 17:19:51 +0200 Subject: [PATCH 0382/2268] fix: Skip waiting on hooks when --no-wait was specified --- pkg/deployment/utils/hooks_util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/deployment/utils/hooks_util.go b/pkg/deployment/utils/hooks_util.go index 9acbb6178..6ebcd928f 100644 --- a/pkg/deployment/utils/hooks_util.go +++ b/pkg/deployment/utils/hooks_util.go @@ -107,7 +107,7 @@ func (u *HooksUtil) RunHooks(hooks []*hook) { if u.a.HadError(ref) { continue } - if !h.wait { + if !h.wait || u.a.o.NoWait { continue } waitResults[ref] = u.a.WaitReadiness(ref, h.timeout) From 92163822f91267ee78f3c6b5ff209418e8426b8c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 08:52:26 +0200 Subject: [PATCH 0383/2268] fix: Fix diffs pretending that an object could be applied to a non-existing ns We should only simulate creation of objects into new NSs if we know that the NS is actually created. --- pkg/deployment/utils/apply_utils.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 58b32270f..bf1043c91 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -297,11 +297,15 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo x = x.Clone() x.SetK8sName(fmt.Sprintf("%s-%s", ref.Name, utils.RandomString(8))) } else if a.o.DryRun && remoteNamespace == nil && ref.Namespace != "" { - // The namespace does not exist, so let's pretend we deploy it to the default namespace with a dummy name - usesDummyName = true - x = x.Clone() - x.SetK8sName(fmt.Sprintf("%s-%s", ref.Name, utils.RandomString(8))) - x.SetK8sNamespace("default") + nsRef := k8s2.NewObjectRef("", "v1", "Namespace", ref.Namespace, "") + if _, ok := a.appliedObjects[nsRef]; ok { + // The namespace does not really exist, but would have been created if dryRun would be false. + // So let's pretend we deploy it to the default namespace with a dummy name + usesDummyName = true + x = x.Clone() + x.SetK8sName(fmt.Sprintf("%s-%s", ref.Name, utils.RandomString(8))) + x.SetK8sNamespace("default") + } } options := k8s.PatchOptions{ From fbfbdc564e7a5d4f1a868607d7e461e0c0ef1f32 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 08:59:30 +0200 Subject: [PATCH 0384/2268] fix: Add ca-certificates to docker container This is required to make https git repos work. --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index d312e8c6f..b22c03a9a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,7 @@ # We must use a glibc based distro due to embedded python not supporting musl libc for aarch64 FROM debian:bullseye-slim + +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* + COPY kluctl /usr/bin/ ENTRYPOINT ["/usr/bin/kluctl"] From d6732bbe13653a50c70147c344968e27cd701fec Mon Sep 17 00:00:00 2001 From: Marko Petrovic <22802784+gitbluf@users.noreply.github.com> Date: Thu, 29 Sep 2022 09:21:22 +0200 Subject: [PATCH 0385/2268] feat: Add flux-kluctl-controller subcommands (#46) * Add: subcommands: flux-reconcile flux-suspend flux-resume * Add: print out revision * refactor: Add NewClientFactoryFromDefaultConfig helper method This method allows to create a client factory for the default kubeconfig. * refactor: Implement PatchObjectWithJsonPatch * Cherry pick suggested commits * Refactor: reconcile,resume,suspend Update: k8s_cluster.go helper fn * Remove: pkg/flux => helper functions moved to k8s * Add: helper functions: GetObjectSource GetObjectStatus WaitForReady * Refactor: flux-reconcile,suspend,resume * Add: Flux VerifyFlag function * Add: e2e test for flux commands * Update: test yaml resources, README * chore: Run go mod tidy * refactor: Add real "flux" sub-command instead of using flux-xxx commands * fix: Fix crash when adding nested sub-commands * fix: Adjust status output of flux sub-commands to be in the same style as other commands * tests: Fix flux tests to use correct sub-commands * fix: Fix patch type in PatchObject * Refactor: Move Flux unique functions to flux_cmd as helper functions Add Flux test asserts of actions Co-authored-by: Alexander Block --- .gitignore | 1 + cmd/kluctl/args/flux.go | 34 + cmd/kluctl/args/helm_credentials.go | 3 +- cmd/kluctl/commands/cmd_flux.go | 70 + cmd/kluctl/commands/cmd_flux_reconcile.go | 108 + cmd/kluctl/commands/cmd_flux_resume.go | 48 + cmd/kluctl/commands/cmd_flux_suspend.go | 47 + cmd/kluctl/commands/cobra_utils.go | 9 +- cmd/kluctl/commands/root.go | 19 +- e2e/flux_test.go | 65 + e2e/hooks_test.go | 5 +- e2e/project.go | 13 +- e2e/test_resources/README.md | 21 +- e2e/test_resources/flux-source-crd.yaml | 2623 +++++++++++++++++++++ e2e/test_resources/kluctl-crds.yaml | 1592 +++++++++++++ e2e/test_resources/kluctl-deployment.yaml | 30 + e2e/test_resources/resources.go | 3 +- e2e/utils.go | 9 +- pkg/k8s/client_factory.go | 17 + pkg/k8s/k8s_cluster.go | 40 +- 20 files changed, 4719 insertions(+), 38 deletions(-) create mode 100644 cmd/kluctl/args/flux.go create mode 100644 cmd/kluctl/commands/cmd_flux.go create mode 100644 cmd/kluctl/commands/cmd_flux_reconcile.go create mode 100644 cmd/kluctl/commands/cmd_flux_resume.go create mode 100644 cmd/kluctl/commands/cmd_flux_suspend.go create mode 100644 e2e/flux_test.go create mode 100644 e2e/test_resources/flux-source-crd.yaml create mode 100644 e2e/test_resources/kluctl-crds.yaml create mode 100644 e2e/test_resources/kluctl-deployment.yaml diff --git a/.gitignore b/.gitignore index 276ce77b0..8160eea20 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ /reports /vendor dist/ +.vscode \ No newline at end of file diff --git a/cmd/kluctl/args/flux.go b/cmd/kluctl/args/flux.go new file mode 100644 index 000000000..a1307a16a --- /dev/null +++ b/cmd/kluctl/args/flux.go @@ -0,0 +1,34 @@ +package args + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" +) + +type KluctlDeploymentFlags struct { + KluctlDeployment string `group:"flux" short:"k" help:"Name of the KluctlDeployment to interact with"` + Namespace string `group:"flux" short:"n" help:"Namespace where KluctlDeployment is located"` + WithSource bool `group:"flux" help:"--with-source will annotate Source object as well, triggering pulling"` + NoWait bool `group:"flux" help:"Don't wait for objects readiness'"` +} + +var KluctlDeploymentGVK = schema.GroupVersionKind{ + Group: "flux.kluctl.io", + Version: "v1alpha1", + Kind: "KluctlDeployment", +} + +var GitRepositoryGVK = schema.GroupVersionKind{ + Group: "source.toolkit.fluxcd.io", + Version: "v1beta2", + Kind: "GitRepository", +} + +func (cmd *KluctlDeploymentFlags) VerifyFlags() bool { + if cmd.KluctlDeployment == "" { + return false + } + if cmd.Namespace == "" { + cmd.Namespace = "default" + } + return true +} diff --git a/cmd/kluctl/args/helm_credentials.go b/cmd/kluctl/args/helm_credentials.go index 23db78c6d..47cd25feb 100644 --- a/cmd/kluctl/args/helm_credentials.go +++ b/cmd/kluctl/args/helm_credentials.go @@ -1,9 +1,10 @@ package args import ( + "strings" + "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/repo" - "strings" ) type HelmCredentials struct { diff --git a/cmd/kluctl/commands/cmd_flux.go b/cmd/kluctl/commands/cmd_flux.go new file mode 100644 index 000000000..2b68aa514 --- /dev/null +++ b/cmd/kluctl/commands/cmd_flux.go @@ -0,0 +1,70 @@ +package commands + +import ( + "context" + "fmt" + "time" + + kube "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" +) + +type fluxCmd struct { + Reconcile fluxReconcileCmd `cmd:"" help:"Reconcile KluctlDeployment"` + Suspend fluxSuspendCmd `cmd:"" help:"Suspend KluctlDeployment"` + Resume fluxResumeCmd `cmd:"" help:"Resume KluctlDeployment"` +} + +func WaitForReady(k *kube.K8sCluster, ref k8s.ObjectRef) (bool, error) { + o, _, err := k.GetSingleObject(ref) + if o == nil { + return false, err + } + retry := 0 + finalStatus := true + var errorMsg error + for s, err := GetObjectStatus(o); s != true; { + if retry >= 5 || err != nil { + finalStatus = false + errorMsg = err + break + } + retry++ + time.Sleep(8 * time.Second) + } + return finalStatus, errorMsg +} + +func GetObjectStatus(o *uo.UnstructuredObject) (bool, error) { + conditions, ok, err := o.GetNestedField("status", "conditions") + if !ok { + return false, err + } + for _, v := range conditions.([]interface{}) { + if (v.(map[string]interface{})["type"]) == "Ready" { + return true, nil + } + } + + return false, err +} + +func GetObjectSource(o *uo.UnstructuredObject, name string, namespace string, ctx context.Context) (string, string, error) { + status.Trace(ctx, "fetching %s in %s", name, namespace) + + sourceName, ok, err := o.GetNestedField("spec", "sourceRef", "name") + if !ok { + return "", "", err + } + + sourceNamespace, ok, err := o.GetNestedField("spec", "sourceRef", "namespace") + if !ok { + return "", "", err + } + + return fmt.Sprintf("%v", sourceName), + fmt.Sprintf("%v", sourceNamespace), + err +} diff --git a/cmd/kluctl/commands/cmd_flux_reconcile.go b/cmd/kluctl/commands/cmd_flux_reconcile.go new file mode 100644 index 000000000..a6ed024e2 --- /dev/null +++ b/cmd/kluctl/commands/cmd_flux_reconcile.go @@ -0,0 +1,108 @@ +package commands + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" +) + +type fluxReconcileCmd struct { + args.KluctlDeploymentFlags +} + +func (cmd *fluxReconcileCmd) Run() error { + var ( + sourceNamespace string + sourceName string + ) + ns := cmd.KluctlDeploymentFlags.Namespace + kd := cmd.KluctlDeploymentFlags.KluctlDeployment + source := cmd.KluctlDeploymentFlags.WithSource + noWait := cmd.KluctlDeploymentFlags.NoWait + timestamp := time.Now().Format(time.RFC3339) + + cf, err := k8s.NewClientFactoryFromDefaultConfig(nil) + if err != nil { + return err + } + k, err := k8s.NewK8sCluster(context.TODO(), cf, false) + if err != nil { + return err + } + + ref := k8s2.ObjectRef{GVK: args.KluctlDeploymentGVK, Name: kd, Namespace: ns} + patch := []k8s.JsonPatch{{ + Op: "replace", + Path: "/metadata/annotations", + Value: map[string]string{ + "fluxcd.io/reconcileAt": timestamp, + }, + }} + + if source { + if !cmd.VerifyFlags() { + fmt.Println("KluctlDeployment name flag not provided..") + os.Exit(1) + } + o, _, err := k.GetSingleObject(ref) + if o == nil { + return err + } + + sourceName, sourceNamespace, err = GetObjectSource(o, kd, ns, context.TODO()) + if err != nil { + return err + } + ref2 := k8s2.ObjectRef{GVK: args.GitRepositoryGVK, Name: sourceName, Namespace: sourceNamespace} + + s := status.Start(cliCtx, "Annotating Source %s in %s namespace", sourceName, sourceNamespace) + defer s.Failed() + + _, _, err = k.PatchObjectWithJsonPatch(ref2, patch, k8s.PatchOptions{}) + if err != nil { + return err + } + s.Success() + + s = status.Start(cliCtx, "Waiting for Source %s to finish reconciliation", sourceName) + + if !noWait { + ready, err := WaitForReady(k, ref2) + if !ready { + s.FailedWithMessage("Failed while waiting for Source %s to get ready..", sourceName) + return err + } + } + + s.Success() + } + + s := status.Start(cliCtx, "Annotating KluctlDeployment %s in %s namespace", kd, ns) + defer s.Failed() + + _, _, err = k.PatchObjectWithJsonPatch(ref, patch, k8s.PatchOptions{}) + if err != nil { + return err + } + s.Success() + + s = status.Start(cliCtx, "Waiting for KluctlDeployment %s in %s namespace to finish reconciliation", kd, ns) + + if !noWait { + ready, err := WaitForReady(k, ref) + if !ready { + s.FailedWithMessage("Failed while waiting for KluctlDeployment %s to get ready..", kd) + return err + } + } + + s.Success() + + return err +} diff --git a/cmd/kluctl/commands/cmd_flux_resume.go b/cmd/kluctl/commands/cmd_flux_resume.go new file mode 100644 index 000000000..8b7aaafc8 --- /dev/null +++ b/cmd/kluctl/commands/cmd_flux_resume.go @@ -0,0 +1,48 @@ +package commands + +import ( + "context" + + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" +) + +type fluxResumeCmd struct { + args.KluctlDeploymentFlags +} + +// TODO add reconciliation after resume +func (cmd *fluxResumeCmd) Run() error { + ns := cmd.KluctlDeploymentFlags.Namespace + kd := cmd.KluctlDeploymentFlags.KluctlDeployment + + cf, err := k8s.NewClientFactoryFromDefaultConfig(nil) + if err != nil { + return err + } + k, err := k8s.NewK8sCluster(context.TODO(), cf, false) + if err != nil { + return err + } + + ref := k8s2.ObjectRef{GVK: args.KluctlDeploymentGVK, Name: kd, Namespace: ns} + + patch := []k8s.JsonPatch{{ + Op: "replace", + Path: "/spec/suspend", + Value: false, + }} + + s := status.Start(cliCtx, "Resuming KluctlDeployment %s in %s namespace", kd, ns) + defer s.Failed() + + _, _, err = k.PatchObjectWithJsonPatch(ref, patch, k8s.PatchOptions{}) + if err != nil { + return err + } + s.Success() + + return err +} diff --git a/cmd/kluctl/commands/cmd_flux_suspend.go b/cmd/kluctl/commands/cmd_flux_suspend.go new file mode 100644 index 000000000..6e5422fb6 --- /dev/null +++ b/cmd/kluctl/commands/cmd_flux_suspend.go @@ -0,0 +1,47 @@ +package commands + +import ( + "context" + + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" +) + +type fluxSuspendCmd struct { + args.KluctlDeploymentFlags +} + +func (cmd *fluxSuspendCmd) Run() error { + ns := cmd.KluctlDeploymentFlags.Namespace + kd := cmd.KluctlDeploymentFlags.KluctlDeployment + + cf, err := k8s.NewClientFactoryFromDefaultConfig(nil) + if err != nil { + return err + } + k, err := k8s.NewK8sCluster(context.TODO(), cf, false) + if err != nil { + return err + } + + ref := k8s2.ObjectRef{GVK: args.KluctlDeploymentGVK, Name: kd, Namespace: ns} + patch := []k8s.JsonPatch{{ + Op: "replace", + Path: "/spec/suspend", + Value: true, + }} + + s := status.Start(cliCtx, "Suspending KluctlDeployment %s in %s namespace", kd, ns) + defer s.Failed() + + _, _, err = k.PatchObjectWithJsonPatch(ref, patch, k8s.PatchOptions{}) + if err != nil { + return err + } + + s.Success() + + return err +} diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index 3f8bc6316..d495f58f6 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -66,11 +66,10 @@ func (c *rootCommand) buildCobraCmd(parent *commandAndGroups, cmdStruct interfac } runP, ok := cmdStruct.(runProvider) - if !ok { - panic(fmt.Sprintf("%s does not implement runProvider", name)) - } - cg.cmd.RunE = func(cmd *cobra.Command, args []string) error { - return runP.Run() + if ok { + cg.cmd.RunE = func(cmd *cobra.Command, args []string) error { + return runP.Run() + } } err := c.buildCobraSubCommands(cg, cmdStruct) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index d98200898..e85358e86 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -18,6 +18,15 @@ package commands import ( "context" "fmt" + "io" + "log" + "net/http" + "os" + "path/filepath" + "runtime/pprof" + "strings" + "time" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -28,15 +37,7 @@ import ( "github.com/mattn/go-isatty" "github.com/spf13/cobra" "github.com/spf13/viper" - "io" "k8s.io/klog/v2" - "log" - "net/http" - "os" - "path/filepath" - "runtime/pprof" - "strings" - "time" ) const latestReleaseUrl = "https://api.github.com/repos/kluctl/kluctl/releases/latest" @@ -61,6 +62,7 @@ type cli struct { Render renderCmd `cmd:"" help:"Renders all resources and configuration files"` Seal sealCmd `cmd:"" help:"Seal secrets based on target's sealingConfig"` Validate validateCmd `cmd:"" help:"Validates the already deployed deployment"` + Flux fluxCmd `cmd:"" help:"Flux sub-commands"` Version versionCmd `cmd:"" help:"Print kluctl version"` } @@ -71,6 +73,7 @@ var flagGroups = []groupInfo{ {group: "images", title: "Image arguments:", description: "Control fixed images and update behaviour."}, {group: "inclusion", title: "Inclusion/Exclusion arguments:", description: "Control inclusion/exclusion."}, {group: "misc", title: "Misc arguments:", description: "Command specific arguments."}, + {group: "flux", title: "Flux arguments:", description: "EXPERIMENTAL: Subcommands for interaction with flux-kluctl-controller"}, } var cliCtx = context.Background() diff --git a/e2e/flux_test.go b/e2e/flux_test.go new file mode 100644 index 000000000..56bde84b3 --- /dev/null +++ b/e2e/flux_test.go @@ -0,0 +1,65 @@ +package e2e + +import ( + "fmt" + "testing" + + "github.com/kluctl/kluctl/v2/e2e/test_resources" + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/stretchr/testify/assert" +) + +func TestFluxCommands(t *testing.T) { + t.Parallel() + + k := defaultKindCluster1 + + p := &testProject{} + p.init(t, k, "simple") + + defer p.cleanup() + + test_resources.ApplyYaml("flux-source-crd.yaml", k) + test_resources.ApplyYaml("kluctl-crds.yaml", k) + test_resources.ApplyYaml("kluctl-deployment.yaml", k) + + assertResourceExists(t, k, "default", "kluctldeployment/microservices-demo-test") + + p.KluctlMust("flux", "suspend", "--namespace", "default", "--kluctl-deployment", "microservices-demo-test") + suspend := getKluctlSuspendField(t, k) + assert.Equal(t, true, suspend, "Field status.suspend is not false") + + p.KluctlMust("flux", "resume", "--namespace", "default", "--kluctl-deployment", "microservices-demo-test", "--no-wait") + resume := getKluctlSuspendField(t, k) + assert.Equal(t, false, resume, "Field status.suspend is not true") + + p.KluctlMust("flux", "reconcile", "--namespace", "default", "--kluctl-deployment", "microservices-demo-test", "--with-source", "--no-wait") + annotation := getKluctlAnnotations(t, k) + assert.Len(t, annotation, 1, "Annotation not present") +} + +func getKluctlSuspendField(t *testing.T, k *test_utils.KindCluster) interface{} { + o := k.KubectlYamlMust(t, "-n", "default", "get", "kluctldeployment", "microservices-demo-test") + result, ok, err := o.GetNestedField("spec", "suspend") + fmt.Println(result) + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatalf("result not found") + } + return result +} + +func getKluctlAnnotations(t *testing.T, k *test_utils.KindCluster) interface{} { + o := k.KubectlYamlMust(t, "-n", "default", "get", "kluctldeployment", "microservices-demo-test") + result, ok, err := o.GetNestedField("metadata", "annotations") + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatalf("result not found") + } + fmt.Println(result) + return result +} diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 18149608c..d105b9a45 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -3,9 +3,10 @@ package e2e import ( "encoding/base64" "fmt" - "github.com/kluctl/kluctl/v2/internal/test-utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" "testing" + + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) const hookScript = ` diff --git a/e2e/project.go b/e2e/project.go index be0bd2823..2b67e294a 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -2,6 +2,13 @@ package e2e import ( "fmt" + "os" + "os/exec" + "path/filepath" + "reflect" + "strings" + "testing" + "github.com/go-git/go-git/v5" "github.com/imdario/mergo" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" @@ -9,12 +16,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "os" - "os/exec" - "path/filepath" - "reflect" - "strings" - "testing" ) type testProject struct { diff --git a/e2e/test_resources/README.md b/e2e/test_resources/README.md index e3655fad6..a95af0258 100644 --- a/e2e/test_resources/README.md +++ b/e2e/test_resources/README.md @@ -1,9 +1,26 @@ # sealed-secrets.yaml - +```bash helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets helm template sealed-secrets-controller sealed-secrets/sealed-secrets -n kube-system --include-crds --skip-tests > sealed-secrets.yaml +``` # vault.yaml - +```bash helm repo add hashicorp https://helm.releases.hashicorp.com helm template vault hashicorp/vault -n vault -f vault-values.yaml --include-crds --skip-tests > vault.yaml +``` + +# kluctl-crds.yaml +Fetches flux-kluctl-controller crd definitions +```bash +kustomize build https://github.com/kluctl/flux-kluctl-controller/config/crd > kluctl-crds.yaml +``` + +# kluctl-deployment.yaml +This is a simple KluctlDeployment that is not really valid. It points to a non-existing GitRepository. This object is only used to test `kluctl flux` subcommands (which only sets annotations and updates field 'suspend'). + +# flux-source-crd.yaml +Fetches flux-source-controller crd definitions +```bash +kustomize build https://github.com/fluxcd/source-controller/config/crd > flux-source-crd.yaml +``` \ No newline at end of file diff --git a/e2e/test_resources/flux-source-crd.yaml b/e2e/test_resources/flux-source-crd.yaml new file mode 100644 index 000000000..4eda189ac --- /dev/null +++ b/e2e/test_resources/flux-source-crd.yaml @@ -0,0 +1,2623 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: buckets.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: Bucket + listKind: BucketList + plural: buckets + singular: bucket + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.endpoint + name: Endpoint + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: Bucket is the Schema for the buckets API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: BucketSpec defines the desired state of an S3 compatible + bucket + properties: + accessFrom: + description: AccessFrom defines an Access Control List for allowing + cross-namespace references to this object. + properties: + namespaceSelectors: + description: NamespaceSelectors is the list of namespace selectors + to which this ACL applies. Items in this list are evaluated + using a logical OR operation. + items: + description: NamespaceSelector selects the namespaces to which + this ACL applies. An empty map of MatchLabels matches all + namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: MatchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + bucketName: + description: The bucket name. + type: string + endpoint: + description: The bucket endpoint address. + type: string + ignore: + description: Ignore overrides the set of excluded patterns in the + .sourceignore format (which is the same as .gitignore). If not provided, + a default will be used, consult the documentation for your version + to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS S3 HTTP endpoint. + type: boolean + interval: + description: The interval at which to check for bucket updates. + type: string + provider: + default: generic + description: The S3 compatible storage provider name, default ('generic'). + enum: + - generic + - aws + - gcp + type: string + region: + description: The bucket region. + type: string + secretRef: + description: The name of the secret containing authentication credentials + for the Bucket. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + timeout: + default: 60s + description: The timeout for download operations, defaults to 60s. + type: string + required: + - bucketName + - endpoint + - interval + type: object + status: + default: + observedGeneration: -1 + description: BucketStatus defines the observed state of a bucket + properties: + artifact: + description: Artifact represents the output of the last successful + Bucket sync. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of this artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: Revision is a human readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm index timestamp, a Helm chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the Bucket. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: URL is the download link for the artifact output of the + last Bucket sync. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.endpoint + name: Endpoint + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1beta2 + schema: + openAPIV3Schema: + description: Bucket is the Schema for the buckets API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: BucketSpec specifies the required configuration to produce + an Artifact for an object storage bucket. + properties: + accessFrom: + description: 'AccessFrom specifies an Access Control List for allowing + cross-namespace references to this object. NOTE: Not implemented, + provisional as of https://github.com/fluxcd/flux2/pull/2092' + properties: + namespaceSelectors: + description: NamespaceSelectors is the list of namespace selectors + to which this ACL applies. Items in this list are evaluated + using a logical OR operation. + items: + description: NamespaceSelector selects the namespaces to which + this ACL applies. An empty map of MatchLabels matches all + namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: MatchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + bucketName: + description: BucketName is the name of the object storage bucket. + type: string + endpoint: + description: Endpoint is the object storage address the BucketName + is located at. + type: string + ignore: + description: Ignore overrides the set of excluded patterns in the + .sourceignore format (which is the same as .gitignore). If not provided, + a default will be used, consult the documentation for your version + to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP Endpoint. + type: boolean + interval: + description: Interval at which to check the Endpoint for updates. + type: string + provider: + default: generic + description: Provider of the object storage bucket. Defaults to 'generic', + which expects an S3 (API) compatible object storage. + enum: + - generic + - aws + - gcp + - azure + type: string + region: + description: Region of the Endpoint where the BucketName is located + in. + type: string + secretRef: + description: SecretRef specifies the Secret containing authentication + credentials for the Bucket. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: Suspend tells the controller to suspend the reconciliation + of this Bucket. + type: boolean + timeout: + default: 60s + description: Timeout for fetch operations, defaults to 60s. + type: string + required: + - bucketName + - endpoint + - interval + type: object + status: + default: + observedGeneration: -1 + description: BucketStatus records the observed state of a Bucket. + properties: + artifact: + description: Artifact represents the last successful Bucket reconciliation. + properties: + checksum: + description: Checksum is the SHA256 checksum of the Artifact file. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of the Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: Path is the relative file path of the Artifact. It + can be used to locate the file in the root of the Artifact storage + on the local file system of the controller managing the Source. + type: string + revision: + description: Revision is a human-readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: URL is the HTTP address of the Artifact as exposed + by the controller managing the Source. It can be used to retrieve + the Artifact for consumption, e.g. by another controller applying + the Artifact contents. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the Bucket. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation of + the Bucket object. + format: int64 + type: integer + url: + description: URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise BucketStatus.Artifact + data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: gitrepositories.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: GitRepository + listKind: GitRepositoryList + plural: gitrepositories + shortNames: + - gitrepo + singular: gitrepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: GitRepository is the Schema for the gitrepositories API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: GitRepositorySpec defines the desired state of a Git repository. + properties: + accessFrom: + description: AccessFrom defines an Access Control List for allowing + cross-namespace references to this object. + properties: + namespaceSelectors: + description: NamespaceSelectors is the list of namespace selectors + to which this ACL applies. Items in this list are evaluated + using a logical OR operation. + items: + description: NamespaceSelector selects the namespaces to which + this ACL applies. An empty map of MatchLabels matches all + namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: MatchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + gitImplementation: + default: go-git + description: Determines which git client library to use. Defaults + to go-git, valid values are ('go-git', 'libgit2'). + enum: + - go-git + - libgit2 + type: string + ignore: + description: Ignore overrides the set of excluded patterns in the + .sourceignore format (which is the same as .gitignore). If not provided, + a default will be used, consult the documentation for your version + to find out what those are. + type: string + include: + description: Extra git repositories to map into the repository + items: + description: GitRepositoryInclude defines a source with a from and + to path. + properties: + fromPath: + description: The path to copy contents from, defaults to the + root directory. + type: string + repository: + description: Reference to a GitRepository to include. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: The path to copy contents to, defaults to the name + of the source ref. + type: string + required: + - repository + type: object + type: array + interval: + description: The interval at which to check for repository updates. + type: string + recurseSubmodules: + description: When enabled, after the clone is created, initializes + all submodules within, using their default settings. This option + is available only when using the 'go-git' GitImplementation. + type: boolean + ref: + description: The Git reference to checkout and monitor for changes, + defaults to master branch. + properties: + branch: + description: The Git branch to checkout, defaults to master. + type: string + commit: + description: The Git commit SHA to checkout, if specified Tag + filters will be ignored. + type: string + semver: + description: The Git tag semver expression, takes precedence over + Tag. + type: string + tag: + description: The Git tag to checkout, takes precedence over Branch. + type: string + type: object + secretRef: + description: The secret name containing the Git credentials. For HTTPS + repositories the secret must contain username and password fields. + For SSH repositories the secret must contain identity and known_hosts + fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + timeout: + default: 60s + description: The timeout for remote Git operations like cloning, defaults + to 60s. + type: string + url: + description: The repository URL, can be a HTTP/S or SSH address. + pattern: ^(http|https|ssh)://.*$ + type: string + verify: + description: Verify OpenPGP signature for the Git commit HEAD points + to. + properties: + mode: + description: Mode describes what git object should be verified, + currently ('head'). + enum: + - head + type: string + secretRef: + description: The secret name containing the public keys of all + trusted Git authors. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - mode + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: GitRepositoryStatus defines the observed state of a Git repository. + properties: + artifact: + description: Artifact represents the output of the last successful + repository sync. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of this artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: Revision is a human readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm index timestamp, a Helm chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the GitRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + includedArtifacts: + description: IncludedArtifacts represents the included artifacts from + the last successful repository sync. + items: + description: Artifact represents the output of a source synchronisation. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of this artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: Revision is a human readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm index timestamp, a Helm chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + type: array + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: URL is the download link for the artifact output of the + last repository sync. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1beta2 + schema: + openAPIV3Schema: + description: GitRepository is the Schema for the gitrepositories API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: GitRepositorySpec specifies the required configuration to + produce an Artifact for a Git repository. + properties: + accessFrom: + description: 'AccessFrom specifies an Access Control List for allowing + cross-namespace references to this object. NOTE: Not implemented, + provisional as of https://github.com/fluxcd/flux2/pull/2092' + properties: + namespaceSelectors: + description: NamespaceSelectors is the list of namespace selectors + to which this ACL applies. Items in this list are evaluated + using a logical OR operation. + items: + description: NamespaceSelector selects the namespaces to which + this ACL applies. An empty map of MatchLabels matches all + namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: MatchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + gitImplementation: + default: go-git + description: GitImplementation specifies which Git client library + implementation to use. Defaults to 'go-git', valid values are ('go-git', + 'libgit2'). + enum: + - go-git + - libgit2 + type: string + ignore: + description: Ignore overrides the set of excluded patterns in the + .sourceignore format (which is the same as .gitignore). If not provided, + a default will be used, consult the documentation for your version + to find out what those are. + type: string + include: + description: Include specifies a list of GitRepository resources which + Artifacts should be included in the Artifact produced for this GitRepository. + items: + description: GitRepositoryInclude specifies a local reference to + a GitRepository which Artifact (sub-)contents must be included, + and where they should be placed. + properties: + fromPath: + description: FromPath specifies the path to copy contents from, + defaults to the root of the Artifact. + type: string + repository: + description: GitRepositoryRef specifies the GitRepository which + Artifact contents must be included. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: ToPath specifies the path to copy contents to, + defaults to the name of the GitRepositoryRef. + type: string + required: + - repository + type: object + type: array + interval: + description: Interval at which to check the GitRepository for updates. + type: string + recurseSubmodules: + description: RecurseSubmodules enables the initialization of all submodules + within the GitRepository as cloned from the URL, using their default + settings. This option is available only when using the 'go-git' + GitImplementation. + type: boolean + ref: + description: Reference specifies the Git reference to resolve and + monitor for changes, defaults to the 'master' branch. + properties: + branch: + description: "Branch to check out, defaults to 'master' if no + other field is defined. \n When GitRepositorySpec.GitImplementation + is set to 'go-git', a shallow clone of the specified branch + is performed." + type: string + commit: + description: "Commit SHA to check out, takes precedence over all + reference fields. \n When GitRepositorySpec.GitImplementation + is set to 'go-git', this can be combined with Branch to shallow + clone the branch, in which the commit is expected to exist." + type: string + semver: + description: SemVer tag expression to check out, takes precedence + over Tag. + type: string + tag: + description: Tag to check out, takes precedence over Branch. + type: string + type: object + secretRef: + description: SecretRef specifies the Secret containing authentication + credentials for the GitRepository. For HTTPS repositories the Secret + must contain 'username' and 'password' fields. For SSH repositories + the Secret must contain 'identity' and 'known_hosts' fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: Suspend tells the controller to suspend the reconciliation + of this GitRepository. + type: boolean + timeout: + default: 60s + description: Timeout for Git operations like cloning, defaults to + 60s. + type: string + url: + description: URL specifies the Git repository URL, it can be an HTTP/S + or SSH address. + pattern: ^(http|https|ssh)://.*$ + type: string + verify: + description: Verification specifies the configuration to verify the + Git commit signature(s). + properties: + mode: + description: Mode specifies what Git object should be verified, + currently ('head'). + enum: + - head + type: string + secretRef: + description: SecretRef specifies the Secret containing the public + keys of trusted Git authors. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - mode + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: GitRepositoryStatus records the observed state of a Git repository. + properties: + artifact: + description: Artifact represents the last successful GitRepository + reconciliation. + properties: + checksum: + description: Checksum is the SHA256 checksum of the Artifact file. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of the Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: Path is the relative file path of the Artifact. It + can be used to locate the file in the root of the Artifact storage + on the local file system of the controller managing the Source. + type: string + revision: + description: Revision is a human-readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: URL is the HTTP address of the Artifact as exposed + by the controller managing the Source. It can be used to retrieve + the Artifact for consumption, e.g. by another controller applying + the Artifact contents. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the GitRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + contentConfigChecksum: + description: 'ContentConfigChecksum is a checksum of all the configurations + related to the content of the source artifact: - .spec.ignore - + .spec.recurseSubmodules - .spec.included and the checksum of the + included artifacts observed in .status.observedGeneration version + of the object. This can be used to determine if the content of the + included repository has changed. It has the format of `:`, + for example: `sha256:`.' + type: string + includedArtifacts: + description: IncludedArtifacts contains a list of the last successfully + included Artifacts as instructed by GitRepositorySpec.Include. + items: + description: Artifact represents the output of a Source reconciliation. + properties: + checksum: + description: Checksum is the SHA256 checksum of the Artifact + file. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of the Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI + annotations. + type: object + path: + description: Path is the relative file path of the Artifact. + It can be used to locate the file in the root of the Artifact + storage on the local file system of the controller managing + the Source. + type: string + revision: + description: Revision is a human-readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: URL is the HTTP address of the Artifact as exposed + by the controller managing the Source. It can be used to retrieve + the Artifact for consumption, e.g. by another controller applying + the Artifact contents. + type: string + required: + - path + - url + type: object + type: array + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation of + the GitRepository object. + format: int64 + type: integer + url: + description: URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise GitRepositoryStatus.Artifact + data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: helmcharts.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: HelmChart + listKind: HelmChartList + plural: helmcharts + shortNames: + - hc + singular: helmchart + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.chart + name: Chart + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.sourceRef.kind + name: Source Kind + type: string + - jsonPath: .spec.sourceRef.name + name: Source Name + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: HelmChart is the Schema for the helmcharts API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: HelmChartSpec defines the desired state of a Helm chart. + properties: + accessFrom: + description: AccessFrom defines an Access Control List for allowing + cross-namespace references to this object. + properties: + namespaceSelectors: + description: NamespaceSelectors is the list of namespace selectors + to which this ACL applies. Items in this list are evaluated + using a logical OR operation. + items: + description: NamespaceSelector selects the namespaces to which + this ACL applies. An empty map of MatchLabels matches all + namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: MatchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + chart: + description: The name or path the Helm chart is available at in the + SourceRef. + type: string + interval: + description: The interval at which to check the Source for updates. + type: string + reconcileStrategy: + default: ChartVersion + description: Determines what enables the creation of a new artifact. + Valid values are ('ChartVersion', 'Revision'). See the documentation + of the values for an explanation on their behavior. Defaults to + ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: The reference to the Source the chart is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent, valid values are ('HelmRepository', + 'GitRepository', 'Bucket'). + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + required: + - kind + - name + type: object + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + valuesFile: + description: Alternative values file to use as the default chart values, + expected to be a relative path in the SourceRef. Deprecated in favor + of ValuesFiles, for backwards compatibility the file defined here + is merged before the ValuesFiles items. Ignored when omitted. + type: string + valuesFiles: + description: Alternative list of values files to use as the chart + values (values.yaml is not included by default), expected to be + a relative path in the SourceRef. Values files are merged in the + order of this list with the last file overriding the first. Ignored + when omitted. + items: + type: string + type: array + version: + default: '*' + description: The chart version semver expression, ignored for charts + from GitRepository and Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: HelmChartStatus defines the observed state of the HelmChart. + properties: + artifact: + description: Artifact represents the output of the last successful + chart sync. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of this artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: Revision is a human readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm index timestamp, a Helm chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmChart. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: URL is the download link for the last chart pulled. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.chart + name: Chart + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.sourceRef.kind + name: Source Kind + type: string + - jsonPath: .spec.sourceRef.name + name: Source Name + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1beta2 + schema: + openAPIV3Schema: + description: HelmChart is the Schema for the helmcharts API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: HelmChartSpec specifies the desired state of a Helm chart. + properties: + accessFrom: + description: 'AccessFrom specifies an Access Control List for allowing + cross-namespace references to this object. NOTE: Not implemented, + provisional as of https://github.com/fluxcd/flux2/pull/2092' + properties: + namespaceSelectors: + description: NamespaceSelectors is the list of namespace selectors + to which this ACL applies. Items in this list are evaluated + using a logical OR operation. + items: + description: NamespaceSelector selects the namespaces to which + this ACL applies. An empty map of MatchLabels matches all + namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: MatchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + chart: + description: Chart is the name or path the Helm chart is available + at in the SourceRef. + type: string + interval: + description: Interval is the interval at which to check the Source + for updates. + type: string + reconcileStrategy: + default: ChartVersion + description: ReconcileStrategy determines what enables the creation + of a new artifact. Valid values are ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their + behavior. Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: SourceRef is the reference to the Source the chart is + available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent, valid values are ('HelmRepository', + 'GitRepository', 'Bucket'). + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + required: + - kind + - name + type: object + suspend: + description: Suspend tells the controller to suspend the reconciliation + of this source. + type: boolean + valuesFile: + description: ValuesFile is an alternative values file to use as the + default chart values, expected to be a relative path in the SourceRef. + Deprecated in favor of ValuesFiles, for backwards compatibility + the file specified here is merged before the ValuesFiles items. + Ignored when omitted. + type: string + valuesFiles: + description: ValuesFiles is an alternative list of values files to + use as the chart values (values.yaml is not included by default), + expected to be a relative path in the SourceRef. Values files are + merged in the order of this list with the last file overriding the + first. Ignored when omitted. + items: + type: string + type: array + version: + default: '*' + description: Version is the chart version semver expression, ignored + for charts from GitRepository and Bucket sources. Defaults to latest + when omitted. + type: string + required: + - chart + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: HelmChartStatus records the observed state of the HelmChart. + properties: + artifact: + description: Artifact represents the output of the last successful + reconciliation. + properties: + checksum: + description: Checksum is the SHA256 checksum of the Artifact file. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of the Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: Path is the relative file path of the Artifact. It + can be used to locate the file in the root of the Artifact storage + on the local file system of the controller managing the Source. + type: string + revision: + description: Revision is a human-readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: URL is the HTTP address of the Artifact as exposed + by the controller managing the Source. It can be used to retrieve + the Artifact for consumption, e.g. by another controller applying + the Artifact contents. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmChart. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedChartName: + description: ObservedChartName is the last observed chart name as + specified by the resolved chart reference. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation of + the HelmChart object. + format: int64 + type: integer + observedSourceArtifactRevision: + description: ObservedSourceArtifactRevision is the last observed Artifact.Revision + of the HelmChartSpec.SourceRef. + type: string + url: + description: URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise BucketStatus.Artifact + data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: helmrepositories.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: HelmRepository + listKind: HelmRepositoryList + plural: helmrepositories + shortNames: + - helmrepo + singular: helmrepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: HelmRepository is the Schema for the helmrepositories API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: HelmRepositorySpec defines the reference to a Helm repository. + properties: + accessFrom: + description: AccessFrom defines an Access Control List for allowing + cross-namespace references to this object. + properties: + namespaceSelectors: + description: NamespaceSelectors is the list of namespace selectors + to which this ACL applies. Items in this list are evaluated + using a logical OR operation. + items: + description: NamespaceSelector selects the namespaces to which + this ACL applies. An empty map of MatchLabels matches all + namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: MatchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + interval: + description: The interval at which to check the upstream for updates. + type: string + passCredentials: + description: PassCredentials allows the credentials from the SecretRef + to be passed on to a host that does not match the host as defined + in URL. This may be required if the host of the advertised chart + URLs in the index differ from the defined URL. Enabling this should + be done with caution, as it can potentially result in credentials + getting stolen in a MITM-attack. + type: boolean + secretRef: + description: The name of the secret containing authentication credentials + for the Helm repository. For HTTP/S basic auth the secret must contain + username and password fields. For TLS the secret must contain a + certFile and keyFile, and/or caCert fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + timeout: + default: 60s + description: The timeout of index downloading, defaults to 60s. + type: string + url: + description: The Helm repository URL, a valid URL contains at least + a protocol and host. + type: string + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: HelmRepositoryStatus defines the observed state of the HelmRepository. + properties: + artifact: + description: Artifact represents the output of the last successful + repository sync. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of this artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: Revision is a human readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm index timestamp, a Helm chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: URL is the download link for the last index fetched. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1beta2 + schema: + openAPIV3Schema: + description: HelmRepository is the Schema for the helmrepositories API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: HelmRepositorySpec specifies the required configuration to + produce an Artifact for a Helm repository index YAML. + properties: + accessFrom: + description: 'AccessFrom specifies an Access Control List for allowing + cross-namespace references to this object. NOTE: Not implemented, + provisional as of https://github.com/fluxcd/flux2/pull/2092' + properties: + namespaceSelectors: + description: NamespaceSelectors is the list of namespace selectors + to which this ACL applies. Items in this list are evaluated + using a logical OR operation. + items: + description: NamespaceSelector selects the namespaces to which + this ACL applies. An empty map of MatchLabels matches all + namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: MatchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + interval: + description: Interval at which to check the URL for updates. + type: string + passCredentials: + description: PassCredentials allows the credentials from the SecretRef + to be passed on to a host that does not match the host as defined + in URL. This may be required if the host of the advertised chart + URLs in the index differ from the defined URL. Enabling this should + be done with caution, as it can potentially result in credentials + getting stolen in a MITM-attack. + type: boolean + provider: + default: generic + description: Provider used for authentication, can be 'aws', 'azure', + 'gcp' or 'generic'. This field is optional, and only taken into + account if the .spec.type field is set to 'oci'. When not specified, + defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + secretRef: + description: SecretRef specifies the Secret containing authentication + credentials for the HelmRepository. For HTTP/S basic auth the secret + must contain 'username' and 'password' fields. For TLS the secret + must contain a 'certFile' and 'keyFile', and/or 'caCert' fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: Suspend tells the controller to suspend the reconciliation + of this HelmRepository. + type: boolean + timeout: + default: 60s + description: Timeout is used for the index fetch operation for an + HTTPS helm repository, and for remote OCI Repository operations + like pulling for an OCI helm repository. Its default value is 60s. + type: string + type: + description: Type of the HelmRepository. When this field is set to "oci", + the URL field value must be prefixed with "oci://". + enum: + - default + - oci + type: string + url: + description: URL of the Helm repository, a valid URL contains at least + a protocol and host. + type: string + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: HelmRepositoryStatus records the observed state of the HelmRepository. + properties: + artifact: + description: Artifact represents the last successful HelmRepository + reconciliation. + properties: + checksum: + description: Checksum is the SHA256 checksum of the Artifact file. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of the Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: Path is the relative file path of the Artifact. It + can be used to locate the file in the root of the Artifact storage + on the local file system of the controller managing the Source. + type: string + revision: + description: Revision is a human-readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: URL is the HTTP address of the Artifact as exposed + by the controller managing the Source. It can be used to retrieve + the Artifact for consumption, e.g. by another controller applying + the Artifact contents. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation of + the HelmRepository object. + format: int64 + type: integer + url: + description: URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise HelmRepositoryStatus.Artifact + data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: ocirepositories.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: OCIRepository + listKind: OCIRepositoryList + plural: ocirepositories + shortNames: + - ocirepo + singular: ocirepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta2 + schema: + openAPIV3Schema: + description: OCIRepository is the Schema for the ocirepositories API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: OCIRepositorySpec defines the desired state of OCIRepository + properties: + certSecretRef: + description: "CertSecretRef can be given the name of a secret containing + either or both of \n - a PEM-encoded client certificate (`certFile`) + and private key (`keyFile`); - a PEM-encoded CA certificate (`caFile`) + \n and whichever are supplied, will be used for connecting to the + \ registry. The client cert and key are useful if you are authenticating + with a certificate; the CA cert is useful if you are using a self-signed + server certificate." + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + ignore: + description: Ignore overrides the set of excluded patterns in the + .sourceignore format (which is the same as .gitignore). If not provided, + a default will be used, consult the documentation for your version + to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP container + registry. + type: boolean + interval: + description: The interval at which to check for image updates. + type: string + layerSelector: + description: LayerSelector specifies which layer should be extracted + from the OCI artifact. When not specified, the first layer found + in the artifact is selected. + properties: + mediaType: + description: MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The first layer + matching this type is selected. + type: string + type: object + provider: + default: generic + description: The provider used for authentication, can be 'aws', 'azure', + 'gcp' or 'generic'. When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + ref: + description: The OCI reference to pull and monitor for changes, defaults + to the latest tag. + properties: + digest: + description: Digest is the image digest to pull, takes precedence + over SemVer. The value should be in the format 'sha256:'. + type: string + semver: + description: SemVer is the range of tags to pull selecting the + latest within the range, takes precedence over Tag. + type: string + tag: + description: Tag is the image tag to pull, defaults to latest. + type: string + type: object + secretRef: + description: SecretRef contains the secret name containing the registry + login credentials to resolve image metadata. The secret must be + of type kubernetes.io/dockerconfigjson. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: 'ServiceAccountName is the name of the Kubernetes ServiceAccount + used to authenticate the image pull if the service account has attached + pull secrets. For more information: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account' + type: string + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + timeout: + default: 60s + description: The timeout for remote OCI Repository operations like + pulling, defaults to 60s. + type: string + url: + description: URL is a reference to an OCI artifact repository hosted + on a remote container registry. + pattern: ^oci://.*$ + type: string + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: OCIRepositoryStatus defines the observed state of OCIRepository + properties: + artifact: + description: Artifact represents the output of the last successful + OCI Repository sync. + properties: + checksum: + description: Checksum is the SHA256 checksum of the Artifact file. + type: string + lastUpdateTime: + description: LastUpdateTime is the timestamp corresponding to + the last update of the Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: Path is the relative file path of the Artifact. It + can be used to locate the file in the root of the Artifact storage + on the local file system of the controller managing the Source. + type: string + revision: + description: Revision is a human-readable identifier traceable + in the origin source system. It can be a Git commit SHA, Git + tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: URL is the HTTP address of the Artifact as exposed + by the controller managing the Source. It can be used to retrieve + the Artifact for consumption, e.g. by another controller applying + the Artifact contents. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the OCIRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: URL is the download link for the artifact output of the + last OCI Repository sync. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/e2e/test_resources/kluctl-crds.yaml b/e2e/test_resources/kluctl-crds.yaml new file mode 100644 index 000000000..d16c0d9ea --- /dev/null +++ b/e2e/test_resources/kluctl-crds.yaml @@ -0,0 +1,1592 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: kluctldeployments.flux.kluctl.io +spec: + group: flux.kluctl.io + names: + kind: KluctlDeployment + listKind: KluctlDeploymentList + plural: kluctldeployments + singular: kluctldeployment + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.lastDeployResult.time + name: Deployed + type: date + - jsonPath: .status.lastPruneResult.time + name: Pruned + type: date + - jsonPath: .status.lastValidateResult.time + name: Validated + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: KluctlDeployment is the Schema for the kluctldeployments API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: KluctlDeploymentSpec defines the desired state of KluctlDeployment + properties: + abortOnError: + default: false + description: ForceReplaceOnError instructs kluctl to abort deployments + immediately when something fails. Equivalent to using '--abort-on-error' + when calling kluctl. + type: boolean + args: + additionalProperties: + type: string + description: Args specifies dynamic target args. Only arguments defined + by 'dynamicArgs' of the target are allowed. + type: object + dependsOn: + description: DependsOn may contain a meta.NamespacedObjectReference + slice with references to resources that must be ready before this + kluctl project can be deployed. + items: + description: NamespacedObjectReference contains enough information + to locate the referenced Kubernetes resource object in any namespace. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - name + type: object + type: array + deployInterval: + description: DeployInterval specifies the interval at which to deploy + the KluctlDeployment. This is independent of the 'Interval' value, + which only causes deployments if some deployment objects have changed. + type: string + deployMode: + default: full-deploy + description: DeployMode specifies what deploy mode should be used + enum: + - full-deploy + - poke-images + type: string + dryRun: + default: false + description: DryRun instructs kluctl to run everything in dry-run + mode. Equivalent to using '--dry-run' when calling kluctl. + type: boolean + excludeDeploymentDirs: + description: ExcludeDeploymentDirs instructs kluctl to exclude deployments + with the given dir. Equivalent to using '--exclude-deployment-dir' + when calling kluctl. + items: + type: string + type: array + excludeTags: + description: ExcludeTags instructs kluctl to exclude deployments with + given tags. Equivalent to using '--exclude-tag' when calling kluctl. + items: + type: string + type: array + forceApply: + default: false + description: ForceApply instructs kluctl to force-apply in case of + SSA conflicts. Equivalent to using '--force-apply' when calling + kluctl. + type: boolean + forceReplaceOnError: + default: false + description: ForceReplaceOnError instructs kluctl to force-replace + resources in case a normal replace fails. Equivalent to using '--force-replace-on-error' + when calling kluctl. + type: boolean + images: + description: Images contains a list of fixed image overrides. Equivalent + to using '--fixed-images-file' when calling kluctl. + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + description: ObjectRef contains the information necessary to + locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + registryImage: + type: string + resultImage: + type: string + versionFilter: + type: string + required: + - image + - resultImage + type: object + type: array + includeDeploymentDirs: + description: IncludeDeploymentDirs instructs kluctl to only include + deployments with the given dir. Equivalent to using '--include-deployment-dir' + when calling kluctl. + items: + type: string + type: array + includeTags: + description: IncludeTags instructs kluctl to only include deployments + with given tags. Equivalent to using '--include-tag' when calling + kluctl. + items: + type: string + type: array + interval: + description: The interval at which to reconcile the KluctlDeployment. + type: string + kubeConfig: + description: The KubeConfig for deploying to the target cluster. Specifies + the kubeconfig to be used when invoking kluctl. Contexts in this + kubeconfig must match the context found in the kluctl target. As + an alternative, RenameContexts can be used to fix non-matching context + names. + properties: + secretRef: + description: SecretRef holds the name of a secret that contains + a key with the kubeconfig file as the value. If no key is set, + the key will default to 'value'. The secret must be in the same + namespace as the Kustomization. It is recommended that the kubeconfig + is self-contained, and the secret is regularly updated if credentials + such as a cloud-access-token expire. Cloud specific `cmd-path` + auth helpers will not function without adding binaries and credentials + to the Pod that is responsible for reconciling the KluctlDeployment. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific + default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + type: object + noWait: + default: false + description: NoWait instructs kluctl to not wait for any resources + to become ready, including hooks. Equivalent to using '--no-wait' + when calling kluctl. + type: boolean + path: + description: Path to the directory containing the .kluctl.yaml file, + or the Defaults to 'None', which translates to the root path of + the SourceRef. + type: string + prune: + default: false + description: Prune enables pruning after deploying. + type: boolean + registrySecrets: + description: RegistrySecrets is a list of secret references to be + used for image registry authentication. The secrets must either + have ".dockerconfigjson" included or "registry", "username" and + "password". Additionally, "caFile" and "insecure" can be specified. + items: + description: LocalObjectReference contains enough information to + locate the referenced Kubernetes resource object. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: array + renameContexts: + description: RenameContexts specifies a list of context rename operations. + This is useful when the kluctl target's context does not match with + the contexts found in the kubeconfig while deploying. This is the + case when using kubeconfigs generated from service accounts, in + which case the context name is always "default". + items: + description: RenameContext specifies a single rename of a context + properties: + newContext: + description: NewContext is the new name of the context + type: string + oldContext: + description: OldContext is the name of the context to be renamed + type: string + required: + - newContext + - oldContext + type: object + type: array + replaceOnError: + default: false + description: ReplaceOnError instructs kluctl to replace resources + on error. Equivalent to using '--replace-on-error' when calling + kluctl. + type: boolean + retryInterval: + description: The interval at which to retry a previously failed reconciliation. + When not specified, the controller uses the KluctlDeploymentSpec.Interval + value to retry failures. + type: string + serviceAccountName: + description: The name of the Kubernetes service account to use while + deploying. If not specified, the default service account is used. + type: string + sourceRef: + description: Reference of the source where the kluctl project is. + The authentication secrets from the source are also used to authenticate + dependent git repositories which are cloned while deploying the + kluctl project. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, defaults to the namespace + of the Kubernetes resource object that contains the reference. + type: string + required: + - kind + - name + type: object + suspend: + description: This flag tells the controller to suspend subsequent + kluctl executions, it does not apply to already started executions. + Defaults to false. + type: boolean + target: + description: Target specifies the kluctl target to deploy + maxLength: 63 + minLength: 1 + type: string + timeout: + description: Timeout for all operations. Defaults to 'Interval' duration. + type: string + updateImages: + default: false + description: UpdateImages instructs kluctl to update dynamic images. + Equivalent to using '-u' when calling kluctl. + type: boolean + validateInterval: + default: 5m + description: ValidateInterval specifies the interval at which to validate + the KluctlDeployment. Validation is performed the same way as with + 'kluctl validate -t '. Defaults to 1m. + type: string + required: + - interval + - sourceRef + - target + type: object + status: + description: KluctlDeploymentStatus defines the observed state of KluctlDeployment + properties: + commonLabels: + additionalProperties: + type: string + description: CommonLabels are the commonLabels found in the deployment + project when the last deployment was done. This is used to perform + cleanup/deletion in case the KluctlDeployment project is deleted + type: object + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: + \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // +listMapKey=type + \ Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastAttemptedRevision: + description: LastAttemptedRevision is the revision of the last reconciliation + attempt. + type: string + lastDeployResult: + description: LastDeployResult is the result of the last deploy command + properties: + error: + type: string + objectsHash: + description: ObjectsHash is the hash of all rendered objects + type: string + result: + properties: + changedObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + deletedObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + errors: + items: + properties: + error: + type: string + ref: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + required: + - error + - ref + type: object + type: array + hookObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + newObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + orphanObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + seenImages: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + registryImage: + type: string + resultImage: + type: string + versionFilter: + type: string + required: + - image + - resultImage + type: object + type: array + warnings: + items: + properties: + error: + type: string + ref: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + required: + - error + - ref + type: object + type: array + type: object + revision: + description: Revision is the source revision. Please note that + kluctl projects have dependent git repositories which are not + considered in the source revision + type: string + targetName: + description: TargetName is the name of the target + type: string + time: + description: AttemptedAt is the time when the attempt was performed + format: date-time + type: string + required: + - targetName + - time + type: object + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + lastPruneResult: + description: LastDeployResult is the result of the last prune command + properties: + error: + type: string + objectsHash: + description: ObjectsHash is the hash of all rendered objects + type: string + result: + properties: + changedObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + deletedObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + errors: + items: + properties: + error: + type: string + ref: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + required: + - error + - ref + type: object + type: array + hookObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + newObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + orphanObjects: + items: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + type: array + seenImages: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + registryImage: + type: string + resultImage: + type: string + versionFilter: + type: string + required: + - image + - resultImage + type: object + type: array + warnings: + items: + properties: + error: + type: string + ref: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + required: + - error + - ref + type: object + type: array + type: object + revision: + description: Revision is the source revision. Please note that + kluctl projects have dependent git repositories which are not + considered in the source revision + type: string + targetName: + description: TargetName is the name of the target + type: string + time: + description: AttemptedAt is the time when the attempt was performed + format: date-time + type: string + required: + - targetName + - time + type: object + lastValidateResult: + description: LastValidateResult is the result of the last validate + command + properties: + error: + type: string + objectsHash: + description: ObjectsHash is the hash of all rendered objects + type: string + result: + properties: + errors: + items: + properties: + error: + type: string + ref: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + required: + - error + - ref + type: object + type: array + ready: + type: boolean + results: + items: + properties: + annotation: + type: string + message: + type: string + ref: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + required: + - annotation + - message + - ref + type: object + type: array + warnings: + items: + properties: + error: + type: string + ref: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + required: + - error + - ref + type: object + type: array + required: + - ready + type: object + revision: + description: Revision is the source revision. Please note that + kluctl projects have dependent git repositories which are not + considered in the source revision + type: string + targetName: + description: TargetName is the name of the target + type: string + time: + description: AttemptedAt is the time when the attempt was performed + format: date-time + type: string + required: + - error + - targetName + - time + type: object + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: kluctlmultideployments.flux.kluctl.io +spec: + group: flux.kluctl.io + names: + kind: KluctlMultiDeployment + listKind: KluctlMultiDeploymentList + plural: kluctlmultideployments + singular: kluctlmultideployment + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.targetPattern + name: Pattern + type: string + - jsonPath: .status.targetCount + name: Targets + type: integer + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: KluctlMultiDeployment is the Schema for the kluctlmultideployments + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: KluctlMultiDeploymentSpec defines the desired state of KluctlMultiDeployment + properties: + dependsOn: + description: DependsOn may contain a meta.NamespacedObjectReference + slice with references to resources that must be ready before this + kluctl project can be deployed. + items: + description: NamespacedObjectReference contains enough information + to locate the referenced Kubernetes resource object in any namespace. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - name + type: object + type: array + interval: + description: The interval at which to reconcile the KluctlDeployment. + type: string + path: + description: Path to the directory containing the .kluctl.yaml file, + or the Defaults to 'None', which translates to the root path of + the SourceRef. + type: string + retryInterval: + description: The interval at which to retry a previously failed reconciliation. + When not specified, the controller uses the KluctlDeploymentSpec.Interval + value to retry failures. + type: string + sourceRef: + description: Reference of the source where the kluctl project is. + The authentication secrets from the source are also used to authenticate + dependent git repositories which are cloned while deploying the + kluctl project. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, defaults to the namespace + of the Kubernetes resource object that contains the reference. + type: string + required: + - kind + - name + type: object + suspend: + description: This flag tells the controller to suspend subsequent + kluctl executions, it does not apply to already started executions. + Defaults to false. + type: boolean + targetPattern: + description: TargetPattern is the regex pattern used to match targets + type: string + template: + description: Template is the object template used to create KluctlDeploymet + objects + properties: + spec: + description: Spec is the KluctlDeployment spec to be used as a + template + properties: + abortOnError: + default: false + description: ForceReplaceOnError instructs kluctl to abort + deployments immediately when something fails. Equivalent + to using '--abort-on-error' when calling kluctl. + type: boolean + args: + additionalProperties: + type: string + description: Args specifies dynamic target args. Only arguments + defined by 'dynamicArgs' of the target are allowed. + type: object + deployInterval: + description: DeployInterval specifies the interval at which + to deploy the KluctlDeployment. This is independent of the + 'Interval' value, which only causes deployments if some + deployment objects have changed. + type: string + deployMode: + default: full-deploy + description: DeployMode specifies what deploy mode should + be used + enum: + - full-deploy + - poke-images + type: string + dryRun: + default: false + description: DryRun instructs kluctl to run everything in + dry-run mode. Equivalent to using '--dry-run' when calling + kluctl. + type: boolean + excludeDeploymentDirs: + description: ExcludeDeploymentDirs instructs kluctl to exclude + deployments with the given dir. Equivalent to using '--exclude-deployment-dir' + when calling kluctl. + items: + type: string + type: array + excludeTags: + description: ExcludeTags instructs kluctl to exclude deployments + with given tags. Equivalent to using '--exclude-tag' when + calling kluctl. + items: + type: string + type: array + forceApply: + default: false + description: ForceApply instructs kluctl to force-apply in + case of SSA conflicts. Equivalent to using '--force-apply' + when calling kluctl. + type: boolean + forceReplaceOnError: + default: false + description: ForceReplaceOnError instructs kluctl to force-replace + resources in case a normal replace fails. Equivalent to + using '--force-replace-on-error' when calling kluctl. + type: boolean + images: + description: Images contains a list of fixed image overrides. + Equivalent to using '--fixed-images-file' when calling kluctl. + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + description: ObjectRef contains the information necessary + to locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + registryImage: + type: string + resultImage: + type: string + versionFilter: + type: string + required: + - image + - resultImage + type: object + type: array + includeDeploymentDirs: + description: IncludeDeploymentDirs instructs kluctl to only + include deployments with the given dir. Equivalent to using + '--include-deployment-dir' when calling kluctl. + items: + type: string + type: array + includeTags: + description: IncludeTags instructs kluctl to only include + deployments with given tags. Equivalent to using '--include-tag' + when calling kluctl. + items: + type: string + type: array + interval: + description: The interval at which to reconcile the KluctlDeployment. + type: string + kubeConfig: + description: The KubeConfig for deploying to the target cluster. + Specifies the kubeconfig to be used when invoking kluctl. + Contexts in this kubeconfig must match the context found + in the kluctl target. As an alternative, RenameContexts + can be used to fix non-matching context names. + properties: + secretRef: + description: SecretRef holds the name of a secret that + contains a key with the kubeconfig file as the value. + If no key is set, the key will default to 'value'. The + secret must be in the same namespace as the Kustomization. + It is recommended that the kubeconfig is self-contained, + and the secret is regularly updated if credentials such + as a cloud-access-token expire. Cloud specific `cmd-path` + auth helpers will not function without adding binaries + and credentials to the Pod that is responsible for reconciling + the KluctlDeployment. + properties: + key: + description: Key in the Secret, when not specified + an implementation-specific default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + type: object + noWait: + default: false + description: NoWait instructs kluctl to not wait for any resources + to become ready, including hooks. Equivalent to using '--no-wait' + when calling kluctl. + type: boolean + prune: + default: false + description: Prune enables pruning after deploying. + type: boolean + registrySecrets: + description: RegistrySecrets is a list of secret references + to be used for image registry authentication. The secrets + must either have ".dockerconfigjson" included or "registry", + "username" and "password". Additionally, "caFile" and "insecure" + can be specified. + items: + description: LocalObjectReference contains enough information + to locate the referenced Kubernetes resource object. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: array + renameContexts: + description: RenameContexts specifies a list of context rename + operations. This is useful when the kluctl target's context + does not match with the contexts found in the kubeconfig + while deploying. This is the case when using kubeconfigs + generated from service accounts, in which case the context + name is always "default". + items: + description: RenameContext specifies a single rename of + a context + properties: + newContext: + description: NewContext is the new name of the context + type: string + oldContext: + description: OldContext is the name of the context to + be renamed + type: string + required: + - newContext + - oldContext + type: object + type: array + replaceOnError: + default: false + description: ReplaceOnError instructs kluctl to replace resources + on error. Equivalent to using '--replace-on-error' when + calling kluctl. + type: boolean + retryInterval: + description: The interval at which to retry a previously failed + reconciliation. When not specified, the controller uses + the KluctlDeploymentSpec.Interval value to retry failures. + type: string + serviceAccountName: + description: The name of the Kubernetes service account to + use while deploying. If not specified, the default service + account is used. + type: string + timeout: + description: Timeout for all operations. Defaults to 'Interval' + duration. + type: string + updateImages: + default: false + description: UpdateImages instructs kluctl to update dynamic + images. Equivalent to using '-u' when calling kluctl. + type: boolean + validateInterval: + default: 5m + description: ValidateInterval specifies the interval at which + to validate the KluctlDeployment. Validation is performed + the same way as with 'kluctl validate -t '. Defaults + to 1m. + type: string + required: + - interval + type: object + required: + - spec + type: object + timeout: + description: Timeout for all operations. Defaults to 'Interval' duration. + type: string + required: + - interval + - sourceRef + - targetPattern + - template + type: object + status: + description: KluctlMultiDeploymentStatus defines the observed state of + KluctlMultiDeployment + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: + \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // +listMapKey=type + \ Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastAttemptedRevision: + description: LastAttemptedRevision is the revision of the last reconciliation + attempt. + type: string + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + targetCount: + description: TargetCount is the number of targets detected + type: integer + targets: + description: Targets is the list of detected targets + items: + description: KluctlMultiDeploymentTargetStatus describes the status + of a single target + properties: + kluctlDeploymentName: + description: KluctlDeploymentName is the name of the generated + KluctlDeployment object + type: string + name: + description: Name is the name of the detected target + type: string + required: + - kluctlDeploymentName + - name + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/e2e/test_resources/kluctl-deployment.yaml b/e2e/test_resources/kluctl-deployment.yaml new file mode 100644 index 000000000..e10aeede8 --- /dev/null +++ b/e2e/test_resources/kluctl-deployment.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: flux.kluctl.io/v1alpha1 +kind: KluctlDeployment +metadata: + name: microservices-demo-test + namespace: default +spec: + interval: 10m + path: ./simple + prune: true + renameContexts: + - newContext: kind-kind + oldContext: default + sourceRef: + kind: GitRepository + name: microservices-demo + namespace: default + target: simple + timeout: 2m +--- +apiVersion: source.toolkit.fluxcd.io/v1beta1 +kind: GitRepository +metadata: + name: microservices-demo + namespace: default +spec: + interval: 5m + ref: + branch: main + url: https://github.com/gitbluf/kluctl-examples.git diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go index 7644d1f09..24d724f66 100644 --- a/e2e/test_resources/resources.go +++ b/e2e/test_resources/resources.go @@ -2,9 +2,10 @@ package test_resources import ( "embed" + "os" + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils" - "os" ) //go:embed *.yaml diff --git a/e2e/utils.go b/e2e/utils.go index 274f5946c..9798a9de1 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -4,16 +4,17 @@ import ( "bufio" "bytes" "fmt" - "github.com/kluctl/kluctl/v2/internal/test-utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/validation" - log "github.com/sirupsen/logrus" "io" "os/exec" "reflect" "strings" "testing" "time" + + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/validation" + log "github.com/sirupsen/logrus" ) func recreateNamespace(t *testing.T, k *test_utils.KindCluster, namespace string) { diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go index 8480a2540..1c9e451df 100644 --- a/pkg/k8s/client_factory.go +++ b/pkg/k8s/client_factory.go @@ -7,6 +7,7 @@ import ( "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" "net/http" "net/url" "path/filepath" @@ -81,3 +82,19 @@ func NewClientFactory(configIn *rest.Config) (ClientFactory, error) { httpClient: httpClient, }, nil } + +func NewClientFactoryFromDefaultConfig(context *string) (ClientFactory, error) { + configOverrides := &clientcmd.ConfigOverrides{} + if context != nil { + configOverrides.CurrentContext = *context + } + + config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), + configOverrides).ClientConfig() + if err != nil { + return nil, err + } + + return NewClientFactory(config) +} diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index de92465ac..69bd95feb 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -2,14 +2,19 @@ package k8s import ( "context" + "encoding/json" "fmt" + "io" + "sync" + "time" + "github.com/Masterminds/semver/v3" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - "io" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -378,14 +383,8 @@ type PatchOptions struct { ForceApply bool } -func (k *K8sCluster) PatchObject(o *uo.UnstructuredObject, options PatchOptions) (*uo.UnstructuredObject, []ApiWarning, error) { +func (k *K8sCluster) doPatch(ref k8s.ObjectRef, data []byte, patchType types.PatchType, options PatchOptions) (*uo.UnstructuredObject, []ApiWarning, error) { dryRun := k.DryRun || options.ForceDryRun - ref := o.GetK8sRef() - - data, err := yaml.WriteYamlBytes(o) - if err != nil { - return nil, nil, err - } po := v1.PatchOptions{ FieldManager: "kluctl", @@ -401,7 +400,7 @@ func (k *K8sCluster) PatchObject(o *uo.UnstructuredObject, options PatchOptions) var result *uo.UnstructuredObject apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { - x, err := r.Patch(k.ctx, ref.Name, types.ApplyPatchType, data, po) + x, err := r.Patch(k.ctx, ref.Name, patchType, data, po) if err != nil { return fmt.Errorf("failed to patch %s: %w", ref.String(), err) } @@ -411,6 +410,29 @@ func (k *K8sCluster) PatchObject(o *uo.UnstructuredObject, options PatchOptions) return result, apiWarnings, err } +func (k *K8sCluster) PatchObject(o *uo.UnstructuredObject, options PatchOptions) (*uo.UnstructuredObject, []ApiWarning, error) { + data, err := yaml.WriteYamlBytes(o) + if err != nil { + return nil, nil, err + } + + return k.doPatch(o.GetK8sRef(), data, types.ApplyPatchType, options) +} + +type JsonPatch struct { + Op string `json:"op"` + Path string `json:"path"` + Value any `json:"value"` +} + +func (k *K8sCluster) PatchObjectWithJsonPatch(ref k8s.ObjectRef, patch interface{}, options PatchOptions) (*uo.UnstructuredObject, []ApiWarning, error) { + data, err := json.Marshal(patch) + if err != nil { + return nil, nil, err + } + return k.doPatch(ref, data, types.JSONPatchType, options) +} + type UpdateOptions struct { ForceDryRun bool } From 0e2503532226dda8bba670d639c05f6268f8d8b6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 09:33:14 +0200 Subject: [PATCH 0386/2268] fix: Remove redundant imports (#52) --- pkg/k8s/k8s_cluster.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 69bd95feb..f8e8a07a7 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -26,8 +26,6 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" "k8s.io/client-go/restmapper" - "sync" - "time" ) type K8sCluster struct { From 58aa90b428d7c871e17142a8c29456263979b590 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 11:33:05 +0200 Subject: [PATCH 0387/2268] chore: Prepare Makefile for installation/setup of envtest --- .gitignore | 1 + Makefile | 62 +++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 8160eea20..d7ad8b4f4 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ /kluctl.exe /e2e.test* /bin +/build /reports /vendor dist/ diff --git a/Makefile b/Makefile index 631cc08ee..7d7c482d2 100644 --- a/Makefile +++ b/Makefile @@ -8,27 +8,27 @@ TEST_BINARY_NAME=kluctl-e2e REQUIRED_ENV_VARS=GOOS GOARCH EXPORT_RESULT?=false +# If gobin not set, create one on ./build and add to path. +ifeq (,$(shell go env GOBIN)) +GOBIN=$(BUILD_DIR)/gobin +else +GOBIN=$(shell go env GOBIN) +endif +export PATH:=$(GOBIN):${PATH} + +# Architecture to use envtest with +ENVTEST_ARCH ?= amd64 + GREEN := $(shell tput -Txterm setaf 2) YELLOW := $(shell tput -Txterm setaf 3) WHITE := $(shell tput -Txterm setaf 7) CYAN := $(shell tput -Txterm setaf 6) RESET := $(shell tput -Txterm sgr0) -.PHONY: all test build check-kubectl check-helm check-kind +.PHONY: all test build all: help -## Check: - -check-kubectl: ## Checks if kubectl is installed - kubectl version --client=true - -check-helm: ## Checks if helm is installed - helm version - -check-kind: ## Checks if kind is installed - kind version - ## Build: build: build-go ## Run the complete build pipeline @@ -42,11 +42,28 @@ clean: ## Remove build related file rm -fr ./reports rm -fr ./download-python +## Envtest setup +# Repository root based on Git metadata +REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel) +BUILD_DIR := $(REPOSITORY_ROOT)/build +ENVTEST = $(GOBIN)/setup-envtest +.PHONY: envtest +setup-envtest: ## Download envtest-setup locally if necessary. + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) + +# Download the envtest binaries to testbin +ENVTEST_ASSETS_DIR=$(BUILD_DIR)/testbin +ENVTEST_KUBERNETES_VERSION?=latest +install-envtest: setup-envtest + mkdir -p ${ENVTEST_ASSETS_DIR} + $(ENVTEST) use $(ENVTEST_KUBERNETES_VERSION) --arch=$(ENVTEST_ARCH) --bin-dir=$(ENVTEST_ASSETS_DIR) + ## Test: test: test-unit test-e2e ## Runs the complete test suite -test-e2e: check-kubectl check-helm check-kind ## Runs the end to end tests - CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -o ./bin/$(TEST_BINARY_NAME) ./e2e +KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_KUBERNETES_VERSION) --bin-dir=$(ENVTEST_ASSETS_DIR) -p path)" +test-e2e: install-envtest ## Runs the end to end tests + KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -o ./bin/$(TEST_BINARY_NAME) ./e2e test-unit: ## Run the unit tests of the project ifeq ($(EXPORT_RESULT), true) @@ -98,4 +115,19 @@ help: ## Show this help. @awk 'BEGIN {FS = ":.*?## "} { \ if (/^[0-9a-zA-Z_-]+:.*?##.*$$/) {printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n", $$1, $$2} \ else if (/^## .*$$/) {printf " ${CYAN}%s${RESET}\n", substr($$1,4)} \ - }' $(MAKEFILE_LIST) \ No newline at end of file + }' $(MAKEFILE_LIST) + +## Tools +# go-install-tool will 'go install' any package $2 and install it to $1. +PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) +define go-install-tool +@[ -f $(1) ] || { \ +set -e ;\ +TMP_DIR=$$(mktemp -d) ;\ +cd $$TMP_DIR ;\ +go mod init tmp ;\ +echo "Downloading $(2)" ;\ +GOBIN=$(GOBIN) go install $(2) ;\ +rm -rf $$TMP_DIR ;\ +} +endef From f19dad3dbaa993a14047a37bf9c41c81b4674ebf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 11:35:42 +0200 Subject: [PATCH 0388/2268] tests: Switch to envtest based clusters --- e2e/contexts_test.go | 14 +- e2e/default_clusters.go | 67 ++------- e2e/deploy_test.go | 2 +- e2e/external_projects_test.go | 6 +- e2e/flux_test.go | 4 +- e2e/hooks_test.go | 20 +-- e2e/inclusion_test.go | 6 +- e2e/project.go | 15 +- e2e/seal_test.go | 24 ++-- e2e/test_resources/resources.go | 8 +- e2e/utils.go | 27 ++-- go.mod | 4 +- go.sum | 16 +-- internal/test-utils/envtest_cluster.go | 100 ++++++++++++++ internal/test-utils/kind_cluster.go | 181 ------------------------- 15 files changed, 173 insertions(+), 321 deletions(-) create mode 100644 internal/test-utils/envtest_cluster.go delete mode 100644 internal/test-utils/kind_cluster.go diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index b55c5ab6d..34d036a1e 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -3,7 +3,6 @@ package e2e import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/client-go/tools/clientcmd/api" - "sync" "testing" ) @@ -12,17 +11,8 @@ func prepareContextTest(t *testing.T, name string) *testProject { p.init(t, defaultKindCluster1, name) p.mergeKubeconfig(defaultKindCluster2) - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() - recreateNamespace(t, defaultKindCluster1, p.projectName) - }() - go func() { - defer wg.Done() - recreateNamespace(t, defaultKindCluster2, p.projectName) - }() - wg.Wait() + createNamespace(t, defaultKindCluster1, p.projectName) + createNamespace(t, defaultKindCluster2, p.projectName) addConfigMapDeployment(p, "cm", nil, resourceOpts{ name: "cm", diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 466df5b4f..c042571cc 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -1,62 +1,11 @@ package e2e import ( - "fmt" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" - "github.com/kluctl/kluctl/v2/pkg/utils" - "os" - "path/filepath" - "strconv" "sync" ) -func createDefaultKindCluster(num int) (*test_utils.KindCluster, int) { - kindClusterName := os.Getenv(fmt.Sprintf("KIND_CLUSTER_NAME%d", num)) - kindApiHost := os.Getenv(fmt.Sprintf("KIND_API_HOST%d", num)) - kindApiPort := os.Getenv(fmt.Sprintf("KIND_API_PORT%d", num)) - kindExtraPortsOffset := os.Getenv(fmt.Sprintf("KIND_EXTRA_PORTS_OFFSET%d", num)) - kindKubeconfig := os.Getenv(fmt.Sprintf("KIND_KUBECONFIG%d", num)) - if kindClusterName == "" { - kindClusterName = fmt.Sprintf("kluctl-e2e-%d", num) - } - if kindApiHost == "" { - kindApiHost = "localhost" - } - if kindExtraPortsOffset == "" { - kindExtraPortsOffset = fmt.Sprintf("%d", 30000+num*1000) - } - if kindKubeconfig == "" { - kindKubeconfig = filepath.Join(utils.GetTmpBaseDir(), fmt.Sprintf("kluctl-e2e-kubeconfig-%d.yml", num)) - } - - var err error - var kindApiPortInt, kindExtraPortsOffsetInt int64 - - if kindApiPort != "" { - kindApiPortInt, err = strconv.ParseInt(kindApiPort, 0, 32) - if err != nil { - panic(err) - } - } - kindExtraPortsOffsetInt, err = strconv.ParseInt(kindExtraPortsOffset, 0, 32) - if err != nil { - panic(err) - } - - vaultPort := int(kindExtraPortsOffsetInt) + 0 - - kindExtraPorts := map[int]int{ - vaultPort: 30000, - } - - k, err := test_utils.CreateKindCluster(kindClusterName, kindApiHost, int(kindApiPortInt), kindExtraPorts, kindKubeconfig) - if err != nil { - panic(err) - } - return k, vaultPort -} - -var defaultKindCluster1, defaultKindCluster2 *test_utils.KindCluster +var defaultKindCluster1, defaultKindCluster2 *test_utils.EnvTestCluster var defaultKindCluster1VaultPort, defaultKindCluster2VaultPort int func init() { @@ -64,13 +13,19 @@ func init() { wg.Add(2) go func() { defer wg.Done() - defaultKindCluster1, defaultKindCluster1VaultPort = createDefaultKindCluster(1) - deleteTestNamespaces(defaultKindCluster1) + var err error + defaultKindCluster1, err = test_utils.CreateEnvTestCluster("cluster1") + if err != nil { + panic(err) + } }() go func() { defer wg.Done() - defaultKindCluster2, defaultKindCluster2VaultPort = createDefaultKindCluster(2) - deleteTestNamespaces(defaultKindCluster2) + var err error + defaultKindCluster2, err = test_utils.CreateEnvTestCluster("cluster2") + if err != nil { + panic(err) + } }() wg.Wait() } diff --git a/e2e/deploy_test.go b/e2e/deploy_test.go index f8d451032..97044e7eb 100644 --- a/e2e/deploy_test.go +++ b/e2e/deploy_test.go @@ -13,7 +13,7 @@ func TestCommandDeploySimple(t *testing.T) { p.init(t, k, "simple") defer p.cleanup() - recreateNamespace(t, k, p.projectName) + createNamespace(t, k, p.projectName) p.updateTarget("test", nil) diff --git a/e2e/external_projects_test.go b/e2e/external_projects_test.go index 18dbe95ca..f2a4dd9db 100644 --- a/e2e/external_projects_test.go +++ b/e2e/external_projects_test.go @@ -12,12 +12,12 @@ func doTestProject(t *testing.T, namespace string, p *testProject) { p.init(t, k, fmt.Sprintf("project-%s", namespace)) defer p.cleanup() - recreateNamespace(t, k, namespace) + createNamespace(t, k, namespace) p.updateKindCluster(k, uo.FromMap(map[string]interface{}{ "cluster_var": "cluster_value1", })) - p.updateTargetDeprecated("test", k.Name, uo.FromMap(map[string]interface{}{ + p.updateTargetDeprecated("test", k.Context, uo.FromMap(map[string]interface{}{ "target_var": "target_value1", })) addConfigMapDeployment(p, "cm1", map[string]string{}, resourceOpts{name: "cm1", namespace: namespace}) @@ -46,7 +46,7 @@ func doTestProject(t *testing.T, namespace string, p *testProject) { assertNestedFieldEquals(t, o, "cluster_value2", "data", "cluster_var") assertNestedFieldEquals(t, o, "target_value1", "data", "target_var") - p.updateTargetDeprecated("test", k.Name, uo.FromMap(map[string]interface{}{ + p.updateTargetDeprecated("test", k.Context, uo.FromMap(map[string]interface{}{ "target_var": "target_value2", })) p.KluctlMust("deploy", "--yes", "-t", "test") diff --git a/e2e/flux_test.go b/e2e/flux_test.go index 56bde84b3..d11f62790 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -38,7 +38,7 @@ func TestFluxCommands(t *testing.T) { assert.Len(t, annotation, 1, "Annotation not present") } -func getKluctlSuspendField(t *testing.T, k *test_utils.KindCluster) interface{} { +func getKluctlSuspendField(t *testing.T, k *test_utils.EnvTestCluster) interface{} { o := k.KubectlYamlMust(t, "-n", "default", "get", "kluctldeployment", "microservices-demo-test") result, ok, err := o.GetNestedField("spec", "suspend") fmt.Println(result) @@ -51,7 +51,7 @@ func getKluctlSuspendField(t *testing.T, k *test_utils.KindCluster) interface{} return result } -func getKluctlAnnotations(t *testing.T, k *test_utils.KindCluster) interface{} { +func getKluctlAnnotations(t *testing.T, k *test_utils.EnvTestCluster) interface{} { o := k.KubectlYamlMust(t, "-n", "default", "get", "kluctldeployment", "microservices-demo-test") result, ok, err := o.GetNestedField("metadata", "annotations") if err != nil { diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index d105b9a45..4c9cf2930 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -55,7 +55,7 @@ func addConfigMap(p *testProject, dir string, opts resourceOpts) { }) } -func getHookResult(t *testing.T, p *testProject, k *test_utils.KindCluster, secretName string) *uo.UnstructuredObject { +func getHookResult(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, secretName string) *uo.UnstructuredObject { o := k.KubectlYamlMust(t, "-n", p.projectName, "get", "secret", secretName) s, ok, err := o.GetNestedString("data", "result") if err != nil { @@ -75,7 +75,7 @@ func getHookResult(t *testing.T, p *testProject, k *test_utils.KindCluster, secr return r } -func getHookResultCMNames(t *testing.T, p *testProject, k *test_utils.KindCluster, second bool) []string { +func getHookResultCMNames(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, second bool) []string { secretName := "hook-result" if second { secretName = "hook-result2" @@ -89,7 +89,7 @@ func getHookResultCMNames(t *testing.T, p *testProject, k *test_utils.KindCluste return names } -func assertHookResultCMName(t *testing.T, p *testProject, k *test_utils.KindCluster, second bool, cmName string) { +func assertHookResultCMName(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, second bool, cmName string) { names := getHookResultCMNames(t, p, k, second) for _, x := range names { if x == cmName { @@ -99,7 +99,7 @@ func assertHookResultCMName(t *testing.T, p *testProject, k *test_utils.KindClus t.Fatalf("%s not found in hook result", cmName) } -func assertHookResultNotCMName(t *testing.T, p *testProject, k *test_utils.KindCluster, second bool, cmName string) { +func assertHookResultNotCMName(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, second bool, cmName string) { names := getHookResultCMNames(t, p, k, second) for _, x := range names { if x == cmName { @@ -108,7 +108,7 @@ func assertHookResultNotCMName(t *testing.T, p *testProject, k *test_utils.KindC } } -func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) (*testProject, *test_utils.KindCluster) { +func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) (*testProject, *test_utils.EnvTestCluster) { isDone := false namespace := fmt.Sprintf("hook-%s", name) k := defaultKindCluster1 @@ -120,7 +120,7 @@ func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletion } }() - recreateNamespace(t, k, namespace) + createNamespace(t, k, namespace) p.updateTarget("test", nil) @@ -131,14 +131,14 @@ func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletion return p, k } -func ensureHookExecuted(t *testing.T, p *testProject, k *test_utils.KindCluster) { - _, _ = k.Kubectl("delete", "-n", p.projectName, "secret", "hook-result", "hook-result2") +func ensureHookExecuted(t *testing.T, p *testProject, k *test_utils.EnvTestCluster) { + _, _, _ = k.Kubectl("delete", "-n", p.projectName, "secret", "hook-result", "hook-result2") p.KluctlMust("deploy", "--yes", "-t", "test") assertResourceExists(t, k, p.projectName, "ConfigMap/cm1") } -func ensureHookNotExecuted(t *testing.T, p *testProject, k *test_utils.KindCluster) { - _, _ = k.Kubectl("delete", "-n", p.projectName, "secret", "hook-result", "hook-result2") +func ensureHookNotExecuted(t *testing.T, p *testProject, k *test_utils.EnvTestCluster) { + _, _, _ = k.Kubectl("delete", "-n", p.projectName, "secret", "hook-result", "hook-result2") p.KluctlMust("deploy", "--yes", "-t", "test") assertResourceNotExists(t, k, p.projectName, "Secret/hook-result") } diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 0395bff9f..247073d64 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bool) (*testProject, *test_utils.KindCluster) { +func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bool) (*testProject, *test_utils.EnvTestCluster) { isDone := false k := defaultKindCluster1 @@ -20,7 +20,7 @@ func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bo } }() - recreateNamespace(t, k, p.projectName) + createNamespace(t, k, p.projectName) p.updateTarget("test", nil) @@ -49,7 +49,7 @@ func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bo return p, k } -func assertExistsHelper(t *testing.T, p *testProject, k *test_utils.KindCluster, shouldExists map[string]bool, add []string, remove []string) { +func assertExistsHelper(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, shouldExists map[string]bool, add []string, remove []string) { for _, x := range add { shouldExists[x] = true } diff --git a/e2e/project.go b/e2e/project.go index 2b67e294a..f540b6b3d 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -37,7 +37,7 @@ type testProject struct { gitServer *test_utils.GitServer } -func (p *testProject) init(t *testing.T, k *test_utils.KindCluster, projectName string) { +func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster, projectName string) { p.t = t p.gitServer = test_utils.NewGitServer(t) p.projectName = projectName @@ -92,9 +92,9 @@ func (p *testProject) cleanup() { } } -func (p *testProject) mergeKubeconfig(k *test_utils.KindCluster) { +func (p *testProject) mergeKubeconfig(k *test_utils.EnvTestCluster) { p.updateMergedKubeconfig(func(config *clientcmdapi.Config) { - nkcfg, err := clientcmd.LoadFromFile(k.Kubeconfig) + nkcfg, err := clientcmd.Load(k.Kubeconfig) if err != nil { p.t.Fatal(err) } @@ -187,13 +187,10 @@ func (p *testProject) updateCluster(name string, context string, vars *uo.Unstru }, fmt.Sprintf("add/update cluster %s", name)) } -func (p *testProject) updateKindCluster(k *test_utils.KindCluster, vars *uo.UnstructuredObject) { - context, err := k.Kubectl("config", "current-context") - if err != nil { - p.t.Fatal(err) - } +func (p *testProject) updateKindCluster(k *test_utils.EnvTestCluster, vars *uo.UnstructuredObject) { + context := k.KubectlMust(p.t, "config", "current-context") context = strings.TrimSpace(context) - p.updateCluster(k.Name, context, vars) + p.updateCluster(k.Context, context, vars) } func (p *testProject) updateTargetDeprecated(name string, cluster string, args *uo.UnstructuredObject) { diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 28e6b976f..da18bb655 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -18,26 +18,26 @@ import ( "time" ) -func installSealedSecretsOperator(k *test_utils.KindCluster) { +func installSealedSecretsOperator(k *test_utils.EnvTestCluster) { test_resources.ApplyYaml("sealed-secrets.yaml", k) } -func waitForSealedSecretsOperator(t *testing.T, k *test_utils.KindCluster) { +func waitForSealedSecretsOperator(t *testing.T, k *test_utils.EnvTestCluster) { waitForReadiness(t, k, "kube-system", "deployment/sealed-secrets-controller", 5*time.Minute) } -func deleteSealedSecretsOperator(k *test_utils.KindCluster) { +func deleteSealedSecretsOperator(k *test_utils.EnvTestCluster) { test_resources.DeleteYaml("sealed-secrets.yaml", k) - _, _ = k.Kubectl("-n", "kube-system", "delete", "secret", "-l", "sealedsecrets.bitnami.com/sealed-secrets-key", "--wait") - _, _ = k.Kubectl("-n", "kube-system", "delete", "configmap", "sealed-secrets-key-kluctl-bootstrap", "--wait") + _, _, _ = k.Kubectl("-n", "kube-system", "delete", "secret", "-l", "sealedsecrets.bitnami.com/sealed-secrets-key", "--wait") + _, _, _ = k.Kubectl("-n", "kube-system", "delete", "configmap", "sealed-secrets-key-kluctl-bootstrap", "--wait") } -func installVault(k *test_utils.KindCluster) { - _, _ = k.Kubectl("create", "ns", "vault") +func installVault(k *test_utils.EnvTestCluster) { + _, _, _ = k.Kubectl("create", "ns", "vault") test_resources.ApplyYaml("vault.yaml", k) } -func waitForVault(t *testing.T, k *test_utils.KindCluster) { +func waitForVault(t *testing.T, k *test_utils.EnvTestCluster) { waitForReadiness(t, k, "vault", "statefulset/vault", 5*time.Minute) } @@ -56,11 +56,11 @@ func init() { wg.Wait() } -func prepareSealTest(t *testing.T, k *test_utils.KindCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject) *testProject { +func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject) *testProject { p := &testProject{} p.init(t, k, fmt.Sprintf("seal-%s", namespace)) - recreateNamespace(t, k, namespace) + createNamespace(t, k, namespace) addSecretsSet(p, "test", varsSources) addSecretsSetToTarget(p, "test-target", "test") @@ -84,7 +84,7 @@ func addSecretsSetToTarget(p *testProject, targetName string, secretSetName stri }) } -func assertDecryptedSecrets(t *testing.T, k *test_utils.KindCluster, namespace string, secretName string, expectedSecrets map[string]string) { +func assertDecryptedSecrets(t *testing.T, k *test_utils.EnvTestCluster, namespace string, secretName string, expectedSecrets map[string]string) { s := k.KubectlYamlMust(t, "-n", namespace, "get", "secret", secretName) for key, value := range expectedSecrets { @@ -294,7 +294,7 @@ func TestSeal_MultipleTargets(t *testing.T) { addSecretsSetToTarget(p, "test-target2", "test2") p.mergeKubeconfig(defaultKindCluster2) - recreateNamespace(t, defaultKindCluster2, namespace) + createNamespace(t, defaultKindCluster2, namespace) p.updateTarget("test-target", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultKindCluster1.Context, "context") }) diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go index 24d724f66..90454201d 100644 --- a/e2e/test_resources/resources.go +++ b/e2e/test_resources/resources.go @@ -26,21 +26,21 @@ func GetYamlTmpFile(name string) string { return tmpFile.Name() } -func ApplyYaml(name string, k *test_utils.KindCluster) { +func ApplyYaml(name string, k *test_utils.EnvTestCluster) { tmpFile := GetYamlTmpFile(name) defer os.Remove(tmpFile) - _, err := k.Kubectl("apply", "-f", tmpFile) + _, _, err := k.Kubectl("apply", "-f", tmpFile) if err != nil { panic(err) } } -func DeleteYaml(name string, k *test_utils.KindCluster) { +func DeleteYaml(name string, k *test_utils.EnvTestCluster) { tmpFile := GetYamlTmpFile(name) defer os.Remove(tmpFile) - _, err := k.Kubectl("delete", "-f", tmpFile) + _, _, err := k.Kubectl("delete", "-f", tmpFile) if err != nil { panic(err) } diff --git a/e2e/utils.go b/e2e/utils.go index 9798a9de1..eaf69dea2 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -17,24 +17,19 @@ import ( log "github.com/sirupsen/logrus" ) -func recreateNamespace(t *testing.T, k *test_utils.KindCluster, namespace string) { - _, _ = k.Kubectl("delete", "ns", namespace) +func createNamespace(t *testing.T, k *test_utils.EnvTestCluster, namespace string) { k.KubectlMust(t, "create", "ns", namespace) k.KubectlMust(t, "label", "ns", namespace, "kluctl-e2e=true") } -func deleteTestNamespaces(k *test_utils.KindCluster) { - _, _ = k.Kubectl("delete", "ns", "-l", "kubectl-e2e=true") -} - -func waitForReadiness(t *testing.T, k *test_utils.KindCluster, namespace string, resource string, timeout time.Duration) bool { +func waitForReadiness(t *testing.T, k *test_utils.EnvTestCluster, namespace string, resource string, timeout time.Duration) bool { t.Logf("Waiting for readiness: %s/%s", namespace, resource) startTime := time.Now() for time.Now().Sub(startTime) < timeout { - y, err := k.KubectlYaml("-n", namespace, "get", resource) + y, stderr, err := k.KubectlYaml("-n", namespace, "get", resource) if err != nil { - if ee, ok := err.(*exec.ExitError); !ok || strings.Index(string(ee.Stderr), "NotFound") == -1 { + if strings.Index(stderr, "NotFound") == -1 { t.Fatal(err) } time.Sleep(1 * time.Second) @@ -61,13 +56,13 @@ func waitForReadiness(t *testing.T, k *test_utils.KindCluster, namespace string, return false } -func assertReadiness(t *testing.T, k *test_utils.KindCluster, namespace string, resource string, timeout time.Duration) { +func assertReadiness(t *testing.T, k *test_utils.EnvTestCluster, namespace string, resource string, timeout time.Duration) { if !waitForReadiness(t, k, namespace, resource, timeout) { t.Errorf("%s/%s did not get ready in time", namespace, resource) } } -func assertResourceExists(t *testing.T, k *test_utils.KindCluster, namespace string, resource string) *uo.UnstructuredObject { +func assertResourceExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, resource string) *uo.UnstructuredObject { var args []string if namespace != "" { args = append(args, "-n", namespace) @@ -76,21 +71,17 @@ func assertResourceExists(t *testing.T, k *test_utils.KindCluster, namespace str return k.KubectlYamlMust(t, args...) } -func assertResourceNotExists(t *testing.T, k *test_utils.KindCluster, namespace string, resource string) { +func assertResourceNotExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, resource string) { var args []string if namespace != "" { args = append(args, "-n", namespace) } args = append(args, "get", resource) - _, err := k.KubectlYaml(args...) + _, stderr, err := k.KubectlYaml(args...) if err == nil { t.Fatalf("'kubectl get' for %s should not have succeeded", resource) } else { - ee, ok := err.(*exec.ExitError) - if !ok { - t.Fatal(err) - } - if strings.Index(string(ee.Stderr), "(NotFound)") == -1 { + if strings.Index(stderr, "(NotFound)") == -1 { t.Fatal(err) } } diff --git a/go.mod b/go.mod index cc78c4587..5a7fa1316 100644 --- a/go.mod +++ b/go.mod @@ -53,12 +53,13 @@ require ( k8s.io/apimachinery v0.25.2 k8s.io/client-go v0.25.2 k8s.io/klog/v2 v2.80.1 - sigs.k8s.io/kind v0.16.0 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) +require sigs.k8s.io/controller-runtime v0.13.0 + require ( cloud.google.com/go/compute v1.10.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect @@ -76,7 +77,6 @@ require ( github.com/Microsoft/go-winio v0.6.0 // indirect github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect - github.com/alessio/shellescape v1.4.1 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect diff --git a/go.sum b/go.sum index 1b9d4d57c..c5651ad7a 100644 --- a/go.sum +++ b/go.sum @@ -63,7 +63,6 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= 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.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -101,8 +100,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy 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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -163,7 +160,6 @@ github.com/containerd/stargz-snapshotter/estargz v0.12.0 h1:idtwRTLjk2erqiYhPWy2 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/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -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/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= @@ -255,6 +251,7 @@ github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE github.com/go-logr/logr v1.2.0/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/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= 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= @@ -616,6 +613,7 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m 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/ohler55/ojg v1.14.4 h1:L2ds8AlB5t/QbqSfhRwvagJzQ7pgmdrefMIypQs0Xik= github.com/ohler55/ojg v1.14.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= @@ -632,7 +630,6 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -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.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= @@ -732,7 +729,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU 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.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -807,8 +803,10 @@ go.starlark.net v0.0.0-20220926145019-14b050677505/go.mod h1:qsNirHv+Awo5xHuNyQ/ 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/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1098,6 +1096,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= 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= @@ -1227,6 +1226,7 @@ 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/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1282,10 +1282,10 @@ oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= 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/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= +sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kind v0.16.0 h1:GFXyyxtPnHFKqXr3ZG8/X0+0K9sl69lejStlPn2WQyM= -sigs.k8s.io/kind v0.16.0/go.mod h1:cKTqagdRyUQmihhBOd+7p43DpOPRn9rHsUC08K1Jbsk= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go new file mode 100644 index 000000000..1151e87a7 --- /dev/null +++ b/internal/test-utils/envtest_cluster.go @@ -0,0 +1,100 @@ +package test_utils + +import ( + "bytes" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "io" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "testing" +) + +type EnvTestCluster struct { + env envtest.Environment + + user *envtest.AuthenticatedUser + Kubeconfig []byte + Context string + config *rest.Config +} + +func CreateEnvTestCluster(context string) (*EnvTestCluster, error) { + k := &EnvTestCluster{ + Context: context, + } + + _, err := k.env.Start() + if err != nil { + return nil, err + } + + isOk := false + defer func() { + if !isOk { + _ = k.env.Stop() + } + }() + + user, err := k.env.AddUser(envtest.User{Name: "default", Groups: []string{"system:masters"}}, &rest.Config{}) + if err != nil { + return nil, err + } + k.user = user + + kcfg, err := user.KubeConfig() + if err != nil { + return nil, err + } + + kcfg = bytes.ReplaceAll(kcfg, []byte("envtest"), []byte(context)) + + k.Kubeconfig = kcfg + + isOk = true + return k, nil +} + +// RESTConfig returns K8s client config to pass to clientset objects +func (c *EnvTestCluster) RESTConfig() *rest.Config { + return c.config +} + +func (c *EnvTestCluster) Kubectl(args ...string) (string, string, error) { + kctl, err := c.user.Kubectl() + if err != nil { + return "", "", err + } + + stdout1, stderr1, err := kctl.Run(args...) + stdout, _ := io.ReadAll(stdout1) + stderr, _ := io.ReadAll(stderr1) + return string(stdout), string(stderr), err +} + +func (c *EnvTestCluster) KubectlMust(t *testing.T, args ...string) string { + stdout, stderr, err := c.Kubectl(args...) + if err != nil { + t.Fatalf("%v, stderr=%s\n", err, stderr) + } + return stdout +} + +func (c *EnvTestCluster) KubectlYaml(args ...string) (*uo.UnstructuredObject, string, error) { + args = append(args, "-oyaml") + stdout, stderr, err := c.Kubectl(args...) + if err != nil { + return nil, stderr, err + } + ret := uo.New() + err = yaml.ReadYamlString(stdout, ret) + return ret, stderr, err +} + +func (c *EnvTestCluster) KubectlYamlMust(t *testing.T, args ...string) *uo.UnstructuredObject { + o, stderr, err := c.KubectlYaml(args...) + if err != nil { + t.Fatalf("%v, stderr=%s\n", err, stderr) + } + return o +} diff --git a/internal/test-utils/kind_cluster.go b/internal/test-utils/kind_cluster.go deleted file mode 100644 index 99ad404d7..000000000 --- a/internal/test-utils/kind_cluster.go +++ /dev/null @@ -1,181 +0,0 @@ -package test_utils - -import ( - "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - "net/url" - "os" - "os/exec" - "sigs.k8s.io/kind/pkg/apis/config/v1alpha4" - "sigs.k8s.io/kind/pkg/cluster" - kindcmd "sigs.k8s.io/kind/pkg/cmd" - "testing" - "time" -) - -type KindCluster struct { - Name string - Context string - Kubeconfig string - config *rest.Config -} - -func CreateKindCluster(name, apiServerHost string, apiServerPort int, extraPorts map[int]int, kubeconfigPath string) (*KindCluster, error) { - provider := cluster.NewProvider(cluster.ProviderWithLogger(kindcmd.NewLogger())) - - c := &KindCluster{ - Name: name, - Context: fmt.Sprintf("kind-%s", name), - Kubeconfig: kubeconfigPath, - } - - n, err := provider.ListNodes(name) - if err != nil { - return nil, err - } - if len(n) == 0 { - if err := kindCreate(name, apiServerHost, apiServerPort, extraPorts, kubeconfigPath); err != nil { - return nil, err - } - } - - return c, nil -} - -// Delete removes the cluster from kind. The cluster may not be deleted straight away - this only issues a delete command -func (c *KindCluster) Delete() error { - provider := cluster.NewProvider(cluster.ProviderWithLogger(kindcmd.NewLogger())) - return provider.Delete(c.Name, c.Kubeconfig) -} - -// RESTConfig returns K8s client config to pass to clientset objects -func (c *KindCluster) RESTConfig() *rest.Config { - if c.config == nil { - var err error - c.config, err = clientcmd.BuildConfigFromFlags("", c.Kubeconfig) - if err != nil { - panic(err) - } - } - return c.config -} - -func (c *KindCluster) Kubectl(args ...string) (string, error) { - cmd := exec.Command("kubectl", args...) - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, fmt.Sprintf("KUBECONFIG=%s", c.Kubeconfig)) - - stdout, err := cmd.Output() - return string(stdout), err -} - -func (c *KindCluster) KubectlMust(t *testing.T, args ...string) string { - stdout, err := c.Kubectl(args...) - if err != nil { - if e, ok := err.(*exec.ExitError); ok { - t.Fatalf("%v, stderr=%s\n", err, string(e.Stderr)) - } else { - t.Fatal(err) - } - } - return stdout -} - -func (c *KindCluster) KubectlYaml(args ...string) (*uo.UnstructuredObject, error) { - args = append(args, "-oyaml") - stdout, err := c.Kubectl(args...) - if err != nil { - return nil, err - } - ret := uo.New() - err = yaml.ReadYamlString(stdout, ret) - return ret, err -} - -func (c *KindCluster) KubectlYamlMust(t *testing.T, args ...string) *uo.UnstructuredObject { - o, err := c.KubectlYaml(args...) - if err != nil { - if e, ok := err.(*exec.ExitError); ok { - t.Fatalf("%v, stderr=%s\n", err, string(e.Stderr)) - } else { - t.Fatal(err) - } - } - return o -} - -// kindCreate creates the kind cluster. It will retry up to 10 times if cluster creation fails. -func kindCreate(name string, apiServerHost string, apiServerPort int, extraPorts map[int]int, kubeconfig string) error { - var err error - for i := 0; i < 10; i++ { - err = kindCreate2(name, apiServerHost, apiServerPort, extraPorts, kubeconfig) - if err == nil { - return nil - } - } - return err -} - -func kindCreate2(name string, apiServerHost string, apiServerPort int, extraPorts map[int]int, kubeconfig string) error { - fmt.Printf("🌧️ Creating kind cluster %s with apiServerHost=%s, apiServerPort=%d, extraPorts=%v...\n", name, apiServerHost, apiServerPort, extraPorts) - provider := cluster.NewProvider(cluster.ProviderWithLogger(kindcmd.NewLogger())) - config := v1alpha4.Cluster{ - Name: name, - Nodes: []v1alpha4.Node{{ - Role: "control-plane", - }}, - Networking: v1alpha4.Networking{ - APIServerAddress: "0.0.0.0", - APIServerPort: int32(apiServerPort), - }, - } - for hostPort, containerPort := range extraPorts { - config.Nodes[0].ExtraPortMappings = append(config.Nodes[0].ExtraPortMappings, v1alpha4.PortMapping{ - ContainerPort: int32(containerPort), - HostPort: int32(hostPort), - ListenAddress: "0.0.0.0", - Protocol: "TCP", - }) - } - - err := provider.Create( - name, - cluster.CreateWithV1Alpha4Config(&config), - cluster.CreateWithNodeImage(""), - cluster.CreateWithRetain(false), - cluster.CreateWithWaitForReady(time.Duration(0)), - cluster.CreateWithKubeconfigPath(kubeconfig), - cluster.CreateWithDisplayUsage(false), - ) - if err != nil { - return err - } - - kcfg, err := clientcmd.LoadFromFile(kubeconfig) - if err != nil { - return err - } - - c := kcfg.Clusters[fmt.Sprintf("kind-%s", name)] - - u, err := url.Parse(c.Server) - if err != nil { - return err - } - - // override api server host and disable TLS verification - // this is needed to make it work with remote docker hosts - c.InsecureSkipTLSVerify = true - c.CertificateAuthorityData = nil - c.Server = fmt.Sprintf("https://%s:%s", apiServerHost, u.Port()) - - err = clientcmd.WriteToFile(*kcfg, kubeconfig) - if err != nil { - return err - } - - return nil -} From 80952981c34216cd441586ec3ee6b8efa07e4555 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 12:04:57 +0200 Subject: [PATCH 0389/2268] tests: Rename defaultKindClusterX to defaultClusterX --- e2e/contexts_test.go | 48 +++++++++++++++++------------------ e2e/default_clusters.go | 6 ++--- e2e/deploy_test.go | 2 +- e2e/external_projects_test.go | 6 ++--- e2e/flux_test.go | 2 +- e2e/hooks_test.go | 2 +- e2e/inclusion_test.go | 2 +- e2e/project.go | 2 +- e2e/seal_test.go | 36 +++++++++++++------------- 9 files changed, 53 insertions(+), 53 deletions(-) diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index 34d036a1e..5600f3689 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -8,11 +8,11 @@ import ( func prepareContextTest(t *testing.T, name string) *testProject { p := &testProject{} - p.init(t, defaultKindCluster1, name) - p.mergeKubeconfig(defaultKindCluster2) + p.init(t, defaultCluster1, name) + p.mergeKubeconfig(defaultCluster2) - createNamespace(t, defaultKindCluster1, p.projectName) - createNamespace(t, defaultKindCluster2, p.projectName) + createNamespace(t, defaultCluster1, p.projectName) + createNamespace(t, defaultCluster2, p.projectName) addConfigMapDeployment(p, "cm", nil, resourceOpts{ name: "cm", @@ -33,15 +33,15 @@ func TestContextCurrent(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") p.updateMergedKubeconfig(func(config *api.Config) { - config.CurrentContext = defaultKindCluster2.Context + config.CurrentContext = defaultCluster2.Context }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") } func TestContext1(t *testing.T) { @@ -51,12 +51,12 @@ func TestContext1(t *testing.T) { defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultKindCluster1.Context, "context") + _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") } func TestContext2(t *testing.T) { @@ -66,12 +66,12 @@ func TestContext2(t *testing.T) { defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultKindCluster2.Context, "context") + _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") + assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") } func TestContext1And2(t *testing.T) { @@ -81,18 +81,18 @@ func TestContext1And2(t *testing.T) { defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultKindCluster1.Context, "context") + _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.updateTarget("test2", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultKindCluster2.Context, "context") + _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") p.KluctlMust("deploy", "--yes", "-t", "test2") - assertResourceExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") } func TestContextSwitch(t *testing.T) { @@ -102,17 +102,17 @@ func TestContextSwitch(t *testing.T) { defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultKindCluster1.Context, "context") + _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultKindCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") p.updateTarget("test1", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultKindCluster2.Context, "context") + _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultKindCluster2, p.projectName, "ConfigMap/cm") + assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") } diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index c042571cc..1d632c7b4 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -5,7 +5,7 @@ import ( "sync" ) -var defaultKindCluster1, defaultKindCluster2 *test_utils.EnvTestCluster +var defaultCluster1, defaultCluster2 *test_utils.EnvTestCluster var defaultKindCluster1VaultPort, defaultKindCluster2VaultPort int func init() { @@ -14,7 +14,7 @@ func init() { go func() { defer wg.Done() var err error - defaultKindCluster1, err = test_utils.CreateEnvTestCluster("cluster1") + defaultCluster1, err = test_utils.CreateEnvTestCluster("cluster1") if err != nil { panic(err) } @@ -22,7 +22,7 @@ func init() { go func() { defer wg.Done() var err error - defaultKindCluster2, err = test_utils.CreateEnvTestCluster("cluster2") + defaultCluster2, err = test_utils.CreateEnvTestCluster("cluster2") if err != nil { panic(err) } diff --git a/e2e/deploy_test.go b/e2e/deploy_test.go index 97044e7eb..9e54ebcf8 100644 --- a/e2e/deploy_test.go +++ b/e2e/deploy_test.go @@ -7,7 +7,7 @@ import ( func TestCommandDeploySimple(t *testing.T) { t.Parallel() - k := defaultKindCluster1 + k := defaultCluster1 p := &testProject{} p.init(t, k, "simple") diff --git a/e2e/external_projects_test.go b/e2e/external_projects_test.go index f2a4dd9db..94e6aebc6 100644 --- a/e2e/external_projects_test.go +++ b/e2e/external_projects_test.go @@ -7,14 +7,14 @@ import ( ) func doTestProject(t *testing.T, namespace string, p *testProject) { - k := defaultKindCluster1 + k := defaultCluster1 p.init(t, k, fmt.Sprintf("project-%s", namespace)) defer p.cleanup() createNamespace(t, k, namespace) - p.updateKindCluster(k, uo.FromMap(map[string]interface{}{ + p.updateEnvTestCluster(k, uo.FromMap(map[string]interface{}{ "cluster_var": "cluster_value1", })) p.updateTargetDeprecated("test", k.Context, uo.FromMap(map[string]interface{}{ @@ -38,7 +38,7 @@ func doTestProject(t *testing.T, namespace string, p *testProject) { assertNestedFieldEquals(t, o, "cluster_value1", "data", "cluster_var") assertNestedFieldEquals(t, o, "target_value1", "data", "target_var") - p.updateKindCluster(k, uo.FromMap(map[string]interface{}{ + p.updateEnvTestCluster(k, uo.FromMap(map[string]interface{}{ "cluster_var": "cluster_value2", })) p.KluctlMust("deploy", "--yes", "-t", "test") diff --git a/e2e/flux_test.go b/e2e/flux_test.go index d11f62790..1b604dccb 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -12,7 +12,7 @@ import ( func TestFluxCommands(t *testing.T) { t.Parallel() - k := defaultKindCluster1 + k := defaultCluster1 p := &testProject{} p.init(t, k, "simple") diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 4c9cf2930..395640e7b 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -111,7 +111,7 @@ func assertHookResultNotCMName(t *testing.T, p *testProject, k *test_utils.EnvTe func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) (*testProject, *test_utils.EnvTestCluster) { isDone := false namespace := fmt.Sprintf("hook-%s", name) - k := defaultKindCluster1 + k := defaultCluster1 p := &testProject{} p.init(t, k, namespace) defer func() { diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 247073d64..243584b03 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -11,7 +11,7 @@ import ( func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bool) (*testProject, *test_utils.EnvTestCluster) { isDone := false - k := defaultKindCluster1 + k := defaultCluster1 p := &testProject{} p.init(t, k, namespace) defer func() { diff --git a/e2e/project.go b/e2e/project.go index f540b6b3d..39996fa5b 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -187,7 +187,7 @@ func (p *testProject) updateCluster(name string, context string, vars *uo.Unstru }, fmt.Sprintf("add/update cluster %s", name)) } -func (p *testProject) updateKindCluster(k *test_utils.EnvTestCluster, vars *uo.UnstructuredObject) { +func (p *testProject) updateEnvTestCluster(k *test_utils.EnvTestCluster, vars *uo.UnstructuredObject) { context := k.KubectlMust(p.t, "config", "current-context") context = strings.TrimSpace(context) p.updateCluster(k.Context, context, vars) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index da18bb655..f74a7544a 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -46,12 +46,12 @@ func init() { wg.Add(2) go func() { defer wg.Done() - installSealedSecretsOperator(defaultKindCluster1) - installVault(defaultKindCluster1) + installSealedSecretsOperator(defaultCluster1) + installVault(defaultCluster1) }() go func() { defer wg.Done() - installSealedSecretsOperator(defaultKindCluster2) + installSealedSecretsOperator(defaultCluster2) }() wg.Wait() } @@ -95,7 +95,7 @@ func assertDecryptedSecrets(t *testing.T, k *test_utils.EnvTestCluster, namespac } func TestSeal_WithOperator(t *testing.T) { - k := defaultKindCluster1 + k := defaultCluster1 namespace := "seal-with-operator" waitForSealedSecretsOperator(t, k) @@ -131,7 +131,7 @@ func TestSeal_WithOperator(t *testing.T) { } func TestSeal_WithBootstrap(t *testing.T) { - k := defaultKindCluster2 + k := defaultCluster2 namespace := "seal-with-bootstrap" // we still wait for it to be ready before we then delete it @@ -175,7 +175,7 @@ func TestSeal_WithBootstrap(t *testing.T) { func TestSeal_MultipleVarSources(t *testing.T) { t.Parallel() - k := defaultKindCluster1 + k := defaultCluster1 namespace := "seal-multiple-vs" waitForSealedSecretsOperator(t, k) @@ -217,7 +217,7 @@ func TestSeal_MultipleVarSources(t *testing.T) { func TestSeal_MultipleSecretSets(t *testing.T) { t.Parallel() - k := defaultKindCluster1 + k := defaultCluster1 namespace := "seal-multiple-ss" waitForSealedSecretsOperator(t, k) @@ -261,11 +261,11 @@ func TestSeal_MultipleSecretSets(t *testing.T) { } func TestSeal_MultipleTargets(t *testing.T) { - k := defaultKindCluster1 + k := defaultCluster1 namespace := "seal-multiple-targets" waitForSealedSecretsOperator(t, k) - waitForSealedSecretsOperator(t, defaultKindCluster2) + waitForSealedSecretsOperator(t, defaultCluster2) p := prepareSealTest(t, k, namespace, map[string]string{ @@ -293,13 +293,13 @@ func TestSeal_MultipleTargets(t *testing.T) { }) addSecretsSetToTarget(p, "test-target2", "test2") - p.mergeKubeconfig(defaultKindCluster2) - createNamespace(t, defaultKindCluster2, namespace) + p.mergeKubeconfig(defaultCluster2) + createNamespace(t, defaultCluster2, namespace) p.updateTarget("test-target", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultKindCluster1.Context, "context") + _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.updateTarget("test-target2", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultKindCluster2.Context, "context") + _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("seal", "-t", "test-target") @@ -317,8 +317,8 @@ func TestSeal_MultipleTargets(t *testing.T) { "s1": "v1", "s2": "v2", }) - waitForReadiness(t, defaultKindCluster2, namespace, "secret/secret", 1*time.Minute) - assertDecryptedSecrets(t, defaultKindCluster2, namespace, "secret", map[string]string{ + waitForReadiness(t, defaultCluster2, namespace, "secret/secret", 1*time.Minute) + assertDecryptedSecrets(t, defaultCluster2, namespace, "secret", map[string]string{ "s1": "v3", "s2": "v4", }) @@ -327,7 +327,7 @@ func TestSeal_MultipleTargets(t *testing.T) { func TestSeal_File(t *testing.T) { t.Parallel() - k := defaultKindCluster1 + k := defaultCluster1 namespace := "seal-file" waitForSealedSecretsOperator(t, k) @@ -372,13 +372,13 @@ func TestSeal_File(t *testing.T) { func TestSeal_Vault(t *testing.T) { t.Parallel() - k := defaultKindCluster1 + k := defaultCluster1 namespace := "seal-vault" waitForSealedSecretsOperator(t, k) waitForVault(t, k) - u, err := url.Parse(defaultKindCluster1.RESTConfig().Host) + u, err := url.Parse(defaultCluster1.RESTConfig().Host) if err != nil { t.Fatal(err) } From dfc44a928a70d693b336663700d8c4f88b4eff78 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 12:16:24 +0200 Subject: [PATCH 0390/2268] tests: Enforce use of modified kubeconfig in Kubectl calls --- internal/test-utils/envtest_cluster.go | 30 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go index 1151e87a7..eba311716 100644 --- a/internal/test-utils/envtest_cluster.go +++ b/internal/test-utils/envtest_cluster.go @@ -2,10 +2,13 @@ package test_utils import ( "bytes" + "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "io" "k8s.io/client-go/rest" + "os" + "os/exec" "sigs.k8s.io/controller-runtime/pkg/envtest" "testing" ) @@ -46,7 +49,7 @@ func CreateEnvTestCluster(context string) (*EnvTestCluster, error) { if err != nil { return nil, err } - + kcfg = bytes.ReplaceAll(kcfg, []byte("envtest"), []byte(context)) k.Kubeconfig = kcfg @@ -61,14 +64,31 @@ func (c *EnvTestCluster) RESTConfig() *rest.Config { } func (c *EnvTestCluster) Kubectl(args ...string) (string, string, error) { - kctl, err := c.user.Kubectl() + tmp, err := os.CreateTemp("", "") if err != nil { return "", "", err } + defer func() { + tmp.Close() + os.Remove(tmp.Name()) + }() + + _, err = tmp.Write(c.Kubeconfig) + if err != nil { + return "", "", err + } + + stdoutBuffer := &bytes.Buffer{} + stderrBuffer := &bytes.Buffer{} + allArgs := append([]string{fmt.Sprintf("--kubeconfig=%s", tmp.Name())}, args...) + + cmd := exec.Command(c.env.ControlPlane.KubectlPath, allArgs...) + cmd.Stdout = stdoutBuffer + cmd.Stderr = stderrBuffer - stdout1, stderr1, err := kctl.Run(args...) - stdout, _ := io.ReadAll(stdout1) - stderr, _ := io.ReadAll(stderr1) + err = cmd.Run() + stdout, _ := io.ReadAll(stdoutBuffer) + stderr, _ := io.ReadAll(stderrBuffer) return string(stdout), string(stderr), err } From 15ba31bab821aef6ae4b359d8312b70108c24934 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 23:35:44 +0200 Subject: [PATCH 0391/2268] tests: Implement webhook callbacks in EnvTestCluster --- internal/test-utils/envtest_cluster.go | 46 +++++--- .../test-utils/envtest_cluster_callback.go | 109 ++++++++++++++++++ 2 files changed, 141 insertions(+), 14 deletions(-) create mode 100644 internal/test-utils/envtest_cluster_callback.go diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go index eba311716..7683f8079 100644 --- a/internal/test-utils/envtest_cluster.go +++ b/internal/test-utils/envtest_cluster.go @@ -2,6 +2,7 @@ package test_utils import ( "bytes" + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -10,52 +11,69 @@ import ( "os" "os/exec" "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/webhook" "testing" ) type EnvTestCluster struct { - env envtest.Environment + env envtest.Environment + started bool user *envtest.AuthenticatedUser Kubeconfig []byte Context string config *rest.Config + + callbackServer webhook.Server + callbackServerStop context.CancelFunc } -func CreateEnvTestCluster(context string) (*EnvTestCluster, error) { +func CreateEnvTestCluster(context string) *EnvTestCluster { k := &EnvTestCluster{ Context: context, } + return k +} +func (k *EnvTestCluster) Start() error { _, err := k.env.Start() if err != nil { - return nil, err + return err } + k.started = true - isOk := false - defer func() { - if !isOk { - _ = k.env.Stop() - } - }() + err = k.startCallbackServer() + if err != nil { + return err + } user, err := k.env.AddUser(envtest.User{Name: "default", Groups: []string{"system:masters"}}, &rest.Config{}) if err != nil { - return nil, err + return err } k.user = user kcfg, err := user.KubeConfig() if err != nil { - return nil, err + return err } - kcfg = bytes.ReplaceAll(kcfg, []byte("envtest"), []byte(context)) + kcfg = bytes.ReplaceAll(kcfg, []byte("envtest"), []byte(k.Context)) k.Kubeconfig = kcfg - isOk = true - return k, nil + return nil +} + +func (c *EnvTestCluster) Stop() { + if c.started { + _ = c.env.Stop() + c.started = false + } + if c.callbackServerStop != nil { + c.callbackServerStop() + c.callbackServerStop = nil + } } // RESTConfig returns K8s client config to pass to clientset objects diff --git a/internal/test-utils/envtest_cluster_callback.go b/internal/test-utils/envtest_cluster_callback.go new file mode 100644 index 000000000..2ec870b3c --- /dev/null +++ b/internal/test-utils/envtest_cluster_callback.go @@ -0,0 +1,109 @@ +package test_utils + +import ( + "context" + "fmt" + "github.com/go-logr/logr" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + admissionv1 "k8s.io/api/admissionregistration/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "net/http" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +type CallbackHandler func(o *uo.UnstructuredObject) + +func (k *EnvTestCluster) buildServeCallback(gvr schema.GroupVersionResource, cb CallbackHandler) http.Handler { + wh := &webhook.Admission{ + Handler: admission.HandlerFunc(func(ctx context.Context, request admission.Request) admission.Response { + k.serveCallback(gvr, request, cb) + return admission.Allowed("") + }), + } + _ = wh.InjectLogger(logr.New(log.NullLogSink{})) + return wh +} + +func (k *EnvTestCluster) serveCallback(gvr schema.GroupVersionResource, request admission.Request, cb CallbackHandler) { + o, err := uo.FromString(string(request.Object.Raw)) + if err != nil { + panic(err) + } + + cb(o) +} + +func (k *EnvTestCluster) startCallbackServer() error { + k.callbackServer.Host = k.env.WebhookInstallOptions.LocalServingHost + k.callbackServer.Port = k.env.WebhookInstallOptions.LocalServingPort + k.callbackServer.CertDir = k.env.WebhookInstallOptions.LocalServingCertDir + + ctx, cancel := context.WithCancel(context.Background()) + k.callbackServerStop = cancel + + go func() { + _ = k.callbackServer.Start(ctx) + }() + + return nil +} + +func (k *EnvTestCluster) AddWebhookCallback(gvr schema.GroupVersionResource, isNamespaced bool, cb CallbackHandler) { + scope := admissionv1.ClusterScope + if isNamespaced { + scope = admissionv1.NamespacedScope + } + + failedTypeV1 := admissionv1.Fail + equivalentTypeV1 := admissionv1.Equivalent + noSideEffectsV1 := admissionv1.SideEffectClassNone + timeout := int32(30) + + group := gvr.Group + if gvr.Group == "" { + group = "none" + } + name := fmt.Sprintf("%s-%s-%s-callback", group, gvr.Version, gvr.Resource) + path := "/" + name + + k.env.WebhookInstallOptions.ValidatingWebhooks = append(k.env.WebhookInstallOptions.ValidatingWebhooks, &admissionv1.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "ValidatingWebhookConfiguration", + APIVersion: "admissionregistration.k8s.io/v1", + }, + Webhooks: []admissionv1.ValidatingWebhook{ + { + Name: fmt.Sprintf("%s.callback.kubectl.io", name), + Rules: []admissionv1.RuleWithOperations{ + { + Operations: []admissionv1.OperationType{"CREATE", "UPDATE"}, + Rule: admissionv1.Rule{ + APIGroups: []string{gvr.Group}, + APIVersions: []string{gvr.Version}, + Resources: []string{gvr.Resource}, + Scope: &scope, + }, + }, + }, + FailurePolicy: &failedTypeV1, + MatchPolicy: &equivalentTypeV1, + SideEffects: &noSideEffectsV1, + ClientConfig: admissionv1.WebhookClientConfig{ + Service: &admissionv1.ServiceReference{ + Path: &name, + }, + }, + AdmissionReviewVersions: []string{"v1"}, + TimeoutSeconds: &timeout, + }, + }, + }) + + k.callbackServer.Register(path, k.buildServeCallback(gvr, cb)) +} From c3daf546e27dc3aff0a2dee6a85ebe04572bb4cc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 23:36:08 +0200 Subject: [PATCH 0392/2268] tests: Simplify hook tests by using webhook callbacks --- e2e/default_clusters.go | 9 +- e2e/hooks_test.go | 268 +++++++++++++++------------------------- 2 files changed, 107 insertions(+), 170 deletions(-) diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 1d632c7b4..b65f26b6d 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -5,7 +5,8 @@ import ( "sync" ) -var defaultCluster1, defaultCluster2 *test_utils.EnvTestCluster +var defaultCluster1 = test_utils.CreateEnvTestCluster("cluster1") +var defaultCluster2 = test_utils.CreateEnvTestCluster("cluster2") var defaultKindCluster1VaultPort, defaultKindCluster2VaultPort int func init() { @@ -13,16 +14,14 @@ func init() { wg.Add(2) go func() { defer wg.Done() - var err error - defaultCluster1, err = test_utils.CreateEnvTestCluster("cluster1") + err := defaultCluster1.Start() if err != nil { panic(err) } }() go func() { defer wg.Done() - var err error - defaultCluster2, err = test_utils.CreateEnvTestCluster("cluster2") + err := defaultCluster2.Start() if err != nil { panic(err) } diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 395640e7b..f860553ed 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -1,27 +1,58 @@ package e2e import ( - "encoding/base64" "fmt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "k8s.io/apimachinery/pkg/runtime/schema" "testing" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) -const hookScript = ` -kubectl get configmap -oyaml > /tmp/result.yml -cat /tmp/result.yml -if ! kubectl get secret {{ .name }}-result; then - name="{{ .name }}-result" -else - name="{{ .name }}-result2" -fi -kubectl create secret generic $name --from-file=result=/tmp/result.yml -kubectl delete configmap cm2 || true -` - -func addHookDeployment(p *testProject, dir string, opts resourceOpts, isHelm bool, hook string, hookDeletionPolicy string) { +type HookTestSuite struct { + suite.Suite + + k *test_utils.EnvTestCluster + + seenConfigMaps []string +} + +func TestHooks(t *testing.T) { + suite.Run(t, &HookTestSuite{}) +} + +func (s *HookTestSuite) SetupSuite() { + s.clearSeenConfigmaps() + + s.k = test_utils.CreateEnvTestCluster("cluster1") + s.k.AddWebhookCallback(schema.GroupVersionResource{ + Version: "v1", Resource: "configmaps", + }, true, s.handleConfigmap) + err := s.k.Start() + if err != nil { + s.T().Fatal(err) + } +} + +func (s *HookTestSuite) TearDownSuite() { + s.k.Stop() +} + +func (s *HookTestSuite) SetupTest() { + s.clearSeenConfigmaps() +} + +func (s *HookTestSuite) handleConfigmap(o *uo.UnstructuredObject) { + s.seenConfigMaps = append(s.seenConfigMaps, o.GetK8sName()) +} + +func (s *HookTestSuite) clearSeenConfigmaps() { + s.seenConfigMaps = nil +} + +func (s *HookTestSuite) addHookConfigMap(p *testProject, dir string, opts resourceOpts, isHelm bool, hook string, hookDeletionPolicy string) { annotations := make(map[string]string) if isHelm { annotations["helm.sh/hook"] = hook @@ -35,17 +66,12 @@ func addHookDeployment(p *testProject, dir string, opts resourceOpts, isHelm boo annotations["kluctl.io/hook-deletion-policy"] = hookDeletionPolicy } - script := renderTemplateHelper(hookScript, map[string]interface{}{ - "name": opts.name, - "namespace": opts.namespace, - }) - opts.annotations = uo.CopyMergeStrMap(opts.annotations, annotations) - addJobDeployment(p, dir, opts, "bitnami/kubectl:1.21", []string{"sh"}, []string{"-c", script}) + s.addConfigMap(p, dir, opts) } -func addConfigMap(p *testProject, dir string, opts resourceOpts) { +func (s *HookTestSuite) addConfigMap(p *testProject, dir string, opts resourceOpts) { o := uo.New() o.SetK8sGVKs("", "v1", "ConfigMap") mergeMetadata(o, opts) @@ -55,203 +81,115 @@ func addConfigMap(p *testProject, dir string, opts resourceOpts) { }) } -func getHookResult(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, secretName string) *uo.UnstructuredObject { - o := k.KubectlYamlMust(t, "-n", p.projectName, "get", "secret", secretName) - s, ok, err := o.GetNestedString("data", "result") - if err != nil { - t.Fatal(err) - } - if !ok { - t.Fatalf("result not found") - } - b, err := base64.StdEncoding.DecodeString(s) - if err != nil { - t.Fatal(err) - } - r, err := uo.FromString(string(b)) - if err != nil { - t.Fatal(err) - } - return r -} - -func getHookResultCMNames(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, second bool) []string { - secretName := "hook-result" - if second { - secretName = "hook-result2" - } - o := getHookResult(t, p, k, secretName) - items, _, _ := o.GetNestedObjectList("items") - var names []string - for _, x := range items { - names = append(names, x.GetK8sName()) - } - return names -} - -func assertHookResultCMName(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, second bool, cmName string) { - names := getHookResultCMNames(t, p, k, second) - for _, x := range names { - if x == cmName { - return - } - } - t.Fatalf("%s not found in hook result", cmName) -} - -func assertHookResultNotCMName(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, second bool, cmName string) { - names := getHookResultCMNames(t, p, k, second) - for _, x := range names { - if x == cmName { - t.Fatalf("%s found in hook result", cmName) - } - } -} - -func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) (*testProject, *test_utils.EnvTestCluster) { +func (s *HookTestSuite) prepareHookTestProject(name string, hook string, hookDeletionPolicy string) *testProject { isDone := false namespace := fmt.Sprintf("hook-%s", name) - k := defaultCluster1 p := &testProject{} - p.init(t, k, namespace) + p.init(s.T(), s.k, namespace) defer func() { if !isDone { p.cleanup() } }() - createNamespace(t, k, namespace) + createNamespace(s.T(), s.k, namespace) p.updateTarget("test", nil) - addHookDeployment(p, "hook", resourceOpts{name: "hook", namespace: namespace}, false, hook, hookDeletionPolicy) - addConfigMap(p, "hook", resourceOpts{name: "cm1", namespace: namespace}) + p.addKustomizeDeployment("hook", nil, nil) + + s.addConfigMap(p, "hook", resourceOpts{name: "cm1", namespace: namespace}) + s.addHookConfigMap(p, "hook", resourceOpts{name: "hook1", namespace: namespace}, false, hook, hookDeletionPolicy) isDone = true - return p, k + return p } -func ensureHookExecuted(t *testing.T, p *testProject, k *test_utils.EnvTestCluster) { - _, _, _ = k.Kubectl("delete", "-n", p.projectName, "secret", "hook-result", "hook-result2") +func (s *HookTestSuite) ensureHookExecuted(p *testProject, expectedCms ...string) { + s.clearSeenConfigmaps() p.KluctlMust("deploy", "--yes", "-t", "test") - assertResourceExists(t, k, p.projectName, "ConfigMap/cm1") + assert.Equal(s.T(), expectedCms, s.seenConfigMaps) } -func ensureHookNotExecuted(t *testing.T, p *testProject, k *test_utils.EnvTestCluster) { - _, _, _ = k.Kubectl("delete", "-n", p.projectName, "secret", "hook-result", "hook-result2") +func (s *HookTestSuite) ensureHookNotExecuted(p *testProject) { + _, _, _ = s.k.Kubectl("delete", "-n", p.projectName, "configmaps", "cm1") p.KluctlMust("deploy", "--yes", "-t", "test") - assertResourceNotExists(t, k, p.projectName, "Secret/hook-result") + assertResourceNotExists(s.T(), s.k, p.projectName, "ConfigMap/cm1") } -func TestHooksPreDeployInitial(t *testing.T) { - t.Parallel() - p, k := prepareHookTestProject(t, "pre-deploy-initial", "pre-deploy-initial", "") +func (s *HookTestSuite) TestHooksPreDeployInitial() { + p := s.prepareHookTestProject("pre-deploy-initial", "pre-deploy-initial", "") defer p.cleanup() - ensureHookExecuted(t, p, k) - assertHookResultNotCMName(t, p, k, false, "cm1") - ensureHookNotExecuted(t, p, k) + s.ensureHookExecuted(p, "hook1", "cm1") + s.ensureHookExecuted(p, "cm1") } -func TestHooksPostDeployInitial(t *testing.T) { - t.Parallel() - p, k := prepareHookTestProject(t, "post-deploy-initial", "post-deploy-initial", "") +func (s *HookTestSuite) TestHooksPostDeployInitial() { + p := s.prepareHookTestProject("post-deploy-initial", "post-deploy-initial", "") defer p.cleanup() - ensureHookExecuted(t, p, k) - assertHookResultCMName(t, p, k, false, "cm1") - ensureHookNotExecuted(t, p, k) + s.ensureHookExecuted(p, "cm1", "hook1") + s.ensureHookExecuted(p, "cm1") } -func TestHooksPreDeployUpgrade(t *testing.T) { - t.Parallel() - p, k := prepareHookTestProject(t, "pre-deploy-upgrade", "pre-deploy-upgrade", "") +func (s *HookTestSuite) TestHooksPreDeployUpgrade() { + p := s.prepareHookTestProject("pre-deploy-upgrade", "pre-deploy-upgrade", "") defer p.cleanup() - addConfigMap(p, "hook", resourceOpts{name: "cm2", namespace: p.projectName}) - ensureHookNotExecuted(t, p, k) - k.KubectlMust(t, "delete", "-n", p.projectName, "configmap", "cm1") - ensureHookExecuted(t, p, k) - assertHookResultNotCMName(t, p, k, false, "cm1") - ensureHookExecuted(t, p, k) - assertHookResultCMName(t, p, k, false, "cm1") + s.ensureHookExecuted(p, "cm1") + s.ensureHookExecuted(p, "hook1", "cm1") + s.ensureHookExecuted(p, "hook1", "cm1") } -func TestHooksPostDeployUpgrade(t *testing.T) { - t.Parallel() - p, k := prepareHookTestProject(t, "post-deploy-upgrade", "post-deploy-upgrade", "") +func (s *HookTestSuite) TestHooksPostDeployUpgrade() { + p := s.prepareHookTestProject("post-deploy-upgrade", "post-deploy-upgrade", "") defer p.cleanup() - addConfigMap(p, "hook", resourceOpts{name: "cm2", namespace: p.projectName}) - ensureHookNotExecuted(t, p, k) - k.KubectlMust(t, "delete", "-n", p.projectName, "configmap", "cm1") - ensureHookExecuted(t, p, k) - assertHookResultCMName(t, p, k, false, "cm1") + s.ensureHookExecuted(p, "cm1") + s.ensureHookExecuted(p, "cm1", "hook1") + s.ensureHookExecuted(p, "cm1", "hook1") } -func doTestHooksPreDeploy(t *testing.T, name string, hooks string) { - p, k := prepareHookTestProject(t, name, hooks, "") +func (s *HookTestSuite) doTestHooksPreDeploy(name string, hooks string) { + p := s.prepareHookTestProject(name, hooks, "") defer p.cleanup() - addConfigMap(p, "hook", resourceOpts{name: "cm2", namespace: p.projectName}) - ensureHookExecuted(t, p, k) - k.KubectlMust(t, "delete", "-n", p.projectName, "configmap", "cm1") - ensureHookExecuted(t, p, k) - assertHookResultNotCMName(t, p, k, false, "cm1") - ensureHookExecuted(t, p, k) - assertHookResultCMName(t, p, k, false, "cm1") + s.ensureHookExecuted(p, "hook1", "cm1") + s.ensureHookExecuted(p, "hook1", "cm1") } -func doTestHooksPostDeploy(t *testing.T, name string, hooks string) { - p, k := prepareHookTestProject(t, name, hooks, "") +func (s *HookTestSuite) doTestHooksPostDeploy(name string, hooks string) { + p := s.prepareHookTestProject(name, hooks, "") defer p.cleanup() - addConfigMap(p, "hook", resourceOpts{name: "cm2", namespace: p.projectName}) - ensureHookExecuted(t, p, k) - k.KubectlMust(t, "delete", "-n", p.projectName, "configmap", "cm1") - ensureHookExecuted(t, p, k) - assertHookResultCMName(t, p, k, false, "cm1") + s.ensureHookExecuted(p, "cm1", "hook1") + s.ensureHookExecuted(p, "cm1", "hook1") } -func doTestHooksPrePostDeploy(t *testing.T, name string, hooks string) { - p, k := prepareHookTestProject(t, name, hooks, "") +func (s *HookTestSuite) doTestHooksPrePostDeploy(name string, hooks string) { + p := s.prepareHookTestProject(name, hooks, "") defer p.cleanup() - addConfigMap(p, "hook", resourceOpts{name: "cm2", namespace: p.projectName}) - ensureHookExecuted(t, p, k) - assertHookResultNotCMName(t, p, k, false, "cm1") - assertHookResultNotCMName(t, p, k, false, "cm2") - assertHookResultCMName(t, p, k, true, "cm1") - assertHookResultCMName(t, p, k, true, "cm2") - - ensureHookExecuted(t, p, k) - assertHookResultCMName(t, p, k, false, "cm1") - assertHookResultNotCMName(t, p, k, false, "cm2") - assertHookResultCMName(t, p, k, true, "cm1") - assertHookResultCMName(t, p, k, true, "cm2") + s.ensureHookExecuted(p, "hook1", "cm1", "hook1") + s.ensureHookExecuted(p, "hook1", "cm1", "hook1") } -func TestHooksPreDeploy(t *testing.T) { - t.Parallel() - doTestHooksPreDeploy(t, "pre-deploy", "pre-deploy") +func (s *HookTestSuite) TestHooksPreDeploy() { + s.doTestHooksPreDeploy("pre-deploy", "pre-deploy") } -func TestHooksPreDeploy2(t *testing.T) { - t.Parallel() +func (s *HookTestSuite) TestHooksPreDeploy2() { // same as pre-deploy - doTestHooksPreDeploy(t, "pre-deploy2", "pre-deploy-initial,pre-deploy-upgrade") + s.doTestHooksPreDeploy("pre-deploy2", "pre-deploy-initial,pre-deploy-upgrade") } -func TestHooksPostDeploy(t *testing.T) { - t.Parallel() - doTestHooksPostDeploy(t, "post-deploy", "post-deploy") +func (s *HookTestSuite) TestHooksPostDeploy() { + s.doTestHooksPostDeploy("post-deploy", "post-deploy") } -func TestHooksPostDeploy2(t *testing.T) { - t.Parallel() +func (s *HookTestSuite) TestHooksPostDeploy2() { // same as post-deploy - doTestHooksPostDeploy(t, "post-deploy2", "post-deploy-initial,post-deploy-upgrade") + s.doTestHooksPostDeploy("post-deploy2", "post-deploy-initial,post-deploy-upgrade") } -func TestHooksPrePostDeploy(t *testing.T) { - t.Parallel() - doTestHooksPrePostDeploy(t, "pre-post-deploy", "pre-deploy,post-deploy") +func (s *HookTestSuite) TestHooksPrePostDeploy() { + s.doTestHooksPrePostDeploy("pre-post-deploy", "pre-deploy,post-deploy") } -func TestHooksPrePostDeploy2(t *testing.T) { - t.Parallel() - doTestHooksPrePostDeploy(t, "pre-post-deploy2", "pre-deploy-initial,pre-deploy-upgrade,post-deploy-initial,post-deploy-upgrade") +func (s *HookTestSuite) TestHooksPrePostDeploy2() { + s.doTestHooksPrePostDeploy("pre-post-deploy2", "pre-deploy-initial,pre-deploy-upgrade,post-deploy-initial,post-deploy-upgrade") } From 3cab5788e1ce77caa7c5523166ba617700169628 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 29 Sep 2022 23:36:48 +0200 Subject: [PATCH 0393/2268] fix: Use background instead of forground deletion --- pkg/k8s/k8s_cluster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index f8e8a07a7..1656e080d 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -236,7 +236,7 @@ type DeleteOptions struct { func (k *K8sCluster) DeleteSingleObject(ref k8s.ObjectRef, options DeleteOptions) ([]ApiWarning, error) { dryRun := k.DryRun || options.ForceDryRun - pp := v1.DeletePropagationForeground + pp := v1.DeletePropagationBackground o := v1.DeleteOptions{ PropagationPolicy: &pp, } From c320b15d4314514af78da85ff1da81ce3579d40e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 30 Sep 2022 15:13:48 +0200 Subject: [PATCH 0394/2268] tests: Simplify sealing tests and remove the need to install a real sealed-secrets controller --- e2e/default_clusters.go | 1 - e2e/project.go | 13 +- e2e/seal_test.go | 377 ++++++++++++++----------- e2e/test_resources/README.md | 6 - e2e/test_resources/vault-values.yaml | 10 - e2e/test_resources/vault.yaml | 235 --------------- e2e/utils_resources.go | 95 ------- go.mod | 7 +- go.sum | 2 + internal/test-utils/envtest_cluster.go | 8 +- pkg/k8s/k8s_cluster.go | 42 ++- pkg/seal/fetch_cert.go | 11 + pkg/seal/sealer.go | 10 +- 13 files changed, 294 insertions(+), 523 deletions(-) delete mode 100644 e2e/test_resources/vault-values.yaml delete mode 100644 e2e/test_resources/vault.yaml diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index b65f26b6d..3d0f10fd8 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -7,7 +7,6 @@ import ( var defaultCluster1 = test_utils.CreateEnvTestCluster("cluster1") var defaultCluster2 = test_utils.CreateEnvTestCluster("cluster2") -var defaultKindCluster1VaultPort, defaultKindCluster2VaultPort int func init() { var wg sync.WaitGroup diff --git a/e2e/project.go b/e2e/project.go index 39996fa5b..e106c795a 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -2,13 +2,6 @@ package e2e import ( "fmt" - "os" - "os/exec" - "path/filepath" - "reflect" - "strings" - "testing" - "github.com/go-git/go-git/v5" "github.com/imdario/mergo" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" @@ -16,6 +9,12 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "os" + "os/exec" + "path/filepath" + "reflect" + "strings" + "testing" ) type testProject struct { diff --git a/e2e/seal_test.go b/e2e/seal_test.go index f74a7544a..531fef2c1 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -1,82 +1,154 @@ package e2e import ( - "encoding/base64" + "crypto/x509" + "encoding/pem" "fmt" + "github.com/bitnami-labs/sealed-secrets/pkg/crypto" "github.com/kluctl/kluctl/v2/e2e/test_resources" "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/pkg/seal" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/stretchr/testify/assert" - "io/ioutil" + "github.com/stretchr/testify/suite" + certUtil "k8s.io/client-go/util/cert" + "k8s.io/client-go/util/keyutil" + "net" "net/http" - "net/url" + "net/http/httptest" "path/filepath" - "strings" - "sync" "testing" "time" ) -func installSealedSecretsOperator(k *test_utils.EnvTestCluster) { - test_resources.ApplyYaml("sealed-secrets.yaml", k) +type SealedSecretsTestSuite struct { + suite.Suite + + k *test_utils.EnvTestCluster + k2 *test_utils.EnvTestCluster + + certServer1 *certServer + certServer2 *certServer } -func waitForSealedSecretsOperator(t *testing.T, k *test_utils.EnvTestCluster) { - waitForReadiness(t, k, "kube-system", "deployment/sealed-secrets-controller", 5*time.Minute) +type certServer struct { + server http.Server + url string + certHash string } -func deleteSealedSecretsOperator(k *test_utils.EnvTestCluster) { - test_resources.DeleteYaml("sealed-secrets.yaml", k) - _, _, _ = k.Kubectl("-n", "kube-system", "delete", "secret", "-l", "sealedsecrets.bitnami.com/sealed-secrets-key", "--wait") - _, _, _ = k.Kubectl("-n", "kube-system", "delete", "configmap", "sealed-secrets-key-kluctl-bootstrap", "--wait") +func TestSealedSecrets(t *testing.T) { + suite.Run(t, &SealedSecretsTestSuite{}) } -func installVault(k *test_utils.EnvTestCluster) { - _, _, _ = k.Kubectl("create", "ns", "vault") - test_resources.ApplyYaml("vault.yaml", k) +func (s *SealedSecretsTestSuite) SetupSuite() { + s.k = defaultCluster1 + s.k2 = defaultCluster2 + + test_resources.ApplyYaml("sealed-secrets.yaml", s.k) + test_resources.ApplyYaml("sealed-secrets.yaml", s.k2) + + var err error + s.certServer1, err = s.startCertServer() + if err != nil { + s.T().Fatal(err) + } + s.certServer2, err = s.startCertServer() + if err != nil { + s.T().Fatal(err) + } } -func waitForVault(t *testing.T, k *test_utils.EnvTestCluster) { - waitForReadiness(t, k, "vault", "statefulset/vault", 5*time.Minute) +func (s *SealedSecretsTestSuite) TearDownSuite() { + s.k = nil + s.k2 = nil + + if s.certServer1 != nil { + s.certServer1.server.Close() + } + if s.certServer2 != nil { + s.certServer2.server.Close() + } + s.certServer1 = nil + s.certServer2 = nil } -func init() { - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() - installSealedSecretsOperator(defaultCluster1) - installVault(defaultCluster1) - }() +func (s *SealedSecretsTestSuite) startCertServer() (*certServer, error) { + key, cert, err := crypto.GeneratePrivateKeyAndCert(2048, 10*365*24*time.Hour, "tests.kluctl.io") + if err != nil { + return nil, err + } + + certbytes := []byte{} + certbytes = append(certbytes, pem.EncodeToMemory(&pem.Block{Type: certUtil.CertificateBlockType, Bytes: cert.Raw})...) + keybytes := pem.EncodeToMemory(&pem.Block{Type: keyutil.RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(key)}) + _ = keybytes + l, err := net.Listen("tcp", "") + if err != nil { + return nil, err + } + + mux := http.NewServeMux() + mux.Handle("/v1/cert.pem", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/x-pem-file") + _, _ = w.Write(certbytes) + })) + + certUrl := fmt.Sprintf("http://localhost:%d", l.Addr().(*net.TCPAddr).Port) + + var cs certServer + cs.url = certUrl + cs.server.Handler = mux + cs.certHash, err = seal.HashPublicKey(cert) + if err != nil { + return nil, err + } + go func() { - defer wg.Done() - installSealedSecretsOperator(defaultCluster2) + _ = cs.server.Serve(l) }() - wg.Wait() + + return &cs, nil +} + +func (s *SealedSecretsTestSuite) addProxyVars(p *testProject) { + f := func(idx int, k *test_utils.EnvTestCluster, cs *certServer) { + p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_API_HOST=%s", idx, k.RESTConfig().Host)) + p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_NAMESPACE=%s", idx, "kube-system")) + p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_NAME=%s", idx, "sealed-secrets-controller")) + p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_PORT=%s", idx, "http")) + p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_LOCAL_URL=%s", idx, cs.url)) + } + f(0, s.k, s.certServer1) + f(1, s.k2, s.certServer2) } -func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject) *testProject { +func (s *SealedSecretsTestSuite) prepareSealTest(k *test_utils.EnvTestCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *testProject { p := &testProject{} - p.init(t, k, fmt.Sprintf("seal-%s", namespace)) + p.init(s.T(), k, fmt.Sprintf("seal-%s", namespace)) + if proxy { + s.addProxyVars(p) + } - createNamespace(t, k, namespace) + createNamespace(s.T(), k, namespace) - addSecretsSet(p, "test", varsSources) - addSecretsSetToTarget(p, "test-target", "test") + s.addSecretsSet(p, "test", varsSources) + s.addSecretsSetToTarget(p, "test-target", "test") addSecretDeployment(p, "secret-deployment", secrets, true, resourceOpts{name: "secret", namespace: namespace}) return p } -func addSecretsSet(p *testProject, name string, varsSources []*uo.UnstructuredObject) { +func (s *SealedSecretsTestSuite) addSecretsSet(p *testProject, name string, varsSources []*uo.UnstructuredObject) { p.updateSecretSet(name, func(secretSet *uo.UnstructuredObject) { _ = secretSet.SetNestedField(varsSources, "vars") }) } -func addSecretsSetToTarget(p *testProject, targetName string, secretSetName string) { +func (s *SealedSecretsTestSuite) addSecretsSetToTarget(p *testProject, targetName string, secretSetName string) { p.updateTarget(targetName, func(target *uo.UnstructuredObject) { l, _, _ := target.GetNestedList("sealingConfig", "secretSets") l = append(l, secretSetName) @@ -84,23 +156,37 @@ func addSecretsSetToTarget(p *testProject, targetName string, secretSetName stri }) } -func assertDecryptedSecrets(t *testing.T, k *test_utils.EnvTestCluster, namespace string, secretName string, expectedSecrets map[string]string) { - s := k.KubectlYamlMust(t, "-n", namespace, "get", "secret", secretName) +func (s *SealedSecretsTestSuite) assertSealedSecret(k *test_utils.EnvTestCluster, namespace string, secretName string, expectedCertHash string, expectedSecrets map[string]string) { + y := k.KubectlYamlMust(s.T(), "-n", namespace, "get", "sealedsecret", secretName) + + h1 := y.GetK8sAnnotation("kluctl.io/sealedsecret-cert-hash") + if h1 == nil { + s.T().Fatal("kluctl.io/sealedsecret-cert-hash annotation not found") + } + s.Assertions.Equal(expectedCertHash, *h1) + + hashesStr := y.GetK8sAnnotation("kluctl.io/sealedsecret-hashes") + if hashesStr == nil { + s.T().Fatal("kluctl.io/sealedsecret-hashes annotation not found") + } + hashes, err := uo.FromString(*hashesStr) + if err != nil { + s.T().Fatal(err) + } - for key, value := range expectedSecrets { - x, _, _ := s.GetNestedString("data", key) - decoded, _ := base64.StdEncoding.DecodeString(x) - assert.Equal(t, value, string(decoded)) + expectedHashes := map[string]any{} + for k, v := range expectedSecrets { + expectedHashes[k] = seal.HashSecret(k, []byte(v), secretName, namespace, "strict") } + + s.Assertions.Equal(expectedHashes, hashes.Object) } -func TestSeal_WithOperator(t *testing.T) { - k := defaultCluster1 +func (s *SealedSecretsTestSuite) TestSeal_WithOperator() { + k := s.k namespace := "seal-with-operator" - waitForSealedSecretsOperator(t, k) - - p := prepareSealTest(t, k, namespace, + p := s.prepareSealTest(k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -112,34 +198,29 @@ func TestSeal_WithOperator(t *testing.T) { "s2": "v2", }, }), - }, - ) + }, true) defer p.cleanup() p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) - assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - - waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) - assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func TestSeal_WithBootstrap(t *testing.T) { - k := defaultCluster2 +func (s *SealedSecretsTestSuite) TestSeal_WithBootstrap() { + k := s.k namespace := "seal-with-bootstrap" - // we still wait for it to be ready before we then delete it - // this way it's pre-pulled and pre-warmed when we later start it - waitForSealedSecretsOperator(t, k) - deleteSealedSecretsOperator(k) + // deleting the crd causes kluctl to not recognize the operator, so it will do a bootstrap + k.KubectlMust(s.T(), "delete", "crd", "sealedsecrets.bitnami.com") - p := prepareSealTest(t, k, namespace, + p := s.prepareSealTest(k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -151,36 +232,40 @@ func TestSeal_WithBootstrap(t *testing.T) { "s2": "v2", }, }), - }, - ) + }, false) + defer p.cleanup() p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) - assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) - installSealedSecretsOperator(k) - waitForSealedSecretsOperator(t, k) + test_resources.ApplyYaml("sealed-secrets.yaml", k) p.KluctlMust("deploy", "--yes", "-t", "test-target") - waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) - assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + pkCm := k.KubectlYamlMust(s.T(), "-n", "kube-system", "get", "cm", "sealed-secrets-key-kluctl-bootstrap") + certBytes, ok, _ := pkCm.GetNestedString("data", "tls.crt") + s.Assertions.True(ok) + + cert, err := seal.ParseCert([]byte(certBytes)) + s.Assertions.NoError(err) + + certHash, err := seal.HashPublicKey(cert) + s.Assertions.NoError(err) + + s.assertSealedSecret(k, namespace, "secret", certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func TestSeal_MultipleVarSources(t *testing.T) { - t.Parallel() - - k := defaultCluster1 +func (s *SealedSecretsTestSuite) TestSeal_MultipleVarSources() { + k := s.k namespace := "seal-multiple-vs" - waitForSealedSecretsOperator(t, k) - - p := prepareSealTest(t, k, namespace, + p := s.prepareSealTest(k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -196,33 +281,27 @@ func TestSeal_MultipleVarSources(t *testing.T) { "s2": "v2", }, }), - }, - ) + }, true) defer p.cleanup() p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) - assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) - assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func TestSeal_MultipleSecretSets(t *testing.T) { - t.Parallel() - - k := defaultCluster1 +func (s *SealedSecretsTestSuite) TestSeal_MultipleSecretSets() { + k := s.k namespace := "seal-multiple-ss" - waitForSealedSecretsOperator(t, k) - - p := prepareSealTest(t, k, namespace, + p := s.prepareSealTest(k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -233,41 +312,37 @@ func TestSeal_MultipleSecretSets(t *testing.T) { "s1": "v1", }, }), - }, - ) + }, true) + defer p.cleanup() - addSecretsSet(p, "test2", []*uo.UnstructuredObject{ + s.addSecretsSet(p, "test2", []*uo.UnstructuredObject{ uo.FromMap(map[string]interface{}{ "values": map[string]interface{}{ "s2": "v2", }, }), }) - addSecretsSetToTarget(p, "test-target", "test2") + s.addSecretsSetToTarget(p, "test-target", "test2") p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) - assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) - assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func TestSeal_MultipleTargets(t *testing.T) { - k := defaultCluster1 +func (s *SealedSecretsTestSuite) TestSeal_MultipleTargets() { + k := s.k namespace := "seal-multiple-targets" - waitForSealedSecretsOperator(t, k) - waitForSealedSecretsOperator(t, defaultCluster2) - - p := prepareSealTest(t, k, namespace, + p := s.prepareSealTest(k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -279,11 +354,10 @@ func TestSeal_MultipleTargets(t *testing.T) { "s2": "v2", }, }), - }, - ) + }, true) defer p.cleanup() - addSecretsSet(p, "test2", []*uo.UnstructuredObject{ + s.addSecretsSet(p, "test2", []*uo.UnstructuredObject{ uo.FromMap(map[string]interface{}{ "values": map[string]interface{}{ "s1": "v3", @@ -291,48 +365,42 @@ func TestSeal_MultipleTargets(t *testing.T) { }, }), }) - addSecretsSetToTarget(p, "test-target2", "test2") + s.addSecretsSetToTarget(p, "test-target2", "test2") - p.mergeKubeconfig(defaultCluster2) - createNamespace(t, defaultCluster2, namespace) + p.mergeKubeconfig(s.k2) + createNamespace(s.T(), s.k2, namespace) p.updateTarget("test-target", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultCluster1.Context, "context") + _ = target.SetNestedField(s.k.Context, "context") }) p.updateTarget("test-target2", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(defaultCluster2.Context, "context") + _ = target.SetNestedField(s.k2.Context, "context") }) p.KluctlMust("seal", "-t", "test-target") p.KluctlMust("seal", "-t", "test-target2") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) - assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) - assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target2/secret-secret.yml")) + assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target2/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") p.KluctlMust("deploy", "--yes", "-t", "test-target2") - waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) - assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) - waitForReadiness(t, defaultCluster2, namespace, "secret/secret", 1*time.Minute) - assertDecryptedSecrets(t, defaultCluster2, namespace, "secret", map[string]string{ + s.assertSealedSecret(s.k2, namespace, "secret", s.certServer2.certHash, map[string]string{ "s1": "v3", "s2": "v4", }) } -func TestSeal_File(t *testing.T) { - t.Parallel() - - k := defaultCluster1 +func (s *SealedSecretsTestSuite) TestSeal_File() { + k := s.k namespace := "seal-file" - waitForSealedSecretsOperator(t, k) - - p := prepareSealTest(t, k, namespace, + p := s.prepareSealTest(k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -341,8 +409,7 @@ func TestSeal_File(t *testing.T) { uo.FromMap(map[string]interface{}{ "file": utils.StrPtr("secret-values.yaml"), }), - }, - ) + }, true) defer p.cleanup() p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), "secret-values.yaml", func(o *uo.UnstructuredObject) error { @@ -358,50 +425,44 @@ func TestSeal_File(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) - assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) - assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func TestSeal_Vault(t *testing.T) { - t.Parallel() - - k := defaultCluster1 +func (s *SealedSecretsTestSuite) TestSeal_Vault() { + k := s.k namespace := "seal-vault" - waitForSealedSecretsOperator(t, k) - waitForVault(t, k) - - u, err := url.Parse(defaultCluster1.RESTConfig().Host) - if err != nil { - t.Fatal(err) - } - - vaultUrl := fmt.Sprintf("http://%s:%d", u.Hostname(), defaultKindCluster1VaultPort) + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + if request.URL.Path != "/v1/secret/data/secret" { + http.NotFound(writer, request) + return + } + o := uo.FromMap(map[string]interface{}{ + "data": map[string]interface{}{ + "data": map[string]interface{}{ + "secrets": map[string]interface{}{ + "s1": "v1", + "s2": "v2", + }, + }, + }, + }) + s, _ := yaml.WriteJsonString(o) + writer.Header().Set("Content-Type", "application/json") + _, _ = writer.Write([]byte(s)) + })) + defer server.Close() - req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/secret/data/secret", vaultUrl), strings.NewReader(`{"data": {"secrets":{"s1":"v1","s2":"v2"}}}`)) - if err != nil { - t.Fatal(err) - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-Vault-Token", "root") - resp, err := http.DefaultClient.Do(req) - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - body, _ := ioutil.ReadAll(resp.Body) - t.Fatalf("vault response status %d, body=%s", resp.StatusCode, string(body)) - } + vaultUrl := server.URL - p := prepareSealTest(t, k, namespace, + p := s.prepareSealTest(k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -413,20 +474,18 @@ func TestSeal_Vault(t *testing.T) { "path": "secret/data/secret", }, }), - }, - ) + }, true) defer p.cleanup() p.extraEnv = append(p.extraEnv, "VAULT_TOKEN=root") p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) - assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - waitForReadiness(t, k, namespace, "secret/secret", 1*time.Minute) - assertDecryptedSecrets(t, k, namespace, "secret", map[string]string{ + s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) diff --git a/e2e/test_resources/README.md b/e2e/test_resources/README.md index a95af0258..742fd70c2 100644 --- a/e2e/test_resources/README.md +++ b/e2e/test_resources/README.md @@ -4,12 +4,6 @@ helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets helm template sealed-secrets-controller sealed-secrets/sealed-secrets -n kube-system --include-crds --skip-tests > sealed-secrets.yaml ``` -# vault.yaml -```bash -helm repo add hashicorp https://helm.releases.hashicorp.com -helm template vault hashicorp/vault -n vault -f vault-values.yaml --include-crds --skip-tests > vault.yaml -``` - # kluctl-crds.yaml Fetches flux-kluctl-controller crd definitions ```bash diff --git a/e2e/test_resources/vault-values.yaml b/e2e/test_resources/vault-values.yaml deleted file mode 100644 index 1934c072a..000000000 --- a/e2e/test_resources/vault-values.yaml +++ /dev/null @@ -1,10 +0,0 @@ -server: - # Must be RollingUpdate as otherwise it's reported as ready much too early - updateStrategyType: RollingUpdate - dev: - enabled: true - service: - type: NodePort - nodePort: 30000 -injector: - enabled: false diff --git a/e2e/test_resources/vault.yaml b/e2e/test_resources/vault.yaml deleted file mode 100644 index 13e65b084..000000000 --- a/e2e/test_resources/vault.yaml +++ /dev/null @@ -1,235 +0,0 @@ ---- -# Source: vault/templates/server-serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: vault - namespace: vault - labels: - helm.sh/chart: vault-0.20.1 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: vault - app.kubernetes.io/managed-by: Helm ---- -# Source: vault/templates/server-clusterrolebinding.yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: vault-server-binding - labels: - helm.sh/chart: vault-0.20.1 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: vault - app.kubernetes.io/managed-by: Helm -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: -- kind: ServiceAccount - name: vault - namespace: vault ---- -# Source: vault/templates/server-headless-service.yaml -# Service for Vault cluster -apiVersion: v1 -kind: Service -metadata: - name: vault-internal - namespace: vault - labels: - helm.sh/chart: vault-0.20.1 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: vault - app.kubernetes.io/managed-by: Helm - annotations: - -spec: - clusterIP: None - publishNotReadyAddresses: true - ports: - - name: "http" - port: 8200 - targetPort: 8200 - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: vault - component: server ---- -# Source: vault/templates/server-service.yaml -# Service for Vault cluster -apiVersion: v1 -kind: Service -metadata: - name: vault - namespace: vault - labels: - helm.sh/chart: vault-0.20.1 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: vault - app.kubernetes.io/managed-by: Helm - annotations: - -spec: - type: NodePort - externalTrafficPolicy: Cluster - # We want the servers to become available even if they're not ready - # since this DNS is also used for join operations. - publishNotReadyAddresses: true - ports: - - name: http - port: 8200 - targetPort: 8200 - nodePort: 30000 - - name: https-internal - port: 8201 - targetPort: 8201 - selector: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: vault - component: server ---- -# Source: vault/templates/server-statefulset.yaml -# StatefulSet to run the actual vault server cluster. -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: vault - namespace: vault - labels: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: vault - app.kubernetes.io/managed-by: Helm -spec: - serviceName: vault-internal - podManagementPolicy: Parallel - replicas: 1 - updateStrategy: - type: RollingUpdate - selector: - matchLabels: - app.kubernetes.io/name: vault - app.kubernetes.io/instance: vault - component: server - template: - metadata: - labels: - helm.sh/chart: vault-0.20.1 - app.kubernetes.io/name: vault - app.kubernetes.io/instance: vault - component: server - spec: - - - - - terminationGracePeriodSeconds: 10 - serviceAccountName: vault - - securityContext: - runAsNonRoot: true - runAsGroup: 1000 - runAsUser: 100 - fsGroup: 1000 - volumes: - - - name: home - emptyDir: {} - containers: - - name: vault - - image: hashicorp/vault:1.10.3 - imagePullPolicy: IfNotPresent - command: - - "/bin/sh" - - "-ec" - args: - - | - /usr/local/bin/docker-entrypoint.sh vault server -dev - - securityContext: - allowPrivilegeEscalation: false - env: - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.hostIP - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: VAULT_K8S_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: VAULT_K8S_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: VAULT_ADDR - value: "http://127.0.0.1:8200" - - name: VAULT_API_ADDR - value: "http://$(POD_IP):8200" - - name: SKIP_CHOWN - value: "true" - - name: SKIP_SETCAP - value: "true" - - name: HOSTNAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: VAULT_CLUSTER_ADDR - value: "https://$(HOSTNAME).vault-internal:8201" - - name: HOME - value: "/home/vault" - - - name: VAULT_DEV_ROOT_TOKEN_ID - value: root - - name: VAULT_DEV_LISTEN_ADDRESS - value: "[::]:8200" - - - - volumeMounts: - - - - - name: home - mountPath: /home/vault - ports: - - containerPort: 8200 - name: http - - containerPort: 8201 - name: https-internal - - containerPort: 8202 - name: http-rep - readinessProbe: - # Check status; unsealed vault servers return 0 - # The exit code reflects the seal status: - # 0 - unsealed - # 1 - error - # 2 - sealed - exec: - command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"] - failureThreshold: 2 - initialDelaySeconds: 5 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 3 - lifecycle: - # Vault container doesn't receive SIGTERM from Kubernetes - # and after the grace period ends, Kube sends SIGKILL. This - # causes issues with graceful shutdowns such as deregistering itself - # from Consul (zombie services). - preStop: - exec: - command: [ - "/bin/sh", "-c", - # Adding a sleep here to give the pod eviction a - # chance to propagate, so requests will not be made - # to this pod while it's terminating - "sleep 5 && kill -SIGTERM $(pidof vault)", - ] diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index e6f9e4a20..38ebbde01 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -73,98 +73,3 @@ func addSecretDeployment(p *testProject, dir string, data map[string]string, sea {fname, fname + ".sealme", o}, }, opts.tags) } - -func addDeploymentHelper(p *testProject, dir string, o *uo.UnstructuredObject, opts resourceOpts) { - rbac := renderTemplateObjectHelper(podRbacTemplate, map[string]interface{}{ - "name": o.GetK8sName(), - "namespace": o.GetK8sNamespace(), - }) - for _, x := range rbac { - mergeMetadata(x, opts) - } - mergeMetadata(o, opts) - - resources := []kustomizeResource{ - {"rbac.yml", "", rbac}, - {"deploy.yml", "", o}, - } - - p.addKustomizeDeployment(dir, resources, opts.tags) -} - -func addJobDeployment(p *testProject, dir string, opts resourceOpts, image string, command []string, args []string) { - o := renderTemplateObjectHelper(jobTemplate, map[string]interface{}{ - "name": opts.name, - "namespace": opts.namespace, - "image": image, - }) - o[0].SetNestedField(command, "spec", "template", "spec", "containers", 0, "command") - o[0].SetNestedField(args, "spec", "template", "spec", "containers", 0, "args") - addDeploymentHelper(p, dir, o[0], opts) -} - -const podRbacTemplate = ` -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ .name }} - namespace: {{ .namespace }} ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: "{{ .name }}" - namespace: "{{ .namespace }}" -subjects: -- kind: ServiceAccount - name: "{{ .name }}" - namespace: "{{ .namespace }}" -roleRef: - kind: ClusterRole - name: "cluster-admin" - apiGroup: rbac.authorization.k8s.io -` - -const deploymentTemplate = ` -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .name }} - namespace: {{ .namespace }} - labels: - app.kubernetes.io/name: {{ .name }} -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: {{ .name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ .name }} - spec: - terminationGracePeriodSeconds: 0 - serviceAccountName: {{ .name }} - containers: - - image: {{ .image }} - imagePullPolicy: IfNotPresent - name: container -` - -const jobTemplate = ` -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ .name }} - namespace: {{ .namespace }} -spec: - template: - spec: - terminationGracePeriodSeconds: 0 - restartPolicy: OnFailure - serviceAccountName: {{ .name }} - containers: - - image: {{ .image }} - imagePullPolicy: IfNotPresent - name: container -` diff --git a/go.mod b/go.mod index 5a7fa1316..57771f841 100644 --- a/go.mod +++ b/go.mod @@ -58,7 +58,10 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) -require sigs.k8s.io/controller-runtime v0.13.0 +require ( + github.com/go-logr/logr v1.2.3 + sigs.k8s.io/controller-runtime v0.13.0 +) require ( cloud.google.com/go/compute v1.10.0 // indirect @@ -104,7 +107,6 @@ require ( github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect github.com/go-gorp/gorp/v3 v3.0.2 // indirect - github.com/go-logr/logr v1.2.3 // 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.22.3 // indirect @@ -202,6 +204,7 @@ require ( golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/tools v0.1.12 // indirect + gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc // indirect google.golang.org/grpc v1.49.0 // indirect diff --git a/go.sum b/go.sum index c5651ad7a..4249e8b49 100644 --- a/go.sum +++ b/go.sum @@ -202,6 +202,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m 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/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= @@ -1097,6 +1098,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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= diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go index 7683f8079..5bfd6100a 100644 --- a/internal/test-utils/envtest_cluster.go +++ b/internal/test-utils/envtest_cluster.go @@ -53,6 +53,8 @@ func (k *EnvTestCluster) Start() error { } k.user = user + k.config = user.Config() + kcfg, err := user.KubeConfig() if err != nil { return err @@ -113,7 +115,11 @@ func (c *EnvTestCluster) Kubectl(args ...string) (string, string, error) { func (c *EnvTestCluster) KubectlMust(t *testing.T, args ...string) string { stdout, stderr, err := c.Kubectl(args...) if err != nil { - t.Fatalf("%v, stderr=%s\n", err, stderr) + if t != nil { + t.Fatalf("%v, stderr=%s\n", err, stderr) + } else { + panic(fmt.Sprintf("%v, stderr=%s\n", err, stderr)) + } } return stdout } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 1656e080d..751664902 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" "io" + "net/http" + "strings" "sync" "time" @@ -460,9 +462,47 @@ func (k *K8sCluster) UpdateObject(o *uo.UnstructuredObject, options UpdateOption return result, apiWarnings, err } +// envtestProxyGet checks the environment variables KLUCTL_K8S_SERVICE_PROXY_XXX to enable testing of proxy requests with envtest +func (k *K8sCluster) envtestProxyGet(scheme, namespace, name, port, path string, params map[string]string) (io.ReadCloser, error) { + for _, m := range utils.ParseEnvConfigSets("KLUCTL_K8S_SERVICE_PROXY") { + envApiHost := strings.TrimSuffix(m["API_HOST"], "/") + envNamespace := m["SERVICE_NAMESPACE"] + envName := m["SERVICE_NAME"] + envPort := m["SERVICE_PORT"] + envUrl := m["LOCAL_URL"] + + apiHost := strings.TrimSuffix(k.clientFactory.RESTConfig().Host, "/") + + if envApiHost != apiHost || envNamespace != namespace || envName != name || port != envPort { + continue + } + + status.Trace(k.ctx, "envtestProxyGet apiHost=%s, ns=%s, name=%s, port=%s, path=%s, envUrl=%s", apiHost, namespace, name, port, path, envUrl) + + envUrl = fmt.Sprintf("%s%s", envUrl, path) + resp, err := http.Get(envUrl) + if err != nil { + return nil, err + } + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return nil, fmt.Errorf("http get returned status %d: %s", resp.StatusCode, resp.Status) + } + return resp.Body, nil + } + return nil, nil +} + func (k *K8sCluster) ProxyGet(scheme, namespace, name, port, path string, params map[string]string) (io.ReadCloser, error) { + stream, err := k.envtestProxyGet(scheme, namespace, name, port, path, params) + if err != nil { + return nil, err + } + if stream != nil { + return stream, nil + } + var ret rest.ResponseWrapper - _, err := k.clients.withClientFromPool(func(p *parallelClientEntry) error { + _, err = k.clients.withClientFromPool(func(p *parallelClientEntry) error { ret = p.corev1.Services(namespace).ProxyGet(scheme, name, port, path, params) return nil }) diff --git a/pkg/seal/fetch_cert.go b/pkg/seal/fetch_cert.go index 07fd5e7c0..5c1c28963 100644 --- a/pkg/seal/fetch_cert.go +++ b/pkg/seal/fetch_cert.go @@ -2,7 +2,9 @@ package seal import ( "context" + "crypto/sha256" "crypto/x509" + "encoding/hex" "errors" "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -105,3 +107,12 @@ func ParseCert(data []byte) (*x509.Certificate, error) { return certs[0], nil } + +func HashPublicKey(cert *x509.Certificate) (string, error) { + pkBytes, err := x509.MarshalPKIXPublicKey(cert.PublicKey) + if err != nil { + return "", err + } + h := sha256.Sum256(pkBytes) + return hex.EncodeToString(h[:]), nil +} diff --git a/pkg/seal/sealer.go b/pkg/seal/sealer.go index 268009cc6..e821f2e99 100644 --- a/pkg/seal/sealer.go +++ b/pkg/seal/sealer.go @@ -4,7 +4,6 @@ import ( "context" "crypto/rand" "crypto/rsa" - "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" @@ -46,17 +45,16 @@ func NewSealer(ctx context.Context, cert *x509.Certificate, forceReseal bool) (* } s.pubKey = pk - pkBytes, err := x509.MarshalPKIXPublicKey(cert.PublicKey) + var err error + s.certHash, err = HashPublicKey(cert) if err != nil { return nil, err } - h := sha256.Sum256(pkBytes) - s.certHash = hex.EncodeToString(h[:]) return s, nil } -func (s *Sealer) doHash(key string, secret []byte, secretName string, secretNamespace string, scope string) string { +func HashSecret(key string, secret []byte, secretName string, secretNamespace string, scope string) string { if secretNamespace == "" { secretNamespace = "*" } @@ -220,7 +218,7 @@ func (s *Sealer) SealFile(p string, targetFile string) error { } for k, v := range secrets { - hash := s.doHash(k, v, secretName, secretNamespace, *scope) + hash := HashSecret(k, v, secretName, secretNamespace, *scope) existingHash, _, _ := existingHashes.GetNestedString(k) doEncrypt := resealAll From 7d1f28804e43dacd0f8b5dbd13c2cc87ffb18b06 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 30 Sep 2022 15:20:44 +0200 Subject: [PATCH 0395/2268] chore: Fix exclusion of e2e tests from "make test-unit" --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7d7c482d2..f4f6b06d0 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ ifeq ($(EXPORT_RESULT), true) GO111MODULE=off $(GOCMD) get -u github.com/jstemmer/go-junit-report $(eval OUTPUT_OPTIONS = | tee /dev/tty | go-junit-report -set-exit-code > reports/test-unit/junit-report.xml) endif - $(GOTEST) -v -race $(shell go list ./... | grep -v /e2e/) $(OUTPUT_OPTIONS) + $(GOTEST) -v -race $(shell go list ./... | grep -v 'v2/e2e') $(OUTPUT_OPTIONS) coverage-unit: ## Run the unit tests of the project and export the coverage $(GOTEST) -cover -covermode=count -coverprofile=reports/coverage-unit/profile.cov $(shell go list ./... | grep -v /e2e/) From b042dd909492ef42c6212afe9b9b9c6d08b457d8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 30 Sep 2022 16:28:41 +0200 Subject: [PATCH 0396/2268] chore: Enable verbose e2e tests --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f4f6b06d0..82f7d3504 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,7 @@ test: test-unit test-e2e ## Runs the complete test suite KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_KUBERNETES_VERSION) --bin-dir=$(ENVTEST_ASSETS_DIR) -p path)" test-e2e: install-envtest ## Runs the end to end tests - KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -o ./bin/$(TEST_BINARY_NAME) ./e2e + KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -o ./bin/$(TEST_BINARY_NAME) ./e2e -test.v test-unit: ## Run the unit tests of the project ifeq ($(EXPORT_RESULT), true) From 6fa34a316782b4509c72999a911c1a8dd2c9e11a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 30 Sep 2022 17:27:11 +0200 Subject: [PATCH 0397/2268] tests: Paralellize a few tests --- e2e/hooks_test.go | 1 + e2e/seal_test.go | 1 + 2 files changed, 2 insertions(+) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index f860553ed..347ef2cf8 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -20,6 +20,7 @@ type HookTestSuite struct { } func TestHooks(t *testing.T) { + t.Parallel() suite.Run(t, &HookTestSuite{}) } diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 531fef2c1..a06c03156 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -40,6 +40,7 @@ type certServer struct { } func TestSealedSecrets(t *testing.T) { + t.Parallel() suite.Run(t, &SealedSecretsTestSuite{}) } From 44982e64a4a974aa27b3ff5f5250043c7f992521 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 30 Sep 2022 17:27:14 +0200 Subject: [PATCH 0398/2268] chore: Download kubebuilder-tools for windows from kubebuilder-tools-releases-windows repo --- Makefile | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 82f7d3504..446ffbcf9 100644 --- a/Makefile +++ b/Makefile @@ -52,11 +52,21 @@ setup-envtest: ## Download envtest-setup locally if necessary. $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) # Download the envtest binaries to testbin +ENVTEST_KUBERNETES_VERSION?=1.25.0 ENVTEST_ASSETS_DIR=$(BUILD_DIR)/testbin -ENVTEST_KUBERNETES_VERSION?=latest + +ifeq ($(OS),Windows_NT) +ENVTEST_ASSETS_DIR_WINDOWS=$(ENVTEST_ASSETS_DIR)/k8s/$(ENVTEST_KUBERNETES_VERSION)-windows-amd64 +install-envtest: setup-envtest $(ENVTEST_ASSETS_DIR_WINDOWS)/etcd.exe +$(ENVTEST_ASSETS_DIR_WINDOWS)/etcd.exe: + mkdir -p $(ENVTEST_ASSETS_DIR_WINDOWS) + curl -o $(ENVTEST_ASSETS_DIR_WINDOWS)/kubebuilder-tools.tar.gz "https://raw.githubusercontent.com/kluctl/kubebuilder-tools-releases-windows/main/releases/kubebuilder-tools-$(ENVTEST_KUBERNETES_VERSION)_windows_amd64.tar.gz" + cd $(ENVTEST_ASSETS_DIR_WINDOWS) && tar xzf kubebuilder-tools.tar.gz && mv kubebuilder/bin/* . + rm $(ENVTEST_ASSETS_DIR_WINDOWS)/kubebuilder-tools.tar.gz +else install-envtest: setup-envtest - mkdir -p ${ENVTEST_ASSETS_DIR} $(ENVTEST) use $(ENVTEST_KUBERNETES_VERSION) --arch=$(ENVTEST_ARCH) --bin-dir=$(ENVTEST_ASSETS_DIR) +endif ## Test: test: test-unit test-e2e ## Runs the complete test suite From 53bd717555dc314bb0307056433d7c0e05f583fa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 30 Sep 2022 15:25:55 +0200 Subject: [PATCH 0399/2268] ci: Stop using zerotier in CI and instead rely on envtest based tests (no docker) --- .github/workflows/tests.yml | 199 ++++------------------------- Makefile | 3 + hack/zerotier-create-network.sh | 29 ----- hack/zerotier-delete-network.sh | 16 --- hack/zerotier-join-network.sh | 28 ---- hack/zerotier-setup-docker-host.sh | 29 ----- 6 files changed, 28 insertions(+), 276 deletions(-) delete mode 100755 hack/zerotier-create-network.sh delete mode 100755 hack/zerotier-delete-network.sh delete mode 100755 hack/zerotier-join-network.sh delete mode 100755 hack/zerotier-setup-docker-host.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fad114c1a..b26c3e618 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,9 +21,9 @@ jobs: path: | ~/go/pkg/mod ~/.cache/go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + key: build-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-go- + build-go-${{ runner.os }}- - name: Run unit tests run: | go test ./cmd/... ./pkg/... -v @@ -66,64 +66,7 @@ jobs: e2e.test-darwin-amd64 e2e.test-windows-amd64.exe - docker-host: - if: "!startsWith(github.ref, 'refs/tags/')" - runs-on: ubuntu-20.04 - needs: - - build - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install zerotier - run: | - sudo apt update - sudo apt install -y gpg jq - curl -s https://install.zerotier.com | sudo bash - - name: Setup inotify limits - run: | - # see https://kind.sigs.k8s.io/docs/user/known-issues/#pod-errors-due-to-too-many-open-files - sudo sysctl fs.inotify.max_user_watches=524288 - sudo sysctl fs.inotify.max_user_instances=512 - - name: Stop docker - run: | - # Ensure docker is down and that the test jobs can wait for it to be available again after joining the network. - # Otherwise they might join and then docker restarts - sudo systemctl stop docker - - name: Create network - run: | - export CI_ZEROTIER_API_KEY=${{ secrets.CI_ZEROTIER_API_KEY }} - ./hack/zerotier-create-network.sh $GITHUB_RUN_ID - - name: Join network - run: | - export CI_ZEROTIER_API_KEY=${{ secrets.CI_ZEROTIER_API_KEY }} - ./hack/zerotier-join-network.sh $GITHUB_RUN_ID docker - - name: Restart ssh - run: | - sudo systemctl restart ssh - - name: Restart docker - run: | - sudo sed -i 's|-H fd://|-H fd:// -H tcp://0.0.0.0:2375|g' /lib/systemd/system/docker.service - sudo systemctl daemon-reload - sudo systemctl start docker - - name: Wait for other jobs to finish - run: | - export GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}" - while true; do - JOBS=$(gh api /repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/jobs | jq '.jobs[] | select(.name | startswith("tests "))') - NON_COMPLETED=$(echo $JOBS | jq '. | select(.status != "completed")') - if [ "$NON_COMPLETED" == "" ]; then - break - fi - sleep 5 - done - - name: Delete network - if: always() - run: | - export CI_ZEROTIER_API_KEY=${{ secrets.CI_ZEROTIER_API_KEY }} - ./hack/zerotier-delete-network.sh $GITHUB_RUN_ID - tests: - if: "!startsWith(github.ref, 'refs/tags/')" strategy: matrix: include: @@ -141,125 +84,33 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - name: Install zerotier (linux) - if: runner.os == 'Linux' - run: | - sudo apt update - sudo apt install -y gpg jq - curl -s https://install.zerotier.com | sudo bash - - name: Install zerotier (macOS) - if: runner.os == 'macOS' - run: | - brew install zerotier-one - - name: Install zerotier (windows) - if: runner.os == 'Windows' - shell: bash - run: | - choco install zerotier-one - echo "#!/usr/bin/env bash" > /usr/bin/zerotier-cli - echo '/c/ProgramData/ZeroTier/One/zerotier-one_x64.exe -q "$@"' >> /usr/bin/zerotier-cli - chmod +x /usr/bin/zerotier-cli - - choco install netcat - echo /c/ProgramData/chocolatey/bin >> $GITHUB_PATH - - name: Join network - shell: bash - run: | - export CI_ZEROTIER_API_KEY=${{ secrets.CI_ZEROTIER_API_KEY }} - ./hack/zerotier-join-network.sh $GITHUB_RUN_ID ${{ matrix.binary-suffix }} - - name: Determine DOCKER_HOST - shell: bash - run: | - export CI_ZEROTIER_API_KEY=${{ secrets.CI_ZEROTIER_API_KEY }} - ./hack/zerotier-setup-docker-host.sh $GITHUB_RUN_ID - - name: Setup TOOLS envs - shell: bash - run: | - if [ "${{ runner.os }}" != "Windows" ]; then - echo "SUDO=sudo" >> $GITHUB_ENV - fi - - TOOLS_EXE= - TOOLS_TARGET_DIR=$GITHUB_WORKSPACE/bin - mkdir $TOOLS_TARGET_DIR - - if [ "${{ runner.os }}" == "macOS" ]; then - TOOLS_OS=darwin - elif [ "${{ runner.os }}" == "Windows" ]; then - TOOLS_OS=windows - TOOLS_EXE=.exe - else - TOOLS_OS=linux - fi - echo "TOOLS_EXE=$TOOLS_EXE" >> $GITHUB_ENV - echo "TOOLS_OS=$TOOLS_OS" >> $GITHUB_ENV - echo "TOOLS_TARGET_DIR=$TOOLS_TARGET_DIR" >> $GITHUB_ENV - echo "$TOOLS_TARGET_DIR" >> $GITHUB_PATH - - name: "[Windows] Install openssh" - if: runner.os == 'Windows' - shell: bash - run: | - choco install openssh - - name: Provide required tools versions - shell: bash - run: | - echo "KUBECTL_VERSION=1.21.5" >> $GITHUB_ENV - echo "KIND_VERSION=0.11.1" >> $GITHUB_ENV - echo "DOCKER_VERSION=20.10.9" >> $GITHUB_ENV - - name: Download required tools - shell: bash - run: | - curl -L -o kubectl$TOOLS_EXE https://storage.googleapis.com/kubernetes-release/release/v$KUBECTL_VERSION/bin/${TOOLS_OS}/amd64/kubectl$TOOLS_EXE && \ - $SUDO mv kubectl$TOOLS_EXE "$TOOLS_TARGET_DIR/" - curl -L -o kind$TOOLS_EXE https://github.com/kubernetes-sigs/kind/releases/download/v${KIND_VERSION}/kind-${TOOLS_OS}-amd64 && \ - $SUDO mv kind$TOOLS_EXE "$TOOLS_TARGET_DIR/" - if [ "${{ runner.os }}" == "macOS" ]; then - curl -L -o docker.tar.gz https://download.docker.com/mac/static/stable/x86_64/docker-$DOCKER_VERSION.tgz - tar xzf docker.tar.gz - $SUDO mv docker/docker "$TOOLS_TARGET_DIR/" - rm -rf docker - elif [ "${{ runner.os }}" == "Windows" ]; then - curl -L -o docker.zip https://download.docker.com/win/static/stable/x86_64/docker-$DOCKER_VERSION.zip - unzip docker.zip - mv docker/docker.exe "$TOOLS_TARGET_DIR/" - rm -rf docker - fi - $SUDO chmod -R +x "$TOOLS_TARGET_DIR/" - - name: Test required tools + - uses: actions/setup-go@v2 + with: + go-version: '1.19' + - uses: actions/cache@v2 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: tests-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} + restore-keys: | + tests-go-${{ runner.os }}- + - name: Download artifacts + uses: actions/download-artifact@v2 + - name: setup-envtest shell: bash run: | - kubectl version || true - kind version || true - - name: Prepare kind cluster variables + make install-envtest + - name: Run e2e tests shell: bash run: | - if [ "${{ runner.os }}" == "Linux" ]; then - echo "KIND_API_PORT1=10000" >> $GITHUB_ENV - echo "KIND_API_PORT2=20000" >> $GITHUB_ENV - echo "KIND_EXTRA_PORTS_OFFSET1=30000" >> $GITHUB_ENV - echo "KIND_EXTRA_PORTS_OFFSET2=31000" >> $GITHUB_ENV - elif [ "${{ runner.os }}" == "Windows" ]; then - echo "KIND_API_PORT1=10001" >> $GITHUB_ENV - echo "KIND_API_PORT2=20001" >> $GITHUB_ENV - echo "KIND_EXTRA_PORTS_OFFSET1=30100" >> $GITHUB_ENV - echo "KIND_EXTRA_PORTS_OFFSET2=31100" >> $GITHUB_ENV - else - echo "KIND_API_PORT1=10002" >> $GITHUB_ENV - echo "KIND_API_PORT2=20002" >> $GITHUB_ENV - echo "KIND_EXTRA_PORTS_OFFSET1=30200" >> $GITHUB_ENV - echo "KIND_EXTRA_PORTS_OFFSET2=31200" >> $GITHUB_ENV + EXE= + if [ "${{ runner.os }}" == "Windows" ]; then + EXE=.exe fi - KIND_CLUSTER_NAME_BASE=$(echo "kluctl-${{ runner.os }}" | awk '{{ print tolower($1) }}') - echo "KIND_API_HOST1=$DOCKER_IP" >> $GITHUB_ENV - echo "KIND_API_HOST2=$DOCKER_IP" >> $GITHUB_ENV - echo "KIND_CLUSTER_NAME1=$KIND_CLUSTER_NAME_BASE-1" >> $GITHUB_ENV - echo "KIND_CLUSTER_NAME2=$KIND_CLUSTER_NAME_BASE-2" >> $GITHUB_ENV - - name: Download artifacts - uses: actions/download-artifact@v2 - - name: Run e2e tests - shell: bash - run: | chmod +x ./binaries/* - export KLUCTL_EXE=./binaries/kluctl-${{ matrix.binary-suffix }}$TOOLS_EXE - ./binaries/e2e.test-${{ matrix.binary-suffix }}$TOOLS_EXE -test.v + mv ./binaries/e2e.test-${{ matrix.binary-suffix }}$EXE ./e2e.test$EXE + + export KLUCTL_EXE=./binaries/kluctl-${{ matrix.binary-suffix }}$EXE + make test-e2e-pre-built diff --git a/Makefile b/Makefile index 446ffbcf9..d8a176b7f 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,9 @@ KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_ test-e2e: install-envtest ## Runs the end to end tests KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -o ./bin/$(TEST_BINARY_NAME) ./e2e -test.v +test-e2e-pre-built: install-envtest + KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) ./e2e.test -test.v + test-unit: ## Run the unit tests of the project ifeq ($(EXPORT_RESULT), true) mkdir -p reports/test-unit diff --git a/hack/zerotier-create-network.sh b/hack/zerotier-create-network.sh deleted file mode 100755 index 000e77af9..000000000 --- a/hack/zerotier-create-network.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -set -e - -NETWORK_NAME=$1 - -NETWORK=$(curl -H"Authorization: bearer $CI_ZEROTIER_API_KEY" -XPOST -d"{}" https://my.zerotier.com/api/v1/network) -NETWORK_ID=$(echo $NETWORK | jq '.config.id' -r) - -echo "Configuring network" -cat << EOF | curl -H"Authorization: bearer $CI_ZEROTIER_API_KEY" -XPOST -d@- https://my.zerotier.com/api/v1/network/$NETWORK_ID -{ - "config": { - "private": false, - "name": "$NETWORK_NAME", - "ipAssignmentPools": [ - { - "ipRangeStart":"10.147.17.1", - "ipRangeEnd":"10.147.17.254" - } - ], - "routes": [ - { - "target":"10.147.17.0/24" - } - ] - } -} -EOF diff --git a/hack/zerotier-delete-network.sh b/hack/zerotier-delete-network.sh deleted file mode 100755 index a8c22a7f7..000000000 --- a/hack/zerotier-delete-network.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -e - -NETWORK_NAME=$1 - -echo "Searching network " -NETWORKS=$(curl -H"Authorization: bearer $CI_ZEROTIER_API_KEY" https://my.zerotier.com/api/v1/network) -NETWORK_ID=$(echo $NETWORKS | jq ".[] | select(.config.name == \"$NETWORK_NAME\") | .config.id" -r) - -if [ "$NETWORK_ID" = "" ]; then - exit 0 -fi - -echo "Deleting network" -curl -H"Authorization: bearer $CI_ZEROTIER_API_KEY" -XDELETE https://my.zerotier.com/api/v1/network/$NETWORK_ID diff --git a/hack/zerotier-join-network.sh b/hack/zerotier-join-network.sh deleted file mode 100755 index 3f2094e41..000000000 --- a/hack/zerotier-join-network.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -set -e - -NETWORK_NAME=$1 -MEMBER_NAME=$2 - -while true; do - echo "Searching network" - NETWORKS=$(curl -H"Authorization: bearer $CI_ZEROTIER_API_KEY" https://my.zerotier.com/api/v1/network) - NETWORK_ID=$(echo $NETWORKS | jq ".[] | select(.config.name == \"$NETWORK_NAME\") | .config.id" -r) - if [ "$NETWORK_ID" != "" ]; then - break - fi - sleep 5 -done - -SUDO= -if which sudo &> /dev/null; then - SUDO=sudo -fi - -echo "Joining network" -$SUDO zerotier-cli join $NETWORK_ID -MEMBER_ID=$($SUDO zerotier-cli status | awk '{print $3}') - -echo "Renaming member $MEMBER_ID" -curl -H"Authorization: bearer $CI_ZEROTIER_API_KEY" -XPOST -d"{\"name\": \"$MEMBER_NAME\"}" https://my.zerotier.com/api/v1/network/$NETWORK_ID/member/$MEMBER_ID diff --git a/hack/zerotier-setup-docker-host.sh b/hack/zerotier-setup-docker-host.sh deleted file mode 100755 index 4c06dbfc1..000000000 --- a/hack/zerotier-setup-docker-host.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -set -e - -NETWORK_NAME=$1 - -echo "Searching network " -NETWORKS=$(curl -H"Authorization: bearer $CI_ZEROTIER_API_KEY" https://my.zerotier.com/api/v1/network) -NETWORK_ID=$(echo $NETWORKS | jq ".[] | select(.config.name == \"$NETWORK_NAME\") | .config.id" -r) - -echo "Waiting for IP to be assigned to docker member" -while true; do - MEMBERS=$(curl -H"Authorization: bearer $CI_ZEROTIER_API_KEY" https://my.zerotier.com/api/v1/network/$NETWORK_ID/member) - IP=$(echo $MEMBERS | jq ".[] | select(.name == \"docker\") | .config.ipAssignments[0]" -r) - if [ "$IP" != "null" ]; then - break - fi - sleep 5 - echo "Still waiting..." -done - -echo "DOCKER_IP=$IP" >> $GITHUB_ENV -echo "DOCKER_HOST=tcp://$IP:2375" >> $GITHUB_ENV - -echo "Waiting for docker to become available" -while ! nc -z $IP 2375; do - sleep 5 - echo "Still waiting..." -done From 8538ad7cbe99a5a19a7cf5a16603e2e47d292f47 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 30 Sep 2022 22:39:59 +0200 Subject: [PATCH 0400/2268] ci: Run tests workflow for pull requests --- .github/workflows/tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b26c3e618..9f00b5a06 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,8 +2,9 @@ name: tests on: push: + pull_request: branches: - - '**' + - master jobs: build: From c6a093e55018cb8a4a955955f37b923bba2eaab2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 11 Oct 2022 09:54:56 +0200 Subject: [PATCH 0401/2268] fix: Upgrade go-jinja2 to fix zombie processes --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 57771f841..d00ca0ba4 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 - github.com/kluctl/go-jinja2 v0.0.0-20220927093149-38ad307371ca + github.com/kluctl/go-jinja2 v0.0.0-20221011075324-ee187dbffa85 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 diff --git a/go.sum b/go.sum index 4249e8b49..df2ed7fd8 100644 --- a/go.sum +++ b/go.sum @@ -500,8 +500,8 @@ github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= -github.com/kluctl/go-jinja2 v0.0.0-20220927093149-38ad307371ca h1:OSl16ha01V8QHu/yKmsfKHx8r6ts/O3xHuT5SaaJ/1I= -github.com/kluctl/go-jinja2 v0.0.0-20220927093149-38ad307371ca/go.mod h1:kMEDb2dWTbpe6CT+XuGVPcnY1ph9IfHf4b43KN80qas= +github.com/kluctl/go-jinja2 v0.0.0-20221011075324-ee187dbffa85 h1:T2Xhn0xx4MYwFBzc2ADMMJbO2SwH5lfL6r3txHCPYYE= +github.com/kluctl/go-jinja2 v0.0.0-20221011075324-ee187dbffa85/go.mod h1:kMEDb2dWTbpe6CT+XuGVPcnY1ph9IfHf4b43KN80qas= 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/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= From 6fe7abcabffea0520a737ea49d3fe8548c895d91 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 09:27:27 +0200 Subject: [PATCH 0402/2268] fix: Skip postprocessCRDs in case --offline-kubernetes was used Fixes #58 --- pkg/deployment/deployment_item.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index e0c30c28e..588a08fca 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -439,6 +439,9 @@ func (di *DeploymentItem) postprocessCRDs() error { if di.dir == nil { return nil } + if di.ctx.K == nil { + return nil + } for _, o := range di.Objects { gvk := o.GetK8sGVK() From 6b4cf92cc8847fc75c1d0df236e47ce8019a1373 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 14:42:41 +0200 Subject: [PATCH 0403/2268] feat: Allow to pass --offline-kuberntes to list-images --- cmd/kluctl/commands/cmd_list_images.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/kluctl/commands/cmd_list_images.go b/cmd/kluctl/commands/cmd_list_images.go index cfd9ef56f..4ec7d3543 100644 --- a/cmd/kluctl/commands/cmd_list_images.go +++ b/cmd/kluctl/commands/cmd_list_images.go @@ -14,14 +14,15 @@ type listImagesCmd struct { args.OutputFlags args.RenderOutputDirFlags - Simple bool `group:"misc" help:"Output a simplified version of the images list"` + OfflineKubernetes bool `group:"misc" help:"Run list-images in offline mode, meaning that it will not try to connect the target cluster"` + Simple bool `group:"misc" help:"Output a simplified version of the images list"` } func (cmd *listImagesCmd) Help() string { return `The result is a compatible with yaml files expected by --fixed-images-file. If fixed images ('-f/--fixed-image') are provided, these are also taken into account, -as described in for the deploy command.` +as described in the deploy command.` } func (cmd *listImagesCmd) Run() error { @@ -32,6 +33,7 @@ func (cmd *listImagesCmd) Run() error { imageFlags: cmd.ImageFlags, inclusionFlags: cmd.InclusionFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, + offlineKubernetes: cmd.OfflineKubernetes, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { result := types.FixedImagesConfig{ From 63c11e52dad6d3bc23a77f67605ccfc672afd461 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 14:56:28 +0200 Subject: [PATCH 0404/2268] feat: Allow loading args from files, e.g. -amy_arg=@file.yaml --- cmd/kluctl/args/project.go | 2 +- cmd/kluctl/commands/utils.go | 2 +- pkg/deployment/external_args.go | 13 ++++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index d2c933721..b9e839fc7 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -18,7 +18,7 @@ type ProjectFlags struct { } type ArgsFlags struct { - Arg []string `group:"project" short:"a" help:"Passes a template argument in the form of name=value. Nested args can be set with the '-a my.nested.arg=value' syntax. Values are interpreted as yaml values, meaning that 'true' and 'false' will lead to boolean values and numbers will be treated as numbers. Use quotes if you want these to be treated as strings."` + Arg []string `group:"project" short:"a" help:"Passes a template argument in the form of name=value. Nested args can be set with the '-a my.nested.arg=value' syntax. Values are interpreted as yaml values, meaning that 'true' and 'false' will lead to boolean values and numbers will be treated as numbers. Use quotes if you want these to be treated as strings. If the value starts with @, it is treated as a file, meaning that the contents of the file will be loaded and treated as yaml."` } type TargetFlags struct { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 968077a02..5b9373376 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -146,7 +146,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm if err != nil { return err } - optionArgs2, err := deployment.ConvertArgsToVars(optionArgs) + optionArgs2, err := deployment.ConvertArgsToVars(optionArgs, true) if err != nil { return err } diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index 955b74b27..952dfa063 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -6,6 +6,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" + "os" "path/filepath" "regexp" "strings" @@ -28,9 +29,19 @@ func ParseArgs(argsList []string) (map[string]string, error) { return args, nil } -func ConvertArgsToVars(args map[string]string) (*uo.UnstructuredObject, error) { +func ConvertArgsToVars(args map[string]string, allowLoadFromFiles bool) (*uo.UnstructuredObject, error) { vars := uo.New() for n, v := range args { + if allowLoadFromFiles && strings.HasPrefix(v, "@") { + b, err := os.ReadFile(v[1:]) + if err != nil { + return nil, err + } + v = string(b) + } else if strings.HasPrefix(v, "\\@") { + v = v[1:] + } + var p []interface{} for _, x := range strings.Split(n, ".") { p = append(p, x) From 08b3c3df11fe4126c3beccbcaafb4b41f62151a5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 15:06:14 +0200 Subject: [PATCH 0405/2268] feat: Implement --args-from-file to load args from a yaml file --- cmd/kluctl/args/project.go | 3 ++- cmd/kluctl/commands/utils.go | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index b9e839fc7..6dfbc8bf0 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -18,7 +18,8 @@ type ProjectFlags struct { } type ArgsFlags struct { - Arg []string `group:"project" short:"a" help:"Passes a template argument in the form of name=value. Nested args can be set with the '-a my.nested.arg=value' syntax. Values are interpreted as yaml values, meaning that 'true' and 'false' will lead to boolean values and numbers will be treated as numbers. Use quotes if you want these to be treated as strings. If the value starts with @, it is treated as a file, meaning that the contents of the file will be loaded and treated as yaml."` + Arg []string `group:"project" short:"a" help:"Passes a template argument in the form of name=value. Nested args can be set with the '-a my.nested.arg=value' syntax. Values are interpreted as yaml values, meaning that 'true' and 'false' will lead to boolean values and numbers will be treated as numbers. Use quotes if you want these to be treated as strings. If the value starts with @, it is treated as a file, meaning that the contents of the file will be loaded and treated as yaml."` + ArgsFromFile []string `group:"project" help:"Loads a yaml file and makes it available as arguments, meaning that they will be available thought the global 'args' variable."` } type TargetFlags struct { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 5b9373376..eb0c9a1ad 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -15,6 +15,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/registries" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/ioutil" "k8s.io/client-go/rest" @@ -150,6 +151,13 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm if err != nil { return err } + for _, a := range args.argsFlags.ArgsFromFile { + optionArgs3, err := uo.FromFile(a) + if err != nil { + return err + } + optionArgs2.Merge(optionArgs3) + } renderOutputDir := args.renderOutputDirFlags.RenderOutputDir if renderOutputDir == "" { From 4bad414e42a3b4bfd616c477ece47b97405dedf4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 15:51:33 +0200 Subject: [PATCH 0406/2268] tests: Add tests for -a and --args-from-file --- e2e/args_test.go | 125 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 e2e/args_test.go diff --git a/e2e/args_test.go b/e2e/args_test.go new file mode 100644 index 000000000..b606df688 --- /dev/null +++ b/e2e/args_test.go @@ -0,0 +1,125 @@ +package e2e + +import ( + "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "os" + "testing" +) + +func TestArgs(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k, "args") + defer p.cleanup() + + createNamespace(t, k, p.projectName) + + p.updateTarget("test", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField([]any{ + map[string]any{ + "name": "a", + }, + map[string]any{ + "name": "b", + }, + map[string]any{ + "name": "c", + }, + map[string]any{ + "name": "d", + }, + }, "dynamicArgs") + }) + p.updateDeploymentYaml(".", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField([]any{ + map[string]any{ + "name": "a", + }, + map[string]any{ + "name": "b", + "default": "default", + }, + map[string]any{ + "name": "d", + "default": map[string]any{ + "nested": "default", + }, + }, + }, "args") + return nil + }) + + addConfigMapDeployment(p, "cm", map[string]string{ + "a": `{{ args.a | default("na") }}`, + "b": `{{ args.b | default("na") }}`, + "c": `{{ args.c | default("na") }}`, + "d": "{{ args.d | to_json }}", + }, resourceOpts{ + name: "cm", + namespace: p.projectName, + }) + + p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a") + cm := k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + assertNestedFieldEquals(t, cm, "a", "data", "a") + assertNestedFieldEquals(t, cm, "default", "data", "b") + assertNestedFieldEquals(t, cm, "na", "data", "c") + assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") + + p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") + cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + assertNestedFieldEquals(t, cm, "a", "data", "a") + assertNestedFieldEquals(t, cm, "b", "data", "b") + assertNestedFieldEquals(t, cm, "na", "data", "c") + assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") + + p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c") + cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + assertNestedFieldEquals(t, cm, "a", "data", "a") + assertNestedFieldEquals(t, cm, "b", "data", "b") + assertNestedFieldEquals(t, cm, "c", "data", "c") + assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") + + p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", "-ad.nested=d") + cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + assertNestedFieldEquals(t, cm, `{"nested": "d"}`, "data", "d") + + p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", `-ad={"nested": "d2"}`) + cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + assertNestedFieldEquals(t, cm, `{"nested": "d2"}`, "data", "d") + + tmpFile, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpFile.Name()) + _, _ = tmpFile.WriteString(` +nested: + nested2: d3 +`) + + p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", fmt.Sprintf(`-ad=@%s`, tmpFile.Name())) + cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "d3"}}`, "data", "d") + + _ = tmpFile.Truncate(0) + _, _ = tmpFile.Seek(0, 0) + _, _ = tmpFile.WriteString(` +a: a2 +c: c2 +d: + nested: + nested2: d4 +`) + + p.KluctlMust("deploy", "--yes", "-t", "test", fmt.Sprintf(`--args-from-file=%s`, tmpFile.Name())) + cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + assertNestedFieldEquals(t, cm, "a2", "data", "a") + assertNestedFieldEquals(t, cm, "default", "data", "b") + assertNestedFieldEquals(t, cm, "c2", "data", "c") + assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "d4"}}`, "data", "d") +} From 7393163edbc2f2af1592105f23f25701934cf27c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 16:02:44 +0200 Subject: [PATCH 0407/2268] tests: Add some log outputs to debug hook tests flakiness --- e2e/hooks_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 347ef2cf8..86bbf45fa 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -46,10 +46,12 @@ func (s *HookTestSuite) SetupTest() { } func (s *HookTestSuite) handleConfigmap(o *uo.UnstructuredObject) { + s.T().Logf("handleConfigmap: %s", o.GetK8sName()) s.seenConfigMaps = append(s.seenConfigMaps, o.GetK8sName()) } func (s *HookTestSuite) clearSeenConfigmaps() { + s.T().Logf("clearSeenConfigmaps: %v", s.seenConfigMaps) s.seenConfigMaps = nil } From 43a4f58cc4f6645ebb938f243b41c15135e8b4b8 Mon Sep 17 00:00:00 2001 From: Aljoscha Poertner Date: Mon, 17 Oct 2022 16:15:18 +0200 Subject: [PATCH 0408/2268] chore: move check list in PR template to comment Signed-off-by: Aljoscha Poertner --- .github/pull_request_template.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index dc3dd31cb..205acfcdf 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,6 +14,7 @@ Please delete options that are not relevant. - [ ] This change requires a documentation update - [ ] This change requires a new example + From 909f06b35a77444cef4b693cebfbfd8b0dabd806 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 16:59:24 +0200 Subject: [PATCH 0409/2268] fix: Treat strings as raw strings (no escape sequences handles) in version patterns This avoids unnecessary warnings being printed in case of invalid escape sequences in regex strings. The parser should actually NOT try to parse these escape sequences as it would lead to invalid regex strings in case a valid escape sequence (e.g. \n) is provided. --- pkg/utils/python_scanner/README.md | 2 +- pkg/utils/python_scanner/scanner.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/utils/python_scanner/README.md b/pkg/utils/python_scanner/README.md index 3ae1e4c85..645cb1299 100644 --- a/pkg/utils/python_scanner/README.md +++ b/pkg/utils/python_scanner/README.md @@ -1,2 +1,2 @@ -This package is a copy of golangs text/scanner package, with the difference that it interprets ' the same way as ", so +This package is a copy of golangs text/scanner package, with the difference that it interprets ' and ` the same way as ", so that it's a little bit more like python. \ No newline at end of file diff --git a/pkg/utils/python_scanner/scanner.go b/pkg/utils/python_scanner/scanner.go index c64b1e506..006f9107b 100644 --- a/pkg/utils/python_scanner/scanner.go +++ b/pkg/utils/python_scanner/scanner.go @@ -593,9 +593,9 @@ func (s *Scanner) scanString(quote rune) (n int) { return } -func (s *Scanner) scanRawString() { +func (s *Scanner) scanRawString(quote rune) { ch := s.next() // read character after '`' - for ch != '`' { + for ch != quote { if ch < 0 { s.error("literal not terminated") return @@ -697,14 +697,14 @@ redo: break case '"': if s.Mode&ScanStrings != 0 { - s.scanString('"') + s.scanRawString('"') tok = String } ch = s.next() case '\'': // This is the difference to golang's text/scanner package, we handle ' as a string and not as a char if s.Mode&ScanStrings != 0 { - s.scanString('\'') + s.scanRawString('\'') tok = String } ch = s.next() @@ -726,7 +726,7 @@ redo: } case '`': if s.Mode&ScanRawStrings != 0 { - s.scanRawString() + s.scanRawString('`') tok = RawString } ch = s.next() From 20da8b095a6bad2df0ad3b248a492d7d0bb8aa7c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 17:34:35 +0200 Subject: [PATCH 0410/2268] feat: Upgrade go-jinja2 to add slugify filter --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index d00ca0ba4..6b01fa205 100644 --- a/go.mod +++ b/go.mod @@ -21,8 +21,8 @@ require ( github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 - github.com/kluctl/go-jinja2 v0.0.0-20221011075324-ee187dbffa85 + github.com/kluctl/go-embed-python v0.0.0-3.10.7-20221017-1 + github.com/kluctl/go-jinja2 v0.0.0-20221017153334-80addb432305 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 @@ -40,9 +40,9 @@ require ( github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.2 golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be - golang.org/x/net v0.0.0-20220923203811-8be639271d50 + golang.org/x/net v0.0.0-20221014081412-f15817d10f9b golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 - golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 + golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 golang.org/x/term v0.0.0-20220919170432-7a66f970e087 golang.org/x/text v0.3.7 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index df2ed7fd8..5d9e2e5ba 100644 --- a/go.sum +++ b/go.sum @@ -498,10 +498,10 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2 h1:zjJAVyZiWJhufcafhykvXNar1vqDp/ukQk0hFuCJ9zw= -github.com/kluctl/go-embed-python v0.0.0-3.10.6-20220906-2/go.mod h1:uekQDAlvKAYJWuDxxP1SGn1vTPUfoBwMS+aB6kR+v+o= -github.com/kluctl/go-jinja2 v0.0.0-20221011075324-ee187dbffa85 h1:T2Xhn0xx4MYwFBzc2ADMMJbO2SwH5lfL6r3txHCPYYE= -github.com/kluctl/go-jinja2 v0.0.0-20221011075324-ee187dbffa85/go.mod h1:kMEDb2dWTbpe6CT+XuGVPcnY1ph9IfHf4b43KN80qas= +github.com/kluctl/go-embed-python v0.0.0-3.10.7-20221017-1 h1:4XlPysiwaex+j7+r6pPA12WyuBM9Sy6gSoeKYTqxU9M= +github.com/kluctl/go-embed-python v0.0.0-3.10.7-20221017-1/go.mod h1:fuMI+yFFgrViXsRl+NIWQeuPDTvJtOk2pe0HoeujT54= +github.com/kluctl/go-jinja2 v0.0.0-20221017153334-80addb432305 h1:95WNlnGuRfLXgWoeBTDV0DhHwX26hUfnIzgiYTitsLk= +github.com/kluctl/go-jinja2 v0.0.0-20221017153334-80addb432305/go.mod h1:2G09vDjMA8VSWJT7Kox9EQi8l7DCkGnyBNcd7eomM1s= 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/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= @@ -912,8 +912,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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= @@ -1015,8 +1015,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 8a66a63f0264acf88e79a9968aafd3596af778aa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 17:36:44 +0200 Subject: [PATCH 0411/2268] chore: Run go get -u ./... --- go.mod | 59 +++++++++++++++++++++++++-------------------------- go.sum | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 6b01fa205..fbccf67c5 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.105 - github.com/bitnami-labs/sealed-secrets v0.18.5 + github.com/aws/aws-sdk-go v1.44.116 + github.com/bitnami-labs/sealed-secrets v0.19.1 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/fluxcd/pkg/kustomize v0.8.0 @@ -16,7 +16,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 github.com/google/go-containerregistry v0.11.0 - github.com/hashicorp/vault/api v1.8.0 + github.com/hashicorp/vault/api v1.8.1 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 @@ -28,30 +28,30 @@ require ( github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.14.4 + github.com/ohler55/ojg v1.14.5 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.9.0 github.com/sirupsen/logrus v1.9.0 - github.com/spf13/cobra v1.5.0 + github.com/spf13/cobra v1.6.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.13.0 github.com/stretchr/testify v1.8.0 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.2 - golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be + golang.org/x/crypto v0.0.0-20221012134737-56aed061732a golang.org/x/net v0.0.0-20221014081412-f15817d10f9b - golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 + golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 golang.org/x/term v0.0.0-20220919170432-7a66f970e087 - golang.org/x/text v0.3.7 + golang.org/x/text v0.3.8 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.10.0 - k8s.io/api v0.25.2 - k8s.io/apiextensions-apiserver v0.25.2 - k8s.io/apimachinery v0.25.2 - k8s.io/client-go v0.25.2 + helm.sh/helm/v3 v3.10.1 + k8s.io/api v0.25.3 + k8s.io/apiextensions-apiserver v0.25.3 + k8s.io/apimachinery v0.25.3 + k8s.io/client-go v0.25.3 k8s.io/klog/v2 v2.80.1 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/kustomize/kyaml v0.13.9 @@ -78,7 +78,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect @@ -90,8 +90,8 @@ require ( github.com/cloudflare/circl v1.2.0 // indirect github.com/containerd/containerd v1.6.8 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.18+incompatible // indirect - github.com/docker/docker v20.10.18+incompatible // indirect + github.com/docker/cli v20.10.19+incompatible // indirect + github.com/docker/docker v20.10.19+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -102,7 +102,7 @@ require ( github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect @@ -173,7 +173,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc1 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect @@ -198,28 +198,29 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect - go.starlark.net v0.0.0-20220926145019-14b050677505 // indirect + go.etcd.io/etcd/api/v3 v3.5.5 // indirect + go.starlark.net v0.0.0-20221010140840-6bf6f0955179 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect + golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/tools v0.1.12 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc // indirect - google.golang.org/grpc v1.49.0 // indirect + google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a // indirect + google.golang.org/grpc v1.50.1 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiserver v0.25.2 // indirect - k8s.io/cli-runtime v0.25.2 // indirect - k8s.io/component-base v0.25.2 // indirect - k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea // indirect - k8s.io/kubectl v0.25.2 // indirect - k8s.io/utils v0.0.0-20220922133306-665eaaec4324 // indirect - oras.land/oras-go v1.2.0 // indirect + k8s.io/apiserver v0.25.3 // indirect + k8s.io/cli-runtime v0.25.3 // indirect + k8s.io/component-base v0.25.3 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + k8s.io/kubectl v0.25.3 // indirect + k8s.io/utils v0.0.0-20221012122500-cfd413dd9e85 // indirect + oras.land/oras-go v1.2.1 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 5d9e2e5ba..cc6a47da0 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErIPzRrnogAXYwSoU7txA977LjDGrbkewJbg= github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= +github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad h1:QeeqI2zxxgZVe11UrYFXXx6gVxPVF40ygekjBzEg4XY= +github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= @@ -116,6 +118,8 @@ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.44.105 h1:UUwoD1PRKIj3ltrDUYTDQj5fOTK3XsnqolLpRTMmSEM= github.com/aws/aws-sdk-go v1.44.105/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.116 h1:NpLIhcvLWXJZAEwvPj3TDHeqp7DleK6ZUVYyW01WNHY= +github.com/aws/aws-sdk-go v1.44.116/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= 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= @@ -123,6 +127,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitnami-labs/sealed-secrets v0.18.5 h1:PT/lxfgWnP8l8ybpvTsHuJltY4A35LI8if19Ww9FbcU= github.com/bitnami-labs/sealed-secrets v0.18.5/go.mod h1:nzCyKhzDRbNySRUZXR6tvui4s95LbuaNpBoGRvV9Nk8= +github.com/bitnami-labs/sealed-secrets v0.19.1 h1:ZNLcVtTXRf7VkyNzyhe9omlwNYI0OHDteTbIHsQI7Ug= +github.com/bitnami-labs/sealed-secrets v0.19.1/go.mod h1:5UcsiOdOoviJUtXY1GNSeFa8zezbBY+j9pQAA2RtKyw= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= @@ -134,6 +140,7 @@ github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -153,6 +160,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX 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/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/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs= github.com/containerd/containerd v1.6.8/go.mod h1:By6p5KqPK0/7/CgO/A6t/Gz+CUYUu2zf1hUaaymVXB0= @@ -173,10 +181,14 @@ github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27N github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= github.com/docker/cli v20.10.18+incompatible h1:f/GQLsVpo10VvToRay2IraVA1wHz9KktZyjev3SIVDU= github.com/docker/cli v20.10.18+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.19+incompatible h1:VKVBUb0KY/bx0FUCrCiNCL8wqgy8VxQli1dtNTn38AE= +github.com/docker/cli v20.10.19+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.19+incompatible h1:lzEmjivyNHFHMNAFLXORMBXyGIhw/UP4DvJwvyKYq64= +github.com/docker/docker v20.10.19+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -201,6 +213,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y 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= @@ -222,6 +235,8 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -443,6 +458,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/vault/api v1.8.0 h1:7765sW1XBt+qf4XKIYE4ebY9qc/yi9V2/egzGSUNMZU= github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= +github.com/hashicorp/vault/api v1.8.1 h1:bMieWIe6dAlqAAPReZO/8zYtXaWUg/21umwqGZpEjCI= +github.com/hashicorp/vault/api v1.8.1/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= github.com/hashicorp/vault/sdk v0.6.0 h1:6Z+In5DXHiUfZvIZdMx7e2loL1PPyDjA4bVh9ZTIAhs= github.com/hashicorp/vault/sdk v0.6.0/go.mod h1:+DRpzoXIdMvKc88R4qxr+edwy/RvH5QK8itmxLiDHLc= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= @@ -617,6 +634,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/ohler55/ojg v1.14.4 h1:L2ds8AlB5t/QbqSfhRwvagJzQ7pgmdrefMIypQs0Xik= github.com/ohler55/ojg v1.14.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= +github.com/ohler55/ojg v1.14.5 h1:xCX2oyh/ZaoesbLH6fwVHStSJpk4o4eJs8ttXutzdg0= +github.com/ohler55/ojg v1.14.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= @@ -627,6 +646,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc1 h1:lfG+OTa7V8PD3PKvkocSG9KAcA9MANqJn53m31Fvwkc= github.com/opencontainers/image-spec v1.1.0-rc1/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -732,6 +753,8 @@ github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155 github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= +github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 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= @@ -788,6 +811,8 @@ github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1 github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= +go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -801,6 +826,8 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20220926145019-14b050677505 h1:W0MibAL5BiEenQR+F/EF/a4HJhgLngHVvm6jbtUW0PM= go.starlark.net v0.0.0-20220926145019-14b050677505/go.mod h1:qsNirHv+Awo5xHuNyQ/0niov6kDxdBs+bqpVMBCW77k= +go.starlark.net v0.0.0-20221010140840-6bf6f0955179 h1:Mc5MkF55Iasgq23vSYpL6/l7EJXtlNjzw+8hbMQ/ShY= +go.starlark.net v0.0.0-20221010140840-6bf6f0955179/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= 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= @@ -830,6 +857,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= +golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -930,6 +959,8 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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= @@ -943,6 +974,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 h1:ZrnxWX62AgTKOSagEqxvb3ffipvEDX2pl7E1TdqLqIc= golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1015,6 +1048,7 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1032,6 +1066,8 @@ 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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 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= @@ -1175,6 +1211,8 @@ google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxH google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc h1:saaNe2+SBQxandnzcD/qB1JEBQ2Pqew+KlFLLdA/XcM= google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc/go.mod h1:yEEpwVWKMZZzo81NwRgyEJnA2fQvpXAYPVisv8EgDVs= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a h1:GH6UPn3ixhWcKDhpnEC55S75cerLPdpp3hrhfKYjZgw= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= 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= @@ -1196,8 +1234,11 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/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.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 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= @@ -1250,6 +1291,8 @@ gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= helm.sh/helm/v3 v3.10.0 h1:y/MYONZ/bsld9kHwqgBX2uPggnUr5hahpjwt9/jrHlI= helm.sh/helm/v3 v3.10.0/go.mod h1:paPw0hO5KVfrCMbi1M8+P8xdfBri3IiJiVKATZsFR94= +helm.sh/helm/v3 v3.10.1 h1:uTnNlYx8QcTSNA4ZJ50Llwife4CSohUY4ehumyVf2QE= +helm.sh/helm/v3 v3.10.1/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= 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= @@ -1259,28 +1302,51 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= +k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= +k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8= +k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= +k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= +k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= +k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= k8s.io/apiserver v0.25.2 h1:YePimobk187IMIdnmsMxsfIbC5p4eX3WSOrS9x6FEYw= k8s.io/apiserver v0.25.2/go.mod h1:30r7xyQTREWCkG2uSjgjhQcKVvAAlqoD+YyrqR6Cn+I= +k8s.io/apiserver v0.25.3 h1:m7+xGuG5+KYAnEsqaFtDyWMkmMMEOFYlu+NlWv5qSBI= +k8s.io/apiserver v0.25.3/go.mod h1:9bT47iM2fzRuhICJpM/RcQR9sqDDfZ7Yw60h0p3JW08= k8s.io/cli-runtime v0.25.2 h1:XOx+SKRjBpYMLY/J292BHTkmyDffl/qOx3YSuFZkTuc= k8s.io/cli-runtime v0.25.2/go.mod h1:OQx3+/0st6x5YpkkJQlEWLC73V0wHsOFMC1/roxV8Oc= +k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= +k8s.io/cli-runtime v0.25.3/go.mod h1:InHHsjkyW5hQsILJGpGjeruiDZT/R0OkROQgD6GzxO4= k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= +k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0= +k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA= k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY= k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60= +k8s.io/component-base v0.25.3 h1:UrsxciGdrCY03ULT1h/S/gXFCOPnLhUVwSyx+hM/zq4= +k8s.io/component-base v0.25.3/go.mod h1:WYoS8L+IlTZgU7rhAl5Ctpw0WdMxDfCC5dkxcEFa/TI= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea h1:3QOH5+2fGsY8e1qf+GIFpg+zw/JGNrgyZRQR7/m6uWg= k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +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/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA= k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg= +k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= +k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= k8s.io/utils v0.0.0-20220922133306-665eaaec4324 h1:i+xdFemcSNuJvIfBlaYuXgRondKxK4z4prVPKzEaelI= k8s.io/utils v0.0.0-20220922133306-665eaaec4324/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20221012122500-cfd413dd9e85 h1:cTdVh7LYu82xeClmfzGtgyspNh6UxpwLWGi8R4sspNo= +k8s.io/utils v0.0.0-20221012122500-cfd413dd9e85/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4= oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= +oras.land/oras-go v1.2.1 h1:/VcGS8FUy3eEXLl/1vC4QypLHwrfSmgW7ygsoklqKK8= +oras.land/oras-go v1.2.1/go.mod h1:3N11Z5E3c4ZzOjroCl1RtAdB4yNAYl7A27j2SVf913A= 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= From 8db57a5531abd06e04c553ef9be348d443b55737 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 21:42:14 +0200 Subject: [PATCH 0412/2268] feat: Remove deprecated external projects and cluster configs --- cmd/kluctl/args/project.go | 11 +- cmd/kluctl/commands/cmd_seal.go | 3 - cmd/kluctl/commands/completion.go | 36 ------ cmd/kluctl/commands/utils.go | 22 +--- e2e/external_projects_test.go | 77 ----------- e2e/project.go | 109 +--------------- e2e/seal_test.go | 14 +- pkg/kluctl_project/git.go | 27 +--- pkg/kluctl_project/project.go | 2 - pkg/kluctl_project/project_load.go | 187 ++------------------------- pkg/kluctl_project/target_context.go | 46 +------ pkg/kluctl_project/targets.go | 10 -- pkg/types/cluster_config.go | 83 ------------ pkg/types/git_project.go | 15 --- pkg/types/kluctl_project.go | 8 +- 15 files changed, 37 insertions(+), 613 deletions(-) delete mode 100644 e2e/external_projects_test.go delete mode 100644 pkg/types/cluster_config.go diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 6dfbc8bf0..d142ece1a 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -3,15 +3,8 @@ package args import "time" type ProjectFlags struct { - ProjectUrl string `group:"project" short:"p" help:"Git url of the kluctl project. If not specified, the current directory will be used instead of a remote Git project"` - ProjectRef string `group:"project" short:"b" help:"Git ref of the kluctl project. Only used when --project-url was given."` - - ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` - LocalClusters existingDirType `group:"project" help:"DEPRECATED. Local clusters directory. Overrides the project from .kluctl.yaml"` - LocalDeployment existingDirType `group:"project" help:"DEPRECATED. Local deployment directory. Overrides the project from .kluctl.yaml"` - LocalSealedSecrets existingDirType `group:"project" help:"DEPRECATED. Local sealed-secrets directory. Overrides the project from .kluctl.yaml" ` - OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to."` - Cluster string `group:"project" help:"DEPRECATED. Specify/Override cluster"` + ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` + OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to."` Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 636444179..e2388b317 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -161,9 +161,6 @@ func (cmd *sealCmd) Run() error { if cmd.Target != "" && cmd.Target != target.Target.Name { continue } - if cmd.Cluster != "" && target.Target.Cluster != nil && cmd.Cluster != *target.Target.Cluster { - continue - } if target.Target.SealingConfig == nil { status.Info(ctx, "Target %s has no sealingConfig", target.Target.Name) continue diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 3c8ae9e37..7830d90e4 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -5,12 +5,8 @@ import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/spf13/cobra" - "os" - "path/filepath" "reflect" "strings" "sync" @@ -24,10 +20,6 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err inclusionFlags := v.FieldByName("InclusionFlags") imageFlags := v.FieldByName("ImageFlags") - if projectFlags.IsValid() { - _ = ccmd.RegisterFlagCompletionFunc("cluster", buildClusterCompletionFunc(projectFlags.Addr().Interface().(*args.ProjectFlags))) - } - if projectFlags.IsValid() && targetFlags.IsValid() { _ = ccmd.RegisterFlagCompletionFunc("target", buildTargetCompletionFunc(projectFlags.Addr().Interface().(*args.ProjectFlags))) } @@ -56,34 +48,6 @@ func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(ctx contex }) } -func buildClusterCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var ret []string - err := withProjectForCompletion(projectArgs, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { - dents, err := os.ReadDir(p.ClustersDir) - if err != nil { - return err - } - for _, de := range dents { - var config types.ClusterConfig - err = yaml.ReadYamlFile(filepath.Join(p.ClustersDir, de.Name()), &config) - if err != nil { - continue - } - if config.Cluster.Name != "" { - ret = append(ret, config.Cluster.Name) - } - } - return nil - }) - if err != nil { - status.Error(cliCtx, err.Error()) - return nil, cobra.ShellCompDirectiveError - } - return ret, cobra.ShellCompDirectiveDefault - } -} - func buildTargetCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var ret []string diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index eb0c9a1ad..84b5f9162 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -7,7 +7,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/repocache" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" @@ -25,15 +24,6 @@ import ( ) func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { - var url *git_url.GitUrl - if projectFlags.ProjectUrl != "" { - var err error - url, err = git_url.Parse(projectFlags.ProjectUrl) - if err != nil { - return err - } - } - tmpDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "project-") if err != nil { return fmt.Errorf("creating temporary project directory failed: %w", err) @@ -67,12 +57,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b loadArgs := kluctl_project.LoadKluctlProjectArgs{ RepoRoot: repoRoot, ProjectDir: cwd, - ProjectUrl: url, - ProjectRef: projectFlags.ProjectRef, ProjectConfig: projectFlags.ProjectConfig.String(), - LocalClusters: projectFlags.LocalClusters.String(), - LocalDeployment: projectFlags.LocalDeployment.String(), - LocalSealedSecrets: projectFlags.LocalSealedSecrets.String(), RP: rp, ClientConfigGetter: clientConfigGetter(forCompletion), } @@ -169,13 +154,8 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm renderOutputDir = tmpDir } - var clusterName *string - if args.projectFlags.Cluster != "" { - clusterName = &args.projectFlags.Cluster - } - targetCtx, err := p.NewTargetContext(ctx, - args.targetFlags.Target, clusterName, args.offlineKubernetes, + args.targetFlags.Target, args.offlineKubernetes, args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, optionArgs2, args.forSeal, images, inclusion, renderOutputDir) diff --git a/e2e/external_projects_test.go b/e2e/external_projects_test.go deleted file mode 100644 index 94e6aebc6..000000000 --- a/e2e/external_projects_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package e2e - -import ( - "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "testing" -) - -func doTestProject(t *testing.T, namespace string, p *testProject) { - k := defaultCluster1 - - p.init(t, k, fmt.Sprintf("project-%s", namespace)) - defer p.cleanup() - - createNamespace(t, k, namespace) - - p.updateEnvTestCluster(k, uo.FromMap(map[string]interface{}{ - "cluster_var": "cluster_value1", - })) - p.updateTargetDeprecated("test", k.Context, uo.FromMap(map[string]interface{}{ - "target_var": "target_value1", - })) - addConfigMapDeployment(p, "cm1", map[string]string{}, resourceOpts{name: "cm1", namespace: namespace}) - - p.KluctlMust("deploy", "--yes", "-t", "test") - assertResourceExists(t, k, namespace, "ConfigMap/cm1") - - cmData := map[string]string{ - "cluster_var": "{{ cluster.cluster_var }}", - "target_var": "{{ args.target_var }}", - } - - assertResourceNotExists(t, k, namespace, "ConfigMap/cm2") - addConfigMapDeployment(p, "cm2", cmData, resourceOpts{name: "cm2", namespace: namespace}) - p.KluctlMust("deploy", "--yes", "-t", "test") - - o := assertResourceExists(t, k, namespace, "ConfigMap/cm2") - assertNestedFieldEquals(t, o, "cluster_value1", "data", "cluster_var") - assertNestedFieldEquals(t, o, "target_value1", "data", "target_var") - - p.updateEnvTestCluster(k, uo.FromMap(map[string]interface{}{ - "cluster_var": "cluster_value2", - })) - p.KluctlMust("deploy", "--yes", "-t", "test") - o = assertResourceExists(t, k, namespace, "ConfigMap/cm2") - assertNestedFieldEquals(t, o, "cluster_value2", "data", "cluster_var") - assertNestedFieldEquals(t, o, "target_value1", "data", "target_var") - - p.updateTargetDeprecated("test", k.Context, uo.FromMap(map[string]interface{}{ - "target_var": "target_value2", - })) - p.KluctlMust("deploy", "--yes", "-t", "test") - o = assertResourceExists(t, k, namespace, "ConfigMap/cm2") - assertNestedFieldEquals(t, o, "cluster_value2", "data", "cluster_var") - assertNestedFieldEquals(t, o, "target_value2", "data", "target_var") -} - -func TestExternalProjects(t *testing.T) { - testCases := []struct { - name string - p testProject - }{ - {name: "external-kluctl-project", p: testProject{kluctlProjectExternal: true}}, - {name: "external-clusters-project", p: testProject{clustersExternal: true}}, - {name: "external-deployment-project", p: testProject{deploymentExternal: true}}, - {name: "external-sealed-secrets-project", p: testProject{sealedSecretsExternal: true}}, - {name: "external-all-projects", p: testProject{kluctlProjectExternal: true, clustersExternal: true, deploymentExternal: true, sealedSecretsExternal: true}}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - doTestProject(t, tc.name, &tc.p) - }) - } -} diff --git a/e2e/project.go b/e2e/project.go index e106c795a..3249dd8c5 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -22,15 +22,6 @@ type testProject struct { extraEnv []string projectName string - kluctlProjectExternal bool - clustersExternal bool - deploymentExternal bool - sealedSecretsExternal bool - - localClusters *string - localDeployment *string - localSealedSecrets *string - mergedKubeconfig string gitServer *test_utils.GitServer @@ -42,29 +33,8 @@ func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster, projectNa p.projectName = projectName p.gitServer.GitInit(p.getKluctlProjectRepo()) - if p.clustersExternal { - p.gitServer.GitInit(p.getClustersRepo()) - } - if p.deploymentExternal { - p.gitServer.GitInit(p.getDeploymentRepo()) - } - if p.sealedSecretsExternal { - p.gitServer.GitInit(p.getSealedSecretsRepo()) - } - - _ = os.MkdirAll(filepath.Join(p.gitServer.LocalRepoDir(p.getClustersRepo()), "clusters"), 0o700) - _ = os.MkdirAll(filepath.Join(p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()), ".sealed-secrets"), 0o700) p.updateKluctlYaml(func(o *uo.UnstructuredObject) error { - if p.clustersExternal { - o.SetNestedField(p.gitServer.LocalGitUrl(p.getClustersRepo()), "clusters", "project") - } - if p.deploymentExternal { - o.SetNestedField(p.gitServer.LocalGitUrl(p.getDeploymentRepo()), "deployment", "project") - } - if p.sealedSecretsExternal { - o.SetNestedField(p.gitServer.LocalGitUrl(p.getSealedSecretsRepo()), "sealedSecrets", "project") - } return nil }) p.updateDeploymentYaml(".", func(c *uo.UnstructuredObject) error { @@ -124,7 +94,7 @@ func (p *testProject) updateKluctlYaml(update func(o *uo.UnstructuredObject) err } func (p *testProject) updateDeploymentYaml(dir string, update func(o *uo.UnstructuredObject) error) { - p.gitServer.UpdateYaml(p.getDeploymentRepo(), filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { + p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { if dir == "." { o.SetNestedField(p.projectName, "commonLabels", "project_name") } @@ -133,7 +103,7 @@ func (p *testProject) updateDeploymentYaml(dir string, update func(o *uo.Unstruc } func (p *testProject) getDeploymentYaml(dir string) *uo.UnstructuredObject { - o, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getDeploymentRepo()), dir, "deployment.yml")) + o, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, "deployment.yml")) if err != nil { p.t.Fatal(err) } @@ -165,43 +135,14 @@ func (p *testProject) listDeploymentItemPathes(dir string, fullPath bool) []stri } func (p *testProject) updateKustomizeDeployment(dir string, update func(o *uo.UnstructuredObject, wt *git.Worktree) error) { - wt := p.gitServer.GetWorktree(p.getDeploymentRepo()) + wt := p.gitServer.GetWorktree(p.getKluctlProjectRepo()) pth := filepath.Join(dir, "kustomization.yml") - p.gitServer.UpdateYaml(p.getDeploymentRepo(), pth, func(o *uo.UnstructuredObject) error { + p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), pth, func(o *uo.UnstructuredObject) error { return update(o, wt) }, fmt.Sprintf("Update kustomization.yml for %s", dir)) } -func (p *testProject) updateCluster(name string, context string, vars *uo.UnstructuredObject) { - pth := filepath.Join("clusters", fmt.Sprintf("%s.yml", name)) - p.gitServer.UpdateYaml(p.getClustersRepo(), pth, func(o *uo.UnstructuredObject) error { - o.Clear() - o.SetNestedField(name, "cluster", "name") - o.SetNestedField(context, "cluster", "context") - if vars != nil { - o.MergeChild("cluster", vars) - } - return nil - }, fmt.Sprintf("add/update cluster %s", name)) -} - -func (p *testProject) updateEnvTestCluster(k *test_utils.EnvTestCluster, vars *uo.UnstructuredObject) { - context := k.KubectlMust(p.t, "config", "current-context") - context = strings.TrimSpace(context) - p.updateCluster(k.Context, context, vars) -} - -func (p *testProject) updateTargetDeprecated(name string, cluster string, args *uo.UnstructuredObject) { - p.updateTarget(name, func(target *uo.UnstructuredObject) { - if args != nil { - target.MergeChild("args", args) - } - // compatibility - _ = target.SetNestedField(cluster, "cluster") - }) -} - func (p *testProject) updateTarget(name string, cb func(target *uo.UnstructuredObject)) { p.updateNamedListItem(uo.KeyPath{"targets"}, name, cb) } @@ -286,7 +227,7 @@ func (p *testProject) addKustomizeDeployment(dir string, resources []kustomizeRe p.addDeploymentIncludes(deploymentDir) } - absKustomizeDir := filepath.Join(p.gitServer.LocalRepoDir(p.getDeploymentRepo()), dir) + absKustomizeDir := filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir) err := os.MkdirAll(absKustomizeDir, 0o700) if err != nil { @@ -350,7 +291,7 @@ func (p *testProject) addKustomizeResources(dir string, resources []kustomizeRes if fileName == "" { fileName = r.name } - err := yaml.WriteYamlAllFile(filepath.Join(p.gitServer.LocalRepoDir(p.getDeploymentRepo()), dir, fileName), x) + err := yaml.WriteYamlAllFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, fileName), x) if err != nil { return err } @@ -383,48 +324,12 @@ func (p *testProject) getKluctlProjectRepo() string { return "kluctl-project" } -func (p *testProject) getClustersRepo() string { - if p.clustersExternal { - return "external-clusters" - } - return p.getKluctlProjectRepo() -} - -func (p *testProject) getDeploymentRepo() string { - if p.deploymentExternal { - return "external-deployment" - } - return p.getKluctlProjectRepo() -} - -func (p *testProject) getSealedSecretsRepo() string { - if p.sealedSecretsExternal { - return "external-sealed-secrets" - } - return p.getKluctlProjectRepo() -} - func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { var args []string args = append(args, argsIn...) args = append(args, "--no-update-check") - cwd := "" - if p.kluctlProjectExternal { - args = append(args, "--project-url", p.gitServer.LocalGitUrl(p.getKluctlProjectRepo())) - } else { - cwd = p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) - } - - if p.localClusters != nil { - args = append(args, "--local-clusters", *p.localClusters) - } - if p.localDeployment != nil { - args = append(args, "--local-deployment", *p.localDeployment) - } - if p.localSealedSecrets != nil { - args = append(args, "--local-sealed-secrets", *p.localSealedSecrets) - } + cwd := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) args = append(args, "--debug") diff --git a/e2e/seal_test.go b/e2e/seal_test.go index a06c03156..14219dd66 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -204,7 +204,7 @@ func (s *SealedSecretsTestSuite) TestSeal_WithOperator() { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -239,7 +239,7 @@ func (s *SealedSecretsTestSuite) TestSeal_WithBootstrap() { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) test_resources.ApplyYaml("sealed-secrets.yaml", k) @@ -287,7 +287,7 @@ func (s *SealedSecretsTestSuite) TestSeal_MultipleVarSources() { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -328,7 +328,7 @@ func (s *SealedSecretsTestSuite) TestSeal_MultipleSecretSets() { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -380,7 +380,7 @@ func (s *SealedSecretsTestSuite) TestSeal_MultipleTargets() { p.KluctlMust("seal", "-t", "test-target") p.KluctlMust("seal", "-t", "test-target2") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target2/secret-secret.yml")) @@ -425,7 +425,7 @@ func (s *SealedSecretsTestSuite) TestSeal_File() { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -481,7 +481,7 @@ func (s *SealedSecretsTestSuite) TestSeal_Vault() { p.extraEnv = append(p.extraEnv, "VAULT_TOKEN=root") p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getSealedSecretsRepo()) + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go index 12e7253e5..393836704 100644 --- a/pkg/kluctl_project/git.go +++ b/pkg/kluctl_project/git.go @@ -3,7 +3,6 @@ package kluctl_project import ( "fmt" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - types2 "github.com/kluctl/kluctl/v2/pkg/types" "sync" ) @@ -33,37 +32,13 @@ func (c *LoadedKluctlProject) updateGitCaches() error { return nil } - doUpdateExternalProject := func(p *types2.ExternalProject) error { - if p == nil || p.Project == nil { - return nil - } - return doUpdateGitProject(p.Project.Url) - } - - err := doUpdateExternalProject(c.Config.Deployment) - if err != nil { - waitGroup.Wait() - return err - } - err = doUpdateExternalProject(c.Config.SealedSecrets) - if err != nil { - waitGroup.Wait() - return err - } - for _, ep := range c.Config.Clusters.Projects { - err = doUpdateExternalProject(&ep) - if err != nil { - waitGroup.Wait() - return err - } - } for _, target := range c.Config.Targets { if target.TargetConfig == nil || target.TargetConfig.Project == nil { continue } - err = doUpdateGitProject(target.TargetConfig.Project.Url) + err := doUpdateGitProject(target.TargetConfig.Project.Url) if err != nil { waitGroup.Wait() return err diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index ebb5b2e26..0f89a9eb1 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -19,8 +19,6 @@ type LoadedKluctlProject struct { projectRootDir string ProjectDir string - DeploymentDir string - ClustersDir string sealedSecretsDir string Config types2.KluctlProject diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 738c3219c..29ea9e871 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -1,43 +1,25 @@ package kluctl_project import ( - "fmt" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/status" - types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" - "io/ioutil" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd/api" - "os" "path/filepath" ) type LoadKluctlProjectArgs struct { - RepoRoot string - ProjectDir string - ProjectUrl *git_url.GitUrl - ProjectRef string - ProjectConfig string - LocalClusters string - LocalDeployment string - LocalSealedSecrets string + RepoRoot string + ProjectDir string + ProjectConfig string RP *repocache.GitRepoCache ClientConfigGetter func(context *string) (*rest.Config, *api.Config, error) } -type gitProjectInfo struct { - url git_url.GitUrl - ref string - commit string - repoRoot string - dir string -} - func (c *LoadedKluctlProject) getConfigPath() string { configPath := c.loadArgs.ProjectConfig if configPath == "" { @@ -49,91 +31,14 @@ func (c *LoadedKluctlProject) getConfigPath() string { return configPath } -func (c *LoadedKluctlProject) localProject(dir string) gitProjectInfo { - return gitProjectInfo{ - dir: dir, - } -} - -func (c *LoadedKluctlProject) loadGitProject(gitProject *types2.GitProject, defaultSubDir string) (ret gitProjectInfo, err error) { - ge, err := c.RP.GetEntry(gitProject.Url) - if err != nil { - return - } - - cloneDir, ri, err := ge.GetClonedDir(gitProject.Ref) - if err != nil { - return - } - - ret.url = gitProject.Url - ret.ref = ri.CheckedOutRef - ret.commit = ri.CheckedOutCommit - ret.repoRoot = cloneDir - - subDir := gitProject.SubDir - if subDir == "" { - subDir = defaultSubDir - } - ret.dir = filepath.Join(ret.repoRoot, subDir) - err = utils.CheckInDir(ret.repoRoot, ret.dir) - if err != nil { - return - } - - return -} - -func (c *LoadedKluctlProject) loadExternalProject(ep *types2.ExternalProject, defaultGitSubDir string, localDir string) (gitProjectInfo, error) { - if localDir != "" { - return c.localProject(localDir), nil - } - - if ep == nil { - // no ExternalProject provided, so we point into the kluctl project + defaultGitSubDir - p := filepath.Join(c.ProjectDir, defaultGitSubDir) - return c.localProject(p), nil - } - - if ep.Project != nil { - status.Deprecation(c.ctx, "external-projects", "External projects are deprecated and support for them will be removed in the future. "+ - "Use Git variable sources as replacement for cluster configs and Git includes as replacement for external deployment projects.") - - // pointing to an actual external project, so let's try to clone it - return c.loadGitProject(ep.Project, defaultGitSubDir) - } - - // ExternalProject was provided but without an external repo url, so point into the kluctl project. - // We also allow to leave the kluctl project dir but limit it to the git project - - p := filepath.Join(c.ProjectDir, *ep.Path) - err := utils.CheckInDir(c.projectRootDir, p) - if err != nil { - return gitProjectInfo{}, fmt.Errorf("path '%s' is not inside git project root '%s': %w", p, c.projectRootDir, err) - } - return c.localProject(p), nil -} - func (c *LoadedKluctlProject) loadKluctlProject() error { var err error - if c.loadArgs.ProjectUrl == nil { - c.projectRootDir = c.loadArgs.RepoRoot - c.ProjectDir = c.loadArgs.ProjectDir - err = utils.CheckInDir(c.projectRootDir, c.ProjectDir) - if err != nil { - return err - } - } else { - gi, err := c.loadGitProject(&types2.GitProject{ - Url: *c.loadArgs.ProjectUrl, - Ref: c.loadArgs.ProjectRef, - }, "") - if err != nil { - return err - } - c.projectRootDir = gi.repoRoot - c.ProjectDir = gi.dir + c.projectRootDir = c.loadArgs.RepoRoot + c.ProjectDir = c.loadArgs.ProjectDir + err = utils.CheckInDir(c.projectRootDir, c.ProjectDir) + if err != nil { + return err } configPath := c.getConfigPath() @@ -145,89 +50,17 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { } } - err = c.updateGitCaches() - if err != nil { - return err - } - s := status.Start(c.ctx, "Loading kluctl project") defer s.Failed() - if c.loadArgs.LocalClusters != "" { - status.Deprecation(c.ctx, "--local-clusters", "--local-clusters is deprecated and will be removed in an upcoming version. Use variables loaded from git instead.") - } - if c.loadArgs.LocalDeployment != "" { - status.Deprecation(c.ctx, "--local-deployment", "--local-deployment is deprecated and will be removed in an upcoming version. Use git includes instead.") - } - if c.loadArgs.LocalSealedSecrets != "" { - status.Deprecation(c.ctx, "--local-sealed-secrets", "--local-sealed-secrets is deprecated and will be removed in an upcoming version.") - } - - deploymentInfo, err := c.loadExternalProject(c.Config.Deployment, "", c.loadArgs.LocalDeployment) - if err != nil { - return err - } - sealedSecretsInfo, err := c.loadExternalProject(c.Config.SealedSecrets, ".sealed-secrets", c.loadArgs.LocalSealedSecrets) - if err != nil { - return err - } - var clustersInfos []gitProjectInfo - if c.loadArgs.LocalClusters != "" { - clustersInfos = append(clustersInfos, c.localProject(c.loadArgs.LocalClusters)) - } else if len(c.Config.Clusters.Projects) != 0 { - for _, ep := range c.Config.Clusters.Projects { - info, err := c.loadExternalProject(&ep, "clusters", "") - if err != nil { - return err - } - clustersInfos = append(clustersInfos, info) - } - } else { - ci, err := c.loadExternalProject(nil, "clusters", "") - if err != nil { - return err - } - clustersInfos = append(clustersInfos, ci) - } - - mergedClustersDir := filepath.Join(c.TmpDir, "merged-clusters") - err = c.mergeClustersDirs(mergedClustersDir, clustersInfos) + err = c.updateGitCaches() if err != nil { return err } - c.DeploymentDir = deploymentInfo.dir - c.ClustersDir = mergedClustersDir - c.sealedSecretsDir = sealedSecretsInfo.dir + c.sealedSecretsDir = filepath.Join(c.ProjectDir, ".sealed-secrets") s.Success() return nil } - -func (c *LoadedKluctlProject) mergeClustersDirs(mergedClustersDir string, clustersInfos []gitProjectInfo) error { - err := os.MkdirAll(mergedClustersDir, 0o700) - if err != nil { - return err - } - - for _, ci := range clustersInfos { - if !utils.IsDirectory(ci.dir) { - continue - } - files, err := ioutil.ReadDir(ci.dir) - if err != nil { - return err - } - for _, fi := range files { - p := filepath.Join(ci.dir, fi.Name()) - if utils.IsFile(p) { - err = utils.CopyFile(p, filepath.Join(mergedClustersDir, fi.Name())) - if err != nil { - return err - } - } - } - } - return nil -} diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index b2c9ad978..3015d4345 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -26,8 +26,8 @@ type TargetContext struct { DeploymentCollection *deployment.DeploymentCollection } -func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName string, clusterName *string, offlineK8s bool, dryRun bool, externalArgs *uo.UnstructuredObject, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { - deploymentDir, err := filepath.Abs(p.DeploymentDir) +func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName string, offlineK8s bool, dryRun bool, externalArgs *uo.UnstructuredObject, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { + deploymentDir, err := filepath.Abs(p.ProjectDir) if err != nil { return nil, err } @@ -43,7 +43,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s images.PrependFixedImages(target.Images) } - varsCtx, clientConfig, clusterContext, err := p.buildVars(target, clusterName, offlineK8s, externalArgs, forSeal) + varsCtx, clientConfig, clusterContext, err := p.buildVars(target, offlineK8s, externalArgs, forSeal) if err != nil { return nil, err } @@ -104,32 +104,14 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s return targetCtx, nil } -func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *string, offlineK8s bool, externalArgs *uo.UnstructuredObject, forSeal bool) (*vars.VarsCtx, *rest.Config, string, error) { +func (p *LoadedKluctlProject) buildVars(target *types.Target, offlineK8s bool, externalArgs *uo.UnstructuredObject, forSeal bool) (*vars.VarsCtx, *rest.Config, string, error) { doError := func(err error) (*vars.VarsCtx, *rest.Config, string, error) { return nil, nil, "", err } varsCtx := vars.NewVarsCtx(p.J2) - var contextName *string - if clusterName == nil && target != nil { - clusterName = target.Cluster - contextName = target.Context - } - - if clusterName != nil { - clusterConfig, err := p.LoadClusterConfig(*clusterName) - if err != nil { - return doError(err) - } - err = varsCtx.UpdateChildFromStruct("cluster", clusterConfig.Cluster) - if err != nil { - return doError(err) - } - if contextName == nil { - contextName = &clusterConfig.Cluster.Context - } - } + contextName := target.Context var err error var clientConfig *rest.Config @@ -173,7 +155,7 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, clusterName *strin } } - err = deployment.LoadDeploymentArgs(p.DeploymentDir, varsCtx, allArgs) + err = deployment.LoadDeploymentArgs(p.ProjectDir, varsCtx, allArgs) if err != nil { return doError(err) } @@ -197,7 +179,7 @@ func (p *LoadedKluctlProject) findSecretsEntry(name string) (*types.SecretSet, e } func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.VarsCtx, varsLoader *vars.VarsLoader) error { - searchDirs := []string{p.DeploymentDir} + searchDirs := []string{p.ProjectDir} for _, secretSetName := range target.SealingConfig.SecretSets { secretEntry, err := p.findSecretsEntry(secretSetName) @@ -216,17 +198,3 @@ func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.Va } return nil } - -func (p *LoadedKluctlProject) LoadClusterConfig(clusterName string) (*types.ClusterConfig, error) { - var err error - var clusterConfig *types.ClusterConfig - - status.Deprecation(p.ctx, "cluster-config", "Cluster configurations have been deprecated and support for them will be removed in a future kluctl release.") - - clusterConfig, err = types.LoadClusterConfig(p.ClustersDir, clusterName) - if err != nil { - return nil, err - } - - return clusterConfig, nil -} diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 17f3f784c..3be7f1d60 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -87,16 +87,6 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) error { return err } - if target.Cluster != nil { - cc, err := c.LoadClusterConfig(*target.Cluster) - if err == nil { - err = varsCtx.UpdateChildFromStruct("cluster", cc.Cluster) - if err != nil { - return err - } - } - } - changed, err := varsCtx.RenderStruct(target) if err == nil && !changed { return nil diff --git a/pkg/types/cluster_config.go b/pkg/types/cluster_config.go deleted file mode 100644 index fe8ef11b2..000000000 --- a/pkg/types/cluster_config.go +++ /dev/null @@ -1,83 +0,0 @@ -package types - -import ( - "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" - "path/filepath" -) - -type ClusterConfig2 struct { - Name string `yaml:"name" validate:"required"` - Context string `yaml:"context" validate:"required"` - Vars *uo.UnstructuredObject `yaml:"vars,omitempty"` -} - -type ClusterConfig struct { - Cluster *ClusterConfig2 `yaml:"cluster"` -} - -// TODO remove custom unmarshaller when https://github.com/goccy/go-yaml/pull/220 gets released -func (cc *ClusterConfig2) UnmarshalYAML(unmarshal func(interface{}) error) error { - var u uo.UnstructuredObject - err := unmarshal(&u) - if err != nil { - return err - } - - name, ok, err := u.GetNestedString("name") - if !ok { - return fmt.Errorf("name is missing (or not a string) in cluster config") - } - context, ok, err := u.GetNestedString("context") - if !ok { - return fmt.Errorf("context is missing (or not a string) in cluster config") - } - - cc.Name = name - cc.Context = context - - _ = u.RemoveNestedField("name") - _ = u.RemoveNestedField("context") - - cc.Vars = &u - return nil -} - -func (cc *ClusterConfig2) MarshalYAML() (interface{}, error) { - o := uo.FromMap(map[string]interface{}{ - "name": cc.Name, - "context": cc.Context, - }) - o.Merge(cc.Vars) - return o, nil -} - -func LoadClusterConfig(clusterDir string, clusterName string) (*ClusterConfig, error) { - if clusterName == "" { - return nil, fmt.Errorf("cluster name must be specified") - } - - p := yaml.FixPathExt(filepath.Join(clusterDir, fmt.Sprintf("%s.yml", clusterName))) - if !utils.IsFile(p) { - return nil, fmt.Errorf("cluster config for %s not found", clusterName) - } - - err := utils.CheckInDir(clusterDir, p) - if err != nil { - return nil, fmt.Errorf("cluster config for %s is not in cluster dir", clusterName) - } - - var config ClusterConfig - err = yaml.ReadYamlFile(p, &config) - if err != nil { - return nil, err - } - - if config.Cluster.Name != clusterName { - return nil, fmt.Errorf("cluster name in config (%s) does not match requested cluster name %s", config.Cluster.Name, clusterName) - } - - return &config, nil -} diff --git a/pkg/types/git_project.go b/pkg/types/git_project.go index efa7c6dc8..dd8c7bae6 100644 --- a/pkg/types/git_project.go +++ b/pkg/types/git_project.go @@ -43,21 +43,6 @@ func ValidateExternalProject(sl validator.StructLevel) { } } -type ExternalProjects struct { - Projects []ExternalProject -} - -func (gp *ExternalProjects) UnmarshalYAML(unmarshal func(interface{}) error) error { - singleProject := ExternalProject{} - if err := unmarshal(&singleProject); err == nil { - // it's a single project - gp.Projects = []ExternalProject{singleProject} - return nil - } - // try as array - return unmarshal(&gp.Projects) -} - func init() { yaml.Validator.RegisterStructValidation(ValidateGitProject, GitProject{}) yaml.Validator.RegisterStructValidation(ValidateExternalProject, ExternalProject{}) diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 3f7efdf59..9fddb42e8 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -30,7 +30,6 @@ type SealingConfig struct { type Target struct { Name string `yaml:"name" validate:"required"` - Cluster *string `yaml:"cluster,omitempty"` Context *string `yaml:"context,omitempty"` Args *uo.UnstructuredObject `yaml:"args,omitempty"` DynamicArgs []DynamicArg `yaml:"dynamicArgs,omitempty"` @@ -71,11 +70,8 @@ type SecretsConfig struct { } type KluctlProject struct { - Deployment *ExternalProject `yaml:"deployment,omitempty"` - SealedSecrets *ExternalProject `yaml:"sealedSecrets,omitempty"` - Clusters ExternalProjects `yaml:"clusters,omitempty"` - Targets []*Target `yaml:"targets,omitempty"` - SecretsConfig *SecretsConfig `yaml:"secretsConfig,omitempty"` + Targets []*Target `yaml:"targets,omitempty"` + SecretsConfig *SecretsConfig `yaml:"secretsConfig,omitempty"` } func init() { From 0cb6433818d30f18602688fa15aa3cfe40a7af99 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 22:06:24 +0200 Subject: [PATCH 0413/2268] chore: Run go mod tidy --- go.sum | 60 ---------------------------------------------------------- 1 file changed, 60 deletions(-) diff --git a/go.sum b/go.sum index cc6a47da0..379bb254c 100644 --- a/go.sum +++ b/go.sum @@ -88,8 +88,6 @@ github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2B github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErIPzRrnogAXYwSoU7txA977LjDGrbkewJbg= -github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad h1:QeeqI2zxxgZVe11UrYFXXx6gVxPVF40ygekjBzEg4XY= github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= @@ -116,8 +114,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.105 h1:UUwoD1PRKIj3ltrDUYTDQj5fOTK3XsnqolLpRTMmSEM= -github.com/aws/aws-sdk-go v1.44.105/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.116 h1:NpLIhcvLWXJZAEwvPj3TDHeqp7DleK6ZUVYyW01WNHY= github.com/aws/aws-sdk-go v1.44.116/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -125,8 +121,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.18.5 h1:PT/lxfgWnP8l8ybpvTsHuJltY4A35LI8if19Ww9FbcU= -github.com/bitnami-labs/sealed-secrets v0.18.5/go.mod h1:nzCyKhzDRbNySRUZXR6tvui4s95LbuaNpBoGRvV9Nk8= github.com/bitnami-labs/sealed-secrets v0.19.1 h1:ZNLcVtTXRf7VkyNzyhe9omlwNYI0OHDteTbIHsQI7Ug= github.com/bitnami-labs/sealed-secrets v0.19.1/go.mod h1:5UcsiOdOoviJUtXY1GNSeFa8zezbBY+j9pQAA2RtKyw= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= @@ -140,7 +134,6 @@ github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -179,14 +172,10 @@ 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/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= -github.com/docker/cli v20.10.18+incompatible h1:f/GQLsVpo10VvToRay2IraVA1wHz9KktZyjev3SIVDU= -github.com/docker/cli v20.10.18+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.19+incompatible h1:VKVBUb0KY/bx0FUCrCiNCL8wqgy8VxQli1dtNTn38AE= github.com/docker/cli v20.10.19+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= -github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.19+incompatible h1:lzEmjivyNHFHMNAFLXORMBXyGIhw/UP4DvJwvyKYq64= github.com/docker/docker v20.10.19+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= @@ -233,8 +222,6 @@ github.com/fluxcd/pkg/kustomize v0.8.0/go.mod h1:zGtCZF6V3hMWcf46SqrQc10fS9yUlKz github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -456,8 +443,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.8.0 h1:7765sW1XBt+qf4XKIYE4ebY9qc/yi9V2/egzGSUNMZU= -github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= github.com/hashicorp/vault/api v1.8.1 h1:bMieWIe6dAlqAAPReZO/8zYtXaWUg/21umwqGZpEjCI= github.com/hashicorp/vault/api v1.8.1/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= github.com/hashicorp/vault/sdk v0.6.0 h1:6Z+In5DXHiUfZvIZdMx7e2loL1PPyDjA4bVh9ZTIAhs= @@ -632,8 +617,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW 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/ohler55/ojg v1.14.4 h1:L2ds8AlB5t/QbqSfhRwvagJzQ7pgmdrefMIypQs0Xik= -github.com/ohler55/ojg v1.14.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/ohler55/ojg v1.14.5 h1:xCX2oyh/ZaoesbLH6fwVHStSJpk4o4eJs8ttXutzdg0= github.com/ohler55/ojg v1.14.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= @@ -644,8 +627,6 @@ github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc1 h1:lfG+OTa7V8PD3PKvkocSG9KAcA9MANqJn53m31Fvwkc= -github.com/opencontainers/image-spec v1.1.0-rc1/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -751,8 +732,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU 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.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -824,8 +803,6 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.starlark.net v0.0.0-20220926145019-14b050677505 h1:W0MibAL5BiEenQR+F/EF/a4HJhgLngHVvm6jbtUW0PM= -go.starlark.net v0.0.0-20220926145019-14b050677505/go.mod h1:qsNirHv+Awo5xHuNyQ/0niov6kDxdBs+bqpVMBCW77k= go.starlark.net v0.0.0-20221010140840-6bf6f0955179 h1:Mc5MkF55Iasgq23vSYpL6/l7EJXtlNjzw+8hbMQ/ShY= go.starlark.net v0.0.0-20221010140840-6bf6f0955179/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -855,8 +832,6 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -957,8 +932,6 @@ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -972,8 +945,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220923202941-7f9b1623fab7 h1:ZrnxWX62AgTKOSagEqxvb3ffipvEDX2pl7E1TdqLqIc= -golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1044,7 +1015,6 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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= @@ -1064,7 +1034,6 @@ 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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= @@ -1209,8 +1178,6 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc h1:saaNe2+SBQxandnzcD/qB1JEBQ2Pqew+KlFLLdA/XcM= -google.golang.org/genproto v0.0.0-20220923205249-dd2d53f1fffc/go.mod h1:yEEpwVWKMZZzo81NwRgyEJnA2fQvpXAYPVisv8EgDVs= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a h1:GH6UPn3ixhWcKDhpnEC55S75cerLPdpp3hrhfKYjZgw= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1235,8 +1202,6 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG 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.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1289,8 +1254,6 @@ 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.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -helm.sh/helm/v3 v3.10.0 h1:y/MYONZ/bsld9kHwqgBX2uPggnUr5hahpjwt9/jrHlI= -helm.sh/helm/v3 v3.10.0/go.mod h1:paPw0hO5KVfrCMbi1M8+P8xdfBri3IiJiVKATZsFR94= helm.sh/helm/v3 v3.10.1 h1:uTnNlYx8QcTSNA4ZJ50Llwife4CSohUY4ehumyVf2QE= helm.sh/helm/v3 v3.10.1/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1300,51 +1263,28 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= -k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= -k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= -k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8= k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= -k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= -k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= -k8s.io/apiserver v0.25.2 h1:YePimobk187IMIdnmsMxsfIbC5p4eX3WSOrS9x6FEYw= -k8s.io/apiserver v0.25.2/go.mod h1:30r7xyQTREWCkG2uSjgjhQcKVvAAlqoD+YyrqR6Cn+I= k8s.io/apiserver v0.25.3 h1:m7+xGuG5+KYAnEsqaFtDyWMkmMMEOFYlu+NlWv5qSBI= k8s.io/apiserver v0.25.3/go.mod h1:9bT47iM2fzRuhICJpM/RcQR9sqDDfZ7Yw60h0p3JW08= -k8s.io/cli-runtime v0.25.2 h1:XOx+SKRjBpYMLY/J292BHTkmyDffl/qOx3YSuFZkTuc= -k8s.io/cli-runtime v0.25.2/go.mod h1:OQx3+/0st6x5YpkkJQlEWLC73V0wHsOFMC1/roxV8Oc= k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= k8s.io/cli-runtime v0.25.3/go.mod h1:InHHsjkyW5hQsILJGpGjeruiDZT/R0OkROQgD6GzxO4= -k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= -k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0= k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA= -k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY= -k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60= k8s.io/component-base v0.25.3 h1:UrsxciGdrCY03ULT1h/S/gXFCOPnLhUVwSyx+hM/zq4= k8s.io/component-base v0.25.3/go.mod h1:WYoS8L+IlTZgU7rhAl5Ctpw0WdMxDfCC5dkxcEFa/TI= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea h1:3QOH5+2fGsY8e1qf+GIFpg+zw/JGNrgyZRQR7/m6uWg= -k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= 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/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA= -k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg= k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= -k8s.io/utils v0.0.0-20220922133306-665eaaec4324 h1:i+xdFemcSNuJvIfBlaYuXgRondKxK4z4prVPKzEaelI= -k8s.io/utils v0.0.0-20220922133306-665eaaec4324/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20221012122500-cfd413dd9e85 h1:cTdVh7LYu82xeClmfzGtgyspNh6UxpwLWGi8R4sspNo= k8s.io/utils v0.0.0-20221012122500-cfd413dd9e85/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4= -oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= oras.land/oras-go v1.2.1 h1:/VcGS8FUy3eEXLl/1vC4QypLHwrfSmgW7ygsoklqKK8= oras.land/oras-go v1.2.1/go.mod h1:3N11Z5E3c4ZzOjroCl1RtAdB4yNAYl7A27j2SVf913A= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From 5c2cb4fa9505d450b682ee34a3cba7918bfa4670 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 22:18:33 +0200 Subject: [PATCH 0414/2268] refactor: Remove some unused code --- e2e/utils_resources.go | 21 --------------------- pkg/utils/debugger.go | 26 -------------------------- 2 files changed, 47 deletions(-) delete mode 100644 pkg/utils/debugger.go diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 38ebbde01..4bf65af62 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -1,10 +1,8 @@ package e2e import ( - "bytes" "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "text/template" ) type resourceOpts struct { @@ -30,25 +28,6 @@ func mergeMetadata(o *uo.UnstructuredObject, opts resourceOpts) { } } -func renderTemplateHelper(tmpl string, m map[string]interface{}) string { - t := template.Must(template.New("").Parse(tmpl)) - r := bytes.NewBuffer(nil) - err := t.Execute(r, m) - if err != nil { - panic(err) - } - return r.String() -} - -func renderTemplateObjectHelper(tmpl string, m map[string]interface{}) []*uo.UnstructuredObject { - s := renderTemplateHelper(tmpl, m) - ret, err := uo.FromStringMulti(s) - if err != nil { - panic(err) - } - return ret -} - func addConfigMapDeployment(p *testProject, dir string, data map[string]string, opts resourceOpts) { o := uo.New() o.SetK8sGVKs("", "v1", "ConfigMap") diff --git a/pkg/utils/debugger.go b/pkg/utils/debugger.go deleted file mode 100644 index d16910726..000000000 --- a/pkg/utils/debugger.go +++ /dev/null @@ -1,26 +0,0 @@ -package utils - -import ( - "os" - - "github.com/mitchellh/go-ps" -) - -func IsLaunchedByDebugger() bool { - pid := os.Getppid() - - // We loop in case there were intermediary processes like the gopls language server. - for pid != 0 { - switch p, err := ps.FindProcess(pid); { - case p == nil: - return false - case err != nil: - return false - case p.Executable() == "dlv": - return true - default: - pid = p.PPid() - } - } - return false -} From 96e1a086fa86380d6ee95cca4cbc8817a2483b4e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 22:19:23 +0200 Subject: [PATCH 0415/2268] refactor: Stop using deprecated ioutil funcs --- cmd/kluctl/commands/utils.go | 7 +++---- internal/test-utils/git_server.go | 3 +-- pkg/deployment/deployment_item.go | 5 ++--- pkg/deployment/helm_chart.go | 3 +-- pkg/git/auth/env_auth_provider.go | 6 +++--- pkg/git/auth/ssh_auth_provider.go | 7 +++---- pkg/git/mirrored_repo.go | 11 +++++------ pkg/git/poor_mans_clone.go | 3 +-- pkg/git/repocache/cache.go | 3 +-- pkg/registries/registries.go | 10 +++++----- pkg/seal/fetch_cert.go | 4 ++-- pkg/vars/vars_loader.go | 3 +-- pkg/vars/vars_loader_http.go | 3 +-- pkg/vars/vars_loader_test.go | 18 +++++++++--------- 14 files changed, 38 insertions(+), 48 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 84b5f9162..0b3fe41d8 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -16,7 +16,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - "io/ioutil" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" @@ -24,7 +23,7 @@ import ( ) func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { - tmpDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "project-") + tmpDir, err := os.MkdirTemp(utils.GetTmpBaseDir(), "project-") if err != nil { return fmt.Errorf("creating temporary project directory failed: %w", err) } @@ -73,7 +72,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b if err != nil { return err } - err = ioutil.WriteFile(projectFlags.OutputMetadata, b, 0o640) + err = os.WriteFile(projectFlags.OutputMetadata, b, 0o640) if err != nil { return err } @@ -146,7 +145,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm renderOutputDir := args.renderOutputDirFlags.RenderOutputDir if renderOutputDir == "" { - tmpDir, err := ioutil.TempDir(p.TmpDir, "rendered") + tmpDir, err := os.MkdirTemp(p.TmpDir, "rendered") if err != nil { return err } diff --git a/internal/test-utils/git_server.go b/internal/test-utils/git_server.go index 29727fdeb..45a969474 100644 --- a/internal/test-utils/git_server.go +++ b/internal/test-utils/git_server.go @@ -8,7 +8,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - "io/ioutil" "log" "net" "net/http" @@ -33,7 +32,7 @@ func NewGitServer(t *testing.T) *GitServer { t: t, } - baseDir, err := ioutil.TempDir(os.TempDir(), "kluctl-tests-") + baseDir, err := os.MkdirTemp(os.TempDir(), "kluctl-tests-") if err != nil { p.t.Fatal(err) } diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 588a08fca..7f944e92f 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -9,7 +9,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/fs" - "io/ioutil" "k8s.io/apimachinery/pkg/runtime/schema" "os" "path/filepath" @@ -307,11 +306,11 @@ func (di *DeploymentItem) resolveSealedSecrets() error { if !utils.IsFile(sourcePath) { return fmt.Errorf("%s. %s not found. You might need to seal it first", baseError, sourcePath) } - b, err := ioutil.ReadFile(sourcePath) + b, err := os.ReadFile(sourcePath) if err != nil { return fmt.Errorf("failed to read source secret file %s: %w", sourcePath, err) } - err = ioutil.WriteFile(targetPath, b, 0o600) + err = os.WriteFile(targetPath, b, 0o600) if err != nil { return fmt.Errorf("failed to write target secret file %s: %w", targetPath, err) } diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 2d930c4b0..b7930f0f8 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -22,7 +22,6 @@ import ( "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/repo" - "io/ioutil" "k8s.io/apimachinery/pkg/runtime/schema" "os" "path/filepath" @@ -339,7 +338,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { return err } - err = ioutil.WriteFile(outputPath, rendered, 0o600) + err = os.WriteFile(outputPath, rendered, 0o600) if err != nil { return err } diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index 2c1483811..7f692b661 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -5,7 +5,7 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" - "io/ioutil" + "os" ) type GitEnvAuthProvider struct { @@ -31,7 +31,7 @@ func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr if ssh_key_path != "" { ssh_key_path = utils.ExpandPath(ssh_key_path) - b, err := ioutil.ReadFile(ssh_key_path) + b, err := os.ReadFile(ssh_key_path) if err != nil { status.Trace(ctx, "GitEnvAuthProvider: failed to read key %s: %v", ssh_key_path, err) } else { @@ -41,7 +41,7 @@ func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr ca_bundle_path := m["CA_BUNDLE"] if ca_bundle_path != "" { ca_bundle_path = utils.ExpandPath(ca_bundle_path) - b, err := ioutil.ReadFile(ca_bundle_path) + b, err := os.ReadFile(ca_bundle_path) if err != nil { status.Trace(ctx, "GitEnvAuthProvider: failed to read ca bundle %s: %v", ca_bundle_path, err) } else { diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index 9c347192b..c1be1b032 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -12,7 +12,6 @@ import ( sshagent "github.com/xanzy/ssh-agent" "golang.org/x/crypto/ssh" "io" - "io/ioutil" "os" "os/user" "path/filepath" @@ -176,7 +175,7 @@ func (k *deferredPassphraseKey) parse() { return } - pemBytes, err := ioutil.ReadFile(k.path) + pemBytes, err := os.ReadFile(k.path) if err != nil { k.err = err status.Warning(k.ctx, "Failed to parse key %s: %v", k.path, err) @@ -213,7 +212,7 @@ func (k *dummyPublicKey) Verify(data []byte, sig *ssh.Signature) error { } func (k *deferredPassphraseKey) Hash() ([]byte, error) { - pemBytes, err := ioutil.ReadFile(k.path) + pemBytes, err := os.ReadFile(k.path) if err != nil { return nil, err } @@ -252,7 +251,7 @@ func (k *deferredPassphraseKey) Sign(rand io.Reader, data []byte) (*ssh.Signatur } func (a *GitSshAuthProvider) readKey(ctx context.Context, path string) (ssh.Signer, error) { - pemBytes, err := ioutil.ReadFile(path) + pemBytes, err := os.ReadFile(path) if err != nil { return nil, err } else { diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 00eb928c6..c3a08ff7d 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -14,7 +14,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/rogpeppe/go-internal/lockedfile" - "io/ioutil" "os" "path/filepath" "strings" @@ -120,7 +119,7 @@ func (g *MirroredGitRepo) IsLocked() bool { } func (g *MirroredGitRepo) LastUpdateTime() time.Time { - s, err := ioutil.ReadFile(filepath.Join(g.mirrorDir, ".update-time")) + s, err := os.ReadFile(filepath.Join(g.mirrorDir, ".update-time")) if err != nil { return time.Time{} } @@ -183,7 +182,7 @@ func (g *MirroredGitRepo) buildRepositoryObject() (*git.Repository, error) { func (g *MirroredGitRepo) cleanupMirrorDir() error { if utils.IsDirectory(g.mirrorDir) { - files, err := ioutil.ReadDir(g.mirrorDir) + files, err := os.ReadDir(g.mirrorDir) if err != nil { return err } @@ -280,7 +279,7 @@ func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string) error } } - _ = ioutil.WriteFile(filepath.Join(g.mirrorDir, ".update-time"), []byte(time.Now().Format(time.RFC3339Nano)), 0644) + _ = os.WriteFile(filepath.Join(g.mirrorDir, ".update-time"), []byte(time.Now().Format(time.RFC3339Nano)), 0644) return nil } @@ -295,7 +294,7 @@ func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext) error { return err } - tmpMirrorDir, err := ioutil.TempDir(utils.GetTmpBaseDir(), "mirror-") + tmpMirrorDir, err := os.MkdirTemp(utils.GetTmpBaseDir(), "mirror-") if err != nil { return err } @@ -320,7 +319,7 @@ func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext) error { return err } - files, err := ioutil.ReadDir(tmpMirrorDir) + files, err := os.ReadDir(tmpMirrorDir) if err != nil { return err } diff --git a/pkg/git/poor_mans_clone.go b/pkg/git/poor_mans_clone.go index d5216cb49..f5f306924 100644 --- a/pkg/git/poor_mans_clone.go +++ b/pkg/git/poor_mans_clone.go @@ -4,7 +4,6 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/kluctl/kluctl/v2/pkg/utils" - "io/ioutil" "os" "path/filepath" "runtime" @@ -63,7 +62,7 @@ func PoorMansClone(sourceDir string, targetDir string, coOptions *git.CheckoutOp if err != nil { return err } - err = ioutil.WriteFile(filepath.Join(targetDir, ".git", "config"), b, 0o600) + err = os.WriteFile(filepath.Join(targetDir, ".git", "config"), b, 0o600) if err != nil { return err } diff --git a/pkg/git/repocache/cache.go b/pkg/git/repocache/cache.go index 76b4aa813..0f9dfcb90 100644 --- a/pkg/git/repocache/cache.go +++ b/pkg/git/repocache/cache.go @@ -9,7 +9,6 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/utils" - "io/ioutil" "os" "path" "path/filepath" @@ -198,7 +197,7 @@ func (e *CacheEntry) GetClonedDir(ref string) (string, git.CheckoutInfo, error) } repoName = strings.ReplaceAll(repoName, "/", "-") - p, err := ioutil.TempDir(tmpDir, repoName) + p, err := os.MkdirTemp(tmpDir, repoName) if err != nil { return "", git.CheckoutInfo{}, err } diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index f51f99913..731f3cf27 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -15,7 +15,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "io/ioutil" + "io" "net/http" "net/http/httputil" "net/url" @@ -180,7 +180,7 @@ func (rh *RegistryHelper) ParseAuthEntriesFromEnv() error { ca_bundle_path := m["CA_BUNDLE"] if ca_bundle_path != "" { ca_bundle_path = utils.ExpandPath(ca_bundle_path) - b, err := ioutil.ReadFile(ca_bundle_path) + b, err := os.ReadFile(ca_bundle_path) if err != nil { return fmt.Errorf("failed to read ca bundle %s: %w", ca_bundle_path, err) } else { @@ -319,7 +319,7 @@ func (rh *RegistryHelper) readCachedResponse(key string) []byte { return nil } - b, err := ioutil.ReadFile(cachePath) + b, err := os.ReadFile(cachePath) if err != nil { return nil } @@ -327,7 +327,7 @@ func (rh *RegistryHelper) readCachedResponse(key string) []byte { res, err := http.ReadResponse(bufio.NewReader(bytes.NewReader(b)), nil) if strings.HasPrefix(res.Header.Get("Content-Type"), "application/json") { - jb, err := ioutil.ReadAll(res.Body) + jb, err := io.ReadAll(res.Body) if err != nil { return nil } @@ -349,7 +349,7 @@ func (rh *RegistryHelper) writeCachedResponse(key string, data []byte) { } } - err := ioutil.WriteFile(cachePath+".tmp", data, 0o600) + err := os.WriteFile(cachePath+".tmp", data, 0o600) if err != nil { status.Warning(rh.ctx, "writeCachedResponse failed: %v", err) return diff --git a/pkg/seal/fetch_cert.go b/pkg/seal/fetch_cert.go index 5c1c28963..6916a1845 100644 --- a/pkg/seal/fetch_cert.go +++ b/pkg/seal/fetch_cert.go @@ -10,7 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "io/ioutil" + "io" v12 "k8s.io/api/core/v1" "k8s.io/client-go/util/cert" ) @@ -68,7 +68,7 @@ func openCertFromController(k *k8s.K8sCluster, namespace, name string) ([]byte, } defer r.Close() - cert, err := ioutil.ReadAll(r) + cert, err := io.ReadAll(r) if err != nil { return nil, err } diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 84986f0a1..6a1953644 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -16,7 +16,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/vars/aws" "github.com/kluctl/kluctl/v2/pkg/vars/vault" "github.com/kluctl/kluctl/v2/pkg/yaml" - "io/ioutil" "k8s.io/apimachinery/pkg/runtime/schema" "os" ) @@ -185,7 +184,7 @@ func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, roo return err } - f, err := ioutil.ReadFile(path) + f, err := os.ReadFile(path) if err != nil { return err } diff --git a/pkg/vars/vars_loader_http.go b/pkg/vars/vars_loader_http.go index 379e1730e..2f70cd260 100644 --- a/pkg/vars/vars_loader_http.go +++ b/pkg/vars/vars_loader_http.go @@ -10,7 +10,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "io" - "io/ioutil" "net/http" "strings" ) @@ -53,7 +52,7 @@ func (v *VarsLoader) doHttp(httpSource *types.VarsSourceHttp, username string, p } defer resp.Body.Close() - respBody, err := ioutil.ReadAll(resp.Body) + respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, "", err } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 6cac07687..95c8ca022 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -15,7 +15,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars/aws" "github.com/stretchr/testify/assert" - "io/ioutil" + "io" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -28,7 +28,7 @@ import ( ) func newTestDir(t *testing.T) string { - tmp, err := ioutil.TempDir("", "") + tmp, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } @@ -74,7 +74,7 @@ func TestVarsLoader_Values(t *testing.T) { func TestVarsLoader_File(t *testing.T) { d := newTestDir(t) - _ = ioutil.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": 42}}`), 0o600) + _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": 42}}`), 0o600) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { err := vl.LoadVars(vc, &types.VarsSource{ @@ -89,8 +89,8 @@ func TestVarsLoader_File(t *testing.T) { func TestVarsLoader_FileWithLoad(t *testing.T) { d := newTestDir(t) - _ = ioutil.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{ load_template("test2.txt") }}}}`), 0o600) - _ = ioutil.WriteFile(filepath.Join(d, "test2.txt"), []byte(`42`), 0o600) + _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{ load_template("test2.txt") }}}}`), 0o600) + _ = os.WriteFile(filepath.Join(d, "test2.txt"), []byte(`42`), 0o600) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { err := vl.LoadVars(vc, &types.VarsSource{ @@ -106,8 +106,8 @@ func TestVarsLoader_FileWithLoad(t *testing.T) { func TestVarsLoader_FileWithLoadSubDir(t *testing.T) { d := newTestDir(t) _ = os.Mkdir(filepath.Join(d, "subdir"), 0o700) - _ = ioutil.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{ load_template("test2.txt") }}}}`), 0o600) - _ = ioutil.WriteFile(filepath.Join(d, "subdir/test2.txt"), []byte(`42`), 0o600) + _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{ load_template("test2.txt") }}}}`), 0o600) + _ = os.WriteFile(filepath.Join(d, "subdir/test2.txt"), []byte(`42`), 0o600) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { err := vl.LoadVars(vc, &types.VarsSource{ @@ -122,7 +122,7 @@ func TestVarsLoader_FileWithLoadSubDir(t *testing.T) { func TestVarsLoader_FileWithLoadNotExists(t *testing.T) { d := newTestDir(t) - _ = ioutil.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{load_template("test3.txt")}}}}`), 0o600) + _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{load_template("test3.txt")}}}}`), 0o600) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { err := vl.LoadVars(vc, &types.VarsSource{ @@ -385,7 +385,7 @@ func TestVarsLoader_Http_POST(t *testing.T) { w.WriteHeader(542) return } - body, _ := ioutil.ReadAll(r.Body) + body, _ := io.ReadAll(r.Body) if string(body) != "body" { w.WriteHeader(543) return From a4d30666a09d2ccfa81c05303f99bdec7ef05664 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 22:19:37 +0200 Subject: [PATCH 0416/2268] refactor: Remove unused WithPriority --- pkg/status/status.go | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/pkg/status/status.go b/pkg/status/status.go index 8c8f158c3..5c8675bdd 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -14,11 +14,10 @@ type StatusContext struct { finished bool failed bool - prefix string - startMessage string - startPriority int - startTotal int - disableLogs bool + prefix string + startMessage string + startTotal int + disableLogs bool } type EndResult int @@ -101,12 +100,6 @@ func WithStatus(message string, args ...any) Option { } } -func WithPriority(p int) Option { - return func(s *StatusContext) { - s.startPriority = p - } -} - func WithTotal(t int) Option { return func(s *StatusContext) { s.startTotal = t From b34c45e0f69eb83f114ce4c62fffa5135601887f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 17 Oct 2022 22:25:13 +0200 Subject: [PATCH 0417/2268] fix: Fix mixed value and pointer receivers --- pkg/git/git-url/url.go | 2 +- pkg/types/url.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/git/git-url/url.go b/pkg/git/git-url/url.go index 452df12ee..ff3ae35bd 100644 --- a/pkg/git/git-url/url.go +++ b/pkg/git/git-url/url.go @@ -33,7 +33,7 @@ func (u *GitUrl) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } -func (u GitUrl) MarshalYAML() (interface{}, error) { +func (u *GitUrl) MarshalYAML() (interface{}, error) { return u.String(), nil } diff --git a/pkg/types/url.go b/pkg/types/url.go index 703f8dfba..400328b3a 100644 --- a/pkg/types/url.go +++ b/pkg/types/url.go @@ -22,6 +22,6 @@ func (u *YamlUrl) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } -func (u YamlUrl) MarshalYAML() (interface{}, error) { +func (u *YamlUrl) MarshalYAML() (interface{}, error) { return u.String(), nil } From 73ec171bc0138a2821d217bbfbbcb1ff446595c7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 08:51:13 +0200 Subject: [PATCH 0418/2268] feat: Remove --output-metadata flag --- cmd/kluctl/args/project.go | 3 +-- cmd/kluctl/commands/utils.go | 12 ------------ pkg/kluctl_project/project.go | 7 ------- pkg/types/metadata.go | 5 ----- 4 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 pkg/types/metadata.go diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index d142ece1a..a5a29038e 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -3,8 +3,7 @@ package args import "time" type ProjectFlags struct { - ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` - OutputMetadata string `group:"project" help:"Specify the output path for the project metadata to be written to."` + ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 0b3fe41d8..209052620 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -15,7 +15,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" @@ -66,17 +65,6 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b return err } - if projectFlags.OutputMetadata != "" { - md := p.GetMetadata() - b, err := yaml.WriteYamlBytes(md) - if err != nil { - return err - } - err = os.WriteFile(projectFlags.OutputMetadata, b, 0o640) - if err != nil { - return err - } - } return cb(ctx, p) } diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 0f89a9eb1..29d27077f 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -28,13 +28,6 @@ type LoadedKluctlProject struct { RP *repocache.GitRepoCache } -func (c *LoadedKluctlProject) GetMetadata() *types2.ProjectMetadata { - md := &types2.ProjectMetadata{ - Targets: c.DynamicTargets, - } - return md -} - func (c *LoadedKluctlProject) FindBaseTarget(name string) (*types2.Target, error) { for _, target := range c.Config.Targets { if target.Name == name { diff --git a/pkg/types/metadata.go b/pkg/types/metadata.go deleted file mode 100644 index f734de0ad..000000000 --- a/pkg/types/metadata.go +++ /dev/null @@ -1,5 +0,0 @@ -package types - -type ProjectMetadata struct { - Targets []*DynamicTarget `yaml:"targets"` -} From f464d4999bb42eed2aec9dba91492a8a493cba13 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 11:50:00 +0200 Subject: [PATCH 0419/2268] Revert "fix: Fix mixed value and pointer receivers" This reverts commit b34c45e0f69eb83f114ce4c62fffa5135601887f. --- pkg/git/git-url/url.go | 2 +- pkg/types/url.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/git/git-url/url.go b/pkg/git/git-url/url.go index ff3ae35bd..452df12ee 100644 --- a/pkg/git/git-url/url.go +++ b/pkg/git/git-url/url.go @@ -33,7 +33,7 @@ func (u *GitUrl) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } -func (u *GitUrl) MarshalYAML() (interface{}, error) { +func (u GitUrl) MarshalYAML() (interface{}, error) { return u.String(), nil } diff --git a/pkg/types/url.go b/pkg/types/url.go index 400328b3a..703f8dfba 100644 --- a/pkg/types/url.go +++ b/pkg/types/url.go @@ -22,6 +22,6 @@ func (u *YamlUrl) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } -func (u *YamlUrl) MarshalYAML() (interface{}, error) { +func (u YamlUrl) MarshalYAML() (interface{}, error) { return u.String(), nil } From 6a8160b488e5276514481d4167557335b5e6c32d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 12:04:34 +0200 Subject: [PATCH 0420/2268] refactor: Stop using GetGitTree and instead do a plain clone again Speed improvement was not worth it and it made implementing overrides harder. --- pkg/git/repocache/cache.go | 23 ------------- pkg/kluctl_project/targets.go | 53 ++++-------------------------- pkg/kluctl_project/targets_test.go | 42 +---------------------- 3 files changed, 7 insertions(+), 111 deletions(-) diff --git a/pkg/git/repocache/cache.go b/pkg/git/repocache/cache.go index 0f9dfcb90..e7f6dd11b 100644 --- a/pkg/git/repocache/cache.go +++ b/pkg/git/repocache/cache.go @@ -3,7 +3,6 @@ package repocache import ( "context" "fmt" - "github.com/go-git/go-git/v5/plumbing/object" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" @@ -224,25 +223,3 @@ func (e *CacheEntry) GetClonedDir(ref string) (string, git.CheckoutInfo, error) } return p, repoInfo, nil } - -func (e *CacheEntry) GetGitTree(ref string) (*object.Tree, error) { - e.updateMutex.Lock() - defer e.updateMutex.Unlock() - - err := e.mr.Lock() - if err != nil { - return nil, err - } - defer e.mr.Unlock() - - if ref == "" { - ref = e.defaultRef - } - - _, commit, err := e.findCommit(ref) - if err != nil { - return nil, err - } - - return e.mr.GetGitTreeByCommit(commit) -} diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 3be7f1d60..0600dae27 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -3,14 +3,12 @@ package kluctl_project import ( "fmt" securejoin "github.com/cyphar/filepath-securejoin" - "github.com/go-git/go-git/v5/plumbing/object" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" - "io" "os" "regexp" "sort" @@ -20,7 +18,6 @@ import ( type dynamicTargetInfo struct { baseTarget *types.Target dir string - gitTree *object.Tree gitProject *types.GitProject ref *string refPattern *string @@ -169,11 +166,14 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Ta return nil, err } - gitTree, err := ge.GetGitTree(refShortName) + dir, _, err := ge.GetClonedDir(refShortName) + if err != nil { + return nil, err + } dynamicTargets = append(dynamicTargets, &dynamicTargetInfo{ baseTarget: baseTarget, - gitTree: gitTree, + dir: dir, gitProject: baseTarget.TargetConfig.Project, ref: &refShortName, refPattern: refPattern, @@ -208,40 +208,7 @@ func (c *LoadedKluctlProject) matchRef(s string, pattern string) (bool, string, } } -func (c *LoadedKluctlProject) loadTargetConfigFileFromGit(targetInfo *dynamicTargetInfo) ([]byte, error) { - existsFunc := func(path string) bool { - e, err := targetInfo.gitTree.FindEntry(path) - if e == nil || err != nil { - return false - } - return true - } - - var configFile string - - if targetInfo.baseTarget.TargetConfig.File != nil { - configFile = *targetInfo.baseTarget.TargetConfig.File - } else { - configFile = "target-config.yml" - if !existsFunc(configFile) { - configFile = "target-config.yaml" - } - } - - f, err := targetInfo.gitTree.File(configFile) - if err != nil { - return nil, fmt.Errorf("failed to load target config: %w", err) - } - r, err := f.Reader() - if err != nil { - return nil, fmt.Errorf("failed to load target config: %w", err) - } - defer r.Close() - - return io.ReadAll(r) -} - -func (c *LoadedKluctlProject) loadTargetConfigFileFromLocal(targetInfo *dynamicTargetInfo) ([]byte, error) { +func (c *LoadedKluctlProject) loadTargetConfigFile(targetInfo *dynamicTargetInfo) ([]byte, error) { configFile := yaml.FixNameExt(targetInfo.dir, "target-config.yml") if targetInfo.baseTarget.TargetConfig.File != nil { configFile = *targetInfo.baseTarget.TargetConfig.File @@ -257,14 +224,6 @@ func (c *LoadedKluctlProject) loadTargetConfigFileFromLocal(targetInfo *dynamicT return os.ReadFile(configPath) } -func (c *LoadedKluctlProject) loadTargetConfigFile(targetInfo *dynamicTargetInfo) ([]byte, error) { - if targetInfo.gitTree != nil { - return c.loadTargetConfigFileFromGit(targetInfo) - } else { - return c.loadTargetConfigFileFromLocal(targetInfo) - } -} - func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) (*types.Target, error) { var target types.Target err := utils.DeepCopy(&target, targetInfo.baseTarget) diff --git a/pkg/kluctl_project/targets_test.go b/pkg/kluctl_project/targets_test.go index 628a69d4b..8f1cf30b1 100644 --- a/pkg/kluctl_project/targets_test.go +++ b/pkg/kluctl_project/targets_test.go @@ -1,17 +1,14 @@ package kluctl_project import ( - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/object" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/stretchr/testify/assert" "os" "path/filepath" "testing" - "time" ) -func TestLoadTargetConfigFileFromLocal(t *testing.T) { +func TestLoadTargetConfigFile(t *testing.T) { c := LoadedKluctlProject{} ti := &dynamicTargetInfo{ baseTarget: &types.Target{ @@ -27,40 +24,3 @@ func TestLoadTargetConfigFileFromLocal(t *testing.T) { assert.Equal(t, []byte("test"), data) } - -func TestLoadTargetConfigFileFromGit(t *testing.T) { - dir := t.TempDir() - r, err := git.PlainInit(dir, false) - assert.NoError(t, err) - - wt, err := r.Worktree() - assert.NoError(t, err) - - err = os.WriteFile(filepath.Join(dir, "target-config.yml"), []byte("test"), 0600) - assert.NoError(t, err) - - _, err = wt.Add("target-config.yml") - assert.NoError(t, err) - - h, err := wt.Commit("test", &git.CommitOptions{Author: &object.Signature{Name: "test", Email: "test@test.com", When: time.Now()}}) - assert.NoError(t, err) - - commit, err := object.GetCommit(r.Storer, h) - assert.NoError(t, err) - - gitTree, err := commit.Tree() - assert.NoError(t, err) - - c := LoadedKluctlProject{} - ti := &dynamicTargetInfo{ - baseTarget: &types.Target{ - TargetConfig: &types.ExternalTargetConfig{}, - }, - gitTree: gitTree, - } - - data, err := c.loadTargetConfigFile(ti) - assert.NoError(t, err) - - assert.Equal(t, []byte("test"), data) -} From 530b0268b6aaf6c70009c9af66a02192eb383c96 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 12:05:55 +0200 Subject: [PATCH 0421/2268] chore: Implement status.WarningOnce --- pkg/status/status.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pkg/status/status.go b/pkg/status/status.go index 5c8675bdd..50dedcdd2 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -58,8 +58,9 @@ type StatusHandler interface { type contextKey struct{} type contextValue struct { - slh StatusHandler - deprecation utils.OnceByKey + slh StatusHandler + warningOnce utils.OnceByKey + deprecationOnce utils.OnceByKey } var noopContextValue = contextValue{ @@ -240,6 +241,13 @@ func Warning(ctx context.Context, status string, args ...any) { slh.Warning(fmt.Sprintf(status, args...)) } +func WarningOnce(ctx context.Context, key string, status string, args ...any) { + cv := getContextValue(ctx) + cv.deprecationOnce.Do(key, func() { + Warning(ctx, status, args...) + }) +} + func Trace(ctx context.Context, status string, args ...any) { slh := FromContext(ctx) slh.Trace(fmt.Sprintf(status, args...)) @@ -262,7 +270,7 @@ func Prompt(ctx context.Context, password bool, message string, args ...any) (st func Deprecation(ctx context.Context, key string, message string) { cv := getContextValue(ctx) - cv.deprecation.Do(key, func() { + cv.deprecationOnce.Do(key, func() { cv.slh.Warning(message) }) } From ccade82cd5e48386f79296f1bc3f8b8ec92f4a06 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 12:06:26 +0200 Subject: [PATCH 0422/2268] feat: Implement --local-git-override to allow local testing of external git repos --- cmd/kluctl/args/project.go | 1 + cmd/kluctl/commands/utils.go | 37 ++++++++++++++++++++++++++++++- pkg/git/repocache/cache.go | 43 +++++++++++++++++++++++++++++++----- pkg/vars/vars_loader_test.go | 2 +- 4 files changed, 75 insertions(+), 8 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index a5a29038e..8b49faa53 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -7,6 +7,7 @@ type ProjectFlags struct { Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` + LocalGitOverride []string `group:"project" help:"Specify a local git override in the form of 'github.com:my-org/my-repo=/local/path/to/override'. This will cause kluctl to not use git to clone for the specified repository but instead use the local directory. This is useful in case you need to test out changes in external git repositories without pushing them. To only override a single branch of the repo, use 'github.com:my-org/my-repo:my-branch=/local/path/to/override'"` } type ArgsFlags struct { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 209052620..7a49bf371 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/repocache" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" @@ -19,6 +20,7 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" "os" + "strings" ) func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { @@ -49,7 +51,16 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b sshPool := &ssh_pool.SshPool{} - rp := repocache.NewGitRepoCache(ctx, sshPool, auth.NewDefaultAuthProviders(), projectFlags.GitCacheUpdateInterval) + var repoOverrides []repocache.RepoOverride + for _, x := range projectFlags.LocalGitOverride { + ro, err := parseRepoOverride(x) + if err != nil { + return err + } + repoOverrides = append(repoOverrides, ro) + } + + rp := repocache.NewGitRepoCache(ctx, sshPool, auth.NewDefaultAuthProviders(), repoOverrides, projectFlags.GitCacheUpdateInterval) defer rp.Clear() loadArgs := kluctl_project.LoadKluctlProjectArgs{ @@ -192,3 +203,27 @@ func clientConfigGetter(forCompletion bool) func(context *string) (*rest.Config, return restConfig, &rawConfig, nil } } + +func parseRepoOverride(s string) (ret repocache.RepoOverride, err error) { + sp := strings.SplitN(s, "=", 2) + if len(sp) != 2 { + return repocache.RepoOverride{}, fmt.Errorf("invalid --local-git-override %s", s) + } + + sp2 := strings.Split(sp[0], ":") + if len(sp2) < 2 || len(sp2) > 3 { + return repocache.RepoOverride{}, fmt.Errorf("invalid --local-git-override %s", s) + } + + u, err := git_url.Parse(fmt.Sprintf("%s:%s", sp2[0], sp2[1])) + if err != nil { + return repocache.RepoOverride{}, fmt.Errorf("invalid --local-git-override %s: %w", s, err) + } + + ret.RepoKey = u.NormalizedRepoKey() + if len(sp2) == 3 { + ret.Ref = sp2[2] + } + ret.Override = sp[1] + return +} diff --git a/pkg/git/repocache/cache.go b/pkg/git/repocache/cache.go index e7f6dd11b..59e89ef6f 100644 --- a/pkg/git/repocache/cache.go +++ b/pkg/git/repocache/cache.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "os" "path" @@ -21,8 +22,11 @@ type GitRepoCache struct { authProviders *auth.GitAuthProviders sshPool *ssh_pool.SshPool updateInterval time.Duration - repos map[string]*CacheEntry - reposMutex sync.Mutex + + repos map[string]*CacheEntry + reposMutex sync.Mutex + + repoOverrides []RepoOverride cleanupDirs []string cleeanupDirsMutex sync.Mutex @@ -44,18 +48,25 @@ type RepoInfo struct { DefaultRef string `yaml:"defaultRef"` } +type RepoOverride struct { + RepoKey string + Ref string + Override string +} + type clonedDir struct { dir string info git.CheckoutInfo } -func NewGitRepoCache(ctx context.Context, sshPool *ssh_pool.SshPool, authProviders *auth.GitAuthProviders, updateInterval time.Duration) *GitRepoCache { +func NewGitRepoCache(ctx context.Context, sshPool *ssh_pool.SshPool, authProviders *auth.GitAuthProviders, repoOverrides []RepoOverride, updateInterval time.Duration) *GitRepoCache { return &GitRepoCache{ ctx: ctx, sshPool: sshPool, authProviders: authProviders, updateInterval: updateInterval, repos: map[string]*CacheEntry{}, + repoOverrides: repoOverrides, } } @@ -205,9 +216,29 @@ func (e *CacheEntry) GetClonedDir(ref string) (string, git.CheckoutInfo, error) e.rp.cleanupDirs = append(e.rp.cleanupDirs, p) e.rp.cleeanupDirsMutex.Unlock() - err = e.mr.CloneProjectByCommit(commit, p) - if err != nil { - return "", git.CheckoutInfo{}, err + var foundRo *RepoOverride + for _, ro := range e.rp.repoOverrides { + u := e.mr.Url() + if ro.RepoKey == u.NormalizedRepoKey() { + if ro.Ref == "" || strings.HasSuffix(ref2, "/"+ro.Ref) { + foundRo = &ro + break + } + } + } + + if foundRo != nil { + u := e.mr.Url() + status.WarningOnce(e.rp.ctx, fmt.Sprintf("git-override-%s|%s", foundRo.RepoKey, foundRo.Ref), "Overriding git repo %s with local directory %s", u.String(), foundRo.Override) + err = utils.CopyDir(foundRo.Override, p) + if err != nil { + return "", git.CheckoutInfo{}, err + } + } else { + err = e.mr.CloneProjectByCommit(commit, p) + if err != nil { + return "", git.CheckoutInfo{}, err + } } repoInfo, err := git.GetCheckoutInfo(p) diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 95c8ca022..872b6a6c3 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -39,7 +39,7 @@ func newTestDir(t *testing.T) string { } func newRP(t *testing.T) *repocache.GitRepoCache { - grc := repocache.NewGitRepoCache(context.TODO(), &ssh_pool.SshPool{}, auth.NewDefaultAuthProviders(), 0) + grc := repocache.NewGitRepoCache(context.TODO(), &ssh_pool.SshPool{}, auth.NewDefaultAuthProviders(), nil, 0) t.Cleanup(func() { grc.Clear() }) From 2733b1b825e7dfbdf7c4b766ff7e97b542506e5e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 13:36:27 +0200 Subject: [PATCH 0423/2268] feat: Remove deprecated "sources" from secretSets --- pkg/types/kluctl_project.go | 20 ++------------------ pkg/vars/vars_loader.go | 4 ---- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 9fddb42e8..b1e7e5f68 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -1,9 +1,7 @@ package types import ( - "github.com/go-playground/validator/v10" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" ) type DynamicArg struct { @@ -44,18 +42,8 @@ type DynamicTarget struct { } type SecretSet struct { - Name string `yaml:"name" validate:"required"` - // TODO deprecated, use vars instead - Sources []*VarsSource `yaml:"sources,omitempty"` - Vars []*VarsSource `yaml:"vars,omitempty"` -} - -func ValidateSecretSet(sl validator.StructLevel) { - s := sl.Current().Interface().(SecretSet) - - if len(s.Sources) != 0 && len(s.Vars) != 0 { - sl.ReportError(s, "vars", "vars", "sources and vars can't be set at the same time", "") - } + Name string `yaml:"name" validate:"required"` + Vars []*VarsSource `yaml:"vars,omitempty"` } type GlobalSealedSecretsConfig struct { @@ -73,7 +61,3 @@ type KluctlProject struct { Targets []*Target `yaml:"targets,omitempty"` SecretsConfig *SecretsConfig `yaml:"secretsConfig,omitempty"` } - -func init() { - yaml.Validator.RegisterStructValidation(ValidateSecretSet, SecretSet{}) -} diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 6a1953644..53f004f8f 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -8,7 +8,6 @@ import ( "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -74,9 +73,6 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear if source.Values != nil { v.mergeVars(varsCtx, source.Values, rootKey) return nil - } else if source.Path != nil { - status.Deprecation(v.ctx, "vars-path", "'path' is deprecated as vars source, use 'file' instead") - return v.loadFile(varsCtx, *source.Path, searchDirs, rootKey) } else if source.File != nil { return v.loadFile(varsCtx, *source.File, searchDirs, rootKey) } else if source.Git != nil { From 5a853eba8e95d493026f330c321e8581227d4915 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 13:36:47 +0200 Subject: [PATCH 0424/2268] feat: Remove deprecated "path" from "vars" --- pkg/kluctl_project/target_context.go | 7 +------ pkg/types/vars_source.go | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 3015d4345..60ea9db07 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -186,12 +186,7 @@ func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.Va if err != nil { return err } - if len(secretEntry.Sources) != 0 { - status.Deprecation(p.ctx, "secrets-sets-sources", "'sources' in secretSets is deprecated, use 'vars' instead") - err = varsLoader.LoadVarsList(varsCtx, secretEntry.Sources, searchDirs, "secrets") - } else { - err = varsLoader.LoadVarsList(varsCtx, secretEntry.Vars, searchDirs, "secrets") - } + err = varsLoader.LoadVarsList(varsCtx, secretEntry.Vars, searchDirs, "secrets") if err != nil { return err } diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index 89ca64760..af8a235cc 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -56,7 +56,6 @@ type VarsSourceVault struct { type VarsSource struct { Values *uo.UnstructuredObject `yaml:"values,omitempty"` File *string `yaml:"file,omitempty"` - Path *string `yaml:"path,omitempty"` Git *VarsSourceGit `yaml:"git,omitempty"` ClusterConfigMap *VarsSourceClusterConfigMapOrSecret `yaml:"clusterConfigMap,omitempty"` ClusterSecret *VarsSourceClusterConfigMapOrSecret `yaml:"clusterSecret,omitempty"` From 48cf6dba60ef11a051cf35c2c1bc54932302c868 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 16:18:20 +0200 Subject: [PATCH 0425/2268] feat: Deprecate dynamicTargets configuration per target --- pkg/kluctl_project/project.go | 16 ---------------- pkg/kluctl_project/target_context.go | 9 --------- pkg/kluctl_project/targets.go | 16 ++++------------ 3 files changed, 4 insertions(+), 37 deletions(-) diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 29d27077f..7d36b0872 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -6,7 +6,6 @@ import ( "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/git/repocache" types2 "github.com/kluctl/kluctl/v2/pkg/types" - "strings" ) type LoadedKluctlProject struct { @@ -45,18 +44,3 @@ func (c *LoadedKluctlProject) FindDynamicTarget(name string) (*types2.DynamicTar } return nil, fmt.Errorf("target %s not existent in kluctl project config", name) } - -func (c *LoadedKluctlProject) CheckDynamicArg(target *types2.Target, argName string, argValue any) error { - var dynArg *types2.DynamicArg - for _, x := range target.DynamicArgs { - if x.Name == argName || strings.HasPrefix(argName, x.Name+".") { - dynArg = &x - break - } - } - if dynArg == nil { - return fmt.Errorf("dynamic argument %s is not allowed for target", argName) - } - - return nil -} diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 60ea9db07..6b91e5fd0 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -134,15 +134,6 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, offlineK8s bool, e allArgs := uo.New() - if target != nil { - for argName, argValue := range externalArgs.Object { - err = p.CheckDynamicArg(target, argName, argValue) - if err != nil { - return doError(err) - } - } - } - allArgs.Merge(externalArgs) if target != nil { if target.Args != nil { diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 0600dae27..baa2aa52f 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -6,7 +6,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" "os" @@ -245,19 +244,12 @@ func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) return nil, err } + if len(target.DynamicArgs) != 0 { + status.Deprecation(c.ctx, "dynamic-args", "dynamicArgs are deprecated and ignored. The field will be removed in a future kluctl release.") + } + // check and merge args if targetConfig.Args != nil { - err = targetConfig.Args.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { - strValue := fmt.Sprintf("%v", it.Value()) - err := c.CheckDynamicArg(&target, it.KeyPath().ToJsonPath(), strValue) - if err != nil { - return err - } - return nil - }) - if err != nil { - return nil, err - } target.Args.Merge(targetConfig.Args) } // We prepend the dynamic images to ensure they get higher priority later From 107155bcfc3bc881bee7759df0ff023fd0396884 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 16:19:25 +0200 Subject: [PATCH 0426/2268] feat: Allow to run without targets (through the use of a no-name target) --- pkg/kluctl_project/target_context.go | 56 ++++++++++++++++------------ 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 6b91e5fd0..741cccbfe 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -39,11 +39,19 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s return nil, err } target = t.Target + } else { + target = &types.Target{} + } + + images.PrependFixedImages(target.Images) - images.PrependFixedImages(target.Images) + clientConfig, clusterContext, err := p.loadK8sConfig(target, offlineK8s) + if err != nil { + return nil, err } + target.Context = &clusterContext - varsCtx, clientConfig, clusterContext, err := p.buildVars(target, offlineK8s, externalArgs, forSeal) + varsCtx, err := p.buildVars(target, externalArgs, forSeal) if err != nil { return nil, err } @@ -79,7 +87,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s VarsLoader: varsLoader, RenderDir: renderOutputDir, SealedSecretsDir: p.sealedSecretsDir, - DefaultSealedSecretsOutputPattern: targetName, + DefaultSealedSecretsOutputPattern: target.Name, } d, err := deployment.NewDeploymentProject(dctx, varsCtx, deployment.NewSource(deploymentDir), ".", nil) @@ -104,31 +112,35 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s return targetCtx, nil } -func (p *LoadedKluctlProject) buildVars(target *types.Target, offlineK8s bool, externalArgs *uo.UnstructuredObject, forSeal bool) (*vars.VarsCtx, *rest.Config, string, error) { - doError := func(err error) (*vars.VarsCtx, *rest.Config, string, error) { - return nil, nil, "", err +func (p *LoadedKluctlProject) loadK8sConfig(target *types.Target, offlineK8s bool) (*rest.Config, string, error) { + if offlineK8s { + return nil, "", nil } - varsCtx := vars.NewVarsCtx(p.J2) - contextName := target.Context var err error var clientConfig *rest.Config - if !offlineK8s { - var restConfig *api.Config - clientConfig, restConfig, err = p.loadArgs.ClientConfigGetter(contextName) - if err != nil { - return doError(err) - } - if contextName == nil { - contextName = &restConfig.CurrentContext - } + var restConfig *api.Config + clientConfig, restConfig, err = p.loadArgs.ClientConfigGetter(contextName) + if err != nil { + return nil, "", err } + if contextName == nil { + contextName = &restConfig.CurrentContext + } + if contextName != nil { + return clientConfig, *contextName, nil + } + return clientConfig, "", nil +} + +func (p *LoadedKluctlProject) buildVars(target *types.Target, externalArgs *uo.UnstructuredObject, forSeal bool) (*vars.VarsCtx, error) { + varsCtx := vars.NewVarsCtx(p.J2) targetVars, err := uo.FromStruct(target) if err != nil { - return doError(err) + return nil, err } varsCtx.UpdateChild("target", targetVars) @@ -148,16 +160,12 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, offlineK8s bool, e err = deployment.LoadDeploymentArgs(p.ProjectDir, varsCtx, allArgs) if err != nil { - return doError(err) + return nil, err } varsCtx.UpdateChild("args", allArgs) - var contextName2 string - if contextName != nil { - contextName2 = *contextName - } - return varsCtx, clientConfig, contextName2, nil + return varsCtx, nil } func (p *LoadedKluctlProject) findSecretsEntry(name string) (*types.SecretSet, error) { From 09e34f7d7b33e0b76c9b3b375ea549ecee77d043 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 16:23:24 +0200 Subject: [PATCH 0427/2268] refactor: Pass target context params via struct --- cmd/kluctl/commands/utils.go | 17 ++++++++++----- pkg/kluctl_project/target_context.go | 31 +++++++++++++++++++--------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 7a49bf371..5e41a4633 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -152,11 +152,18 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm renderOutputDir = tmpDir } - targetCtx, err := p.NewTargetContext(ctx, - args.targetFlags.Target, args.offlineKubernetes, - args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, - optionArgs2, args.forSeal, images, inclusion, - renderOutputDir) + targetParams := kluctl_project.TargetContextParams{ + TargetName: args.targetFlags.Target, + OfflineK8s: args.offlineKubernetes, + DryRun: args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, + ExternalArgs: optionArgs2, + ForSeal: args.forSeal, + Images: images, + Inclusion: inclusion, + RenderOutputDir: renderOutputDir, + } + + targetCtx, err := p.NewTargetContext(ctx, targetParams) if err != nil { return err } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 741cccbfe..41faba801 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -26,15 +26,26 @@ type TargetContext struct { DeploymentCollection *deployment.DeploymentCollection } -func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName string, offlineK8s bool, dryRun bool, externalArgs *uo.UnstructuredObject, forSeal bool, images *deployment.Images, inclusion *utils.Inclusion, renderOutputDir string) (*TargetContext, error) { +type TargetContextParams struct { + TargetName string + OfflineK8s bool + DryRun bool + ExternalArgs *uo.UnstructuredObject + ForSeal bool + Images *deployment.Images + Inclusion *utils.Inclusion + RenderOutputDir string +} + +func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params TargetContextParams) (*TargetContext, error) { deploymentDir, err := filepath.Abs(p.ProjectDir) if err != nil { return nil, err } var target *types.Target - if targetName != "" { - t, err := p.FindDynamicTarget(targetName) + if params.TargetName != "" { + t, err := p.FindDynamicTarget(params.TargetName) if err != nil { return nil, err } @@ -43,15 +54,15 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s target = &types.Target{} } - images.PrependFixedImages(target.Images) + params.Images.PrependFixedImages(target.Images) - clientConfig, clusterContext, err := p.loadK8sConfig(target, offlineK8s) + clientConfig, clusterContext, err := p.loadK8sConfig(target, params.OfflineK8s) if err != nil { return nil, err } target.Context = &clusterContext - varsCtx, err := p.buildVars(target, externalArgs, forSeal) + varsCtx, err := p.buildVars(target, params.ExternalArgs, params.ForSeal) if err != nil { return nil, err } @@ -63,7 +74,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s if err != nil { return nil, err } - k, err = k8s.NewK8sCluster(ctx, clientFactory, dryRun) + k, err = k8s.NewK8sCluster(ctx, clientFactory, params.DryRun) if err != nil { s.Failed() return nil, err @@ -73,7 +84,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s varsLoader := vars.NewVarsLoader(ctx, k, p.RP, aws.NewClientFactory()) - if forSeal { + if params.ForSeal { err = p.loadSecrets(target, varsCtx, varsLoader) if err != nil { return nil, err @@ -85,7 +96,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s K: k, RP: p.RP, VarsLoader: varsLoader, - RenderDir: renderOutputDir, + RenderDir: params.RenderOutputDir, SealedSecretsDir: p.sealedSecretsDir, DefaultSealedSecretsOutputPattern: target.Name, } @@ -95,7 +106,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, targetName s return nil, err } - c, err := deployment.NewDeploymentCollection(dctx, d, images, inclusion, forSeal) + c, err := deployment.NewDeploymentCollection(dctx, d, params.Images, params.Inclusion, params.ForSeal) if err != nil { return nil, err } From c7d8dfbe647cd67015a8734a764b1a40ece32f80 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 16:39:21 +0200 Subject: [PATCH 0428/2268] feat: Allow to override target name via -T --- cmd/kluctl/args/project.go | 4 +++- cmd/kluctl/commands/utils.go | 18 ++++++++++-------- pkg/kluctl_project/target_context.go | 26 +++++++++++++++++--------- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 8b49faa53..f60f27482 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -16,5 +16,7 @@ type ArgsFlags struct { } type TargetFlags struct { - Target string `group:"project" short:"t" help:"Target name to run command for. Target must exist in .kluctl.yaml."` + Target string `group:"project" short:"t" help:"Target name to run command for. Target must exist in .kluctl.yaml."` + TargetNameOverride string `group:"project" short:"T" help:"Overrides the target name. If -t is used at the same time, then the target will be looked up based on -t and then renamed to the value of -T. If no target is specified via -t, then the no-name target is renamed to the value of -T."` + Context string `group:"project" help:"Overrides the context name specified in the target. If the selected target does not specify a context or the no-name target is used, --context will override the currently active context."` } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 5e41a4633..cd67132ad 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -153,14 +153,16 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm } targetParams := kluctl_project.TargetContextParams{ - TargetName: args.targetFlags.Target, - OfflineK8s: args.offlineKubernetes, - DryRun: args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, - ExternalArgs: optionArgs2, - ForSeal: args.forSeal, - Images: images, - Inclusion: inclusion, - RenderOutputDir: renderOutputDir, + TargetName: args.targetFlags.Target, + TargetNameOverride: args.targetFlags.TargetNameOverride, + ContextOverride: args.targetFlags.Context, + OfflineK8s: args.offlineKubernetes, + DryRun: args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, + ExternalArgs: optionArgs2, + ForSeal: args.forSeal, + Images: images, + Inclusion: inclusion, + RenderOutputDir: renderOutputDir, } targetCtx, err := p.NewTargetContext(ctx, targetParams) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 41faba801..246762e92 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -27,14 +27,16 @@ type TargetContext struct { } type TargetContextParams struct { - TargetName string - OfflineK8s bool - DryRun bool - ExternalArgs *uo.UnstructuredObject - ForSeal bool - Images *deployment.Images - Inclusion *utils.Inclusion - RenderOutputDir string + TargetName string + TargetNameOverride string + ContextOverride string + OfflineK8s bool + DryRun bool + ExternalArgs *uo.UnstructuredObject + ForSeal bool + Images *deployment.Images + Inclusion *utils.Inclusion + RenderOutputDir string } func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params TargetContextParams) (*TargetContext, error) { @@ -49,10 +51,16 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe if err != nil { return nil, err } - target = t.Target + target = &*t.Target } else { target = &types.Target{} } + if params.TargetNameOverride != "" { + target.Name = params.TargetNameOverride + } + if params.ContextOverride != "" { + target.Context = ¶ms.ContextOverride + } params.Images.PrependFixedImages(target.Images) From a1cf0f34af14b32e51610f84667dde2dc8ff8ae2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 16:44:37 +0200 Subject: [PATCH 0429/2268] tests: Add test for context override --- e2e/contexts_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index 5600f3689..6d9d10c7a 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -116,3 +116,21 @@ func TestContextSwitch(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test1") assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") } + +func TestContextOverride(t *testing.T) { + t.Parallel() + + p := prepareContextTest(t, "context-override") + defer p.cleanup() + + p.updateTarget("test1", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(defaultCluster1.Context, "context") + }) + + p.KluctlMust("deploy", "--yes", "-t", "test1") + assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + + p.KluctlMust("deploy", "--yes", "-t", "test1", "--context", defaultCluster2.Context) + assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") +} From ae1fcd5f4c82deb64ab88ef21b24dcba7526b8dc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 16:53:18 +0200 Subject: [PATCH 0430/2268] tests: Add test for no-name targets --- e2e/no_target_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 e2e/no_target_test.go diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go new file mode 100644 index 000000000..80f419298 --- /dev/null +++ b/e2e/no_target_test.go @@ -0,0 +1,54 @@ +package e2e + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func prepareNoTargetTest(t *testing.T, name string) *testProject { + p := &testProject{} + p.init(t, defaultCluster1, name) + p.mergeKubeconfig(defaultCluster2) + + createNamespace(t, defaultCluster1, p.projectName) + createNamespace(t, defaultCluster2, p.projectName) + + addConfigMapDeployment(p, "cm", map[string]string{ + "targetName": `{{ target.name }}`, + "targetContext": `{{ target.context }}`, + }, resourceOpts{ + name: "cm", + namespace: p.projectName, + }) + + return p +} + +func TestNoTarget(t *testing.T) { + t.Parallel() + + p := prepareNoTargetTest(t, "no-target") + defer p.cleanup() + + p.KluctlMust("deploy", "--yes") + cm := assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") + assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assert.Equal(t, map[string]any{ + "targetName": "", + "targetContext": defaultCluster1.Context, + }, cm.Object["data"]) + + p.KluctlMust("deploy", "--yes", "-T", "override-name") + cm = assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") + assert.Equal(t, map[string]any{ + "targetName": "override-name", + "targetContext": defaultCluster1.Context, + }, cm.Object["data"]) + + p.KluctlMust("deploy", "--yes", "-T", "override-name", "--context", defaultCluster2.Context) + cm = assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assert.Equal(t, map[string]any{ + "targetName": "override-name", + "targetContext": defaultCluster2.Context, + }, cm.Object["data"]) +} From 929fec104964e9599907ac221be5f23230b9a999 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 19 Oct 2022 09:18:00 +0200 Subject: [PATCH 0431/2268] feat: Implement kluctl.io/validate-ignore annotation This allows to ignore an object while validation is running. --- pkg/validation/validation.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 7a3ecb5d2..7a1f1083c 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" @@ -50,6 +51,10 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError // We assume all is good in case no validation is performed ret.Ready = true + if utils.ParseBoolOrFalse(o.GetK8sAnnotation("kluctl.io/validate-ignore")) { + return + } + defer func() { if r := recover(); r != nil { if _, ok := r.(*validationFailed); ok { From e9cbef252118b2304274af29e6176ba1c09524b2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 17:56:27 +0200 Subject: [PATCH 0432/2268] ci: Fix wrong pull_request branch --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9f00b5a06..012806f23 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ on: push: pull_request: branches: - - master + - main jobs: build: From ae017d389cb50009ac80f6588d8467c51496e9b9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 22:33:41 +0200 Subject: [PATCH 0433/2268] docs: Copy docs from www-kluctl.io project --- docs/_index.md | 93 ++++++ docs/concepts.md | 54 ++++ docs/get-started.md | 119 +++++++ docs/history.md | 25 ++ docs/installation.md | 64 ++++ docs/philosophy.md | 36 +++ docs/reference/_index.md | 8 + docs/reference/commands/_index.md | 14 + docs/reference/commands/common-arguments.md | 106 ++++++ docs/reference/commands/delete.md | 46 +++ docs/reference/commands/deploy.md | 78 +++++ docs/reference/commands/diff.md | 50 +++ .../commands/environment-variables.md | 23 ++ docs/reference/commands/helm-pull.md | 24 ++ docs/reference/commands/helm-update.md | 45 +++ docs/reference/commands/list-images.md | 39 +++ docs/reference/commands/list-targets.md | 28 ++ docs/reference/commands/poke-images.md | 41 +++ docs/reference/commands/prune.md | 39 +++ docs/reference/commands/render.md | 37 +++ docs/reference/commands/seal.md | 42 +++ docs/reference/commands/validate.md | 39 +++ docs/reference/deployments/_index.md | 62 ++++ .../deployments/annotations/_index.md | 7 + .../deployments/annotations/all-resources.md | 91 ++++++ .../deployments/annotations/hooks.md | 23 ++ .../deployments/annotations/kustomization.md | 33 ++ .../deployments/annotations/validation.md | 16 + docs/reference/deployments/deployment-yml.md | 306 ++++++++++++++++++ docs/reference/deployments/helm.md | 174 ++++++++++ docs/reference/deployments/hooks.md | 48 +++ docs/reference/deployments/images.md | 130 ++++++++ docs/reference/deployments/kustomize.md | 17 + docs/reference/deployments/readiness.md | 15 + docs/reference/deployments/tags.md | 83 +++++ docs/reference/kluctl-project/_index.md | 60 ++++ .../kluctl-project/secrets-config/_index.md | 73 +++++ .../kluctl-project/targets/_index.md | 103 ++++++ .../kluctl-project/targets/dynamic-targets.md | 110 +++++++ docs/reference/sealed-secrets.md | 152 +++++++++ docs/reference/templating/_index.md | 53 +++ docs/reference/templating/filters.md | 80 +++++ docs/reference/templating/functions.md | 65 ++++ .../templating/predefined-variables.md | 27 ++ docs/reference/templating/variable-sources.md | 215 ++++++++++++ 45 files changed, 2993 insertions(+) create mode 100644 docs/_index.md create mode 100644 docs/concepts.md create mode 100644 docs/get-started.md create mode 100644 docs/history.md create mode 100644 docs/installation.md create mode 100644 docs/philosophy.md create mode 100644 docs/reference/_index.md create mode 100644 docs/reference/commands/_index.md create mode 100644 docs/reference/commands/common-arguments.md create mode 100644 docs/reference/commands/delete.md create mode 100644 docs/reference/commands/deploy.md create mode 100644 docs/reference/commands/diff.md create mode 100644 docs/reference/commands/environment-variables.md create mode 100644 docs/reference/commands/helm-pull.md create mode 100644 docs/reference/commands/helm-update.md create mode 100644 docs/reference/commands/list-images.md create mode 100644 docs/reference/commands/list-targets.md create mode 100644 docs/reference/commands/poke-images.md create mode 100644 docs/reference/commands/prune.md create mode 100644 docs/reference/commands/render.md create mode 100644 docs/reference/commands/seal.md create mode 100644 docs/reference/commands/validate.md create mode 100644 docs/reference/deployments/_index.md create mode 100644 docs/reference/deployments/annotations/_index.md create mode 100644 docs/reference/deployments/annotations/all-resources.md create mode 100644 docs/reference/deployments/annotations/hooks.md create mode 100644 docs/reference/deployments/annotations/kustomization.md create mode 100644 docs/reference/deployments/annotations/validation.md create mode 100644 docs/reference/deployments/deployment-yml.md create mode 100644 docs/reference/deployments/helm.md create mode 100644 docs/reference/deployments/hooks.md create mode 100644 docs/reference/deployments/images.md create mode 100644 docs/reference/deployments/kustomize.md create mode 100644 docs/reference/deployments/readiness.md create mode 100644 docs/reference/deployments/tags.md create mode 100644 docs/reference/kluctl-project/_index.md create mode 100644 docs/reference/kluctl-project/secrets-config/_index.md create mode 100644 docs/reference/kluctl-project/targets/_index.md create mode 100644 docs/reference/kluctl-project/targets/dynamic-targets.md create mode 100644 docs/reference/sealed-secrets.md create mode 100644 docs/reference/templating/_index.md create mode 100644 docs/reference/templating/filters.md create mode 100644 docs/reference/templating/functions.md create mode 100644 docs/reference/templating/predefined-variables.md create mode 100644 docs/reference/templating/variable-sources.md diff --git a/docs/_index.md b/docs/_index.md new file mode 100644 index 000000000..c6a4cdaa4 --- /dev/null +++ b/docs/_index.md @@ -0,0 +1,93 @@ +--- +title: "Kluctl Documentation" +linkTitle: "Docs" +description: "The missing glue to put together large Kubernetes deployments." +taxonomyCloud: [] +weight: 20 +menu: + main: + weight: 20 +--- + +Kluctl is the missing glue that puts together your (and any third-party) deployments into one large declarative +Kubernetes deployment, while making it fully manageable (deploy, diff, prune, delete, ...) via one unified command +line interface. + +Kluctl tries to be as flexible as possible, while remaining as simple as possible. It reuses established +tools (e.g. Kustomize and Helm), making it possible to re-use a large set of available third-party deployments. + +Kluctl is centered around "targets", which can be a cluster or a specific environment (e.g. test, dev, prod, ...) on one +or multiple clusters. Targets can be deployed, diffed, pruned, deleted, and so on. The idea is to have the same set of +operations for every target, no matter how simple or complex the deployment and/or target is. + +Kluctl does not depend on external operators/controllers and allows to use the same deployment wherever you want, +as long as access to the kluctl project and clusters is available. This means, that you can use it from your +local machine, from your CI/CD pipelines or any automation platform/system that allows to call custom tools. + +Flux support is in alpha state and available via the [flux-kluctl-controller](https://github.com/kluctl/flux-kluctl-controller). + +## Kluctl in Short + + + +| | | +| --- | --- | +| 💪 Kluctl handles all your deployments | You can manage all your deployments with Kluctl, including infrastructure related and your applications. | +| 🪶 Complex or simple, all the same | You can manage complex and simple deployments with Kluctl. Simple deployments are lightweight while complex deployment are easily manageable. | +| 🤖 Native git support | Kluctl has native Git support integrated, meaning that it can easily deploy remote Kluctl projects or externalize parts (e.g. configuration) of your Kluctl project. | +| 🪐 Multiple environments | Deploy the same deployment to multiple environments (dev, test, prod, ...), with flexible differences in configuration. | +| 🌌 Multiple clusters | Manage multiple target clusters (in multiple clouds or bare-metal if you want). | +| 🔩 Configuration and Templating | Kluctl allows to use templating in nearly all places, making it easy to have dynamic configuration. | +| ⎈ Helm and Kustomize | The Helm and Kustomize integrations allow you to reuse plenty of third-party charts and kustomizations. | +| 🔍 See what's different | Always know what the state of your deployments is by being able to run diffs on the whole deployment. | +| 🔎 See what happened | Always know what you actually changed after performing a deployment. | +| 💥 Know what went wrong | Kluctl will show you what part of your deployment failed and why. | +| 👐 Live and let live | Kluctl tries to not interfere with any other tools or operators. This is possible due to it's use of server-side-apply. | +| 🧹 Keep it clean | Keep your clusters clean by issuing regular prune calls. | +| 🔐 Encrypted Secrets | Manage encrypted secrets for multiple target environments and clusters. | + +## What can I do with Kluctl? + +Kluctl allows you to define a Kluctl project, which in turn defines Kluctl +deployments and sub-deployments. Each Kluctl deployment defines Kustomize deployments. + +A Kluctl project also defines targets, which represent your target environments +and/or clusters. + +The Kluctl CLI then allows to deploy, diff, prune, delete, ... your deployments. + +## Where do I start? + +{{% alert title="Get started with Kluctl!" %}} +Following this [guide]({{< ref "docs/get-started" >}}) will just take a couple of minutes to complete: +After installing `kluctl`, you can either check out the [example]({{< ref "docs/guides/examples" >}}) or [tutorials]({{< ref "docs/guides/tutorials" >}}). +{{% /alert %}} + + diff --git a/docs/concepts.md b/docs/concepts.md new file mode 100644 index 000000000..c410e32e1 --- /dev/null +++ b/docs/concepts.md @@ -0,0 +1,54 @@ +--- +title: Core Concepts +description: Core Concepts of Kluctl. +weight: 10 +--- + +These are some core concepts in Kluctl. + +## Kluctl project +The kluctl project defines targets, secret sources and external git projects. +It is defined via the [.kluctl.yaml]({{< ref "docs/reference/kluctl-project" >}}) configuration file. + +The kluctl project can also optionally define where the deployment project and clusters configs are located (external +git projects). + +## Targets +A target defines a target cluster and a set of deployment arguments. Multiple targets can use the same cluster. Targets +allow implementing multi-cluster, multi-environment, multi-customer, ... deployments. + +## Deployments +A [deployment]({{< ref "docs/reference/deployments" >}}) defines which Kustomize deployments and which sub-deployments +to deploy. It also controls the order of deployments. + +Deployments may be configured through deployment arguments, which are typically provided via the targets but might also +be provided through the CLI. + +## Variables +[Variables]({{< ref "docs/reference/templating" >}}) are the main source of configuration. They are either loaded yaml +files or directly defined inside deployments. Each variables file that is loaded has access to all the variables which +were defined before, allowing complex composition of configuration. + +After being loaded, variables are usable through the templating engine at all nearly all places. + +## Templating +All configuration files (including .kluctl.yaml and deployment.yaml) and all Kubernetes manifests involved are processed +through a templating engine. +The [templating engine]({{< ref "docs/reference/templating" >}}) allows simple variable substitution and also complex +control structures (if/else, for loops, ...). + +## Secrets +Secrets are loaded from [external sources]({{< ref "docs/reference/kluctl-project" >}}) and are only available +while [sealing]({{< ref "docs/reference/sealed-secrets" >}}). After the sealing process, only the public-key encrypted +sealed secrets are available. + +## Sealed Secrets +[Sealed Secrets]({{< ref "docs/reference/sealed-secrets" >}}) are based on +[Bitnami's sealed-secrets controller](https://github.com/bitnami-labs/sealed-secrets). Kluctl offers integration of +sealed secrets through the `seal` command. Kluctl allows managing multiple sets of sealed secrets for multiple targets. + +## Unified CLI +The CLI of kluctl is designed to be unified/consistent as much as possible. Most commands are centered around targets +and thus require you to specify the target name (via `-t `). If you remember how one command works, it's easy +to figure out how the others work. Output from all targets based commands is also unified, allowing you to easily see +what will and what did happen. diff --git a/docs/get-started.md b/docs/get-started.md new file mode 100644 index 000000000..d8868313b --- /dev/null +++ b/docs/get-started.md @@ -0,0 +1,119 @@ +--- +title: "Get Started with Kluctl" +linkTitle: "Get Started" +description: "Get Started with Kluctl." +weight: 20 +--- + +This tutorial shows you how to bootstrap Flux to a Kubernetes cluster and deploy a sample application in a GitOps manner. + +## Before you begin + +A few things must be prepared before you actually begin. + +### Get a Kubernetes cluster + +The first step is of course: You need a kubernetes cluster. It doesn't really matter where this cluster is hosted, if +it's a local (e.g. [kind](https://kind.sigs.k8s.io/docs/user/quick-start/)) cluster, managed cluster, or a self-hosted +cluster, kops or kubespray based, AWS, GCE, Azure, ... and so on. kluctl +is completely independent of how Kubernetes is deployed and where it is hosted. + +There is however a minimum Kubernetes version that must be met: 1.20.0. This is due to the heavy use of server-side apply +which was not stable enough in older versions of Kubernetes. + +### Prepare your kubeconfig + +Your local kubeconfig should be configured to have access to the target Kubernetes cluster via a dedicated context. The context +name should match with the name that you want to use for the cluster from now on. Let's assume the name is `test.example.com`, +then you'd have to ensure that the kubeconfig context `test.example.com` is correctly pointing and authorized for this +cluster. + +See [Configure Access to Multiple Clusters](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/) for documentation +on how to manage multiple clusters with a single kubeconfig. Depending on the Kubernets provisioning/deployment tooling +you used, you might also be able to directly export the context into your local kubeconfig. For example, +[kops](https://github.com/kubernetes/kops/blob/master/docs/cli/kops_export.md) is able to export and merge the kubeconfig +for a given cluster. + +## Objectives + +- Checkout one of the example Kluctl projects +- Deploy to your local cluster +- Change something and re-deploy + +## Install Kluctl + +The `kluctl` command-line interface (CLI) is required to perform deployments. + +To install the CLI with Homebrew run: + +```sh +brew install kluctl/tap/kluctl +``` + +For other installation methods, see the [install documentation]({{< ref "docs/installation" >}}). + +## Clone the kluctl examples + +Clone the example project found at https://github.com/kluctl/kluctl-examples + +```sh +git clone https://github.com/kluctl/kluctl-examples.git +``` + +## Choose one of the examples + +You can choose whatever example you like from the clones repository. We will however continue this guide by referring +to the `simple-helm` example found in that repository. Change the current directory: + +```sh +cd kluctl-examples/simple-helm +``` + +## Create your local cluster + +Create a local cluster with [kind](https://kind.sigs.k8s.io): + +```sh +kind create cluster +``` + +This will update your kubeconfig to contain a context with the name `kind-kind`. By default, all examples will use +the currently active context. + +## Deploy the example + +Now run the following command to deploy the example: + +```sh +kluctl deploy -t simple-helm +``` + +Kluctl will perform a diff first and then ask for your confirmation to deploy it. In this case, you should only see +some objects being newly deployed. + +```sh +kubectl -nsimple-helm get pod +``` + +## Change something and re-deploy + +Now change something inside the deployment project. You could for example add `replicaCount: 2` to `deployment/nginx/helm-values.yml`. +After you have saved your changes, run the deploy command again: + +```sh +kluctl deploy -t simple-helm +``` + +This time it should show your modifications in the diff. Confirm that you want to perform the deployment and then verify +it: + +```sh +kubectl -nsimple-helm get pod +``` + +You should need 2 instances of the nginx POD running now. + +## Where to continue? + +Continue by reading through the [tutorials]({{< ref "docs/guides/tutorials" >}}) and by consulting +the [reference documentation]({{< ref "reference" >}}). diff --git a/docs/history.md b/docs/history.md new file mode 100644 index 000000000..a1baceb2a --- /dev/null +++ b/docs/history.md @@ -0,0 +1,25 @@ +--- +title: "History" +linkTitle: "History" +weight: 40 +description: "The history of kluctl." +--- + +Kluctl was created after multiple incarnations of complex multi-environment (e.g. dev, test, prod) deployments, including everything +from monitoring, persistency and the actual custom services. The philosophy of these deployments was always +"what belongs together, should be put together", meaning that only as much Git repositories were involved as necessary. + +The problems to solve turned out to be always the same: +* Dozens of Helm Charts, kustomize deployments and standalone Kubernetes manifests needed to be orchestrated in a way + that they work together (services need to connect to the correct databases, and so on) +* (Encrypted) Secrets needed to be managed and orchestrated for multiple environments and clusters +* Updates of components was always risky and required keeping track of what actually changed since the last deployment +* Available tools (Helm, Kustomize) were not suitable to solve this on its own in an easy/natural way +* A lot of bash scripting was required to put things together + +When this got more and more complex, and the bash scripts started to become a mess (as "simple" Bash scripts always tend to become), +kluctl was started from scratch. It now tries to solve the mentioned problems and provide a useful set of features (commands) +in a sane and unified way. + +The first versions of kluctl were written in Python, hence the use of Jinja2 templating in kluctl. With version 2.0.0, +kluctl was rewritten in Go. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 000000000..94ae4641a --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,64 @@ +--- +title: "Installation" +linkTitle: "Installation" +weight: 20 +description: "Installing kluctl." +--- + +## Install kluctl + +The kluctl CLI is available as a binary executable for all major platforms, +the binaries can be downloaded form GitHub +[releases page](https://github.com/kluctl/kluctl/releases). + +{{% tabs %}} +{{% tab "Homebrew" %}} + +With [Homebrew](https://brew.sh) for macOS and Linux: + +```sh +brew install kluctl/tap/kluctl +``` + +{{% /tab %}} +{{% tab "bash" %}} + +With [Bash](https://www.gnu.org/software/bash/) for macOS and Linux: + +```sh +curl -s https://kluctl.io/install.sh | bash +``` + +{{% /tab %}} + + +{{% /tabs %}} + + + +## Container images + +A container image with `kluctl` is available on GitHub: + +* `ghcr.io/kluctl/kluctl:` diff --git a/docs/philosophy.md b/docs/philosophy.md new file mode 100644 index 000000000..580b9f397 --- /dev/null +++ b/docs/philosophy.md @@ -0,0 +1,36 @@ +--- +title: "Philosophy" +linkTitle: "Philosophy" +weight: 30 +description: "The philosophy behind kluctl." +--- + +Kluctl tries to follow a few basic ideas and a philosophy. Project and deployments structure, as well as all commands +are centered on these. + +## Be practical +Everything found in kluctl is based on years of experience in daily business, from the perspective of a DevOps Engineer. +Kluctl prefers practicability when possible, trying to make the daily life of a DevOps Engineer as comfortable as possible. + +## Consistent CLI +Commands try to be as consistent as possible, making it easy to remember how they are used. For example, a `diff` is used the same way as a `deploy`. This applies to all sizes and complexities of projects. A simple/single-application deployment is used the same way as a complex one, so that it is easy to switch between projects. + +## Mostly declarative +Kluctl tries to be declarative whenever possible, but loosens this in some cases to stay practical. +For example, hooks, barriers and waitReadiness allows you to control order of deployments in a way that a pure declarative approach would not allow. + +## Predictable and traceable +Always know what will happen (`diff` or `--dry-run`) and always know what happened (output changes done by a command). +There is nothing worse than not knowing what's going to happen when you deploy the current state to prod. Not knowing what happened is on the same level. + +## Live and let live +Kluctl tries to not interfere with any other tools or operators. It achieves this by honoring managed fields in an intelligent way. +Kluctl will never force-apply anything without being told so, it will also always inform you about fields that you lost ownership of. + +## CLI/Client first +Kluctl is centered around a unified command line interface and will always prioritize this. This +guarantees that the DevOps Engineer never looses control, even if automation and/or GitOps style operators are being used. + +## No scripting +Kluctl tries its best to remove the need for scripts (e.g. Bash) around deployments. It tries to remove the need +for external orchestration of deployment order and/or dependencies. diff --git a/docs/reference/_index.md b/docs/reference/_index.md new file mode 100644 index 000000000..06d1ad3b4 --- /dev/null +++ b/docs/reference/_index.md @@ -0,0 +1,8 @@ +--- +title: "Reference" +linkTitle: "Reference" +description: > + Description of configuration files and commands +weight: 110 +--- + diff --git a/docs/reference/commands/_index.md b/docs/reference/commands/_index.md new file mode 100644 index 000000000..438c870aa --- /dev/null +++ b/docs/reference/commands/_index.md @@ -0,0 +1,14 @@ +--- +title: "Commands" +linkTitle: "Commands" +weight: 10 +description: > + Description of available commands. +--- + +kluctl offers a unified command line interface that allows to standardize all your deployments. Every project, +no matter how different it is from other projects, is managed the same way. + +You can always call `kluctl --help` or `kluctl --help` for a help prompt. + +Individual commands are documented in sub-sections. diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md new file mode 100644 index 000000000..429d78235 --- /dev/null +++ b/docs/reference/commands/common-arguments.md @@ -0,0 +1,106 @@ +--- +title: "Common Arguments" +linkTitle: "Common Arguments" +weight: 1 +description: > + Common arguments +--- + +A few sets of arguments are common between multiple commands. These arguments are still part of the command itself and +must be placed *after* the command name. + +## Global arguments + +These arguments are available for all commands. + + +``` +Global arguments: + --cpu-profile string Enable CPU profiling and write the result to the given path + --debug Enable debug logging + --no-color Disable colored output + --no-update-check Disable update check on startup + +``` + + +## Project arguments + +These arguments are available for all commands that are based on a Kluctl project. +They control where and how to load the kluctl project and deployment project. + + +``` +Project arguments: + Define where and how to load the kluctl project and its components from. + + -a, --arg stringArray Template argument in the form name=value + --cluster string DEPRECATED. Specify/Override cluster + --git-cache-update-interval duration Specify the time to wait between git cache updates. Defaults to not + wait at all and always updating caches. + --local-clusters existingdir DEPRECATED. Local clusters directory. Overrides the project from + .kluctl.yaml + --local-deployment existingdir DEPRECATED. Local deployment directory. Overrides the project from + .kluctl.yaml + --local-sealed-secrets existingdir DEPRECATED. Local sealed-secrets directory. Overrides the project + from .kluctl.yaml + --output-metadata string Specify the output path for the project metadata to be written to. + -c, --project-config existingfile Location of the .kluctl.yaml config file. Defaults to + $PROJECT/.kluctl.yaml + -b, --project-ref string Git ref of the kluctl project. Only used when --project-url was given. + -p, --project-url string Git url of the kluctl project. If not specified, the current + directory will be used instead of a remote Git project + -t, --target string Target name to run command for. Target must exist in .kluctl.yaml. + --timeout duration Specify timeout for all operations, including loading of the project, + all external api calls and waiting for readiness. (default 10m0s) + +``` + + +## Image arguments + +These arguments are available on some target based commands. +They control image versions requested by `images.get_image(...)` [calls]({{< ref "docs/reference/deployments/images#imagesget_image" >}}). + + +``` +Image arguments: + Control fixed images and update behaviour. + + -F, --fixed-image stringArray Pin an image to a given version. Expects + '--fixed-image=image<:namespace:deployment:container>=result' + --fixed-images-file existingfile Use .yaml file to pin image versions. See output of list-images + sub-command or read the documentation for details about the output format + --offline-images Omit contacting image registries and do not query for latest image tags. + -u, --update-images This causes kluctl to prefer the latest image found in registries, based + on the 'latest_image' filters provided to 'images.get_image(...)' calls. + Use this flag if you want to update to the latest versions/tags of all + images. '-u' takes precedence over '--fixed-image/--fixed-images-file', + meaning that the latest images are used even if an older image is given + via fixed images. + +``` + + +## Inclusion/Exclusion arguments + +These arguments are available for some target based commands. +They control inclusion/exclusion based on tags and deployment item pathes. + + +``` +Inclusion/Exclusion arguments: + Control inclusion/exclusion. + + --exclude-deployment-dir stringArray Exclude deployment dir. The path must be relative to the root + deployment project. Exclusion has precedence over inclusion, same as + in --exclude-tag + -E, --exclude-tag stringArray Exclude deployments with given tag. Exclusion has precedence over + inclusion, meaning that explicitly excluded deployments will always + be excluded even if an inclusion rule would match the same deployment. + --include-deployment-dir stringArray Include deployment dir. The path must be relative to the root + deployment project. + -I, --include-tag stringArray Include deployments with given tag. + +``` + diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md new file mode 100644 index 000000000..d2753f4b4 --- /dev/null +++ b/docs/reference/commands/delete.md @@ -0,0 +1,46 @@ +--- +title: "delete" +linkTitle: "delete" +weight: 10 +description: > + delete command +--- + +## Command + +Usage: kluctl delete [flags] + +Delete a target (or parts of it) from the corresponding cluster +Objects are located based on 'commonLabels', configured in 'deployment.yaml' + +WARNING: This command will also delete objects which are not part of your deployment +project (anymore). It really only decides based on the 'deleteByLabel' labels and does NOT +take the local target/state into account! + + + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) +1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) +1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + -l, --delete-by-label stringArray Override the labels used to find objects for deletion. + --dry-run Performs all kubernetes API calls in dry-run mode. + -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format + can either be 'text' or 'yaml'. Can be specified multiple times. The actual + format for yaml is currently not documented and subject to change. + --render-output-dir string Specifies the target directory to render the project into. If omitted, a + temporary directory is used. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + +``` + + +They have the same meaning as described in [deploy](#deploy). diff --git a/docs/reference/commands/deploy.md b/docs/reference/commands/deploy.md new file mode 100644 index 000000000..2114ac54c --- /dev/null +++ b/docs/reference/commands/deploy.md @@ -0,0 +1,78 @@ +--- +title: "deploy" +linkTitle: "deploy" +weight: 10 +description: > + deploy command +--- + +## Command + +Usage: kluctl deploy [flags] + +Deploys a target to the corresponding cluster +This command will also output a diff between the initial state and the state after +deployment. The format of this diff is the same as for the 'diff' command. +It will also output a list of prunable objects (without actually deleting them). + + + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) +1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) +1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + --abort-on-error Abort deploying when an error occurs instead of trying the remaining deployments + --dry-run Performs all kubernetes API calls in dry-run mode. + --force-apply Force conflict resolution when applying. See documentation for details + --force-replace-on-error Same as --replace-on-error, but also try to delete and re-create objects. See + documentation for more details. + --no-wait Don't wait for objects readiness' + -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format + can either be 'text' or 'yaml'. Can be specified multiple times. The actual + format for yaml is currently not documented and subject to change. + --readiness-timeout duration Maximum time to wait for object readiness. The timeout is meant per-object. + Timeouts are in the duration format (1s, 1m, 1h, ...). If not specified, a + default timeout of 5m is used. (default 5m0s) + --render-output-dir string Specifies the target directory to render the project into. If omitted, a + temporary directory is used. + --replace-on-error When patching an object fails, try to replace it. See documentation for more + details. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + +``` + + +### --force-apply +kluctl implements deployments via [server-side apply](https://kubernetes.io/reference/using-api/server-side-apply/) +and a custom automatic conflict resolution algorithm. This algurithm is an automatic implementation of the +"[Don't overwrite value, give up management claim](https://kubernetes.io/reference/using-api/server-side-apply/#conflicts)" +method. It should work in most cases, but might still fail. In case of such failure, you can use `--force-apply` to +use the "Overwrite value, become sole manager" strategy instead. + +Please note that this is a risky operation which might overwrite fields which were initially managed by kluctl but were +then overtaken by other managers (e.g. by operators). Always use this option with caution and perform a dry-run +before to ensure nothing unexpected gets overwritten. + +### --replace-on-error +In some situations, patching Kubernetes objects might fail for different reasons. In such cases, you can try +`--replace-on-error` to instruct kluctl to retry with an update operation. + +Please note that this will cause all fields to be overwritten, even if owned by other field managers. + +### --force-replace-on-error +This flag will cause the same replacement attempt on failure as with `--replace-on-error`. In addition, it will fallback +to a delete+recreate operation in case the replace also fails. + +Please note that this is a potentially risky operation, especially when an object carries some kind of important state. + +### --abort-on-error +kluctl does not abort a command when an individual object fails can not be updated. It collects all errors and warnings +and outputs them instead. This option modifies the behaviour to immediately abort the command. diff --git a/docs/reference/commands/diff.md b/docs/reference/commands/diff.md new file mode 100644 index 000000000..3af235dd6 --- /dev/null +++ b/docs/reference/commands/diff.md @@ -0,0 +1,50 @@ +--- +title: "diff" +linkTitle: "diff" +weight: 10 +description: > + diff command +--- + +## Command + +Usage: kluctl diff [flags] + +Perform a diff between the locally rendered target and the already deployed target +The output is by default in human readable form (a table combined with unified diffs). +The output can also be changed to output a yaml file. Please note however that the format +is currently not documented and prone to changes. +After the diff is performed, the command will also search for prunable objects and list them. + + + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) +1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) +1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + --force-apply Force conflict resolution when applying. See documentation for details + --force-replace-on-error Same as --replace-on-error, but also try to delete and re-create objects. See + documentation for more details. + --ignore-annotations Ignores changes in annotations when diffing + --ignore-labels Ignores changes in labels when diffing + --ignore-tags Ignores changes in tags when diffing + -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can + either be 'text' or 'yaml'. Can be specified multiple times. The actual format + for yaml is currently not documented and subject to change. + --render-output-dir string Specifies the target directory to render the project into. If omitted, a + temporary directory is used. + --replace-on-error When patching an object fails, try to replace it. See documentation for more + details. + +``` + + +`--force-apply` and `--replace-on-error` have the same meaning as in [deploy](#deploy). diff --git a/docs/reference/commands/environment-variables.md b/docs/reference/commands/environment-variables.md new file mode 100644 index 000000000..15b7f003c --- /dev/null +++ b/docs/reference/commands/environment-variables.md @@ -0,0 +1,23 @@ +--- +title: "Environment Variables" +linkTitle: "Environment Variables" +weight: 2 +description: > + Controlling Kluctl via environment variables +--- + +In addition to arguments, Kluctl can be controlled via a set of environment variables. + +## Environment variables as arguments +All options/arguments accepted by kluctl can also be specified via environment variables. The name of the environment +variables always start with `KLUCTL_` and end with the option/argument in uppercase and dashes replaced with +underscores. As an example, `--project-url=my-project` can also be specified with the environment variable +`KLUCTL_PROJECT_URL=my-project`. + +## Additional environment variables +A few additional environment variables are supported which do not belong to an option/argument. These are: + +1. `KLUCTL_REGISTRY__HOST`, `KLUCTL_REGISTRY__USERNAME`, and so on. See [registries]({{< ref "docs/reference/deployments/images#supported-image-registries-and-authentication" >}}) for details. +2. `KLUCTL_SSH_DISABLE_STRICT_HOST_KEY_CHECKING`. Disable ssh host key checking when accessing git repositories. +3. `KLUCTL_NO_THREADS`. Do not use multithreading while performing work. This is only useful for debugging purposes. +4. `KLUCTL_IGNORE_DEBUGGER`. Pretend that there is no debugger attached when automatically deciding if multi-threading should be enabled or not. diff --git a/docs/reference/commands/helm-pull.md b/docs/reference/commands/helm-pull.md new file mode 100644 index 000000000..576324be4 --- /dev/null +++ b/docs/reference/commands/helm-pull.md @@ -0,0 +1,24 @@ +--- +title: "helm-pull" +linkTitle: "helm-pull" +weight: 10 +description: > + helm-pull command +--- + +## Command + +Usage: kluctl helm-pull [flags] + +Recursively searches for 'helm-chart.yaml' files and pulls the specified Helm charts +The Helm charts are stored under the sub-directory 'charts/' next to the +'helm-chart.yaml'. These Helm charts are meant to be added to version control so that +pulling is only needed when really required (e.g. when the chart version changes). + + + +See [helm-integration]({{< ref "docs/reference/deployments/helm">}}) for more details. + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) (except `-a`) diff --git a/docs/reference/commands/helm-update.md b/docs/reference/commands/helm-update.md new file mode 100644 index 000000000..a9e71f641 --- /dev/null +++ b/docs/reference/commands/helm-update.md @@ -0,0 +1,45 @@ +--- +title: "helm-update" +linkTitle: "helm-update" +weight: 10 +description: > + helm-update command +--- + +## Command + +Usage: kluctl helm-update [flags] + +Recursively searches for 'helm-chart.yaml' files and checks for new available versions +Optionally performs the actual upgrade and/or add a commit to version control. + + + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) (except `-a`) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + --commit Create a git commit for every updated chart + --insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --key-file=:, where must match + the id specified in the helm-chart.yaml. + --password stringArray Specify password to use for Helm Repository authentication. Must be + in the form --password=:, where + must match the id specified in the helm-chart.yaml. + --upgrade Write new versions into helm-chart.yaml and perform helm-pull afterwards + --username stringArray Specify username to use for Helm Repository authentication. Must be + in the form --username=:, where + must match the id specified in the helm-chart.yaml. + +``` + \ No newline at end of file diff --git a/docs/reference/commands/list-images.md b/docs/reference/commands/list-images.md new file mode 100644 index 000000000..cf56b6af1 --- /dev/null +++ b/docs/reference/commands/list-images.md @@ -0,0 +1,39 @@ +--- +title: "list-images" +linkTitle: "list-images" +weight: 10 +description: > + list-images command +--- + +## Command + +Usage: kluctl list-images [flags] + +Renders the target and outputs all images used via 'images.get_image(...) +The result is a compatible with yaml files expected by --fixed-images-file. + +If fixed images ('-f/--fixed-image') are provided, these are also taken into account, +as described in for the deploy command. + + + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) +1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) +1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + -o, --output stringArray Specify output target file. Can be specified multiple times + --render-output-dir string Specifies the target directory to render the project into. If omitted, a + temporary directory is used. + --simple Output a simplified version of the images list + +``` + diff --git a/docs/reference/commands/list-targets.md b/docs/reference/commands/list-targets.md new file mode 100644 index 000000000..3ef135116 --- /dev/null +++ b/docs/reference/commands/list-targets.md @@ -0,0 +1,28 @@ +--- +title: "list-targets" +linkTitle: "list-targets" +weight: 10 +description: > + list-targets command +--- + +## Command + +Usage: kluctl list-targets [flags] + +Outputs a yaml list with all target, including dynamic targets +Outputs a yaml list with all target, including dynamic targets + + + +## Arguments +The following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + -o, --output stringArray Specify output target file. Can be specified multiple times + +``` + \ No newline at end of file diff --git a/docs/reference/commands/poke-images.md b/docs/reference/commands/poke-images.md new file mode 100644 index 000000000..2a0aa64f2 --- /dev/null +++ b/docs/reference/commands/poke-images.md @@ -0,0 +1,41 @@ +--- +title: "poke-images" +linkTitle: "poke-images" +weight: 10 +description: > + poke-images command +--- + +## Command + +Usage: kluctl poke-images [flags] + +Replace all images in target +This command will fully render the target and then only replace images instead of fully +deploying the target. Only images used in combination with 'images.get_image(...)' are +replaced + + + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) +1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) +1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + --dry-run Performs all kubernetes API calls in dry-run mode. + -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can + either be 'text' or 'yaml'. Can be specified multiple times. The actual format + for yaml is currently not documented and subject to change. + --render-output-dir string Specifies the target directory to render the project into. If omitted, a + temporary directory is used. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + +``` + \ No newline at end of file diff --git a/docs/reference/commands/prune.md b/docs/reference/commands/prune.md new file mode 100644 index 000000000..f019404ac --- /dev/null +++ b/docs/reference/commands/prune.md @@ -0,0 +1,39 @@ +--- +title: "prune" +linkTitle: "prune" +weight: 10 +description: > + prune command +--- + +## Command + +Usage: kluctl prune [flags] + +Searches the target cluster for prunable objects and deletes them + + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) +1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) +1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + --dry-run Performs all kubernetes API calls in dry-run mode. + -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can + either be 'text' or 'yaml'. Can be specified multiple times. The actual format + for yaml is currently not documented and subject to change. + --render-output-dir string Specifies the target directory to render the project into. If omitted, a + temporary directory is used. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + +``` + + +They have the same meaning as described in [deploy](#deploy). diff --git a/docs/reference/commands/render.md b/docs/reference/commands/render.md new file mode 100644 index 000000000..f4869433b --- /dev/null +++ b/docs/reference/commands/render.md @@ -0,0 +1,37 @@ +--- +title: "render" +linkTitle: "render" +weight: 10 +description: > + render command +--- + +## Command + +Usage: kluctl render [flags] + +Renders all resources and configuration files +Renders all resources and configuration files and stores the result in either +a temporary directory or a specified directory. + + + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) +1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + --offline-kubernetes Run render in offline mode, meaning that it will not try to connect the target + cluster + --print-all Write all rendered manifests to stdout + --render-output-dir string Specifies the target directory to render the project into. If omitted, a + temporary directory is used. + +``` + \ No newline at end of file diff --git a/docs/reference/commands/seal.md b/docs/reference/commands/seal.md new file mode 100644 index 000000000..e87b720eb --- /dev/null +++ b/docs/reference/commands/seal.md @@ -0,0 +1,42 @@ +--- +title: "seal" +linkTitle: "seal" +weight: 10 +description: > + seal command +--- + +## Command + +Usage: kluctl seal [flags] + +Seal secrets based on target's sealingConfig +Loads all secrets from the specified secrets sets from the target's sealingConfig and +then renders the target, including all files with the '.sealme' extension. Then runs +kubeseal on each '.sealme' file and stores secrets in the directory specified by +'--local-sealed-secrets', using the outputPattern from your deployment project. + +If no '--target' is specified, sealing is performed for all targets. + + + +See [sealed-secrets]({{< ref "docs/reference/sealed-secrets">}}) for more details. + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) (except `-a`) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + --cert-file string Use the given certificate for sealing instead of requesting it from the + sealed-secrets controller + --force-reseal Lets kluctl ignore secret hashes found in already sealed secrets and thus forces + resealing of those. + --offline-kubernetes Run seal in offline mode, meaning that it will not try to connect the target cluster + +``` + diff --git a/docs/reference/commands/validate.md b/docs/reference/commands/validate.md new file mode 100644 index 000000000..d27b9f59d --- /dev/null +++ b/docs/reference/commands/validate.md @@ -0,0 +1,39 @@ +--- +title: "validate" +linkTitle: "validate" +weight: 10 +description: > + validate command +--- + +## Command + +Usage: kluctl validate [flags] + +Validates the already deployed deployment +This means that all objects are retrieved from the cluster and checked for readiness. + +TODO: This needs to be better documented! + + + +## Arguments +The following sets of arguments are available: +1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) +1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + -o, --output stringArray Specify output target file. Can be specified multiple times + --render-output-dir string Specifies the target directory to render the project into. If omitted, a + temporary directory is used. + --sleep duration Sleep duration between validation attempts (default 5s) + --wait duration Wait for the given amount of time until the deployment validates + --warnings-as-errors Consider warnings as failures + +``` + diff --git a/docs/reference/deployments/_index.md b/docs/reference/deployments/_index.md new file mode 100644 index 000000000..de97255a3 --- /dev/null +++ b/docs/reference/deployments/_index.md @@ -0,0 +1,62 @@ +--- +title: "Deployments" +linkTitle: "Deployments" +weight: 2 +description: > + Deployments and sub-deployments. +--- + +A deployment project is collection of deployment items and sub-deployments. Deployment items are usually +[Kustomize]({{< ref "./kustomize" >}}) deployments, but can also integrate [Helm Charts]({{< ref "./helm" >}}). + +## Basic structure + +The following visualization shows the basic structure of a deployment project. The entry point of every deployment +project is the `deployment.yaml` file, which then includes further sub-deployments and kustomize deployments. It also +provides some additional configuration required for multiple kluctl features to work as expected. + +As can be seen, sub-deployments can include other sub-deployments, allowing you to structure the deployment project +as you need. + +Each level in this structure recursively adds [tags]({{< ref "./tags" >}}) to each deployed resources, allowing you to control +precisely what is deployed in the future. + +Some visualized files/directories have links attached, follow them to get more information. + +
+-- project-dir/
+   |-- }}">deployment.yaml
+   |-- .gitignore
+   |-- kustomize-deployment1/
+   |   |-- kustomization.yaml
+   |   `-- resource.yaml
+   |-- sub-deployment/
+   |   |-- deployment.yaml
+   |   |-- kustomize-deployment2/
+   |   |   |-- kustomization.yaml
+   |   |   |-- resource1.yaml
+   |   |   `-- ...
+   |   |-- kustomize-deployment3/
+   |   |   |-- kustomization.yaml
+   |   |   |-- resource1.yaml
+   |   |   |-- resource2.yaml
+   |   |   |-- patch1.yaml
+   |   |   `-- ...
+   |   |-- }}">kustomize-with-helm-deployment/
+   |   |   |-- charts/
+   |   |   |   `-- ...
+   |   |   |-- kustomization.yaml
+   |   |   |-- helm-chart.yaml
+   |   |   `-- helm-values.yaml
+   |   `-- subsub-deployment/
+   |       |-- deployment.yaml
+   |       |-- ... kustomize deployments
+   |       `-- ... subsubsub deployments
+   `-- sub-deployment/
+       `-- ...
+
+ +## Order of deployments +Deployments are done in parallel, meaning that there are usually no order guarantees. The only way to somehow control +order, is by placing [barriers]({{< ref "./deployment-yml#barriers" >}}) between kustomize deployments. +You should however not overuse barriers, as they negatively impact the speed of kluctl. diff --git a/docs/reference/deployments/annotations/_index.md b/docs/reference/deployments/annotations/_index.md new file mode 100644 index 000000000..a4187a3e9 --- /dev/null +++ b/docs/reference/deployments/annotations/_index.md @@ -0,0 +1,7 @@ +--- +title: "Annotations" +linkTitle: "Annotations" +weight: 10 +description: > + Annotations usable in Kubernetes resources. +--- diff --git a/docs/reference/deployments/annotations/all-resources.md b/docs/reference/deployments/annotations/all-resources.md new file mode 100644 index 000000000..c77c2604f --- /dev/null +++ b/docs/reference/deployments/annotations/all-resources.md @@ -0,0 +1,91 @@ +--- +title: "All resources" +linkTitle: "All resources" +weight: 1 +description: > + Annotations on all resources +--- + +The following annotations control the behavior of the `deploy` and related commands. + +## Control deploy behavior + +The following annotations control deploy behavior, especially in regard to conflict resolution. + +### kluctl.io/delete +If set to "true", the resource will be deleted at deployment time. Kluctl will not emit an error in case the resource +does not exist. A resource with this annotation does not have to be complete/valid as it is never sent to the Kubernetes +api server. + +### kluctl.io/force-apply +If set to "true", the whole resource will be force-applied, meaning that all fields will be overwritten in case of +field manager conflicts. + +### kluctl.io/force-apply-field +Specifies a [JSON Path](https://goessner.net/articles/JsonPath/) for fields that should be force-applied. Matching +fields will be overwritten in case of field manager conflicts. + +If more than one field needs to be specified, add `-xxx` to the annotation key, where `xxx` is an arbitrary number. + +## Control deletion/pruning + +The following annotations control how delete/prune is behaving. + +### kluctl.io/skip-delete +If set to "true", the annotated resource will not be deleted when [delete]({{< ref "docs/reference/commands/delete" >}}) or +[prune]({{< ref "docs/reference/commands/prune" >}}) is called. + +### kluctl.io/skip-delete-if-tags +If set to "true", the annotated resource will not be deleted when [delete]({{< ref "docs/reference/commands/delete" >}}) or +[prune]({{< ref "docs/reference/commands/prune" >}}) is called and inclusion/exclusion tags are used at the same time. + +This tag is especially useful and required on resources that would otherwise cause cascaded deletions of resources that +do not match the specified inclusion/exclusion tags. Namespaces are the most prominent example of such resources, as +they most likely don't match exclusion tags, but cascaded deletion would still cause deletion of the excluded resources. + +## Control diff behavior + +The following annotations control how diffs are performed. + +### kluctl.io/diff-name +This annotation will override the name of the object when looking for the in-cluster version of an object used for +diffs. This is useful when you are forced to use new names for the same objects whenever the content changes, e.g. +for all kinds of immutable resource types. + +Example (filename job.yaml): +```yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: myjob-{{ load_sha256("job.yaml", 6) }} + annotations: + kluctl.io/diff-name: myjob +spec: + template: + spec: + containers: + - name: hello + image: busybox + command: ["sh", "-c", "echo hello"] + restartPolicy: Never +``` + +Without the `kluctl.io/diff-name` annotation, any change to the `job.yaml` would be treated as a new object in resulting +diffs from various commands. This is due to the inclusion of the file hash in the job name. This would make it very hard +to figure out what exactly changed in an object. + +With the `kluctl.io/diff-name` annotation, kluctl will pick an existing job from the cluster with the same diff-name +and use it for the diff, making it a lot easier to analyze changes. If multiple objects match, the one with the youngest +`creationTimestamp` is chosen. + +Please note that this will not cause old objects (with the same diff-name) to be prunes. You still have to regularely +prune the deployment. + +### kluctl.io/ignore-diff +If set to "true", the whole resource will be ignored while calculating diffs. + +### kluctl.io/ignore-diff-field +Specifies a [JSON Path](https://goessner.net/articles/JsonPath/) for fields that should be ignored while calculating +diffs. + +If more than one field needs to be specified, add `-xxx` to the annotation key, where `xxx` is an arbitrary number. diff --git a/docs/reference/deployments/annotations/hooks.md b/docs/reference/deployments/annotations/hooks.md new file mode 100644 index 000000000..c937cc95e --- /dev/null +++ b/docs/reference/deployments/annotations/hooks.md @@ -0,0 +1,23 @@ +--- +title: "Hooks" +linkTitle: "Hooks" +weight: 2 +description: > + Annotations on hooks +--- +The following annotations control hook execution + +See [hooks]({{< ref "docs/reference/deployments/hooks" >}}) for more details. + +### kluctl.io/hook +Declares a resource to be a hook, which is deployed/executed as described in [hooks]({{< ref "docs/reference/deployments/hooks" >}}). The value of the +annotation determines when the hook is deployed/executed. + +### kluctl.io/hook-weight +Specifies a weight for the hook, used to determine deployment/execution order. + +### kluctl.io/hook-delete-policy +Defines when to delete the hook resource. + +### kluctl.io/hook-wait +Defines whether kluctl should wait for hook-completion. diff --git a/docs/reference/deployments/annotations/kustomization.md b/docs/reference/deployments/annotations/kustomization.md new file mode 100644 index 000000000..8c2f98e0d --- /dev/null +++ b/docs/reference/deployments/annotations/kustomization.md @@ -0,0 +1,33 @@ +--- +title: "Kustomize" +linkTitle: "Kustomize" +weight: 4 +description: > + Annotations on the kustomization.yaml resource +--- + +Even though the `kustomization.yaml` from Kustomize deployments are not really Kubernetes resources (as they are not +really deployed), they have the same structure as Kubernetes resources. This also means that the `kustomization.yaml` +can define metadata and annotations. Through these annotations, additional behavior on the deployment can be controlled. + +Example: +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +metadata: + annotations: + kluctl.io/barrier: "true" + kluctl.io/wait-readiness: "true" + +resources: + - deployment.yaml +``` + +### kluctl.io/barrier +If set to `true`, kluctl will wait for all previous objects to be applied (but not necessarily ready). This has the +same effect as [barrier]({{< ref "docs/reference/deployments#barriers" >}}) from deployment projects. + +### kluctl.io/wait-readiness +If set to `true`, kluctl will wait for readiness of all objects from this kustomization project. Readiness is defined +the same as in [hook readiness]({{< ref "docs/reference/deployments/readiness" >}}). diff --git a/docs/reference/deployments/annotations/validation.md b/docs/reference/deployments/annotations/validation.md new file mode 100644 index 000000000..df528c164 --- /dev/null +++ b/docs/reference/deployments/annotations/validation.md @@ -0,0 +1,16 @@ +--- +title: "Validation" +linkTitle: "Validation" +weight: 3 +description: > + Annotations to control validation +--- + +The following annotations influence the [validate]({{< ref "docs/reference/commands/validate" >}}) command. + +### validate-result.kluctl.io/xxx +If this annotation is found on a resource that is checked while validation, the key and the value of the annotation +are added to the validation result, which is then returned by the validate command. + +The annotation key is dynamic, meaning that all annotations that begin with `validate-result.kluctl.io/` are taken +into account. diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md new file mode 100644 index 000000000..bb22bac01 --- /dev/null +++ b/docs/reference/deployments/deployment-yml.md @@ -0,0 +1,306 @@ +--- +title: "deployment.yaml" +linkTitle: "deployment.yaml" +weight: 1 +description: > + Structure of deployment.yaml. +--- + +The `deployment.yaml` file is the entrypoint for the deployment project. Included sub-deployments also provide a +`deployment.yaml` file with the same structure as the initial one. + +An example `deployment.yaml` looks like this: +```yaml +sealedSecrets: + outputPattern: "{{ cluster.name }}/{{ args.environment }}" + +deployments: +- path: nginx +- path: my-app +- include: monitoring + +commonLabels: + my.prefix/target: "{{ target.name }}" + my.prefix/deployment-project: my-deployment-project + +args: +- name: environment +``` + +The following sub-chapters describe the available fields in the `deployment.yaml` + +## sealedSecrets +`sealedSecrets` configures how sealed secrets are stored while sealing and located while rendering. +See [Sealed Secrets]({{< ref "docs/reference/sealed-secrets#outputpattern-and-location-of-stored-sealed-secrets" >}}) +for details. + +## deployments + +`deployments` is a list of deployment items. Multiple deployment types are supported, which is documented further down. +Individual deployments are performed in parallel, unless a [barrier](#barriers) is encountered which causes kluctl to +wait for all previous deployments to finish. + +### Kustomize deployments + +Specifies a [kustomize](https://kustomize.io/) deployment. +Please see [Kustomize integration]({{< ref "./kustomize" >}}) for more details. + +Example: +```yaml +deployments: +- path: path/to/deployment1 +- path: path/to/deployment2 + waitReadiness: true +``` + +The `path` must point to a directory relative to the directory containing the `deployment.yaml`. Only directories +that are part of the kluctl project are allowed. The directory must contain a valid `kustomization.yaml`. + +`waitReadiness` is optional and if set to `true` instructs kluctl to wait for readiness of each individual object +of the kustomize deployment. Readiness is defined in [readiness]({{< ref "./readiness" >}}). + +### Includes + +Specifies a sub-deployment project to be included. The included sub-deployment project will inherit many properties +of the parent project, e.g. tags, commonLabels and so on. + +Example: +```yaml +deployments: +- include: path/to/sub-deployment +``` + +The `path` must point to a directory relative to the directory containing the `deployment.yaml`. Only directories +that are part of the kluctl project are allowed. The directory must contain a valid `deployment.yaml`. + +### Git includes + +Specifies an external git project to be included. The project is included the same way with regular includes, except +that the included project can not use/load templates from the parent project. An included project might also include +further git projects. + +Simple example: +```yaml +deployments: +- git: git@github.com/example/example.git +``` + +This will clone the git repository at `git@github.com/example/example.git`, checkout the default branch and include it +into the current project. + +Advanced Example: +```yaml +deployments: +- git: + url: git@github.com/example/example.git + ref: my-branch + subDir: some/sub/dir +``` + +The url specifies the Git url to be cloned and checked out. `ref` is optional and specifies the branch or tag to be used. +If `ref` is omitted, the default branch will be checked out. `subDir` is optional and specifies the sub directory inside +the git repository to include. + +### Barriers +Causes kluctl to wait until all previous kustomize deployments have been applied. This is useful when +upcoming deployments need the current or previous deployments to be finished beforehand. Previous deployments also +include all sub-deployments from included deployments. + +Example: +```yaml +deployments: +- path: kustomizeDeployment1 +- path: kustomizeDeployment2 +- include: subDeployment1 +- barrier: true +# At this point, it's ensured that kustomizeDeployment1, kustomizeDeployment2 and all sub-deployments from +# subDeployment1 are fully deployed. +- path: kustomizeDeployment3 +``` + +## deployments common properties +All entries in `deployments` can have the following common properties: + +### vars (deployment item) +A list of variable sets to be loaded into the templating context, which is then available in all [deployment items](#deployments) +and [sub-deployments](#includes). + +See [templating]({{< ref "docs/reference/templating#vars-from-deploymentyaml" >}}) for more details. + +Example: +```yaml +deployments: +- path: kustomizeDeployment1 + vars: + - file: vars1.yaml + - values: + var1: value1 +- path: kustomizeDeployment2 +# all sub-deployments of this include will have the given variables available in their Jinj2 context. +- include: subDeployment1 + vars: + - file: vars2.yaml +``` + +### tags (deployment item) +A list of tags the deployment should have. See [tags]({{< ref "./tags" >}}) for more details. For includes, this means that all +sub-deployments will get these tags applied to. If not specified, the default tags logic as described in [tags]({{< ref "./tags" >}}) +is applied. + +Example: + +```yaml +deployments: +- path: kustomizeDeployment1 + tags: + - tag1 + - tag2 +- path: kustomizeDeployment2 + tags: + - tag3 +# all sub-deployments of this include will get tag4 applied +- include: subDeployment1 + tags: + - tag4 +``` + +### alwaysDeploy +Forces a deployment to be included everytime, ignoring inclusion/exclusion sets from the command line. +See [Deploying with tag inclusion/exclusion]({{< ref "./tags#deploying-with-tag-inclusionexclusion" >}}) for details. + +```yaml +deployments: +- path: kustomizeDeployment1 + alwaysDeploy: true +- path: kustomizeDeployment2 +``` + +### skipDeleteIfTags +Forces exclusion of a deployment whenever inclusion/exclusion tags are specified via command line. +See [Deleting with tag inclusion/exclusion]({{< ref "./tags#deploying-with-tag-inclusionexclusion" >}}) for details. + +```yaml +deployments: +- path: kustomizeDeployment1 + skipDeleteIfTags: true +- path: kustomizeDeployment2 +``` + +## vars (deployment project) +A list of variable sets to be loaded into the templating context, which is then available in all [deployment items](#deployments) +and [sub-deployments](#includes). + +See [templating]({{< ref "docs/reference/templating#vars-from-deploymentyaml" >}}) for more details. + +## commonLabels +A dictionary of [labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) and values to be +added to all resources deployed by any of the kustomize deployments in this deployment project. + +This feature is mainly meant to make it possible to identify all objects in a kubernetes cluster that were once deployed +through a specific deployment project. + +Consider the following example `deployment.yaml`: +```yaml +deployments: + - path: nginx + - include: sub-deployment1 + +commonLabels: + my.prefix/target: {{ target.name }} + my.prefix/deployment-name: my-deployment-project-name + my.prefix/label-1: value-1 + my.prefix/label-2: value-2 +``` + +Every resource deployed by the kustomize deployment `nginx` will now get the two provided labels attached. All included +sub-deployment projects (e.g. `sub-deployment1`) will also recursively inherit these labels and pass them to further +down. + +In case an included sub-deployment project also contains `commonLabels`, both dictionaries of common labels are merged +inside the included sub-deployment project. In case of conflicts, the included common labels override the inherited. + +The root deployment's `commonLabels` is also used to identify objects to be deleted when performing `kluctl delete` +or `kluctl prune` operations + +Please note that these `commonLabels` are not related to `commonLabels` supported in `kustomization.yaml` files. It was +decided to not rely on this feature but instead attach labels manually to resources right before sending them to +kubernetes. This is due to an [implementation detail](https://github.com/kubernetes-sigs/kustomize/issues/1009) in +kustomize which causes `commonLabels` to also be applied to label selectors, which makes otherwise editable resources +read-only when it comes to `commonLabels`. + +## overrideNamespace +A string that is used as the default namespace for all kustomize deployments which don't have a `namespace` set in their +`kustomization.yaml`. + +## tags (deployment project) +A list of common tags which are applied to all kustomize deployments and sub-deployment includes. + +See [tags]({{< ref "./tags" >}}) for more details. + +## args +A list of arguments that can or must be passed to most kluctl operations. Each of these arguments is then available +in templating via the global `args` object. Only the root `deployment.yaml` can contain such argument definitions. + +An example looks like this: +```yaml +deployments: + - path: nginx + +args: + - name: environment + - name: enable_debug + default: false + - name: complex_arg + default: + my: + nested1: arg1 + nested2: arg2 +``` + +These arguments can then be used in templating, e.g. by using `{{ args.environment }}`. + +When calling kluctl, most of the commands will then require you to specify at least `-a environment=xxx` and optionally +`-a enable_debug=true` + +The following sub chapters describe the fields for argument entries. + +### name +The name of the argument. + +### default +If specified, the argument becomes optional and will use the given value as default when not specified. + +The default value can be an arbitrary yaml value, meaning that it can also be a nested dictionary. In that case, passing +args in nested form will only set the nested value. With the above example of `complex_arg`, running: + +``` +kluctl deploy -t my-target -a my.nested1=override` +``` + +will only modify the value below `my.nested1` and keep the value of `my.nested2`. + +## ignoreForDiff + +A list of objects and fields to ignore while performing diffs. Consider the following example: + +```yaml +deployments: + - ... + +ignoreForDiff: + - group: apps + kind: Deployment + namespace: my-namespace + name: my-deployment + fieldPath: spec.replicas +``` + +This will remove the `spec.replicas` field from every resource that matches the object. +`group`, `kind`, `namespace` and `name` can be omitted, which results in all objects matching. `fieldPath` must be a +valid [JSON Path](https://goessner.net/articles/JsonPath/). `fieldPath` may also be a list of JSON paths. + +The JSON Path implementation used in kluctl has extended support for wildcards in field +names, allowing you to also specify paths like `metadata.labels.my-prefix-*`. + +As an alternative, [annotations]({{< ref "./annotations/all-resources#control-diff-behavior" >}}) can be used to control +diff behavior of individual resources. diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md new file mode 100644 index 000000000..0bc1733d9 --- /dev/null +++ b/docs/reference/deployments/helm.md @@ -0,0 +1,174 @@ +--- +title: "Helm Integration" +linkTitle: "Helm Integration" +weight: 3 +description: > + How Helm is integrated into Kluctl. +--- + +kluctl offers a simple-to-use Helm integration, which allows you to reuse many common third-party Helm Charts. + +The integration is split into 2 parts/steps/layers. The first is the management and pulling of the Helm Charts, while +the second part handles configuration/customization and deployment of the chart. + +Pulled Helm Charts are meant to be added to version control to ensure proper speed and consistency. + +## How it works + +Helm charts are not directly deployed via Helm. Instead, kluctl renders the Helm Chart into a single file and then +hands over the rendered yaml to [kustomize](https://kustomize.io/). Rendering is done in combination with a provided +`helm-values.yaml`, which contains the necessary values to configure the Helm Chart. + +The resulting rendered yaml is then referred by your `kustomization.yaml`, from which point on the +[kustomize integration]({{< ref "docs/reference/sealed-secrets#outputpattern-and-location-of-stored-sealed-secrets" >}}) +takes over. This means, that you can perform all desired customization (patches, namespace override, ...) as if you +provided your own resources via yaml files. + +### Helm hooks + +[Helm Hooks](https://helm.sh/docs/topics/charts_hooks/) are implemented by mapping them +to [kluctl hooks]({{< ref "./hooks" >}}), based on the following mapping table: + +| Helm hook | kluctl hook | +|---------------|---------------------| +| pre-install | pre-deploy-initial | +| post-install | post-deploy-initial | +| pre-delete | Not supported | +| post-delete | Not supported | +| pre-upgrade | pre-deploy-upgrade | +| post-upgrade | post-deploy-upgrade | +| pre-rollback | Not supported | +| post-rollback | Not supported | +| test | Not supported | + +Please note that this is a best effort approach and not 100% compatible to how Helm would run hooks. + +## helm-chart.yaml + +The `helm-chart.yaml` defines where to get the chart from, which version should be pulled, the rendered output file name, +and a few more Helm options. After this file is added to your project, you need to invoke the `helm-pull` command +to pull the Helm Chart into your local project. It is advised to put the pulled Helm Chart into version control, so +that deployments will always be based on the exact same Chart (Helm does not guarantee this when pulling). + +Example `helm-chart.yaml`: + +```yaml +helmChart: + repo: https://charts.bitnami.com/bitnami + chartName: redis + chartVersion: 12.1.1 + skipUpdate: false + releaseName: redis-cache + namespace: "{{ my.jinja2.var }}" + output: helm-rendered.yaml # this is optional +``` + +When running the `helm-pull` command, it will search for all `helm-chart.yaml` files in your project and then pull the +chart from the specified repository with the specified version. The pull chart will then be located in the sub-directory +`charts` below the same directory as the `helm-chart.yaml` + +The same filename that was specified in `output` must then be referred in a `kustomization.yaml` as a normal local +resource. If `output` is omitted, the default value `helm-rendered.yaml` is used and must also be referenced in +`kustomization.yaml`. + +`helmChart` inside `helm-chart.yaml` supports the following fields: + +### repo +The url to the Helm repository where the Helm Chart is located. You can use hub.helm.sh to search for repositories and +charts and then use the repos found there. + +oci based repositories are also supported, for example: +```yaml +helmChart: + repo: oci://r.myreg.io/mycharts/pepper + chartName: pepper + chartVersion: 1.2.3 + releaseName: pepper + namespace: pepper +``` + +### chartName +The name of the chart that can be found in the repository. + +### chartVersion +The version of the chart. + +### skipUpdate +Skip this Helm Chart when the [helm-update]({{< ref "docs/reference/commands/helm-update" >}}) command is called. +If omitted, defaults to `false`. + +### releaseName +The name of the Helm Release. + +### namespace +The namespace that this Helm Chart is going to be deployed to. Please note that this should match the namespace +that you're actually deploying the kustomize deployment to. This means, that either `namespace` in `kustomization.yaml` +or `overrideNamespace` in `deployment.yaml` should match the namespace given here. The namespace should also be existing +already at the point in time when the kustomize deployment is deployed. + +### output +This is the file name into which the Helm Chart is rendered into. Your `kustomization.yaml` should include this same +file. The file should not be existing in your project, as it is created on-the-fly while deploying. + +### skipCRDs +If set to `true`, kluctl will pass `--skip-crds` to Helm when rendering the deployment. If set to `false` (which is +the default), kluctl will pass `--include-crds` to Helm. + +## helm-values.yaml +This file should be present when you need to pass custom Helm Value to Helm while rendering the deployment. Please +read the documentation of the used Helm Charts for details on what is supported. + +## Updates to helm-charts +In case a Helm Chart needs to be updated, you can either do this manually by replacing the [chartVersion](#chartversion) +value in `helm-chart.yaml` and the calling the [helm-pull]({{< ref "docs/reference/commands/helm-pull" >}}) command or by simply invoking +[helm-update]({{< ref "docs/reference/commands/helm-update" >}}) with `--upgrade` and/or `--commit` being set. + +## Private Chart Repositories +It is also possible to use private chart repositories. There are currently two options to provide Helm Repository +credentials to Kluctl. + +### Use `helm repo add --username xxx --password xxx` before +Kluctl will try to find known repositories that are managed by the Helm CLI and then try to reuse the credentials of +these. The repositories are identified by the URL of the repository, so it doesn't matter what name you used when you +added the repository to Helm. The same method can be used for client certificate based authentication (`--key-file` +in `helm repo add`). + +### Use the --username/--password arguments in `kluctl helm-pull` +See the [helm-pull command]({{< ref "docs/reference/commands/helm-pull" >}}). You can control repository credentials +via `--username`, `--password` and `--key-file`. Each argument must be in the form `credentialsId:value`, where +the `credentialsId` must match the id specified in the `helm-chart.yaml`. Example: + +```yaml +helmChart: + repo: https://raw.githubusercontent.com/example/private-helm-repo/main/ + credentialsId: private-helm-repo + chartName: my-chart + chartVersion: 1.2.3 + releaseName: my-chart + namespace: default +``` + +When credentialsId is specified, Kluctl will require you to specify `--username=private-helm-repo:my-username` and +`--password=private-helm-repo:my-password`. You can also specify a client-side certificate instead via +`--key-file=private-helm-repo:/path/to/cert`. + +Multiple Helm Charts can use the same `credentialsId`. + +Environment variables can also be used instead of arguments. See +[Environment Variables]({{< ref "docs/reference/commands/environment-variables" >}}) for details. + +## Templating + +Both `helm-chart.yaml` and `helm-values.yaml` are rendered by the [templating engine]({{< ref "docs/reference/templating" >}}) before they +are actually used. This means, that you can use all available Jinja2 variables at that point, which can for example be +seen in the above `helm-chart.yaml` example for the namespace. + +There is however one exception that leads to a small limitation. When `helm-pull` reads the `helm-chart.yaml`, it does +NOT render the file via the templating engine. This is because it can not know how to properly render the template as it +does have no information about targets (there are no `-t` arguments set) at that point. + +This exception leads to the limitation that the `helm-chart.yaml` MUST be valid yaml even in case it is not rendered +via the templating engine. This makes using control statements (if/for/...) impossible in this file. It also makes it +a requirement to use quotes around values that contain templates (e.g. the namespace in the above example). + +`helm-values.yaml` is not subject to these limitations as it is only interpreted while deploying. diff --git a/docs/reference/deployments/hooks.md b/docs/reference/deployments/hooks.md new file mode 100644 index 000000000..db83a5a5b --- /dev/null +++ b/docs/reference/deployments/hooks.md @@ -0,0 +1,48 @@ +--- +title: "Hooks" +linkTitle: "Hooks" +weight: 4 +description: > + Kluctl hooks. +--- + +Kluctl supports hooks in a similar fashion as known from Helm Charts. Hooks are executed/deployed before and/or after the +actual deployment of a kustomize deployment. + +To mark a resource as a hook, add the `kluctl.io/hook` annotation to a resource. The value of the annotation must be +a comma separated list of hook names. Possible value are described in the next chapter. + +## Hook types + +| Hook Type | Description | +|---|---| +| pre-deploy-initial | Executed right before the initial deployment is performed. | +| post-deploy-initial | Executed right after the initial deployment is performed. | +| pre-deploy-upgrade | Executed right before a non-initial deployment is performed. | +| post-deploy-upgrade | Executed right after a non-initial deployment is performed. | +| pre-deploy | Executed right before any (initial and non-initial) deployment is performed.| +| post-deploy | Executed right after any (initial and non-initial) deployment is performed. | + +A deployment is considered to be an "initial" deployment if none of the resources related to the current kustomize +deployment are found on the cluster at the time of deployment. + +If you need to execute hooks for every deployment, independent of its "initial" state, use +`pre-deploy-initial,pre-deploy` to indicate that it should be executed all the time. + +## Hook deletion + +Hook resources are by default deleted right before creation (if they already existed before). This behavior can be +changed by setting the `kluctl.io/hook-delete-policy` to a comma separated list of the following values: + +| Policy | Description | +|---|---| +| before-hook-creation | The default behavior, which means that the hook resource is deleted right before (re-)creation. | +| hook-succeeded | Delete the hook resource directly after it got "ready" | +| hook-failed | Delete the hook resource when it failed to get "ready" | + +## Hook readiness + +After each deployment/execution of the hooks that belong to a deployment stage (before/after deployment), kluctl +waits for the hook resources to become "ready". Readiness is defined [here]({{< ref "./readiness" >}}). + +It is possible to disable waiting for hook readiness by setting the annotation `kluctl.io/hook-wait` to "false". diff --git a/docs/reference/deployments/images.md b/docs/reference/deployments/images.md new file mode 100644 index 000000000..3c419381c --- /dev/null +++ b/docs/reference/deployments/images.md @@ -0,0 +1,130 @@ +--- +title: "Container Images" +linkTitle: "Container Images" +weight: 3 +description: > + Dynamic configuration of container images. +--- + +There are usually 2 different scenarios where Container Images need to be specified: +1. When deploying third party applications like nginx, redis, ... (e.g. via the [Helm integration]({{< ref "./helm" >}})).
+ * In this case, image versions/tags rarely change, and if they do, this is an explicit change to the deployment. +1. When deploying your own applications.
+ * In this case, image versions/tags might change very rapidly, sometimes multiple times per hour. It would be too much + effort and overhead when this would be managed explicitly via your deployment. Even with Jinja2 templating, this + would be hard to maintain. + +kluctl offers a better solution for the second case. + +## Dynamic versions/tags + +kluctl is able to ask the used container registry for a list of tags/versions available for an image. It then can +sort the list of images via a configurable order and then use the latest image for your deployment. + +It however only does this when the involved resource (e.g. a `Deployment` or `StatefulSet`) is not yet deployed. In case +it is already deployed, the already deployed image will be reused to avoid undesired re-deployment/re-starting of +otherwise unchanged resources. + +## images.get_image() + +This is solved via a templating function that is available in all templates/resources. The function is part of the global +`images` object and expects the following arguments: + +`images.get_image(image, latest_version)` + +* image + * The image location, excluding the tag. Please see [supported image registries](#supported-image-registries-and-authentication) to + understand which registries are supported.` +* latest_version + * Configures how tags/versions are sorted and thus how the latest image is determined. Can be: + * `version.semver()`
+ Filters and sorts by loose semantic versioning. Versions must start with a number. It allows unlimited + `.` inside the version. It treats versions with a suffix as less then versions without a suffix + (e.g. 1.0-rc1 < 1.0). Two versions which only differ by suffix are sorted semantically. + * `version.prefix(prefix)`
+ Only allows tags with the given prefix and then applies the same logic as images.semver() to whatever + follows right after the prefix. You can override the handling of the right part by providing `suffix=xxx`, + while `xxx` is another version filter, e.g. `version.prefix("master-", suffix=version.number()) + * `version.number()`
+ Only allows plain numbers as version numbers sorts them accordingly. + * `version.regex(regex)`
+ Only allows versions/tags that match the given regex. Sorting is done the same way as in version.semver(), + except that versions do not necessarily need to start with a number. + +The mentioned version filters must be specified as strings. For example, + +`images.get_version("my-image", "prefix('master-', suffix=number())")`. + +If no version_filter is specified, then it defaults to `"semver()"`. + +Example deployment: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + containers: + - name: c1 + image: "{{ images.get_image('registry.gitlab.com/my-group/my-project') }}" +``` + +## Always using the latest images +If you want to use the latest image no matter if an older version is already deployed, use the `-u` flag to your +`deploy`, `diff` or `list-images` commands. + +You can restrict updating to individual images by using `-I/--include-tag`. This is useful when using CI/CD for example, +where you often want to perform a deployment that only updates a single application/service from your large deployment. + +## Fixed images via CLI +The described `images.get_image` logic however leads to a loosely defined state on your target cluster/environment. This +might be fine in a CI/CD environment, but might be undesired when deploying to production. In that case, it might be +desirable to explicitly define which versions need to be deployed. + +To achieve this, you can use the `-F FIXED_IMAGE` [argument]({{< ref "docs/reference/commands/common-arguments#image-arguments" >}}). +`FIXED_IMAGE` must be in the form of `-F image<:namespace:deployment:container>=result`. For example, to pin the image +`registry.gitlab.com/my-group/my-project` to the tag `1.1.2` you'd have to specify +`-F registry.gitlab.com/my-group/my-project=registry.gitlab.com/my-group/my-project:1.1.2`. + +## Fixed images via a yaml file + +As an alternative to specifying each fixed image via CLI (`--fixed-images-file=`), you can also specify a single +yaml file via CLI which then contains a list of entries that define image/deployment -> imageResult mappings. + +An example fixed-images files looks like this: + +```yaml +images: + - image: registry.gitlab.com/my-group/my-project + resultImage: registry.gitlab.com/my-group/my-project:1.1.0 + - image: registry.gitlab.com/my-group/my-project2 + resultImage: registry.gitlab.com/my-group/my-project2:2.0.0 + - deployment: StatefulSet/my-sts + resultImage: registry.gitlab.com/my-group/my-project3:1.0.0 +``` + +You can also take an existing deployment and export the already deployed image versions into a fixed-images file by +using the `list-images` command. It will produce a compatible fixed-images file based on the calls to +`images.get_image` if a deployment would be performed with the given arguments. The result of that call is quite +expressive, as it contains all the information gathered while images were collected. Use `--simple` to only return +a list with image -> resultImage mappings. + +## Supported image registries and authentication +All [v2 API](https://docs.docker.com/registry/spec/api/) based image registries are supported, including the Docker Hub, +Gitlab, and many more. Private registries will need credentials to be setup correctly. This can be done by locally +logging in via `docker login ` or by specifying the following environment variables: + +Simply set the following environment variables to pass credentials to you private repository: +1. KLUCTL_REGISTRY_HOST=registry.example.com +2. KLUCTL_REGISTRY_USERNAME=username +3. KLUCTL_REGISTRY_PASSWORD=password + +You can also pass credentials for more registries by adding an index to the environment variables, +e.g. "KLUCTL_REGISTRY_1_HOST=registry.gitlab.com" + +In case your registry uses self-signed TLS certificates, it is currently required to disable TLS verification for these. +You can do this via `KLUCTL_REGISTRY_TLSVERIFY=1`/`KLUCTL_REGISTRY__TLSVERIFY=1` for the corresponding +`KLUCTL_REGISTRY_HOST`/`KLUCTL_REGISTRY__HOST` or by globally disabling it via `KLUCTL_REGISTRY_DEFAULT_TLSVERIFY=1`. diff --git a/docs/reference/deployments/kustomize.md b/docs/reference/deployments/kustomize.md new file mode 100644 index 000000000..d2099397c --- /dev/null +++ b/docs/reference/deployments/kustomize.md @@ -0,0 +1,17 @@ +--- +title: "Kustomize Integration" +linkTitle: "Kustomize Integration" +weight: 2 +description: > + How Kustomize is integrated into Kluctl +--- + +kluctl uses [kustomize](https://kustomize.io/) to render final resources. This means, that the finest/lowest +level in kluctl is represented with kustomize deployments. These kustomize deployments can then perform further +customization, e.g. patching and more. You can also use kustomize to easily generate ConfigMaps or secrets from files. + +Generally, everything is possible via `kustomization.yaml`, is thus possible in kluctl. + +We advise to read the kustomize +[reference](https://kubectl.docs.kubernetes.io/references/kustomize/). You can also look into the official kustomize +[example](https://github.com/kubernetes-sigs/kustomize/tree/master/examples). diff --git a/docs/reference/deployments/readiness.md b/docs/reference/deployments/readiness.md new file mode 100644 index 000000000..4712fc172 --- /dev/null +++ b/docs/reference/deployments/readiness.md @@ -0,0 +1,15 @@ +--- +title: "Readiness" +linkTitle: "Readiness" +weight: 5 +description: + Definition of readiness. +--- + +There are multiple places where kluctl can wait for "readiness" of resources, e.g. for hooks or when `waitReadiness` is +specified on a deployment item. Readiness depends on the resource kind, e.g. for a Job, kluctl would wait until it +finishes successfully. + +After each deployment/execution of the hooks that belong to a deployment stage (before/after deployment), kluctl +waits for the hook resources to become "ready". Readiness depends on the resource kind, e.g. for a Job, kluctl would +wait until it finishes successfully. diff --git a/docs/reference/deployments/tags.md b/docs/reference/deployments/tags.md new file mode 100644 index 000000000..bf2a00d86 --- /dev/null +++ b/docs/reference/deployments/tags.md @@ -0,0 +1,83 @@ +--- +title: "Tags" +linkTitle: "Tags" +weight: 6 +--- + +Every kustomize deployment has a set of tags assigned to it. These tags are defined in multiple places, which is +documented in [deployment.yaml]({{< ref "./deployment-yml" >}}). Look for the `tags` field, which is available in multiple places per +deployment project. + +Tags are useful when only one or more specific kustomize deployments need to be deployed or deleted. + +## Default tags + +[deployment items]({{< ref "./deployment-yml#deployments" >}}) in deployment projects can have an optional list of tags assigned. + +If this list is completely omitted, one single entry is added by default. This single entry equals to the last element +of the `path` in the `deployments` entry. + +Consider the following example: + +```yaml +deployments: + - path: nginx + - path: some/subdir +``` + +In this example, two kustomize deployments are defined. The first would get the tag `nginx` while the second +would get the tag `subdir`. + +In most cases this heuristic is enough to get proper tags with which you can work. It might however lead to strange +or even conflicting tags (e.g. `subdir` is really a bad tag), in which case you'd have to explicitly set tags. + +## Tag inheritance + +Deployment projects and deployments items inherit the tags of their parents. For example, if a deployment project +has a [tags]({{< ref "./deployment-yml#tags-deployment-project" >}}) property defined, all `deployments` entries would +inherit all these tags. Also, the sub-deployment projects included via deployment items of type +[include]({{< ref "./deployment-yml#includes" >}}) inherit the tags of the deployment project. These included sub-deployments also +inherit the [tags]({{< ref "./deployment-yml#tags-deployment-item" >}}) specified by the deployment item itself. + +Consider the following example `deployment.yaml`: + +```yaml +deployments: + - include: sub-deployment1 + tags: + - tag1 + - tag2 + - include: sub-deployment2 + tags: + - tag3 + - tag4 + - include: subdir/subsub +``` + +Any kustomize deployment found in `sub-deployment1` would now inherit `tag1` and `tag2`. If `sub-deployment1` performs +any further includes, these would also inherit these two tags. Inheriting is additive and recursive. + +The last sub-deployment project in the example is subject to the same default-tags logic as described +in [Default tags](#default-tags), meaning that it will get the default tag `subsub`. + +## Deploying with tag inclusion/exclusion + +Special care needs to be taken when trying to deploy only a specific part of your deployment which requires some base +resources to be deployed as well. + +Imagine a large deployment is able to deploy 10 applications, but you only want to deploy one of them. When using tags +to achieve this, there might be some base resources (e.g. Namespaces) which are needed no matter if everything or just +this single application is deployed. In that case, you'd need to set [alwaysDeploy]({{< ref "./deployment-yml#deployments" >}}) +to `true`. + +## Deleting with tag inclusion/exclusion + +Also, in most cases, even more special care has to be taken for the same types of resources as decribed before. + +Imagine a kustomize deployment being responsible for namespaces deployments. If you now want to delete everything except +deployments that have the `persistency` tag assigned, the exclusion logic would NOT exclude deletion of the namespace. +This would ultimately lead to everything being deleted, and the exclusion tag having no effect. + +In such a case, you'd need to set [skipDeleteIfTags]({{< ref "./deployment-yml#skipdeleteiftags" >}}) to `true` as well. + +In most cases, setting `alwaysDeploy` to `true` also requires setting `skipDeleteIfTags` to `true`. diff --git a/docs/reference/kluctl-project/_index.md b/docs/reference/kluctl-project/_index.md new file mode 100644 index 000000000..32d6bd66b --- /dev/null +++ b/docs/reference/kluctl-project/_index.md @@ -0,0 +1,60 @@ +--- +title: "Kluctl project (.kluctl.yaml)" +linkTitle: ".kluctl.yaml" +weight: 1 +description: > + Kluctl project configuration, found in the .kluctl.yaml file. +--- + +The `.kluctl.yaml` is the central configuration and entry point for your deployments. It defines where the actual +[deployment project]({{< ref "docs/reference/deployments" >}}) is located, +where [sealed secrets]({{< ref "docs/reference/sealed-secrets" >}}) and unencrypted secrets are localed and which targets are available to +invoke [commands]({{< ref "docs/reference/commands" >}}) on. + +## Example + +An example .kluctl.yaml looks like this: + +```yaml +targets: + # test cluster, dev env + - name: dev + context: test.example.com + args: + environment_name: dev + sealingConfig: + secretSets: + - non-prod + # test cluster, test env + - name: test + context: test.example.com + args: + environment_name: test + sealingConfig: + secretSets: + - non-prod + # prod cluster, prod env + - name: prod + context: prod.example.com + args: + environment_name: prod + sealingConfig: + secretSets: + - prod + +# This is only required if you actually need sealed secrets +secretsConfig: + secretSets: + - name: prod + vars: + # This file should not be part of version control! + - file: .secrets-prod.yaml + - name: non-prod + vars: + # This file should not be part of version control! + - file: .secrets-non-prod.yaml +``` + +## Allowed fields + +Please check the sub-sections of this section to see which fields are allowed at the root level of `.kluctl.yaml`. diff --git a/docs/reference/kluctl-project/secrets-config/_index.md b/docs/reference/kluctl-project/secrets-config/_index.md new file mode 100644 index 000000000..4aa9394f1 --- /dev/null +++ b/docs/reference/kluctl-project/secrets-config/_index.md @@ -0,0 +1,73 @@ +--- +title: "secretsConfig" +linkTitle: "secretsConfig" +weight: 4 +description: > + Optional, defines where to load secrets from. +--- + +This configures how secrets are retrieved while sealing. It is basically a list of named secret sets which can be +referenced from targets. + +It has the following form: +```yaml +... +secretsConfig: + secretSets: + - name: + vars: + - ... + sealedSecrets: ... +... +``` + +## secretSets + +Each `secretSets` entry has the following fields. + +### name +This field specifies the name of the secret set. The name can be used in targets to refer to this secret set. + +### vars +A list of variables sources. Check the documentation of +[variables sources]({{< ref "docs/reference/templating/variable-sources" >}}) for details. + +Each variables source must have a root dictionary with the name `secrets` and all the actual secret values +below that dictionary. Every other root key will be ignored. + +Example variables file: + +```yaml +secrets: + secret: value1 + nested: + secret: value2 + list: + - a + - b +... +``` + +## sealedSecrets +This field specifies the configuration for sealing. It has the following form: + +```yaml +... +secretsConfig: + secretSets: ... + sealedSecrets: + bootstrap: true + namespace: kube-system + controllerName: sealed-secrets-controller +... +``` + +### bootstrap +Controls whether kluctl should bootstrap the initial private key in case the controller is not yet installed on +the target cluster. Defaults to `true`. + +### namespace +Specifies the namespace where the sealed-secrets controller is installed. Defaults to "kube-system". + +### controllerName +Specifies the name of the sealed-secrets controller. Defaults to "sealed-secrets-controller". diff --git a/docs/reference/kluctl-project/targets/_index.md b/docs/reference/kluctl-project/targets/_index.md new file mode 100644 index 000000000..63951d5f0 --- /dev/null +++ b/docs/reference/kluctl-project/targets/_index.md @@ -0,0 +1,103 @@ +--- +title: "targets" +linkTitle: "targets" +weight: 4 +description: > + Required, defines targets for this kluctl project. +--- + +Specifies a list of targets for which commands can be invoked. A target puts together environment/target specific +configuration and the target cluster. Multiple targets can exist which target the same cluster but with differing +configuration (via `args`). Target entries also specifies which secrets to use while [sealing]({{< ref "docs/reference/sealed-secrets" >}}). + +Each value found in the target definition is rendered with a simple Jinja2 context that only contains the target itself. +The rendering process is retried 10 times until it finally succeeds, allowing you to reference +the target itself in complex ways. This is especially useful when using [dynamic targets]({{< ref "./dynamic-targets" >}}). + +Target entries have the following form: +```yaml +targets: +... + - name: + context: + args: + arg1: + arg2: + ... + dynamicArgs: + - name: + ... + images: + - image: my-image + resultImage: my-image:1.2.3 + sealingConfig: + secretSets: + - +... +``` + +The following fields are allowed per target: + +## name +This field specifies the name of the target. The name must be unique. It is referred in all commands via the +[-t]({{< ref "docs/reference/commands/common-arguments" >}}) option. + +## context +This field specifies the kubectl context of the target cluster. The context must exist in the currently active kubeconfig. +If this field is omitted, Kluctl will always use the currently active context. + +## args +This fields specifies a map of arguments to be passed to the deployment project when it is rendered. Allowed argument names +are configured via [deployment args]({{< ref "docs/reference/deployments/deployment-yml#args" >}}). + +The arguments specified in the [dynamic target config]({{< ref "docs/reference/kluctl-project/targets/dynamic-targets#args" >}}) +have higher priority. + +## dynamicArgs +This field specifies a list of CLI arguments that can be passed to kluctl when performing any commands on the target. These +arguments are passed with `-a arg_name=arg_value` when for example calling `kluctl deploy -t target_name`. + +Each entry has the following fields: + +## images +This field specifies a list of fixed images to be used by [`images.get_image(...)`]({{< ref "docs/reference/deployments/images#imagesget_image" >}}). +The format is identical to the [fixed images file](https://kluctl.io/docs/reference/deployments/images/#fixed-images-via-a-yaml-file). + +The fixed images specified in the [dynamic target config]({{< ref "docs/reference/kluctl-project/targets/dynamic-targets#images" >}}) +have higher priority. + +### name +The name of the argument. + +## sealingConfig +This field configures how sealing is performed when the [seal command] ({{< ref "docs/reference/commands/seal" >}}) is invoked for this target. +It has the following form: + +```yaml +targets: +... +- name: + ... + sealingConfig: + args: + arg1: + certFile: + dynamicSealing: + secretSets: + - +``` + +### args +This field allows adding extra arguments to the target args. These are only used while sealing and may override +arguments which are already configured for the target. + +### certFile +Optional path to a local (inside your project) public certificate used for sealing. Such a certificate can be fetched +from the sealed-secrets controller using `kubeseal --fetch-cert`. + +### dynamicSealing +This field specifies weather sealing should happen per [dynamic target]({{< ref "./dynamic-targets" >}}) or only once. This +field is optional and defaults to `true`. + +### secretSets +This field specifies a list of secret set names, which all must exist in the [secretsConfig]({{< ref "../secrets-config" >}}). diff --git a/docs/reference/kluctl-project/targets/dynamic-targets.md b/docs/reference/kluctl-project/targets/dynamic-targets.md new file mode 100644 index 000000000..f15ed7506 --- /dev/null +++ b/docs/reference/kluctl-project/targets/dynamic-targets.md @@ -0,0 +1,110 @@ +--- +title: "Dynamic Targets" +linkTitle: "Dynamic Targets" +weight: 1 +description: > + Dynamically defined targets. +--- + +Targets can also be "dynamic", meaning that additional configuration can be sourced from another git repository. +This can be based on a single target repository and branch, or on a target repository and branch/ref pattern, resulting +in multiple dynamic targets being created from one target definition. + +Please note that a single entry in `target` might end up with multiple dynamic targets, meaning that the name must be +made unique between these dynamic targets. This can be achieved by using templating in the `name` field. As an example, +`{{ target.targetConfig.ref }}` can be used to set the target name to the branch name of the dynamic target. + +Dynamic targets have the following form: +```yaml +targets: +... + - name: + context: + args: ... + arg1: + arg2: + ... + targetConfig: + project: + url: + ref: + refPattern: + file: + sealingConfig: + dynamicSealing: + secretSets: + - +... +``` + +All fields known from normal targets are allowed. In addition, the targetConfig with following fields is available. + +## targetConfig + +The presence of this field causes the target to become a dynamic target. +It specifies where to look for dynamic targets and their addional configuration. It has the following form: + +```yaml +... +targets: +... +- name: + ... + targetConfig: + project: + url: + ref: + refPattern: +... +``` + +### project.url +This field specifies the git clone url of the target configuration project. + +### ref +This field specifies the branch or tag to use. If this field is specified, using `refPattern` is forbidden. +This will result in one single dynamic target. + +### refPattern +This field specifies a regex pattern to use when looking for candidate branches and tags. If this is specified, +using `ref` is forbidden. This will result in multiple dynamic targets. Each dynamic target will have `ref` set to +the actual branch name it belong to. This allows using of `{{ target.targetConfig.ref }}` in all other target fields. + +### file +This field specifies the config file name to read externalized target config from. + +## Format of the target config +The target config file referenced in `targetConfig` must be of the following format: + +```yaml +args: + arg1: value1 + arg2: value2 +images: + - image: registry.gitlab.com/my-group/my-project + resultImage: registry.gitlab.com/my-group/my-project:1.1.0 +``` + +### args +An optional map of arguments, in the same format as in the normal [target args]({{< ref "docs/reference/kluctl-project/targets/#args" >}}). + +The arguments specified here have higher priority. + +### images +An optional list of fixed images, in the same format as in the normal [target images]({{< ref "docs/reference/kluctl-project/targets/#images" >}}) + +## Simple dynamic targets + +A simplified form of dynamic targets is to store target config inside the same directory/project as the `.kluctl.yaml`. +This can be done by omitting `project`, `ref` and `refPattern` from `targetConfig` and only specify `file`. + +## A note on sealing + +When sealing dynamic targets, it is very likely that it is not known yet which dynamic targets will actually exist in +the future. This requires some special care when sealing secrets for these targets. Sealed secrets are usually namespace +scoped, which might need to be changed to cluster-wide scoping so that the same sealed secret can be deployed into +multiple targets (assuming you deploy to different namespaces for each target). When you do this, watch out to not +compromise security, e.g. by sealing production level secrets with a cluster-wide scope! + +It is also very likely required to set `target.sealingConfig.dynamicSealing` to `false`, so that sealing is only performed +once and not for all dynamic targets. \ No newline at end of file diff --git a/docs/reference/sealed-secrets.md b/docs/reference/sealed-secrets.md new file mode 100644 index 000000000..fd9bf6fb3 --- /dev/null +++ b/docs/reference/sealed-secrets.md @@ -0,0 +1,152 @@ +--- +title: "Sealed Secrets" +linkTitle: "Sealed Secrets" +weight: 2 +description: > + Sealed Secrets integration +--- + +kluctl has an integration for [sealed secrets](https://github.com/bitnami-labs/sealed-secrets), allowing you to +securely store secrets for multiple target clusters and/or environments inside version control. + +The integration consists of two parts: +1. Sealing of secrets +1. Automatically choosing and deploying the correct sealed secrets for a target + +## Requirements + +The Sealed Secrets integration relies on the [sealed-secrets operator](https://github.com/bitnami-labs/sealed-secrets) +being installed. Installing the operator is the responsibility of you (or whoever is managing/operating the cluster). + +Kluctl can however perform sealing of secrets without an existing sealed-secrets operator installation. This is solved +by automatically pre-provisioning a key onto the cluster that is compatible with the operator or by providing the +public certificate via `certFile` in the targets [sealingConfig]({{< ref "docs/reference/kluctl-project/targets#certfile" >}}). + +## Sealing of .sealme files + +Sealing is done via the [seal command]({{< ref "docs/reference/commands/seal" >}}). It must be done before the actual +deployment is performed. + +The `seal` command recursively searches for files that end with `.sealme`, renders them with the +[templating engine]({{< ref "docs/reference/templating" >}}) engine. The rendered secret resource is then +converted/encrypted into a sealed secret. + +The `.sealme` files itself have to be [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/), +but without any actual secret data inside. The secret data is referenced via templating variables and is expected to be +provided only at the time of sealing. This means, that the sensitive secret data must only be in clear text while sealing. +Afterwards the sealed secrets can be added to version control. + +Example file (the name could be for example `db-secrets.yaml.sealme`): +```yaml +kind: Secret +apiVersion: v1 +metadata: + name: db-secrets + namespace: {{ my.namespace.variable }} +stringData: + DB_URL: {{ secrets.database.url }} + DB_USERNAME: {{ secrets.database.username }} + DB_PASSWORD: {{ secrets.database.password }} +``` + +While sealing, the full templating context (same as in [templating]({{< ref "docs/reference/templating" >}})) is available. +Additionally, the global `secrets` object/variable is available which contains the sensitive secrets. + +## Secret Sources + +Secrets are only loaded while sealing. Available secret sets and sources are configured via +[.kluctl.yaml]({{< ref "docs/reference/kluctl-project/secrets-config" >}}). The secrets used per target are configured via the +[secrets config]({{< ref "docs/reference/kluctl-project/targets#secretsets" >}}) of the targets. + +## Using sealed secrets + +After sealing a secret, it can be used inside kustomize deployments. While deploying, kluctl will look for resources +included from `kustomization.yaml` which are not existent but for which a file with a `.sealme` extension exists. If such +a file is found, the appropriate sealed secrets is located based on the +[outputPattern](#outputpattern-and-location-of-stored-sealed-secrets). + +An example `kustomization.yaml`: +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +# please note that we do not specify the .sealme suffix here +- db-secrets.yaml +- my-deployments.yaml +``` + +## outputPattern and location of stored sealed secrets + +It is possible to override the output pattern in the root [deployment project]({{< ref "docs/reference/deployments" >}}). +The output pattern must be a template string that is rendered with the full +[templating context]({{< ref "docs/reference/templating" >}}) available for the deployment.yaml. + +When manually specifying the outputPattern, ensure that it works well with multiple clusters and targets. You can +for example use the `{{ target.name }}` and `{{ cluster.name }}` inside the outputPattern. + +```yaml +# deployment.yaml in root directory +sealedSecrets: + outputPattern: "{{ cluster.name }}/{{ target.name }}" +``` + +The default outputPattern is simply `{{ target.name }}`, which should work well in most cases. + +The final storage location for the sealed secret is: + +`///` + +with: +* `base_dir`: The base directory for sealed secrets, which defaults to to the subdirectory `.sealed-secrets` in the kluctl project root + diretory. +* `rendered_output_pattern`: The rendered outputPattern as described above. +* `relative_sealme_file_dir`: The relative path from the deployment root directory. +* `file_name`: The filename of the sealed secret, excluding the `.sealme` extension. + +## Content Hashes and re-sealing +Sealed secrets are stored together with hashes of all individual secret entries. These hashes are then used to avoid +unnecessary re-sealing in future [seal]({{< ref "docs/reference/commands/seal" >}}) invocations. If you want to force re-sealing, use the +[--force-reseal]({{< ref "docs/reference/commands/seal" >}}) option. + +Hashing of secrets is done with bcrypt and the cluster id as salt. The cluster id is currently defined as the sha256 hash +of the cluster CA certificate. This will cause re-sealing of all secrets in case a cluster is set up from scratch +(which causes key from the sealed secrets operator to get wiped as well). + +## Clusters and namespaces +Sealed secrets are usually only decryptable by one cluster, simply because each cluster has its own set of randomly +generated public/private key pairs. This means, that a secret that was sealed for your production cluster can't be +unsealed on your test cluster. + +In addition, sealed secrets can be bound to a single namespace, making them undecryptable for any other namespace. +To limit a sealed secret to a namespace, simply fill the `metadata.namespace` field of the input secret (which is in +the `.sealme` file). This way, the sealed secret can only be deployed to a single namespace. + +You can also use [Scopes](https://github.com/bitnami-labs/sealed-secrets#scopes) to lift/limit restrictions. + +## Using reflectors/replicators +In case a sealed secrets needs to be deployed to more than one namespace, some form of replication must be used. You'd +then seal the secret for a single namespace and use a reflection/replication controller to reflect the unsealed secret +into one or multiple other namespaces. Example controllers that can accomplish this are the +[Mittwald kubernetes-reflector](https://github.com/mittwald/kubernetes-replicator) and the +[Emberstack Kubernetes Reflector](https://github.com/emberstack/kubernetes-reflector). + +Consider the following example (using the Mittwald replicator): +```yaml +kind: Secret +apiVersion: v1 +metadata: + name: db-secrets + namespace: {{ my.namespace.variable }} + annotations: + replicator.v1.mittwald.de/replicate-to: '{{ my.namespace.variable }}-.*' +stringData: + DB_URL: {{ secrets.database.url }} + DB_USERNAME: {{ secrets.database.username }} + DB_PASSWORD: {{ secrets.database.password }} +``` + +The above example would cause automatic replication into every namespace that matches the replicate-to pattern. + +Please watch out for security implications. In the above example, everyone who has the right to create a namespace that +matches the pattern will get access to the secret. diff --git a/docs/reference/templating/_index.md b/docs/reference/templating/_index.md new file mode 100644 index 000000000..ddef4c163 --- /dev/null +++ b/docs/reference/templating/_index.md @@ -0,0 +1,53 @@ +--- +title: "Templating" +linkTitle: "Templating" +weight: 2 +description: > + Templating Engine. +--- + +kluctl uses a Jinja2 Templating engine to pre-process/render every involved configuration file and resource before +actually interpreting it. Only files that are explicitly excluded via [.templateignore files](#templateignore) +are not rendered via Jinja2. + +Generally, everything that is possible with Jinja2 is possible in kluctl configuration/resources. Please +read into the [Jinja2 documentation](https://jinja.palletsprojects.com/en/3.0.x/templates/) to understand what exactly +is possible and how to use it. + +## .templateignore +In some cases it is required to exclude specific files from templating, for example when the contents conflict with +the used template engine (e.g. Go templates conflict with Jinja2 and cause errors). In such cases, you can place +a `.templateignore` beside the excluded files or into a parent folder of it. The contents/format of the `.templateignore` +file is the same as you would use in a `.gitignore` file. + +## Includes and imports +Standard Jinja2 [includes](https://jinja.palletsprojects.com/en/2.11.x/templates/#include) and +[imports](https://jinja.palletsprojects.com/en/2.11.x/templates/#import) can be used in all templates. + +The path given to include/import is searched in the directory of the root template and all it's parent directories up +until the project root. Please note that the search path is not altered in included templates, meaning that it will +always search in the same directories even if an include happens inside a file that was included as well. + +To include/import a file relative to the currently rendered file (which is not necessarily the root template), prefix +the path with `./`, e.g. use `{% include "./my-relative-file.j2" %}"`. + +## Macros + +[Jinja2 macros](https://jinja.palletsprojects.com/en/2.11.x/templates/#macros) are fully supported. When writing +macros that produce yaml resources, you must use the `---` yaml separator in case you want to produce multiple resources +in one go. + +## Why no Go Templating + +kluctl started as a python project and was then migrated to be a Go project. In the python world, Jinja2 is the obvious +choice when it comes to templating. In the Go world, of course Go Templates would be the first choice. + +When the migration to Go was performed, it was a conscious and opinionated decision to stick with Jinja2 templating. +The reason is that I (@codablock) believe that Go Templates are hard to read and write and at the same time quite limited +in their features (without extensive work). It never felt natural to write Go Templates. + +This "feeling" was confirmed by multiple users of kluctl when it started and users described as "relieving" to not +be forced to use Go Templates. + +The above is my personal experience and opinion. I'm still quite open for contributions in regard to Go Templating +support, as long as Jinja2 support is kept. diff --git a/docs/reference/templating/filters.md b/docs/reference/templating/filters.md new file mode 100644 index 000000000..cebf0275c --- /dev/null +++ b/docs/reference/templating/filters.md @@ -0,0 +1,80 @@ +--- +title: "Filters" +linkTitle: "Filters" +weight: 3 +description: > + Available filters. +--- + +In addition to the [builtin Jinja2 filters](https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-builtin-filters), +kluctl provides a few additional filters: + +### b64encode +Encodes the input value as base64. Example: `{{ "test" | b64encode }}` will result in `dGVzdA==`. + +### b64decode +Decodes an input base64 encoded string. Example `{{ my.source.var | b64decode }}`. + +### from_yaml +Parses a yaml string and returns an object. Please note that json is valid yaml, meaning that you can also use this +filter to parse json. + +### to_yaml +Converts a variable/object into its yaml representation. Please note that in most cases the resulting string will not +be properly indented, which will require you to also use the `indent` filter. Example: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config +data: + config.yaml: | + {{ my_config | to_yaml | indent(4) }} +``` + +### to_json +Same as `to_yaml`, but with json as output. Please note that json is always valid yaml, meaning that you can also use +`to_json` in yaml files. Consider the following example: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + containers: + - name: c1 + image: my-image + env: {{ my_list_of_env_entries | to_json }} +``` + +This would render json into a yaml file, which is still a valid yaml file. Compare this to how this would have to be +solved with `to_yaml`: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + containers: + - name: c1 + image: my-image + env: + {{ my_list_of_env_entries | to_yaml | indent(10) }} +``` + +The required indention filter is the part that makes this error-prone and hard to maintain. Consider using `to_json` +whenever you can. + +### render +Renders the input string with the current Jinja2 context. Example: +``` +{% set a="{{ my_var }}" %} +{{ a | render }} +``` \ No newline at end of file diff --git a/docs/reference/templating/functions.md b/docs/reference/templating/functions.md new file mode 100644 index 000000000..e24b4108e --- /dev/null +++ b/docs/reference/templating/functions.md @@ -0,0 +1,65 @@ +--- +title: "Functions" +linkTitle: "Functions" +weight: 4 +description: > + Available functions. +--- + +In addition to the provided +[builtin global functions](https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-global-functions), +kluctl also provides a few global functions: + +### load_template(file) +Loads the given file into memory, renders it with the current Jinja2 context and then returns it as a string. Example: +``` +{% set a=load_template('file.yaml') %} +{{ a }} +``` + +`load_template` uses the same path searching rules as described in [includes/imports]({{< ref "docs/reference/templating#includes-and-imports" >}}). + +### load_sha256(file, digest_len) +Loads the given file into memory, renders it and calculates the sha256 hash of the result. + +The filename given to `load_sha256` is treated the same as in `load_template`. Recursive loading/calculating of hashes +is allowed and is solved by replacing `load_sha256` invocations with currently loaded templates with dummy strings. +This also allows to calculate the hash of the currently rendered template, for example: + +``` +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config-{{ load_sha256("configmap.yaml") }} +data: +``` + +`digest_len` is an optional parameter that allows to limit the length of the returned hex digest. + +### get_var(field_path, default) +Convenience method to navigate through the current context variables via a +[JSON Path](https://goessner.net/articles/JsonPath/). Let's assume you currently have these variables defined +(e.g. via [vars]({{< ref "docs/reference/deployments/deployment-yml#vars-deployment-project" >}})): +```yaml +my: + deep: + var: value +``` +Then `{{ get_var('my.deep.var', 'my-default') }}` would return `value`. +When any of the elements inside the field path are non-existent, the given default value is returned instead. + +The `field_path` parameter can also be a list of pathes, which are then tried one after the another, returning the first +result that gives a value that is not None. For example, `{{ get_var(['non.existing.var', my.deep.var'], 'my-default') }}` +would also return `value`. + +### merge_dict(d1, d2) +Clones d1 and then recursively merges d2 into it and returns the result. Values inside d2 will override values in d1. + +### update_dict(d1, d2) +Same as `merge_dict`, but merging is performed in-place into d1. + +### raise(msg) +Raises a python exception with the given message. This causes the current command to abort. + +### debug_print(msg) +Prints a line to stderr. \ No newline at end of file diff --git a/docs/reference/templating/predefined-variables.md b/docs/reference/templating/predefined-variables.md new file mode 100644 index 000000000..a5aabcee1 --- /dev/null +++ b/docs/reference/templating/predefined-variables.md @@ -0,0 +1,27 @@ +--- +title: "Predefined Variables" +linkTitle: "Predefined Variables" +weight: 1 +description: > + Available predefined variables. +--- + +There are multiple variables available which are pre-defined by kluctl. These are: + +### args +This is a dictionary of arguments given via command line. It contains every argument defined in +[deployment args]({{< ref "docs/reference/deployments/deployment-yml#args" >}}). + +### target +This is the target definition of the currently processed target. It contains all values found in the +[target definition]({{< ref "docs/reference/kluctl-project/targets" >}}), for example `target.name`. + +### images +This global object provides the dynamic images features described in [images]({{< ref "docs/reference/deployments/images" >}}). + +### version +This global object defines latest version filters for `images.get_image(...)`. See [images]({{< ref "docs/reference/deployments/images" >}}) for details. + +### secrets +This global object is only available while [sealing]({{< ref "docs/reference/sealed-secrets" >}}) and contains the loaded +secrets defined via the currently sealed target. diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md new file mode 100644 index 000000000..34ea6bf09 --- /dev/null +++ b/docs/reference/templating/variable-sources.md @@ -0,0 +1,215 @@ +--- +title: "Variable Sources" +linkTitle: "Variable Sources" +weight: 2 +description: > + Available variable sources. +--- + +There are multiple places in deployment projects (deployment.yaml) where additional variables can be loaded into +future Jinja2 contexts. + +The first place where vars can be specified is the deployment root, as documented [here]({{< ref "docs/reference/deployments/deployment-yml#vars-deployment-project" >}}). +These vars are visible for all deployments inside the deployment project, including sub-deployments from includes. + +The second place to specify variables is in the deployment items, as documented [here]({{< ref "docs/reference/deployments/deployment-yml#vars-deployment-item" >}}). + +The variables loaded for each entry in `vars` are not available inside the `deployment.yaml` file itself. +However, each entry in `vars` can use all variables defined before that specific entry is processed. Consider the +following example. + +```yaml +vars: +- file: vars1.yaml +- file: vars2.yaml +``` + +`vars2.yaml` can now use variables that are defined in `vars1.yaml`. At all times, variables defined by +parents of the current sub-deployment project can be used in the current vars file. + +Different types of vars entries are possible: + +### file +This loads variables from a yaml file. Assume the following yaml file with the name `vars1.yaml`: +```yaml +my_vars: + a: 1 + b: "b" + c: + - l1 + - l2 +``` + +This file can be loaded via: + +```yaml +vars: + - file: vars1.yaml +``` + +After which all included deployments and sub-deployments can use the jinja2 variables from `vars1.yaml`. + +### values +An inline definition of variables. Example: + +```yaml +vars: + - values: + a: 1 + b: c +``` + +These variables can then be used in all deployments and sub-deployments. + +### git +This loads variables from a git repository. Example: + +```yaml +vars: + - git: + url: ssh://git@github.com/example/repo.git + ref: my-branch + path: path/to/vars.yaml +``` + +### clusterConfigMap +Loads a configmap from the target's cluster and loads the specified key's value as a yaml file into the jinja2 variables +context. + +Assume the following configmap to be deployed to the target cluster: +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-vars + namespace: my-namespace +data: + vars: | + a: 1 + b: "b" + c: + - l1 + - l2 +``` + +This configmap can be loaded via: + +```yaml +vars: + - clusterConfigMap: + name: my-vars + namespace: my-namespace + key: vars +``` + +It assumes that the configmap is already deployed before the kluctl deployment happens. This might for example be +useful to store meta information about the cluster itself and then make it available to kluctl deployments. + +### clusterSecret +Same as clusterConfigMap, but for secrets. + +### http +The http variables source allows to load variables from an arbitrary HTTP resource by performing a GET (or any other +configured HTTP method) on the URL. Example: + +```yaml +vars: + - http: + url: https://example.com/path/to/my/vars +``` + +The above source will load a variables file from the given URL. The file is expected to be in yaml or json format. + +The following additional properties are supported for http sources: + +##### method +Specifies the HTTP method to be used when requesting the given resource. Defaults to `GET`. + +##### body +The body to send along with the request. If not specified, nothing is sent. + +#### headers +A map of key/values pairs representing the header entries to be added to the request. If not specified, nothing is added. + +##### jsonPath +Can be used to select a nested element from the yaml/json document returned by the HTTP request. This is useful in case +some REST api is used which does not directly return the variables file. Example: + +```yaml +vars: + - http: + url: https://example.com/path/to/my/vars + jsonPath: $[0].data +``` + +The above example would successfully use the following json document as variables source: + +```json +[{"data": {"vars": {"var1": "value1"}}}] +``` + +#### Authentication + +Kluctl currently supports BASIC and NTLM authentication. It will prompt for credentials when needed. + +### awsSecretsManager +[AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) integration. Loads a variables YAML from an AWS Secrets +Manager secret. The secret can either be specified via an ARN or via a secretName and region combination. An AWS +config profile can also be specified (which must exist while sealing). + +The secrets stored in AWS Secrets manager must contain a valid yaml or json file. + +Example using an ARN: +```yaml +vars: + - awsSecretsManager: + secretName: arn:aws:secretsmanager:eu-central-1:12345678:secret:secret-name-XYZ + profile: my-prod-profile +``` + +Example using a secret name and region: +```yaml +vars: + - awsSecretsManager: + secretName: secret-name + region: eu-central-1 + profile: my-prod-profile +``` + +The advantage of the latter is that the auto-generated suffix in the ARN (which might not be known at the time of +writing the configuration) doesn't have to be specified. + +### vault + +[Vault by HashiCorp](https://www.vaultproject.io/) with [Tokens](https://www.vaultproject.io/docs/concepts/tokens) +authentication integration. The address and the path to the secret can be configured. +The implementation was tested with KV Secrets Engine. + +Example using vault: +```yaml +vars: + - vault: + address: http://localhost:8200 + path: secret/data/simple +``` + +Before deploying or sealing please make sure that you have access to vault. You can do this for example by setting +the environment variable `VAULT_TOKEN`. + +### systemEnvVars +Load variables from environment variables. Children of `systemEnvVars` can be arbitrary yaml, e.g. dictionaries or lists. +The leaf values are used to get a value from the system environment. + +Example: +```yaml +vars: +- systemEnvVars: + var1: ENV_VAR_NAME1 + someDict: + var2: ENV_VAR_NAME2 + someList: + - var3: ENV_VAR_NAME3 +``` + +The above example will make 3 variables available: `var1`, `someDict.var2` and +`someList[0].var3`, each having the values of the environment variables specified by the leaf values. From 16aacf901b31e10cc5bc594f6ae93eb47fc11122 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 22:36:16 +0200 Subject: [PATCH 0434/2268] docs: Remove mentions of external projects --- docs/concepts.md | 5 +---- docs/reference/commands/environment-variables.md | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/concepts.md b/docs/concepts.md index c410e32e1..583b8ccbe 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -7,12 +7,9 @@ weight: 10 These are some core concepts in Kluctl. ## Kluctl project -The kluctl project defines targets, secret sources and external git projects. +The kluctl project defines targets and secret sources. It is defined via the [.kluctl.yaml]({{< ref "docs/reference/kluctl-project" >}}) configuration file. -The kluctl project can also optionally define where the deployment project and clusters configs are located (external -git projects). - ## Targets A target defines a target cluster and a set of deployment arguments. Multiple targets can use the same cluster. Targets allow implementing multi-cluster, multi-environment, multi-customer, ... deployments. diff --git a/docs/reference/commands/environment-variables.md b/docs/reference/commands/environment-variables.md index 15b7f003c..be7ade381 100644 --- a/docs/reference/commands/environment-variables.md +++ b/docs/reference/commands/environment-variables.md @@ -11,8 +11,8 @@ In addition to arguments, Kluctl can be controlled via a set of environment vari ## Environment variables as arguments All options/arguments accepted by kluctl can also be specified via environment variables. The name of the environment variables always start with `KLUCTL_` and end with the option/argument in uppercase and dashes replaced with -underscores. As an example, `--project-url=my-project` can also be specified with the environment variable -`KLUCTL_PROJECT_URL=my-project`. +underscores. As an example, `--dry-run` can also be specified with the environment variable +`KLUCTL_DRY_RUN=true`. ## Additional environment variables A few additional environment variables are supported which do not belong to an option/argument. These are: From 89f36d5ebdde0c53a08f38587009f77acd6cc94a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 22:36:48 +0200 Subject: [PATCH 0435/2268] docs: Update documentation about environment variables --- docs/reference/commands/environment-variables.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/reference/commands/environment-variables.md b/docs/reference/commands/environment-variables.md index be7ade381..9bc60def5 100644 --- a/docs/reference/commands/environment-variables.md +++ b/docs/reference/commands/environment-variables.md @@ -18,6 +18,5 @@ underscores. As an example, `--dry-run` can also be specified with the environme A few additional environment variables are supported which do not belong to an option/argument. These are: 1. `KLUCTL_REGISTRY__HOST`, `KLUCTL_REGISTRY__USERNAME`, and so on. See [registries]({{< ref "docs/reference/deployments/images#supported-image-registries-and-authentication" >}}) for details. -2. `KLUCTL_SSH_DISABLE_STRICT_HOST_KEY_CHECKING`. Disable ssh host key checking when accessing git repositories. -3. `KLUCTL_NO_THREADS`. Do not use multithreading while performing work. This is only useful for debugging purposes. -4. `KLUCTL_IGNORE_DEBUGGER`. Pretend that there is no debugger attached when automatically deciding if multi-threading should be enabled or not. +2. `KLUCTL_GIT__HOST`, `KLUCTL_GIT__USERNAME`, and so on. +3. `KLUCTL_SSH_DISABLE_STRICT_HOST_KEY_CHECKING`. Disable ssh host key checking when accessing git repositories. From c7f4e47a8fcb1ca24917e706c286a072779807cf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 22:42:53 +0200 Subject: [PATCH 0436/2268] docs: Document slugify filter --- docs/reference/templating/filters.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/reference/templating/filters.md b/docs/reference/templating/filters.md index cebf0275c..0f556d1f3 100644 --- a/docs/reference/templating/filters.md +++ b/docs/reference/templating/filters.md @@ -77,4 +77,7 @@ Renders the input string with the current Jinja2 context. Example: ``` {% set a="{{ my_var }}" %} {{ a | render }} -``` \ No newline at end of file +``` + +### slugify +Slugify a string based on [python-slugify](https://github.com/un33k/python-slugify). From 89d16e797790a05060002709290aed9b840e4af4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 22:57:24 +0200 Subject: [PATCH 0437/2268] docs: Add documentation for time template functions --- docs/reference/templating/functions.md | 31 +++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/reference/templating/functions.md b/docs/reference/templating/functions.md index e24b4108e..50c474ff9 100644 --- a/docs/reference/templating/functions.md +++ b/docs/reference/templating/functions.md @@ -62,4 +62,33 @@ Same as `merge_dict`, but merging is performed in-place into d1. Raises a python exception with the given message. This causes the current command to abort. ### debug_print(msg) -Prints a line to stderr. \ No newline at end of file +Prints a line to stderr. + +### time.now() +Returns the current time. The returned object has the following members: + +| member | description | +|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| t.as_timezone(tz) | Converts and returns the time `t` in the given timezone. Example:
`{{ time.now().as_timezone("Europe/Berlin") }}` | +| t.weekday() | Returns the time's weekday. 0 means Monday and 6 means Sunday. | +| t.hour() | Returns the time's hour from 0-23. | +| t.minute() | Returns the time's minute from 0-59. | +| t.second() | Returns the time's second from 0-59. | +| t.nanosecond() | Returns the time's nanosecond from 0-999999999. | +| t + delta | Adds a delta to `t`. Example: `{{ time.now() + time.second * 10 }}` | +| t - delta | Subtracts a delta from `t`. Example: `{{ time.now() - time.second * 10 }}` | +| t1 < t2
t1 >= t2
... | Time objects can be compared to other time objects. Example:
`{% if time.now() < time.parse_iso("2022-10-01T10:00") %}...{% endif %}`
All logical operators are supported. | + +### time.utcnow() +Returns the current time in UTC. +The object has the same members as described in [time.now()](#timenow). + +### time.parse_iso(iso_time_str) +Parse the given string and return a time object. The string must be in ISO time. +The object has the same members as described in [time.now()](#timenow). + +### time.second, time.minute, time.hour +Represents a time delta to be used with `t + delta` and `t - delta`. Example +``` +{{ time.now() + time.minute * 10 }} +``` From 638248c9ea1e6db2390b56cab832bc8fc53c951b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 18 Oct 2022 23:11:05 +0200 Subject: [PATCH 0438/2268] docs: Rename all _index.md files to README.md --- docs/{_index.md => README.md} | 0 docs/reference/{_index.md => README.md} | 0 docs/reference/commands/{_index.md => README.md} | 0 docs/reference/deployments/{_index.md => README.md} | 0 docs/reference/deployments/annotations/{_index.md => README.md} | 0 docs/reference/kluctl-project/{_index.md => README.md} | 0 .../kluctl-project/secrets-config/{_index.md => README.md} | 0 docs/reference/kluctl-project/targets/{_index.md => README.md} | 0 docs/reference/templating/{_index.md => README.md} | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename docs/{_index.md => README.md} (100%) rename docs/reference/{_index.md => README.md} (100%) rename docs/reference/commands/{_index.md => README.md} (100%) rename docs/reference/deployments/{_index.md => README.md} (100%) rename docs/reference/deployments/annotations/{_index.md => README.md} (100%) rename docs/reference/kluctl-project/{_index.md => README.md} (100%) rename docs/reference/kluctl-project/secrets-config/{_index.md => README.md} (100%) rename docs/reference/kluctl-project/targets/{_index.md => README.md} (100%) rename docs/reference/templating/{_index.md => README.md} (100%) diff --git a/docs/_index.md b/docs/README.md similarity index 100% rename from docs/_index.md rename to docs/README.md diff --git a/docs/reference/_index.md b/docs/reference/README.md similarity index 100% rename from docs/reference/_index.md rename to docs/reference/README.md diff --git a/docs/reference/commands/_index.md b/docs/reference/commands/README.md similarity index 100% rename from docs/reference/commands/_index.md rename to docs/reference/commands/README.md diff --git a/docs/reference/deployments/_index.md b/docs/reference/deployments/README.md similarity index 100% rename from docs/reference/deployments/_index.md rename to docs/reference/deployments/README.md diff --git a/docs/reference/deployments/annotations/_index.md b/docs/reference/deployments/annotations/README.md similarity index 100% rename from docs/reference/deployments/annotations/_index.md rename to docs/reference/deployments/annotations/README.md diff --git a/docs/reference/kluctl-project/_index.md b/docs/reference/kluctl-project/README.md similarity index 100% rename from docs/reference/kluctl-project/_index.md rename to docs/reference/kluctl-project/README.md diff --git a/docs/reference/kluctl-project/secrets-config/_index.md b/docs/reference/kluctl-project/secrets-config/README.md similarity index 100% rename from docs/reference/kluctl-project/secrets-config/_index.md rename to docs/reference/kluctl-project/secrets-config/README.md diff --git a/docs/reference/kluctl-project/targets/_index.md b/docs/reference/kluctl-project/targets/README.md similarity index 100% rename from docs/reference/kluctl-project/targets/_index.md rename to docs/reference/kluctl-project/targets/README.md diff --git a/docs/reference/templating/_index.md b/docs/reference/templating/README.md similarity index 100% rename from docs/reference/templating/_index.md rename to docs/reference/templating/README.md From d7b9b1c2e4cb54f05185ae501ed8ed8ddbee986a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 19 Oct 2022 09:15:26 +0200 Subject: [PATCH 0439/2268] docs: Document kluctl.io/validate-ignore --- docs/reference/deployments/annotations/validation.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/reference/deployments/annotations/validation.md b/docs/reference/deployments/annotations/validation.md index df528c164..f291a837c 100644 --- a/docs/reference/deployments/annotations/validation.md +++ b/docs/reference/deployments/annotations/validation.md @@ -14,3 +14,6 @@ are added to the validation result, which is then returned by the validate comma The annotation key is dynamic, meaning that all annotations that begin with `validate-result.kluctl.io/` are taken into account. + +### kluctl.io/validate-ignore +If this annotation is set to `true`, the object will be ignored while `kluctl validate` is run. \ No newline at end of file From 91ab5bf74666efa988947aa40c99e8e9d6e4fa3e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 15:04:59 +0200 Subject: [PATCH 0440/2268] docs: Prepare docs to be syncable to www-kluctl.io --- README.md | 24 ++++--- docs/README.md | 62 +++---------------- docs/concepts.md | 5 ++ docs/get-started.md | 28 ++++----- docs/history.md | 5 ++ docs/installation.md | 41 ++++++++++-- docs/philosophy.md | 5 ++ docs/reference/README.md | 9 +++ docs/reference/commands/README.md | 22 +++++++ docs/reference/commands/common-arguments.md | 5 ++ docs/reference/commands/delete.md | 3 + docs/reference/commands/deploy.md | 3 + docs/reference/commands/diff.md | 3 + .../commands/environment-variables.md | 3 + docs/reference/commands/helm-pull.md | 3 + docs/reference/commands/helm-update.md | 3 + docs/reference/commands/list-images.md | 3 + docs/reference/commands/list-targets.md | 3 + docs/reference/commands/poke-images.md | 3 + docs/reference/commands/prune.md | 3 + docs/reference/commands/render.md | 3 + docs/reference/commands/seal.md | 3 + docs/reference/commands/validate.md | 3 + docs/reference/deployments/README.md | 14 +++++ .../deployments/annotations/README.md | 10 +++ .../deployments/annotations/all-resources.md | 5 ++ .../deployments/annotations/hooks.md | 6 ++ .../deployments/annotations/kustomization.md | 5 ++ .../deployments/annotations/validation.md | 5 ++ docs/reference/deployments/deployment-yml.md | 5 ++ docs/reference/deployments/helm.md | 5 ++ docs/reference/deployments/hooks.md | 5 ++ docs/reference/deployments/images.md | 5 ++ docs/reference/deployments/kustomize.md | 5 ++ docs/reference/deployments/readiness.md | 5 ++ docs/reference/deployments/tags.md | 5 ++ docs/reference/kluctl-project/README.md | 10 ++- .../kluctl-project/secrets-config/README.md | 5 ++ .../kluctl-project/targets/README.md | 5 ++ .../kluctl-project/targets/dynamic-targets.md | 5 ++ docs/reference/sealed-secrets.md | 7 ++- docs/reference/templating/README.md | 12 ++++ docs/reference/templating/filters.md | 5 ++ docs/reference/templating/functions.md | 5 ++ .../templating/predefined-variables.md | 5 ++ docs/reference/templating/variable-sources.md | 5 ++ install/README.md | 40 +----------- 47 files changed, 308 insertions(+), 121 deletions(-) diff --git a/README.md b/README.md index c19f8f86f..899b40f2e 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ kluctl - - Kluctl is the missing glue that puts together your (and any third-party) deployments into one large declarative Kubernetes deployment, while making it fully manageable (deploy, diff, prune, delete, ...) via one unified command line interface. @@ -25,17 +23,29 @@ local machine, from your CI/CD pipelines or any automation platform/system that Flux support is in alpha stadium and available via the [flux-kluctl-controller](https://github.com/kluctl/flux-kluctl-controller). -## Installation +## What can I do with Kluctl? + +Kluctl allows you to define a Kluctl project, which in turn defines Kluctl +deployments and sub-deployments. Each Kluctl deployment defines Kustomize deployments. + +A Kluctl project also defines targets, which represent your target environments +and/or clusters. + +The Kluctl CLI then allows to deploy, diff, prune, delete, ... your deployments. -See [installation](./install). +## Where do I start? + +Installation instructions can be found [here](./docs/installation.md). For a getting started guide, continue +[here](./docs/get-started.md). ## Documentation -Documentation can be found here: https://kluctl.io/docs +Documentation, news and blog posts can be found on https://kluctl.io. -## Kluctl in Short +The underlying documentation is synced from this repo (look into ./docs) to the website whenever something is merged +into main. - +## Kluctl in Short | | | | --- | --- | diff --git a/docs/README.md b/docs/README.md index c6a4cdaa4..f9311fca4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,3 +1,5 @@ + -Kluctl is the missing glue that puts together your (and any third-party) deployments into one large declarative -Kubernetes deployment, while making it fully manageable (deploy, diff, prune, delete, ...) via one unified command -line interface. - -Kluctl tries to be as flexible as possible, while remaining as simple as possible. It reuses established -tools (e.g. Kustomize and Helm), making it possible to re-use a large set of available third-party deployments. - -Kluctl is centered around "targets", which can be a cluster or a specific environment (e.g. test, dev, prod, ...) on one -or multiple clusters. Targets can be deployed, diffed, pruned, deleted, and so on. The idea is to have the same set of -operations for every target, no matter how simple or complex the deployment and/or target is. - -Kluctl does not depend on external operators/controllers and allows to use the same deployment wherever you want, -as long as access to the kluctl project and clusters is available. This means, that you can use it from your -local machine, from your CI/CD pipelines or any automation platform/system that allows to call custom tools. - -Flux support is in alpha state and available via the [flux-kluctl-controller](https://github.com/kluctl/flux-kluctl-controller). - -## Kluctl in Short - - - -| | | -| --- | --- | -| 💪 Kluctl handles all your deployments | You can manage all your deployments with Kluctl, including infrastructure related and your applications. | -| 🪶 Complex or simple, all the same | You can manage complex and simple deployments with Kluctl. Simple deployments are lightweight while complex deployment are easily manageable. | -| 🤖 Native git support | Kluctl has native Git support integrated, meaning that it can easily deploy remote Kluctl projects or externalize parts (e.g. configuration) of your Kluctl project. | -| 🪐 Multiple environments | Deploy the same deployment to multiple environments (dev, test, prod, ...), with flexible differences in configuration. | -| 🌌 Multiple clusters | Manage multiple target clusters (in multiple clouds or bare-metal if you want). | -| 🔩 Configuration and Templating | Kluctl allows to use templating in nearly all places, making it easy to have dynamic configuration. | -| ⎈ Helm and Kustomize | The Helm and Kustomize integrations allow you to reuse plenty of third-party charts and kustomizations. | -| 🔍 See what's different | Always know what the state of your deployments is by being able to run diffs on the whole deployment. | -| 🔎 See what happened | Always know what you actually changed after performing a deployment. | -| 💥 Know what went wrong | Kluctl will show you what part of your deployment failed and why. | -| 👐 Live and let live | Kluctl tries to not interfere with any other tools or operators. This is possible due to it's use of server-side-apply. | -| 🧹 Keep it clean | Keep your clusters clean by issuing regular prune calls. | -| 🔐 Encrypted Secrets | Manage encrypted secrets for multiple target environments and clusters. | - -## What can I do with Kluctl? - -Kluctl allows you to define a Kluctl project, which in turn defines Kluctl -deployments and sub-deployments. Each Kluctl deployment defines Kustomize deployments. - -A Kluctl project also defines targets, which represent your target environments -and/or clusters. - -The Kluctl CLI then allows to deploy, diff, prune, delete, ... your deployments. - -## Where do I start? +# Table of Contents -{{% alert title="Get started with Kluctl!" %}} -Following this [guide]({{< ref "docs/get-started" >}}) will just take a couple of minutes to complete: -After installing `kluctl`, you can either check out the [example]({{< ref "docs/guides/examples" >}}) or [tutorials]({{< ref "docs/guides/tutorials" >}}). -{{% /alert %}} +1. [Get Started](./get-started.md) +2. [Core Concepts](./concepts.md) +3. [Installation](./installation) +4. [Philosophy](./philosophy.md) +5. [History](./history.md) +6. [Reference](./reference) + +# Core Concepts These are some core concepts in Kluctl. diff --git a/docs/get-started.md b/docs/get-started.md index d8868313b..711235908 100644 --- a/docs/get-started.md +++ b/docs/get-started.md @@ -1,11 +1,16 @@ + + +# Get Started -This tutorial shows you how to bootstrap Flux to a Kubernetes cluster and deploy a sample application in a GitOps manner. +This tutorial shows you how to start using kluctl. ## Before you begin @@ -15,7 +20,7 @@ A few things must be prepared before you actually begin. The first step is of course: You need a kubernetes cluster. It doesn't really matter where this cluster is hosted, if it's a local (e.g. [kind](https://kind.sigs.k8s.io/docs/user/quick-start/)) cluster, managed cluster, or a self-hosted -cluster, kops or kubespray based, AWS, GCE, Azure, ... and so on. kluctl +cluster, kops or kubespray based, AWS, GCE, Azure, ... and so on. Kluctl is completely independent of how Kubernetes is deployed and where it is hosted. There is however a minimum Kubernetes version that must be met: 1.20.0. This is due to the heavy use of server-side apply @@ -29,7 +34,7 @@ then you'd have to ensure that the kubeconfig context `test.example.com` is corr cluster. See [Configure Access to Multiple Clusters](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/) for documentation -on how to manage multiple clusters with a single kubeconfig. Depending on the Kubernets provisioning/deployment tooling +on how to manage multiple clusters with a single kubeconfig. Depending on the Kubernetes provisioning/deployment tooling you used, you might also be able to directly export the context into your local kubeconfig. For example, [kops](https://github.com/kubernetes/kops/blob/master/docs/cli/kops_export.md) is able to export and merge the kubeconfig for a given cluster. @@ -42,15 +47,8 @@ for a given cluster. ## Install Kluctl -The `kluctl` command-line interface (CLI) is required to perform deployments. - -To install the CLI with Homebrew run: - -```sh -brew install kluctl/tap/kluctl -``` - -For other installation methods, see the [install documentation]({{< ref "docs/installation" >}}). +The `kluctl` command-line interface (CLI) is required to perform deployments. Read the [installation instructions](./installation.md) +to figure out how to install it. ## Clone the kluctl examples @@ -62,7 +60,7 @@ git clone https://github.com/kluctl/kluctl-examples.git ## Choose one of the examples -You can choose whatever example you like from the clones repository. We will however continue this guide by referring +You can choose whatever example you like from the cloned repository. We will however continue this guide by referring to the `simple-helm` example found in that repository. Change the current directory: ```sh @@ -115,5 +113,5 @@ You should need 2 instances of the nginx POD running now. ## Where to continue? -Continue by reading through the [tutorials]({{< ref "docs/guides/tutorials" >}}) and by consulting -the [reference documentation]({{< ref "reference" >}}). +Continue by reading through the [tutorials](https://kluctl.io/docs/guides/tutorials/) and by consulting +the [reference documentation](./reference). diff --git a/docs/history.md b/docs/history.md index a1baceb2a..f2f6fad8a 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,9 +1,14 @@ + + +# History Kluctl was created after multiple incarnations of complex multi-environment (e.g. dev, test, prod) deployments, including everything from monitoring, persistency and the actual custom services. The philosophy of these deployments was always diff --git a/docs/installation.md b/docs/installation.md index 94ae4641a..3bf922ec7 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,18 +1,22 @@ + -## Install kluctl +# Installation + +## Binaries The kluctl CLI is available as a binary executable for all major platforms, the binaries can be downloaded form GitHub [releases page](https://github.com/kluctl/kluctl/releases). -{{% tabs %}} -{{% tab "Homebrew" %}} +## Installation with Homebrew With [Homebrew](https://brew.sh) for macOS and Linux: @@ -20,8 +24,7 @@ With [Homebrew](https://brew.sh) for macOS and Linux: brew install kluctl/tap/kluctl ``` -{{% /tab %}} -{{% tab "bash" %}} +## Installation with Bash With [Bash](https://www.gnu.org/software/bash/) for macOS and Linux: @@ -29,7 +32,33 @@ With [Bash](https://www.gnu.org/software/bash/) for macOS and Linux: curl -s https://kluctl.io/install.sh | bash ``` -{{% /tab %}} +The install script does the following: +* attempts to detect your OS +* downloads and unpacks the release tar file in a temporary directory +* copies the kluctl binary to `/usr/local/bin` +* removes the temporary directory + +## Build from source + +Clone the repository: + +```bash +git clone https://github.com/kluctl/kluctl +cd kluctl +``` + +Build the `kluctl` binary (requires go >= 1.19): + +```bash +make build +``` + +Run the binary: + +```bash +./bin/kluctl -h +``` + + +# Philosophy Kluctl tries to follow a few basic ideas and a philosophy. Project and deployments structure, as well as all commands are centered on these. diff --git a/docs/reference/README.md b/docs/reference/README.md index 06d1ad3b4..98ee9a446 100644 --- a/docs/reference/README.md +++ b/docs/reference/README.md @@ -1,3 +1,5 @@ + + +# Table of Contents +1. [.kluctl.yaml](./kluctl-project) +2. [Deployments](./deployments) +3. [Sealed Secrets](./sealed-secrets.md) +4. [Kluctl Commands](./commands) diff --git a/docs/reference/commands/README.md b/docs/reference/commands/README.md index 438c870aa..1fe1a1923 100644 --- a/docs/reference/commands/README.md +++ b/docs/reference/commands/README.md @@ -1,3 +1,5 @@ + + +# Commands kluctl offers a unified command line interface that allows to standardize all your deployments. Every project, no matter how different it is from other projects, is managed the same way. @@ -12,3 +17,20 @@ no matter how different it is from other projects, is managed the same way. You can always call `kluctl --help` or `kluctl --help` for a help prompt. Individual commands are documented in sub-sections. + +## Table of Contents + +1. [Common Arguments](./common-arguments.md) +2. [Environment Variables](./environment-variables.md) +3. [delete](./delete.md) +4. [deploy](./deploy.md) +5. [diff](./diff.md) +6. [helm-pull](./helm-pull.md) +7. [helm-update](./helm-update.md) +8. [list-images](./list-images.md) +9. [list-targets](./list-targets.md) +10. [poke-images](./poke-images.md) +11. [prune](./prune.md) +12. [render](./render.md) +13. [seal](./seal.md) +14. [validate](./validate.md) diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index 429d78235..9cfe45558 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -1,3 +1,5 @@ + + +# Common Arguments A few sets of arguments are common between multiple commands. These arguments are still part of the command itself and must be placed *after* the command name. diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index d2753f4b4..70483166f 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/deploy.md b/docs/reference/commands/deploy.md index 2114ac54c..7ecfc1b24 100644 --- a/docs/reference/commands/deploy.md +++ b/docs/reference/commands/deploy.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/diff.md b/docs/reference/commands/diff.md index 3af235dd6..c9d4546f4 100644 --- a/docs/reference/commands/diff.md +++ b/docs/reference/commands/diff.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/environment-variables.md b/docs/reference/commands/environment-variables.md index 9bc60def5..6db995487 100644 --- a/docs/reference/commands/environment-variables.md +++ b/docs/reference/commands/environment-variables.md @@ -1,3 +1,5 @@ + In addition to arguments, Kluctl can be controlled via a set of environment variables. diff --git a/docs/reference/commands/helm-pull.md b/docs/reference/commands/helm-pull.md index 576324be4..80fb685a3 100644 --- a/docs/reference/commands/helm-pull.md +++ b/docs/reference/commands/helm-pull.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/helm-update.md b/docs/reference/commands/helm-update.md index a9e71f641..e1a526ae8 100644 --- a/docs/reference/commands/helm-update.md +++ b/docs/reference/commands/helm-update.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/list-images.md b/docs/reference/commands/list-images.md index cf56b6af1..ce0f2aaba 100644 --- a/docs/reference/commands/list-images.md +++ b/docs/reference/commands/list-images.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/list-targets.md b/docs/reference/commands/list-targets.md index 3ef135116..f99d5b2e0 100644 --- a/docs/reference/commands/list-targets.md +++ b/docs/reference/commands/list-targets.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/poke-images.md b/docs/reference/commands/poke-images.md index 2a0aa64f2..585ae6d47 100644 --- a/docs/reference/commands/poke-images.md +++ b/docs/reference/commands/poke-images.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/prune.md b/docs/reference/commands/prune.md index f019404ac..a895db0e8 100644 --- a/docs/reference/commands/prune.md +++ b/docs/reference/commands/prune.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/render.md b/docs/reference/commands/render.md index f4869433b..c2b626415 100644 --- a/docs/reference/commands/render.md +++ b/docs/reference/commands/render.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/seal.md b/docs/reference/commands/seal.md index e87b720eb..ee99cba1c 100644 --- a/docs/reference/commands/seal.md +++ b/docs/reference/commands/seal.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/commands/validate.md b/docs/reference/commands/validate.md index d27b9f59d..5ff5a0a6e 100644 --- a/docs/reference/commands/validate.md +++ b/docs/reference/commands/validate.md @@ -1,3 +1,5 @@ + ## Command diff --git a/docs/reference/deployments/README.md b/docs/reference/deployments/README.md index de97255a3..08c0d07d9 100644 --- a/docs/reference/deployments/README.md +++ b/docs/reference/deployments/README.md @@ -1,3 +1,5 @@ + + +# Table of Contents + +1. [deployment.yaml](./deployment-yml.md) +2. [Kustomize Integration](./kustomize.md) +3. [Container Images](./images.md) +4. [Helm Integration](./helm.md) +5. [Hooks](./hooks.md) +6. [Readiness](./readiness.md) +7. [Tags](./tags.md) +8. [Annotations](./annotations) A deployment project is collection of deployment items and sub-deployments. Deployment items are usually [Kustomize]({{< ref "./kustomize" >}}) deployments, but can also integrate [Helm Charts]({{< ref "./helm" >}}). diff --git a/docs/reference/deployments/annotations/README.md b/docs/reference/deployments/annotations/README.md index a4187a3e9..398001649 100644 --- a/docs/reference/deployments/annotations/README.md +++ b/docs/reference/deployments/annotations/README.md @@ -1,3 +1,5 @@ + + +# Table of Contents + +1. [All Resources](./all-resources.md) +2. [Hooks](./hooks.md) +3. [Validation](./validation.md) +4. [Kustomize](./kustomization.md) diff --git a/docs/reference/deployments/annotations/all-resources.md b/docs/reference/deployments/annotations/all-resources.md index c77c2604f..5f2ee3327 100644 --- a/docs/reference/deployments/annotations/all-resources.md +++ b/docs/reference/deployments/annotations/all-resources.md @@ -1,3 +1,5 @@ + + +# All resources The following annotations control the behavior of the `deploy` and related commands. diff --git a/docs/reference/deployments/annotations/hooks.md b/docs/reference/deployments/annotations/hooks.md index c937cc95e..ca946554e 100644 --- a/docs/reference/deployments/annotations/hooks.md +++ b/docs/reference/deployments/annotations/hooks.md @@ -1,3 +1,5 @@ + + +# Hooks + The following annotations control hook execution See [hooks]({{< ref "docs/reference/deployments/hooks" >}}) for more details. diff --git a/docs/reference/deployments/annotations/kustomization.md b/docs/reference/deployments/annotations/kustomization.md index 8c2f98e0d..dfdcbf661 100644 --- a/docs/reference/deployments/annotations/kustomization.md +++ b/docs/reference/deployments/annotations/kustomization.md @@ -1,3 +1,5 @@ + + +# Kustomize Even though the `kustomization.yaml` from Kustomize deployments are not really Kubernetes resources (as they are not really deployed), they have the same structure as Kubernetes resources. This also means that the `kustomization.yaml` diff --git a/docs/reference/deployments/annotations/validation.md b/docs/reference/deployments/annotations/validation.md index f291a837c..d4720d2e4 100644 --- a/docs/reference/deployments/annotations/validation.md +++ b/docs/reference/deployments/annotations/validation.md @@ -1,3 +1,5 @@ + + +# Validation The following annotations influence the [validate]({{< ref "docs/reference/commands/validate" >}}) command. diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index bb22bac01..ab36ffc00 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -1,3 +1,5 @@ + + +# Deployments The `deployment.yaml` file is the entrypoint for the deployment project. Included sub-deployments also provide a `deployment.yaml` file with the same structure as the initial one. diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index 0bc1733d9..2d8dab7b4 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -1,3 +1,5 @@ + + +# Helm Integration kluctl offers a simple-to-use Helm integration, which allows you to reuse many common third-party Helm Charts. diff --git a/docs/reference/deployments/hooks.md b/docs/reference/deployments/hooks.md index db83a5a5b..9764f0051 100644 --- a/docs/reference/deployments/hooks.md +++ b/docs/reference/deployments/hooks.md @@ -1,3 +1,5 @@ + + +# Hooks Kluctl supports hooks in a similar fashion as known from Helm Charts. Hooks are executed/deployed before and/or after the actual deployment of a kustomize deployment. diff --git a/docs/reference/deployments/images.md b/docs/reference/deployments/images.md index 3c419381c..6f2a7e3cb 100644 --- a/docs/reference/deployments/images.md +++ b/docs/reference/deployments/images.md @@ -1,3 +1,5 @@ + + +# Container Images There are usually 2 different scenarios where Container Images need to be specified: 1. When deploying third party applications like nginx, redis, ... (e.g. via the [Helm integration]({{< ref "./helm" >}})).
diff --git a/docs/reference/deployments/kustomize.md b/docs/reference/deployments/kustomize.md index d2099397c..bcf694096 100644 --- a/docs/reference/deployments/kustomize.md +++ b/docs/reference/deployments/kustomize.md @@ -1,3 +1,5 @@ + + +# Kustomize Integration kluctl uses [kustomize](https://kustomize.io/) to render final resources. This means, that the finest/lowest level in kluctl is represented with kustomize deployments. These kustomize deployments can then perform further diff --git a/docs/reference/deployments/readiness.md b/docs/reference/deployments/readiness.md index 4712fc172..d571f969f 100644 --- a/docs/reference/deployments/readiness.md +++ b/docs/reference/deployments/readiness.md @@ -1,3 +1,5 @@ + + +# Readiness There are multiple places where kluctl can wait for "readiness" of resources, e.g. for hooks or when `waitReadiness` is specified on a deployment item. Readiness depends on the resource kind, e.g. for a Job, kluctl would wait until it diff --git a/docs/reference/deployments/tags.md b/docs/reference/deployments/tags.md index bf2a00d86..9501d0845 100644 --- a/docs/reference/deployments/tags.md +++ b/docs/reference/deployments/tags.md @@ -1,8 +1,13 @@ + + +# Tags Every kustomize deployment has a set of tags assigned to it. These tags are defined in multiple places, which is documented in [deployment.yaml]({{< ref "./deployment-yml" >}}). Look for the `tags` field, which is available in multiple places per diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index 32d6bd66b..eedd06aaa 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -1,3 +1,5 @@ + + +# Kluctl project The `.kluctl.yaml` is the central configuration and entry point for your deployments. It defines where the actual [deployment project]({{< ref "docs/reference/deployments" >}}) is located, @@ -57,4 +62,7 @@ secretsConfig: ## Allowed fields -Please check the sub-sections of this section to see which fields are allowed at the root level of `.kluctl.yaml`. +Please check the following sub-sections of this section to see which fields are allowed at the root level of `.kluctl.yaml`. + +1. [targets](./targets) +2. [secretsConfig](./secrets-config) diff --git a/docs/reference/kluctl-project/secrets-config/README.md b/docs/reference/kluctl-project/secrets-config/README.md index 4aa9394f1..41663bdb2 100644 --- a/docs/reference/kluctl-project/secrets-config/README.md +++ b/docs/reference/kluctl-project/secrets-config/README.md @@ -1,3 +1,5 @@ + + +# secretsConfig This configures how secrets are retrieved while sealing. It is basically a list of named secret sets which can be referenced from targets. diff --git a/docs/reference/kluctl-project/targets/README.md b/docs/reference/kluctl-project/targets/README.md index 63951d5f0..9349cc476 100644 --- a/docs/reference/kluctl-project/targets/README.md +++ b/docs/reference/kluctl-project/targets/README.md @@ -1,3 +1,5 @@ + + +# targets Specifies a list of targets for which commands can be invoked. A target puts together environment/target specific configuration and the target cluster. Multiple targets can exist which target the same cluster but with differing diff --git a/docs/reference/kluctl-project/targets/dynamic-targets.md b/docs/reference/kluctl-project/targets/dynamic-targets.md index f15ed7506..158262cdc 100644 --- a/docs/reference/kluctl-project/targets/dynamic-targets.md +++ b/docs/reference/kluctl-project/targets/dynamic-targets.md @@ -1,3 +1,5 @@ + + +# Dynamic Targets Targets can also be "dynamic", meaning that additional configuration can be sourced from another git repository. This can be based on a single target repository and branch, or on a target repository and branch/ref pattern, resulting diff --git a/docs/reference/sealed-secrets.md b/docs/reference/sealed-secrets.md index fd9bf6fb3..8c7ebca61 100644 --- a/docs/reference/sealed-secrets.md +++ b/docs/reference/sealed-secrets.md @@ -1,3 +1,5 @@ + + +# Sealed Secrets kluctl has an integration for [sealed secrets](https://github.com/bitnami-labs/sealed-secrets), allowing you to securely store secrets for multiple target clusters and/or environments inside version control. The integration consists of two parts: 1. Sealing of secrets -1. Automatically choosing and deploying the correct sealed secrets for a target +2. Automatically choosing and deploying the correct sealed secrets for a target ## Requirements diff --git a/docs/reference/templating/README.md b/docs/reference/templating/README.md index ddef4c163..fc2a26c3b 100644 --- a/docs/reference/templating/README.md +++ b/docs/reference/templating/README.md @@ -1,3 +1,5 @@ + + +# Table of Contents + +1. [Predefined Variables](./predefined-variables.md) +2. [Variable Sources](./variable-sources.md) +3. [Filters](./filters.md) +4. [Functions](./functions.md) + +# Templating kluctl uses a Jinja2 Templating engine to pre-process/render every involved configuration file and resource before actually interpreting it. Only files that are explicitly excluded via [.templateignore files](#templateignore) diff --git a/docs/reference/templating/filters.md b/docs/reference/templating/filters.md index 0f556d1f3..6495f57cd 100644 --- a/docs/reference/templating/filters.md +++ b/docs/reference/templating/filters.md @@ -1,3 +1,5 @@ + + +# Filters In addition to the [builtin Jinja2 filters](https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-builtin-filters), kluctl provides a few additional filters: diff --git a/docs/reference/templating/functions.md b/docs/reference/templating/functions.md index 50c474ff9..ad4894474 100644 --- a/docs/reference/templating/functions.md +++ b/docs/reference/templating/functions.md @@ -1,3 +1,5 @@ + + +# Functions In addition to the provided [builtin global functions](https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-global-functions), diff --git a/docs/reference/templating/predefined-variables.md b/docs/reference/templating/predefined-variables.md index a5aabcee1..75c962937 100644 --- a/docs/reference/templating/predefined-variables.md +++ b/docs/reference/templating/predefined-variables.md @@ -1,3 +1,5 @@ + + +# Predefined Variables There are multiple variables available which are pre-defined by kluctl. These are: diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index 34ea6bf09..368e15eb6 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -1,3 +1,5 @@ + + +# Variable Sources There are multiple places in deployment projects (deployment.yaml) where additional variables can be loaded into future Jinja2 contexts. diff --git a/install/README.md b/install/README.md index a02fc9390..12aca8ecb 100644 --- a/install/README.md +++ b/install/README.md @@ -1,41 +1,3 @@ # kluctl Installation -Binaries for macOS and Linux AMD64 are available for download on the -[release page](https://github.com/kluctl/kluctl/releases). - -To install the latest release run: - -```bash -curl -s https://raw.githubusercontent.com/kluctl/kluctl/main/install/kluctl.sh | bash -``` - -The install script does the following: -* attempts to detect your OS -* downloads and unpacks the release tar file in a temporary directory -* copies the kluctl binary to `/usr/local/bin` -* removes the temporary directory - -## Alternative installation methods - -See https://kluctl.io/docs/installation for alternative installation methods. - -## Build from source - -Clone the repository: - -```bash -git clone https://github.com/kluctl/kluctl -cd kluctl -``` - -Build the `kluctl` binary (requires go >= 1.18 and python >= 3.10): - -```bash -make build -``` - -Run the binary: - -```bash -./bin/kluctl -h -``` +Read [installation](../docs/installation.md) for instructions. From ef2c8979a06cea33cc039131f9b9cd9d89274fd1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 15:49:18 +0200 Subject: [PATCH 0441/2268] ci: Use make in CI build --- .github/workflows/tests.yml | 48 +++++++++++++------------------------ Makefile | 17 ++++++++----- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 012806f23..6caae2381 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,42 +30,28 @@ jobs: go test ./cmd/... ./pkg/... -v - name: Build kluctl (linux) run: | - export CGO_ENABLED=0 - export GOARCH=amd64 - export GOOS=linux - go build - go test -c ./e2e - mv kluctl kluctl-linux-amd64 - mv e2e.test e2e.test-linux-amd64 + make build GOARCH=amd64 GOOS=linux + make test-e2e-build GOARCH=amd64 GOOS=linux + mv ./bin/kluctl ./bin/kluctl-linux-amd64 + mv ./bin/kluctl-e2e ./bin/kluctl-e2e-linux-amd64 - name: Build kluctl (darwin) run: | - export CGO_ENABLED=0 - export GOARCH=amd64 - export GOOS=darwin - go build - go test -c ./e2e - mv kluctl kluctl-darwin-amd64 - mv e2e.test e2e.test-darwin-amd64 + make build GOARCH=amd64 GOOS=darwin + make test-e2e-build GOARCH=amd64 GOOS=darwin + mv ./bin/kluctl ./bin/kluctl-darwin-amd64 + mv ./bin/kluctl-e2e ./bin/kluctl-e2e-darwin-amd64 - name: Build kluctl (windows) run: | - export CGO_ENABLED=0 - export GOARCH=amd64 - export GOOS=windows - go build - go test -c ./e2e - mv kluctl.exe kluctl-windows-amd64.exe - mv e2e.test.exe e2e.test-windows-amd64.exe + make build GOARCH=amd64 GOOS=windows + make test-e2e-build GOARCH=amd64 GOOS=windows + mv ./bin/kluctl.exe ./bin/kluctl-windows-amd64.exe + mv ./bin/kluctl-e2e.exe ./bin/kluctl-e2e-windows-amd64.exe - name: Upload binaries uses: actions/upload-artifact@v2 with: - name: binaries + name: bin path: | - kluctl-linux-amd64 - kluctl-darwin-amd64 - kluctl-windows-amd64.exe - e2e.test-linux-amd64 - e2e.test-darwin-amd64 - e2e.test-windows-amd64.exe + ./bin tests: strategy: @@ -110,8 +96,8 @@ jobs: EXE=.exe fi - chmod +x ./binaries/* - mv ./binaries/e2e.test-${{ matrix.binary-suffix }}$EXE ./e2e.test$EXE + chmod +x ./bin/* + mv ./bin/kluctl-${{ matrix.binary-suffix }}$EXE ./kluctl$EXE + mv ./bin/kluctl-e2e-${{ matrix.binary-suffix }}$EXE ./kluctl-e2e$EXE - export KLUCTL_EXE=./binaries/kluctl-${{ matrix.binary-suffix }}$EXE make test-e2e-pre-built diff --git a/Makefile b/Makefile index d8a176b7f..b20f9f51e 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,14 @@ # Based on the work of Thomas Poignant (thomaspoignant) # https://gist.github.com/thomaspoignant/5b72d579bd5f311904d973652180c705 +EXE= +ifeq ($(GOOS), windows) +EXE=.exe +endif GOCMD=go GOTEST=$(GOCMD) test GOVET=$(GOCMD) vet -BINARY_NAME=kluctl -TEST_BINARY_NAME=kluctl-e2e -REQUIRED_ENV_VARS=GOOS GOARCH +BINARY_NAME=kluctl$(EXE) +TEST_BINARY_NAME=kluctl-e2e$(EXE) EXPORT_RESULT?=false # If gobin not set, create one on ./build and add to path. @@ -72,11 +75,13 @@ endif test: test-unit test-e2e ## Runs the complete test suite KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_KUBERNETES_VERSION) --bin-dir=$(ENVTEST_ASSETS_DIR) -p path)" -test-e2e: install-envtest ## Runs the end to end tests - KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -o ./bin/$(TEST_BINARY_NAME) ./e2e -test.v +test-e2e: test-e2e-build test-e2e-pre-built ## Runs the end to end tests + +test-e2e-build: ## Builds the end to end tests + CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -c ./e2e -o ./bin/$(TEST_BINARY_NAME) test-e2e-pre-built: install-envtest - KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) ./e2e.test -test.v + KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) ./bin/$(TEST_BINARY_NAME) -test.v test-unit: ## Run the unit tests of the project ifeq ($(EXPORT_RESULT), true) From 3e4e5ff475c03c757c76dd30cc6b25b4d58deae2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 15:50:36 +0200 Subject: [PATCH 0442/2268] chore: Add replace-commands-help script and enforce up-to-date commands help --- .github/workflows/tests.yml | 9 ++ Makefile | 2 + internal/replace-commands-help/main.go | 172 +++++++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 internal/replace-commands-help/main.go diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6caae2381..1b3f73653 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,6 +46,15 @@ jobs: make test-e2e-build GOARCH=amd64 GOOS=windows mv ./bin/kluctl.exe ./bin/kluctl-windows-amd64.exe mv ./bin/kluctl-e2e.exe ./bin/kluctl-e2e-windows-amd64.exe + - name: Verify commands help is up-to-date + run: | + make replace-commands-help + if [ ! -z "$(git status --porcelain)" ]; then + echo "replace-commands-help must be invoked and the result committed" + git status + git diff + exit 1 + fi - name: Upload binaries uses: actions/upload-artifact@v2 with: diff --git a/Makefile b/Makefile index b20f9f51e..6c4304f17 100644 --- a/Makefile +++ b/Makefile @@ -113,6 +113,8 @@ else endif docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:latest-alpine golangci-lint run $(OUTPUT_OPTIONS) +replace-commands-help: ## Replace commands help in docs + $(GOCMD) run ./internal/replace-commands-help --docs-dir ./docs/reference/commands ## Release: version: ## Write next version into version file diff --git a/internal/replace-commands-help/main.go b/internal/replace-commands-help/main.go new file mode 100644 index 000000000..6d4e781fb --- /dev/null +++ b/internal/replace-commands-help/main.go @@ -0,0 +1,172 @@ +package main + +import ( + "flag" + "fmt" + "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" + "io/fs" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "reflect" + "regexp" + "strings" + "syscall" +) + +var docsDir = flag.String("docs-dir", "", "Path to documentation") + +type section struct { + start int + end int + command string + section string + code bool +} + +func main() { + _ = syscall.Setenv("COLUMNS", "120") + + if os.Getenv("CALL_KLUCTL") == "true" { + kluctlMain() + return + } + + flag.Parse() + + filepath.WalkDir(*docsDir, func(path string, d fs.DirEntry, err error) error { + if !strings.HasSuffix(path, ".md") { + return nil + } + processFile(path) + return nil + }) +} + +func kluctlMain() { + commands.Execute() +} + +func processFile(path string) { + text, err := ioutil.ReadFile(path) + if err != nil { + log.Fatal(err) + } + lines := strings.Split(string(text), "\n") + + var newLines []string + pos := 0 + for true { + s := findNextSection(lines, pos) + if s == nil { + newLines = append(newLines, lines[pos:]...) + break + } + + newLines = append(newLines, lines[pos:s.start+1]...) + + s2 := getHelpSection(s.command, s.section) + + if s.code { + newLines = append(newLines, "```") + } + newLines = append(newLines, s2...) + if s.code { + newLines = append(newLines, "```") + } + + newLines = append(newLines, lines[s.end]) + pos = s.end + 1 + } + + if !reflect.DeepEqual(lines, newLines) { + err = ioutil.WriteFile(path, []byte(strings.Join(newLines, "\n")), 0o600) + if err != nil { + log.Fatal(err) + } + } +} + +var beginPattern = regexp.MustCompile(``) +var endPattern = regexp.MustCompile(``) + +func findNextSection(lines []string, start int) *section { + + for i := start; i < len(lines); i++ { + m := beginPattern.FindSubmatch([]byte(lines[i])) + if m == nil { + continue + } + + var s section + s.start = i + s.command = string(m[1]) + s.section = string(m[2]) + s.code = string(m[3]) == "true" + + for j := i + 1; j < len(lines); j++ { + m = endPattern.FindSubmatch([]byte(lines[j])) + if m == nil { + continue + } + s.end = j + return &s + } + } + return nil +} + +func countIndent(str string) int { + for i := 0; i < len(str); i++ { + if str[i] != ' ' { + return i + } + } + return 0 +} + +func getHelpSection(command string, section string) []string { + log.Printf("Getting section '%s' from command '%s'", section, command) + + exe, err := os.Executable() + if err != nil { + log.Fatal(err) + } + + helpCmd := exec.Command(exe, command, "--help") + helpCmd.Env = os.Environ() + helpCmd.Env = append(helpCmd.Env, "CALL_KLUCTL=true") + + out, err := helpCmd.CombinedOutput() + if err != nil { + log.Fatal(err) + } + + lines := strings.Split(string(out), "\n") + + sectionStart := -1 + for i := 0; i < len(lines); i++ { + indent := countIndent(lines[i]) + if strings.HasPrefix(lines[i][indent:], fmt.Sprintf("%s:", section)) { + sectionStart = i + break + } + } + if sectionStart == -1 { + log.Fatalf("Section %s not found in command %s", section, command) + } + + var ret []string + ret = append(ret, lines[sectionStart]) + for i := sectionStart + 1; i < len(lines); i++ { + indent := countIndent(lines[i]) + if len(lines[i]) != 0 && indent == 0 && lines[i][len(lines[i])-1] == ':' { + // new section has started + break + } + ret = append(ret, lines[i]) + } + return ret +} From 27a85b75aa266951b47d6d10cbbda7650ad3329e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 16:02:58 +0200 Subject: [PATCH 0443/2268] tests: Implement a hack that removes the need for kluctl to be pre-build for e2e tests --- .github/workflows/tests.yml | 13 +++---------- e2e/call_kluctl_hack.go | 19 +++++++++++++++++++ e2e/default_clusters.go | 4 ++++ e2e/project.go | 24 +++++++----------------- 4 files changed, 33 insertions(+), 27 deletions(-) create mode 100644 e2e/call_kluctl_hack.go diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1b3f73653..20f223be9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,23 +28,17 @@ jobs: - name: Run unit tests run: | go test ./cmd/... ./pkg/... -v - - name: Build kluctl (linux) + - name: Build kluctl-e2e (linux) run: | - make build GOARCH=amd64 GOOS=linux make test-e2e-build GOARCH=amd64 GOOS=linux - mv ./bin/kluctl ./bin/kluctl-linux-amd64 mv ./bin/kluctl-e2e ./bin/kluctl-e2e-linux-amd64 - - name: Build kluctl (darwin) + - name: Build kluctl-e2e (darwin) run: | - make build GOARCH=amd64 GOOS=darwin make test-e2e-build GOARCH=amd64 GOOS=darwin - mv ./bin/kluctl ./bin/kluctl-darwin-amd64 mv ./bin/kluctl-e2e ./bin/kluctl-e2e-darwin-amd64 - - name: Build kluctl (windows) + - name: Build kluctl-e2e (windows) run: | - make build GOARCH=amd64 GOOS=windows make test-e2e-build GOARCH=amd64 GOOS=windows - mv ./bin/kluctl.exe ./bin/kluctl-windows-amd64.exe mv ./bin/kluctl-e2e.exe ./bin/kluctl-e2e-windows-amd64.exe - name: Verify commands help is up-to-date run: | @@ -106,7 +100,6 @@ jobs: fi chmod +x ./bin/* - mv ./bin/kluctl-${{ matrix.binary-suffix }}$EXE ./kluctl$EXE mv ./bin/kluctl-e2e-${{ matrix.binary-suffix }}$EXE ./kluctl-e2e$EXE make test-e2e-pre-built diff --git a/e2e/call_kluctl_hack.go b/e2e/call_kluctl_hack.go new file mode 100644 index 000000000..6da895464 --- /dev/null +++ b/e2e/call_kluctl_hack.go @@ -0,0 +1,19 @@ +package e2e + +import ( + "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" + "os" +) + +func isCallKluctlHack() bool { + return os.Getenv("CALL_KLUCTL") == "true" +} + +func init() { + // We use the Golang's initializing mechanism to run kluctl even though the test executable was invoked + // This is clearly a hack, but it avoids the requirement to have a kluctl executable pre-built + if isCallKluctlHack() { + commands.Execute() + os.Exit(0) + } +} diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 3d0f10fd8..ed28ccbbb 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -9,6 +9,10 @@ var defaultCluster1 = test_utils.CreateEnvTestCluster("cluster1") var defaultCluster2 = test_utils.CreateEnvTestCluster("cluster2") func init() { + if isCallKluctlHack() { + return + } + var wg sync.WaitGroup wg.Add(2) go func() { diff --git a/e2e/project.go b/e2e/project.go index 3249dd8c5..b6f74c66f 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -337,27 +337,17 @@ func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { env = append(env, p.extraEnv...) env = append(env, fmt.Sprintf("KUBECONFIG=%s", p.mergedKubeconfig)) + // this will cause the init() function from call_kluctl_hack.go to invoke the kluctl root command and then exit + env = append(env, "CALL_KLUCTL=true") + p.t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) - kluctlExe := os.Getenv("KLUCTL_EXE") - if kluctlExe == "" { - curDir, _ := os.Getwd() - for i, p := range env { - x := strings.SplitN(p, "=", 2) - if x[0] == "PATH" { - env[i] = fmt.Sprintf("PATH=%s%c%s%c%s", curDir, os.PathListSeparator, filepath.Join(curDir, ".."), os.PathListSeparator, x[1]) - } - } - kluctlExe = "kluctl" - } else { - p, err := filepath.Abs(kluctlExe) - if err != nil { - return "", "", err - } - kluctlExe = p + testExe, err := os.Executable() + if err != nil { + panic(err) } - cmd := exec.Command(kluctlExe, args...) + cmd := exec.Command(testExe, args...) cmd.Dir = cwd cmd.Env = env From 2be5c441af959e2d125ea197d449dfa5db2a42d5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 16:13:47 +0200 Subject: [PATCH 0444/2268] tests: Build e2e tests with -race --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6c4304f17..ffa103c7d 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_ test-e2e: test-e2e-build test-e2e-pre-built ## Runs the end to end tests test-e2e-build: ## Builds the end to end tests - CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -c ./e2e -o ./bin/$(TEST_BINARY_NAME) + CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -race -c ./e2e -o ./bin/$(TEST_BINARY_NAME) test-e2e-pre-built: install-envtest KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) ./bin/$(TEST_BINARY_NAME) -test.v From d0433bafd0b64146e9ac21822c8d24731c1615e5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 16:15:22 +0200 Subject: [PATCH 0445/2268] ci: Fix path to ./bin/kluctl-e2e --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 20f223be9..6e24c9004 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -100,6 +100,6 @@ jobs: fi chmod +x ./bin/* - mv ./bin/kluctl-e2e-${{ matrix.binary-suffix }}$EXE ./kluctl-e2e$EXE + mv ./bin/kluctl-e2e-${{ matrix.binary-suffix }}$EXE ./bin/kluctl-e2e$EXE make test-e2e-pre-built From ce112fb489be7cadefda4844335bbbeefa2f865b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 16:30:15 +0200 Subject: [PATCH 0446/2268] chore: Remove CGO_ENABLED=0 We need CGO to enable -race --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index ffa103c7d..0431f4133 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ build: build-go ## Run the complete build pipeline build-go: ## Build your project and put the output binary in ./bin/ mkdir -p ./bin - CGO_ENBALED=0 GO111MODULE=on $(GOCMD) build -o ./bin/$(BINARY_NAME) + GO111MODULE=on $(GOCMD) build -o ./bin/$(BINARY_NAME) clean: ## Remove build related file rm -fr ./bin @@ -78,7 +78,7 @@ KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_ test-e2e: test-e2e-build test-e2e-pre-built ## Runs the end to end tests test-e2e-build: ## Builds the end to end tests - CGO_ENBALED=0 GO111MODULE=on $(GOCMD) test -race -c ./e2e -o ./bin/$(TEST_BINARY_NAME) + GO111MODULE=on $(GOCMD) test -race -c ./e2e -o ./bin/$(TEST_BINARY_NAME) test-e2e-pre-built: install-envtest KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) ./bin/$(TEST_BINARY_NAME) -test.v From 1fcfd5317490f6c9b49aaae8cedef690bcee889c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 16:31:52 +0200 Subject: [PATCH 0447/2268] chore: Remove GO111MODULE=on as it is the default now --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 0431f4133..5cd44b6c1 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ build: build-go ## Run the complete build pipeline build-go: ## Build your project and put the output binary in ./bin/ mkdir -p ./bin - GO111MODULE=on $(GOCMD) build -o ./bin/$(BINARY_NAME) + $(GOCMD) build -o ./bin/$(BINARY_NAME) clean: ## Remove build related file rm -fr ./bin @@ -78,7 +78,7 @@ KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_ test-e2e: test-e2e-build test-e2e-pre-built ## Runs the end to end tests test-e2e-build: ## Builds the end to end tests - GO111MODULE=on $(GOCMD) test -race -c ./e2e -o ./bin/$(TEST_BINARY_NAME) + $(GOCMD) test -race -c ./e2e -o ./bin/$(TEST_BINARY_NAME) test-e2e-pre-built: install-envtest KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) ./bin/$(TEST_BINARY_NAME) -test.v From fdd1e72d8d3bc7c66a178f9ac8c12294622ce358 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 17:02:38 +0200 Subject: [PATCH 0448/2268] ci: Only run race detection on linux --- Makefile | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5cd44b6c1..92f691a82 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,12 @@ EXE= ifeq ($(GOOS), windows) EXE=.exe endif + +RACE= +ifeq ($(GOOS), linux) +RACE=-race +endif + GOCMD=go GOTEST=$(GOCMD) test GOVET=$(GOCMD) vet @@ -78,7 +84,7 @@ KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_ test-e2e: test-e2e-build test-e2e-pre-built ## Runs the end to end tests test-e2e-build: ## Builds the end to end tests - $(GOCMD) test -race -c ./e2e -o ./bin/$(TEST_BINARY_NAME) + $(GOCMD) test $(RACE) -c ./e2e -o ./bin/$(TEST_BINARY_NAME) test-e2e-pre-built: install-envtest KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) ./bin/$(TEST_BINARY_NAME) -test.v @@ -89,7 +95,7 @@ ifeq ($(EXPORT_RESULT), true) GO111MODULE=off $(GOCMD) get -u github.com/jstemmer/go-junit-report $(eval OUTPUT_OPTIONS = | tee /dev/tty | go-junit-report -set-exit-code > reports/test-unit/junit-report.xml) endif - $(GOTEST) -v -race $(shell go list ./... | grep -v 'v2/e2e') $(OUTPUT_OPTIONS) + $(GOTEST) -v $(RACE) $(shell go list ./... | grep -v 'v2/e2e') $(OUTPUT_OPTIONS) coverage-unit: ## Run the unit tests of the project and export the coverage $(GOTEST) -cover -covermode=count -coverprofile=reports/coverage-unit/profile.cov $(shell go list ./... | grep -v /e2e/) From 6b649574432a23781ec3b2b9d8f0039be047795b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 17:09:24 +0200 Subject: [PATCH 0449/2268] docs: Replace hugo refs with simple MD links --- docs/concepts.md | 14 ++++++------ docs/installation.md | 4 +--- docs/reference/commands/common-arguments.md | 2 +- docs/reference/commands/delete.md | 6 ++--- docs/reference/commands/deploy.md | 6 ++--- docs/reference/commands/diff.md | 6 ++--- .../commands/environment-variables.md | 2 +- docs/reference/commands/helm-pull.md | 4 ++-- docs/reference/commands/helm-update.md | 2 +- docs/reference/commands/list-images.md | 6 ++--- docs/reference/commands/poke-images.md | 6 ++--- docs/reference/commands/prune.md | 6 ++--- docs/reference/commands/render.md | 4 ++-- docs/reference/commands/seal.md | 4 ++-- docs/reference/commands/validate.md | 4 ++-- docs/reference/deployments/README.md | 10 ++++----- .../deployments/annotations/all-resources.md | 8 +++---- .../deployments/annotations/hooks.md | 4 ++-- .../deployments/annotations/kustomization.md | 4 ++-- .../deployments/annotations/validation.md | 2 +- docs/reference/deployments/deployment-yml.md | 22 +++++++++---------- docs/reference/deployments/helm.md | 16 +++++++------- docs/reference/deployments/hooks.md | 2 +- docs/reference/deployments/images.md | 4 ++-- docs/reference/deployments/tags.md | 14 ++++++------ docs/reference/kluctl-project/README.md | 6 ++--- .../kluctl-project/secrets-config/README.md | 2 +- .../kluctl-project/targets/README.md | 20 ++++++++--------- .../kluctl-project/targets/dynamic-targets.md | 4 ++-- docs/reference/sealed-secrets.md | 20 ++++++++--------- docs/reference/templating/functions.md | 4 ++-- .../templating/predefined-variables.md | 10 ++++----- docs/reference/templating/variable-sources.md | 4 ++-- 33 files changed, 115 insertions(+), 117 deletions(-) diff --git a/docs/concepts.md b/docs/concepts.md index b1ff352a0..946f69c0f 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -13,21 +13,21 @@ These are some core concepts in Kluctl. ## Kluctl project The kluctl project defines targets and secret sources. -It is defined via the [.kluctl.yaml]({{< ref "docs/reference/kluctl-project" >}}) configuration file. +It is defined via the [.kluctl.yaml](./reference/kluctl-project) configuration file. ## Targets A target defines a target cluster and a set of deployment arguments. Multiple targets can use the same cluster. Targets allow implementing multi-cluster, multi-environment, multi-customer, ... deployments. ## Deployments -A [deployment]({{< ref "docs/reference/deployments" >}}) defines which Kustomize deployments and which sub-deployments +A [deployment](./reference/deployments) defines which Kustomize deployments and which sub-deployments to deploy. It also controls the order of deployments. Deployments may be configured through deployment arguments, which are typically provided via the targets but might also be provided through the CLI. ## Variables -[Variables]({{< ref "docs/reference/templating" >}}) are the main source of configuration. They are either loaded yaml +[Variables](./reference/templating) are the main source of configuration. They are either loaded yaml files or directly defined inside deployments. Each variables file that is loaded has access to all the variables which were defined before, allowing complex composition of configuration. @@ -36,16 +36,16 @@ After being loaded, variables are usable through the templating engine at all ne ## Templating All configuration files (including .kluctl.yaml and deployment.yaml) and all Kubernetes manifests involved are processed through a templating engine. -The [templating engine]({{< ref "docs/reference/templating" >}}) allows simple variable substitution and also complex +The [templating engine](./reference/templating) allows simple variable substitution and also complex control structures (if/else, for loops, ...). ## Secrets -Secrets are loaded from [external sources]({{< ref "docs/reference/kluctl-project" >}}) and are only available -while [sealing]({{< ref "docs/reference/sealed-secrets" >}}). After the sealing process, only the public-key encrypted +Secrets are loaded from [external sources](./reference/kluctl-project) and are only available +while [sealing](./reference/sealed-secrets). After the sealing process, only the public-key encrypted sealed secrets are available. ## Sealed Secrets -[Sealed Secrets]({{< ref "docs/reference/sealed-secrets" >}}) are based on +[Sealed Secrets](./reference/sealed-secrets) are based on [Bitnami's sealed-secrets controller](https://github.com/bitnami-labs/sealed-secrets). Kluctl offers integration of sealed secrets through the `seal` command. Kluctl allows managing multiple sets of sealed secrets for multiple targets. diff --git a/docs/installation.md b/docs/installation.md index 3bf922ec7..bd42145bf 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -61,7 +61,7 @@ Run the binary: -{{% /tabs %}} ``` diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index 70483166f..33e8e2acd 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -24,9 +24,9 @@ take the local target/state into account! ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) -1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) -1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) +1. [project arguments](./common-arguments#project-arguments) +1. [image arguments](./common-arguments#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/deploy.md b/docs/reference/commands/deploy.md index 7ecfc1b24..65e472674 100644 --- a/docs/reference/commands/deploy.md +++ b/docs/reference/commands/deploy.md @@ -22,9 +22,9 @@ It will also output a list of prunable objects (without actually deleting them). ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) -1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) -1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) +1. [project arguments](./common-arguments#project-arguments) +1. [image arguments](./common-arguments#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/diff.md b/docs/reference/commands/diff.md index c9d4546f4..d54d6091b 100644 --- a/docs/reference/commands/diff.md +++ b/docs/reference/commands/diff.md @@ -23,9 +23,9 @@ After the diff is performed, the command will also search for prunable objects a ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) -1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) -1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) +1. [project arguments](./common-arguments#project-arguments) +1. [image arguments](./common-arguments#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/environment-variables.md b/docs/reference/commands/environment-variables.md index 6db995487..2247be7cf 100644 --- a/docs/reference/commands/environment-variables.md +++ b/docs/reference/commands/environment-variables.md @@ -20,6 +20,6 @@ underscores. As an example, `--dry-run` can also be specified with the environme ## Additional environment variables A few additional environment variables are supported which do not belong to an option/argument. These are: -1. `KLUCTL_REGISTRY__HOST`, `KLUCTL_REGISTRY__USERNAME`, and so on. See [registries]({{< ref "docs/reference/deployments/images#supported-image-registries-and-authentication" >}}) for details. +1. `KLUCTL_REGISTRY__HOST`, `KLUCTL_REGISTRY__USERNAME`, and so on. See [registries](../deployments/images#supported-image-registries-and-authentication) for details. 2. `KLUCTL_GIT__HOST`, `KLUCTL_GIT__USERNAME`, and so on. 3. `KLUCTL_SSH_DISABLE_STRICT_HOST_KEY_CHECKING`. Disable ssh host key checking when accessing git repositories. diff --git a/docs/reference/commands/helm-pull.md b/docs/reference/commands/helm-pull.md index 80fb685a3..68d3871b4 100644 --- a/docs/reference/commands/helm-pull.md +++ b/docs/reference/commands/helm-pull.md @@ -20,8 +20,8 @@ pulling is only needed when really required (e.g. when the chart version changes -See [helm-integration]({{< ref "docs/reference/deployments/helm">}}) for more details. +See [helm-integration](../deployments/helm) for more details. ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) (except `-a`) +1. [project arguments](./common-arguments#project-arguments) (except `-a`) diff --git a/docs/reference/commands/helm-update.md b/docs/reference/commands/helm-update.md index e1a526ae8..d719c76a0 100644 --- a/docs/reference/commands/helm-update.md +++ b/docs/reference/commands/helm-update.md @@ -20,7 +20,7 @@ Optionally performs the actual upgrade and/or add a commit to version control. ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) (except `-a`) +1. [project arguments](./common-arguments#project-arguments) (except `-a`) In addition, the following arguments are available: diff --git a/docs/reference/commands/list-images.md b/docs/reference/commands/list-images.md index ce0f2aaba..5d59e6949 100644 --- a/docs/reference/commands/list-images.md +++ b/docs/reference/commands/list-images.md @@ -23,9 +23,9 @@ as described in for the deploy command. ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) -1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) -1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) +1. [project arguments](./common-arguments#project-arguments) +1. [image arguments](./common-arguments#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/poke-images.md b/docs/reference/commands/poke-images.md index 585ae6d47..b5279c256 100644 --- a/docs/reference/commands/poke-images.md +++ b/docs/reference/commands/poke-images.md @@ -22,9 +22,9 @@ replaced ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) -1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) -1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) +1. [project arguments](./common-arguments#project-arguments) +1. [image arguments](./common-arguments#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/prune.md b/docs/reference/commands/prune.md index a895db0e8..86161c3ea 100644 --- a/docs/reference/commands/prune.md +++ b/docs/reference/commands/prune.md @@ -18,9 +18,9 @@ Searches the target cluster for prunable objects and deletes them ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) -1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) -1. [inclusion/exclusion arguments]({{< ref "./common-arguments#inclusionexclusion-arguments" >}}) +1. [project arguments](./common-arguments#project-arguments) +1. [image arguments](./common-arguments#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/render.md b/docs/reference/commands/render.md index c2b626415..05a48e59c 100644 --- a/docs/reference/commands/render.md +++ b/docs/reference/commands/render.md @@ -21,8 +21,8 @@ a temporary directory or a specified directory. ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) -1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) +1. [project arguments](./common-arguments#project-arguments) +1. [image arguments](./common-arguments#image-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/seal.md b/docs/reference/commands/seal.md index ee99cba1c..88edc02c7 100644 --- a/docs/reference/commands/seal.md +++ b/docs/reference/commands/seal.md @@ -23,11 +23,11 @@ If no '--target' is specified, sealing is performed for all targets. -See [sealed-secrets]({{< ref "docs/reference/sealed-secrets">}}) for more details. +See [sealed-secrets](../sealed-secrets) for more details. ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) (except `-a`) +1. [project arguments](./common-arguments#project-arguments) (except `-a`) In addition, the following arguments are available: diff --git a/docs/reference/commands/validate.md b/docs/reference/commands/validate.md index 5ff5a0a6e..d904c98fb 100644 --- a/docs/reference/commands/validate.md +++ b/docs/reference/commands/validate.md @@ -22,8 +22,8 @@ TODO: This needs to be better documented! ## Arguments The following sets of arguments are available: -1. [project arguments]({{< ref "./common-arguments#project-arguments" >}}) -1. [image arguments]({{< ref "./common-arguments#image-arguments" >}}) +1. [project arguments](./common-arguments#project-arguments) +1. [image arguments](./common-arguments#image-arguments) In addition, the following arguments are available: diff --git a/docs/reference/deployments/README.md b/docs/reference/deployments/README.md index 08c0d07d9..d4e211174 100644 --- a/docs/reference/deployments/README.md +++ b/docs/reference/deployments/README.md @@ -21,7 +21,7 @@ description: > 8. [Annotations](./annotations) A deployment project is collection of deployment items and sub-deployments. Deployment items are usually -[Kustomize]({{< ref "./kustomize" >}}) deployments, but can also integrate [Helm Charts]({{< ref "./helm" >}}). +[Kustomize](./kustomize.md) deployments, but can also integrate [Helm Charts](./helm.md). ## Basic structure @@ -32,14 +32,14 @@ provides some additional configuration required for multiple kluctl features to As can be seen, sub-deployments can include other sub-deployments, allowing you to structure the deployment project as you need. -Each level in this structure recursively adds [tags]({{< ref "./tags" >}}) to each deployed resources, allowing you to control +Each level in this structure recursively adds [tags](./tags.md) to each deployed resources, allowing you to control precisely what is deployed in the future. Some visualized files/directories have links attached, follow them to get more information.
 -- project-dir/
-   |-- }}">deployment.yaml
+   |-- deployment.yaml
    |-- .gitignore
    |-- kustomize-deployment1/
    |   |-- kustomization.yaml
@@ -56,7 +56,7 @@ Some visualized files/directories have links attached, follow them to get more i
    |   |   |-- resource2.yaml
    |   |   |-- patch1.yaml
    |   |   `-- ...
-   |   |-- }}">kustomize-with-helm-deployment/
+   |   |-- kustomize-with-helm-deployment/
    |   |   |-- charts/
    |   |   |   `-- ...
    |   |   |-- kustomization.yaml
@@ -72,5 +72,5 @@ Some visualized files/directories have links attached, follow them to get more i
 
 ## Order of deployments
 Deployments are done in parallel, meaning that there are usually no order guarantees. The only way to somehow control
-order, is by placing [barriers]({{< ref "./deployment-yml#barriers" >}}) between kustomize deployments.
+order, is by placing [barriers](./deployment-yml.md#barriers) between kustomize deployments.
 You should however not overuse barriers, as they negatively impact the speed of kluctl.
diff --git a/docs/reference/deployments/annotations/all-resources.md b/docs/reference/deployments/annotations/all-resources.md
index 5f2ee3327..e1aa989d6 100644
--- a/docs/reference/deployments/annotations/all-resources.md
+++ b/docs/reference/deployments/annotations/all-resources.md
@@ -37,12 +37,12 @@ If more than one field needs to be specified, add `-xxx` to the annotation key,
 The following annotations control how delete/prune is behaving.
 
 ### kluctl.io/skip-delete
-If set to "true", the annotated resource will not be deleted when [delete]({{< ref "docs/reference/commands/delete" >}}) or
-[prune]({{< ref "docs/reference/commands/prune" >}}) is called.
+If set to "true", the annotated resource will not be deleted when [delete](../../commands/delete) or
+[prune](../../commands/prune) is called.
 
 ### kluctl.io/skip-delete-if-tags
-If set to "true", the annotated resource will not be deleted when [delete]({{< ref "docs/reference/commands/delete" >}}) or
-[prune]({{< ref "docs/reference/commands/prune" >}}) is called and inclusion/exclusion tags are used at the same time.
+If set to "true", the annotated resource will not be deleted when [delete](../../commands/delete) or
+[prune](../../commands/prune) is called and inclusion/exclusion tags are used at the same time.
 
 This tag is especially useful and required on resources that would otherwise cause cascaded deletions of resources that
 do not match the specified inclusion/exclusion tags. Namespaces are the most prominent example of such resources, as
diff --git a/docs/reference/deployments/annotations/hooks.md b/docs/reference/deployments/annotations/hooks.md
index ca946554e..1b033bf9b 100644
--- a/docs/reference/deployments/annotations/hooks.md
+++ b/docs/reference/deployments/annotations/hooks.md
@@ -13,10 +13,10 @@ description: >
 
 The following annotations control hook execution
 
-See [hooks]({{< ref "docs/reference/deployments/hooks" >}}) for more details.
+See [hooks](../../deployments/hooks) for more details.
 
 ### kluctl.io/hook
-Declares a resource to be a hook, which is deployed/executed as described in [hooks]({{< ref "docs/reference/deployments/hooks" >}}). The value of the
+Declares a resource to be a hook, which is deployed/executed as described in [hooks](../../deployments/hooks). The value of the
 annotation determines when the hook is deployed/executed.
 
 ### kluctl.io/hook-weight
diff --git a/docs/reference/deployments/annotations/kustomization.md b/docs/reference/deployments/annotations/kustomization.md
index dfdcbf661..f0312940c 100644
--- a/docs/reference/deployments/annotations/kustomization.md
+++ b/docs/reference/deployments/annotations/kustomization.md
@@ -31,8 +31,8 @@ resources:
 
 ### kluctl.io/barrier
 If set to `true`, kluctl will wait for all previous objects to be applied (but not necessarily ready). This has the
-same effect as [barrier]({{< ref "docs/reference/deployments#barriers" >}}) from deployment projects.
+same effect as [barrier](../../deployments/deployment-yml.md#barriers) from deployment projects.
 
 ### kluctl.io/wait-readiness
 If set to `true`, kluctl will wait for readiness of all objects from this kustomization project. Readiness is defined
-the same as in [hook readiness]({{< ref "docs/reference/deployments/readiness" >}}).
+the same as in [hook readiness](../../deployments/readiness).
diff --git a/docs/reference/deployments/annotations/validation.md b/docs/reference/deployments/annotations/validation.md
index d4720d2e4..a36fcf2e4 100644
--- a/docs/reference/deployments/annotations/validation.md
+++ b/docs/reference/deployments/annotations/validation.md
@@ -11,7 +11,7 @@ description: >
 
 # Validation
 
-The following annotations influence the [validate]({{< ref "docs/reference/commands/validate" >}}) command.
+The following annotations influence the [validate](../../commands/validate) command.
 
 ### validate-result.kluctl.io/xxx
 If this annotation is found on a resource that is checked while validation, the key and the value of the annotation
diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md
index ab36ffc00..0ca5cb75b 100644
--- a/docs/reference/deployments/deployment-yml.md
+++ b/docs/reference/deployments/deployment-yml.md
@@ -36,7 +36,7 @@ The following sub-chapters describe the available fields in the `deployment.yaml
 
 ## sealedSecrets
 `sealedSecrets` configures how sealed secrets are stored while sealing and located while rendering.
-See [Sealed Secrets]({{< ref "docs/reference/sealed-secrets#outputpattern-and-location-of-stored-sealed-secrets" >}})
+See [Sealed Secrets](../sealed-secrets#outputpattern-and-location-of-stored-sealed-secrets)
 for details.
 
 ## deployments
@@ -48,7 +48,7 @@ wait for all previous deployments to finish.
 ### Kustomize deployments
 
 Specifies a [kustomize](https://kustomize.io/) deployment.
-Please see [Kustomize integration]({{< ref "./kustomize" >}}) for more details.
+Please see [Kustomize integration](./kustomize.md) for more details.
 
 Example:
 ```yaml
@@ -62,7 +62,7 @@ The `path` must point to a directory relative to the directory containing the `d
 that are part of the kluctl project are allowed. The directory must contain a valid `kustomization.yaml`.
 
 `waitReadiness` is optional and if set to `true` instructs kluctl to wait for readiness of each individual object
-of the kustomize deployment. Readiness is defined in [readiness]({{< ref "./readiness" >}}).
+of the kustomize deployment. Readiness is defined in [readiness](./readiness.md).
 
 ### Includes
 
@@ -130,7 +130,7 @@ All entries in `deployments` can have the following common properties:
 A list of variable sets to be loaded into the templating context, which is then available in all [deployment items](#deployments)
 and [sub-deployments](#includes).
 
-See [templating]({{< ref "docs/reference/templating#vars-from-deploymentyaml" >}}) for more details.
+See [templating](../templating/variable-sources.md) for more details.
 
 Example:
 ```yaml
@@ -148,8 +148,8 @@ deployments:
 ```
 
 ### tags (deployment item)
-A list of tags the deployment should have. See [tags]({{< ref "./tags" >}}) for more details. For includes, this means that all
-sub-deployments will get these tags applied to. If not specified, the default tags logic as described in [tags]({{< ref "./tags" >}})
+A list of tags the deployment should have. See [tags](./tags.md) for more details. For includes, this means that all
+sub-deployments will get these tags applied to. If not specified, the default tags logic as described in [tags](./tags.md)
 is applied.
 
 Example:
@@ -171,7 +171,7 @@ deployments:
 
 ### alwaysDeploy
 Forces a deployment to be included everytime, ignoring inclusion/exclusion sets from the command line.
-See [Deploying with tag inclusion/exclusion]({{< ref "./tags#deploying-with-tag-inclusionexclusion" >}}) for details.
+See [Deploying with tag inclusion/exclusion](./tags.md#deploying-with-tag-inclusionexclusion) for details.
 
 ```yaml
 deployments:
@@ -182,7 +182,7 @@ deployments:
 
 ### skipDeleteIfTags
 Forces exclusion of a deployment whenever inclusion/exclusion tags are specified via command line.
-See [Deleting with tag inclusion/exclusion]({{< ref "./tags#deploying-with-tag-inclusionexclusion" >}}) for details.
+See [Deleting with tag inclusion/exclusion](./tags.md#deploying-with-tag-inclusionexclusion) for details.
 
 ```yaml
 deployments:
@@ -195,7 +195,7 @@ deployments:
 A list of variable sets to be loaded into the templating context, which is then available in all [deployment items](#deployments)
 and [sub-deployments](#includes).
 
-See [templating]({{< ref "docs/reference/templating#vars-from-deploymentyaml" >}}) for more details.
+See [templating](../templating/variable-sources.md) for more details.
 
 ## commonLabels
 A dictionary of [labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) and values to be
@@ -240,7 +240,7 @@ A string that is used as the default namespace for all kustomize deployments whi
 ## tags (deployment project)
 A list of common tags which are applied to all kustomize deployments and sub-deployment includes.
 
-See [tags]({{< ref "./tags" >}}) for more details.
+See [tags](./tags.md) for more details.
 
 ## args
 A list of arguments that can or must be passed to most kluctl operations. Each of these arguments is then available
@@ -307,5 +307,5 @@ valid [JSON Path](https://goessner.net/articles/JsonPath/). `fieldPath` may also
 The JSON Path implementation used in kluctl has extended support for wildcards in field
 names, allowing you to also specify paths like `metadata.labels.my-prefix-*`.
 
-As an alternative, [annotations]({{< ref "./annotations/all-resources#control-diff-behavior" >}}) can be used to control
+As an alternative, [annotations](./annotations/all-resources#control-diff-behavior) can be used to control
 diff behavior of individual resources.
diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md
index 2d8dab7b4..aab1fdbf0 100644
--- a/docs/reference/deployments/helm.md
+++ b/docs/reference/deployments/helm.md
@@ -25,14 +25,14 @@ hands over the rendered yaml to [kustomize](https://kustomize.io/). Rendering is
 `helm-values.yaml`, which contains the necessary values to configure the Helm Chart.
 
 The resulting rendered yaml is then referred by your `kustomization.yaml`, from which point on the
-[kustomize integration]({{< ref "docs/reference/sealed-secrets#outputpattern-and-location-of-stored-sealed-secrets" >}}) 
+[kustomize integration](../sealed-secrets#outputpattern-and-location-of-stored-sealed-secrets) 
 takes over. This means, that you can perform all desired customization (patches, namespace override, ...) as if you
 provided your own resources via yaml files.
 
 ### Helm hooks
 
 [Helm Hooks](https://helm.sh/docs/topics/charts_hooks/) are implemented by mapping them 
-to [kluctl hooks]({{< ref "./hooks" >}}), based on the following mapping table:
+to [kluctl hooks](./hooks.md), based on the following mapping table:
 
 | Helm hook     | kluctl hook         |
 |---------------|---------------------|
@@ -99,7 +99,7 @@ The name of the chart that can be found in the repository.
 The version of the chart.
 
 ### skipUpdate
-Skip this Helm Chart when the [helm-update]({{< ref "docs/reference/commands/helm-update" >}}) command is called.
+Skip this Helm Chart when the [helm-update](../commands/helm-update) command is called.
 If omitted, defaults to `false`.
 
 ### releaseName
@@ -125,8 +125,8 @@ read the documentation of the used Helm Charts for details on what is supported.
 
 ## Updates to helm-charts
 In case a Helm Chart needs to be updated, you can either do this manually by replacing the [chartVersion](#chartversion)
-value in `helm-chart.yaml` and the calling the [helm-pull]({{< ref "docs/reference/commands/helm-pull" >}}) command or by simply invoking
-[helm-update]({{< ref "docs/reference/commands/helm-update" >}}) with `--upgrade` and/or `--commit` being set.
+value in `helm-chart.yaml` and the calling the [helm-pull](../commands/helm-pull) command or by simply invoking
+[helm-update](../commands/helm-update) with `--upgrade` and/or `--commit` being set.
 
 ## Private Chart Repositories
 It is also possible to use private chart repositories. There are currently two options to provide Helm Repository
@@ -139,7 +139,7 @@ added the repository to Helm. The same method can be used for client certificate
 in `helm repo add`).
 
 ### Use the --username/--password arguments in `kluctl helm-pull`
-See the [helm-pull command]({{< ref "docs/reference/commands/helm-pull" >}}). You can control repository credentials
+See the [helm-pull command](../commands/helm-pull). You can control repository credentials
 via `--username`, `--password` and `--key-file`. Each argument must be in the form `credentialsId:value`, where
 the `credentialsId` must match the id specified in the `helm-chart.yaml`. Example:
 
@@ -160,11 +160,11 @@ When credentialsId is specified, Kluctl will require you to specify `--username=
 Multiple Helm Charts can use the same `credentialsId`.
 
 Environment variables can also be used instead of arguments. See
-[Environment Variables]({{< ref "docs/reference/commands/environment-variables" >}}) for details.
+[Environment Variables](../commands/environment-variables) for details.
 
 ## Templating
 
-Both `helm-chart.yaml` and `helm-values.yaml` are rendered by the [templating engine]({{< ref "docs/reference/templating" >}}) before they
+Both `helm-chart.yaml` and `helm-values.yaml` are rendered by the [templating engine](../templating) before they
 are actually used. This means, that you can use all available Jinja2 variables at that point, which can for example be
 seen in the above `helm-chart.yaml` example for the namespace.
 
diff --git a/docs/reference/deployments/hooks.md b/docs/reference/deployments/hooks.md
index 9764f0051..b9f5879c2 100644
--- a/docs/reference/deployments/hooks.md
+++ b/docs/reference/deployments/hooks.md
@@ -48,6 +48,6 @@ changed by setting the `kluctl.io/hook-delete-policy` to a comma separated list
 ## Hook readiness
 
 After each deployment/execution of the hooks that belong to a deployment stage (before/after deployment), kluctl
-waits for the hook resources to become "ready". Readiness is defined [here]({{< ref "./readiness" >}}).
+waits for the hook resources to become "ready". Readiness is defined [here](./readiness.md).
 
 It is possible to disable waiting for hook readiness by setting the annotation `kluctl.io/hook-wait` to "false".
diff --git a/docs/reference/deployments/images.md b/docs/reference/deployments/images.md
index 6f2a7e3cb..3b88af2bb 100644
--- a/docs/reference/deployments/images.md
+++ b/docs/reference/deployments/images.md
@@ -12,7 +12,7 @@ description: >
 # Container Images
 
 There are usually 2 different scenarios where Container Images need to be specified:
-1. When deploying third party applications like nginx, redis, ... (e.g. via the [Helm integration]({{< ref "./helm" >}})). 
+1. When deploying third party applications like nginx, redis, ... (e.g. via the [Helm integration](./helm.md)).
* In this case, image versions/tags rarely change, and if they do, this is an explicit change to the deployment. 1. When deploying your own applications.
* In this case, image versions/tags might change very rapidly, sometimes multiple times per hour. It would be too much @@ -89,7 +89,7 @@ The described `images.get_image` logic however leads to a loosely defined state might be fine in a CI/CD environment, but might be undesired when deploying to production. In that case, it might be desirable to explicitly define which versions need to be deployed. -To achieve this, you can use the `-F FIXED_IMAGE` [argument]({{< ref "docs/reference/commands/common-arguments#image-arguments" >}}). +To achieve this, you can use the `-F FIXED_IMAGE` [argument](../commands/common-arguments#image-arguments). `FIXED_IMAGE` must be in the form of `-F image<:namespace:deployment:container>=result`. For example, to pin the image `registry.gitlab.com/my-group/my-project` to the tag `1.1.2` you'd have to specify `-F registry.gitlab.com/my-group/my-project=registry.gitlab.com/my-group/my-project:1.1.2`. diff --git a/docs/reference/deployments/tags.md b/docs/reference/deployments/tags.md index 9501d0845..6bbfe18f4 100644 --- a/docs/reference/deployments/tags.md +++ b/docs/reference/deployments/tags.md @@ -10,14 +10,14 @@ weight: 6 # Tags Every kustomize deployment has a set of tags assigned to it. These tags are defined in multiple places, which is -documented in [deployment.yaml]({{< ref "./deployment-yml" >}}). Look for the `tags` field, which is available in multiple places per +documented in [deployment.yaml](./deployment-yml.md). Look for the `tags` field, which is available in multiple places per deployment project. Tags are useful when only one or more specific kustomize deployments need to be deployed or deleted. ## Default tags -[deployment items]({{< ref "./deployment-yml#deployments" >}}) in deployment projects can have an optional list of tags assigned. +[deployment items](./deployment-yml.md#deployments) in deployment projects can have an optional list of tags assigned. If this list is completely omitted, one single entry is added by default. This single entry equals to the last element of the `path` in the `deployments` entry. @@ -39,10 +39,10 @@ or even conflicting tags (e.g. `subdir` is really a bad tag), in which case you' ## Tag inheritance Deployment projects and deployments items inherit the tags of their parents. For example, if a deployment project -has a [tags]({{< ref "./deployment-yml#tags-deployment-project" >}}) property defined, all `deployments` entries would +has a [tags](./deployment-yml.md#tags-deployment-project) property defined, all `deployments` entries would inherit all these tags. Also, the sub-deployment projects included via deployment items of type -[include]({{< ref "./deployment-yml#includes" >}}) inherit the tags of the deployment project. These included sub-deployments also -inherit the [tags]({{< ref "./deployment-yml#tags-deployment-item" >}}) specified by the deployment item itself. +[include](./deployment-yml.md#includes) inherit the tags of the deployment project. These included sub-deployments also +inherit the [tags](./deployment-yml.md#tags-deployment-item) specified by the deployment item itself. Consider the following example `deployment.yaml`: @@ -72,7 +72,7 @@ resources to be deployed as well. Imagine a large deployment is able to deploy 10 applications, but you only want to deploy one of them. When using tags to achieve this, there might be some base resources (e.g. Namespaces) which are needed no matter if everything or just -this single application is deployed. In that case, you'd need to set [alwaysDeploy]({{< ref "./deployment-yml#deployments" >}}) +this single application is deployed. In that case, you'd need to set [alwaysDeploy](./deployment-yml.md#deployments) to `true`. ## Deleting with tag inclusion/exclusion @@ -83,6 +83,6 @@ Imagine a kustomize deployment being responsible for namespaces deployments. If deployments that have the `persistency` tag assigned, the exclusion logic would NOT exclude deletion of the namespace. This would ultimately lead to everything being deleted, and the exclusion tag having no effect. -In such a case, you'd need to set [skipDeleteIfTags]({{< ref "./deployment-yml#skipdeleteiftags" >}}) to `true` as well. +In such a case, you'd need to set [skipDeleteIfTags](./deployment-yml.md#skipdeleteiftags) to `true` as well. In most cases, setting `alwaysDeploy` to `true` also requires setting `skipDeleteIfTags` to `true`. diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index eedd06aaa..556e1313f 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -12,9 +12,9 @@ description: > # Kluctl project The `.kluctl.yaml` is the central configuration and entry point for your deployments. It defines where the actual -[deployment project]({{< ref "docs/reference/deployments" >}}) is located, -where [sealed secrets]({{< ref "docs/reference/sealed-secrets" >}}) and unencrypted secrets are localed and which targets are available to -invoke [commands]({{< ref "docs/reference/commands" >}}) on. +[deployment project](../deployments) is located, +where [sealed secrets](../sealed-secrets) and unencrypted secrets are localed and which targets are available to +invoke [commands](../commands) on. ## Example diff --git a/docs/reference/kluctl-project/secrets-config/README.md b/docs/reference/kluctl-project/secrets-config/README.md index 41663bdb2..81207967a 100644 --- a/docs/reference/kluctl-project/secrets-config/README.md +++ b/docs/reference/kluctl-project/secrets-config/README.md @@ -35,7 +35,7 @@ This field specifies the name of the secret set. The name can be used in targets ### vars A list of variables sources. Check the documentation of -[variables sources]({{< ref "docs/reference/templating/variable-sources" >}}) for details. +[variables sources](../../templating/variable-sources) for details. Each variables source must have a root dictionary with the name `secrets` and all the actual secret values below that dictionary. Every other root key will be ignored. diff --git a/docs/reference/kluctl-project/targets/README.md b/docs/reference/kluctl-project/targets/README.md index 9349cc476..ce1a19f3c 100644 --- a/docs/reference/kluctl-project/targets/README.md +++ b/docs/reference/kluctl-project/targets/README.md @@ -13,11 +13,11 @@ description: > Specifies a list of targets for which commands can be invoked. A target puts together environment/target specific configuration and the target cluster. Multiple targets can exist which target the same cluster but with differing -configuration (via `args`). Target entries also specifies which secrets to use while [sealing]({{< ref "docs/reference/sealed-secrets" >}}). +configuration (via `args`). Target entries also specifies which secrets to use while [sealing](../../sealed-secrets). Each value found in the target definition is rendered with a simple Jinja2 context that only contains the target itself. The rendering process is retried 10 times until it finally succeeds, allowing you to reference -the target itself in complex ways. This is especially useful when using [dynamic targets]({{< ref "./dynamic-targets" >}}). +the target itself in complex ways. This is especially useful when using [dynamic targets](./dynamic-targets.md). Target entries have the following form: ```yaml @@ -45,7 +45,7 @@ The following fields are allowed per target: ## name This field specifies the name of the target. The name must be unique. It is referred in all commands via the -[-t]({{< ref "docs/reference/commands/common-arguments" >}}) option. +[-t](../../commands/common-arguments) option. ## context This field specifies the kubectl context of the target cluster. The context must exist in the currently active kubeconfig. @@ -53,9 +53,9 @@ If this field is omitted, Kluctl will always use the currently active context. ## args This fields specifies a map of arguments to be passed to the deployment project when it is rendered. Allowed argument names -are configured via [deployment args]({{< ref "docs/reference/deployments/deployment-yml#args" >}}). +are configured via [deployment args](../../deployments/deployment-yml#args). -The arguments specified in the [dynamic target config]({{< ref "docs/reference/kluctl-project/targets/dynamic-targets#args" >}}) +The arguments specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets#args) have higher priority. ## dynamicArgs @@ -65,17 +65,17 @@ arguments are passed with `-a arg_name=arg_value` when for example calling `kluc Each entry has the following fields: ## images -This field specifies a list of fixed images to be used by [`images.get_image(...)`]({{< ref "docs/reference/deployments/images#imagesget_image" >}}). +This field specifies a list of fixed images to be used by [`images.get_image(...)`](../../deployments/images#imagesget_image). The format is identical to the [fixed images file](https://kluctl.io/docs/reference/deployments/images/#fixed-images-via-a-yaml-file). -The fixed images specified in the [dynamic target config]({{< ref "docs/reference/kluctl-project/targets/dynamic-targets#images" >}}) +The fixed images specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets#images) have higher priority. ### name The name of the argument. ## sealingConfig -This field configures how sealing is performed when the [seal command] ({{< ref "docs/reference/commands/seal" >}}) is invoked for this target. +This field configures how sealing is performed when the [seal command](../../commands/seal) is invoked for this target. It has the following form: ```yaml @@ -101,8 +101,8 @@ Optional path to a local (inside your project) public certificate used for seali from the sealed-secrets controller using `kubeseal --fetch-cert`. ### dynamicSealing -This field specifies weather sealing should happen per [dynamic target]({{< ref "./dynamic-targets" >}}) or only once. This +This field specifies weather sealing should happen per [dynamic target](./dynamic-targets.md) or only once. This field is optional and defaults to `true`. ### secretSets -This field specifies a list of secret set names, which all must exist in the [secretsConfig]({{< ref "../secrets-config" >}}). +This field specifies a list of secret set names, which all must exist in the [secretsConfig](../secrets-config). diff --git a/docs/reference/kluctl-project/targets/dynamic-targets.md b/docs/reference/kluctl-project/targets/dynamic-targets.md index 158262cdc..32fa6a1af 100644 --- a/docs/reference/kluctl-project/targets/dynamic-targets.md +++ b/docs/reference/kluctl-project/targets/dynamic-targets.md @@ -91,12 +91,12 @@ images: ``` ### args -An optional map of arguments, in the same format as in the normal [target args]({{< ref "docs/reference/kluctl-project/targets/#args" >}}). +An optional map of arguments, in the same format as in the normal [target args](../../kluctl-project/targets#args). The arguments specified here have higher priority. ### images -An optional list of fixed images, in the same format as in the normal [target images]({{< ref "docs/reference/kluctl-project/targets/#images" >}}) +An optional list of fixed images, in the same format as in the normal [target images](../../kluctl-project/targets#images) ## Simple dynamic targets diff --git a/docs/reference/sealed-secrets.md b/docs/reference/sealed-secrets.md index 8c7ebca61..81dd38c84 100644 --- a/docs/reference/sealed-secrets.md +++ b/docs/reference/sealed-secrets.md @@ -25,15 +25,15 @@ being installed. Installing the operator is the responsibility of you (or whoeve Kluctl can however perform sealing of secrets without an existing sealed-secrets operator installation. This is solved by automatically pre-provisioning a key onto the cluster that is compatible with the operator or by providing the -public certificate via `certFile` in the targets [sealingConfig]({{< ref "docs/reference/kluctl-project/targets#certfile" >}}). +public certificate via `certFile` in the targets [sealingConfig](./kluctl-project/targets#certfile). ## Sealing of .sealme files -Sealing is done via the [seal command]({{< ref "docs/reference/commands/seal" >}}). It must be done before the actual +Sealing is done via the [seal command](./commands/seal). It must be done before the actual deployment is performed. The `seal` command recursively searches for files that end with `.sealme`, renders them with the -[templating engine]({{< ref "docs/reference/templating" >}}) engine. The rendered secret resource is then +[templating engine](./templating) engine. The rendered secret resource is then converted/encrypted into a sealed secret. The `.sealme` files itself have to be [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/), @@ -54,14 +54,14 @@ stringData: DB_PASSWORD: {{ secrets.database.password }} ``` -While sealing, the full templating context (same as in [templating]({{< ref "docs/reference/templating" >}})) is available. +While sealing, the full templating context (same as in [templating](./templating)) is available. Additionally, the global `secrets` object/variable is available which contains the sensitive secrets. ## Secret Sources Secrets are only loaded while sealing. Available secret sets and sources are configured via -[.kluctl.yaml]({{< ref "docs/reference/kluctl-project/secrets-config" >}}). The secrets used per target are configured via the -[secrets config]({{< ref "docs/reference/kluctl-project/targets#secretsets" >}}) of the targets. +[.kluctl.yaml](./kluctl-project/secrets-config). The secrets used per target are configured via the +[secrets config](./kluctl-project/targets#secretsets) of the targets. ## Using sealed secrets @@ -83,9 +83,9 @@ resources: ## outputPattern and location of stored sealed secrets -It is possible to override the output pattern in the root [deployment project]({{< ref "docs/reference/deployments" >}}). +It is possible to override the output pattern in the root [deployment project](./deployments). The output pattern must be a template string that is rendered with the full -[templating context]({{< ref "docs/reference/templating" >}}) available for the deployment.yaml. +[templating context](./templating) available for the deployment.yaml. When manually specifying the outputPattern, ensure that it works well with multiple clusters and targets. You can for example use the `{{ target.name }}` and `{{ cluster.name }}` inside the outputPattern. @@ -111,8 +111,8 @@ with: ## Content Hashes and re-sealing Sealed secrets are stored together with hashes of all individual secret entries. These hashes are then used to avoid -unnecessary re-sealing in future [seal]({{< ref "docs/reference/commands/seal" >}}) invocations. If you want to force re-sealing, use the -[--force-reseal]({{< ref "docs/reference/commands/seal" >}}) option. +unnecessary re-sealing in future [seal](./commands/seal) invocations. If you want to force re-sealing, use the +[--force-reseal](./commands/seal) option. Hashing of secrets is done with bcrypt and the cluster id as salt. The cluster id is currently defined as the sha256 hash of the cluster CA certificate. This will cause re-sealing of all secrets in case a cluster is set up from scratch diff --git a/docs/reference/templating/functions.md b/docs/reference/templating/functions.md index ad4894474..db0eac48e 100644 --- a/docs/reference/templating/functions.md +++ b/docs/reference/templating/functions.md @@ -22,7 +22,7 @@ Loads the given file into memory, renders it with the current Jinja2 context and {{ a }} ``` -`load_template` uses the same path searching rules as described in [includes/imports]({{< ref "docs/reference/templating#includes-and-imports" >}}). +`load_template` uses the same path searching rules as described in [includes/imports](../templating#includes-and-imports). ### load_sha256(file, digest_len) Loads the given file into memory, renders it and calculates the sha256 hash of the result. @@ -44,7 +44,7 @@ data: ### get_var(field_path, default) Convenience method to navigate through the current context variables via a [JSON Path](https://goessner.net/articles/JsonPath/). Let's assume you currently have these variables defined -(e.g. via [vars]({{< ref "docs/reference/deployments/deployment-yml#vars-deployment-project" >}})): +(e.g. via [vars](../deployments/deployment-yml#vars-deployment-project)): ```yaml my: deep: diff --git a/docs/reference/templating/predefined-variables.md b/docs/reference/templating/predefined-variables.md index 75c962937..5c4e88645 100644 --- a/docs/reference/templating/predefined-variables.md +++ b/docs/reference/templating/predefined-variables.md @@ -15,18 +15,18 @@ There are multiple variables available which are pre-defined by kluctl. These ar ### args This is a dictionary of arguments given via command line. It contains every argument defined in -[deployment args]({{< ref "docs/reference/deployments/deployment-yml#args" >}}). +[deployment args](../deployments/deployment-yml#args). ### target This is the target definition of the currently processed target. It contains all values found in the -[target definition]({{< ref "docs/reference/kluctl-project/targets" >}}), for example `target.name`. +[target definition](../kluctl-project/targets), for example `target.name`. ### images -This global object provides the dynamic images features described in [images]({{< ref "docs/reference/deployments/images" >}}). +This global object provides the dynamic images features described in [images](../deployments/images). ### version -This global object defines latest version filters for `images.get_image(...)`. See [images]({{< ref "docs/reference/deployments/images" >}}) for details. +This global object defines latest version filters for `images.get_image(...)`. See [images](../deployments/images) for details. ### secrets -This global object is only available while [sealing]({{< ref "docs/reference/sealed-secrets" >}}) and contains the loaded +This global object is only available while [sealing](../sealed-secrets) and contains the loaded secrets defined via the currently sealed target. diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index 368e15eb6..c81465380 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -14,10 +14,10 @@ description: > There are multiple places in deployment projects (deployment.yaml) where additional variables can be loaded into future Jinja2 contexts. -The first place where vars can be specified is the deployment root, as documented [here]({{< ref "docs/reference/deployments/deployment-yml#vars-deployment-project" >}}). +The first place where vars can be specified is the deployment root, as documented [here](../deployments/deployment-yml#vars-deployment-project). These vars are visible for all deployments inside the deployment project, including sub-deployments from includes. -The second place to specify variables is in the deployment items, as documented [here]({{< ref "docs/reference/deployments/deployment-yml#vars-deployment-item" >}}). +The second place to specify variables is in the deployment items, as documented [here](../deployments/deployment-yml#vars-deployment-item). The variables loaded for each entry in `vars` are not available inside the `deployment.yaml` file itself. However, each entry in `vars` can use all variables defined before that specific entry is processed. Consider the From ceb13154a42e3ab04eef960ee096a734ab0b2a80 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 17:10:06 +0200 Subject: [PATCH 0450/2268] docs: Run make replace-commands-help --- docs/reference/commands/common-arguments.md | 35 ++++++++++++++------- docs/reference/commands/list-images.md | 4 ++- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index fdaa1c016..235b823e1 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -39,23 +39,34 @@ They control where and how to load the kluctl project and deployment project. Project arguments: Define where and how to load the kluctl project and its components from. - -a, --arg stringArray Template argument in the form name=value - --cluster string DEPRECATED. Specify/Override cluster + -a, --arg stringArray Passes a template argument in the form of name=value. Nested args can + be set with the '-a my.nested.arg=value' syntax. Values are + interpreted as yaml values, meaning that 'true' and 'false' will lead + to boolean values and numbers will be treated as numbers. Use quotes + if you want these to be treated as strings. If the value starts with + @, it is treated as a file, meaning that the contents of the file + will be loaded and treated as yaml. + --args-from-file stringArray Loads a yaml file and makes it available as arguments, meaning that + they will be available thought the global 'args' variable. + --context string Overrides the context name specified in the target. If the selected + target does not specify a context or the no-name target is used, + --context will override the currently active context. --git-cache-update-interval duration Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches. - --local-clusters existingdir DEPRECATED. Local clusters directory. Overrides the project from - .kluctl.yaml - --local-deployment existingdir DEPRECATED. Local deployment directory. Overrides the project from - .kluctl.yaml - --local-sealed-secrets existingdir DEPRECATED. Local sealed-secrets directory. Overrides the project - from .kluctl.yaml - --output-metadata string Specify the output path for the project metadata to be written to. + --local-git-override stringArray Specify a local git override in the form of + 'github.com:my-org/my-repo=/local/path/to/override'. This will cause + kluctl to not use git to clone for the specified repository but + instead use the local directory. This is useful in case you need to + test out changes in external git repositories without pushing them. + To only override a single branch of the repo, use + 'github.com:my-org/my-repo:my-branch=/local/path/to/override' -c, --project-config existingfile Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml - -b, --project-ref string Git ref of the kluctl project. Only used when --project-url was given. - -p, --project-url string Git url of the kluctl project. If not specified, the current - directory will be used instead of a remote Git project -t, --target string Target name to run command for. Target must exist in .kluctl.yaml. + -T, --target-name-override string Overrides the target name. If -t is used at the same time, then the + target will be looked up based on -t and then renamed to the + value of -T. If no target is specified via -t, then the no-name + target is renamed to the value of -T. --timeout duration Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness. (default 10m0s) diff --git a/docs/reference/commands/list-images.md b/docs/reference/commands/list-images.md index 5d59e6949..e9deee305 100644 --- a/docs/reference/commands/list-images.md +++ b/docs/reference/commands/list-images.md @@ -17,7 +17,7 @@ Renders the target and outputs all images used via 'images.get_image(...) The result is a compatible with yaml files expected by --fixed-images-file. If fixed images ('-f/--fixed-image') are provided, these are also taken into account, -as described in for the deploy command. +as described in the deploy command. @@ -33,6 +33,8 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. + --offline-kubernetes Run list-images in offline mode, meaning that it will not try to connect the + target cluster -o, --output stringArray Specify output target file. Can be specified multiple times --render-output-dir string Specifies the target directory to render the project into. If omitted, a temporary directory is used. From 545cd85e93765756729b1736b945298cafe0813b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 17:43:35 +0200 Subject: [PATCH 0451/2268] ci: Run markdown-link-check on markdown files --- .github/workflows/check-links-cron.yml | 19 +++++++++++++++++++ .github/workflows/tests.yml | 11 +++++++++++ Makefile | 3 +++ 3 files changed, 33 insertions(+) create mode 100644 .github/workflows/check-links-cron.yml diff --git a/.github/workflows/check-links-cron.yml b/.github/workflows/check-links-cron.yml new file mode 100644 index 000000000..117b11289 --- /dev/null +++ b/.github/workflows/check-links-cron.yml @@ -0,0 +1,19 @@ +name: Check Markdown links + +on: + push: + branches: + - main + schedule: + # Run everyday at 9:00 AM (See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07) + - cron: "0 9 * * *" + +jobs: + markdown-link-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + folder-path: 'docs, install' + file-path: './README.md' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6e24c9004..4ce1e0de4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,6 +7,17 @@ on: - main jobs: + pre-checks: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Check links on changed files + run: | + make markdown-link-check + build: runs-on: ubuntu-20.04 steps: diff --git a/Makefile b/Makefile index 92f691a82..6dee972ce 100644 --- a/Makefile +++ b/Makefile @@ -122,6 +122,9 @@ endif replace-commands-help: ## Replace commands help in docs $(GOCMD) run ./internal/replace-commands-help --docs-dir ./docs/reference/commands +markdown-link-check: ## Check markdown files for dead links + find . -name '*.md' | xargs docker run -v ${PWD}:/tmp:ro --rm -i -w /tmp ghcr.io/tcort/markdown-link-check:stable + ## Release: version: ## Write next version into version file $(GOCMD) install github.com/bvieira/sv4git/v2/cmd/git-sv@v2.7.0 From 4b1a44511d1c4938f82bebdb467ba94b8a4e56e4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 18:05:08 +0200 Subject: [PATCH 0452/2268] ci: Move replace-commands-help into docs-check job --- .github/workflows/tests.yml | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ce1e0de4..3dd57cb2a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,16 +7,36 @@ on: - main jobs: - pre-checks: + docs-checks: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 + - uses: actions/setup-go@v2 + with: + go-version: '1.19' + - uses: actions/cache@v2 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: docs-check-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} + restore-keys: | + docs-check-go-${{ runner.os }}- - name: Check links on changed files run: | make markdown-link-check + - name: Verify commands help is up-to-date + run: | + make replace-commands-help + if [ ! -z "$(git status --porcelain)" ]; then + echo "replace-commands-help must be invoked and the result committed" + git status + git diff + exit 1 + fi build: runs-on: ubuntu-20.04 @@ -51,15 +71,6 @@ jobs: run: | make test-e2e-build GOARCH=amd64 GOOS=windows mv ./bin/kluctl-e2e.exe ./bin/kluctl-e2e-windows-amd64.exe - - name: Verify commands help is up-to-date - run: | - make replace-commands-help - if [ ! -z "$(git status --porcelain)" ]; then - echo "replace-commands-help must be invoked and the result committed" - git status - git diff - exit 1 - fi - name: Upload binaries uses: actions/upload-artifact@v2 with: From c2c2e81ac7c1cd4dd9baa7acef2309c899640fd4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 17:57:00 +0200 Subject: [PATCH 0453/2268] docs: Fix dead links --- docs/README.md | 2 +- docs/concepts.md | 4 ++-- docs/reference/commands/common-arguments.md | 2 +- docs/reference/commands/delete.md | 8 ++++---- docs/reference/commands/deploy.md | 10 +++++----- docs/reference/commands/diff.md | 8 ++++---- docs/reference/commands/environment-variables.md | 2 +- docs/reference/commands/helm-pull.md | 4 ++-- docs/reference/commands/helm-update.md | 2 +- docs/reference/commands/list-images.md | 6 +++--- docs/reference/commands/poke-images.md | 6 +++--- docs/reference/commands/prune.md | 8 ++++---- docs/reference/commands/render.md | 4 ++-- docs/reference/commands/seal.md | 4 ++-- docs/reference/commands/validate.md | 4 ++-- .../deployments/annotations/all-resources.md | 8 ++++---- docs/reference/deployments/annotations/hooks.md | 4 ++-- .../deployments/annotations/kustomization.md | 2 +- .../deployments/annotations/validation.md | 2 +- docs/reference/deployments/deployment-yml.md | 4 ++-- docs/reference/deployments/helm.md | 12 ++++++------ docs/reference/deployments/images.md | 2 +- docs/reference/kluctl-project/README.md | 2 +- .../kluctl-project/secrets-config/README.md | 2 +- docs/reference/kluctl-project/targets/README.md | 16 ++++++++-------- docs/reference/sealed-secrets.md | 6 +++--- docs/reference/templating/functions.md | 2 +- .../reference/templating/predefined-variables.md | 8 ++++---- docs/reference/templating/variable-sources.md | 4 ++-- 29 files changed, 74 insertions(+), 74 deletions(-) diff --git a/docs/README.md b/docs/README.md index f9311fca4..1ccac0e55 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,7 @@ menu: 1. [Get Started](./get-started.md) 2. [Core Concepts](./concepts.md) -3. [Installation](./installation) +3. [Installation](./installation.md) 4. [Philosophy](./philosophy.md) 5. [History](./history.md) 6. [Reference](./reference) diff --git a/docs/concepts.md b/docs/concepts.md index 946f69c0f..2967356fe 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -41,11 +41,11 @@ control structures (if/else, for loops, ...). ## Secrets Secrets are loaded from [external sources](./reference/kluctl-project) and are only available -while [sealing](./reference/sealed-secrets). After the sealing process, only the public-key encrypted +while [sealing](./reference/sealed-secrets.md). After the sealing process, only the public-key encrypted sealed secrets are available. ## Sealed Secrets -[Sealed Secrets](./reference/sealed-secrets) are based on +[Sealed Secrets](./reference/sealed-secrets.md) are based on [Bitnami's sealed-secrets controller](https://github.com/bitnami-labs/sealed-secrets). Kluctl offers integration of sealed secrets through the `seal` command. Kluctl allows managing multiple sets of sealed secrets for multiple targets. diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index 235b823e1..aedafc415 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -76,7 +76,7 @@ Project arguments: ## Image arguments These arguments are available on some target based commands. -They control image versions requested by `images.get_image(...)` [calls](../deployments/images#imagesget_image). +They control image versions requested by `images.get_image(...)` [calls](../deployments/images.md#imagesget_image). ``` diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index 33e8e2acd..7dd91db7c 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -24,9 +24,9 @@ take the local target/state into account! ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) -1. [image arguments](./common-arguments#image-arguments) -1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) +1. [project arguments](./common-arguments.md#project-arguments) +1. [image arguments](./common-arguments.md#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) In addition, the following arguments are available: @@ -46,4 +46,4 @@ Misc arguments: ``` -They have the same meaning as described in [deploy](#deploy). +They have the same meaning as described in [deploy](./deploy.md). diff --git a/docs/reference/commands/deploy.md b/docs/reference/commands/deploy.md index 65e472674..73ce93477 100644 --- a/docs/reference/commands/deploy.md +++ b/docs/reference/commands/deploy.md @@ -22,9 +22,9 @@ It will also output a list of prunable objects (without actually deleting them). ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) -1. [image arguments](./common-arguments#image-arguments) -1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) +1. [project arguments](./common-arguments.md#project-arguments) +1. [image arguments](./common-arguments.md#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) In addition, the following arguments are available: @@ -54,9 +54,9 @@ Misc arguments: ### --force-apply -kluctl implements deployments via [server-side apply](https://kubernetes.io/reference/using-api/server-side-apply/) +kluctl implements deployments via [server-side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/) and a custom automatic conflict resolution algorithm. This algurithm is an automatic implementation of the -"[Don't overwrite value, give up management claim](https://kubernetes.io/reference/using-api/server-side-apply/#conflicts)" +"[Don't overwrite value, give up management claim](https://kubernetes.io/docs/reference/using-api/server-side-apply/#conflicts)" method. It should work in most cases, but might still fail. In case of such failure, you can use `--force-apply` to use the "Overwrite value, become sole manager" strategy instead. diff --git a/docs/reference/commands/diff.md b/docs/reference/commands/diff.md index d54d6091b..320833839 100644 --- a/docs/reference/commands/diff.md +++ b/docs/reference/commands/diff.md @@ -23,9 +23,9 @@ After the diff is performed, the command will also search for prunable objects a ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) -1. [image arguments](./common-arguments#image-arguments) -1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) +1. [project arguments](./common-arguments.md#project-arguments) +1. [image arguments](./common-arguments.md#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) In addition, the following arguments are available: @@ -50,4 +50,4 @@ Misc arguments: ``` -`--force-apply` and `--replace-on-error` have the same meaning as in [deploy](#deploy). +`--force-apply` and `--replace-on-error` have the same meaning as in [deploy](./deploy.md). diff --git a/docs/reference/commands/environment-variables.md b/docs/reference/commands/environment-variables.md index 2247be7cf..62f775ed6 100644 --- a/docs/reference/commands/environment-variables.md +++ b/docs/reference/commands/environment-variables.md @@ -20,6 +20,6 @@ underscores. As an example, `--dry-run` can also be specified with the environme ## Additional environment variables A few additional environment variables are supported which do not belong to an option/argument. These are: -1. `KLUCTL_REGISTRY__HOST`, `KLUCTL_REGISTRY__USERNAME`, and so on. See [registries](../deployments/images#supported-image-registries-and-authentication) for details. +1. `KLUCTL_REGISTRY__HOST`, `KLUCTL_REGISTRY__USERNAME`, and so on. See [registries](../deployments/images.md#supported-image-registries-and-authentication) for details. 2. `KLUCTL_GIT__HOST`, `KLUCTL_GIT__USERNAME`, and so on. 3. `KLUCTL_SSH_DISABLE_STRICT_HOST_KEY_CHECKING`. Disable ssh host key checking when accessing git repositories. diff --git a/docs/reference/commands/helm-pull.md b/docs/reference/commands/helm-pull.md index 68d3871b4..8cc7ee552 100644 --- a/docs/reference/commands/helm-pull.md +++ b/docs/reference/commands/helm-pull.md @@ -20,8 +20,8 @@ pulling is only needed when really required (e.g. when the chart version changes -See [helm-integration](../deployments/helm) for more details. +See [helm-integration](../deployments/helm.md) for more details. ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) (except `-a`) +1. [project arguments](./common-arguments.md#project-arguments) (except `-a`) diff --git a/docs/reference/commands/helm-update.md b/docs/reference/commands/helm-update.md index d719c76a0..f9e679928 100644 --- a/docs/reference/commands/helm-update.md +++ b/docs/reference/commands/helm-update.md @@ -20,7 +20,7 @@ Optionally performs the actual upgrade and/or add a commit to version control. ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) (except `-a`) +1. [project arguments](./common-arguments.md#project-arguments) (except `-a`) In addition, the following arguments are available: diff --git a/docs/reference/commands/list-images.md b/docs/reference/commands/list-images.md index e9deee305..e4f6f7e2f 100644 --- a/docs/reference/commands/list-images.md +++ b/docs/reference/commands/list-images.md @@ -23,9 +23,9 @@ as described in the deploy command. ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) -1. [image arguments](./common-arguments#image-arguments) -1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) +1. [project arguments](./common-arguments.md#project-arguments) +1. [image arguments](./common-arguments.md#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/poke-images.md b/docs/reference/commands/poke-images.md index b5279c256..8c3ba5ca8 100644 --- a/docs/reference/commands/poke-images.md +++ b/docs/reference/commands/poke-images.md @@ -22,9 +22,9 @@ replaced ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) -1. [image arguments](./common-arguments#image-arguments) -1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) +1. [project arguments](./common-arguments.md#project-arguments) +1. [image arguments](./common-arguments.md#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/prune.md b/docs/reference/commands/prune.md index 86161c3ea..c33800f8c 100644 --- a/docs/reference/commands/prune.md +++ b/docs/reference/commands/prune.md @@ -18,9 +18,9 @@ Searches the target cluster for prunable objects and deletes them ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) -1. [image arguments](./common-arguments#image-arguments) -1. [inclusion/exclusion arguments](./common-arguments#inclusionexclusion-arguments) +1. [project arguments](./common-arguments.md#project-arguments) +1. [image arguments](./common-arguments.md#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) In addition, the following arguments are available: @@ -39,4 +39,4 @@ Misc arguments: ``` -They have the same meaning as described in [deploy](#deploy). +They have the same meaning as described in [deploy](./prune.md). diff --git a/docs/reference/commands/render.md b/docs/reference/commands/render.md index 05a48e59c..2fb25364c 100644 --- a/docs/reference/commands/render.md +++ b/docs/reference/commands/render.md @@ -21,8 +21,8 @@ a temporary directory or a specified directory. ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) -1. [image arguments](./common-arguments#image-arguments) +1. [project arguments](./common-arguments.md#project-arguments) +1. [image arguments](./common-arguments.md#image-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/seal.md b/docs/reference/commands/seal.md index 88edc02c7..4ec5fe457 100644 --- a/docs/reference/commands/seal.md +++ b/docs/reference/commands/seal.md @@ -23,11 +23,11 @@ If no '--target' is specified, sealing is performed for all targets. -See [sealed-secrets](../sealed-secrets) for more details. +See [sealed-secrets](../sealed-secrets.md) for more details. ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) (except `-a`) +1. [project arguments](./common-arguments.md#project-arguments) (except `-a`) In addition, the following arguments are available: diff --git a/docs/reference/commands/validate.md b/docs/reference/commands/validate.md index d904c98fb..987592774 100644 --- a/docs/reference/commands/validate.md +++ b/docs/reference/commands/validate.md @@ -22,8 +22,8 @@ TODO: This needs to be better documented! ## Arguments The following sets of arguments are available: -1. [project arguments](./common-arguments#project-arguments) -1. [image arguments](./common-arguments#image-arguments) +1. [project arguments](./common-arguments.md#project-arguments) +1. [image arguments](./common-arguments.md#image-arguments) In addition, the following arguments are available: diff --git a/docs/reference/deployments/annotations/all-resources.md b/docs/reference/deployments/annotations/all-resources.md index e1aa989d6..aa517a46c 100644 --- a/docs/reference/deployments/annotations/all-resources.md +++ b/docs/reference/deployments/annotations/all-resources.md @@ -37,12 +37,12 @@ If more than one field needs to be specified, add `-xxx` to the annotation key, The following annotations control how delete/prune is behaving. ### kluctl.io/skip-delete -If set to "true", the annotated resource will not be deleted when [delete](../../commands/delete) or -[prune](../../commands/prune) is called. +If set to "true", the annotated resource will not be deleted when [delete](../../commands/delete.md) or +[prune](../../commands/prune.md) is called. ### kluctl.io/skip-delete-if-tags -If set to "true", the annotated resource will not be deleted when [delete](../../commands/delete) or -[prune](../../commands/prune) is called and inclusion/exclusion tags are used at the same time. +If set to "true", the annotated resource will not be deleted when [delete](../../commands/delete.md) or +[prune](../../commands/prune.md) is called and inclusion/exclusion tags are used at the same time. This tag is especially useful and required on resources that would otherwise cause cascaded deletions of resources that do not match the specified inclusion/exclusion tags. Namespaces are the most prominent example of such resources, as diff --git a/docs/reference/deployments/annotations/hooks.md b/docs/reference/deployments/annotations/hooks.md index 1b033bf9b..bcc81b6e1 100644 --- a/docs/reference/deployments/annotations/hooks.md +++ b/docs/reference/deployments/annotations/hooks.md @@ -13,10 +13,10 @@ description: > The following annotations control hook execution -See [hooks](../../deployments/hooks) for more details. +See [hooks](../../deployments/hooks.md) for more details. ### kluctl.io/hook -Declares a resource to be a hook, which is deployed/executed as described in [hooks](../../deployments/hooks). The value of the +Declares a resource to be a hook, which is deployed/executed as described in [hooks](../../deployments/hooks.md). The value of the annotation determines when the hook is deployed/executed. ### kluctl.io/hook-weight diff --git a/docs/reference/deployments/annotations/kustomization.md b/docs/reference/deployments/annotations/kustomization.md index f0312940c..6a740fc18 100644 --- a/docs/reference/deployments/annotations/kustomization.md +++ b/docs/reference/deployments/annotations/kustomization.md @@ -35,4 +35,4 @@ same effect as [barrier](../../deployments/deployment-yml.md#barriers) from depl ### kluctl.io/wait-readiness If set to `true`, kluctl will wait for readiness of all objects from this kustomization project. Readiness is defined -the same as in [hook readiness](../../deployments/readiness). +the same as in [hook readiness](../../deployments/readiness.md). diff --git a/docs/reference/deployments/annotations/validation.md b/docs/reference/deployments/annotations/validation.md index a36fcf2e4..a6c35372c 100644 --- a/docs/reference/deployments/annotations/validation.md +++ b/docs/reference/deployments/annotations/validation.md @@ -11,7 +11,7 @@ description: > # Validation -The following annotations influence the [validate](../../commands/validate) command. +The following annotations influence the [validate](../../commands/validate.md) command. ### validate-result.kluctl.io/xxx If this annotation is found on a resource that is checked while validation, the key and the value of the annotation diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index 0ca5cb75b..722a81e2a 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -36,7 +36,7 @@ The following sub-chapters describe the available fields in the `deployment.yaml ## sealedSecrets `sealedSecrets` configures how sealed secrets are stored while sealing and located while rendering. -See [Sealed Secrets](../sealed-secrets#outputpattern-and-location-of-stored-sealed-secrets) +See [Sealed Secrets](../sealed-secrets.md#outputpattern-and-location-of-stored-sealed-secrets) for details. ## deployments @@ -307,5 +307,5 @@ valid [JSON Path](https://goessner.net/articles/JsonPath/). `fieldPath` may also The JSON Path implementation used in kluctl has extended support for wildcards in field names, allowing you to also specify paths like `metadata.labels.my-prefix-*`. -As an alternative, [annotations](./annotations/all-resources#control-diff-behavior) can be used to control +As an alternative, [annotations](./annotations/all-resources.md#control-diff-behavior) can be used to control diff behavior of individual resources. diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index aab1fdbf0..75016f9e8 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -25,7 +25,7 @@ hands over the rendered yaml to [kustomize](https://kustomize.io/). Rendering is `helm-values.yaml`, which contains the necessary values to configure the Helm Chart. The resulting rendered yaml is then referred by your `kustomization.yaml`, from which point on the -[kustomize integration](../sealed-secrets#outputpattern-and-location-of-stored-sealed-secrets) +[kustomize integration](../sealed-secrets.md#outputpattern-and-location-of-stored-sealed-secrets) takes over. This means, that you can perform all desired customization (patches, namespace override, ...) as if you provided your own resources via yaml files. @@ -99,7 +99,7 @@ The name of the chart that can be found in the repository. The version of the chart. ### skipUpdate -Skip this Helm Chart when the [helm-update](../commands/helm-update) command is called. +Skip this Helm Chart when the [helm-update](../commands/helm-update.md) command is called. If omitted, defaults to `false`. ### releaseName @@ -125,8 +125,8 @@ read the documentation of the used Helm Charts for details on what is supported. ## Updates to helm-charts In case a Helm Chart needs to be updated, you can either do this manually by replacing the [chartVersion](#chartversion) -value in `helm-chart.yaml` and the calling the [helm-pull](../commands/helm-pull) command or by simply invoking -[helm-update](../commands/helm-update) with `--upgrade` and/or `--commit` being set. +value in `helm-chart.yaml` and the calling the [helm-pull](../commands/helm-pull.md) command or by simply invoking +[helm-update](../commands/helm-update.md) with `--upgrade` and/or `--commit` being set. ## Private Chart Repositories It is also possible to use private chart repositories. There are currently two options to provide Helm Repository @@ -139,7 +139,7 @@ added the repository to Helm. The same method can be used for client certificate in `helm repo add`). ### Use the --username/--password arguments in `kluctl helm-pull` -See the [helm-pull command](../commands/helm-pull). You can control repository credentials +See the [helm-pull command](../commands/helm-pull.md). You can control repository credentials via `--username`, `--password` and `--key-file`. Each argument must be in the form `credentialsId:value`, where the `credentialsId` must match the id specified in the `helm-chart.yaml`. Example: @@ -160,7 +160,7 @@ When credentialsId is specified, Kluctl will require you to specify `--username= Multiple Helm Charts can use the same `credentialsId`. Environment variables can also be used instead of arguments. See -[Environment Variables](../commands/environment-variables) for details. +[Environment Variables](../commands/environment-variables.md) for details. ## Templating diff --git a/docs/reference/deployments/images.md b/docs/reference/deployments/images.md index 3b88af2bb..b6270cf7d 100644 --- a/docs/reference/deployments/images.md +++ b/docs/reference/deployments/images.md @@ -89,7 +89,7 @@ The described `images.get_image` logic however leads to a loosely defined state might be fine in a CI/CD environment, but might be undesired when deploying to production. In that case, it might be desirable to explicitly define which versions need to be deployed. -To achieve this, you can use the `-F FIXED_IMAGE` [argument](../commands/common-arguments#image-arguments). +To achieve this, you can use the `-F FIXED_IMAGE` [argument](../commands/common-arguments.md#image-arguments). `FIXED_IMAGE` must be in the form of `-F image<:namespace:deployment:container>=result`. For example, to pin the image `registry.gitlab.com/my-group/my-project` to the tag `1.1.2` you'd have to specify `-F registry.gitlab.com/my-group/my-project=registry.gitlab.com/my-group/my-project:1.1.2`. diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index 556e1313f..472a6f80d 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -13,7 +13,7 @@ description: > The `.kluctl.yaml` is the central configuration and entry point for your deployments. It defines where the actual [deployment project](../deployments) is located, -where [sealed secrets](../sealed-secrets) and unencrypted secrets are localed and which targets are available to +where [sealed secrets](../sealed-secrets.md) and unencrypted secrets are localed and which targets are available to invoke [commands](../commands) on. ## Example diff --git a/docs/reference/kluctl-project/secrets-config/README.md b/docs/reference/kluctl-project/secrets-config/README.md index 81207967a..8e1387af2 100644 --- a/docs/reference/kluctl-project/secrets-config/README.md +++ b/docs/reference/kluctl-project/secrets-config/README.md @@ -35,7 +35,7 @@ This field specifies the name of the secret set. The name can be used in targets ### vars A list of variables sources. Check the documentation of -[variables sources](../../templating/variable-sources) for details. +[variables sources](../../templating/variable-sources.md) for details. Each variables source must have a root dictionary with the name `secrets` and all the actual secret values below that dictionary. Every other root key will be ignored. diff --git a/docs/reference/kluctl-project/targets/README.md b/docs/reference/kluctl-project/targets/README.md index ce1a19f3c..46eba7293 100644 --- a/docs/reference/kluctl-project/targets/README.md +++ b/docs/reference/kluctl-project/targets/README.md @@ -13,7 +13,7 @@ description: > Specifies a list of targets for which commands can be invoked. A target puts together environment/target specific configuration and the target cluster. Multiple targets can exist which target the same cluster but with differing -configuration (via `args`). Target entries also specifies which secrets to use while [sealing](../../sealed-secrets). +configuration (via `args`). Target entries also specifies which secrets to use while [sealing](../../sealed-secrets.md). Each value found in the target definition is rendered with a simple Jinja2 context that only contains the target itself. The rendering process is retried 10 times until it finally succeeds, allowing you to reference @@ -45,7 +45,7 @@ The following fields are allowed per target: ## name This field specifies the name of the target. The name must be unique. It is referred in all commands via the -[-t](../../commands/common-arguments) option. +[-t](../../commands/common-arguments.md) option. ## context This field specifies the kubectl context of the target cluster. The context must exist in the currently active kubeconfig. @@ -53,9 +53,9 @@ If this field is omitted, Kluctl will always use the currently active context. ## args This fields specifies a map of arguments to be passed to the deployment project when it is rendered. Allowed argument names -are configured via [deployment args](../../deployments/deployment-yml#args). +are configured via [deployment args](../../deployments/deployment-yml.md#args). -The arguments specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets#args) +The arguments specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets.md#args) have higher priority. ## dynamicArgs @@ -65,17 +65,17 @@ arguments are passed with `-a arg_name=arg_value` when for example calling `kluc Each entry has the following fields: ## images -This field specifies a list of fixed images to be used by [`images.get_image(...)`](../../deployments/images#imagesget_image). -The format is identical to the [fixed images file](https://kluctl.io/docs/reference/deployments/images/#fixed-images-via-a-yaml-file). +This field specifies a list of fixed images to be used by [`images.get_image(...)`](../../deployments/images.md#imagesget_image). +The format is identical to the [fixed images file](../../deployments/images.md#fixed-images-via-a-yaml-file). -The fixed images specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets#images) +The fixed images specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets.md#images) have higher priority. ### name The name of the argument. ## sealingConfig -This field configures how sealing is performed when the [seal command](../../commands/seal) is invoked for this target. +This field configures how sealing is performed when the [seal command](../../commands/seal.md) is invoked for this target. It has the following form: ```yaml diff --git a/docs/reference/sealed-secrets.md b/docs/reference/sealed-secrets.md index 81dd38c84..cfd40a2d1 100644 --- a/docs/reference/sealed-secrets.md +++ b/docs/reference/sealed-secrets.md @@ -29,7 +29,7 @@ public certificate via `certFile` in the targets [sealingConfig](./kluctl-projec ## Sealing of .sealme files -Sealing is done via the [seal command](./commands/seal). It must be done before the actual +Sealing is done via the [seal command](./commands/seal.md). It must be done before the actual deployment is performed. The `seal` command recursively searches for files that end with `.sealme`, renders them with the @@ -111,8 +111,8 @@ with: ## Content Hashes and re-sealing Sealed secrets are stored together with hashes of all individual secret entries. These hashes are then used to avoid -unnecessary re-sealing in future [seal](./commands/seal) invocations. If you want to force re-sealing, use the -[--force-reseal](./commands/seal) option. +unnecessary re-sealing in future [seal](./commands/seal.md) invocations. If you want to force re-sealing, use the +[--force-reseal](./commands/seal.md) option. Hashing of secrets is done with bcrypt and the cluster id as salt. The cluster id is currently defined as the sha256 hash of the cluster CA certificate. This will cause re-sealing of all secrets in case a cluster is set up from scratch diff --git a/docs/reference/templating/functions.md b/docs/reference/templating/functions.md index db0eac48e..0ce224c56 100644 --- a/docs/reference/templating/functions.md +++ b/docs/reference/templating/functions.md @@ -44,7 +44,7 @@ data: ### get_var(field_path, default) Convenience method to navigate through the current context variables via a [JSON Path](https://goessner.net/articles/JsonPath/). Let's assume you currently have these variables defined -(e.g. via [vars](../deployments/deployment-yml#vars-deployment-project)): +(e.g. via [vars](../deployments/deployment-yml.md#vars-deployment-project)): ```yaml my: deep: diff --git a/docs/reference/templating/predefined-variables.md b/docs/reference/templating/predefined-variables.md index 5c4e88645..951226f57 100644 --- a/docs/reference/templating/predefined-variables.md +++ b/docs/reference/templating/predefined-variables.md @@ -15,18 +15,18 @@ There are multiple variables available which are pre-defined by kluctl. These ar ### args This is a dictionary of arguments given via command line. It contains every argument defined in -[deployment args](../deployments/deployment-yml#args). +[deployment args](../deployments/deployment-yml.md#args). ### target This is the target definition of the currently processed target. It contains all values found in the [target definition](../kluctl-project/targets), for example `target.name`. ### images -This global object provides the dynamic images features described in [images](../deployments/images). +This global object provides the dynamic images features described in [images](../deployments/images.md). ### version -This global object defines latest version filters for `images.get_image(...)`. See [images](../deployments/images) for details. +This global object defines latest version filters for `images.get_image(...)`. See [images](../deployments/images.md) for details. ### secrets -This global object is only available while [sealing](../sealed-secrets) and contains the loaded +This global object is only available while [sealing](../sealed-secrets.md) and contains the loaded secrets defined via the currently sealed target. diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index c81465380..f2391f2a6 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -14,10 +14,10 @@ description: > There are multiple places in deployment projects (deployment.yaml) where additional variables can be loaded into future Jinja2 contexts. -The first place where vars can be specified is the deployment root, as documented [here](../deployments/deployment-yml#vars-deployment-project). +The first place where vars can be specified is the deployment root, as documented [here](../deployments/deployment-yml.md#vars-deployment-project). These vars are visible for all deployments inside the deployment project, including sub-deployments from includes. -The second place to specify variables is in the deployment items, as documented [here](../deployments/deployment-yml#vars-deployment-item). +The second place to specify variables is in the deployment items, as documented [here](../deployments/deployment-yml.md#vars-deployment-item). The variables loaded for each entry in `vars` are not available inside the `deployment.yaml` file itself. However, each entry in `vars` can use all variables defined before that specific entry is processed. Consider the From bda3a5dd9433263ef53dcd7889eb4a2932e957ab Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 19:45:46 +0200 Subject: [PATCH 0454/2268] docs: Fix issues related to hugo sync --- docs/get-started.md | 2 +- docs/reference/README.md | 2 +- docs/reference/deployments/README.md | 4 ++-- docs/reference/deployments/annotations/README.md | 2 +- docs/reference/deployments/deployment-yml.md | 4 ++-- docs/reference/kluctl-project/README.md | 2 +- docs/reference/templating/README.md | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/get-started.md b/docs/get-started.md index 711235908..e1cc96cce 100644 --- a/docs/get-started.md +++ b/docs/get-started.md @@ -1,7 +1,7 @@ -# Table of Contents +## Table of Contents 1. [.kluctl.yaml](./kluctl-project) 2. [Deployments](./deployments) diff --git a/docs/reference/deployments/README.md b/docs/reference/deployments/README.md index d4e211174..81af0afa7 100644 --- a/docs/reference/deployments/README.md +++ b/docs/reference/deployments/README.md @@ -9,9 +9,9 @@ description: > --- --> -# Table of Contents +## Table of Contents -1. [deployment.yaml](./deployment-yml.md) +1. [Deployments](./deployment-yml.md) 2. [Kustomize Integration](./kustomize.md) 3. [Container Images](./images.md) 4. [Helm Integration](./helm.md) diff --git a/docs/reference/deployments/annotations/README.md b/docs/reference/deployments/annotations/README.md index 398001649..3c38ab58b 100644 --- a/docs/reference/deployments/annotations/README.md +++ b/docs/reference/deployments/annotations/README.md @@ -9,7 +9,7 @@ description: > --- --> -# Table of Contents +## Table of Contents 1. [All Resources](./all-resources.md) 2. [Hooks](./hooks.md) diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index 722a81e2a..1d5914919 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -1,8 +1,8 @@ -# Table of Contents +## Table of Contents 1. [Predefined Variables](./predefined-variables.md) 2. [Variable Sources](./variable-sources.md) From 5f1589719a4929321ea8d0ef1cc0374ac7ade735 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 21 Oct 2022 22:28:41 +0200 Subject: [PATCH 0455/2268] docs: Remove .md from raw-html links --- docs/reference/deployments/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/reference/deployments/README.md b/docs/reference/deployments/README.md index 81af0afa7..ede5bdaa1 100644 --- a/docs/reference/deployments/README.md +++ b/docs/reference/deployments/README.md @@ -37,9 +37,10 @@ precisely what is deployed in the future. Some visualized files/directories have links attached, follow them to get more information. +
 -- project-dir/
-   |-- deployment.yaml
+   |-- deployment.yaml
    |-- .gitignore
    |-- kustomize-deployment1/
    |   |-- kustomization.yaml
@@ -56,7 +57,7 @@ Some visualized files/directories have links attached, follow them to get more i
    |   |   |-- resource2.yaml
    |   |   |-- patch1.yaml
    |   |   `-- ...
-   |   |-- kustomize-with-helm-deployment/
+   |   |-- kustomize-with-helm-deployment/
    |   |   |-- charts/
    |   |   |   `-- ...
    |   |   |-- kustomization.yaml
@@ -69,6 +70,7 @@ Some visualized files/directories have links attached, follow them to get more i
    `-- sub-deployment/
        `-- ...
 
+ ## Order of deployments Deployments are done in parallel, meaning that there are usually no order guarantees. The only way to somehow control From bf052f3986131f642e7bd4cbbced2fcc210e67c9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 11:56:50 +0200 Subject: [PATCH 0456/2268] feat: Allow to specify default values for systemEnvVars --- docs/reference/templating/variable-sources.md | 12 +++++++++++ pkg/vars/vars_loader.go | 20 ++++++++++++++++--- pkg/vars/vars_loader_test.go | 8 ++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index f2391f2a6..b2ab642c9 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -218,3 +218,15 @@ vars: The above example will make 3 variables available: `var1`, `someDict.var2` and `someList[0].var3`, each having the values of the environment variables specified by the leaf values. + +All specified environment variables must be set before calling kluctl unless a default value is set. Default values +can be set by using the `ENV_VAR_NAME:default-value` form. + +Example: +```yaml +vars: +- systemEnvVars: + var1: ENV_VAR_NAME4:defaultValue +``` + +The above example will set the variable `var1` to `defaultValue` in case ENV_VAR_NAME4 is not set. diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 53f004f8f..1f37ae405 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -17,6 +17,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/apimachinery/pkg/runtime/schema" "os" + "strings" ) type usernamePassword struct { @@ -127,11 +128,24 @@ func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, if !ok { return fmt.Errorf("value at %s is not a string", it.KeyPath().ToJsonPath()) } - envValue, ok := os.LookupEnv(envName) - if !ok { + var defaultValue string + hasDefaultValue := false + if strings.IndexRune(envName, ':') != -1 { + s := strings.SplitN(envName, ":", 2) + envName = s[0] + defaultValue = s[1] + hasDefaultValue = true + } + envValueStr := "" + if v, ok := os.LookupEnv(envName); ok { + envValueStr = v + } else if hasDefaultValue { + envValueStr = defaultValue + } else { return fmt.Errorf("environment variable %s not found for %s", envName, it.KeyPath().ToJsonPath()) } - err := newVars.SetNestedField(envValue, it.KeyPath()...) + + err := newVars.SetNestedField(envValueStr, it.KeyPath()...) if err != nil { return fmt.Errorf("failed to set value for %s: %w", it.KeyPath().ToJsonPath(), err) } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 872b6a6c3..2845892a4 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -334,6 +334,8 @@ func TestVarsLoader_SystemEnv(t *testing.T) { "test3": map[string]interface{}{ "test4": "TEST4", }, + "test5": "TEST5:def", + "test6": "TEST1:def", }), }, nil, "") assert.NoError(t, err) @@ -346,6 +348,12 @@ func TestVarsLoader_SystemEnv(t *testing.T) { v, _, _ = vc.Vars.GetNestedString("test3", "test4") assert.Equal(t, "44", v) + + v, _, _ = vc.Vars.GetNestedString("test5") + assert.Equal(t, "def", v) + + v, _, _ = vc.Vars.GetNestedString("test6") + assert.Equal(t, "42", v) }) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { From 89f88cecdddaa6666b4a9ffbf86e0987543c0b91 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 12:07:29 +0200 Subject: [PATCH 0457/2268] feat: Treat systemEnvVars as yaml instead of plain string This is a potentially breakting change, as it will cause integers and booleans to not be treated as strings anymore. --- docs/reference/templating/variable-sources.md | 14 ++++++++++++++ pkg/vars/vars_loader.go | 8 +++++++- pkg/vars/vars_loader_test.go | 18 +++++++++--------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index b2ab642c9..2941e532b 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -230,3 +230,17 @@ vars: ``` The above example will set the variable `var1` to `defaultValue` in case ENV_VAR_NAME4 is not set. + +All values retrieved from environment variables (or specified as default values) will be treated as YAML, meaning that +integers and booleans will be treated as integers/booleans. If you want to enforce strings, encapsulate the values in +quotes. + +Example: +```yaml +vars: +- systemEnvVars: + var1: ENV_VAR_NAME5:'true' +``` + +The above example will treat `true` as a string instead of a boolean. When the environment variable is set outside +kluctl, it should also contain the quotes. Please note that your shell might require escaping to properly pass quotes. diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 1f37ae405..176c59d4e 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -145,7 +145,13 @@ func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, return fmt.Errorf("environment variable %s not found for %s", envName, it.KeyPath().ToJsonPath()) } - err := newVars.SetNestedField(envValueStr, it.KeyPath()...) + var envValue any + err := yaml.ReadYamlString(envValueStr, &envValue) + if err != nil { + return fmt.Errorf("failed to parse env value '%s': %w", envValueStr, err) + } + + err = newVars.SetNestedField(envValue, it.KeyPath()...) if err != nil { return fmt.Errorf("failed to set value for %s: %w", it.KeyPath().ToJsonPath(), err) } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 2845892a4..65027e369 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -323,7 +323,7 @@ func TestVarsLoader_K8sObjectLabels(t *testing.T) { func TestVarsLoader_SystemEnv(t *testing.T) { t.Setenv("TEST1", "42") - t.Setenv("TEST2", "43") + t.Setenv("TEST2", "'43'") t.Setenv("TEST4", "44") testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { @@ -340,20 +340,20 @@ func TestVarsLoader_SystemEnv(t *testing.T) { }, nil, "") assert.NoError(t, err) - v, _, _ := vc.Vars.GetNestedString("test1") - assert.Equal(t, "42", v) + v, _, _ := vc.Vars.GetNestedField("test1") + assert.Equal(t, 42, v) - v, _, _ = vc.Vars.GetNestedString("test2") + v, _, _ = vc.Vars.GetNestedField("test2") assert.Equal(t, "43", v) - v, _, _ = vc.Vars.GetNestedString("test3", "test4") - assert.Equal(t, "44", v) + v, _, _ = vc.Vars.GetNestedField("test3", "test4") + assert.Equal(t, 44, v) - v, _, _ = vc.Vars.GetNestedString("test5") + v, _, _ = vc.Vars.GetNestedField("test5") assert.Equal(t, "def", v) - v, _, _ = vc.Vars.GetNestedString("test6") - assert.Equal(t, "42", v) + v, _, _ = vc.Vars.GetNestedField("test6") + assert.Equal(t, 42, v) }) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { From 0bf1753d4a0e284e56a1a7e9cc7cd0b9bc26394d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 14:44:43 +0200 Subject: [PATCH 0458/2268] feat: Allow to pass CLI arguments multiple times via environment variables --- cmd/kluctl/commands/cobra_utils.go | 31 +++++++-- cmd/kluctl/commands/root.go | 4 -- .../commands/environment-variables.md | 3 + pkg/utils/env.go | 67 ++++++++++++++----- pkg/utils/env_test.go | 32 +++++++++ 5 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 pkg/utils/env_test.go diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index d495f58f6..af5644631 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -234,13 +234,36 @@ func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { return } v := viper.Get(flag.Name) + + var a []string if v != nil { - s, ok := v.(string) - if !ok { - retErr = append(retErr, fmt.Errorf("viper flag %s is not a string", flag.Name)) + if x, ok := v.(string); ok { + a = []string{x} + } else if x, ok := v.([]any); ok { + for _, y := range x { + s, ok := y.(string) + if !ok { + retErr = append(retErr, fmt.Errorf("viper flag %s has unexpected type", flag.Name)) + return + } + a = append(a, s) + } + } else { + retErr = append(retErr, fmt.Errorf("viper flag %s has unexpected type", flag.Name)) return } - err := flag.Value.Set(s) + } + + envName := strings.ReplaceAll(flag.Name, "-", "_") + envName = strings.ToUpper(envName) + envName = fmt.Sprintf("KLUCTL_%s", envName) + + for _, v := range utils.ParseEnvConfigList(envName) { + a = append(a, v) + } + + for _, x := range a { + err := flag.Value.Set(x) if err != nil { retErr = append(retErr, err) } diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index e85358e86..d864584f0 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -212,10 +212,6 @@ func initViper() { os.Exit(1) } } - - viper.SetEnvPrefix("kluctl") - viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - viper.AutomaticEnv() } func Execute() { diff --git a/docs/reference/commands/environment-variables.md b/docs/reference/commands/environment-variables.md index 62f775ed6..a76dcea9b 100644 --- a/docs/reference/commands/environment-variables.md +++ b/docs/reference/commands/environment-variables.md @@ -17,6 +17,9 @@ variables always start with `KLUCTL_` and end with the option/argument in upperc underscores. As an example, `--dry-run` can also be specified with the environment variable `KLUCTL_DRY_RUN=true`. +If an argument needs to be specified multiple times through environment variables, indexed can be appended to the +names of the environment variables, e.g. `KLUCTL_ARG_0=name1=value1` and `KLUCTL_ARG_1=name2=value2`. + ## Additional environment variables A few additional environment variables are supported which do not belong to an option/argument. These are: diff --git a/pkg/utils/env.go b/pkg/utils/env.go index 66ae280f9..b78f7dd39 100644 --- a/pkg/utils/env.go +++ b/pkg/utils/env.go @@ -19,11 +19,25 @@ func ParseEnvBool(name string, def bool) (bool, error) { return def, nil } -func ParseEnvConfigSets(prefix string) map[int]map[string]string { +func parseEnv(prefix string, withIndex bool, withSuffix bool) map[int]map[string]string { ret := make(map[int]map[string]string) - r := regexp.MustCompile(fmt.Sprintf(`^%s_(\d+)_(.*)$`, prefix)) - r2 := regexp.MustCompile(fmt.Sprintf(`^%s_(.*)$`, prefix)) + rs := prefix + curGroup := 1 + indexGroup := -1 + suffixGroup := -1 + if withIndex { + rs += `(_\d+)?` + indexGroup = curGroup + curGroup++ + } + if withSuffix { + rs += `_(.*)` + suffixGroup = curGroup + curGroup++ + } + rs = fmt.Sprintf("^%s$", rs) + r := regexp.MustCompile(rs) for _, e := range os.Environ() { eq := strings.Index(e, "=") @@ -34,26 +48,45 @@ func ParseEnvConfigSets(prefix string) map[int]map[string]string { v := e[eq+1:] idx := -1 - key := "" + suffix := "" m := r.FindStringSubmatch(n) - if m != nil { - x, _ := strconv.ParseInt(m[1], 10, 32) - idx = int(x) - key = m[2] - } else { - m = r2.FindStringSubmatch(n) - if m != nil { - key = m[1] - } + if m == nil { + continue } - if key != "" { - if _, ok := ret[idx]; !ok { - ret[idx] = make(map[string]string) + if withIndex { + idxStr := m[indexGroup] + if idxStr != "" { + idxStr = idxStr[1:] // remove leading _ + x, err := strconv.ParseInt(idxStr, 10, 32) + if err != nil { + continue + } + idx = int(x) } - ret[idx][key] = v } + if withSuffix { + suffix = m[suffixGroup] + } + + if _, ok := ret[idx]; !ok { + ret[idx] = map[string]string{} + } + ret[idx][suffix] = v + } + return ret +} + +func ParseEnvConfigSets(prefix string) map[int]map[string]string { + return parseEnv(prefix, true, true) +} + +func ParseEnvConfigList(prefix string) map[int]string { + ret := make(map[int]string) + + for idx, m := range parseEnv(prefix, true, false) { + ret[idx] = m[""] } return ret } diff --git a/pkg/utils/env_test.go b/pkg/utils/env_test.go new file mode 100644 index 000000000..2da463533 --- /dev/null +++ b/pkg/utils/env_test.go @@ -0,0 +1,32 @@ +package utils + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestParseEnvConfigSets_Prefixes(t *testing.T) { + t.Setenv("PREFIX_A", "a") + t.Setenv("PREFIX_B", "b") + t.Setenv("PREFIX2_C", "c") + assert.Equal(t, map[int]map[string]string{}, ParseEnvConfigSets("DUMMY")) + assert.Equal(t, map[int]map[string]string{-1: {"A": "a", "B": "b"}}, ParseEnvConfigSets("PREFIX")) + assert.Equal(t, map[int]map[string]string{-1: {"C": "c"}}, ParseEnvConfigSets("PREFIX2")) +} + +func TestParseEnvConfigSets_Indexes(t *testing.T) { + t.Setenv("PREFIX_A", "a") + t.Setenv("PREFIX_0_A", "a0") + t.Setenv("PREFIX_0_B", "b0") + t.Setenv("PREFIX_1_A", "a1") + assert.Equal(t, map[int]map[string]string{-1: {"A": "a"}, 0: {"A": "a0", "B": "b0"}, 1: {"A": "a1"}}, ParseEnvConfigSets("PREFIX")) +} + +func TestParseEnvConfigList(t *testing.T) { + t.Setenv("PREFIX", "a") + assert.Equal(t, map[int]string{-1: "a"}, ParseEnvConfigList("PREFIX")) + + t.Setenv("PREFIX_0", "b") + t.Setenv("PREFIX_1", "c") + assert.Equal(t, map[int]string{-1: "a", 0: "b", 1: "c"}, ParseEnvConfigList("PREFIX")) +} From 076093234143e97cfabdfdb70df32c62acfb8a2d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 15:08:13 +0200 Subject: [PATCH 0459/2268] tests: Test passing multiple args via environment variables --- e2e/args_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/e2e/args_test.go b/e2e/args_test.go index b606df688..df588a8a6 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -123,3 +123,41 @@ d: assertNestedFieldEquals(t, cm, "c2", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "d4"}}`, "data", "d") } + +func TestArgsFromEnv(t *testing.T) { + t.Setenv("KLUCTL_ARG", "a=a") + t.Setenv("KLUCTL_ARG_1", "b=b") + t.Setenv("KLUCTL_ARG_2", `c={"nested":{"nested2":"c"}}`) + t.Setenv("KLUCTL_ARG_3", "d=true") + t.Setenv("KLUCTL_ARG_4", "e='true'") + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k, "args-from-envs") + defer p.cleanup() + + createNamespace(t, k, p.projectName) + + p.updateTarget("test", func(target *uo.UnstructuredObject) { + }) + + addConfigMapDeployment(p, "cm", map[string]string{ + "a": `{{ args.a }}`, + "b": `{{ args.b }}`, + "c": `{{ args.c | to_json }}`, + "d": `{{ args.d }}`, + "e": `{{ args.e }}`, + }, resourceOpts{ + name: "cm", + namespace: p.projectName, + }) + + p.KluctlMust("deploy", "--yes", "-t", "test") + cm := k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + assertNestedFieldEquals(t, cm, "a", "data", "a") + assertNestedFieldEquals(t, cm, "b", "data", "b") + assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "c"}}`, "data", "c") + assertNestedFieldEquals(t, cm, "True", "data", "d") + assertNestedFieldEquals(t, cm, "true", "data", "e") +} From f3a727c47955e4b7f35bf022b75741845a5738dc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 15:08:31 +0200 Subject: [PATCH 0460/2268] fix: Print arg value that was invalid --- pkg/deployment/external_args.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index 952dfa063..365342560 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -18,7 +18,7 @@ func ParseArgs(argsList []string) (map[string]string, error) { args := make(map[string]string) for _, arg := range argsList { if !argPattern.MatchString(arg) { - return nil, fmt.Errorf("invalid --arg argument. Must be --arg=some_var_name=value") + return nil, fmt.Errorf("invalid --arg argument. Must be --arg=some_var_name=value, not '%s'", arg) } s := strings.SplitN(arg, "=", 2) From a739b97ee79905ca84d10c3e5bef386f4df50a84 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 15:11:01 +0200 Subject: [PATCH 0461/2268] docs: Remove mentions of dynamicArgs --- docs/reference/kluctl-project/targets/README.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/reference/kluctl-project/targets/README.md b/docs/reference/kluctl-project/targets/README.md index 46eba7293..a9d0de7a5 100644 --- a/docs/reference/kluctl-project/targets/README.md +++ b/docs/reference/kluctl-project/targets/README.md @@ -29,9 +29,6 @@ targets: arg1: arg2: ... - dynamicArgs: - - name: - ... images: - image: my-image resultImage: my-image:1.2.3 @@ -58,12 +55,6 @@ are configured via [deployment args](../../deployments/deployment-yml.md#args). The arguments specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets.md#args) have higher priority. -## dynamicArgs -This field specifies a list of CLI arguments that can be passed to kluctl when performing any commands on the target. These -arguments are passed with `-a arg_name=arg_value` when for example calling `kluctl deploy -t target_name`. - -Each entry has the following fields: - ## images This field specifies a list of fixed images to be used by [`images.get_image(...)`](../../deployments/images.md#imagesget_image). The format is identical to the [fixed images file](../../deployments/images.md#fixed-images-via-a-yaml-file). @@ -71,9 +62,6 @@ The format is identical to the [fixed images file](../../deployments/images.md#f The fixed images specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets.md#images) have higher priority. -### name -The name of the argument. - ## sealingConfig This field configures how sealing is performed when the [seal command](../../commands/seal.md) is invoked for this target. It has the following form: From 9a1703f6c1aed0e2a80f6c72526df27edfaca0e3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 15:11:16 +0200 Subject: [PATCH 0462/2268] tests: Remove dynamicArgs from TestArgs --- e2e/args_test.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index df588a8a6..840203e04 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -19,20 +19,6 @@ func TestArgs(t *testing.T) { createNamespace(t, k, p.projectName) p.updateTarget("test", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField([]any{ - map[string]any{ - "name": "a", - }, - map[string]any{ - "name": "b", - }, - map[string]any{ - "name": "c", - }, - map[string]any{ - "name": "d", - }, - }, "dynamicArgs") }) p.updateDeploymentYaml(".", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField([]any{ From d35c8c08165b45d3cd1e6e2e96b3cad8e0da70e4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 09:24:34 +0200 Subject: [PATCH 0463/2268] ci: Only run tests workflow on main branch and PRs --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3dd57cb2a..8e060e2ca 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,6 +2,8 @@ name: tests on: push: + branches: + - main pull_request: branches: - main From da90721b96ef3c1f1d9029382532657b63ed86e2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 09:54:00 +0200 Subject: [PATCH 0464/2268] ci: Stop depending on pre-built test binaries and build on the target os runner --- .github/workflows/tests.yml | 48 +++++++++++-------------------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8e060e2ca..faa1a4ee5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,7 +40,7 @@ jobs: exit 1 fi - build: + cross-compile: runs-on: ubuntu-20.04 steps: - name: Checkout @@ -55,30 +55,18 @@ jobs: path: | ~/go/pkg/mod ~/.cache/go-build - key: build-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} + key: cross-compile-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} restore-keys: | - build-go-${{ runner.os }}- - - name: Run unit tests - run: | - go test ./cmd/... ./pkg/... -v - - name: Build kluctl-e2e (linux) + cross-compile-go-${{ runner.os }}- + - name: Build kluctl (linux) run: | - make test-e2e-build GOARCH=amd64 GOOS=linux - mv ./bin/kluctl-e2e ./bin/kluctl-e2e-linux-amd64 - - name: Build kluctl-e2e (darwin) + make build GOARCH=amd64 GOOS=linux + - name: Build kluctl (darwin) run: | - make test-e2e-build GOARCH=amd64 GOOS=darwin - mv ./bin/kluctl-e2e ./bin/kluctl-e2e-darwin-amd64 - - name: Build kluctl-e2e (windows) + make build GOARCH=amd64 GOOS=darwin + - name: Build kluctl (windows) run: | - make test-e2e-build GOARCH=amd64 GOOS=windows - mv ./bin/kluctl-e2e.exe ./bin/kluctl-e2e-windows-amd64.exe - - name: Upload binaries - uses: actions/upload-artifact@v2 - with: - name: bin - path: | - ./bin + make build GOARCH=amd64 GOOS=windows tests: strategy: @@ -92,8 +80,6 @@ jobs: binary-suffix: windows-amd64 os: [ubuntu-20.04, macos-11, windows-2019] fail-fast: false - needs: - - build runs-on: ${{ matrix.os }} steps: - name: Checkout @@ -109,21 +95,15 @@ jobs: key: tests-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} restore-keys: | tests-go-${{ runner.os }}- - - name: Download artifacts - uses: actions/download-artifact@v2 - name: setup-envtest shell: bash run: | make install-envtest + - name: Run unit tests + shell: bash + run: | + make test-unit - name: Run e2e tests shell: bash run: | - EXE= - if [ "${{ runner.os }}" == "Windows" ]; then - EXE=.exe - fi - - chmod +x ./bin/* - mv ./bin/kluctl-e2e-${{ matrix.binary-suffix }}$EXE ./bin/kluctl-e2e$EXE - - make test-e2e-pre-built + make test-e2e From 9664ff02b8f3ffd3f8badb95a210b5f693f5e22b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 11:01:50 +0200 Subject: [PATCH 0465/2268] chore: Setup GOOS/GOARCH properly in Makefile --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 6dee972ce..dcae8fd0d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,8 @@ # Based on the work of Thomas Poignant (thomaspoignant) # https://gist.github.com/thomaspoignant/5b72d579bd5f311904d973652180c705 +GOOS=$(shell go env GOOS) +GOARCH=$(shell go env GOARCH) + EXE= ifeq ($(GOOS), windows) EXE=.exe From d9f3483b473fcec8a84abd07d38e7b5a9baf7f41 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 10:55:06 +0200 Subject: [PATCH 0466/2268] tests: Refactor webhook handler --- e2e/hooks_test.go | 7 ++++--- internal/test-utils/envtest_cluster_callback.go | 10 ++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 86bbf45fa..1d6555f3a 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "testing" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" @@ -45,9 +46,9 @@ func (s *HookTestSuite) SetupTest() { s.clearSeenConfigmaps() } -func (s *HookTestSuite) handleConfigmap(o *uo.UnstructuredObject) { - s.T().Logf("handleConfigmap: %s", o.GetK8sName()) - s.seenConfigMaps = append(s.seenConfigMaps, o.GetK8sName()) +func (s *HookTestSuite) handleConfigmap(request admission.Request) { + s.T().Logf("handleConfigmap: %s %s/%s", request.Operation, request.Namespace, request.Name) + s.seenConfigMaps = append(s.seenConfigMaps, request.Name) } func (s *HookTestSuite) clearSeenConfigmaps() { diff --git a/internal/test-utils/envtest_cluster_callback.go b/internal/test-utils/envtest_cluster_callback.go index 2ec870b3c..31a6dc8f4 100644 --- a/internal/test-utils/envtest_cluster_callback.go +++ b/internal/test-utils/envtest_cluster_callback.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "github.com/go-logr/logr" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" admissionv1 "k8s.io/api/admissionregistration/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -14,7 +13,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) -type CallbackHandler func(o *uo.UnstructuredObject) +type CallbackHandler func(request admission.Request) func (k *EnvTestCluster) buildServeCallback(gvr schema.GroupVersionResource, cb CallbackHandler) http.Handler { wh := &webhook.Admission{ @@ -28,12 +27,11 @@ func (k *EnvTestCluster) buildServeCallback(gvr schema.GroupVersionResource, cb } func (k *EnvTestCluster) serveCallback(gvr schema.GroupVersionResource, request admission.Request, cb CallbackHandler) { - o, err := uo.FromString(string(request.Object.Raw)) - if err != nil { - panic(err) + if request.Resource.Group != gvr.Group || request.Resource.Version != gvr.Version || request.Resource.Resource != gvr.Resource { + return } - cb(o) + cb(request) } func (k *EnvTestCluster) startCallbackServer() error { From 4cf053b8f4085deab16f2e80c30c922d7f6112e0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 11:12:31 +0200 Subject: [PATCH 0467/2268] tests: Add some more info to webhook logging --- e2e/hooks_test.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 1d6555f3a..ef4e44fba 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -47,7 +47,21 @@ func (s *HookTestSuite) SetupTest() { } func (s *HookTestSuite) handleConfigmap(request admission.Request) { - s.T().Logf("handleConfigmap: %s %s/%s", request.Operation, request.Namespace, request.Name) + x, err := uo.FromString(string(request.Object.Raw)) + if err != nil { + s.T().Fatal(err) + } + generation, _, err := x.GetNestedInt("metadata", "generation") + if err != nil { + s.T().Fatal(err) + } + uid, _, err := x.GetNestedString("metadata", "uid") + if err != nil { + s.T().Fatal(err) + } + + s.T().Logf("handleConfigmap: op=%s, name=%s/%s, generation=%d, uid=%s", request.Operation, request.Namespace, request.Name, generation, uid) + s.seenConfigMaps = append(s.seenConfigMaps, request.Name) } From 73df58aeed572e6332404fefb9775d24e251846f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 16:06:59 +0200 Subject: [PATCH 0468/2268] tests: Add DynamicClient to EnvTestCluster --- internal/test-utils/envtest_cluster.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go index 5bfd6100a..12891067b 100644 --- a/internal/test-utils/envtest_cluster.go +++ b/internal/test-utils/envtest_cluster.go @@ -7,7 +7,9 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "io" + "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" + "net/http" "os" "os/exec" "sigs.k8s.io/controller-runtime/pkg/envtest" @@ -24,6 +26,9 @@ type EnvTestCluster struct { Context string config *rest.Config + HttpClient *http.Client + DynamicClient dynamic.Interface + callbackServer webhook.Server callbackServerStop context.CancelFunc } @@ -64,6 +69,18 @@ func (k *EnvTestCluster) Start() error { k.Kubeconfig = kcfg + client, err := rest.HTTPClientFor(k.config) + if err != nil { + return err + } + k.HttpClient = client + + dynamicClient, err := dynamic.NewForConfigAndClient(k.config, k.HttpClient) + if err != nil { + return err + } + k.DynamicClient = dynamicClient + return nil } From 1a779a9a81e58866680eaa7fd4cac9136768e502 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 16:07:43 +0200 Subject: [PATCH 0469/2268] tests: Let flux tests use DynamicClient --- e2e/flux_test.go | 36 ++++++++++++++++++----- e2e/test_resources/kluctl-deployment.yaml | 11 +++++-- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/e2e/flux_test.go b/e2e/flux_test.go index 1b604dccb..269562405 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -1,7 +1,11 @@ package e2e import ( + "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "testing" "github.com/kluctl/kluctl/v2/e2e/test_resources" @@ -9,13 +13,30 @@ import ( "github.com/stretchr/testify/assert" ) +var kluctlDeploymentGVR = schema.GroupVersionResource{ + Group: "flux.kluctl.io", + Version: "v1alpha1", + Resource: "kluctldeployments", +} + +func getKluctlDeploymentObject(t *testing.T, k *test_utils.EnvTestCluster) *uo.UnstructuredObject { + kd, err := k.DynamicClient.Resource(kluctlDeploymentGVR).Namespace("flux-test").Get(context.Background(), "microservices-demo-test", v1.GetOptions{}) + if err != nil { + t.Fatal(err) + } + if kd == nil { + t.Fatal("kluctldeployment not found") + } + return uo.FromUnstructured(kd) +} + func TestFluxCommands(t *testing.T) { t.Parallel() k := defaultCluster1 p := &testProject{} - p.init(t, k, "simple") + p.init(t, k, "flux-test") defer p.cleanup() @@ -23,23 +44,24 @@ func TestFluxCommands(t *testing.T) { test_resources.ApplyYaml("kluctl-crds.yaml", k) test_resources.ApplyYaml("kluctl-deployment.yaml", k) - assertResourceExists(t, k, "default", "kluctldeployment/microservices-demo-test") + // assert that it was created + _ = getKluctlDeploymentObject(t, k) - p.KluctlMust("flux", "suspend", "--namespace", "default", "--kluctl-deployment", "microservices-demo-test") + p.KluctlMust("flux", "suspend", "--namespace", "flux-test", "--kluctl-deployment", "microservices-demo-test") suspend := getKluctlSuspendField(t, k) assert.Equal(t, true, suspend, "Field status.suspend is not false") - p.KluctlMust("flux", "resume", "--namespace", "default", "--kluctl-deployment", "microservices-demo-test", "--no-wait") + p.KluctlMust("flux", "resume", "--namespace", "flux-test", "--kluctl-deployment", "microservices-demo-test", "--no-wait") resume := getKluctlSuspendField(t, k) assert.Equal(t, false, resume, "Field status.suspend is not true") - p.KluctlMust("flux", "reconcile", "--namespace", "default", "--kluctl-deployment", "microservices-demo-test", "--with-source", "--no-wait") + p.KluctlMust("flux", "reconcile", "--namespace", "flux-test", "--kluctl-deployment", "microservices-demo-test", "--with-source", "--no-wait") annotation := getKluctlAnnotations(t, k) assert.Len(t, annotation, 1, "Annotation not present") } func getKluctlSuspendField(t *testing.T, k *test_utils.EnvTestCluster) interface{} { - o := k.KubectlYamlMust(t, "-n", "default", "get", "kluctldeployment", "microservices-demo-test") + o := getKluctlDeploymentObject(t, k) result, ok, err := o.GetNestedField("spec", "suspend") fmt.Println(result) if err != nil { @@ -52,7 +74,7 @@ func getKluctlSuspendField(t *testing.T, k *test_utils.EnvTestCluster) interface } func getKluctlAnnotations(t *testing.T, k *test_utils.EnvTestCluster) interface{} { - o := k.KubectlYamlMust(t, "-n", "default", "get", "kluctldeployment", "microservices-demo-test") + o := getKluctlDeploymentObject(t, k) result, ok, err := o.GetNestedField("metadata", "annotations") if err != nil { t.Fatal(err) diff --git a/e2e/test_resources/kluctl-deployment.yaml b/e2e/test_resources/kluctl-deployment.yaml index e10aeede8..eae1bec80 100644 --- a/e2e/test_resources/kluctl-deployment.yaml +++ b/e2e/test_resources/kluctl-deployment.yaml @@ -1,9 +1,14 @@ --- +apiVersion: v1 +kind: Namespace +metadata: + name: flux-test +--- apiVersion: flux.kluctl.io/v1alpha1 kind: KluctlDeployment metadata: name: microservices-demo-test - namespace: default + namespace: flux-test spec: interval: 10m path: ./simple @@ -14,7 +19,7 @@ spec: sourceRef: kind: GitRepository name: microservices-demo - namespace: default + namespace: flux-test target: simple timeout: 2m --- @@ -22,7 +27,7 @@ apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: GitRepository metadata: name: microservices-demo - namespace: default + namespace: flux-test spec: interval: 5m ref: From 0f96267fc13780fb5f875fe0cc8661667900d5f8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 16:08:53 +0200 Subject: [PATCH 0470/2268] tests: Remove waitReadiness and waitForReadiness --- e2e/utils.go | 49 ++----------------------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/e2e/utils.go b/e2e/utils.go index eaf69dea2..e4f75094a 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -3,18 +3,13 @@ package e2e import ( "bufio" "bytes" - "fmt" + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "io" "os/exec" "reflect" "strings" "testing" - "time" - - test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/validation" - log "github.com/sirupsen/logrus" ) func createNamespace(t *testing.T, k *test_utils.EnvTestCluster, namespace string) { @@ -22,46 +17,6 @@ func createNamespace(t *testing.T, k *test_utils.EnvTestCluster, namespace strin k.KubectlMust(t, "label", "ns", namespace, "kluctl-e2e=true") } -func waitForReadiness(t *testing.T, k *test_utils.EnvTestCluster, namespace string, resource string, timeout time.Duration) bool { - t.Logf("Waiting for readiness: %s/%s", namespace, resource) - - startTime := time.Now() - for time.Now().Sub(startTime) < timeout { - y, stderr, err := k.KubectlYaml("-n", namespace, "get", resource) - if err != nil { - if strings.Index(stderr, "NotFound") == -1 { - t.Fatal(err) - } - time.Sleep(1 * time.Second) - continue - } - - v := validation.ValidateObject(nil, y, true) - if v.Ready { - return true - } - - if log.IsLevelEnabled(log.DebugLevel) { - errTxt := "" - for _, e := range v.Errors { - if errTxt != "" { - errTxt += "\n" - } - errTxt += fmt.Sprintf("%s: %s", e.Ref.String(), e.Error) - } - log.Debugf("validation failed for %s/%s. errors:\n%s", namespace, resource, errTxt) - } - time.Sleep(1 * time.Second) - } - return false -} - -func assertReadiness(t *testing.T, k *test_utils.EnvTestCluster, namespace string, resource string, timeout time.Duration) { - if !waitForReadiness(t, k, namespace, resource, timeout) { - t.Errorf("%s/%s did not get ready in time", namespace, resource) - } -} - func assertResourceExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, resource string) *uo.UnstructuredObject { var args []string if namespace != "" { From da3134335991590f26bfcad82e1e361d8690ca97 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 16:18:17 +0200 Subject: [PATCH 0471/2268] tests: Use DynamicClient instead of kubectl to check for configmaps --- e2e/contexts_test.go | 32 +++++++++--------- e2e/deploy_test.go | 6 ++-- e2e/hooks_test.go | 2 +- e2e/no_target_test.go | 8 ++--- e2e/utils.go | 45 ++++++++++++++------------ internal/test-utils/envtest_cluster.go | 26 +++++++++++++++ 6 files changed, 74 insertions(+), 45 deletions(-) diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index 6d9d10c7a..7de30a13f 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -33,15 +33,15 @@ func TestContextCurrent(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") + assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") p.updateMergedKubeconfig(func(config *api.Config) { config.CurrentContext = defaultCluster2.Context }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") } func TestContext1(t *testing.T) { @@ -55,8 +55,8 @@ func TestContext1(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") + assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") } func TestContext2(t *testing.T) { @@ -70,8 +70,8 @@ func TestContext2(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapNotExists(t, defaultCluster1, p.projectName, "cm") } func TestContext1And2(t *testing.T) { @@ -88,11 +88,11 @@ func TestContext1And2(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") + assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") p.KluctlMust("deploy", "--yes", "-t", "test2") - assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") } func TestContextSwitch(t *testing.T) { @@ -106,15 +106,15 @@ func TestContextSwitch(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") + assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") } func TestContextOverride(t *testing.T) { @@ -128,9 +128,9 @@ func TestContextOverride(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") + assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") p.KluctlMust("deploy", "--yes", "-t", "test1", "--context", defaultCluster2.Context) - assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") } diff --git a/e2e/deploy_test.go b/e2e/deploy_test.go index 9e54ebcf8..c6b0493da 100644 --- a/e2e/deploy_test.go +++ b/e2e/deploy_test.go @@ -22,14 +22,14 @@ func TestCommandDeploySimple(t *testing.T) { namespace: p.projectName, }) p.KluctlMust("deploy", "--yes", "-t", "test") - assertResourceExists(t, k, p.projectName, "ConfigMap/cm") + assertConfigMapExists(t, k, p.projectName, "cm") addConfigMapDeployment(p, "cm2", nil, resourceOpts{ name: "cm2", namespace: p.projectName, }) p.KluctlMust("deploy", "--yes", "-t", "test", "--dry-run") - assertResourceNotExists(t, k, p.projectName, "ConfigMap/cm2") + assertConfigMapNotExists(t, k, p.projectName, "cm2") p.KluctlMust("deploy", "--yes", "-t", "test") - assertResourceExists(t, k, p.projectName, "ConfigMap/cm2") + assertConfigMapExists(t, k, p.projectName, "cm2") } diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index ef4e44fba..0385eb418 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -132,7 +132,7 @@ func (s *HookTestSuite) ensureHookExecuted(p *testProject, expectedCms ...string func (s *HookTestSuite) ensureHookNotExecuted(p *testProject) { _, _, _ = s.k.Kubectl("delete", "-n", p.projectName, "configmaps", "cm1") p.KluctlMust("deploy", "--yes", "-t", "test") - assertResourceNotExists(s.T(), s.k, p.projectName, "ConfigMap/cm1") + assertConfigMapNotExists(s.T(), s.k, p.projectName, "cm1") } func (s *HookTestSuite) TestHooksPreDeployInitial() { diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index 80f419298..31351c3eb 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -31,22 +31,22 @@ func TestNoTarget(t *testing.T) { defer p.cleanup() p.KluctlMust("deploy", "--yes") - cm := assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") - assertResourceNotExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + cm := assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") + assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") assert.Equal(t, map[string]any{ "targetName": "", "targetContext": defaultCluster1.Context, }, cm.Object["data"]) p.KluctlMust("deploy", "--yes", "-T", "override-name") - cm = assertResourceExists(t, defaultCluster1, p.projectName, "ConfigMap/cm") + cm = assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") assert.Equal(t, map[string]any{ "targetName": "override-name", "targetContext": defaultCluster1.Context, }, cm.Object["data"]) p.KluctlMust("deploy", "--yes", "-T", "override-name", "--context", defaultCluster2.Context) - cm = assertResourceExists(t, defaultCluster2, p.projectName, "ConfigMap/cm") + cm = assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") assert.Equal(t, map[string]any{ "targetName": "override-name", "targetContext": defaultCluster2.Context, diff --git a/e2e/utils.go b/e2e/utils.go index e4f75094a..7e7ac0d26 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -3,42 +3,45 @@ package e2e import ( "bufio" "bytes" + "context" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "io" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "os/exec" "reflect" - "strings" "testing" ) func createNamespace(t *testing.T, k *test_utils.EnvTestCluster, namespace string) { - k.KubectlMust(t, "create", "ns", namespace) - k.KubectlMust(t, "label", "ns", namespace, "kluctl-e2e=true") -} + var ns unstructured.Unstructured + ns.SetName(namespace) + + _, err := k.DynamicClient.Resource(v1.SchemeGroupVersion.WithResource("namespaces")).Create(context.Background(), &ns, metav1.CreateOptions{}) -func assertResourceExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, resource string) *uo.UnstructuredObject { - var args []string - if namespace != "" { - args = append(args, "-n", namespace) + if err != nil { + t.Fatal(err) } - args = append(args, "get", resource) - return k.KubectlYamlMust(t, args...) } -func assertResourceNotExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, resource string) { - var args []string - if namespace != "" { - args = append(args, "-n", namespace) +func assertConfigMapExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, name string) *uo.UnstructuredObject { + x, err := k.Get(v1.SchemeGroupVersion.WithResource("configmaps"), namespace, name) + if err != nil { + t.Fatalf("unexpected error '%v' while getting ConfigMap %s/%s", err, namespace, name) } - args = append(args, "get", resource) - _, stderr, err := k.KubectlYaml(args...) + return x +} + +func assertConfigMapNotExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, name string) { + _, err := k.Get(v1.SchemeGroupVersion.WithResource("configmaps"), namespace, name) if err == nil { - t.Fatalf("'kubectl get' for %s should not have succeeded", resource) - } else { - if strings.Index(stderr, "(NotFound)") == -1 { - t.Fatal(err) - } + t.Fatalf("expected %s/%s to not exist", namespace, name) + } + if !errors.IsNotFound(err) { + t.Fatalf("unexpected error '%v' for %s/%s, expected a NotFound error", err, namespace, name) } } diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go index 12891067b..33fa9a51a 100644 --- a/internal/test-utils/envtest_cluster.go +++ b/internal/test-utils/envtest_cluster.go @@ -7,6 +7,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "io" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "net/http" @@ -100,6 +102,30 @@ func (c *EnvTestCluster) RESTConfig() *rest.Config { return c.config } +func (c *EnvTestCluster) Get(gvr schema.GroupVersionResource, namespace string, name string) (*uo.UnstructuredObject, error) { + x, err := c.DynamicClient.Resource(gvr).Namespace(namespace).Get(context.Background(), name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return uo.FromUnstructured(x), nil +} + +func (c *EnvTestCluster) MustGet(t *testing.T, gvr schema.GroupVersionResource, namespace string, name string) *uo.UnstructuredObject { + x, err := c.Get(gvr, namespace, name) + if err != nil { + t.Fatalf("error while getting %s/%s/%s: %s", gvr.String(), namespace, name, err.Error()) + } + return x +} + +func (c *EnvTestCluster) MustGetCoreV1(t *testing.T, resource string, namespace string, name string) *uo.UnstructuredObject { + return c.MustGet(t, schema.GroupVersionResource{ + Group: "", + Version: "v1", + Resource: resource, + }, namespace, name) +} + func (c *EnvTestCluster) Kubectl(args ...string) (string, string, error) { tmp, err := os.CreateTemp("", "") if err != nil { From 54edaaa93d29e8e6cbb96bf2498bbc276b39c76b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Oct 2022 17:06:33 +0200 Subject: [PATCH 0472/2268] tests: Stop using kubectl in ApplyYamls --- e2e/test_resources/resources.go | 68 +++++++++++++++++++++++++---- pkg/deployment/commands/validate.go | 2 +- pkg/deployment/utils/apply_utils.go | 2 +- pkg/validation/validation.go | 6 ++- 4 files changed, 67 insertions(+), 11 deletions(-) diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go index 90454201d..2e53339ec 100644 --- a/e2e/test_resources/resources.go +++ b/e2e/test_resources/resources.go @@ -1,8 +1,17 @@ package test_resources import ( + "context" "embed" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/validation" + "github.com/kluctl/kluctl/v2/pkg/yaml" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" "os" + "strings" + "time" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -26,22 +35,65 @@ func GetYamlTmpFile(name string) string { return tmpFile.Name() } +// poor mans resource mapper :) +func guessGVR(gvk schema.GroupVersionKind) schema.GroupVersionResource { + gvr := schema.GroupVersionResource{ + Group: gvk.Group, + Version: gvk.Version, + } + if strings.HasSuffix(gvk.Kind, "y") { + gvr.Resource = strings.ToLower(gvk.Kind)[:len(gvk.Kind)-1] + "ies" + } else { + gvr.Resource = strings.ToLower(gvk.Kind) + "s" + } + return gvr +} + func ApplyYaml(name string, k *test_utils.EnvTestCluster) { tmpFile := GetYamlTmpFile(name) defer os.Remove(tmpFile) - _, _, err := k.Kubectl("apply", "-f", tmpFile) + docs, err := yaml.ReadYamlAllFile(tmpFile) if err != nil { panic(err) } -} -func DeleteYaml(name string, k *test_utils.EnvTestCluster) { - tmpFile := GetYamlTmpFile(name) - defer os.Remove(tmpFile) + for _, doc := range docs { + m, ok := doc.(map[string]any) + if !ok { + panic("not a map!") + } + x := uo.FromMap(m) - _, _, err := k.Kubectl("delete", "-f", tmpFile) - if err != nil { - panic(err) + data, err := yaml.WriteYamlBytes(x) + if err != nil { + panic(err) + } + + gvr := guessGVR(x.GetK8sGVK()) + _, err = k.DynamicClient.Resource(gvr). + Namespace(x.GetK8sNamespace()). + Patch(context.Background(), x.GetK8sName(), types.ApplyPatchType, data, metav1.PatchOptions{ + FieldManager: "e2e-tests", + }) + if err != nil { + panic(err) + } + + // wait for CRDs to get accepted + if x.GetK8sGVK().Kind == "CustomResourceDefinition" { + for true { + u, err := k.DynamicClient.Resource(gvr).Get(context.Background(), x.GetK8sName(), metav1.GetOptions{}) + if err != nil { + panic(err) + } + vr := validation.ValidateObject(nil, uo.FromUnstructured(u), true, true) + if vr.Ready { + break + } else { + time.Sleep(time.Millisecond * 100) + } + } + } } } diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 0647a3b51..1b4f20e89 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -56,7 +56,7 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types. result.Errors = append(result.Errors, types.DeploymentError{Ref: ref, Error: "object not found"}) continue } - r := validation.ValidateObject(k, remoteObject, true) + r := validation.ValidateObject(k, remoteObject, true, false) if !r.Ready { result.Ready = false } diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index bf1043c91..440b83e76 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -363,7 +363,7 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo a.HandleError(ref, err) return false } - v := validation.ValidateObject(a.k, o, false) + v := validation.ValidateObject(a.k, o, false, false) if v.Ready { if didLog { a.sctx.InfoFallback("Finished waiting for %s (%ds elapsed)", ref.String(), elapsed) diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 7a1f1083c..36cf3445e 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -45,7 +45,7 @@ const ( reactNotReady ) -func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError bool) (ret types.ValidateResult) { +func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError bool, forceStatusRequired bool) (ret types.ValidateResult) { ref := o.GetK8sRef() // We assume all is good in case no validation is performed @@ -118,6 +118,10 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError status, _, _ := o.GetNestedObject("status") if status == nil { + if forceStatusRequired { + addNotReady("no status available yet") + return + } if k == nil { // can't really say anything... return From 9befb241e5cec495988e0fe868583c48d724af56 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 11:31:05 +0200 Subject: [PATCH 0473/2268] tests: Stop using kubectl in inclusion_test.go --- e2e/inclusion_test.go | 8 +++++--- internal/test-utils/envtest_cluster.go | 27 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 243584b03..99e1beaab 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -1,8 +1,8 @@ package e2e import ( - "fmt" "github.com/kluctl/kluctl/v2/internal/test-utils" + corev1 "k8s.io/api/core/v1" "path/filepath" "reflect" "testing" @@ -58,8 +58,10 @@ func assertExistsHelper(t *testing.T, p *testProject, k *test_utils.EnvTestClust delete(shouldExists, x) } } - exists := k.KubectlYamlMust(t, "-n", p.projectName, "get", "configmaps", "-l", fmt.Sprintf("project_name=%s", p.projectName)) - items, _, _ := exists.GetNestedObjectList("items") + items, err := k.List(corev1.SchemeGroupVersion.WithResource("configmaps"), p.projectName, map[string]string{"project_name": p.projectName}) + if err != nil { + t.Fatal(err) + } found := make(map[string]bool) for _, x := range items { found[x.GetK8sName()] = true diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go index 33fa9a51a..fbf531283 100644 --- a/internal/test-utils/envtest_cluster.go +++ b/internal/test-utils/envtest_cluster.go @@ -126,6 +126,33 @@ func (c *EnvTestCluster) MustGetCoreV1(t *testing.T, resource string, namespace }, namespace, name) } +func (c *EnvTestCluster) List(gvr schema.GroupVersionResource, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, error) { + labelSelector := "" + if len(labels) != 0 { + for k, v := range labels { + if labelSelector != "" { + labelSelector += "," + } + labelSelector += fmt.Sprintf("%s=%s", k, v) + } + } + + l, err := c.DynamicClient.Resource(gvr). + Namespace(namespace). + List(context.Background(), metav1.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil { + return nil, err + } + var ret []*uo.UnstructuredObject + for _, x := range l.Items { + x := x + ret = append(ret, uo.FromUnstructured(&x)) + } + return ret, nil +} + func (c *EnvTestCluster) Kubectl(args ...string) (string, string, error) { tmp, err := os.CreateTemp("", "") if err != nil { From abe958b7e83f351bb49449d0fd9353c5a278b6df Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 11:31:25 +0200 Subject: [PATCH 0474/2268] tests: Stop using kubectl in args_test.go --- e2e/args_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index 840203e04..026f3bee0 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -50,32 +50,32 @@ func TestArgs(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a") - cm := k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + cm := k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "default", "data", "b") assertNestedFieldEquals(t, cm, "na", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") - cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "na", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c") - cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "c", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", "-ad.nested=d") - cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") assertNestedFieldEquals(t, cm, `{"nested": "d"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", `-ad={"nested": "d2"}`) - cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") assertNestedFieldEquals(t, cm, `{"nested": "d2"}`, "data", "d") tmpFile, err := os.CreateTemp("", "") @@ -89,7 +89,7 @@ nested: `) p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", fmt.Sprintf(`-ad=@%s`, tmpFile.Name())) - cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "d3"}}`, "data", "d") _ = tmpFile.Truncate(0) @@ -103,7 +103,7 @@ d: `) p.KluctlMust("deploy", "--yes", "-t", "test", fmt.Sprintf(`--args-from-file=%s`, tmpFile.Name())) - cm = k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") assertNestedFieldEquals(t, cm, "a2", "data", "a") assertNestedFieldEquals(t, cm, "default", "data", "b") assertNestedFieldEquals(t, cm, "c2", "data", "c") @@ -140,7 +140,7 @@ func TestArgsFromEnv(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test") - cm := k.KubectlYamlMust(t, "-n", p.projectName, "get", "cm/cm") + cm := k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "c"}}`, "data", "c") From e6b33ce8ad9ccc6f4aa5d44d91f2c51617bd95fb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 11:31:38 +0200 Subject: [PATCH 0475/2268] tests: Stop using kubectl in hooks_test.go --- e2e/hooks_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 0385eb418..1d16eb736 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -1,9 +1,12 @@ package e2e import ( + "context" "fmt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "testing" @@ -130,7 +133,9 @@ func (s *HookTestSuite) ensureHookExecuted(p *testProject, expectedCms ...string } func (s *HookTestSuite) ensureHookNotExecuted(p *testProject) { - _, _, _ = s.k.Kubectl("delete", "-n", p.projectName, "configmaps", "cm1") + _ = s.k.DynamicClient.Resource(corev1.SchemeGroupVersion.WithResource("configmaps")). + Namespace(p.projectName). + Delete(context.Background(), "cm1", metav1.DeleteOptions{}) p.KluctlMust("deploy", "--yes", "-t", "test") assertConfigMapNotExists(s.T(), s.k, p.projectName, "cm1") } From e805b7f8645c066c234952cfc1227268641e13bc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 11:31:48 +0200 Subject: [PATCH 0476/2268] tests: Stop using kubectl in seal_test.go --- e2e/seal_test.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 14219dd66..808735fa8 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -1,6 +1,7 @@ package e2e import ( + "context" "crypto/x509" "encoding/pem" "fmt" @@ -13,6 +14,9 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" certUtil "k8s.io/client-go/util/cert" "k8s.io/client-go/util/keyutil" "net" @@ -158,7 +162,12 @@ func (s *SealedSecretsTestSuite) addSecretsSetToTarget(p *testProject, targetNam } func (s *SealedSecretsTestSuite) assertSealedSecret(k *test_utils.EnvTestCluster, namespace string, secretName string, expectedCertHash string, expectedSecrets map[string]string) { - y := k.KubectlYamlMust(s.T(), "-n", namespace, "get", "sealedsecret", secretName) + + y := k.MustGet(s.T(), schema.GroupVersionResource{ + Group: "bitnami.com", + Version: "v1alpha1", + Resource: "sealedsecrets", + }, namespace, secretName) h1 := y.GetK8sAnnotation("kluctl.io/sealedsecret-cert-hash") if h1 == nil { @@ -219,7 +228,8 @@ func (s *SealedSecretsTestSuite) TestSeal_WithBootstrap() { namespace := "seal-with-bootstrap" // deleting the crd causes kluctl to not recognize the operator, so it will do a bootstrap - k.KubectlMust(s.T(), "delete", "crd", "sealedsecrets.bitnami.com") + _ = k.DynamicClient.Resource(apiextensionsv1.SchemeGroupVersion.WithResource("customresourcedefinitions")). + Delete(context.Background(), "sealedsecrets.bitnami.com", metav1.DeleteOptions{}) p := s.prepareSealTest(k, namespace, map[string]string{ @@ -246,7 +256,7 @@ func (s *SealedSecretsTestSuite) TestSeal_WithBootstrap() { p.KluctlMust("deploy", "--yes", "-t", "test-target") - pkCm := k.KubectlYamlMust(s.T(), "-n", "kube-system", "get", "cm", "sealed-secrets-key-kluctl-bootstrap") + pkCm := k.MustGetCoreV1(s.T(), "configmaps", "kube-system", "sealed-secrets-key-kluctl-bootstrap") certBytes, ok, _ := pkCm.GetNestedString("data", "tls.crt") s.Assertions.True(ok) From ba963705c677c3ad0cfd7c48a9477041a098dd8d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 11:39:06 +0200 Subject: [PATCH 0477/2268] tests: Paralellize ApplyYaml --- e2e/flux_test.go | 14 ++++- e2e/seal_test.go | 14 ++++- e2e/test_resources/resources.go | 100 +++++++++++++++++++++++--------- 3 files changed, 95 insertions(+), 33 deletions(-) diff --git a/e2e/flux_test.go b/e2e/flux_test.go index 269562405..33f30dbde 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -6,6 +6,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "sync" "testing" "github.com/kluctl/kluctl/v2/e2e/test_resources" @@ -40,8 +41,17 @@ func TestFluxCommands(t *testing.T) { defer p.cleanup() - test_resources.ApplyYaml("flux-source-crd.yaml", k) - test_resources.ApplyYaml("kluctl-crds.yaml", k) + var wg sync.WaitGroup + wg.Add(2) + go func() { + test_resources.ApplyYaml("flux-source-crd.yaml", k) + wg.Done() + }() + go func() { + test_resources.ApplyYaml("kluctl-crds.yaml", k) + wg.Done() + }() + wg.Wait() test_resources.ApplyYaml("kluctl-deployment.yaml", k) // assert that it was created diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 808735fa8..36cc27099 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -23,6 +23,7 @@ import ( "net/http" "net/http/httptest" "path/filepath" + "sync" "testing" "time" ) @@ -52,8 +53,17 @@ func (s *SealedSecretsTestSuite) SetupSuite() { s.k = defaultCluster1 s.k2 = defaultCluster2 - test_resources.ApplyYaml("sealed-secrets.yaml", s.k) - test_resources.ApplyYaml("sealed-secrets.yaml", s.k2) + var wg sync.WaitGroup + wg.Add(2) + go func() { + test_resources.ApplyYaml("sealed-secrets.yaml", s.k) + wg.Done() + }() + go func() { + test_resources.ApplyYaml("sealed-secrets.yaml", s.k2) + wg.Done() + }() + wg.Wait() var err error s.certServer1, err = s.startCertServer() diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go index 2e53339ec..f374d2ee0 100644 --- a/e2e/test_resources/resources.go +++ b/e2e/test_resources/resources.go @@ -3,6 +3,8 @@ package test_resources import ( "context" "embed" + test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -10,11 +12,10 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "os" + "sort" "strings" + "sync" "time" - - test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" - "github.com/kluctl/kluctl/v2/pkg/utils" ) //go:embed *.yaml @@ -49,6 +50,32 @@ func guessGVR(gvk schema.GroupVersionKind) schema.GroupVersionResource { return gvr } +func prio(x *uo.UnstructuredObject) int { + switch x.GetK8sGVK().Kind { + case "Namespace": + return 100 + case "CustomResourceDefinition": + return 100 + default: + return 0 + } +} + +func waitReadiness(k *test_utils.EnvTestCluster, x *uo.UnstructuredObject) { + for true { + u, err := k.DynamicClient.Resource(guessGVR(x.GetK8sGVK())).Get(context.Background(), x.GetK8sName(), metav1.GetOptions{}) + if err != nil { + panic(err) + } + vr := validation.ValidateObject(nil, uo.FromUnstructured(u), true, true) + if vr.Ready { + break + } else { + time.Sleep(time.Millisecond * 100) + } + } +} + func ApplyYaml(name string, k *test_utils.EnvTestCluster) { tmpFile := GetYamlTmpFile(name) defer os.Remove(tmpFile) @@ -58,42 +85,57 @@ func ApplyYaml(name string, k *test_utils.EnvTestCluster) { panic(err) } + var objects []*uo.UnstructuredObject for _, doc := range docs { m, ok := doc.(map[string]any) if !ok { panic("not a map!") } x := uo.FromMap(m) + objects = append(objects, x) + } - data, err := yaml.WriteYamlBytes(x) - if err != nil { - panic(err) - } + sort.SliceStable(objects, func(i, j int) bool { + return prio(objects[i]) > prio(objects[j]) + }) - gvr := guessGVR(x.GetK8sGVK()) - _, err = k.DynamicClient.Resource(gvr). - Namespace(x.GetK8sNamespace()). - Patch(context.Background(), x.GetK8sName(), types.ApplyPatchType, data, metav1.PatchOptions{ - FieldManager: "e2e-tests", - }) - if err != nil { - panic(err) + var wg sync.WaitGroup + prevPrio := prio(objects[0]) + for _, x := range objects { + x := x + + p := prio(x) + if p != prevPrio { + wg.Wait() } + prevPrio = p + + wg.Add(1) + go func() { + defer wg.Done() - // wait for CRDs to get accepted - if x.GetK8sGVK().Kind == "CustomResourceDefinition" { - for true { - u, err := k.DynamicClient.Resource(gvr).Get(context.Background(), x.GetK8sName(), metav1.GetOptions{}) - if err != nil { - panic(err) - } - vr := validation.ValidateObject(nil, uo.FromUnstructured(u), true, true) - if vr.Ready { - break - } else { - time.Sleep(time.Millisecond * 100) - } + data, err := yaml.WriteYamlBytes(x) + if err != nil { + panic(err) } - } + + gvr := guessGVR(x.GetK8sGVK()) + _, err = k.DynamicClient.Resource(gvr). + Namespace(x.GetK8sNamespace()). + Patch(context.Background(), x.GetK8sName(), types.ApplyPatchType, data, metav1.PatchOptions{ + FieldManager: "e2e-tests", + }) + if err != nil { + panic(err) + } + + // wait for CRDs to get accepted + if x.GetK8sGVK().Kind == "CustomResourceDefinition" { + waitReadiness(k, x) + // add some safety net...for some reason the envtest api server still fails if not waiting + time.Sleep(200 * time.Millisecond) + } + }() } + wg.Wait() } From e86e1bae403a9ea17b3ada0b0dc52dd63eff6d7e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 12:06:04 +0200 Subject: [PATCH 0478/2268] tests: Disable rate limitting --- internal/test-utils/envtest_cluster.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go index fbf531283..dd03a5c59 100644 --- a/internal/test-utils/envtest_cluster.go +++ b/internal/test-utils/envtest_cluster.go @@ -11,6 +11,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" + "k8s.io/client-go/util/flowcontrol" "net/http" "os" "os/exec" @@ -61,6 +62,7 @@ func (k *EnvTestCluster) Start() error { k.user = user k.config = user.Config() + k.config.RateLimiter = flowcontrol.NewFakeAlwaysRateLimiter() kcfg, err := user.KubeConfig() if err != nil { From f55474521d423b220ebf0f341a8937c6b6048911 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 12:06:39 +0200 Subject: [PATCH 0479/2268] tests: Remove Kubectl help funcs --- internal/test-utils/envtest_cluster.go | 64 -------------------------- 1 file changed, 64 deletions(-) diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go index dd03a5c59..f113e87ff 100644 --- a/internal/test-utils/envtest_cluster.go +++ b/internal/test-utils/envtest_cluster.go @@ -5,16 +5,12 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" - "io" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "k8s.io/client-go/util/flowcontrol" "net/http" - "os" - "os/exec" "sigs.k8s.io/controller-runtime/pkg/envtest" "sigs.k8s.io/controller-runtime/pkg/webhook" "testing" @@ -154,63 +150,3 @@ func (c *EnvTestCluster) List(gvr schema.GroupVersionResource, namespace string, } return ret, nil } - -func (c *EnvTestCluster) Kubectl(args ...string) (string, string, error) { - tmp, err := os.CreateTemp("", "") - if err != nil { - return "", "", err - } - defer func() { - tmp.Close() - os.Remove(tmp.Name()) - }() - - _, err = tmp.Write(c.Kubeconfig) - if err != nil { - return "", "", err - } - - stdoutBuffer := &bytes.Buffer{} - stderrBuffer := &bytes.Buffer{} - allArgs := append([]string{fmt.Sprintf("--kubeconfig=%s", tmp.Name())}, args...) - - cmd := exec.Command(c.env.ControlPlane.KubectlPath, allArgs...) - cmd.Stdout = stdoutBuffer - cmd.Stderr = stderrBuffer - - err = cmd.Run() - stdout, _ := io.ReadAll(stdoutBuffer) - stderr, _ := io.ReadAll(stderrBuffer) - return string(stdout), string(stderr), err -} - -func (c *EnvTestCluster) KubectlMust(t *testing.T, args ...string) string { - stdout, stderr, err := c.Kubectl(args...) - if err != nil { - if t != nil { - t.Fatalf("%v, stderr=%s\n", err, stderr) - } else { - panic(fmt.Sprintf("%v, stderr=%s\n", err, stderr)) - } - } - return stdout -} - -func (c *EnvTestCluster) KubectlYaml(args ...string) (*uo.UnstructuredObject, string, error) { - args = append(args, "-oyaml") - stdout, stderr, err := c.Kubectl(args...) - if err != nil { - return nil, stderr, err - } - ret := uo.New() - err = yaml.ReadYamlString(stdout, ret) - return ret, stderr, err -} - -func (c *EnvTestCluster) KubectlYamlMust(t *testing.T, args ...string) *uo.UnstructuredObject { - o, stderr, err := c.KubectlYaml(args...) - if err != nil { - t.Fatalf("%v, stderr=%s\n", err, stderr) - } - return o -} From c659f7846174b5121dd174471224e7ce5e296c99 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 13:49:42 +0200 Subject: [PATCH 0480/2268] tests: Make hook tests parallel again --- e2e/default_clusters.go | 4 + e2e/hooks_test.go | 209 +++++++++--------- internal/test-utils/envtest_cluster.go | 4 + .../test-utils/envtest_cluster_callback.go | 44 +++- 4 files changed, 153 insertions(+), 108 deletions(-) diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index ed28ccbbb..ca75f2003 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -2,6 +2,7 @@ package e2e import ( test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "k8s.io/apimachinery/pkg/runtime/schema" "sync" ) @@ -24,6 +25,9 @@ func init() { }() go func() { defer wg.Done() + defaultCluster2.InitWebhookCallback(schema.GroupVersionResource{ + Version: "v1", Resource: "configmaps", + }, true) err := defaultCluster2.Start() if err != nil { panic(err) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 1d16eb736..29a54f239 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -15,65 +14,56 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) -type HookTestSuite struct { - suite.Suite - +type hooksTestContext struct { + t *testing.T k *test_utils.EnvTestCluster + p *testProject + seenConfigMaps []string -} -func TestHooks(t *testing.T) { - t.Parallel() - suite.Run(t, &HookTestSuite{}) + whh *test_utils.CallbackHandlerEntry } -func (s *HookTestSuite) SetupSuite() { - s.clearSeenConfigmaps() - - s.k = test_utils.CreateEnvTestCluster("cluster1") - s.k.AddWebhookCallback(schema.GroupVersionResource{ +func (s *hooksTestContext) setupWebhook() { + s.whh = s.k.AddWebhookHandler(schema.GroupVersionResource{ Version: "v1", Resource: "configmaps", - }, true, s.handleConfigmap) - err := s.k.Start() - if err != nil { - s.T().Fatal(err) - } + }, s.handleConfigmap) } -func (s *HookTestSuite) TearDownSuite() { - s.k.Stop() +func (s *hooksTestContext) removeWebhook() { + s.k.RemoveWebhookHandler(s.whh) } -func (s *HookTestSuite) SetupTest() { - s.clearSeenConfigmaps() -} +func (s *hooksTestContext) handleConfigmap(request admission.Request) { + if s.p.projectName != request.Namespace { + return + } -func (s *HookTestSuite) handleConfigmap(request admission.Request) { x, err := uo.FromString(string(request.Object.Raw)) if err != nil { - s.T().Fatal(err) + s.t.Fatal(err) } generation, _, err := x.GetNestedInt("metadata", "generation") if err != nil { - s.T().Fatal(err) + s.t.Fatal(err) } uid, _, err := x.GetNestedString("metadata", "uid") if err != nil { - s.T().Fatal(err) + s.t.Fatal(err) } - s.T().Logf("handleConfigmap: op=%s, name=%s/%s, generation=%d, uid=%s", request.Operation, request.Namespace, request.Name, generation, uid) + s.t.Logf("handleConfigmap: op=%s, name=%s/%s, generation=%d, uid=%s", request.Operation, request.Namespace, request.Name, generation, uid) s.seenConfigMaps = append(s.seenConfigMaps, request.Name) } -func (s *HookTestSuite) clearSeenConfigmaps() { - s.T().Logf("clearSeenConfigmaps: %v", s.seenConfigMaps) +func (s *hooksTestContext) clearSeenConfigmaps() { + s.t.Logf("clearSeenConfigmaps: %v", s.seenConfigMaps) s.seenConfigMaps = nil } -func (s *HookTestSuite) addHookConfigMap(p *testProject, dir string, opts resourceOpts, isHelm bool, hook string, hookDeletionPolicy string) { +func (s *hooksTestContext) addHookConfigMap(dir string, opts resourceOpts, isHelm bool, hook string, hookDeletionPolicy string) { annotations := make(map[string]string) if isHelm { annotations["helm.sh/hook"] = hook @@ -89,130 +79,137 @@ func (s *HookTestSuite) addHookConfigMap(p *testProject, dir string, opts resour opts.annotations = uo.CopyMergeStrMap(opts.annotations, annotations) - s.addConfigMap(p, dir, opts) + s.addConfigMap(dir, opts) } -func (s *HookTestSuite) addConfigMap(p *testProject, dir string, opts resourceOpts) { +func (s *hooksTestContext) addConfigMap(dir string, opts resourceOpts) { o := uo.New() o.SetK8sGVKs("", "v1", "ConfigMap") mergeMetadata(o, opts) o.SetNestedField(map[string]interface{}{}, "data") - p.addKustomizeResources(dir, []kustomizeResource{ + s.p.addKustomizeResources(dir, []kustomizeResource{ {fmt.Sprintf("%s.yml", opts.name), "", o}, }) } -func (s *HookTestSuite) prepareHookTestProject(name string, hook string, hookDeletionPolicy string) *testProject { - isDone := false +func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) *hooksTestContext { + s := &hooksTestContext{ + t: t, + k: defaultCluster2, // use cluster2 as it has webhooks setup + p: &testProject{}, + } + s.setupWebhook() + namespace := fmt.Sprintf("hook-%s", name) - p := &testProject{} - p.init(s.T(), s.k, namespace) - defer func() { - if !isDone { - p.cleanup() - } - }() - createNamespace(s.T(), s.k, namespace) + s.p.init(t, s.k, namespace) + t.Cleanup(func() { + s.removeWebhook() + s.p.cleanup() + }) + + createNamespace(s.t, s.k, namespace) - p.updateTarget("test", nil) + s.p.updateTarget("test", nil) - p.addKustomizeDeployment("hook", nil, nil) + s.p.addKustomizeDeployment("hook", nil, nil) - s.addConfigMap(p, "hook", resourceOpts{name: "cm1", namespace: namespace}) - s.addHookConfigMap(p, "hook", resourceOpts{name: "hook1", namespace: namespace}, false, hook, hookDeletionPolicy) + s.addConfigMap("hook", resourceOpts{name: "cm1", namespace: namespace}) + s.addHookConfigMap("hook", resourceOpts{name: "hook1", namespace: namespace}, false, hook, hookDeletionPolicy) - isDone = true - return p + return s } -func (s *HookTestSuite) ensureHookExecuted(p *testProject, expectedCms ...string) { +func (s *hooksTestContext) ensureHookExecuted(expectedCms ...string) { s.clearSeenConfigmaps() - p.KluctlMust("deploy", "--yes", "-t", "test") - assert.Equal(s.T(), expectedCms, s.seenConfigMaps) + s.p.KluctlMust("deploy", "--yes", "-t", "test") + assert.Equal(s.t, expectedCms, s.seenConfigMaps) } -func (s *HookTestSuite) ensureHookNotExecuted(p *testProject) { +func (s *hooksTestContext) ensureHookNotExecuted() { _ = s.k.DynamicClient.Resource(corev1.SchemeGroupVersion.WithResource("configmaps")). - Namespace(p.projectName). + Namespace(s.p.projectName). Delete(context.Background(), "cm1", metav1.DeleteOptions{}) - p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapNotExists(s.T(), s.k, p.projectName, "cm1") + s.p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapNotExists(s.t, s.k, s.p.projectName, "cm1") } -func (s *HookTestSuite) TestHooksPreDeployInitial() { - p := s.prepareHookTestProject("pre-deploy-initial", "pre-deploy-initial", "") - defer p.cleanup() - s.ensureHookExecuted(p, "hook1", "cm1") - s.ensureHookExecuted(p, "cm1") +func TestHooksPreDeployInitial(t *testing.T) { + t.Parallel() + s := prepareHookTestProject(t, "pre-deploy-initial", "pre-deploy-initial", "") + s.ensureHookExecuted("hook1", "cm1") + s.ensureHookExecuted("cm1") } -func (s *HookTestSuite) TestHooksPostDeployInitial() { - p := s.prepareHookTestProject("post-deploy-initial", "post-deploy-initial", "") - defer p.cleanup() - s.ensureHookExecuted(p, "cm1", "hook1") - s.ensureHookExecuted(p, "cm1") +func TestHooksPostDeployInitial(t *testing.T) { + t.Parallel() + s := prepareHookTestProject(t, "post-deploy-initial", "post-deploy-initial", "") + s.ensureHookExecuted("cm1", "hook1") + s.ensureHookExecuted("cm1") } -func (s *HookTestSuite) TestHooksPreDeployUpgrade() { - p := s.prepareHookTestProject("pre-deploy-upgrade", "pre-deploy-upgrade", "") - defer p.cleanup() - s.ensureHookExecuted(p, "cm1") - s.ensureHookExecuted(p, "hook1", "cm1") - s.ensureHookExecuted(p, "hook1", "cm1") +func TestHooksPreDeployUpgrade(t *testing.T) { + t.Parallel() + s := prepareHookTestProject(t, "pre-deploy-upgrade", "pre-deploy-upgrade", "") + s.ensureHookExecuted("cm1") + s.ensureHookExecuted("hook1", "cm1") + s.ensureHookExecuted("hook1", "cm1") } -func (s *HookTestSuite) TestHooksPostDeployUpgrade() { - p := s.prepareHookTestProject("post-deploy-upgrade", "post-deploy-upgrade", "") - defer p.cleanup() - s.ensureHookExecuted(p, "cm1") - s.ensureHookExecuted(p, "cm1", "hook1") - s.ensureHookExecuted(p, "cm1", "hook1") +func TestHooksPostDeployUpgrade(t *testing.T) { + t.Parallel() + s := prepareHookTestProject(t, "post-deploy-upgrade", "post-deploy-upgrade", "") + s.ensureHookExecuted("cm1") + s.ensureHookExecuted("cm1", "hook1") + s.ensureHookExecuted("cm1", "hook1") } -func (s *HookTestSuite) doTestHooksPreDeploy(name string, hooks string) { - p := s.prepareHookTestProject(name, hooks, "") - defer p.cleanup() - s.ensureHookExecuted(p, "hook1", "cm1") - s.ensureHookExecuted(p, "hook1", "cm1") +func doTestHooksPreDeploy(t *testing.T, name string, hooks string) { + s := prepareHookTestProject(t, name, hooks, "") + s.ensureHookExecuted("hook1", "cm1") + s.ensureHookExecuted("hook1", "cm1") } -func (s *HookTestSuite) doTestHooksPostDeploy(name string, hooks string) { - p := s.prepareHookTestProject(name, hooks, "") - defer p.cleanup() - s.ensureHookExecuted(p, "cm1", "hook1") - s.ensureHookExecuted(p, "cm1", "hook1") +func doTestHooksPostDeploy(t *testing.T, name string, hooks string) { + s := prepareHookTestProject(t, name, hooks, "") + s.ensureHookExecuted("cm1", "hook1") + s.ensureHookExecuted("cm1", "hook1") } -func (s *HookTestSuite) doTestHooksPrePostDeploy(name string, hooks string) { - p := s.prepareHookTestProject(name, hooks, "") - defer p.cleanup() - s.ensureHookExecuted(p, "hook1", "cm1", "hook1") - s.ensureHookExecuted(p, "hook1", "cm1", "hook1") +func doTestHooksPrePostDeploy(t *testing.T, name string, hooks string) { + s := prepareHookTestProject(t, name, hooks, "") + s.ensureHookExecuted("hook1", "cm1", "hook1") + s.ensureHookExecuted("hook1", "cm1", "hook1") } -func (s *HookTestSuite) TestHooksPreDeploy() { - s.doTestHooksPreDeploy("pre-deploy", "pre-deploy") +func TestHooksPreDeploy(t *testing.T) { + t.Parallel() + doTestHooksPreDeploy(t, "pre-deploy", "pre-deploy") } -func (s *HookTestSuite) TestHooksPreDeploy2() { +func TestHooksPreDeploy2(t *testing.T) { + t.Parallel() // same as pre-deploy - s.doTestHooksPreDeploy("pre-deploy2", "pre-deploy-initial,pre-deploy-upgrade") + doTestHooksPreDeploy(t, "pre-deploy2", "pre-deploy-initial,pre-deploy-upgrade") } -func (s *HookTestSuite) TestHooksPostDeploy() { - s.doTestHooksPostDeploy("post-deploy", "post-deploy") +func TestHooksPostDeploy(t *testing.T) { + t.Parallel() + doTestHooksPostDeploy(t, "post-deploy", "post-deploy") } -func (s *HookTestSuite) TestHooksPostDeploy2() { +func TestHooksPostDeploy2(t *testing.T) { + t.Parallel() // same as post-deploy - s.doTestHooksPostDeploy("post-deploy2", "post-deploy-initial,post-deploy-upgrade") + doTestHooksPostDeploy(t, "post-deploy2", "post-deploy-initial,post-deploy-upgrade") } -func (s *HookTestSuite) TestHooksPrePostDeploy() { - s.doTestHooksPrePostDeploy("pre-post-deploy", "pre-deploy,post-deploy") +func TestHooksPrePostDeploy(t *testing.T) { + t.Parallel() + doTestHooksPrePostDeploy(t, "pre-post-deploy", "pre-deploy,post-deploy") } -func (s *HookTestSuite) TestHooksPrePostDeploy2() { - s.doTestHooksPrePostDeploy("pre-post-deploy2", "pre-deploy-initial,pre-deploy-upgrade,post-deploy-initial,post-deploy-upgrade") +func TestHooksPrePostDeploy2(t *testing.T) { + t.Parallel() + doTestHooksPrePostDeploy(t, "pre-post-deploy2", "pre-deploy-initial,pre-deploy-upgrade,post-deploy-initial,post-deploy-upgrade") } diff --git a/internal/test-utils/envtest_cluster.go b/internal/test-utils/envtest_cluster.go index f113e87ff..c32b82665 100644 --- a/internal/test-utils/envtest_cluster.go +++ b/internal/test-utils/envtest_cluster.go @@ -13,6 +13,7 @@ import ( "net/http" "sigs.k8s.io/controller-runtime/pkg/envtest" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sync" "testing" ) @@ -30,6 +31,9 @@ type EnvTestCluster struct { callbackServer webhook.Server callbackServerStop context.CancelFunc + + webhookHandlers []*CallbackHandlerEntry + webhookHandlersMutex sync.Mutex } func CreateEnvTestCluster(context string) *EnvTestCluster { diff --git a/internal/test-utils/envtest_cluster_callback.go b/internal/test-utils/envtest_cluster_callback.go index 31a6dc8f4..54b95fc02 100644 --- a/internal/test-utils/envtest_cluster_callback.go +++ b/internal/test-utils/envtest_cluster_callback.go @@ -14,6 +14,10 @@ import ( ) type CallbackHandler func(request admission.Request) +type CallbackHandlerEntry struct { + GVR schema.GroupVersionResource + Callback CallbackHandler +} func (k *EnvTestCluster) buildServeCallback(gvr schema.GroupVersionResource, cb CallbackHandler) http.Handler { wh := &webhook.Admission{ @@ -49,7 +53,7 @@ func (k *EnvTestCluster) startCallbackServer() error { return nil } -func (k *EnvTestCluster) AddWebhookCallback(gvr schema.GroupVersionResource, isNamespaced bool, cb CallbackHandler) { +func (k *EnvTestCluster) InitWebhookCallback(gvr schema.GroupVersionResource, isNamespaced bool) { scope := admissionv1.ClusterScope if isNamespaced { scope = admissionv1.NamespacedScope @@ -103,5 +107,41 @@ func (k *EnvTestCluster) AddWebhookCallback(gvr schema.GroupVersionResource, isN }, }) - k.callbackServer.Register(path, k.buildServeCallback(gvr, cb)) + k.callbackServer.Register(path, k.buildServeCallback(gvr, k.handleWebhook)) +} + +func (k *EnvTestCluster) handleWebhook(request admission.Request) { + k.webhookHandlersMutex.Lock() + defer k.webhookHandlersMutex.Unlock() + + for _, e := range k.webhookHandlers { + if e.GVR.Group == request.Resource.Group && e.GVR.Version == request.Resource.Version && e.GVR.Resource == request.Resource.Resource { + e.Callback(request) + } + } +} + +func (k *EnvTestCluster) AddWebhookHandler(gvr schema.GroupVersionResource, cb CallbackHandler) *CallbackHandlerEntry { + k.webhookHandlersMutex.Lock() + defer k.webhookHandlersMutex.Unlock() + + entry := &CallbackHandlerEntry{ + GVR: gvr, + Callback: cb, + } + k.webhookHandlers = append(k.webhookHandlers, entry) + + return entry +} + +func (k *EnvTestCluster) RemoveWebhookHandler(e *CallbackHandlerEntry) { + k.webhookHandlersMutex.Lock() + defer k.webhookHandlersMutex.Unlock() + old := k.webhookHandlers + k.webhookHandlers = make([]*CallbackHandlerEntry, 0, len(k.webhookHandlers)) + for _, e2 := range old { + if e != e2 { + k.webhookHandlers = append(k.webhookHandlers, e2) + } + } } From 984cd77f3667ef15c7fbd29c45f19a56c13e6845 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 14:05:32 +0200 Subject: [PATCH 0481/2268] tests: Paralellize sealed secrets tests --- e2e/default_clusters.go | 3 + e2e/seal_test.go | 220 +++++++++++++++++----------------------- 2 files changed, 94 insertions(+), 129 deletions(-) diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index ca75f2003..3ef14c5c8 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -1,6 +1,7 @@ package e2e import ( + "github.com/kluctl/kluctl/v2/e2e/test_resources" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "k8s.io/apimachinery/pkg/runtime/schema" "sync" @@ -22,6 +23,7 @@ func init() { if err != nil { panic(err) } + test_resources.ApplyYaml("sealed-secrets.yaml", defaultCluster1) }() go func() { defer wg.Done() @@ -32,6 +34,7 @@ func init() { if err != nil { panic(err) } + test_resources.ApplyYaml("sealed-secrets.yaml", defaultCluster2) }() wg.Wait() } diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 36cc27099..8f1b42dfe 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -13,7 +13,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -23,74 +22,32 @@ import ( "net/http" "net/http/httptest" "path/filepath" - "sync" "testing" "time" ) -type SealedSecretsTestSuite struct { - suite.Suite - - k *test_utils.EnvTestCluster - k2 *test_utils.EnvTestCluster - - certServer1 *certServer - certServer2 *certServer -} - type certServer struct { server http.Server url string certHash string } -func TestSealedSecrets(t *testing.T) { - t.Parallel() - suite.Run(t, &SealedSecretsTestSuite{}) -} - -func (s *SealedSecretsTestSuite) SetupSuite() { - s.k = defaultCluster1 - s.k2 = defaultCluster2 - - var wg sync.WaitGroup - wg.Add(2) - go func() { - test_resources.ApplyYaml("sealed-secrets.yaml", s.k) - wg.Done() - }() - go func() { - test_resources.ApplyYaml("sealed-secrets.yaml", s.k2) - wg.Done() - }() - wg.Wait() +var certServer1 *certServer +var certServer2 *certServer +func init() { var err error - s.certServer1, err = s.startCertServer() + certServer1, err = startCertServer() if err != nil { - s.T().Fatal(err) + panic(err) } - s.certServer2, err = s.startCertServer() + certServer2, err = startCertServer() if err != nil { - s.T().Fatal(err) - } -} - -func (s *SealedSecretsTestSuite) TearDownSuite() { - s.k = nil - s.k2 = nil - - if s.certServer1 != nil { - s.certServer1.server.Close() - } - if s.certServer2 != nil { - s.certServer2.server.Close() + panic(err) } - s.certServer1 = nil - s.certServer2 = nil } -func (s *SealedSecretsTestSuite) startCertServer() (*certServer, error) { +func startCertServer() (*certServer, error) { key, cert, err := crypto.GeneratePrivateKeyAndCert(2048, 10*365*24*time.Hour, "tests.kluctl.io") if err != nil { return nil, err @@ -128,7 +85,7 @@ func (s *SealedSecretsTestSuite) startCertServer() (*certServer, error) { return &cs, nil } -func (s *SealedSecretsTestSuite) addProxyVars(p *testProject) { +func addProxyVars(p *testProject) { f := func(idx int, k *test_utils.EnvTestCluster, cs *certServer) { p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_API_HOST=%s", idx, k.RESTConfig().Host)) p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_NAMESPACE=%s", idx, "kube-system")) @@ -136,34 +93,37 @@ func (s *SealedSecretsTestSuite) addProxyVars(p *testProject) { p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_PORT=%s", idx, "http")) p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_LOCAL_URL=%s", idx, cs.url)) } - f(0, s.k, s.certServer1) - f(1, s.k2, s.certServer2) + f(0, defaultCluster1, certServer1) + f(1, defaultCluster2, certServer2) } -func (s *SealedSecretsTestSuite) prepareSealTest(k *test_utils.EnvTestCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *testProject { +func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *testProject { p := &testProject{} - p.init(s.T(), k, fmt.Sprintf("seal-%s", namespace)) + p.init(t, k, fmt.Sprintf("seal-%s", namespace)) + t.Cleanup(func() { + p.cleanup() + }) if proxy { - s.addProxyVars(p) + addProxyVars(p) } - createNamespace(s.T(), k, namespace) + createNamespace(t, k, namespace) - s.addSecretsSet(p, "test", varsSources) - s.addSecretsSetToTarget(p, "test-target", "test") + addSecretsSet(p, "test", varsSources) + addSecretsSetToTarget(p, "test-target", "test") addSecretDeployment(p, "secret-deployment", secrets, true, resourceOpts{name: "secret", namespace: namespace}) return p } -func (s *SealedSecretsTestSuite) addSecretsSet(p *testProject, name string, varsSources []*uo.UnstructuredObject) { +func addSecretsSet(p *testProject, name string, varsSources []*uo.UnstructuredObject) { p.updateSecretSet(name, func(secretSet *uo.UnstructuredObject) { _ = secretSet.SetNestedField(varsSources, "vars") }) } -func (s *SealedSecretsTestSuite) addSecretsSetToTarget(p *testProject, targetName string, secretSetName string) { +func addSecretsSetToTarget(p *testProject, targetName string, secretSetName string) { p.updateTarget(targetName, func(target *uo.UnstructuredObject) { l, _, _ := target.GetNestedList("sealingConfig", "secretSets") l = append(l, secretSetName) @@ -171,9 +131,8 @@ func (s *SealedSecretsTestSuite) addSecretsSetToTarget(p *testProject, targetNam }) } -func (s *SealedSecretsTestSuite) assertSealedSecret(k *test_utils.EnvTestCluster, namespace string, secretName string, expectedCertHash string, expectedSecrets map[string]string) { - - y := k.MustGet(s.T(), schema.GroupVersionResource{ +func assertSealedSecret(t *testing.T, k *test_utils.EnvTestCluster, namespace string, secretName string, expectedCertHash string, expectedSecrets map[string]string) { + y := k.MustGet(t, schema.GroupVersionResource{ Group: "bitnami.com", Version: "v1alpha1", Resource: "sealedsecrets", @@ -181,17 +140,17 @@ func (s *SealedSecretsTestSuite) assertSealedSecret(k *test_utils.EnvTestCluster h1 := y.GetK8sAnnotation("kluctl.io/sealedsecret-cert-hash") if h1 == nil { - s.T().Fatal("kluctl.io/sealedsecret-cert-hash annotation not found") + t.Fatal("kluctl.io/sealedsecret-cert-hash annotation not found") } - s.Assertions.Equal(expectedCertHash, *h1) + assert.Equal(t, expectedCertHash, *h1) hashesStr := y.GetK8sAnnotation("kluctl.io/sealedsecret-hashes") if hashesStr == nil { - s.T().Fatal("kluctl.io/sealedsecret-hashes annotation not found") + t.Fatal("kluctl.io/sealedsecret-hashes annotation not found") } hashes, err := uo.FromString(*hashesStr) if err != nil { - s.T().Fatal(err) + t.Fatal(err) } expectedHashes := map[string]any{} @@ -199,14 +158,15 @@ func (s *SealedSecretsTestSuite) assertSealedSecret(k *test_utils.EnvTestCluster expectedHashes[k] = seal.HashSecret(k, []byte(v), secretName, namespace, "strict") } - s.Assertions.Equal(expectedHashes, hashes.Object) + assert.Equal(t, expectedHashes, hashes.Object) } -func (s *SealedSecretsTestSuite) TestSeal_WithOperator() { - k := s.k +func TestSeal_WithOperator(t *testing.T) { + t.Parallel() + k := defaultCluster1 namespace := "seal-with-operator" - p := s.prepareSealTest(k, namespace, + p := prepareSealTest(t, k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -219,29 +179,30 @@ func (s *SealedSecretsTestSuite) TestSeal_WithOperator() { }, }), }, true) - defer p.cleanup() p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) - assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func (s *SealedSecretsTestSuite) TestSeal_WithBootstrap() { - k := s.k +func TestSeal_WithBootstrap(t *testing.T) { + // this test must NOT run in parallel + + k := defaultCluster1 namespace := "seal-with-bootstrap" // deleting the crd causes kluctl to not recognize the operator, so it will do a bootstrap _ = k.DynamicClient.Resource(apiextensionsv1.SchemeGroupVersion.WithResource("customresourcedefinitions")). Delete(context.Background(), "sealedsecrets.bitnami.com", metav1.DeleteOptions{}) - p := s.prepareSealTest(k, namespace, + p := prepareSealTest(t, k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -255,38 +216,38 @@ func (s *SealedSecretsTestSuite) TestSeal_WithBootstrap() { }), }, false) - defer p.cleanup() - p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) - assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) test_resources.ApplyYaml("sealed-secrets.yaml", k) p.KluctlMust("deploy", "--yes", "-t", "test-target") - pkCm := k.MustGetCoreV1(s.T(), "configmaps", "kube-system", "sealed-secrets-key-kluctl-bootstrap") + pkCm := k.MustGetCoreV1(t, "configmaps", "kube-system", "sealed-secrets-key-kluctl-bootstrap") certBytes, ok, _ := pkCm.GetNestedString("data", "tls.crt") - s.Assertions.True(ok) + assert.True(t, ok) cert, err := seal.ParseCert([]byte(certBytes)) - s.Assertions.NoError(err) + assert.NoError(t, err) certHash, err := seal.HashPublicKey(cert) - s.Assertions.NoError(err) + assert.NoError(t, err) - s.assertSealedSecret(k, namespace, "secret", certHash, map[string]string{ + assertSealedSecret(t, k, namespace, "secret", certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func (s *SealedSecretsTestSuite) TestSeal_MultipleVarSources() { - k := s.k +func TestSeal_MultipleVarSources(t *testing.T) { + t.Parallel() + + k := defaultCluster1 namespace := "seal-multiple-vs" - p := s.prepareSealTest(k, namespace, + p := prepareSealTest(t, k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -303,26 +264,27 @@ func (s *SealedSecretsTestSuite) TestSeal_MultipleVarSources() { }, }), }, true) - defer p.cleanup() p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) - assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func (s *SealedSecretsTestSuite) TestSeal_MultipleSecretSets() { - k := s.k +func TestSeal_MultipleSecretSets(t *testing.T) { + t.Parallel() + + k := defaultCluster1 namespace := "seal-multiple-ss" - p := s.prepareSealTest(k, namespace, + p := prepareSealTest(t, k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -335,35 +297,35 @@ func (s *SealedSecretsTestSuite) TestSeal_MultipleSecretSets() { }), }, true) - defer p.cleanup() - - s.addSecretsSet(p, "test2", []*uo.UnstructuredObject{ + addSecretsSet(p, "test2", []*uo.UnstructuredObject{ uo.FromMap(map[string]interface{}{ "values": map[string]interface{}{ "s2": "v2", }, }), }) - s.addSecretsSetToTarget(p, "test-target", "test2") + addSecretsSetToTarget(p, "test-target", "test2") p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) - assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func (s *SealedSecretsTestSuite) TestSeal_MultipleTargets() { - k := s.k +func TestSeal_MultipleTargets(t *testing.T) { + t.Parallel() + + k := defaultCluster1 namespace := "seal-multiple-targets" - p := s.prepareSealTest(k, namespace, + p := prepareSealTest(t, k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -376,9 +338,8 @@ func (s *SealedSecretsTestSuite) TestSeal_MultipleTargets() { }, }), }, true) - defer p.cleanup() - s.addSecretsSet(p, "test2", []*uo.UnstructuredObject{ + addSecretsSet(p, "test2", []*uo.UnstructuredObject{ uo.FromMap(map[string]interface{}{ "values": map[string]interface{}{ "s1": "v3", @@ -386,42 +347,44 @@ func (s *SealedSecretsTestSuite) TestSeal_MultipleTargets() { }, }), }) - s.addSecretsSetToTarget(p, "test-target2", "test2") + addSecretsSetToTarget(p, "test-target2", "test2") - p.mergeKubeconfig(s.k2) - createNamespace(s.T(), s.k2, namespace) + p.mergeKubeconfig(defaultCluster2) + createNamespace(t, defaultCluster2, namespace) p.updateTarget("test-target", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(s.k.Context, "context") + _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.updateTarget("test-target2", func(target *uo.UnstructuredObject) { - _ = target.SetNestedField(s.k2.Context, "context") + _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("seal", "-t", "test-target") p.KluctlMust("seal", "-t", "test-target2") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) - assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) - assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target2/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target2/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") p.KluctlMust("deploy", "--yes", "-t", "test-target2") - s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ + assertSealedSecret(t, defaultCluster1, namespace, "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) - s.assertSealedSecret(s.k2, namespace, "secret", s.certServer2.certHash, map[string]string{ + assertSealedSecret(t, defaultCluster2, namespace, "secret", certServer2.certHash, map[string]string{ "s1": "v3", "s2": "v4", }) } -func (s *SealedSecretsTestSuite) TestSeal_File() { - k := s.k +func TestSeal_File(t *testing.T) { + t.Parallel() + + k := defaultCluster1 namespace := "seal-file" - p := s.prepareSealTest(k, namespace, + p := prepareSealTest(t, k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -431,7 +394,6 @@ func (s *SealedSecretsTestSuite) TestSeal_File() { "file": utils.StrPtr("secret-values.yaml"), }), }, true) - defer p.cleanup() p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), "secret-values.yaml", func(o *uo.UnstructuredObject) error { *o = *uo.FromMap(map[string]interface{}{ @@ -446,18 +408,20 @@ func (s *SealedSecretsTestSuite) TestSeal_File() { p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) - assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) } -func (s *SealedSecretsTestSuite) TestSeal_Vault() { - k := s.k +func TestSeal_Vault(t *testing.T) { + t.Parallel() + + k := defaultCluster1 namespace := "seal-vault" server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { @@ -479,11 +443,10 @@ func (s *SealedSecretsTestSuite) TestSeal_Vault() { writer.Header().Set("Content-Type", "application/json") _, _ = writer.Write([]byte(s)) })) - defer server.Close() vaultUrl := server.URL - p := s.prepareSealTest(k, namespace, + p := prepareSealTest(t, k, namespace, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -496,17 +459,16 @@ func (s *SealedSecretsTestSuite) TestSeal_Vault() { }, }), }, true) - defer p.cleanup() p.extraEnv = append(p.extraEnv, "VAULT_TOKEN=root") p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) - assert.FileExists(s.T(), filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - s.assertSealedSecret(k, namespace, "secret", s.certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) From 519ec7ebfa91b996db3721d878b55f90581af75c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 14:09:10 +0200 Subject: [PATCH 0482/2268] chore: Run go mod tidy --- go.mod | 1 - go.sum | 2 -- 2 files changed, 3 deletions(-) diff --git a/go.mod b/go.mod index fbccf67c5..729920c5c 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,6 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 - github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/reflectwalk v1.0.2 github.com/ohler55/ojg v1.14.5 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 379bb254c..e00cb9714 100644 --- a/go.sum +++ b/go.sum @@ -575,8 +575,6 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= -github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= From ca84085fcf8a657343e446e8c1206cece120eda4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 14:26:37 +0200 Subject: [PATCH 0483/2268] fix: Fix check for helm-chart.yaml The check did previously not verify that a / was present before the filename. --- pkg/deployment/deployment_item.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 7f944e92f..873a478c1 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -160,13 +160,19 @@ func (di *DeploymentItem) render(forSeal bool) error { ) } +func (di *DeploymentItem) isHelmChartYaml(p string) bool { + _, file := filepath.Split(p) + file = strings.ToLower(file) + return file == "helm-chart.yml" || file == "helm-chart.yaml" +} + func (di *DeploymentItem) renderHelmCharts() error { if di.dir == nil { return nil } err := filepath.Walk(di.RenderedDir, func(p string, info fs.FileInfo, err error) error { - if !strings.HasSuffix(p, "helm-chart.yml") && !strings.HasSuffix(p, "helm-chart.yaml") { + if !di.isHelmChartYaml(p) { return nil } From 72b777b9084b3f1d96532601b84b55244be49c2b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 15:39:22 +0200 Subject: [PATCH 0484/2268] feat: Allow to add deployment items without a kustomization.yaml kluctl will then generate a simple kustomization.yaml. --- docs/reference/deployments/deployment-yml.md | 16 +++- pkg/deployment/deployment_item.go | 86 ++++++++++++++++++-- pkg/deployment/deployment_project.go | 5 -- 3 files changed, 96 insertions(+), 11 deletions(-) diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index 1d5914919..e1aac4061 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -45,9 +45,23 @@ for details. Individual deployments are performed in parallel, unless a [barrier](#barriers) is encountered which causes kluctl to wait for all previous deployments to finish. +### Simple deployments + +Simple deployments are specified via `path` and are expected to be directories with Kubernetes manifests inside. +Kluctl will internally generate a kustomization.yaml from these manifests and treat the deployment item the same way +as it would treat a [Kustomize deployment](#kustomize-deployments). + +Example: +```yaml +deployments: +- path: path/to/manifests +``` + ### Kustomize deployments -Specifies a [kustomize](https://kustomize.io/) deployment. +When the deployment item directory specified via `path` contains a `kustomization.yaml`, Kluctl will use this file +instead of generating one. + Please see [Kustomize integration](./kustomize.md) for more details. Example: diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 873a478c1..0c53716e9 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -11,6 +11,7 @@ import ( "io/fs" "k8s.io/apimachinery/pkg/runtime/schema" "os" + "path" "path/filepath" "strings" ) @@ -166,6 +167,12 @@ func (di *DeploymentItem) isHelmChartYaml(p string) bool { return file == "helm-chart.yml" || file == "helm-chart.yaml" } +func (di *DeploymentItem) isHelmValuesYaml(p string) bool { + _, file := filepath.Split(p) + file = strings.ToLower(file) + return file == "helm-values.yml" || file == "helm-values.yaml" +} + func (di *DeploymentItem) renderHelmCharts() error { if di.dir == nil { return nil @@ -224,15 +231,23 @@ func (di *DeploymentItem) ListSealedSecrets(subdir string) ([]string, error) { return nil, err } - y, err := uo.FromFile(yaml.FixPathExt(filepath.Join(renderedDir, "kustomization.yml"))) + y, err := di.readKustomizationYaml(subdir) if err != nil { return nil, err } - l, _, err := y.GetNestedStringList("resources") + if y == nil { + y, err = di.generateKustomizationYaml(subdir) + if err != nil { + return nil, err + } + } + + resources, _, err := y.GetNestedStringList("resources") if err != nil { return nil, err } - for _, resource := range l { + + for _, resource := range resources { p := filepath.Clean(filepath.Join(renderedDir, resource)) isDir := utils.IsDirectory(p) @@ -376,6 +391,60 @@ func (di *DeploymentItem) readKustomizationYaml(subDir string) (*uo.Unstructured return ky, err } +func (di *DeploymentItem) generateKustomizationYaml(subDir string) (*uo.UnstructuredObject, error) { + kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.RenderedDir, subDir, "kustomization.yml")) + if utils.IsFile(kustomizeYamlPath) { + return nil, nil + } + + des, err := os.ReadDir(filepath.Join(di.RenderedDir, subDir)) + if err != nil { + return nil, err + } + + var list []any + m := map[string]bool{} + + for _, de := range des { + if de.IsDir() { + continue + } + + lname := strings.ToLower(de.Name()) + resourcePath := "" + + if di.isHelmValuesYaml(de.Name()) { + continue + } else if di.isHelmChartYaml(de.Name()) { + c, err := NewHelmChart(filepath.Join(di.RenderedDir, subDir, de.Name())) + if err != nil { + return nil, err + } + if !utils.IsFile(filepath.Join(di.RenderedDir, subDir, c.GetOutputPath())) { + resourcePath = c.GetOutputPath() + } + } else if strings.HasSuffix(lname, ".yml") || strings.HasSuffix(lname, ".yaml") { + resourcePath = de.Name() + } else if strings.HasSuffix(lname, ".yml"+SealmeExt) || strings.HasSuffix(lname, ".yaml"+SealmeExt) { + resourcePath = de.Name()[:len(de.Name())-len(SealmeExt)] + } + + if resourcePath != "" { + resourcePath = filepath.ToSlash(resourcePath) + resourcePath = path.Clean(resourcePath) + if _, ok := m[resourcePath]; !ok { + m[resourcePath] = true + list = append(list, resourcePath) + } + } + } + + generated := uo.New() + _ = generated.SetNestedField(list, "resources") + + return generated, nil +} + func (di *DeploymentItem) writeKustomizationYaml(ky *uo.UnstructuredObject) error { kustomizeYamlPath := yaml.FixPathExt(filepath.Join(di.RenderedDir, "kustomization.yml")) return yaml.WriteYamlFile(kustomizeYamlPath, ky) @@ -387,7 +456,14 @@ func (di *DeploymentItem) prepareKustomizationYaml() error { return err } if ky == nil { - return nil + ky, err = di.generateKustomizationYaml("") + if err != nil { + return err + } + err = di.writeKustomizationYaml(ky) + if err != nil { + return err + } } overrideNamespace := di.Project.getOverrideNamespace() @@ -404,7 +480,7 @@ func (di *DeploymentItem) prepareKustomizationYaml() error { di.Barrier = utils.ParseBoolOrFalse(ky.GetK8sAnnotation("kluctl.io/barrier")) di.WaitReadiness = utils.ParseBoolOrFalse(ky.GetK8sAnnotation("kluctl.io/wait-readiness")) - // Save modified kustomize.yml + // Save modified kustomization.yml return di.writeKustomizationYaml(ky) } diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index ecd599c31..04a06dac7 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -136,11 +136,6 @@ func (p *DeploymentProject) checkDeploymentDirs() error { if !utils.IsDirectory(diDir) { return fmt.Errorf("deployment path is not a directory: %s", *di.Path) } - - pth := yaml.FixPathExt(filepath.Join(diDir, "kustomization.yml")) - if !utils.IsFile(pth) { - return fmt.Errorf("%s not found or not a file", pth) - } } return nil } From 3f86f9e0578d0fa22c6e4cc5c14c16b33253fc71 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 15:50:22 +0200 Subject: [PATCH 0485/2268] tests: Cleanup testProject as part of testing cleanup --- e2e/args_test.go | 2 -- e2e/contexts_test.go | 6 ------ e2e/deploy_test.go | 1 - e2e/flux_test.go | 2 -- e2e/hooks_test.go | 1 - e2e/inclusion_test.go | 14 -------------- e2e/no_target_test.go | 1 - e2e/project.go | 14 +++----------- e2e/seal_test.go | 4 +--- 9 files changed, 4 insertions(+), 41 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index 026f3bee0..527a75ce3 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -14,7 +14,6 @@ func TestArgs(t *testing.T) { p := &testProject{} p.init(t, k, "args") - defer p.cleanup() createNamespace(t, k, p.projectName) @@ -121,7 +120,6 @@ func TestArgsFromEnv(t *testing.T) { p := &testProject{} p.init(t, k, "args-from-envs") - defer p.cleanup() createNamespace(t, k, p.projectName) diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index 7de30a13f..29fa1d133 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -26,7 +26,6 @@ func TestContextCurrent(t *testing.T) { t.Parallel() p := prepareContextTest(t, "context-current") - defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { // no context set, assume the current one is used @@ -48,7 +47,6 @@ func TestContext1(t *testing.T) { t.Parallel() p := prepareContextTest(t, "context-1") - defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") @@ -63,7 +61,6 @@ func TestContext2(t *testing.T) { t.Parallel() p := prepareContextTest(t, "context-2") - defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster2.Context, "context") @@ -78,7 +75,6 @@ func TestContext1And2(t *testing.T) { t.Parallel() p := prepareContextTest(t, "context-1-and-2") - defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") @@ -99,7 +95,6 @@ func TestContextSwitch(t *testing.T) { t.Parallel() p := prepareContextTest(t, "context-switch") - defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") @@ -121,7 +116,6 @@ func TestContextOverride(t *testing.T) { t.Parallel() p := prepareContextTest(t, "context-override") - defer p.cleanup() p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") diff --git a/e2e/deploy_test.go b/e2e/deploy_test.go index c6b0493da..6c3029ea8 100644 --- a/e2e/deploy_test.go +++ b/e2e/deploy_test.go @@ -11,7 +11,6 @@ func TestCommandDeploySimple(t *testing.T) { p := &testProject{} p.init(t, k, "simple") - defer p.cleanup() createNamespace(t, k, p.projectName) diff --git a/e2e/flux_test.go b/e2e/flux_test.go index 33f30dbde..b7b4bcb53 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -39,8 +39,6 @@ func TestFluxCommands(t *testing.T) { p := &testProject{} p.init(t, k, "flux-test") - defer p.cleanup() - var wg sync.WaitGroup wg.Add(2) go func() { diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 29a54f239..af1c2221c 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -105,7 +105,6 @@ func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletion s.p.init(t, s.k, namespace) t.Cleanup(func() { s.removeWebhook() - s.p.cleanup() }) createNamespace(s.t, s.k, namespace) diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 99e1beaab..edba11b1a 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -9,16 +9,9 @@ import ( ) func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bool) (*testProject, *test_utils.EnvTestCluster) { - isDone := false - k := defaultCluster1 p := &testProject{} p.init(t, k, namespace) - defer func() { - if !isDone { - p.cleanup() - } - }() createNamespace(t, k, p.projectName) @@ -45,7 +38,6 @@ func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bo addConfigMapDeployment(p, "include3/icm5", nil, resourceOpts{name: "icm5", namespace: p.projectName, tags: []string{"itag5", "itag6"}}) } - isDone = true return p, k } @@ -74,7 +66,6 @@ func assertExistsHelper(t *testing.T, p *testProject, k *test_utils.EnvTestClust func TestInclusionTags(t *testing.T) { t.Parallel() p, k := prepareInclusionTestProject(t, "inclusion-tags", false) - defer p.cleanup() shouldExists := make(map[string]bool) doAssertExists := func(add ...string) { @@ -109,7 +100,6 @@ func TestInclusionTags(t *testing.T) { func TestExclusionTags(t *testing.T) { t.Parallel() p, k := prepareInclusionTestProject(t, "inclusion-exclusion", false) - defer p.cleanup() shouldExists := make(map[string]bool) doAssertExists := func(add ...string) { @@ -134,7 +124,6 @@ func TestExclusionTags(t *testing.T) { func TestInclusionIncludeDirs(t *testing.T) { t.Parallel() p, k := prepareInclusionTestProject(t, "inclusion-dirs", true) - defer p.cleanup() shouldExists := make(map[string]bool) doAssertExists := func(add ...string) { @@ -156,7 +145,6 @@ func TestInclusionIncludeDirs(t *testing.T) { func TestInclusionDeploymentDirs(t *testing.T) { t.Parallel() p, k := prepareInclusionTestProject(t, "inclusion-kustomize-dirs", true) - defer p.cleanup() shouldExists := make(map[string]bool) doAssertExists := func(add ...string) { @@ -184,7 +172,6 @@ func TestInclusionDeploymentDirs(t *testing.T) { func TestInclusionPrune(t *testing.T) { t.Parallel() p, k := prepareInclusionTestProject(t, "inclusion-prune", false) - defer p.cleanup() shouldExists := make(map[string]bool) doAssertExists := func(add []string, remove []string) { @@ -218,7 +205,6 @@ func TestInclusionPrune(t *testing.T) { func TestInclusionDelete(t *testing.T) { t.Parallel() p, k := prepareInclusionTestProject(t, "inclusion-delete", false) - defer p.cleanup() shouldExists := make(map[string]bool) doAssertExists := func(add []string, remove []string) { diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index 31351c3eb..7c2f74f61 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -28,7 +28,6 @@ func TestNoTarget(t *testing.T) { t.Parallel() p := prepareNoTargetTest(t, "no-target") - defer p.cleanup() p.KluctlMust("deploy", "--yes") cm := assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") diff --git a/e2e/project.go b/e2e/project.go index b6f74c66f..83e3a6ecc 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -47,20 +47,12 @@ func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster, projectNa } _ = tmpFile.Close() p.mergedKubeconfig = tmpFile.Name() + t.Cleanup(func() { + os.Remove(p.mergedKubeconfig) + }) p.mergeKubeconfig(k) } -func (p *testProject) cleanup() { - if p.gitServer != nil { - p.gitServer.Cleanup() - p.gitServer = nil - } - if p.mergedKubeconfig != "" { - _ = os.Remove(p.mergedKubeconfig) - p.mergedKubeconfig = "" - } -} - func (p *testProject) mergeKubeconfig(k *test_utils.EnvTestCluster) { p.updateMergedKubeconfig(func(config *clientcmdapi.Config) { nkcfg, err := clientcmd.Load(k.Kubeconfig) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 8f1b42dfe..ab7fb7fbe 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -100,9 +100,7 @@ func addProxyVars(p *testProject) { func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *testProject { p := &testProject{} p.init(t, k, fmt.Sprintf("seal-%s", namespace)) - t.Cleanup(func() { - p.cleanup() - }) + if proxy { addProxyVars(p) } From 105be5a7ab561616888f2503e5cc46183d415c92 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 16:09:45 +0200 Subject: [PATCH 0486/2268] tests: Add test for generated kustomizations --- e2e/deploy_test.go | 34 ------------- e2e/deployment_items_test.go | 83 +++++++++++++++++++++++++++++++ e2e/project.go | 12 +++-- e2e/seal_test.go | 4 +- e2e/utils_resources.go | 31 ++++++++---- internal/test-utils/git_server.go | 8 +++ 6 files changed, 124 insertions(+), 48 deletions(-) delete mode 100644 e2e/deploy_test.go create mode 100644 e2e/deployment_items_test.go diff --git a/e2e/deploy_test.go b/e2e/deploy_test.go deleted file mode 100644 index 6c3029ea8..000000000 --- a/e2e/deploy_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package e2e - -import ( - "testing" -) - -func TestCommandDeploySimple(t *testing.T) { - t.Parallel() - - k := defaultCluster1 - - p := &testProject{} - p.init(t, k, "simple") - - createNamespace(t, k, p.projectName) - - p.updateTarget("test", nil) - - addConfigMapDeployment(p, "cm", nil, resourceOpts{ - name: "cm", - namespace: p.projectName, - }) - p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.projectName, "cm") - - addConfigMapDeployment(p, "cm2", nil, resourceOpts{ - name: "cm2", - namespace: p.projectName, - }) - p.KluctlMust("deploy", "--yes", "-t", "test", "--dry-run") - assertConfigMapNotExists(t, k, p.projectName, "cm2") - p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.projectName, "cm2") -} diff --git a/e2e/deployment_items_test.go b/e2e/deployment_items_test.go new file mode 100644 index 000000000..c1a8ffe8e --- /dev/null +++ b/e2e/deployment_items_test.go @@ -0,0 +1,83 @@ +package e2e + +import ( + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "testing" +) + +func TestKustomize(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k, "di-kustomize") + + createNamespace(t, k, p.projectName) + + p.updateTarget("test", nil) + + addConfigMapDeployment(p, "cm", nil, resourceOpts{ + name: "cm", + namespace: p.projectName, + }) + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.projectName, "cm") + + addConfigMapDeployment(p, "cm2", nil, resourceOpts{ + name: "cm2", + namespace: p.projectName, + }) + p.KluctlMust("deploy", "--yes", "-t", "test", "--dry-run") + assertConfigMapNotExists(t, k, p.projectName, "cm2") + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.projectName, "cm2") +} + +func TestGeneratedKustomize(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k, "di-generated-kustomize") + + createNamespace(t, k, p.projectName) + + p.updateTarget("test", nil) + + p.updateDeploymentYaml("", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField([]any{ + map[string]any{ + "path": "generated-kustomize", + }, + }, "deployments") + return nil + }) + p.updateYaml("generated-kustomize/cm1.yaml", func(o *uo.UnstructuredObject) error { + *o = *createConfigMapObject(nil, resourceOpts{ + name: "cm1", + namespace: p.projectName, + }) + return nil + }, "") + p.updateYaml("generated-kustomize/cm2.yaml", func(o *uo.UnstructuredObject) error { + *o = *createConfigMapObject(nil, resourceOpts{ + name: "cm2", + namespace: p.projectName, + }) + return nil + }, "") + p.updateYaml("generated-kustomize/cm3._yaml", func(o *uo.UnstructuredObject) error { + *o = *createConfigMapObject(nil, resourceOpts{ + name: "cm3", + namespace: p.projectName, + }) + return nil + }, "") + + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.projectName, "cm1") + assertConfigMapExists(t, k, p.projectName, "cm2") + assertConfigMapNotExists(t, k, p.projectName, "cm3") +} diff --git a/e2e/project.go b/e2e/project.go index 83e3a6ecc..f68025aef 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -82,11 +82,11 @@ func (p *testProject) updateMergedKubeconfig(cb func(config *clientcmdapi.Config } func (p *testProject) updateKluctlYaml(update func(o *uo.UnstructuredObject) error) { - p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), ".kluctl.yml", update, "") + p.updateYaml(".kluctl.yml", update, "") } func (p *testProject) updateDeploymentYaml(dir string, update func(o *uo.UnstructuredObject) error) { - p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { + p.updateYaml(filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { if dir == "." { o.SetNestedField(p.projectName, "commonLabels", "project_name") } @@ -94,6 +94,12 @@ func (p *testProject) updateDeploymentYaml(dir string, update func(o *uo.Unstruc }, "") } +func (p *testProject) updateYaml(path string, update func(o *uo.UnstructuredObject) error, message string) { + p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), path, func(o *uo.UnstructuredObject) error { + return update(o) + }, message) +} + func (p *testProject) getDeploymentYaml(dir string) *uo.UnstructuredObject { o, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, "deployment.yml")) if err != nil { @@ -130,7 +136,7 @@ func (p *testProject) updateKustomizeDeployment(dir string, update func(o *uo.Un wt := p.gitServer.GetWorktree(p.getKluctlProjectRepo()) pth := filepath.Join(dir, "kustomization.yml") - p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), pth, func(o *uo.UnstructuredObject) error { + p.updateYaml(pth, func(o *uo.UnstructuredObject) error { return update(o, wt) }, fmt.Sprintf("Update kustomization.yml for %s", dir)) } diff --git a/e2e/seal_test.go b/e2e/seal_test.go index ab7fb7fbe..7ac2ef26a 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -110,7 +110,7 @@ func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, namespace strin addSecretsSet(p, "test", varsSources) addSecretsSetToTarget(p, "test-target", "test") - addSecretDeployment(p, "secret-deployment", secrets, true, resourceOpts{name: "secret", namespace: namespace}) + addSecretDeployment(p, "secret-deployment", secrets, resourceOpts{name: "secret", namespace: namespace}) return p } @@ -393,7 +393,7 @@ func TestSeal_File(t *testing.T) { }), }, true) - p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), "secret-values.yaml", func(o *uo.UnstructuredObject) error { + p.updateYaml("secret-values.yaml", func(o *uo.UnstructuredObject) error { *o = *uo.FromMap(map[string]interface{}{ "secrets": map[string]interface{}{ "s1": "v1", diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 4bf65af62..073b246c5 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -28,25 +28,38 @@ func mergeMetadata(o *uo.UnstructuredObject, opts resourceOpts) { } } -func addConfigMapDeployment(p *testProject, dir string, data map[string]string, opts resourceOpts) { +func createCoreV1Object(kind string, opts resourceOpts) *uo.UnstructuredObject { o := uo.New() - o.SetK8sGVKs("", "v1", "ConfigMap") + o.SetK8sGVKs("", "v1", kind) mergeMetadata(o, opts) + return o +} + +func createConfigMapObject(data map[string]string, opts resourceOpts) *uo.UnstructuredObject { + o := createCoreV1Object("ConfigMap", opts) if data != nil { o.SetNestedField(data, "data") } - p.addKustomizeDeployment(dir, []kustomizeResource{ - {fmt.Sprintf("configmap-%s.yml", opts.name), "", o}, - }, opts.tags) + return o } -func addSecretDeployment(p *testProject, dir string, data map[string]string, sealedSecret bool, opts resourceOpts) { - o := uo.New() - o.SetK8sGVKs("", "v1", "Secret") - mergeMetadata(o, opts) +func createSecretObject(data map[string]string, opts resourceOpts) *uo.UnstructuredObject { + o := createCoreV1Object("ConfigMap", opts) if data != nil { o.SetNestedField(data, "stringData") } + return o +} + +func addConfigMapDeployment(p *testProject, dir string, data map[string]string, opts resourceOpts) { + o := createConfigMapObject(data, opts) + p.addKustomizeDeployment(dir, []kustomizeResource{ + {fmt.Sprintf("configmap-%s.yml", opts.name), "", o}, + }, opts.tags) +} + +func addSecretDeployment(p *testProject, dir string, data map[string]string, opts resourceOpts) { + o := createSecretObject(data, opts) fname := fmt.Sprintf("secret-%s.yml", opts.name) p.addKustomizeDeployment(dir, []kustomizeResource{ {fname, fname + ".sealme", o}, diff --git a/internal/test-utils/git_server.go b/internal/test-utils/git_server.go index 45a969474..4ed9f70da 100644 --- a/internal/test-utils/git_server.go +++ b/internal/test-utils/git_server.go @@ -150,6 +150,14 @@ func (p *GitServer) CommitFiles(repo string, add []string, all bool, message str func (p *GitServer) CommitYaml(repo string, pth string, message string, y *uo.UnstructuredObject) { fullPath := filepath.Join(p.LocalRepoDir(repo), pth) + dir, _ := filepath.Split(fullPath) + if dir != "" { + err := os.MkdirAll(dir, 0o700) + if err != nil { + panic(err) + } + } + err := yaml.WriteYamlFile(fullPath, y) if err != nil { p.t.Fatal(err) From 0323bf04f825be439053022c7300cc8fbdcbba41 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 16:20:36 +0200 Subject: [PATCH 0487/2268] tests: Add test for sealing of multiple secrets --- e2e/seal_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 7ac2ef26a..801c5b63b 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -376,6 +376,47 @@ func TestSeal_MultipleTargets(t *testing.T) { }) } +func TestSeal_MultipleSecrets(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + namespace := "seal-multiple-secrets" + + secret1 := map[string]string{ + "s1": "{{ secrets.s1 }}", + } + secret2 := map[string]string{ + "s2": "{{ secrets.s2 }}", + } + + p := prepareSealTest(t, k, namespace, + secret1, + []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s1": "v1", + "s2": "v2", + }, + }), + }, true) + addSecretDeployment(p, "secret-deployment2", secret2, resourceOpts{name: "secret2", namespace: namespace}) + + p.KluctlMust("seal", "-t", "test-target") + + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment2/test-target/secret-secret2.yml")) + + p.KluctlMust("deploy", "--yes", "-t", "test-target") + + assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ + "s1": "v1", + }) + assertSealedSecret(t, k, namespace, "secret2", certServer1.certHash, map[string]string{ + "s2": "v2", + }) +} + func TestSeal_File(t *testing.T) { t.Parallel() From 340bd4b67c645a9fc7ec941a568528e6f2b7b085 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 16:33:46 +0200 Subject: [PATCH 0488/2268] test: Add test for sealing of multiple secrets via a single .sealme file --- e2e/seal_test.go | 46 +++++++++++++++++++++++++++++++ internal/test-utils/git_server.go | 26 +++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 801c5b63b..9f5e00490 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -417,6 +417,52 @@ func TestSeal_MultipleSecrets(t *testing.T) { }) } +func TestSeal_MultipleSecretsInOneFile(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + namespace := "seal-multiple-secrets2" + + secret1 := map[string]string{ + "s1": "{{ secrets.s1 }}", + } + secret2 := map[string]string{ + "s2": "{{ secrets.s2 }}", + } + secret2Text, _ := yaml.WriteYamlString(createSecretObject(secret2, resourceOpts{name: "secret2", namespace: namespace})) + + p := prepareSealTest(t, k, namespace, + secret1, + []*uo.UnstructuredObject{ + uo.FromMap(map[string]interface{}{ + "values": map[string]interface{}{ + "s1": "v1", + "s2": "v2", + }, + }), + }, true) + + p.gitServer.UpdateFile(p.getKluctlProjectRepo(), "secret-deployment/secret-secret.yml.sealme", func(f string) (string, error) { + f += "---\n" + f += secret2Text + return f, nil + }, "") + + p.KluctlMust("seal", "-t", "test-target") + + sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) + + p.KluctlMust("deploy", "--yes", "-t", "test-target") + + assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ + "s1": "v1", + }) + assertSealedSecret(t, k, namespace, "secret2", certServer1.certHash, map[string]string{ + "s2": "v2", + }) +} + func TestSeal_File(t *testing.T) { t.Parallel() diff --git a/internal/test-utils/git_server.go b/internal/test-utils/git_server.go index 4ed9f70da..070e4c98e 100644 --- a/internal/test-utils/git_server.go +++ b/internal/test-utils/git_server.go @@ -168,6 +168,32 @@ func (p *GitServer) CommitYaml(repo string, pth string, message string, y *uo.Un p.CommitFiles(repo, []string{pth}, false, message) } +func (p *GitServer) UpdateFile(repo string, pth string, update func(f string) (string, error), message string) { + fullPath := filepath.Join(p.LocalRepoDir(repo), pth) + f := "" + if utils.Exists(fullPath) { + b, err := os.ReadFile(fullPath) + if err != nil { + p.t.Fatal(err) + } + f = string(b) + } + + newF, err := update(f) + if err != nil { + p.t.Fatal(err) + } + + if f == newF { + return + } + err = os.WriteFile(fullPath, []byte(newF), 0o600) + if err != nil { + p.t.Fatal(err) + } + p.CommitFiles(repo, []string{pth}, false, message) +} + func (p *GitServer) UpdateYaml(repo string, pth string, update func(o *uo.UnstructuredObject) error, message string) { fullPath := filepath.Join(p.LocalRepoDir(repo), pth) From b6b5956472d451baf36d84e7af70dbe99db341b7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 17:11:08 +0200 Subject: [PATCH 0489/2268] feat: Allow to have multiple secrets in a single .sealme file --- pkg/seal/sealer.go | 160 +++++++++++++++++++++++++++++---------------- pkg/utils/uo/uo.go | 12 ++++ 2 files changed, 115 insertions(+), 57 deletions(-) diff --git a/pkg/seal/sealer.go b/pkg/seal/sealer.go index e821f2e99..3055768cf 100644 --- a/pkg/seal/sealer.go +++ b/pkg/seal/sealer.go @@ -17,7 +17,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "os" "path/filepath" - "reflect" "strconv" ) @@ -93,18 +92,88 @@ func (s *Sealer) encryptSecret(secret []byte, secretName string, secretNamespace return base64.StdEncoding.EncodeToString(b), nil } +type sealedSecret struct { + content *uo.UnstructuredObject + hashes *uo.UnstructuredObject + certHash string +} + +func buildSecretRef(o *uo.UnstructuredObject) string { + return fmt.Sprintf("%s/%s", o.GetK8sNamespace(), o.GetK8sName()) +} + +func (s *Sealer) loadExistingSealedSecrets(p string) (map[string]*sealedSecret, error) { + ret := map[string]*sealedSecret{} + + if !utils.Exists(p) { + return ret, nil + } + + list, err := uo.FromFileMulti(p) + if err != nil { + return nil, err + } + if len(list) != 1 { + err = nil + } + + for _, x := range list { + var ss sealedSecret + + ss.content = x + + a := x.GetK8sAnnotation(hashAnnotation) + if a != nil { + ss.hashes, _ = uo.FromString(*a) + } + a = x.GetK8sAnnotation(certHashAnnotation) + if a != nil { + ss.certHash = *a + } + if ss.hashes == nil { + ss.hashes = uo.New() + } + + ret[buildSecretRef(x)] = &ss + } + return ret, nil +} + func (s *Sealer) SealFile(p string, targetFile string) error { - baseName := filepath.Base(targetFile) err := os.MkdirAll(filepath.Dir(targetFile), 0o700) if err != nil { return err } - o, err := uo.FromFile(p) + existingSealedSecrets, err := s.loadExistingSealedSecrets(targetFile) + if err != nil { + return err + } + + secrets, err := uo.FromFileMulti(p) + if err != nil { + return err + } + + var result []any + + for _, o := range secrets { + existing, _ := existingSealedSecrets[buildSecretRef(o)] + newSealedSecret, err := s.sealSecret(o, existing) + if err != nil { + return err + } + result = append(result, newSealedSecret.content) + } + + err = yaml.WriteYamlAllFile(targetFile, result) if err != nil { return err } + return nil +} +func (s *Sealer) sealSecret(o *uo.UnstructuredObject, existing *sealedSecret) (*sealedSecret, error) { secretName := o.ToUnstructured().GetName() secretNamespace := o.ToUnstructured().GetNamespace() if secretNamespace == "" { @@ -112,7 +181,7 @@ func (s *Sealer) SealFile(p string, targetFile string) error { } secretType, ok, err := o.GetNestedString("type") if err != nil { - return err + return nil, err } if !ok { secretType = "Opaque" @@ -137,53 +206,34 @@ func (s *Sealer) SealFile(p string, targetFile string) error { scope = &x } - var existingContent *uo.UnstructuredObject - var existingHashes *uo.UnstructuredObject - var existingCertHash string - - if utils.Exists(targetFile) { - existingContent, err = uo.FromFile(targetFile) - a := existingContent.GetK8sAnnotation(hashAnnotation) - if a != nil { - existingHashes, _ = uo.FromString(*a) - } - a = existingContent.GetK8sAnnotation(certHashAnnotation) - if a != nil { - existingCertHash = *a - } - } - if existingHashes == nil { - existingHashes = uo.New() - } - secrets := make(map[string][]byte) data, ok, err := o.GetNestedObject("data") if err != nil { - return err + return nil, err } if ok { for k, v := range data.Object { s, ok := v.(string) if !ok { - return fmt.Errorf("%s is not a string", k) + return nil, fmt.Errorf("%s is not a string", k) } secrets[k], err = base64.StdEncoding.DecodeString(s) if err != nil { - return fmt.Errorf("failed to decode base64 string for secret %s and key %s", secretName, k) + return nil, fmt.Errorf("failed to decode base64 string for secret %s and key %s", secretName, k) } } } stringData, ok, err := o.GetNestedObject("stringData") if err != nil { - return err + return nil, err } if ok { for k, v := range stringData.Object { s, ok := v.(string) if !ok { - return fmt.Errorf("%s is not a string", k) + return nil, fmt.Errorf("%s is not a string", k) } secrets[k] = []byte(s) } @@ -191,47 +241,52 @@ func (s *Sealer) SealFile(p string, targetFile string) error { resultSecretHashes := make(map[string]string) - result := uo.New() - result.SetK8sGVK(schema.GroupVersionKind{Group: "bitnami.com", Version: "v1alpha1", Kind: "SealedSecret"}) - result.SetK8sName(secretName) - result.SetK8sNamespace(secretNamespace) - result.SetK8sAnnotation("sealedsecrets.bitnami.com/scope", *scope) + var result sealedSecret + result.content = uo.New() + result.content.SetK8sGVK(schema.GroupVersionKind{Group: "bitnami.com", Version: "v1alpha1", Kind: "SealedSecret"}) + result.content.SetK8sName(secretName) + result.content.SetK8sNamespace(secretNamespace) + result.content.SetK8sAnnotation("sealedsecrets.bitnami.com/scope", *scope) if *scope == "namespace-wide" { - result.SetK8sAnnotation("sealedsecrets.bitnami.com/namespace-wide", "true") + result.content.SetK8sAnnotation("sealedsecrets.bitnami.com/namespace-wide", "true") } if *scope == "cluster-wide" { - result.SetK8sAnnotation("sealedsecrets.bitnami.com/cluster-wide", "true") + result.content.SetK8sAnnotation("sealedsecrets.bitnami.com/cluster-wide", "true") } - _ = result.SetNestedField(secretType, "spec", "template", "type") + _ = result.content.SetNestedField(secretType, "spec", "template", "type") metadata, ok, _ := o.GetNestedObject("metadata") if ok { - result.SetNestedField(metadata.Object, "spec", "template", "metadata") + result.content.SetNestedField(metadata.Object, "spec", "template", "metadata") } resealAll := false if s.forceReseal { resealAll = true status.Info(s.ctx, "Forcing reseal of secrets in %s", secretName) - } else if existingCertHash != s.certHash { + } else if existing == nil || existing.certHash != s.certHash { resealAll = true status.Info(s.ctx, "Cert for secret %s has changed, forcing reseal", secretName) } for k, v := range secrets { hash := HashSecret(k, v, secretName, secretNamespace, *scope) - existingHash, _, _ := existingHashes.GetNestedString(k) - doEncrypt := resealAll + var existingHash string + if existing != nil { + existingHash, _, _ = existing.hashes.GetNestedString(k) + } + + doEncrypt := existing == nil || resealAll if !doEncrypt && hash != existingHash { status.Info(s.ctx, "Secret %s and key %s has changed, resealing", secretName, k) doEncrypt = true } if !doEncrypt { - e, ok, _ := existingContent.GetNestedString("spec", "encryptedData", k) + e, ok, _ := existing.content.GetNestedString("spec", "encryptedData", k) if ok { status.Trace(s.ctx, "Secret %s and key %s is unchanged", secretName, k) - result.SetNestedField(e, "spec", "encryptedData", k) + result.content.SetNestedField(e, "spec", "encryptedData", k) resultSecretHashes[k] = hash continue } else { @@ -242,27 +297,18 @@ func (s *Sealer) SealFile(p string, targetFile string) error { e, err := s.encryptSecret(v, secretName, secretNamespace, *scope) if err != nil { - return fmt.Errorf("failed to encrypt secret %s with key %s", secretName, k) + return nil, fmt.Errorf("failed to encrypt secret %s with key %s", secretName, k) } - result.SetNestedField(e, "spec", "encryptedData", k) + result.content.SetNestedField(e, "spec", "encryptedData", k) resultSecretHashes[k] = hash } resultSecretHashesStr, err := yaml.WriteYamlString(resultSecretHashes) if err != nil { - return err - } - result.SetK8sAnnotation(hashAnnotation, resultSecretHashesStr) - result.SetK8sAnnotation(certHashAnnotation, s.certHash) - - if reflect.DeepEqual(existingContent, result) { - status.Info(s.ctx, "Skipped %s as it did not change", baseName) - return nil + return nil, err } + result.content.SetK8sAnnotation(hashAnnotation, resultSecretHashesStr) + result.content.SetK8sAnnotation(certHashAnnotation, s.certHash) - err = yaml.WriteYamlFile(targetFile, result) - if err != nil { - return err - } - return nil + return &result, nil } diff --git a/pkg/utils/uo/uo.go b/pkg/utils/uo/uo.go index 8630e1a15..d5190d1ae 100644 --- a/pkg/utils/uo/uo.go +++ b/pkg/utils/uo/uo.go @@ -110,6 +110,18 @@ func FromStringMulti(s string) ([]*UnstructuredObject, error) { if err != nil { return nil, err } + return fromAnyList(ifs) +} + +func FromFileMulti(p string) ([]*UnstructuredObject, error) { + ifs, err := yaml.ReadYamlAllFile(p) + if err != nil { + return nil, err + } + return fromAnyList(ifs) +} + +func fromAnyList(ifs []any) ([]*UnstructuredObject, error) { var ret []*UnstructuredObject for _, i := range ifs { m, ok := i.(map[string]interface{}) From eb2d5d6861bb69cd005011fcf224e7506c10132d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 17:23:04 +0200 Subject: [PATCH 0490/2268] chore: Run go get -u ./... --- go.mod | 42 +++++++++++++-------------- go.sum | 91 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 67 insertions(+), 66 deletions(-) diff --git a/go.mod b/go.mod index 729920c5c..b422ea1b5 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.116 + github.com/aws/aws-sdk-go v1.44.122 github.com/bitnami-labs/sealed-secrets v0.19.1 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible @@ -15,7 +15,7 @@ require ( github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 - github.com/google/go-containerregistry v0.11.0 + github.com/google/go-containerregistry v0.12.0 github.com/hashicorp/vault/api v1.8.1 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 @@ -32,18 +32,18 @@ require ( github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.9.0 github.com/sirupsen/logrus v1.9.0 - github.com/spf13/cobra v1.6.0 + github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.13.0 github.com/stretchr/testify v1.8.0 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.2 - golang.org/x/crypto v0.0.0-20221012134737-56aed061732a - golang.org/x/net v0.0.0-20221014081412-f15817d10f9b - golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 - golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 - golang.org/x/term v0.0.0-20220919170432-7a66f970e087 - golang.org/x/text v0.3.8 + golang.org/x/crypto v0.1.0 + golang.org/x/net v0.1.0 + golang.org/x/sync v0.1.0 + golang.org/x/sys v0.1.0 + golang.org/x/term v0.1.0 + golang.org/x/text v0.4.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.1 @@ -71,7 +71,7 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/BurntSushi/toml v1.2.0 // indirect + github.com/BurntSushi/toml v1.2.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect @@ -87,10 +87,10 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.2.0 // indirect - github.com/containerd/containerd v1.6.8 // indirect + github.com/containerd/containerd v1.6.9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.19+incompatible // indirect - github.com/docker/docker v20.10.19+incompatible // indirect + github.com/docker/cli v20.10.20+incompatible // indirect + github.com/docker/docker v20.10.20+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -105,7 +105,7 @@ require ( github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect - github.com/go-gorp/gorp/v3 v3.0.2 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // 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.22.3 // indirect @@ -179,7 +179,7 @@ require ( github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/rivo/uniseg v0.4.2 // indirect @@ -198,15 +198,15 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.etcd.io/etcd/api/v3 v3.5.5 // indirect - go.starlark.net v0.0.0-20221010140840-6bf6f0955179 // indirect + go.starlark.net v0.0.0-20221020143700-22309ac47eac // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect - golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/mod v0.6.0 // indirect + golang.org/x/oauth2 v0.1.0 // indirect + golang.org/x/time v0.1.0 // indirect + golang.org/x/tools v0.2.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a // indirect + google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect google.golang.org/grpc v1.50.1 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index e00cb9714..7e295f7f2 100644 --- a/go.sum +++ b/go.sum @@ -63,8 +63,8 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= -github.com/BurntSushi/toml v1.2.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/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -114,8 +114,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.116 h1:NpLIhcvLWXJZAEwvPj3TDHeqp7DleK6ZUVYyW01WNHY= -github.com/aws/aws-sdk-go v1.44.116/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo= +github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= 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= @@ -155,9 +155,9 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht 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/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= -github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs= -github.com/containerd/containerd v1.6.8/go.mod h1:By6p5KqPK0/7/CgO/A6t/Gz+CUYUu2zf1hUaaymVXB0= -github.com/containerd/stargz-snapshotter/estargz v0.12.0 h1:idtwRTLjk2erqiYhPWy2L844By8NRFYEwYHcXhoIWPM= +github.com/containerd/containerd v1.6.9 h1:IN/r8DUes/B5lEGTNfIiUkfZBtIQJGx2ai703dV6lRA= +github.com/containerd/containerd v1.6.9/go.mod h1:XVicUvkxOrftE2Q1YWUXgZwkkAxwQYNOFzYWvfVfEfQ= +github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= 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/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -172,12 +172,12 @@ 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/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= -github.com/docker/cli v20.10.19+incompatible h1:VKVBUb0KY/bx0FUCrCiNCL8wqgy8VxQli1dtNTn38AE= -github.com/docker/cli v20.10.19+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.20+incompatible h1:lWQbHSHUFs7KraSN2jOJK7zbMS2jNCHI4mt4xUFUVQ4= +github.com/docker/cli v20.10.20+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.19+incompatible h1:lzEmjivyNHFHMNAFLXORMBXyGIhw/UP4DvJwvyKYq64= -github.com/docker/docker v20.10.19+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.20+incompatible h1:kH9tx6XO+359d+iAkumyKDc5Q1kOwPuAUaeri48nD6E= +github.com/docker/docker v20.10.20+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -241,8 +241,9 @@ github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti 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-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4= github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= +github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= 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= @@ -348,8 +349,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/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/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= -github.com/google/go-containerregistry v0.11.0/go.mod h1:BBaYtsHPHA42uEgAvd/NejvAfPSlz281sJWqupjSxfk= +github.com/google/go-containerregistry v0.12.0 h1:nidOEtFYlgPCRqxCKj/4c/js940HVWplCWc5ftdfdUA= +github.com/google/go-containerregistry v0.12.0/go.mod h1:sdIK+oHQO7B93xI8UweYdl887YhuIwg9vz8BSLH3+8k= github.com/google/gofuzz v1.0.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= @@ -512,8 +513,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB 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/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 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= @@ -561,8 +562,8 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -650,8 +651,8 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= +github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= 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.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= @@ -664,8 +665,9 @@ github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/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.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= @@ -730,8 +732,8 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU 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.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= -github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 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= @@ -785,7 +787,6 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= -github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= @@ -801,8 +802,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.starlark.net v0.0.0-20221010140840-6bf6f0955179 h1:Mc5MkF55Iasgq23vSYpL6/l7EJXtlNjzw+8hbMQ/ShY= -go.starlark.net v0.0.0-20221010140840-6bf6f0955179/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= +go.starlark.net v0.0.0-20221020143700-22309ac47eac h1:gBO5Qfcw5V9404yzsu2FEIsxK/u2mBNTNogK0uIoVhk= +go.starlark.net v0.0.0-20221020143700-22309ac47eac/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= 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= @@ -830,8 +831,8 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= -golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= 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= @@ -867,8 +868,8 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= 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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -914,8 +915,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= 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= @@ -930,8 +931,8 @@ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= +golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= 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= @@ -943,8 +944,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1017,13 +1018,13 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc 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.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= -golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w= -golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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= @@ -1033,13 +1034,13 @@ 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.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.1.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= @@ -1094,8 +1095,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= 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= @@ -1176,8 +1177,8 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a h1:GH6UPn3ixhWcKDhpnEC55S75cerLPdpp3hrhfKYjZgw= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 h1:GEgb2jF5zxsFJpJfg9RoDDWm7tiwc/DDSTE2BtLUkXU= +google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= 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= From c023d41b61703ff608744e2983a67efc8069cd31 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 17:36:33 +0200 Subject: [PATCH 0491/2268] fix: Fix compilation of buildTransport after updating dependencies --- pkg/registries/registries.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 731f3cf27..a280438bf 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -228,7 +228,12 @@ func (rh *RegistryHelper) buildTransport(registry string) (http.RoundTripper, er return remote.DefaultTransport, nil } - t := remote.DefaultTransport.Clone() + httpTransport, ok := remote.DefaultTransport.(*http.Transport) + if !ok { + return nil, fmt.Errorf("remote.DefaultTransport is not a http.Transport anymore. Please report this to https://github.com/kluctl/kluctl") + } + t := httpTransport.Clone() + t.TLSClientConfig.RootCAs = ca t.TLSClientConfig.InsecureSkipVerify = skipTls return t, nil From 5aad1eccc36976d96310d466a20137c4b5753ab8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Oct 2022 22:49:53 +0200 Subject: [PATCH 0492/2268] feat: Also print orphan objects when showing diff before deploy --- pkg/deployment/commands/deploy.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index 0fd1823ca..c82a6a303 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -53,17 +53,19 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, au.GetAppliedObjectsMap()) du.Diff() + orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) diffResult := &types.CommandResult{ NewObjects: du.NewObjects, ChangedObjects: du.ChangedObjects, DeletedObjects: au.GetDeletedObjects(), HookObjects: au.GetAppliedHookObjects(), + OrphanObjects: orphanObjects, Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), SeenImages: cmd.c.Images.SeenImages(false), } - err := diffResultCb(diffResult) + err = diffResultCb(diffResult) if err != nil { return nil, err } From a0ae4c51ea0fcf246f9bdbab071abe27a6b4bae8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Oct 2022 10:33:29 +0200 Subject: [PATCH 0493/2268] fix: Treat "MY_ENV_VAR:" as empty string instead of nil --- pkg/vars/vars_loader.go | 4 ++++ pkg/vars/vars_loader_test.go | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 176c59d4e..81e62d746 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -141,6 +141,10 @@ func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, envValueStr = v } else if hasDefaultValue { envValueStr = defaultValue + if envValueStr == "" { + // treat empty default string as literal empty string instead of treating it as nil + envValueStr = `""` + } } else { return fmt.Errorf("environment variable %s not found for %s", envName, it.KeyPath().ToJsonPath()) } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 65027e369..bf4e3085b 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -336,6 +336,8 @@ func TestVarsLoader_SystemEnv(t *testing.T) { }, "test5": "TEST5:def", "test6": "TEST1:def", + "test7": "TEST5:''", + "test8": "TEST5:", }), }, nil, "") assert.NoError(t, err) @@ -354,6 +356,12 @@ func TestVarsLoader_SystemEnv(t *testing.T) { v, _, _ = vc.Vars.GetNestedField("test6") assert.Equal(t, 42, v) + + v, _, _ = vc.Vars.GetNestedField("test7") + assert.Equal(t, "", v) + + v, _, _ = vc.Vars.GetNestedField("test8") + assert.Equal(t, "", v) }) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { From 951dcccf17ab043620ba8cc8f16da64c86fdda31 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Oct 2022 10:34:06 +0200 Subject: [PATCH 0494/2268] fix: Allow to mix arguments from environment and CLI --- cmd/kluctl/commands/cobra_utils.go | 16 ++++++++++--- e2e/args_test.go | 37 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index af5644631..5982f04bf 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -230,7 +230,8 @@ func copyViperValuesToCobraCmd(cmd *cobra.Command) error { func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { var retErr []error flags.VisitAll(func(flag *pflag.Flag) { - if flag.Changed { + sliceValue, _ := flag.Value.(pflag.SliceValue) + if flag.Changed && sliceValue == nil { return } v := viper.Get(flag.Name) @@ -262,11 +263,20 @@ func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { a = append(a, v) } - for _, x := range a { - err := flag.Value.Set(x) + if sliceValue != nil { + // we must ensure that values passed via CLI are at the end of the slice + a = append(a, sliceValue.GetSlice()...) + err := sliceValue.Replace(a) if err != nil { retErr = append(retErr, err) } + } else { + for _, x := range a { + err := flag.Value.Set(x) + if err != nil { + retErr = append(retErr, err) + } + } } }) return utils.NewErrorListOrNil(retErr) diff --git a/e2e/args_test.go b/e2e/args_test.go index 527a75ce3..41c28870b 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -145,3 +145,40 @@ func TestArgsFromEnv(t *testing.T) { assertNestedFieldEquals(t, cm, "True", "data", "d") assertNestedFieldEquals(t, cm, "true", "data", "e") } + +func TestArgsFromEnvAndCli(t *testing.T) { + t.Setenv("KLUCTL_ARG_1", "a=a") + t.Setenv("KLUCTL_ARG_2", "c=c") + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k, "args-from-envs-and-cli") + + createNamespace(t, k, p.projectName) + + p.updateTarget("test", func(target *uo.UnstructuredObject) { + }) + + addConfigMapDeployment(p, "cm", map[string]string{ + "a": `{{ args.a }}`, + "b": `{{ args.b }}`, + "c": `{{ args.c }}`, + }, resourceOpts{ + name: "cm", + namespace: p.projectName, + }) + + p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "b=b") + cm := k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + assertNestedFieldEquals(t, cm, "a", "data", "a") + assertNestedFieldEquals(t, cm, "b", "data", "b") + assertNestedFieldEquals(t, cm, "c", "data", "c") + + // make sure the CLI overrides values from env + p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "b=b", "-a", "c=c2") + cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + assertNestedFieldEquals(t, cm, "a", "data", "a") + assertNestedFieldEquals(t, cm, "b", "data", "b") + assertNestedFieldEquals(t, cm, "c2", "data", "c") +} From 199955ca1d21b4ca9f6e5fca6f2d3c130099cf39 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Oct 2022 12:13:48 +0200 Subject: [PATCH 0495/2268] docs: Add COC, CONTRIBUTING and DEVELOPMENT docs --- CODE_OF_CONDUCT.md | 3 +++ CONTRIBUTING.md | 59 ++++++++++++++++++++++++++++++++++++++++++++++ DEVELOPMENT.md | 50 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 DEVELOPMENT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..5f90c194f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +## Code of Conduct + +Kluctl follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..bc4ff3e98 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing + +As all Open Source projects, Kluctl lives from contributions. If you consider contributing, we are welcoming you to do so. + +## Communication + +Contributing to Open Source projects also means that you are communicating with other members of the community. Please +follow the [Code of Conduct](./CODE_OF_CONDUCT.md) whenever you communicate. + +## Creating issues / Reporting bugs + +Probably the easiest way to contribute is to create issues on Github. An issue could for example be a bug report or +a feature request. It's also fine to create an issue that requests some change, for example to documentation. Issues +can also be used as reminders/TODOs that something needs to be done in the future. + +The project is still in early stage when it comes to project management, so please bare with us if processes are still +a bit "loose". + +One thing we'd like to ask for is to first search for issues that might already represent what you plan to report. +In many cases it turns out that other people stumbled across the same thing already. If not, then you're "lucky" to +report first :) + +It might also be useful to ask inside the #kluctl channel of the [CNCF Slack](https://slack.cncf.io) if you are unsure +about your issue. + +## Finding issues to work on + +Check the [open issues](https://github.com/kluctl/kluctl/issues) and see if you can find something that you believe you +could help with. + +## Contributing/Modifying documentation + +The next level of contribution is modifying documentation. Fork the repository and start working on the changes locally. +When done, commit the changes, push them and create a pull request. If your pull request fixes an issue, add the proper +`Fixes: #xxx` line so that the issue can be auto-closed. + +Please note that we follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) when committing. + +## Contributing/Modifying source code + +To fix a bug or add a feature, follow the same procedure as described in the previous chapter. Read the +[DEVELOPMENT](./DEVELOPMENT.md) guidelines on how to build and run Kluctl from source. + +Additionally, you should ensure that your changes don't break anything. You can do this by running the +[test suite](./DEVELOPMENT.md#how-to-run-the-test-suite) and by testing your changes manually. Adding new tests for +fixed bugs and/or new features is also nice to see. Reviewers will also point out when tests would be of value. + +However, don't worry if you feel overwhelmed (e.g. with tests). You can also create a `[WIP]` pull request and ask for +help. + +Please note that we follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) when committing. + +## Conventional commits + +As mentioned before, we follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) when committing. +These commits are then used to create release notes and properly bump version numbers. + +We might reconsider this approach in the future, especially when we re-design the +[release process](./DEVELOPMENT.md#releasing-process). diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 000000000..015f2a904 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,50 @@ +# Development + +## Installing required dependencies + +There are a number of dependencies required to be able to compile, run and test Kluctl: + +- [Install Go](https://golang.org/doc/install) + +In addition to the above, the following dependencies are also used by some of the `make` targets: + +- `goreleaser` (latest) +- `setup-envtest` (latest) + +If any of the above dependencies are not present on your system, the first invocation of a `make` target that requires them will install them. + +## How to run the test suite + +Prerequisites: +* Go >= 1.19 + +You can run the test suite by simply doing + +```sh +make test +``` + +Tests are separated between unit tests (found in-tree next to the tested code) and e2e tests (found below `./e2e`). +Running `make test` will run all these tests. To only run unit tests, run `make test-unit`. To only run e2e tests, run +`make test-e2e`. + +e2e tests rely on kubebuilders `envtest` package and thus also require `setup-envtest` and dependent binaries +(e.g. kube-apiserver) to be available as well. The Makefile will take care of downloading these if required. + +## How to build Kluctl + +Simply run `make build`, which will build the kluctl binary any put into `./bin/`. To use, either directly invoke it +with the relative or absolute path or update your `PATH` environment variable to point to the bin directory. + +## Contributions + +See [CONTRIBUTING](./CONTRIBUTING.md) for details. + +## Releasing process + +The release process is currently partly manual and partly automated. A maintainer has to create a version tag and +manually push it to Github. A Github workflow will then react to this tag by running `goreleaser`, which will then +create a draft release. The maintainer then has to manually update the pre-generated release notes and publish the +release. + +This process is going to be modified in the future to contain more automation and more collaboration friendly processes. From bac93151c38802a7919c18c8bc01f811f7bd8b45 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Oct 2022 12:14:04 +0200 Subject: [PATCH 0496/2268] docs: Update README.md with links to new docs --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 899b40f2e..3d6380bc7 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,11 @@ Documentation, news and blog posts can be found on https://kluctl.io. The underlying documentation is synced from this repo (look into ./docs) to the website whenever something is merged into main. +## Development and contributions + +Please read [DEVELOPMENT](./DEVELOPMENT.md) and [CONTRIBUTIONS](./CONTRIBUTING.md) for details on how the Kluctl project +handles these matters. + ## Kluctl in Short | | | From 0f165a912f2f7d46f3fd53e5b3f5c6a87392a07d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Oct 2022 12:14:15 +0200 Subject: [PATCH 0497/2268] docs: Add community section in README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 3d6380bc7..51c58facb 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,15 @@ The Kluctl CLI then allows to deploy, diff, prune, delete, ... your deployments. Installation instructions can be found [here](./docs/installation.md). For a getting started guide, continue [here](./docs/get-started.md). +## Community + +Check the [community page](https://kluctl.io/community/) for details about the Kluctl community. + +In short: We use [Github Issues](https://github.com/kluctl/kluctl/issues) and +[Github Discussions](https://github.com/kluctl/kluctl/discussions) to track and discuss Kluctl related development. +You can also join the #kluctl channel inside the [CNCF Slack](https://slack.cncf.io) to get in contact with other +community members and contributors/developers. + ## Documentation Documentation, news and blog posts can be found on https://kluctl.io. From c5790b8df0571c05eca983d92acac0a74c1f4ab2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Oct 2022 12:23:45 +0200 Subject: [PATCH 0498/2268] docs: Add MAINTAINERS.md --- MAINTAINERS.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 MAINTAINERS.md diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 000000000..cd30797d4 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,11 @@ +# Maintainers + +The maintainers are generally available in Slack at +https://cloud-native.slack.com in #klcutl +(obtain an invitation at https://slack.cncf.io/). + +Currently, the maintainers of Kluctl are: + +- Alexander Block (github: @codablock, slack: codablock) +- Aljoscha Poertner (github: @AljoschaP, slack: aljoshare) +- Matthias Gebbe (github: @matzegebbe) From 652bfb0b94d1f033c74e0095f46502615fa0c38c Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Tue, 1 Nov 2022 23:14:10 +0100 Subject: [PATCH 0499/2268] Update MAINTAINERS.md fix firstname, added slack for Mathias Gebbe --- MAINTAINERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index cd30797d4..715056cf7 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -8,4 +8,4 @@ Currently, the maintainers of Kluctl are: - Alexander Block (github: @codablock, slack: codablock) - Aljoscha Poertner (github: @AljoschaP, slack: aljoshare) -- Matthias Gebbe (github: @matzegebbe) +- Mathias Gebbe (github: @matzegebbe, slack: matzeihnsein) From 2e285bb4848d9c51334fcc847252937249f09e3e Mon Sep 17 00:00:00 2001 From: Prateek Mishra Date: Fri, 4 Nov 2022 22:43:36 +0530 Subject: [PATCH 0500/2268] typo fix --- docs/get-started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/get-started.md b/docs/get-started.md index e1cc96cce..5393963e9 100644 --- a/docs/get-started.md +++ b/docs/get-started.md @@ -90,7 +90,7 @@ Kluctl will perform a diff first and then ask for your confirmation to deploy it some objects being newly deployed. ```sh -kubectl -nsimple-helm get pod +kubectl -n simple-helm get pod ``` ## Change something and re-deploy @@ -106,7 +106,7 @@ This time it should show your modifications in the diff. Confirm that you want t it: ```sh -kubectl -nsimple-helm get pod +kubectl -n simple-helm get pod ``` You should need 2 instances of the nginx POD running now. From 896ffb1344bd8052eb85ec393fc0158ae11bca0e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 8 Nov 2022 08:43:10 +0100 Subject: [PATCH 0501/2268] fix: Also take initContainers into account in poke-images --- pkg/deployment/commands/poke_images.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index cde5280b7..a06bf48a2 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -54,14 +54,19 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type containersAndImages[*fi.Object] = append(containersAndImages[*fi.Object], fi) } - doPokeImage := func(images []types.FixedImage, o *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { - containers, _, _ := o.GetNestedObjectList("spec", "template", "spec", "containers") + var fieldPathes []uo.KeyPath + fieldPathes = append(fieldPathes, uo.KeyPath{"spec", "template", "spec", "containers"}) + fieldPathes = append(fieldPathes, uo.KeyPath{"spec", "template", "spec", "initContainers"}) + doPokeImage := func(images []types.FixedImage, o *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { for _, image := range images { - for _, c := range containers { - containerName, _, _ := c.GetNestedString("name") - if image.Container != nil && containerName == *image.Container { - c.SetNestedField(image.ResultImage, "image") + for _, jsp := range fieldPathes { + containers, _, _ := o.GetNestedObjectList(jsp...) + for _, c := range containers { + containerName, _, _ := c.GetNestedString("name") + if image.Container != nil && containerName == *image.Container { + _ = c.SetNestedField(image.ResultImage, "image") + } } } } From 4bb1f6717620a1350fce21de9d1f08171b189142 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 8 Nov 2022 10:08:07 +0100 Subject: [PATCH 0502/2268] docs: Remove redundant section from readiness.md That same section is already inside hooks.md --- docs/reference/deployments/readiness.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/reference/deployments/readiness.md b/docs/reference/deployments/readiness.md index d571f969f..ec8020bb0 100644 --- a/docs/reference/deployments/readiness.md +++ b/docs/reference/deployments/readiness.md @@ -14,7 +14,3 @@ description: There are multiple places where kluctl can wait for "readiness" of resources, e.g. for hooks or when `waitReadiness` is specified on a deployment item. Readiness depends on the resource kind, e.g. for a Job, kluctl would wait until it finishes successfully. - -After each deployment/execution of the hooks that belong to a deployment stage (before/after deployment), kluctl -waits for the hook resources to become "ready". Readiness depends on the resource kind, e.g. for a Job, kluctl would -wait until it finishes successfully. From 194582a39c5e96ea52dd7ef0e26c5ebb93290a49 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 10 Nov 2022 13:17:45 +0100 Subject: [PATCH 0503/2268] refactor: Move writing of kustomization.yaml into buildKustomize --- pkg/deployment/deployment_item.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 0c53716e9..490cec5f9 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -450,19 +450,19 @@ func (di *DeploymentItem) writeKustomizationYaml(ky *uo.UnstructuredObject) erro return yaml.WriteYamlFile(kustomizeYamlPath, ky) } -func (di *DeploymentItem) prepareKustomizationYaml() error { +func (di *DeploymentItem) prepareKustomizationYaml() (*uo.UnstructuredObject, error) { ky, err := di.readKustomizationYaml("") if err != nil { - return err + return nil, err } if ky == nil { ky, err = di.generateKustomizationYaml("") if err != nil { - return err + return nil, err } err = di.writeKustomizationYaml(ky) if err != nil { - return err + return nil, err } } @@ -470,7 +470,7 @@ func (di *DeploymentItem) prepareKustomizationYaml() error { if overrideNamespace != nil { _, ok, err := ky.GetNestedString("namespace") if err != nil { - return err + return nil, err } if !ok { ky.SetNestedField(*overrideNamespace, "namespace") @@ -480,8 +480,7 @@ func (di *DeploymentItem) prepareKustomizationYaml() error { di.Barrier = utils.ParseBoolOrFalse(ky.GetK8sAnnotation("kluctl.io/barrier")) di.WaitReadiness = utils.ParseBoolOrFalse(ky.GetK8sAnnotation("kluctl.io/wait-readiness")) - // Save modified kustomization.yml - return di.writeKustomizationYaml(ky) + return ky, nil } func (di *DeploymentItem) buildKustomize() error { @@ -489,7 +488,13 @@ func (di *DeploymentItem) buildKustomize() error { return nil } - err := di.prepareKustomizationYaml() + ky, err := di.prepareKustomizationYaml() + if err != nil { + return err + } + + // Save modified kustomization.yml + err = di.writeKustomizationYaml(ky) if err != nil { return err } From d4d381855a1d0299c400c26759ea7815785fb314 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 10 Nov 2022 11:01:47 +0100 Subject: [PATCH 0504/2268] refacor: Reuse loadFile for git vars --- pkg/vars/vars.go | 20 ++++++++++++++------ pkg/vars/vars_loader.go | 15 ++------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/pkg/vars/vars.go b/pkg/vars/vars.go index ecb960224..cc3599471 100644 --- a/pkg/vars/vars.go +++ b/pkg/vars/vars.go @@ -44,12 +44,13 @@ func (vc *VarsCtx) UpdateChildFromStruct(child string, o interface{}) error { return nil } -func (vc *VarsCtx) RenderString(t string) (string, error) { +func (vc *VarsCtx) RenderString(t string, searchDirs []string) (string, error) { globals, err := vc.Vars.ToMap() if err != nil { return "", err } return vc.J2.RenderString(t, + jinja2.WithSearchDirs(searchDirs), jinja2.WithGlobals(globals), ) } @@ -62,24 +63,31 @@ func (vc *VarsCtx) RenderStruct(o interface{}) (bool, error) { return vc.J2.RenderStruct(o, jinja2.WithGlobals(globals)) } -func (vc *VarsCtx) RenderYamlFile(p string, searchDirs []string, out interface{}) error { +func (vc *VarsCtx) RenderFile(p string, searchDirs []string) (string, error) { globals, err := vc.Vars.ToMap() if err != nil { - return err + return "", err } ret, err := vc.J2.RenderFile(p, jinja2.WithSearchDirs(searchDirs), jinja2.WithGlobals(globals), ) if err != nil { - return err + return "", err } - err = yaml.ReadYamlString(ret, out) + return ret, nil +} + +func (vc *VarsCtx) RenderYamlFile(p string, searchDirs []string, out interface{}) error { + rendered, err := vc.RenderFile(p, searchDirs) + if err != nil { + return err + } + err = yaml.ReadYamlString(rendered, out) if err != nil { return err } - return nil } diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 81e62d746..b0ae45db4 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "fmt" - securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -199,17 +198,7 @@ func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, roo return fmt.Errorf("failed to load vars from git repository %s: %w", gitFile.Url.String(), err) } - path, err := securejoin.SecureJoin(clonedDir, gitFile.Path) - if err != nil { - return err - } - - f, err := os.ReadFile(path) - if err != nil { - return err - } - - return v.loadFromString(varsCtx, string(f), "git", rootKey) + return v.loadFile(varsCtx, gitFile.Path, []string{clonedDir}, rootKey) } func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, key string, rootKey string, base64Decode bool) error { @@ -297,7 +286,7 @@ func (v *VarsLoader) loadFromString(varsCtx *VarsCtx, s string, secretType strin } func (v *VarsLoader) renderYamlString(varsCtx *VarsCtx, s string, out interface{}) error { - ret, err := varsCtx.RenderString(s) + ret, err := varsCtx.RenderString(s, nil) if err != nil { return err } From 54a3485d2b1ce96a2cb2a4cc37435b0fa86b0330 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 10 Nov 2022 15:30:17 +0100 Subject: [PATCH 0505/2268] feat: Implement sops support for vars files --- go.mod | 18 +++ go.sum | 185 ++++++++++++++++++++++ pkg/vars/sops_test_resources/.sops.yaml | 3 + pkg/vars/sops_test_resources/README.md | 4 + pkg/vars/sops_test_resources/embed.go | 6 + pkg/vars/sops_test_resources/test-key.txt | 3 + pkg/vars/sops_test_resources/test.yaml | 22 +++ pkg/vars/vars_loader.go | 22 ++- pkg/vars/vars_loader_test.go | 22 ++- pkg/yaml/yaml.go | 4 + 10 files changed, 287 insertions(+), 2 deletions(-) create mode 100644 pkg/vars/sops_test_resources/.sops.yaml create mode 100644 pkg/vars/sops_test_resources/README.md create mode 100644 pkg/vars/sops_test_resources/embed.go create mode 100644 pkg/vars/sops_test_resources/test-key.txt create mode 100644 pkg/vars/sops_test_resources/test.yaml diff --git a/go.mod b/go.mod index b422ea1b5..4bcfb6282 100644 --- a/go.mod +++ b/go.mod @@ -59,16 +59,23 @@ require ( require ( github.com/go-logr/logr v1.2.3 + go.mozilla.org/sops/v3 v3.7.3 sigs.k8s.io/controller-runtime v0.13.0 ) require ( cloud.google.com/go/compute v1.10.0 // indirect + filippo.io/age v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go v63.3.0+incompatible // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.28 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/BurntSushi/toml v1.2.1 // indirect @@ -83,12 +90,14 @@ require ( github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver v3.5.1+incompatible // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.2.0 // indirect github.com/containerd/containerd v1.6.9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect github.com/docker/cli v20.10.20+incompatible // indirect github.com/docker/docker v20.10.20+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect @@ -112,6 +121,7 @@ require ( github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect @@ -120,8 +130,11 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect + github.com/googleapis/gax-go/v2 v2.4.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect + github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -141,6 +154,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/vault/sdk v0.6.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect + github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect @@ -198,6 +212,8 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.etcd.io/etcd/api/v3 v3.5.5 // indirect + go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a // indirect + go.opencensus.io v0.23.0 // indirect go.starlark.net v0.0.0-20221020143700-22309ac47eac // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.6.0 // indirect @@ -205,6 +221,7 @@ require ( golang.org/x/time v0.1.0 // indirect golang.org/x/tools v0.2.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + google.golang.org/api v0.96.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect google.golang.org/grpc v1.50.1 // indirect @@ -212,6 +229,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect k8s.io/apiserver v0.25.3 // indirect k8s.io/cli-runtime v0.25.3 // indirect diff --git a/go.sum b/go.sum index 7e295f7f2..11bfa90e8 100644 --- a/go.sum +++ b/go.sum @@ -20,17 +20,34 @@ cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPT cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= 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 v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0 h1:aoLIYaA1fX3ywihqpBk2APQKOo20nXsp1GEZQbx5Jk4= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= 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= @@ -41,21 +58,35 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl 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= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc= +filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8= +github.com/Azure/azure-sdk-for-go v63.3.0+incompatible h1:INepVujzUrmArRZjDLHbtER+FkvCoEwyRCXGqOlmDII= +github.com/Azure/azure-sdk-for-go v63.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= @@ -86,6 +117,7 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad h1:QeeqI2zxxgZVe11UrYFXXx6gVxPVF40ygekjBzEg4XY= @@ -124,6 +156,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bitnami-labs/sealed-secrets v0.19.1 h1:ZNLcVtTXRf7VkyNzyhe9omlwNYI0OHDteTbIHsQI7Ug= github.com/bitnami-labs/sealed-secrets v0.19.1/go.mod h1:5UcsiOdOoviJUtXY1GNSeFa8zezbBY+j9pQAA2RtKyw= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= @@ -131,6 +165,7 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -152,11 +187,16 @@ github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8 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-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.9 h1:IN/r8DUes/B5lEGTNfIiUkfZBtIQJGx2ai703dV6lRA= github.com/containerd/containerd v1.6.9/go.mod h1:XVicUvkxOrftE2Q1YWUXgZwkkAxwQYNOFzYWvfVfEfQ= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= 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= @@ -171,6 +211,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= github.com/docker/cli v20.10.20+incompatible h1:lWQbHSHUFs7KraSN2jOJK7zbMS2jNCHI4mt4xUFUVQ4= github.com/docker/cli v20.10.20+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -203,6 +245,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m 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/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 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= @@ -301,6 +344,7 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er 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= @@ -309,6 +353,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +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= @@ -327,6 +372,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= @@ -347,6 +393,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-containerregistry v0.12.0 h1:nidOEtFYlgPCRqxCKj/4c/js940HVWplCWc5ftdfdUA= @@ -357,6 +406,7 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ 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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 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= @@ -369,6 +419,9 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -376,8 +429,18 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= 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/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -386,6 +449,8 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce9cOegemcy/9Ai5k3huT6E80F3zaw= +github.com/goware/prefixer v0.0.0-20160118172347-395022866408/go.mod h1:PE1ycukgRPJ7bJ9a1fdfQ9j8i/cEcRAoLZzbxYpNB/s= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -452,6 +517,8 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= +github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -628,6 +695,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -793,6 +862,10 @@ go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k= +go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a/go.mod h1:YDKUvO0b//78PaaEro6CAPH6NqohCmL2Cwju5XI2HoE= +go.mozilla.org/sops/v3 v3.7.3 h1:CYx02LnWTATWv6NqWJIt4JCKVKSnGV+MsRiDpvwWQhg= +go.mozilla.org/sops/v3 v3.7.3/go.mod h1:AutdccISG5Nt/faUigaKPU9aGmhyZuCyUiSx5YCa1O8= 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= @@ -910,11 +983,18 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -930,7 +1010,15 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/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.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -944,6 +1032,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1003,19 +1092,34 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20210514084401-e8d321eab015/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-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/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-20220728004956-3c1f35247d10/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.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= @@ -1093,7 +1197,11 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f 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.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.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= @@ -1101,6 +1209,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 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= @@ -1125,6 +1237,26 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.96.0 h1:F60cuQPJq7K7FzsxMYHAUJSiXh2oKctHxBMbDygxhfM= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= 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= @@ -1174,9 +1306,47 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 h1:GEgb2jF5zxsFJpJfg9RoDDWm7tiwc/DDSTE2BtLUkXU= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1198,11 +1368,22 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 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.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1216,6 +1397,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -1234,6 +1416,8 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1251,6 +1435,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C 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 v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= helm.sh/helm/v3 v3.10.1 h1:uTnNlYx8QcTSNA4ZJ50Llwife4CSohUY4ehumyVf2QE= diff --git a/pkg/vars/sops_test_resources/.sops.yaml b/pkg/vars/sops_test_resources/.sops.yaml new file mode 100644 index 000000000..9071c5ce4 --- /dev/null +++ b/pkg/vars/sops_test_resources/.sops.yaml @@ -0,0 +1,3 @@ +creation_rules: + - path_regex: test.yaml + age: age1q69g6x9jcz7lgnrgdxemystmhec4e8cxlzz45x0tt6t7dddp2ppsnkdxe7 diff --git a/pkg/vars/sops_test_resources/README.md b/pkg/vars/sops_test_resources/README.md new file mode 100644 index 000000000..d9d669b06 --- /dev/null +++ b/pkg/vars/sops_test_resources/README.md @@ -0,0 +1,4 @@ +To edit the test.yaml file, run: +```sh +SOPS_AGE_KEY_FILE=$(pwd)/test-key.txt sops test.yaml +``` diff --git a/pkg/vars/sops_test_resources/embed.go b/pkg/vars/sops_test_resources/embed.go new file mode 100644 index 000000000..de606a4eb --- /dev/null +++ b/pkg/vars/sops_test_resources/embed.go @@ -0,0 +1,6 @@ +package sops_test_resources + +import "embed" + +//go:embed all:* +var TestResources embed.FS diff --git a/pkg/vars/sops_test_resources/test-key.txt b/pkg/vars/sops_test_resources/test-key.txt new file mode 100644 index 000000000..3f4a2f064 --- /dev/null +++ b/pkg/vars/sops_test_resources/test-key.txt @@ -0,0 +1,3 @@ +# created: 2022-11-10T11:37:19+01:00 +# public key: age1q69g6x9jcz7lgnrgdxemystmhec4e8cxlzz45x0tt6t7dddp2ppsnkdxe7 +AGE-SECRET-KEY-1SKGMW8JCGJ5U0UZZ0WC0583NZTRZ58F02A2KXKUULLNRM07XZ9SQ228TEW diff --git a/pkg/vars/sops_test_resources/test.yaml b/pkg/vars/sops_test_resources/test.yaml new file mode 100644 index 000000000..c3a287063 --- /dev/null +++ b/pkg/vars/sops_test_resources/test.yaml @@ -0,0 +1,22 @@ +test1: + test2: ENC[AES256_GCM,data:t/0=,iv:O061yXXLVkr36SYhF5fHHVdq+uzIbuGWMo1exsR0XK0=,tag:x/adK5sLVqdm233w/oWWiw==,type:int] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1q69g6x9jcz7lgnrgdxemystmhec4e8cxlzz45x0tt6t7dddp2ppsnkdxe7 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPUzZtaTl5YlhaOWRoRG9j + Vnc5UlpTUHZVUkcwbmgzM2lmTjJob3lQaURrCi9MSmNsZ3ltUkV4dkZScEJ0cks4 + cmlIRUgvWDBnQVpDdTRudXFMNlc3TFEKLS0tIGJHZEowODRNaittT3htZ29CSFJn + bjFkMEg4cCttZXcwdURNdnVaZ2EyUlEKTaJsay+v0TXNr9NIxFyvTXrcHgSV63Si + YiVg5/ovqVUd26lPvsQwuS9iu5BQaZAzA5uZdw4Ain+SQM9iIf8zTQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2022-11-10T11:17:45Z" + mac: ENC[AES256_GCM,data:mr9GReeEuGOobT0DwL2VBPuapT1Hc6tkhG0djYBybHKIpYaRzqz1RLdxeMHlsIMar6tbKHqRXC4euwXpzArUEhdJXzWMnBbiimibDauvH/Zf5Yf4LSYM0OyCttOfxYKejwtnBJ7Lm4C0udciPcCGKDBpql+yTX9U/vOqPQt0/vA=,iv:2jpr2QLN/zyv1QBXsVQpduDh16i2q/Sr4xTF+ojwXq0=,tag:CCl/Il59RnYe2HuoDJycZg==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.7.3 diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index b0ae45db4..0dd5e66eb 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -14,6 +14,9 @@ import ( "github.com/kluctl/kluctl/v2/pkg/vars/aws" "github.com/kluctl/kluctl/v2/pkg/vars/vault" "github.com/kluctl/kluctl/v2/pkg/yaml" + "go.mozilla.org/sops/v3" + "go.mozilla.org/sops/v3/cmd/sops/formats" + "go.mozilla.org/sops/v3/decrypt" "k8s.io/apimachinery/pkg/runtime/schema" "os" "strings" @@ -102,8 +105,25 @@ func (v *VarsLoader) mergeVars(varsCtx *VarsCtx, newVars *uo.UnstructuredObject, } func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, searchDirs []string, rootKey string) error { + rendered, err := varsCtx.RenderFile(path, searchDirs) + if err != nil { + return fmt.Errorf("failed to render vars file %s: %w", path, err) + } + + if yaml.IsMaybeSopsFile([]byte(rendered)) { + decrypted, err := decrypt.DataWithFormat([]byte(rendered), formats.FormatForPath(path)) + if err != nil && err != sops.MetadataNotFound { + return fmt.Errorf("failed to decrypt vars file %s: %w", path, err) + } else if err == nil { + rendered = string(decrypted) + } + } + newVars := uo.New() - err := varsCtx.RenderYamlFile(path, searchDirs, newVars) + err = yaml.ReadYamlString(rendered, newVars) + if err != nil { + return err + } if err != nil { return fmt.Errorf("failed to load vars from %s: %w", path, err) } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index bf4e3085b..c7e758bdf 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -15,6 +15,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars/aws" "github.com/stretchr/testify/assert" + "go.mozilla.org/sops/v3/age" "io" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -87,6 +88,25 @@ func TestVarsLoader_File(t *testing.T) { }) } +func TestVarsLoader_SopsFile(t *testing.T) { + d := newTestDir(t) + f, _ := sops_test_resources.TestResources.ReadFile("test.yaml") + key, _ := sops_test_resources.TestResources.ReadFile("test-key.txt") + _ = os.WriteFile(filepath.Join(d, "test.yaml"), f, 0o600) + + t.Setenv(age.SopsAgeKeyEnv, string(key)) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + File: utils.StrPtr("test.yaml"), + }, []string{d}, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + func TestVarsLoader_FileWithLoad(t *testing.T) { d := newTestDir(t) _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{ load_template("test2.txt") }}}}`), 0o600) @@ -128,7 +148,7 @@ func TestVarsLoader_FileWithLoadNotExists(t *testing.T) { err := vl.LoadVars(vc, &types.VarsSource{ File: utils.StrPtr("test.yaml"), }, []string{d}, "") - assert.EqualError(t, err, "failed to load vars from test.yaml: template test3.txt not found") + assert.EqualError(t, err, "failed to render vars file test.yaml: template test3.txt not found") }) } diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 89610c94a..0dde23fad 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -285,3 +285,7 @@ func Exists(p string) bool { p = FixPathExt(p) return utils.Exists(p) } + +func IsMaybeSopsFile(s []byte) bool { + return bytes.Index(s, []byte("sops")) != -1 +} From 86c8f83dfd244009b142b1187f8153f3f0cbd081 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 10 Nov 2022 15:21:12 +0100 Subject: [PATCH 0506/2268] feat: Support sops in kustomize resources --- pkg/deployment/deployment_item.go | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 490cec5f9..d42afe526 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -8,6 +8,9 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" + "go.mozilla.org/sops/v3" + "go.mozilla.org/sops/v3/cmd/sops/formats" + "go.mozilla.org/sops/v3/decrypt" "io/fs" "k8s.io/apimachinery/pkg/runtime/schema" "os" @@ -483,6 +486,31 @@ func (di *DeploymentItem) prepareKustomizationYaml() (*uo.UnstructuredObject, er return ky, nil } +func (di *DeploymentItem) decryptSopsFile(p string) error { + file, err := os.ReadFile(p) + if err != nil { + return fmt.Errorf("failed to read file %s: %w", p, err) + } + + if !yaml.IsMaybeSopsFile(file) { + return nil + } + + decrypted, err := decrypt.DataWithFormat(file, formats.FormatForPath(p)) + if err == sops.MetadataNotFound { + // not encrypted, so bail out + return nil + } else if err != nil { + return fmt.Errorf("failed to decrypt file %s: %w", p, err) + } + + err = os.WriteFile(p, decrypted, 0o600) + if err != nil { + return fmt.Errorf("failed to save decrypted file %s: %w", p, err) + } + return nil +} + func (di *DeploymentItem) buildKustomize() error { if di.dir == nil { return nil @@ -499,6 +527,17 @@ func (di *DeploymentItem) buildKustomize() error { return err } + resources, _, _ := ky.GetNestedStringList("resources") + for _, r := range resources { + p := filepath.Join(di.RenderedDir, r) + if utils.IsFile(p) { + err = di.decryptSopsFile(p) + if err != nil { + return err + } + } + } + rm, err := utils.SecureBuildKustomization(di.RenderedSourceRootDir, di.RenderedDir, true) if err != nil { return err From 3e0a1b0af658891f0417714db1dc01f93802ac92 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 10 Nov 2022 16:09:10 +0100 Subject: [PATCH 0507/2268] tests: Add sops tests --- e2e/project.go | 22 +++-- e2e/sops_test.go | 83 +++++++++++++++++++ pkg/vars/sops_test_resources/.sops.yaml | 3 +- pkg/vars/sops_test_resources/README.md | 1 + .../sops_test_resources/test-configmap.yaml | 26 ++++++ 5 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 e2e/sops_test.go create mode 100644 pkg/vars/sops_test_resources/test-configmap.yaml diff --git a/e2e/project.go b/e2e/project.go index f68025aef..2e137e841 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -100,6 +100,10 @@ func (p *testProject) updateYaml(path string, update func(o *uo.UnstructuredObje }, message) } +func (p *testProject) updateFile(path string, update func(f string) (string, error), message string) { + p.gitServer.UpdateFile(p.getKluctlProjectRepo(), path, update, message) +} + func (p *testProject) getDeploymentYaml(dir string) *uo.UnstructuredObject { o, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, "deployment.yml")) if err != nil { @@ -284,18 +288,20 @@ func (p *testProject) addKustomizeResources(dir string, resources []kustomizeRes l, _, _ := o.GetNestedList("resources") for _, r := range resources { l = append(l, r.name) - x := p.convertInterfaceToList(r.content) fileName := r.fileName if fileName == "" { fileName = r.name } - err := yaml.WriteYamlAllFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, fileName), x) - if err != nil { - return err - } - _, err = wt.Add(filepath.Join(dir, fileName)) - if err != nil { - return err + if r.content != nil { + x := p.convertInterfaceToList(r.content) + err := yaml.WriteYamlAllFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, fileName), x) + if err != nil { + return err + } + _, err = wt.Add(filepath.Join(dir, fileName)) + if err != nil { + return err + } } } o.SetNestedField(l, "resources") diff --git a/e2e/sops_test.go b/e2e/sops_test.go new file mode 100644 index 000000000..59e352b82 --- /dev/null +++ b/e2e/sops_test.go @@ -0,0 +1,83 @@ +package e2e + +import ( + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/vars/sops_test_resources" + "go.mozilla.org/sops/v3/age" + "testing" +) + +func TestSopsVars(t *testing.T) { + key, _ := sops_test_resources.TestResources.ReadFile("test-key.txt") + t.Setenv(age.SopsAgeKeyEnv, string(key)) + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k, "sops-vars") + + createNamespace(t, k, p.projectName) + + p.updateTarget("test", nil) + + addConfigMapDeployment(p, "cm", map[string]string{ + "v1": "{{ test1.test2 }}", + }, resourceOpts{ + name: "cm", + namespace: p.projectName, + }) + p.updateDeploymentYaml("", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField([]map[string]any{ + { + "file": "encrypted-vars.yaml", + }, + }, "vars") + return nil + }) + + p.updateFile("encrypted-vars.yaml", func(f string) (string, error) { + b, _ := sops_test_resources.TestResources.ReadFile("test.yaml") + return string(b), nil + }, "") + + p.KluctlMust("deploy", "--yes", "-t", "test") + + cm := assertConfigMapExists(t, k, p.projectName, "cm") + assertNestedFieldEquals(t, cm, map[string]any{ + "v1": "42", + }, "data") +} + +func TestSopsResources(t *testing.T) { + key, _ := sops_test_resources.TestResources.ReadFile("test-key.txt") + t.Setenv(age.SopsAgeKeyEnv, string(key)) + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k, "sops-resources") + + createNamespace(t, k, p.projectName) + + p.updateTarget("test", nil) + p.updateDeploymentYaml("", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(p.projectName, "overrideNamespace") + return nil + }) + + p.addKustomizeDeployment("cm", []kustomizeResource{ + {name: "encrypted-cm.yaml"}, + }, nil) + + p.updateFile("cm/encrypted-cm.yaml", func(f string) (string, error) { + b, _ := sops_test_resources.TestResources.ReadFile("test-configmap.yaml") + return string(b), nil + }, "") + + p.KluctlMust("deploy", "--yes", "-t", "test") + + cm := assertConfigMapExists(t, k, p.projectName, "encrypted-cm") + assertNestedFieldEquals(t, cm, map[string]any{ + "a": "b", + }, "data") +} diff --git a/pkg/vars/sops_test_resources/.sops.yaml b/pkg/vars/sops_test_resources/.sops.yaml index 9071c5ce4..73232ad66 100644 --- a/pkg/vars/sops_test_resources/.sops.yaml +++ b/pkg/vars/sops_test_resources/.sops.yaml @@ -1,3 +1,2 @@ creation_rules: - - path_regex: test.yaml - age: age1q69g6x9jcz7lgnrgdxemystmhec4e8cxlzz45x0tt6t7dddp2ppsnkdxe7 + - age: age1q69g6x9jcz7lgnrgdxemystmhec4e8cxlzz45x0tt6t7dddp2ppsnkdxe7 diff --git a/pkg/vars/sops_test_resources/README.md b/pkg/vars/sops_test_resources/README.md index d9d669b06..f25a10769 100644 --- a/pkg/vars/sops_test_resources/README.md +++ b/pkg/vars/sops_test_resources/README.md @@ -1,4 +1,5 @@ To edit the test.yaml file, run: ```sh SOPS_AGE_KEY_FILE=$(pwd)/test-key.txt sops test.yaml +SOPS_AGE_KEY_FILE=$(pwd)/test-key.txt sops test-configmap.yaml ``` diff --git a/pkg/vars/sops_test_resources/test-configmap.yaml b/pkg/vars/sops_test_resources/test-configmap.yaml new file mode 100644 index 000000000..412303b43 --- /dev/null +++ b/pkg/vars/sops_test_resources/test-configmap.yaml @@ -0,0 +1,26 @@ +apiVersion: ENC[AES256_GCM,data:P+0=,iv:BI/ZR9fS+Pt26Ert1Bm+CgEPba/rN149lhBvMJdrC3M=,tag:/vMKi5bqzS7RiYlHV6ES0w==,type:str] +kind: ENC[AES256_GCM,data:AdJ37WNulWCn,iv:HcLZZEUbzDw0Y/eXaJ/1Lzr/lJ1wexXvq0sf+WRTgAw=,tag:3SpRc9DHXcdINxsBZAB4aw==,type:str] +metadata: + name: ENC[AES256_GCM,data:JzAbIoxlaC/W+2ng,iv:Wiv9cFlmOJov5HL8nWwDNUg+GAsq2NRvyWM9OK5SNCA=,tag:/N+sO//QpnGbcxfq0OxDlg==,type:str] +data: + a: ENC[AES256_GCM,data:Gg==,iv:8cfbT7VXyWdQxWWAB84+RhdEfQ/QYCXEtdQBVtL4dGg=,tag:+EYsouHQU5jXyAMTnLAJHg==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1q69g6x9jcz7lgnrgdxemystmhec4e8cxlzz45x0tt6t7dddp2ppsnkdxe7 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArZ05WY2hiNFdOcnJyYThr + QXZTTXFGVWJtYzcvakRBbzFpRnFwWGVPVjNnCkYyUW9qMEx2K25NbFVYY3d6NWZ3 + YnU4ZGpnamYwWGxrb2ZTandVQXNPR2sKLS0tIGZ2cnZZNXRKRnI2TVdVV1Z0eUsx + V1A2MXp1V3Z3VHUzVzlQc1dISkpmeVUKRNruIW7lGYGZ2zP2bJ/zrfB5ezktDPSU + I21uQv0OMYxCBBxVGCwqGm2LEG9jJZR+8yV7QTQqH4x/G3/V+zFFEA== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2022-11-10T14:53:10Z" + mac: ENC[AES256_GCM,data:Qxl1mPN5nGaH9/K+RGuItcHou7tbPrd1p37067nKoxPPF2a2o7kJV6FfJsB6tUYWaEHZMMseE2Gl5mJ1H8m6mmZBjx+DiBBoGmAh719PJ7kffDYFB0trk2Ysh1TQubvy7lv8pPCo7ruVhfoz9/oUTyoFquD+70f/xFa3nH0HPM8=,iv:5NnE8CfRy4Vz95CxQW5omkTsj+i6LDWzuAWIfzF5qOg=,tag:+dyFsFfsJj+dojMiDRIb7w==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.7.3 From 05fae7cad7d96c8c7660ae84c80841af5e813e23 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 10 Nov 2022 21:57:17 +0100 Subject: [PATCH 0508/2268] tests: Fix tests compilation --- pkg/vars/vars_loader_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index c7e758bdf..95db5f20f 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -14,6 +14,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars/aws" + "github.com/kluctl/kluctl/v2/pkg/vars/sops_test_resources" "github.com/stretchr/testify/assert" "go.mozilla.org/sops/v3/age" "io" From fb17e29216ed48632e724062f1d1f58004e5ebfe Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 10 Nov 2022 21:58:58 +0100 Subject: [PATCH 0509/2268] docs: Add docs for SOPS support --- docs/reference/deployments/README.md | 9 ++-- docs/reference/deployments/helm.md | 2 +- docs/reference/deployments/hooks.md | 2 +- docs/reference/deployments/readiness.md | 2 +- docs/reference/deployments/sops.md | 70 +++++++++++++++++++++++++ docs/reference/deployments/tags.md | 2 +- 6 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 docs/reference/deployments/sops.md diff --git a/docs/reference/deployments/README.md b/docs/reference/deployments/README.md index ede5bdaa1..b54534e4f 100644 --- a/docs/reference/deployments/README.md +++ b/docs/reference/deployments/README.md @@ -15,10 +15,11 @@ description: > 2. [Kustomize Integration](./kustomize.md) 3. [Container Images](./images.md) 4. [Helm Integration](./helm.md) -5. [Hooks](./hooks.md) -6. [Readiness](./readiness.md) -7. [Tags](./tags.md) -8. [Annotations](./annotations) +5. [SOPS Integration](./sops.md) +6. [Hooks](./hooks.md) +7. [Readiness](./readiness.md) +8. [Tags](./tags.md) +9. [Annotations](./annotations) A deployment project is collection of deployment items and sub-deployments. Deployment items are usually [Kustomize](./kustomize.md) deployments, but can also integrate [Helm Charts](./helm.md). diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index 75016f9e8..c5ac21956 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -3,7 +3,7 @@ --- title: "Helm Integration" linkTitle: "Helm Integration" -weight: 3 +weight: 4 description: > How Helm is integrated into Kluctl. --- diff --git a/docs/reference/deployments/hooks.md b/docs/reference/deployments/hooks.md index b9f5879c2..34567de06 100644 --- a/docs/reference/deployments/hooks.md +++ b/docs/reference/deployments/hooks.md @@ -3,7 +3,7 @@ --- title: "Hooks" linkTitle: "Hooks" -weight: 4 +weight: 6 description: > Kluctl hooks. --- diff --git a/docs/reference/deployments/readiness.md b/docs/reference/deployments/readiness.md index ec8020bb0..ff3197659 100644 --- a/docs/reference/deployments/readiness.md +++ b/docs/reference/deployments/readiness.md @@ -3,7 +3,7 @@ --- title: "Readiness" linkTitle: "Readiness" -weight: 5 +weight: 7 description: Definition of readiness. --- diff --git a/docs/reference/deployments/sops.md b/docs/reference/deployments/sops.md new file mode 100644 index 000000000..1b29dcf0e --- /dev/null +++ b/docs/reference/deployments/sops.md @@ -0,0 +1,70 @@ + + +# SOPS Integration + +Kluctl integrates natively with [SOPS](https://github.com/mozilla/sops). Kluctl is able to decrypt all resources +referenced by [Kustomize](./kustomize.md) deployment items (including [simple deployments](./deployment-yml.md#simple-deployments)). +In addition, Kluctl will also decrypt all variable sources of the types [file](../templating/variable-sources.md#file) +and [git](../templating/variable-sources.md#git). + +Kluctl assumes that you have setup sops as usual so that it knows how to decrypt these files. + +## Only encrypting Secrets's data + +To only encrypt the `data` and `stringData` fields of Kubernetes secrets, use a `.sops.yaml` configuration file that +`encrypted_regex` to filter encrypted fields: + +``` +creation_rules: + - path_regex: .*.yaml + encrypted_regex: ^(data|stringData)$ +``` + +## Combining templating and SOPS + +As an alternative, you can split secret values and the resulting Kubernetes resources into two different places and then +use templating to use the secret values wherever needed. Example: + +Write the following content into `secrets/my-secrets.yaml`: + +```yaml +secrets: + mySecret: secret-value +``` + +And encrypt it with SOPS: + +```shell +$ sops -e -i secrets/my-secrets.yaml +``` + +Add this [variables source](../templating/variable-sources.md) to one of your [deployments](./deployment-yml.md): + +```yaml +vars: + - file: secrets/my-secrets.yaml + +deployments: +- ... +``` + +Then, in one of your deployment items define the following `Secret`: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: my-secret + namespace: default +stringData: + secret: "{{ secrets.mySecret }}" +``` diff --git a/docs/reference/deployments/tags.md b/docs/reference/deployments/tags.md index 6bbfe18f4..b1aa08afe 100644 --- a/docs/reference/deployments/tags.md +++ b/docs/reference/deployments/tags.md @@ -3,7 +3,7 @@ --- title: "Tags" linkTitle: "Tags" -weight: 6 +weight: 8 --- --> From 85e0bdc658b1fb0b812ff0c839760d45810135d2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 10 Nov 2022 22:12:13 +0100 Subject: [PATCH 0510/2268] docs: Remove all references to sealed secrets in docs --- docs/concepts.md | 12 +- docs/reference/README.md | 3 +- docs/reference/commands/README.md | 3 +- docs/reference/commands/seal.md | 45 ----- docs/reference/deployments/deployment-yml.md | 8 - docs/reference/deployments/helm.md | 2 +- docs/reference/kluctl-project/README.md | 28 +--- .../kluctl-project/secrets-config/README.md | 78 --------- .../kluctl-project/targets/README.md | 38 +---- .../kluctl-project/targets/dynamic-targets.md | 15 -- docs/reference/sealed-secrets.md | 157 ------------------ .../templating/predefined-variables.md | 4 - docs/reference/templating/variable-sources.md | 6 +- 13 files changed, 10 insertions(+), 389 deletions(-) delete mode 100644 docs/reference/commands/seal.md delete mode 100644 docs/reference/kluctl-project/secrets-config/README.md delete mode 100644 docs/reference/sealed-secrets.md diff --git a/docs/concepts.md b/docs/concepts.md index 2967356fe..ef99583dd 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -12,7 +12,7 @@ weight: 10 These are some core concepts in Kluctl. ## Kluctl project -The kluctl project defines targets and secret sources. +The kluctl project defines targets. It is defined via the [.kluctl.yaml](./reference/kluctl-project) configuration file. ## Targets @@ -39,16 +39,6 @@ through a templating engine. The [templating engine](./reference/templating) allows simple variable substitution and also complex control structures (if/else, for loops, ...). -## Secrets -Secrets are loaded from [external sources](./reference/kluctl-project) and are only available -while [sealing](./reference/sealed-secrets.md). After the sealing process, only the public-key encrypted -sealed secrets are available. - -## Sealed Secrets -[Sealed Secrets](./reference/sealed-secrets.md) are based on -[Bitnami's sealed-secrets controller](https://github.com/bitnami-labs/sealed-secrets). Kluctl offers integration of -sealed secrets through the `seal` command. Kluctl allows managing multiple sets of sealed secrets for multiple targets. - ## Unified CLI The CLI of kluctl is designed to be unified/consistent as much as possible. Most commands are centered around targets and thus require you to specify the target name (via `-t `). If you remember how one command works, it's easy diff --git a/docs/reference/README.md b/docs/reference/README.md index aec1d2c6b..19695290e 100644 --- a/docs/reference/README.md +++ b/docs/reference/README.md @@ -13,5 +13,4 @@ weight: 110 1. [.kluctl.yaml](./kluctl-project) 2. [Deployments](./deployments) -3. [Sealed Secrets](./sealed-secrets.md) -4. [Kluctl Commands](./commands) +3. [Kluctl Commands](./commands) diff --git a/docs/reference/commands/README.md b/docs/reference/commands/README.md index 1fe1a1923..8a3683520 100644 --- a/docs/reference/commands/README.md +++ b/docs/reference/commands/README.md @@ -32,5 +32,4 @@ Individual commands are documented in sub-sections. 10. [poke-images](./poke-images.md) 11. [prune](./prune.md) 12. [render](./render.md) -13. [seal](./seal.md) -14. [validate](./validate.md) +13. [validate](./validate.md) diff --git a/docs/reference/commands/seal.md b/docs/reference/commands/seal.md deleted file mode 100644 index 4ec5fe457..000000000 --- a/docs/reference/commands/seal.md +++ /dev/null @@ -1,45 +0,0 @@ - - -## Command - -Usage: kluctl seal [flags] - -Seal secrets based on target's sealingConfig -Loads all secrets from the specified secrets sets from the target's sealingConfig and -then renders the target, including all files with the '.sealme' extension. Then runs -kubeseal on each '.sealme' file and stores secrets in the directory specified by -'--local-sealed-secrets', using the outputPattern from your deployment project. - -If no '--target' is specified, sealing is performed for all targets. - - - -See [sealed-secrets](../sealed-secrets.md) for more details. - -## Arguments -The following sets of arguments are available: -1. [project arguments](./common-arguments.md#project-arguments) (except `-a`) - -In addition, the following arguments are available: - -``` -Misc arguments: - Command specific arguments. - - --cert-file string Use the given certificate for sealing instead of requesting it from the - sealed-secrets controller - --force-reseal Lets kluctl ignore secret hashes found in already sealed secrets and thus forces - resealing of those. - --offline-kubernetes Run seal in offline mode, meaning that it will not try to connect the target cluster - -``` - diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index e1aac4061..7a0be122b 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -16,9 +16,6 @@ The `deployment.yaml` file is the entrypoint for the deployment project. Include An example `deployment.yaml` looks like this: ```yaml -sealedSecrets: - outputPattern: "{{ cluster.name }}/{{ args.environment }}" - deployments: - path: nginx - path: my-app @@ -34,11 +31,6 @@ args: The following sub-chapters describe the available fields in the `deployment.yaml` -## sealedSecrets -`sealedSecrets` configures how sealed secrets are stored while sealing and located while rendering. -See [Sealed Secrets](../sealed-secrets.md#outputpattern-and-location-of-stored-sealed-secrets) -for details. - ## deployments `deployments` is a list of deployment items. Multiple deployment types are supported, which is documented further down. diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index 75016f9e8..f0aaf8403 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -25,7 +25,7 @@ hands over the rendered yaml to [kustomize](https://kustomize.io/). Rendering is `helm-values.yaml`, which contains the necessary values to configure the Helm Chart. The resulting rendered yaml is then referred by your `kustomization.yaml`, from which point on the -[kustomize integration](../sealed-secrets.md#outputpattern-and-location-of-stored-sealed-secrets) +[kustomize integration](./kustomize.md) takes over. This means, that you can perform all desired customization (patches, namespace override, ...) as if you provided your own resources via yaml files. diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index 367e23778..f442ee3fb 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -11,10 +11,8 @@ description: > # Kluctl project -The `.kluctl.yaml` is the central configuration and entry point for your deployments. It defines where the actual -[deployment project](../deployments) is located, -where [sealed secrets](../sealed-secrets.md) and unencrypted secrets are localed and which targets are available to -invoke [commands](../commands) on. +The `.kluctl.yaml` is the central configuration and entry point for your deployments. It defines which targets are +available to invoke [commands](../commands) on. ## Example @@ -27,37 +25,16 @@ targets: context: test.example.com args: environment_name: dev - sealingConfig: - secretSets: - - non-prod # test cluster, test env - name: test context: test.example.com args: environment_name: test - sealingConfig: - secretSets: - - non-prod # prod cluster, prod env - name: prod context: prod.example.com args: environment_name: prod - sealingConfig: - secretSets: - - prod - -# This is only required if you actually need sealed secrets -secretsConfig: - secretSets: - - name: prod - vars: - # This file should not be part of version control! - - file: .secrets-prod.yaml - - name: non-prod - vars: - # This file should not be part of version control! - - file: .secrets-non-prod.yaml ``` ## Allowed fields @@ -65,4 +42,3 @@ secretsConfig: Please check the following sub-sections of this section to see which fields are allowed at the root level of `.kluctl.yaml`. 1. [targets](./targets) -2. [secretsConfig](./secrets-config) diff --git a/docs/reference/kluctl-project/secrets-config/README.md b/docs/reference/kluctl-project/secrets-config/README.md deleted file mode 100644 index 8e1387af2..000000000 --- a/docs/reference/kluctl-project/secrets-config/README.md +++ /dev/null @@ -1,78 +0,0 @@ - - -# secretsConfig - -This configures how secrets are retrieved while sealing. It is basically a list of named secret sets which can be -referenced from targets. - -It has the following form: -```yaml -... -secretsConfig: - secretSets: - - name: - vars: - - ... - sealedSecrets: ... -... -``` - -## secretSets - -Each `secretSets` entry has the following fields. - -### name -This field specifies the name of the secret set. The name can be used in targets to refer to this secret set. - -### vars -A list of variables sources. Check the documentation of -[variables sources](../../templating/variable-sources.md) for details. - -Each variables source must have a root dictionary with the name `secrets` and all the actual secret values -below that dictionary. Every other root key will be ignored. - -Example variables file: - -```yaml -secrets: - secret: value1 - nested: - secret: value2 - list: - - a - - b -... -``` - -## sealedSecrets -This field specifies the configuration for sealing. It has the following form: - -```yaml -... -secretsConfig: - secretSets: ... - sealedSecrets: - bootstrap: true - namespace: kube-system - controllerName: sealed-secrets-controller -... -``` - -### bootstrap -Controls whether kluctl should bootstrap the initial private key in case the controller is not yet installed on -the target cluster. Defaults to `true`. - -### namespace -Specifies the namespace where the sealed-secrets controller is installed. Defaults to "kube-system". - -### controllerName -Specifies the name of the sealed-secrets controller. Defaults to "sealed-secrets-controller". diff --git a/docs/reference/kluctl-project/targets/README.md b/docs/reference/kluctl-project/targets/README.md index a9d0de7a5..74673d86d 100644 --- a/docs/reference/kluctl-project/targets/README.md +++ b/docs/reference/kluctl-project/targets/README.md @@ -13,7 +13,7 @@ description: > Specifies a list of targets for which commands can be invoked. A target puts together environment/target specific configuration and the target cluster. Multiple targets can exist which target the same cluster but with differing -configuration (via `args`). Target entries also specifies which secrets to use while [sealing](../../sealed-secrets.md). +configuration (via `args`). Each value found in the target definition is rendered with a simple Jinja2 context that only contains the target itself. The rendering process is retried 10 times until it finally succeeds, allowing you to reference @@ -32,9 +32,6 @@ targets: images: - image: my-image resultImage: my-image:1.2.3 - sealingConfig: - secretSets: - - ... ``` @@ -61,36 +58,3 @@ The format is identical to the [fixed images file](../../deployments/images.md#f The fixed images specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets.md#images) have higher priority. - -## sealingConfig -This field configures how sealing is performed when the [seal command](../../commands/seal.md) is invoked for this target. -It has the following form: - -```yaml -targets: -... -- name: - ... - sealingConfig: - args: - arg1: - certFile: - dynamicSealing: - secretSets: - - -``` - -### args -This field allows adding extra arguments to the target args. These are only used while sealing and may override -arguments which are already configured for the target. - -### certFile -Optional path to a local (inside your project) public certificate used for sealing. Such a certificate can be fetched -from the sealed-secrets controller using `kubeseal --fetch-cert`. - -### dynamicSealing -This field specifies weather sealing should happen per [dynamic target](./dynamic-targets.md) or only once. This -field is optional and defaults to `true`. - -### secretSets -This field specifies a list of secret set names, which all must exist in the [secretsConfig](../secrets-config). diff --git a/docs/reference/kluctl-project/targets/dynamic-targets.md b/docs/reference/kluctl-project/targets/dynamic-targets.md index 32fa6a1af..6b18cace4 100644 --- a/docs/reference/kluctl-project/targets/dynamic-targets.md +++ b/docs/reference/kluctl-project/targets/dynamic-targets.md @@ -35,10 +35,6 @@ targets: ref: refPattern: file: - sealingConfig: - dynamicSealing: - secretSets: - - ... ``` @@ -102,14 +98,3 @@ An optional list of fixed images, in the same format as in the normal [target im A simplified form of dynamic targets is to store target config inside the same directory/project as the `.kluctl.yaml`. This can be done by omitting `project`, `ref` and `refPattern` from `targetConfig` and only specify `file`. - -## A note on sealing - -When sealing dynamic targets, it is very likely that it is not known yet which dynamic targets will actually exist in -the future. This requires some special care when sealing secrets for these targets. Sealed secrets are usually namespace -scoped, which might need to be changed to cluster-wide scoping so that the same sealed secret can be deployed into -multiple targets (assuming you deploy to different namespaces for each target). When you do this, watch out to not -compromise security, e.g. by sealing production level secrets with a cluster-wide scope! - -It is also very likely required to set `target.sealingConfig.dynamicSealing` to `false`, so that sealing is only performed -once and not for all dynamic targets. \ No newline at end of file diff --git a/docs/reference/sealed-secrets.md b/docs/reference/sealed-secrets.md deleted file mode 100644 index cfd40a2d1..000000000 --- a/docs/reference/sealed-secrets.md +++ /dev/null @@ -1,157 +0,0 @@ - - -# Sealed Secrets - -kluctl has an integration for [sealed secrets](https://github.com/bitnami-labs/sealed-secrets), allowing you to -securely store secrets for multiple target clusters and/or environments inside version control. - -The integration consists of two parts: -1. Sealing of secrets -2. Automatically choosing and deploying the correct sealed secrets for a target - -## Requirements - -The Sealed Secrets integration relies on the [sealed-secrets operator](https://github.com/bitnami-labs/sealed-secrets) -being installed. Installing the operator is the responsibility of you (or whoever is managing/operating the cluster). - -Kluctl can however perform sealing of secrets without an existing sealed-secrets operator installation. This is solved -by automatically pre-provisioning a key onto the cluster that is compatible with the operator or by providing the -public certificate via `certFile` in the targets [sealingConfig](./kluctl-project/targets#certfile). - -## Sealing of .sealme files - -Sealing is done via the [seal command](./commands/seal.md). It must be done before the actual -deployment is performed. - -The `seal` command recursively searches for files that end with `.sealme`, renders them with the -[templating engine](./templating) engine. The rendered secret resource is then -converted/encrypted into a sealed secret. - -The `.sealme` files itself have to be [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/), -but without any actual secret data inside. The secret data is referenced via templating variables and is expected to be -provided only at the time of sealing. This means, that the sensitive secret data must only be in clear text while sealing. -Afterwards the sealed secrets can be added to version control. - -Example file (the name could be for example `db-secrets.yaml.sealme`): -```yaml -kind: Secret -apiVersion: v1 -metadata: - name: db-secrets - namespace: {{ my.namespace.variable }} -stringData: - DB_URL: {{ secrets.database.url }} - DB_USERNAME: {{ secrets.database.username }} - DB_PASSWORD: {{ secrets.database.password }} -``` - -While sealing, the full templating context (same as in [templating](./templating)) is available. -Additionally, the global `secrets` object/variable is available which contains the sensitive secrets. - -## Secret Sources - -Secrets are only loaded while sealing. Available secret sets and sources are configured via -[.kluctl.yaml](./kluctl-project/secrets-config). The secrets used per target are configured via the -[secrets config](./kluctl-project/targets#secretsets) of the targets. - -## Using sealed secrets - -After sealing a secret, it can be used inside kustomize deployments. While deploying, kluctl will look for resources -included from `kustomization.yaml` which are not existent but for which a file with a `.sealme` extension exists. If such -a file is found, the appropriate sealed secrets is located based on the -[outputPattern](#outputpattern-and-location-of-stored-sealed-secrets). - -An example `kustomization.yaml`: -```yaml -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: -# please note that we do not specify the .sealme suffix here -- db-secrets.yaml -- my-deployments.yaml -``` - -## outputPattern and location of stored sealed secrets - -It is possible to override the output pattern in the root [deployment project](./deployments). -The output pattern must be a template string that is rendered with the full -[templating context](./templating) available for the deployment.yaml. - -When manually specifying the outputPattern, ensure that it works well with multiple clusters and targets. You can -for example use the `{{ target.name }}` and `{{ cluster.name }}` inside the outputPattern. - -```yaml -# deployment.yaml in root directory -sealedSecrets: - outputPattern: "{{ cluster.name }}/{{ target.name }}" -``` - -The default outputPattern is simply `{{ target.name }}`, which should work well in most cases. - -The final storage location for the sealed secret is: - -`///` - -with: -* `base_dir`: The base directory for sealed secrets, which defaults to to the subdirectory `.sealed-secrets` in the kluctl project root - diretory. -* `rendered_output_pattern`: The rendered outputPattern as described above. -* `relative_sealme_file_dir`: The relative path from the deployment root directory. -* `file_name`: The filename of the sealed secret, excluding the `.sealme` extension. - -## Content Hashes and re-sealing -Sealed secrets are stored together with hashes of all individual secret entries. These hashes are then used to avoid -unnecessary re-sealing in future [seal](./commands/seal.md) invocations. If you want to force re-sealing, use the -[--force-reseal](./commands/seal.md) option. - -Hashing of secrets is done with bcrypt and the cluster id as salt. The cluster id is currently defined as the sha256 hash -of the cluster CA certificate. This will cause re-sealing of all secrets in case a cluster is set up from scratch -(which causes key from the sealed secrets operator to get wiped as well). - -## Clusters and namespaces -Sealed secrets are usually only decryptable by one cluster, simply because each cluster has its own set of randomly -generated public/private key pairs. This means, that a secret that was sealed for your production cluster can't be -unsealed on your test cluster. - -In addition, sealed secrets can be bound to a single namespace, making them undecryptable for any other namespace. -To limit a sealed secret to a namespace, simply fill the `metadata.namespace` field of the input secret (which is in -the `.sealme` file). This way, the sealed secret can only be deployed to a single namespace. - -You can also use [Scopes](https://github.com/bitnami-labs/sealed-secrets#scopes) to lift/limit restrictions. - -## Using reflectors/replicators -In case a sealed secrets needs to be deployed to more than one namespace, some form of replication must be used. You'd -then seal the secret for a single namespace and use a reflection/replication controller to reflect the unsealed secret -into one or multiple other namespaces. Example controllers that can accomplish this are the -[Mittwald kubernetes-reflector](https://github.com/mittwald/kubernetes-replicator) and the -[Emberstack Kubernetes Reflector](https://github.com/emberstack/kubernetes-reflector). - -Consider the following example (using the Mittwald replicator): -```yaml -kind: Secret -apiVersion: v1 -metadata: - name: db-secrets - namespace: {{ my.namespace.variable }} - annotations: - replicator.v1.mittwald.de/replicate-to: '{{ my.namespace.variable }}-.*' -stringData: - DB_URL: {{ secrets.database.url }} - DB_USERNAME: {{ secrets.database.username }} - DB_PASSWORD: {{ secrets.database.password }} -``` - -The above example would cause automatic replication into every namespace that matches the replicate-to pattern. - -Please watch out for security implications. In the above example, everyone who has the right to create a namespace that -matches the pattern will get access to the secret. diff --git a/docs/reference/templating/predefined-variables.md b/docs/reference/templating/predefined-variables.md index 951226f57..a547e1d86 100644 --- a/docs/reference/templating/predefined-variables.md +++ b/docs/reference/templating/predefined-variables.md @@ -26,7 +26,3 @@ This global object provides the dynamic images features described in [images](.. ### version This global object defines latest version filters for `images.get_image(...)`. See [images](../deployments/images.md) for details. - -### secrets -This global object is only available while [sealing](../sealed-secrets.md) and contains the loaded -secrets defined via the currently sealed target. diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index 2941e532b..ad6ad30c2 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -159,8 +159,8 @@ Kluctl currently supports BASIC and NTLM authentication. It will prompt for cred ### awsSecretsManager [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) integration. Loads a variables YAML from an AWS Secrets -Manager secret. The secret can either be specified via an ARN or via a secretName and region combination. An AWS -config profile can also be specified (which must exist while sealing). +Manager secret. The secret can either be specified via an ARN or via a secretName and region combination. An existing AWS +config profile can also be specified. The secrets stored in AWS Secrets manager must contain a valid yaml or json file. @@ -198,7 +198,7 @@ vars: path: secret/data/simple ``` -Before deploying or sealing please make sure that you have access to vault. You can do this for example by setting +Before deploying please make sure that you have access to vault. You can do this for example by setting the environment variable `VAULT_TOKEN`. ### systemEnvVars From 3e885a1e4e566d49e21baac6a4650cb8c7af8afe Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 10 Nov 2022 22:17:15 +0100 Subject: [PATCH 0511/2268] feat: Deprecate the SealedSecrets integration --- pkg/kluctl_project/project_load.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 29ea9e871..c4a2b20b9 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -60,6 +60,19 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { c.sealedSecretsDir = filepath.Join(c.ProjectDir, ".sealed-secrets") + sealedSecretsUsed := false + if c.Config.SecretsConfig != nil { + sealedSecretsUsed = true + } + for _, t := range c.Config.Targets { + if t.SealingConfig != nil { + sealedSecretsUsed = true + } + } + if sealedSecretsUsed { + status.Deprecation(c.ctx, "sealed-secrets", "The SealedSecrets integration is deprecated and will be completely removed in an upcoming version. Please switch to using the SOPS integration instead.") + } + s.Success() return nil From 86a7a4cb9e91d622f047f74466b1ae864e503ed9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 13:37:48 +0100 Subject: [PATCH 0512/2268] fix: Upgrade github.com/xanzy/ssh-agent to current master to fix crash See https://github.com/xanzy/ssh-agent/pull/11 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b422ea1b5..ba6357db2 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/spf13/viper v1.13.0 github.com/stretchr/testify v1.8.0 github.com/whilp/git-urls v1.0.0 - github.com/xanzy/ssh-agent v0.3.2 + github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 golang.org/x/crypto v0.1.0 golang.org/x/net v0.1.0 golang.org/x/sync v0.1.0 diff --git a/go.sum b/go.sum index 7e295f7f2..7698848d5 100644 --- a/go.sum +++ b/go.sum @@ -767,8 +767,8 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= -github.com/xanzy/ssh-agent v0.3.2 h1:eKj4SX2Fe7mui28ZgnFW5fmTz1EIr7ugo5s6wDxdHBM= -github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 h1:3kHD8uZqiYes9JHdd3FzuyUbG10g9Bp9EOfqkSHIhg4= +github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= From 12a50d0c29ec28444864c86fbeef0996f59fb79e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 13:57:59 +0100 Subject: [PATCH 0513/2268] fix: Fix clearing of multiline status lines --- pkg/status/multiline/multiline.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/status/multiline/multiline.go b/pkg/status/multiline/multiline.go index 399942828..8d1153b9e 100644 --- a/pkg/status/multiline/multiline.go +++ b/pkg/status/multiline/multiline.go @@ -6,6 +6,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/term" "github.com/mattn/go-runewidth" "io" + "strings" "sync" "time" ) @@ -72,7 +73,9 @@ func (ml *MultiLinePrinter) Flush() { // Count the number of lines that need to be cleared. We need to take wrapping into account as well prevTotalLines := 0 for _, line := range ml.prevLines { - prevTotalLines += ml.countConsoleLines(line, tw) + for _, sl := range strings.Split(line, "\n") { + prevTotalLines += ml.countWrappedLines(sl, tw) + } } if prevTotalLines > 0 { ml.clearLines(prevTotalLines) @@ -93,7 +96,7 @@ func (ml *MultiLinePrinter) Flush() { } } -func (ml *MultiLinePrinter) countConsoleLines(s string, tw int) int { +func (ml *MultiLinePrinter) countWrappedLines(s string, tw int) int { s = stripansi.Strip(s) w := runewidth.StringWidth(s) cnt := 1 From bb421eb47ad24448b2ccc17c17093b2936e1238c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 14:08:31 +0100 Subject: [PATCH 0514/2268] ci: Add dependabot support --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..4143be270 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "daily" From 4ae9a5ee63cc9cc733a3276cbc27dc9dc2032ce2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:25:28 +0000 Subject: [PATCH 0515/2268] chore(deps): Bump actions/setup-go from 2 to 3 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc3074b45..5d1e4aed2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: - name: Fetch all tags run: git fetch --force --tags - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: 1.19 - name: Setup QEMU diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index faa1a4ee5..771c00cc2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: '1.19' - uses: actions/cache@v2 @@ -47,7 +47,7 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: '1.19' - uses: actions/cache@v2 @@ -84,7 +84,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: '1.19' - uses: actions/cache@v2 From 73eca1f83f627d466679d272d75260e5b5e3cdeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:25:33 +0000 Subject: [PATCH 0516/2268] chore(deps): Bump docker/setup-qemu-action from 1 to 2 Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 1 to 2. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc3074b45..af7579d5e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: with: go-version: 1.19 - name: Setup QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v2 - name: Setup Docker Buildx id: buildx uses: docker/setup-buildx-action@v1 From 39273d96af5e36c328b1539425b4e0ac82033cef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:25:42 +0000 Subject: [PATCH 0517/2268] chore(deps): Bump docker/login-action from 1 to 2 Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 2. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc3074b45..9727d90dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,7 @@ jobs: - name: Setup Syft uses: anchore/sbom-action/download-syft@v0 - name: Login to GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: kluctlbot From 0694d18a7b3fbb17f8f62b028fa378576335e15e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:25:47 +0000 Subject: [PATCH 0518/2268] chore(deps): Bump actions/cache from 2 to 3 Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc3074b45..70dff0a0c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,7 @@ jobs: registry: ghcr.io username: kluctlbot password: ${{ secrets.GHCR_TOKEN }} - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/go/pkg/mod diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index faa1a4ee5..0a019bae9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/setup-go@v2 with: go-version: '1.19' - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/go/pkg/mod @@ -50,7 +50,7 @@ jobs: - uses: actions/setup-go@v2 with: go-version: '1.19' - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/go/pkg/mod @@ -87,7 +87,7 @@ jobs: - uses: actions/setup-go@v2 with: go-version: '1.19' - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/go/pkg/mod From 0f2c257b591789ab8d843718862fc9b48544e638 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:25:56 +0000 Subject: [PATCH 0519/2268] chore(deps): Bump docker/setup-buildx-action from 1 to 2 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1 to 2. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc3074b45..f23bc8f45 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: uses: docker/setup-qemu-action@v1 - name: Setup Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Setup Syft uses: anchore/sbom-action/download-syft@v0 - name: Login to GitHub Container Registry From 305e3fd2e4372a5c2ffbe25669ff12de886a0436 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:27:48 +0000 Subject: [PATCH 0520/2268] chore(deps): Bump github.com/google/go-containerregistry Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.12.0 to 0.12.1. - [Release notes](https://github.com/google/go-containerregistry/releases) - [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml) - [Commits](https://github.com/google/go-containerregistry/compare/v0.12.0...v0.12.1) --- updated-dependencies: - dependency-name: github.com/google/go-containerregistry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ebdc3b552..1f37560d5 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 - github.com/google/go-containerregistry v0.12.0 + github.com/google/go-containerregistry v0.12.1 github.com/hashicorp/vault/api v1.8.1 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 diff --git a/go.sum b/go.sum index 9437e853f..176a61dc2 100644 --- a/go.sum +++ b/go.sum @@ -398,8 +398,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-containerregistry v0.12.0 h1:nidOEtFYlgPCRqxCKj/4c/js940HVWplCWc5ftdfdUA= -github.com/google/go-containerregistry v0.12.0/go.mod h1:sdIK+oHQO7B93xI8UweYdl887YhuIwg9vz8BSLH3+8k= +github.com/google/go-containerregistry v0.12.1 h1:W1mzdNUTx4Zla4JaixCRLhORcR7G6KxE5hHl5fkPsp8= +github.com/google/go-containerregistry v0.12.1/go.mod h1:sdIK+oHQO7B93xI8UweYdl887YhuIwg9vz8BSLH3+8k= github.com/google/gofuzz v1.0.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= From 4c21f14d7257186b9c8163c837731db338e87393 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:27:52 +0000 Subject: [PATCH 0521/2268] chore(deps): Bump golang.org/x/sys from 0.1.0 to 0.2.0 Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.1.0 to 0.2.0. - [Release notes](https://github.com/golang/sys/releases) - [Commits](https://github.com/golang/sys/compare/v0.1.0...v0.2.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ebdc3b552..af0f017b4 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( golang.org/x/crypto v0.1.0 golang.org/x/net v0.1.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.1.0 + golang.org/x/sys v0.2.0 golang.org/x/term v0.1.0 golang.org/x/text v0.4.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 9437e853f..ae46d1cbe 100644 --- a/go.sum +++ b/go.sum @@ -1122,8 +1122,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/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.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 59393c0b195409643682e5056ecad3c90292e697 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:27:57 +0000 Subject: [PATCH 0522/2268] chore(deps): Bump github.com/spf13/viper from 1.13.0 to 1.14.0 Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 13 +++-- go.sum | 176 ++++++--------------------------------------------------- 2 files changed, 25 insertions(+), 164 deletions(-) diff --git a/go.mod b/go.mod index ebdc3b552..dba0c8c73 100644 --- a/go.mod +++ b/go.mod @@ -34,8 +34,8 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.13.0 - github.com/stretchr/testify v1.8.0 + github.com/spf13/viper v1.14.0 + github.com/stretchr/testify v1.8.1 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 golang.org/x/crypto v0.1.0 @@ -64,7 +64,8 @@ require ( ) require ( - cloud.google.com/go/compute v1.10.0 // indirect + cloud.google.com/go/compute v1.12.1 // indirect + cloud.google.com/go/compute/metadata v0.2.1 // indirect filippo.io/age v1.0.0 // indirect github.com/Azure/azure-sdk-for-go v63.3.0+incompatible // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect @@ -130,8 +131,8 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect - github.com/googleapis/gax-go/v2 v2.4.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect + github.com/googleapis/gax-go/v2 v2.6.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect @@ -221,7 +222,7 @@ require ( golang.org/x/time v0.1.0 // indirect golang.org/x/tools v0.2.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/api v0.96.0 // indirect + google.golang.org/api v0.102.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect google.golang.org/grpc v1.50.1 // indirect diff --git a/go.sum b/go.sum index 9437e853f..5ef390e2b 100644 --- a/go.sum +++ b/go.sum @@ -20,34 +20,19 @@ cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPT cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= 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 v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0 h1:aoLIYaA1fX3ywihqpBk2APQKOo20nXsp1GEZQbx5Jk4= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= 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= @@ -58,7 +43,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl 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= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc= filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8= @@ -187,12 +171,8 @@ github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8 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-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/containerd v1.6.9 h1:IN/r8DUes/B5lEGTNfIiUkfZBtIQJGx2ai703dV6lRA= github.com/containerd/containerd v1.6.9/go.mod h1:XVicUvkxOrftE2Q1YWUXgZwkkAxwQYNOFzYWvfVfEfQ= @@ -245,7 +225,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m 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/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 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= @@ -353,7 +332,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -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= @@ -372,7 +350,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= @@ -393,9 +370,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-containerregistry v0.12.0 h1:nidOEtFYlgPCRqxCKj/4c/js940HVWplCWc5ftdfdUA= @@ -406,7 +380,6 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ 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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 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= @@ -419,9 +392,6 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -429,18 +399,12 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= 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/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/gax-go/v2 v2.6.0 h1:SXk3ABtQYDT/OH8jAyvEOQ58mgawq5C4o/4/89qN2ZU= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -809,13 +773,14 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn 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.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= -github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= +github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= +github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= 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 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= 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= @@ -824,8 +789,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= @@ -880,8 +846,8 @@ go.starlark.net v0.0.0-20221020143700-22309ac47eac/go.mod h1:kIVgS18CjmEC3PqMd5k 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/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 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/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -983,18 +949,11 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1010,15 +969,7 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/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.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1032,7 +983,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1092,34 +1042,19 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20210514084401-e8d321eab015/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-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/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-20220728004956-3c1f35247d10/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.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= @@ -1197,11 +1132,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f 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.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.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= @@ -1209,10 +1140,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= 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= @@ -1237,26 +1165,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.96.0 h1:F60cuQPJq7K7FzsxMYHAUJSiXh2oKctHxBMbDygxhfM= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.102.0 h1:JxJl2qQ85fRMPNvlZY/enexbxpCjLwGhZUtgfGeQ51I= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= 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= @@ -1306,47 +1216,9 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 h1:GEgb2jF5zxsFJpJfg9RoDDWm7tiwc/DDSTE2BtLUkXU= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1368,22 +1240,11 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 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.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1397,7 +1258,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= From ec0e194db10f0ac5c73e39aaf26e9fa779657005 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:28:15 +0000 Subject: [PATCH 0523/2268] chore(deps): Bump helm.sh/helm/v3 from 3.10.1 to 3.10.2 Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.10.1 to 3.10.2. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.10.1...v3.10.2) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ebdc3b552..d79225173 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( golang.org/x/text v0.4.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.10.1 + helm.sh/helm/v3 v3.10.2 k8s.io/api v0.25.3 k8s.io/apiextensions-apiserver v0.25.3 k8s.io/apimachinery v0.25.3 diff --git a/go.sum b/go.sum index 9437e853f..489310b1e 100644 --- a/go.sum +++ b/go.sum @@ -1438,8 +1438,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -helm.sh/helm/v3 v3.10.1 h1:uTnNlYx8QcTSNA4ZJ50Llwife4CSohUY4ehumyVf2QE= -helm.sh/helm/v3 v3.10.1/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= +helm.sh/helm/v3 v3.10.2 h1:2PmN9NgmqTn5pswfL5Kh2LxOKjkmh0hxKLe6/J0yUY4= +helm.sh/helm/v3 v3.10.2/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= 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= From bedc8a32cae08b487ba468875b0d18c8870dd09a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 14:33:08 +0100 Subject: [PATCH 0524/2268] feat: Move args definitions into .kluctl.yaml and deprecate args in deployment.yaml --- e2e/args_test.go | 54 ++++++++++++++++++---------- pkg/deployment/external_args.go | 18 +++++++--- pkg/kluctl_project/target_context.go | 11 +++++- pkg/types/deployment.go | 5 --- pkg/types/kluctl_project.go | 10 ++++-- 5 files changed, 67 insertions(+), 31 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index 41c28870b..f20bc88b9 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -7,7 +7,7 @@ import ( "testing" ) -func TestArgs(t *testing.T) { +func testArgs(t *testing.T, deprecated bool) { t.Parallel() k := defaultCluster1 @@ -19,24 +19,34 @@ func TestArgs(t *testing.T) { p.updateTarget("test", func(target *uo.UnstructuredObject) { }) - p.updateDeploymentYaml(".", func(o *uo.UnstructuredObject) error { - _ = o.SetNestedField([]any{ - map[string]any{ - "name": "a", - }, - map[string]any{ - "name": "b", - "default": "default", - }, - map[string]any{ - "name": "d", - "default": map[string]any{ - "nested": "default", - }, + + args := []any{ + map[string]any{ + "name": "a", + }, + map[string]any{ + "name": "b", + "default": "default", + }, + map[string]any{ + "name": "d", + "default": map[string]any{ + "nested": "default", }, - }, "args") - return nil - }) + }, + } + + if deprecated { + p.updateDeploymentYaml(".", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(args, "args") + return nil + }) + } else { + p.updateKluctlYaml(func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(args, "args") + return nil + }) + } addConfigMapDeployment(p, "cm", map[string]string{ "a": `{{ args.a | default("na") }}`, @@ -109,6 +119,14 @@ d: assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "d4"}}`, "data", "d") } +func TestDeprecatedArgs(t *testing.T) { + testArgs(t, true) +} + +func TestArgs(t *testing.T) { + testArgs(t, false) +} + func TestArgsFromEnv(t *testing.T) { t.Setenv("KLUCTL_ARG", "a=a") t.Setenv("KLUCTL_ARG_1", "b=b") diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index 365342560..53e6eaf70 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -1,7 +1,9 @@ package deployment import ( + "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars" @@ -56,7 +58,7 @@ func ConvertArgsToVars(args map[string]string, allowLoadFromFiles bool) (*uo.Uns return vars, nil } -func LoadDeploymentArgs(dir string, varsCtx *vars.VarsCtx, deployArgs *uo.UnstructuredObject) error { +func LoadDeprecatedDeploymentArgs(ctx context.Context, dir string, varsCtx *vars.VarsCtx, deployArgs *uo.UnstructuredObject) (bool, error) { // First try to load the config without templating to avoid getting errors while rendering because required // args were not set. Otherwise we won't be able to iterator through the 'args' array in the deployment.yml // when the rendering error is actually args related. @@ -72,17 +74,23 @@ func LoadDeploymentArgs(dir string, varsCtx *vars.VarsCtx, deployArgs *uo.Unstru varsCtx2.UpdateChild("args", deployArgs) err = varsCtx2.RenderYamlFile(yaml.FixNameExt(dir, "deployment.yml"), []string{dir}, &conf) if err != nil { - return err + return false, err } } if len(conf.Args) == 0 { - return nil + return false, nil } + status.Deprecation(ctx, "deployment-args", "'args' in deployment.yaml is deprecated, please use 'args' from .kluctl.yaml instead.") + + return true, LoadDefaultArgs(conf.Args, deployArgs) +} + +func LoadDefaultArgs(args []*types.DeploymentArg, deployArgs *uo.UnstructuredObject) error { // load defaults defaults := uo.New() - for _, a := range conf.Args { + for _, a := range args { if a.Default != nil { a2 := uo.FromMap(map[string]interface{}{ a.Name: a.Default, @@ -93,7 +101,7 @@ func LoadDeploymentArgs(dir string, varsCtx *vars.VarsCtx, deployArgs *uo.Unstru defaults.Merge(deployArgs) *deployArgs = *defaults - err = checkRequiredArgs(conf.Args, deployArgs) + err := checkRequiredArgs(args, deployArgs) if err != nil { return err } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 246762e92..3a47c0642 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -177,7 +177,16 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, externalArgs *uo.U } } - err = deployment.LoadDeploymentArgs(p.ProjectDir, varsCtx, allArgs) + deprecatedArgs, err := deployment.LoadDeprecatedDeploymentArgs(p.ctx, p.ProjectDir, varsCtx, allArgs) + if err != nil { + return nil, err + } + + if deprecatedArgs && len(p.Config.Args) != 0 { + return nil, fmt.Errorf("mixing deprecated 'args' from deployment.yaml and .kluctl.yaml is not allowed") + } + + err = deployment.LoadDefaultArgs(p.Config.Args, allArgs) if err != nil { return nil, err } diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index d331457f2..eee074fda 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -53,11 +53,6 @@ func ValidateDeleteObjectItemConfig(sl validator.StructLevel) { } } -type DeploymentArg struct { - Name string `yaml:"name" validate:"required"` - Default interface{} `yaml:"default,omitempty"` -} - type SealedSecretsConfig struct { OutputPattern *string `yaml:"outputPattern,omitempty"` } diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index b1e7e5f68..0266fa1a0 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -41,6 +41,11 @@ type DynamicTarget struct { BaseTargetName string `yaml:"baseTargetName"` } +type DeploymentArg struct { + Name string `yaml:"name" validate:"required"` + Default interface{} `yaml:"default,omitempty"` +} + type SecretSet struct { Name string `yaml:"name" validate:"required"` Vars []*VarsSource `yaml:"vars,omitempty"` @@ -58,6 +63,7 @@ type SecretsConfig struct { } type KluctlProject struct { - Targets []*Target `yaml:"targets,omitempty"` - SecretsConfig *SecretsConfig `yaml:"secretsConfig,omitempty"` + Targets []*Target `yaml:"targets,omitempty"` + Args []*DeploymentArg `yaml:"args,omitempty"` + SecretsConfig *SecretsConfig `yaml:"secretsConfig,omitempty"` } From 941f90e93a8398fc9f4b0acddce2c80e7cf1c455 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 15:01:46 +0100 Subject: [PATCH 0525/2268] feat: Allow to run kluctl on simple kustomize deployments --- pkg/deployment/deployment_project.go | 22 +++++++++++++++++++--- pkg/deployment/external_args.go | 4 ++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 04a06dac7..a0bb80952 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -70,10 +70,15 @@ func (p *DeploymentProject) loadVarsList(varsCtx *vars.VarsCtx, varsList []*type func (p *DeploymentProject) loadConfig() error { configPath := filepath.Join(p.absDir, "deployment.yml") if !yaml.Exists(configPath) { - if yaml.Exists(filepath.Join(p.absDir, "kustomization.yml")) { + if p.parentProject == nil { + // the root project is allowed to not have a deployment.yaml + // in that case, it is treated as a simple deployment with a single deployment item pointing to "." + return p.generateSingleKustomizeProject() + } else if yaml.Exists(filepath.Join(p.absDir, "kustomization.yml")) { return fmt.Errorf("deployment.yml not found but folder %s contains a kustomization.yml", p.absDir) + } else { + return fmt.Errorf("%s not found", configPath) } - return fmt.Errorf("%s not found", configPath) } configPath = yaml.FixPathExt(configPath) @@ -82,7 +87,18 @@ func (p *DeploymentProject) loadConfig() error { return fmt.Errorf("failed to load deployment.yml: %w", err) } - err = p.loadVarsList(p.VarsCtx, p.Config.Vars) + return p.processConfig() +} + +func (p *DeploymentProject) generateSingleKustomizeProject() error { + p.Config.Deployments = append(p.Config.Deployments, &types.DeploymentItemConfig{ + Path: utils.StrPtr("."), + }) + return nil +} + +func (p *DeploymentProject) processConfig() error { + err := p.loadVarsList(p.VarsCtx, p.Config.Vars) if err != nil { return fmt.Errorf("failed to load deployment.yml vars: %w", err) } diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index 53e6eaf70..c7f0aefcc 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -63,6 +63,10 @@ func LoadDeprecatedDeploymentArgs(ctx context.Context, dir string, varsCtx *vars // args were not set. Otherwise we won't be able to iterator through the 'args' array in the deployment.yml // when the rendering error is actually args related. + if !yaml.Exists(filepath.Join(dir, "deployment.yml")) { + return false, nil + } + var conf types.DeploymentProjectConfig err := yaml.ReadYamlFile(yaml.FixPathExt(filepath.Join(dir, "deployment.yml")), &conf) From 8c5c0e732b3455bf85e8c0186ac2386c081e4658 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 15:19:13 +0100 Subject: [PATCH 0526/2268] refactor: Remove mutex that is not really needed in RemoteObjectUtils --- pkg/deployment/utils/remote_objects_utils.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index 442acb1a6..e61cff4f6 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -8,7 +8,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/apimachinery/pkg/runtime/schema" - "sync" ) type RemoteObjectUtils struct { @@ -16,7 +15,6 @@ type RemoteObjectUtils struct { dew *DeploymentErrorsAndWarnings remoteObjects map[k8s2.ObjectRef]*uo.UnstructuredObject remoteNamespaces map[string]*uo.UnstructuredObject - mutex sync.Mutex } func NewRemoteObjectsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnings) *RemoteObjectUtils { @@ -44,7 +42,6 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st return err } - u.mutex.Lock() for _, o := range allObjects { u.remoteObjects[o.GetK8sRef()] = o } @@ -59,7 +56,6 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st } } } - u.mutex.Unlock() if len(notFoundRefsList) != 0 { s.UpdateAndInfoFallback("Getting %d additional remote objects", len(notFoundRefsList)) @@ -70,11 +66,9 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st if err != nil { return err } - u.mutex.Lock() for _, o := range r { u.remoteObjects[o.GetK8sRef()] = o } - u.mutex.Unlock() } s.UpdateAndInfoFallback("Getting namespaces") @@ -96,31 +90,22 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st } func (u *RemoteObjectUtils) GetRemoteObject(ref k8s2.ObjectRef) *uo.UnstructuredObject { - u.mutex.Lock() - defer u.mutex.Unlock() o, _ := u.remoteObjects[ref] return o } func (u *RemoteObjectUtils) GetRemoteNamespace(name string) *uo.UnstructuredObject { - u.mutex.Lock() - defer u.mutex.Unlock() o, _ := u.remoteNamespaces[name] return o } func (u *RemoteObjectUtils) ForgetRemoteObject(ref k8s2.ObjectRef) { - u.mutex.Lock() - defer u.mutex.Unlock() delete(u.remoteObjects, ref) } func (u *RemoteObjectUtils) GetFilteredRemoteObjects(inclusion *utils.Inclusion) []*uo.UnstructuredObject { var ret []*uo.UnstructuredObject - u.mutex.Lock() - defer u.mutex.Unlock() - for _, o := range u.remoteObjects { iv := u.getInclusionEntries(o) if inclusion.CheckIncluded(iv, false) { From d6aee5b1d63dcd75370c009b9023e862c589f1d5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 15:21:27 +0100 Subject: [PATCH 0527/2268] feat: Allow deployments without commonLabels But forbid deletes. --- e2e/no_target_test.go | 26 +++++++++++++++++--- pkg/deployment/commands/delete.go | 5 ++++ pkg/deployment/deployment_project.go | 4 --- pkg/deployment/utils/remote_objects_utils.go | 21 ++++++++-------- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index 7c2f74f61..7e3083cee 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -2,10 +2,12 @@ package e2e import ( "github.com/stretchr/testify/assert" + "os" + "path/filepath" "testing" ) -func prepareNoTargetTest(t *testing.T, name string) *testProject { +func prepareNoTargetTest(t *testing.T, name string, withDeploymentYaml bool) *testProject { p := &testProject{} p.init(t, defaultCluster1, name) p.mergeKubeconfig(defaultCluster2) @@ -13,7 +15,7 @@ func prepareNoTargetTest(t *testing.T, name string) *testProject { createNamespace(t, defaultCluster1, p.projectName) createNamespace(t, defaultCluster2, p.projectName) - addConfigMapDeployment(p, "cm", map[string]string{ + cm := createConfigMapObject(map[string]string{ "targetName": `{{ target.name }}`, "targetContext": `{{ target.context }}`, }, resourceOpts{ @@ -21,13 +23,21 @@ func prepareNoTargetTest(t *testing.T, name string) *testProject { namespace: p.projectName, }) + if withDeploymentYaml { + p.addKustomizeDeployment("cm", []kustomizeResource{{name: "cm.yaml", content: cm}}, nil) + } else { + p.addKustomizeResources("", []kustomizeResource{{name: "cm.yaml", content: cm}}) + err := os.Remove(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "deployment.yml")) + assert.NoError(t, err) + } + return p } -func TestNoTarget(t *testing.T) { +func testNoTarget(t *testing.T, withDeploymentYaml bool) { t.Parallel() - p := prepareNoTargetTest(t, "no-target") + p := prepareNoTargetTest(t, "no-target", withDeploymentYaml) p.KluctlMust("deploy", "--yes") cm := assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") @@ -51,3 +61,11 @@ func TestNoTarget(t *testing.T) { "targetContext": defaultCluster2.Context, }, cm.Object["data"]) } + +func TestNoTarget(t *testing.T) { + testNoTarget(t, true) +} + +func TestNoTargetNoDeployment(t *testing.T) { + testNoTarget(t, false) +} diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index d4cb32fd8..e32ae759f 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -2,6 +2,7 @@ package commands import ( "context" + "fmt" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -32,6 +33,10 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.Ob labels = cmd.c.Project.GetCommonLabels() } + if len(labels) == 0 { + return nil, fmt.Errorf("deletion without using commonLabels in the root deployment.yaml is not allowed") + } + var inclusion *utils.Inclusion if cmd.c != nil { inclusion = cmd.c.Inclusion diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index a0bb80952..9f3909ab7 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -121,10 +121,6 @@ func (p *DeploymentProject) processConfig() error { return err } - if len(p.GetCommonLabels()) == 0 { - return fmt.Errorf("no commonLabels in root deployment. This is not allowed") - } - if len(p.Config.Args) != 0 && p.parentProject != nil { return fmt.Errorf("only the root deployment.yml can define args") } diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index e61cff4f6..520a786a0 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -34,16 +34,17 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st s := status.Start(u.ctx, "Getting remote objects by commonLabels") defer s.Failed() - allObjects, apiWarnings, err := k.ListAllObjects([]string{"get"}, "", labels) - for gvk, aw := range apiWarnings { - u.dew.AddApiWarnings(k8s2.ObjectRef{GVK: gvk}, aw) - } - if err != nil { - return err - } - - for _, o := range allObjects { - u.remoteObjects[o.GetK8sRef()] = o + if len(labels) != 0 { + allObjects, apiWarnings, err := k.ListAllObjects([]string{"get"}, "", labels) + for gvk, aw := range apiWarnings { + u.dew.AddApiWarnings(k8s2.ObjectRef{GVK: gvk}, aw) + } + if err != nil { + return err + } + for _, o := range allObjects { + u.remoteObjects[o.GetK8sRef()] = o + } } notFoundRefsMap := make(map[k8s2.ObjectRef]bool) From 6d34cf7b25b1056a44ab20e2162852a796f6efd6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 15:40:29 +0100 Subject: [PATCH 0528/2268] fix: Ignore .git folders by default --- pkg/deployment/deployment_item.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index d42afe526..69cdb586f 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -123,6 +123,8 @@ func (di *DeploymentItem) render(forSeal bool) error { } var excludePatterns []string + excludePatterns = append(excludePatterns, "**/.git") + if len(di.Project.Config.TemplateExcludes) != 0 { status.Deprecation(di.ctx.Ctx, "template-excludes", "'templateExcludes' are deprecated, use .templateignore files instead.") } From 12595b79fa4e66101706245bc752e133bd7e17b8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 16:09:47 +0100 Subject: [PATCH 0529/2268] tests: Automate namespace uses in tests --- e2e/args_test.go | 38 ++++++++++----------- e2e/contexts_test.go | 54 ++++++++++++++--------------- e2e/deployment_items_test.go | 30 ++++++++-------- e2e/flux_test.go | 2 +- e2e/hooks_test.go | 16 ++++----- e2e/inclusion_test.go | 44 ++++++++++++------------ e2e/no_target_test.go | 20 +++++------ e2e/project.go | 20 ++++++----- e2e/seal_test.go | 66 ++++++++++++++++-------------------- e2e/sops_test.go | 16 ++++----- 10 files changed, 150 insertions(+), 156 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index f20bc88b9..05e38fe97 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -13,9 +13,9 @@ func testArgs(t *testing.T, deprecated bool) { k := defaultCluster1 p := &testProject{} - p.init(t, k, "args") + p.init(t, k) - createNamespace(t, k, p.projectName) + createNamespace(t, k, p.testSlug()) p.updateTarget("test", func(target *uo.UnstructuredObject) { }) @@ -55,36 +55,36 @@ func testArgs(t *testing.T, deprecated bool) { "d": "{{ args.d | to_json }}", }, resourceOpts{ name: "cm", - namespace: p.projectName, + namespace: p.testSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a") - cm := k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm := k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "default", "data", "b") assertNestedFieldEquals(t, cm, "na", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") - cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "na", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c") - cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "c", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", "-ad.nested=d") - cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, `{"nested": "d"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", `-ad={"nested": "d2"}`) - cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, `{"nested": "d2"}`, "data", "d") tmpFile, err := os.CreateTemp("", "") @@ -98,7 +98,7 @@ nested: `) p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", fmt.Sprintf(`-ad=@%s`, tmpFile.Name())) - cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "d3"}}`, "data", "d") _ = tmpFile.Truncate(0) @@ -112,7 +112,7 @@ d: `) p.KluctlMust("deploy", "--yes", "-t", "test", fmt.Sprintf(`--args-from-file=%s`, tmpFile.Name())) - cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, "a2", "data", "a") assertNestedFieldEquals(t, cm, "default", "data", "b") assertNestedFieldEquals(t, cm, "c2", "data", "c") @@ -137,9 +137,9 @@ func TestArgsFromEnv(t *testing.T) { k := defaultCluster1 p := &testProject{} - p.init(t, k, "args-from-envs") + p.init(t, k) - createNamespace(t, k, p.projectName) + createNamespace(t, k, p.testSlug()) p.updateTarget("test", func(target *uo.UnstructuredObject) { }) @@ -152,11 +152,11 @@ func TestArgsFromEnv(t *testing.T) { "e": `{{ args.e }}`, }, resourceOpts{ name: "cm", - namespace: p.projectName, + namespace: p.testSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test") - cm := k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm := k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "c"}}`, "data", "c") @@ -171,9 +171,9 @@ func TestArgsFromEnvAndCli(t *testing.T) { k := defaultCluster1 p := &testProject{} - p.init(t, k, "args-from-envs-and-cli") + p.init(t, k) - createNamespace(t, k, p.projectName) + createNamespace(t, k, p.testSlug()) p.updateTarget("test", func(target *uo.UnstructuredObject) { }) @@ -184,18 +184,18 @@ func TestArgsFromEnvAndCli(t *testing.T) { "c": `{{ args.c }}`, }, resourceOpts{ name: "cm", - namespace: p.projectName, + namespace: p.testSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "b=b") - cm := k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm := k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "c", "data", "c") // make sure the CLI overrides values from env p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "b=b", "-a", "c=c2") - cm = k.MustGetCoreV1(t, "configmaps", p.projectName, "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "c2", "data", "c") diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index 29fa1d133..a6e2a0898 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -6,17 +6,17 @@ import ( "testing" ) -func prepareContextTest(t *testing.T, name string) *testProject { +func prepareContextTest(t *testing.T) *testProject { p := &testProject{} - p.init(t, defaultCluster1, name) + p.init(t, defaultCluster1) p.mergeKubeconfig(defaultCluster2) - createNamespace(t, defaultCluster1, p.projectName) - createNamespace(t, defaultCluster2, p.projectName) + createNamespace(t, defaultCluster1, p.testSlug()) + createNamespace(t, defaultCluster2, p.testSlug()) addConfigMapDeployment(p, "cm", nil, resourceOpts{ name: "cm", - namespace: p.projectName, + namespace: p.testSlug(), }) return p @@ -25,56 +25,56 @@ func prepareContextTest(t *testing.T, name string) *testProject { func TestContextCurrent(t *testing.T) { t.Parallel() - p := prepareContextTest(t, "context-current") + p := prepareContextTest(t) p.updateTarget("test1", func(target *uo.UnstructuredObject) { // no context set, assume the current one is used }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") - assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") p.updateMergedKubeconfig(func(config *api.Config) { config.CurrentContext = defaultCluster2.Context }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") } func TestContext1(t *testing.T) { t.Parallel() - p := prepareContextTest(t, "context-1") + p := prepareContextTest(t) p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") - assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") } func TestContext2(t *testing.T) { t.Parallel() - p := prepareContextTest(t, "context-2") + p := prepareContextTest(t) p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") - assertConfigMapNotExists(t, defaultCluster1, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster1, p.testSlug(), "cm") } func TestContext1And2(t *testing.T) { t.Parallel() - p := prepareContextTest(t, "context-1-and-2") + p := prepareContextTest(t) p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") @@ -84,47 +84,47 @@ func TestContext1And2(t *testing.T) { }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") - assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") p.KluctlMust("deploy", "--yes", "-t", "test2") - assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") } func TestContextSwitch(t *testing.T) { t.Parallel() - p := prepareContextTest(t, "context-switch") + p := prepareContextTest(t) p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") - assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") } func TestContextOverride(t *testing.T) { t.Parallel() - p := prepareContextTest(t, "context-override") + p := prepareContextTest(t) p.updateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") - assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") p.KluctlMust("deploy", "--yes", "-t", "test1", "--context", defaultCluster2.Context) - assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") + assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") } diff --git a/e2e/deployment_items_test.go b/e2e/deployment_items_test.go index c1a8ffe8e..a9fa3c5b6 100644 --- a/e2e/deployment_items_test.go +++ b/e2e/deployment_items_test.go @@ -11,27 +11,27 @@ func TestKustomize(t *testing.T) { k := defaultCluster1 p := &testProject{} - p.init(t, k, "di-kustomize") + p.init(t, k) - createNamespace(t, k, p.projectName) + createNamespace(t, k, p.testSlug()) p.updateTarget("test", nil) addConfigMapDeployment(p, "cm", nil, resourceOpts{ name: "cm", - namespace: p.projectName, + namespace: p.testSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.projectName, "cm") + assertConfigMapExists(t, k, p.testSlug(), "cm") addConfigMapDeployment(p, "cm2", nil, resourceOpts{ name: "cm2", - namespace: p.projectName, + namespace: p.testSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test", "--dry-run") - assertConfigMapNotExists(t, k, p.projectName, "cm2") + assertConfigMapNotExists(t, k, p.testSlug(), "cm2") p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.projectName, "cm2") + assertConfigMapExists(t, k, p.testSlug(), "cm2") } func TestGeneratedKustomize(t *testing.T) { @@ -40,9 +40,9 @@ func TestGeneratedKustomize(t *testing.T) { k := defaultCluster1 p := &testProject{} - p.init(t, k, "di-generated-kustomize") + p.init(t, k) - createNamespace(t, k, p.projectName) + createNamespace(t, k, p.testSlug()) p.updateTarget("test", nil) @@ -57,27 +57,27 @@ func TestGeneratedKustomize(t *testing.T) { p.updateYaml("generated-kustomize/cm1.yaml", func(o *uo.UnstructuredObject) error { *o = *createConfigMapObject(nil, resourceOpts{ name: "cm1", - namespace: p.projectName, + namespace: p.testSlug(), }) return nil }, "") p.updateYaml("generated-kustomize/cm2.yaml", func(o *uo.UnstructuredObject) error { *o = *createConfigMapObject(nil, resourceOpts{ name: "cm2", - namespace: p.projectName, + namespace: p.testSlug(), }) return nil }, "") p.updateYaml("generated-kustomize/cm3._yaml", func(o *uo.UnstructuredObject) error { *o = *createConfigMapObject(nil, resourceOpts{ name: "cm3", - namespace: p.projectName, + namespace: p.testSlug(), }) return nil }, "") p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.projectName, "cm1") - assertConfigMapExists(t, k, p.projectName, "cm2") - assertConfigMapNotExists(t, k, p.projectName, "cm3") + assertConfigMapExists(t, k, p.testSlug(), "cm1") + assertConfigMapExists(t, k, p.testSlug(), "cm2") + assertConfigMapNotExists(t, k, p.testSlug(), "cm3") } diff --git a/e2e/flux_test.go b/e2e/flux_test.go index b7b4bcb53..b796e792d 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -37,7 +37,7 @@ func TestFluxCommands(t *testing.T) { k := defaultCluster1 p := &testProject{} - p.init(t, k, "flux-test") + p.init(t, k) var wg sync.WaitGroup wg.Add(2) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index af1c2221c..863ba6af3 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -36,7 +36,7 @@ func (s *hooksTestContext) removeWebhook() { } func (s *hooksTestContext) handleConfigmap(request admission.Request) { - if s.p.projectName != request.Namespace { + if s.p.testSlug() != request.Namespace { return } @@ -100,21 +100,19 @@ func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletion } s.setupWebhook() - namespace := fmt.Sprintf("hook-%s", name) - - s.p.init(t, s.k, namespace) + s.p.init(t, s.k) t.Cleanup(func() { s.removeWebhook() }) - createNamespace(s.t, s.k, namespace) + createNamespace(s.t, s.k, s.p.testSlug()) s.p.updateTarget("test", nil) s.p.addKustomizeDeployment("hook", nil, nil) - s.addConfigMap("hook", resourceOpts{name: "cm1", namespace: namespace}) - s.addHookConfigMap("hook", resourceOpts{name: "hook1", namespace: namespace}, false, hook, hookDeletionPolicy) + s.addConfigMap("hook", resourceOpts{name: "cm1", namespace: s.p.testSlug()}) + s.addHookConfigMap("hook", resourceOpts{name: "hook1", namespace: s.p.testSlug()}, false, hook, hookDeletionPolicy) return s } @@ -127,10 +125,10 @@ func (s *hooksTestContext) ensureHookExecuted(expectedCms ...string) { func (s *hooksTestContext) ensureHookNotExecuted() { _ = s.k.DynamicClient.Resource(corev1.SchemeGroupVersion.WithResource("configmaps")). - Namespace(s.p.projectName). + Namespace(s.p.testSlug()). Delete(context.Background(), "cm1", metav1.DeleteOptions{}) s.p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapNotExists(s.t, s.k, s.p.projectName, "cm1") + assertConfigMapNotExists(s.t, s.k, s.p.testSlug(), "cm1") } func TestHooksPreDeployInitial(t *testing.T) { diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index edba11b1a..a50564854 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -8,34 +8,34 @@ import ( "testing" ) -func prepareInclusionTestProject(t *testing.T, namespace string, withIncludes bool) (*testProject, *test_utils.EnvTestCluster) { +func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*testProject, *test_utils.EnvTestCluster) { k := defaultCluster1 p := &testProject{} - p.init(t, k, namespace) + p.init(t, k) - createNamespace(t, k, p.projectName) + createNamespace(t, k, p.testSlug()) p.updateTarget("test", nil) - addConfigMapDeployment(p, "cm1", nil, resourceOpts{name: "cm1", namespace: p.projectName}) - addConfigMapDeployment(p, "cm2", nil, resourceOpts{name: "cm2", namespace: p.projectName}) - addConfigMapDeployment(p, "cm3", nil, resourceOpts{name: "cm3", namespace: p.projectName, tags: []string{"tag1", "tag2"}}) - addConfigMapDeployment(p, "cm4", nil, resourceOpts{name: "cm4", namespace: p.projectName, tags: []string{"tag1", "tag3"}}) - addConfigMapDeployment(p, "cm5", nil, resourceOpts{name: "cm5", namespace: p.projectName, tags: []string{"tag1", "tag4"}}) - addConfigMapDeployment(p, "cm6", nil, resourceOpts{name: "cm6", namespace: p.projectName, tags: []string{"tag1", "tag5"}}) - addConfigMapDeployment(p, "cm7", nil, resourceOpts{name: "cm7", namespace: p.projectName, tags: []string{"tag1", "tag6"}}) + addConfigMapDeployment(p, "cm1", nil, resourceOpts{name: "cm1", namespace: p.testSlug()}) + addConfigMapDeployment(p, "cm2", nil, resourceOpts{name: "cm2", namespace: p.testSlug()}) + addConfigMapDeployment(p, "cm3", nil, resourceOpts{name: "cm3", namespace: p.testSlug(), tags: []string{"tag1", "tag2"}}) + addConfigMapDeployment(p, "cm4", nil, resourceOpts{name: "cm4", namespace: p.testSlug(), tags: []string{"tag1", "tag3"}}) + addConfigMapDeployment(p, "cm5", nil, resourceOpts{name: "cm5", namespace: p.testSlug(), tags: []string{"tag1", "tag4"}}) + addConfigMapDeployment(p, "cm6", nil, resourceOpts{name: "cm6", namespace: p.testSlug(), tags: []string{"tag1", "tag5"}}) + addConfigMapDeployment(p, "cm7", nil, resourceOpts{name: "cm7", namespace: p.testSlug(), tags: []string{"tag1", "tag6"}}) if withIncludes { p.addDeploymentInclude(".", "include1", nil) - addConfigMapDeployment(p, "include1/icm1", nil, resourceOpts{name: "icm1", namespace: p.projectName, tags: []string{"itag1", "itag2"}}) + addConfigMapDeployment(p, "include1/icm1", nil, resourceOpts{name: "icm1", namespace: p.testSlug(), tags: []string{"itag1", "itag2"}}) p.addDeploymentInclude(".", "include2", nil) - addConfigMapDeployment(p, "include2/icm2", nil, resourceOpts{name: "icm2", namespace: p.projectName}) - addConfigMapDeployment(p, "include2/icm3", nil, resourceOpts{name: "icm3", namespace: p.projectName, tags: []string{"itag3", "itag4"}}) + addConfigMapDeployment(p, "include2/icm2", nil, resourceOpts{name: "icm2", namespace: p.testSlug()}) + addConfigMapDeployment(p, "include2/icm3", nil, resourceOpts{name: "icm3", namespace: p.testSlug(), tags: []string{"itag3", "itag4"}}) p.addDeploymentInclude(".", "include3", []string{"itag5"}) - addConfigMapDeployment(p, "include3/icm4", nil, resourceOpts{name: "icm4", namespace: p.projectName}) - addConfigMapDeployment(p, "include3/icm5", nil, resourceOpts{name: "icm5", namespace: p.projectName, tags: []string{"itag5", "itag6"}}) + addConfigMapDeployment(p, "include3/icm4", nil, resourceOpts{name: "icm4", namespace: p.testSlug()}) + addConfigMapDeployment(p, "include3/icm5", nil, resourceOpts{name: "icm5", namespace: p.testSlug(), tags: []string{"itag5", "itag6"}}) } return p, k @@ -50,7 +50,7 @@ func assertExistsHelper(t *testing.T, p *testProject, k *test_utils.EnvTestClust delete(shouldExists, x) } } - items, err := k.List(corev1.SchemeGroupVersion.WithResource("configmaps"), p.projectName, map[string]string{"project_name": p.projectName}) + items, err := k.List(corev1.SchemeGroupVersion.WithResource("configmaps"), p.testSlug(), map[string]string{"project_name": p.testSlug()}) if err != nil { t.Fatal(err) } @@ -65,7 +65,7 @@ func assertExistsHelper(t *testing.T, p *testProject, k *test_utils.EnvTestClust func TestInclusionTags(t *testing.T) { t.Parallel() - p, k := prepareInclusionTestProject(t, "inclusion-tags", false) + p, k := prepareInclusionTestProject(t, false) shouldExists := make(map[string]bool) doAssertExists := func(add ...string) { @@ -99,7 +99,7 @@ func TestInclusionTags(t *testing.T) { func TestExclusionTags(t *testing.T) { t.Parallel() - p, k := prepareInclusionTestProject(t, "inclusion-exclusion", false) + p, k := prepareInclusionTestProject(t, false) shouldExists := make(map[string]bool) doAssertExists := func(add ...string) { @@ -123,7 +123,7 @@ func TestExclusionTags(t *testing.T) { func TestInclusionIncludeDirs(t *testing.T) { t.Parallel() - p, k := prepareInclusionTestProject(t, "inclusion-dirs", true) + p, k := prepareInclusionTestProject(t, true) shouldExists := make(map[string]bool) doAssertExists := func(add ...string) { @@ -144,7 +144,7 @@ func TestInclusionIncludeDirs(t *testing.T) { func TestInclusionDeploymentDirs(t *testing.T) { t.Parallel() - p, k := prepareInclusionTestProject(t, "inclusion-kustomize-dirs", true) + p, k := prepareInclusionTestProject(t, true) shouldExists := make(map[string]bool) doAssertExists := func(add ...string) { @@ -171,7 +171,7 @@ func TestInclusionDeploymentDirs(t *testing.T) { func TestInclusionPrune(t *testing.T) { t.Parallel() - p, k := prepareInclusionTestProject(t, "inclusion-prune", false) + p, k := prepareInclusionTestProject(t, false) shouldExists := make(map[string]bool) doAssertExists := func(add []string, remove []string) { @@ -204,7 +204,7 @@ func TestInclusionPrune(t *testing.T) { func TestInclusionDelete(t *testing.T) { t.Parallel() - p, k := prepareInclusionTestProject(t, "inclusion-delete", false) + p, k := prepareInclusionTestProject(t, false) shouldExists := make(map[string]bool) doAssertExists := func(add []string, remove []string) { diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index 7e3083cee..01cdfe738 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -7,20 +7,20 @@ import ( "testing" ) -func prepareNoTargetTest(t *testing.T, name string, withDeploymentYaml bool) *testProject { +func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *testProject { p := &testProject{} - p.init(t, defaultCluster1, name) + p.init(t, defaultCluster1) p.mergeKubeconfig(defaultCluster2) - createNamespace(t, defaultCluster1, p.projectName) - createNamespace(t, defaultCluster2, p.projectName) + createNamespace(t, defaultCluster1, p.testSlug()) + createNamespace(t, defaultCluster2, p.testSlug()) cm := createConfigMapObject(map[string]string{ "targetName": `{{ target.name }}`, "targetContext": `{{ target.context }}`, }, resourceOpts{ name: "cm", - namespace: p.projectName, + namespace: p.testSlug(), }) if withDeploymentYaml { @@ -37,25 +37,25 @@ func prepareNoTargetTest(t *testing.T, name string, withDeploymentYaml bool) *te func testNoTarget(t *testing.T, withDeploymentYaml bool) { t.Parallel() - p := prepareNoTargetTest(t, "no-target", withDeploymentYaml) + p := prepareNoTargetTest(t, withDeploymentYaml) p.KluctlMust("deploy", "--yes") - cm := assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") - assertConfigMapNotExists(t, defaultCluster2, p.projectName, "cm") + cm := assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") assert.Equal(t, map[string]any{ "targetName": "", "targetContext": defaultCluster1.Context, }, cm.Object["data"]) p.KluctlMust("deploy", "--yes", "-T", "override-name") - cm = assertConfigMapExists(t, defaultCluster1, p.projectName, "cm") + cm = assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") assert.Equal(t, map[string]any{ "targetName": "override-name", "targetContext": defaultCluster1.Context, }, cm.Object["data"]) p.KluctlMust("deploy", "--yes", "-T", "override-name", "--context", defaultCluster2.Context) - cm = assertConfigMapExists(t, defaultCluster2, p.projectName, "cm") + cm = assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") assert.Equal(t, map[string]any{ "targetName": "override-name", "targetContext": defaultCluster2.Context, diff --git a/e2e/project.go b/e2e/project.go index 2e137e841..41fd915eb 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -3,6 +3,7 @@ package e2e import ( "fmt" "github.com/go-git/go-git/v5" + "github.com/huandu/xstrings" "github.com/imdario/mergo" test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -18,20 +19,17 @@ import ( ) type testProject struct { - t *testing.T - extraEnv []string - projectName string + t *testing.T + extraEnv []string mergedKubeconfig string gitServer *test_utils.GitServer } -func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster, projectName string) { +func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster) { p.t = t p.gitServer = test_utils.NewGitServer(t) - p.projectName = projectName - p.gitServer.GitInit(p.getKluctlProjectRepo()) p.updateKluctlYaml(func(o *uo.UnstructuredObject) error { @@ -41,7 +39,7 @@ func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster, projectNa return nil }) - tmpFile, err := os.CreateTemp("", projectName+"-kubeconfig-") + tmpFile, err := os.CreateTemp("", p.testSlug()+"-kubeconfig-") if err != nil { t.Fatal(err) } @@ -53,6 +51,12 @@ func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster, projectNa p.mergeKubeconfig(k) } +func (p *testProject) testSlug() string { + n := p.t.Name() + n = xstrings.ToKebabCase(n) + return n +} + func (p *testProject) mergeKubeconfig(k *test_utils.EnvTestCluster) { p.updateMergedKubeconfig(func(config *clientcmdapi.Config) { nkcfg, err := clientcmd.Load(k.Kubeconfig) @@ -88,7 +92,7 @@ func (p *testProject) updateKluctlYaml(update func(o *uo.UnstructuredObject) err func (p *testProject) updateDeploymentYaml(dir string, update func(o *uo.UnstructuredObject) error) { p.updateYaml(filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { if dir == "." { - o.SetNestedField(p.projectName, "commonLabels", "project_name") + o.SetNestedField(p.testSlug(), "commonLabels", "project_name") } return update(o) }, "") diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 9f5e00490..c5b9268bf 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -97,20 +97,20 @@ func addProxyVars(p *testProject) { f(1, defaultCluster2, certServer2) } -func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, namespace string, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *testProject { +func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *testProject { p := &testProject{} - p.init(t, k, fmt.Sprintf("seal-%s", namespace)) + p.init(t, k) if proxy { addProxyVars(p) } - createNamespace(t, k, namespace) + createNamespace(t, k, p.testSlug()) addSecretsSet(p, "test", varsSources) addSecretsSetToTarget(p, "test-target", "test") - addSecretDeployment(p, "secret-deployment", secrets, resourceOpts{name: "secret", namespace: namespace}) + addSecretDeployment(p, "secret-deployment", secrets, resourceOpts{name: "secret", namespace: p.testSlug()}) return p } @@ -162,9 +162,8 @@ func assertSealedSecret(t *testing.T, k *test_utils.EnvTestCluster, namespace st func TestSeal_WithOperator(t *testing.T) { t.Parallel() k := defaultCluster1 - namespace := "seal-with-operator" - p := prepareSealTest(t, k, namespace, + p := prepareSealTest(t, k, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -184,7 +183,7 @@ func TestSeal_WithOperator(t *testing.T) { assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -194,13 +193,12 @@ func TestSeal_WithBootstrap(t *testing.T) { // this test must NOT run in parallel k := defaultCluster1 - namespace := "seal-with-bootstrap" // deleting the crd causes kluctl to not recognize the operator, so it will do a bootstrap _ = k.DynamicClient.Resource(apiextensionsv1.SchemeGroupVersion.WithResource("customresourcedefinitions")). Delete(context.Background(), "sealedsecrets.bitnami.com", metav1.DeleteOptions{}) - p := prepareSealTest(t, k, namespace, + p := prepareSealTest(t, k, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -233,7 +231,7 @@ func TestSeal_WithBootstrap(t *testing.T) { certHash, err := seal.HashPublicKey(cert) assert.NoError(t, err) - assertSealedSecret(t, k, namespace, "secret", certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret", certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -243,9 +241,8 @@ func TestSeal_MultipleVarSources(t *testing.T) { t.Parallel() k := defaultCluster1 - namespace := "seal-multiple-vs" - p := prepareSealTest(t, k, namespace, + p := prepareSealTest(t, k, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -270,7 +267,7 @@ func TestSeal_MultipleVarSources(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -280,9 +277,8 @@ func TestSeal_MultipleSecretSets(t *testing.T) { t.Parallel() k := defaultCluster1 - namespace := "seal-multiple-ss" - p := prepareSealTest(t, k, namespace, + p := prepareSealTest(t, k, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -311,7 +307,7 @@ func TestSeal_MultipleSecretSets(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -321,9 +317,8 @@ func TestSeal_MultipleTargets(t *testing.T) { t.Parallel() k := defaultCluster1 - namespace := "seal-multiple-targets" - p := prepareSealTest(t, k, namespace, + p := prepareSealTest(t, k, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -348,7 +343,7 @@ func TestSeal_MultipleTargets(t *testing.T) { addSecretsSetToTarget(p, "test-target2", "test2") p.mergeKubeconfig(defaultCluster2) - createNamespace(t, defaultCluster2, namespace) + createNamespace(t, defaultCluster2, p.testSlug()) p.updateTarget("test-target", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") }) @@ -366,11 +361,11 @@ func TestSeal_MultipleTargets(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") p.KluctlMust("deploy", "--yes", "-t", "test-target2") - assertSealedSecret(t, defaultCluster1, namespace, "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, defaultCluster1, p.testSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) - assertSealedSecret(t, defaultCluster2, namespace, "secret", certServer2.certHash, map[string]string{ + assertSealedSecret(t, defaultCluster2, p.testSlug(), "secret", certServer2.certHash, map[string]string{ "s1": "v3", "s2": "v4", }) @@ -380,7 +375,6 @@ func TestSeal_MultipleSecrets(t *testing.T) { t.Parallel() k := defaultCluster1 - namespace := "seal-multiple-secrets" secret1 := map[string]string{ "s1": "{{ secrets.s1 }}", @@ -389,7 +383,7 @@ func TestSeal_MultipleSecrets(t *testing.T) { "s2": "{{ secrets.s2 }}", } - p := prepareSealTest(t, k, namespace, + p := prepareSealTest(t, k, secret1, []*uo.UnstructuredObject{ uo.FromMap(map[string]interface{}{ @@ -399,7 +393,7 @@ func TestSeal_MultipleSecrets(t *testing.T) { }, }), }, true) - addSecretDeployment(p, "secret-deployment2", secret2, resourceOpts{name: "secret2", namespace: namespace}) + addSecretDeployment(p, "secret-deployment2", secret2, resourceOpts{name: "secret2", namespace: p.testSlug()}) p.KluctlMust("seal", "-t", "test-target") @@ -409,10 +403,10 @@ func TestSeal_MultipleSecrets(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", }) - assertSealedSecret(t, k, namespace, "secret2", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret2", certServer1.certHash, map[string]string{ "s2": "v2", }) } @@ -421,7 +415,6 @@ func TestSeal_MultipleSecretsInOneFile(t *testing.T) { t.Parallel() k := defaultCluster1 - namespace := "seal-multiple-secrets2" secret1 := map[string]string{ "s1": "{{ secrets.s1 }}", @@ -429,9 +422,8 @@ func TestSeal_MultipleSecretsInOneFile(t *testing.T) { secret2 := map[string]string{ "s2": "{{ secrets.s2 }}", } - secret2Text, _ := yaml.WriteYamlString(createSecretObject(secret2, resourceOpts{name: "secret2", namespace: namespace})) - p := prepareSealTest(t, k, namespace, + p := prepareSealTest(t, k, secret1, []*uo.UnstructuredObject{ uo.FromMap(map[string]interface{}{ @@ -442,6 +434,8 @@ func TestSeal_MultipleSecretsInOneFile(t *testing.T) { }), }, true) + secret2Text, _ := yaml.WriteYamlString(createSecretObject(secret2, resourceOpts{name: "secret2", namespace: p.testSlug()})) + p.gitServer.UpdateFile(p.getKluctlProjectRepo(), "secret-deployment/secret-secret.yml.sealme", func(f string) (string, error) { f += "---\n" f += secret2Text @@ -455,10 +449,10 @@ func TestSeal_MultipleSecretsInOneFile(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", }) - assertSealedSecret(t, k, namespace, "secret2", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret2", certServer1.certHash, map[string]string{ "s2": "v2", }) } @@ -467,9 +461,8 @@ func TestSeal_File(t *testing.T) { t.Parallel() k := defaultCluster1 - namespace := "seal-file" - p := prepareSealTest(t, k, namespace, + p := prepareSealTest(t, k, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -497,7 +490,7 @@ func TestSeal_File(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -507,7 +500,6 @@ func TestSeal_Vault(t *testing.T) { t.Parallel() k := defaultCluster1 - namespace := "seal-vault" server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { if request.URL.Path != "/v1/secret/data/secret" { @@ -531,7 +523,7 @@ func TestSeal_Vault(t *testing.T) { vaultUrl := server.URL - p := prepareSealTest(t, k, namespace, + p := prepareSealTest(t, k, map[string]string{ "s1": "{{ secrets.s1 }}", "s2": "{{ secrets.s2 }}", @@ -553,7 +545,7 @@ func TestSeal_Vault(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, namespace, "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) diff --git a/e2e/sops_test.go b/e2e/sops_test.go index 59e352b82..ee9af770a 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -14,9 +14,9 @@ func TestSopsVars(t *testing.T) { k := defaultCluster1 p := &testProject{} - p.init(t, k, "sops-vars") + p.init(t, k) - createNamespace(t, k, p.projectName) + createNamespace(t, k, p.testSlug()) p.updateTarget("test", nil) @@ -24,7 +24,7 @@ func TestSopsVars(t *testing.T) { "v1": "{{ test1.test2 }}", }, resourceOpts{ name: "cm", - namespace: p.projectName, + namespace: p.testSlug(), }) p.updateDeploymentYaml("", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField([]map[string]any{ @@ -42,7 +42,7 @@ func TestSopsVars(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test") - cm := assertConfigMapExists(t, k, p.projectName, "cm") + cm := assertConfigMapExists(t, k, p.testSlug(), "cm") assertNestedFieldEquals(t, cm, map[string]any{ "v1": "42", }, "data") @@ -55,13 +55,13 @@ func TestSopsResources(t *testing.T) { k := defaultCluster1 p := &testProject{} - p.init(t, k, "sops-resources") + p.init(t, k) - createNamespace(t, k, p.projectName) + createNamespace(t, k, p.testSlug()) p.updateTarget("test", nil) p.updateDeploymentYaml("", func(o *uo.UnstructuredObject) error { - _ = o.SetNestedField(p.projectName, "overrideNamespace") + _ = o.SetNestedField(p.testSlug(), "overrideNamespace") return nil }) @@ -76,7 +76,7 @@ func TestSopsResources(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test") - cm := assertConfigMapExists(t, k, p.projectName, "encrypted-cm") + cm := assertConfigMapExists(t, k, p.testSlug(), "encrypted-cm") assertNestedFieldEquals(t, cm, map[string]any{ "a": "b", }, "data") From 8aeab4110760b3e4dc0ef8069770b997f8894d4d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 11 Nov 2022 16:31:04 +0100 Subject: [PATCH 0530/2268] docs: Add docs about plain Kustomize deployments --- docs/get-started.md | 16 +++++++++++++++- docs/reference/deployments/README.md | 7 +++++++ docs/reference/kluctl-project/README.md | 5 +++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/get-started.md b/docs/get-started.md index 5393963e9..c92642802 100644 --- a/docs/get-started.md +++ b/docs/get-started.md @@ -50,8 +50,22 @@ for a given cluster. The `kluctl` command-line interface (CLI) is required to perform deployments. Read the [installation instructions](./installation.md) to figure out how to install it. -## Clone the kluctl examples +## Use Kluctl with a plain Kustomize deployment +The simplest way to test out Kluctl is to use an existing Kustomize deployment and just test out the CLI. For example, +try it with the [podtato-head project](https://github.com/podtato-head/podtato-head): + +```shell +$ git clone https://github.com/podtato-head/podtato-head.git +$ cd podtato-head/delivery/kustomize/base +$ kluctl deploy +``` + +Then try to modify something inside the Kustomize deployment and retry the `kluctl deploy` call. + +## Try out the Kluctl examples + +For more advanced examples, check out the Kluctl example projects. Clone the example project found at https://github.com/kluctl/kluctl-examples ```sh diff --git a/docs/reference/deployments/README.md b/docs/reference/deployments/README.md index b54534e4f..19145cca4 100644 --- a/docs/reference/deployments/README.md +++ b/docs/reference/deployments/README.md @@ -77,3 +77,10 @@ Some visualized files/directories have links attached, follow them to get more i Deployments are done in parallel, meaning that there are usually no order guarantees. The only way to somehow control order, is by placing [barriers](./deployment-yml.md#barriers) between kustomize deployments. You should however not overuse barriers, as they negatively impact the speed of kluctl. + +## Plain Kustomize + +It's also possible to use Kluctl on plain Kustomize deployments. Simply run `kluctl deploy` from inside the +folder of your `kustomization.yaml`. If you also don't have a `.kluctl.yaml`, you can also work without targets. + +Please note that pruning and deletion is not supported in this mode. \ No newline at end of file diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index f442ee3fb..ba14d1b65 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -42,3 +42,8 @@ targets: Please check the following sub-sections of this section to see which fields are allowed at the root level of `.kluctl.yaml`. 1. [targets](./targets) + +## Using Kluctl without .kluctl.yaml + +It's possible to use Kluctl without any `.kluctl.yaml`. In that case, all commands must be used without specifying the +target. From 551d76ad66fb0846f7b0fec6ef6535a2a4b0a5fa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 12 Nov 2022 22:01:46 +0100 Subject: [PATCH 0531/2268] docs: Mention SOPS in variable-sources.md --- docs/reference/templating/variable-sources.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index ad6ad30c2..2f4a4137f 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -54,6 +54,9 @@ vars: After which all included deployments and sub-deployments can use the jinja2 variables from `vars1.yaml`. +Kluctl also supports variable files encrypted with [SOPS](https://github.com/mozilla/sops). See the +[sops integration](../deployments/sops.md) integration for more details. + ### values An inline definition of variables. Example: @@ -77,6 +80,9 @@ vars: path: path/to/vars.yaml ``` +Kluctl also supports variable files encrypted with [SOPS](https://github.com/mozilla/sops). See the +[sops integration](../deployments/sops.md) integration for more details. + ### clusterConfigMap Loads a configmap from the target's cluster and loads the specified key's value as a yaml file into the jinja2 variables context. From f33d4185f5c445253709f405c7bc5e7ce3b085a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 22:10:08 +0000 Subject: [PATCH 0532/2268] chore(deps): Bump goreleaser/goreleaser-action from 2 to 3 Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 2 to 3. - [Release notes](https://github.com/goreleaser/goreleaser-action/releases) - [Commits](https://github.com/goreleaser/goreleaser-action/compare/v2...v3) --- updated-dependencies: - dependency-name: goreleaser/goreleaser-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b5e3dd1f8..dd4ea8163 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: restore-keys: | ${{ runner.os }}-goreleaser- - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2 + uses: goreleaser/goreleaser-action@v3 with: distribution: goreleaser version: latest From 4ec6a5e533c831b9c7ed91c13d445a6cf5d9c822 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 22:12:48 +0000 Subject: [PATCH 0533/2268] chore(deps): Bump k8s.io/client-go from 0.25.3 to 0.25.4 Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.25.3 to 0.25.4. - [Release notes](https://github.com/kubernetes/client-go/releases) - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.25.3...v0.25.4) --- updated-dependencies: - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index cd6924cbe..114f968a3 100644 --- a/go.mod +++ b/go.mod @@ -47,10 +47,10 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.2 - k8s.io/api v0.25.3 + k8s.io/api v0.25.4 k8s.io/apiextensions-apiserver v0.25.3 - k8s.io/apimachinery v0.25.3 - k8s.io/client-go v0.25.3 + k8s.io/apimachinery v0.25.4 + k8s.io/client-go v0.25.4 k8s.io/klog/v2 v2.80.1 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/kustomize/kyaml v0.13.9 @@ -59,6 +59,7 @@ require ( require ( github.com/go-logr/logr v1.2.3 + github.com/huandu/xstrings v1.3.2 go.mozilla.org/sops/v3 v3.7.3 sigs.k8s.io/controller-runtime v0.13.0 ) @@ -156,7 +157,6 @@ require ( github.com/hashicorp/vault/sdk v0.6.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect - github.com/huandu/xstrings v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect diff --git a/go.sum b/go.sum index 83a6e4635..3d9ef5775 100644 --- a/go.sum +++ b/go.sum @@ -1307,18 +1307,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= -k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= +k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs= +k8s.io/api v0.25.4/go.mod h1:IG2+RzyPQLllQxnhzD8KQNEu4c4YvyDTpSMztf4A0OQ= k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= -k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= -k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= +k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= k8s.io/apiserver v0.25.3 h1:m7+xGuG5+KYAnEsqaFtDyWMkmMMEOFYlu+NlWv5qSBI= k8s.io/apiserver v0.25.3/go.mod h1:9bT47iM2fzRuhICJpM/RcQR9sqDDfZ7Yw60h0p3JW08= k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= k8s.io/cli-runtime v0.25.3/go.mod h1:InHHsjkyW5hQsILJGpGjeruiDZT/R0OkROQgD6GzxO4= -k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0= -k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA= +k8s.io/client-go v0.25.4 h1:3RNRDffAkNU56M/a7gUfXaEzdhZlYhoW8dgViGy5fn8= +k8s.io/client-go v0.25.4/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw= k8s.io/component-base v0.25.3 h1:UrsxciGdrCY03ULT1h/S/gXFCOPnLhUVwSyx+hM/zq4= k8s.io/component-base v0.25.3/go.mod h1:WYoS8L+IlTZgU7rhAl5Ctpw0WdMxDfCC5dkxcEFa/TI= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= From b07d6489a20d2086ce682299a2e9b01a5746af47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 22:13:03 +0000 Subject: [PATCH 0534/2268] chore(deps): Bump sigs.k8s.io/controller-runtime from 0.13.0 to 0.13.1 Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.13.0 to 0.13.1. - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/master/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.13.0...v0.13.1) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index cd6924cbe..fd9a08fd4 100644 --- a/go.mod +++ b/go.mod @@ -59,8 +59,9 @@ require ( require ( github.com/go-logr/logr v1.2.3 + github.com/huandu/xstrings v1.3.2 go.mozilla.org/sops/v3 v3.7.3 - sigs.k8s.io/controller-runtime v0.13.0 + sigs.k8s.io/controller-runtime v0.13.1 ) require ( @@ -156,7 +157,6 @@ require ( github.com/hashicorp/vault/sdk v0.6.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect - github.com/huandu/xstrings v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect diff --git a/go.sum b/go.sum index 83a6e4635..bb045f8fb 100644 --- a/go.sum +++ b/go.sum @@ -1334,8 +1334,8 @@ oras.land/oras-go v1.2.1/go.mod h1:3N11Z5E3c4ZzOjroCl1RtAdB4yNAYl7A27j2SVf913A= 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/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= -sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= +sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= +sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= From 7b3a7f341e86cab645fac9b76e2c7f777e5ecfa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 22:13:19 +0000 Subject: [PATCH 0535/2268] chore(deps): Bump golang.org/x/net from 0.1.0 to 0.2.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.1.0 to 0.2.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.1.0...v0.2.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index cd6924cbe..cc815544c 100644 --- a/go.mod +++ b/go.mod @@ -39,10 +39,10 @@ require ( github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 golang.org/x/crypto v0.1.0 - golang.org/x/net v0.1.0 + golang.org/x/net v0.2.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.2.0 - golang.org/x/term v0.1.0 + golang.org/x/term v0.2.0 golang.org/x/text v0.4.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 @@ -59,6 +59,7 @@ require ( require ( github.com/go-logr/logr v1.2.3 + github.com/huandu/xstrings v1.3.2 go.mozilla.org/sops/v3 v3.7.3 sigs.k8s.io/controller-runtime v0.13.0 ) @@ -156,7 +157,6 @@ require ( github.com/hashicorp/vault/sdk v0.6.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect - github.com/huandu/xstrings v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect diff --git a/go.sum b/go.sum index 83a6e4635..cac032fe8 100644 --- a/go.sum +++ b/go.sum @@ -954,8 +954,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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= @@ -1062,8 +1062,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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= From 04d450f6a13fa32a5c9c8ef93e988c7b04b0f7b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 22:13:59 +0000 Subject: [PATCH 0536/2268] chore(deps): Bump github.com/hashicorp/vault/api from 1.8.1 to 1.8.2 Bumps [github.com/hashicorp/vault/api](https://github.com/hashicorp/vault) from 1.8.1 to 1.8.2. - [Release notes](https://github.com/hashicorp/vault/releases) - [Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/vault/compare/v1.8.1...v1.8.2) --- updated-dependencies: - dependency-name: github.com/hashicorp/vault/api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index cd6924cbe..7553e9e47 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 github.com/google/go-containerregistry v0.12.1 - github.com/hashicorp/vault/api v1.8.1 + github.com/hashicorp/vault/api v1.8.2 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 @@ -59,6 +59,7 @@ require ( require ( github.com/go-logr/logr v1.2.3 + github.com/huandu/xstrings v1.3.2 go.mozilla.org/sops/v3 v3.7.3 sigs.k8s.io/controller-runtime v0.13.0 ) @@ -156,7 +157,6 @@ require ( github.com/hashicorp/vault/sdk v0.6.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect - github.com/huandu/xstrings v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect diff --git a/go.sum b/go.sum index 83a6e4635..473fff58b 100644 --- a/go.sum +++ b/go.sum @@ -473,8 +473,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.8.1 h1:bMieWIe6dAlqAAPReZO/8zYtXaWUg/21umwqGZpEjCI= -github.com/hashicorp/vault/api v1.8.1/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= +github.com/hashicorp/vault/api v1.8.2 h1:C7OL9YtOtwQbTKI9ogB0A1wffRbCN+rH/LLCHO3d8HM= +github.com/hashicorp/vault/api v1.8.2/go.mod h1:ML8aYzBIhY5m1MD1B2Q0JV89cC85YVH4t5kBaZiyVaE= github.com/hashicorp/vault/sdk v0.6.0 h1:6Z+In5DXHiUfZvIZdMx7e2loL1PPyDjA4bVh9ZTIAhs= github.com/hashicorp/vault/sdk v0.6.0/go.mod h1:+DRpzoXIdMvKc88R4qxr+edwy/RvH5QK8itmxLiDHLc= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= From 3758a64805fbdbbd4d382df7b3604103bba75541 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 14 Nov 2022 14:37:41 +0100 Subject: [PATCH 0537/2268] feat: Remove --local-deployment from helm-pull/helm-update --- cmd/kluctl/commands/cmd_helm_pull.go | 17 +++++++++++------ cmd/kluctl/commands/cmd_helm_update.go | 19 ++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 90d8c9fc8..74adaf8cf 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -4,15 +4,15 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" + git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/status" "io/fs" + "os" "path/filepath" ) type helmPullCmd struct { args.HelmCredentials - - LocalDeployment string `group:"project" help:"Local deployment directory. Defaults to current directory"` } func (cmd *helmPullCmd) Help() string { @@ -22,12 +22,17 @@ pulling is only needed when really required (e.g. when the chart version changes } func (cmd *helmPullCmd) Run() error { - rootPath := "." - if cmd.LocalDeployment != "" { - rootPath = cmd.LocalDeployment + cwd, err := os.Getwd() + if err != nil { + return err + } + + gitRootPath, err := git2.DetectGitRepositoryRoot(cwd) + if err != nil { + return err } - err := filepath.WalkDir(rootPath, func(p string, d fs.DirEntry, err error) error { + err = filepath.WalkDir(cwd, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { s := status.Start(cliCtx, "Pulling for %s", p) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 55c30b8e8..9210c9e38 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -8,15 +8,15 @@ import ( git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/status" "io/fs" + "os" "path/filepath" ) type helmUpdateCmd struct { args.HelmCredentials - LocalDeployment string `group:"project" help:"Local deployment directory. Defaults to current directory"` - Upgrade bool `group:"misc" help:"Write new versions into helm-chart.yaml and perform helm-pull afterwards"` - Commit bool `group:"misc" help:"Create a git commit for every updated chart"` + Upgrade bool `group:"misc" help:"Write new versions into helm-chart.yaml and perform helm-pull afterwards"` + Commit bool `group:"misc" help:"Create a git commit for every updated chart"` } func (cmd *helmUpdateCmd) Help() string { @@ -24,16 +24,17 @@ func (cmd *helmUpdateCmd) Help() string { } func (cmd *helmUpdateCmd) Run() error { - rootPath := "." - if cmd.LocalDeployment != "" { - rootPath = cmd.LocalDeployment + cwd, err := os.Getwd() + if err != nil { + return err } - gitRootPath, err := git2.DetectGitRepositoryRoot(rootPath) + + gitRootPath, err := git2.DetectGitRepositoryRoot(cwd) if err != nil { return err } - err = filepath.WalkDir(rootPath, func(p string, d fs.DirEntry, err error) error { + err = filepath.WalkDir(cwd, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { statusPrefix := filepath.Base(filepath.Dir(p)) @@ -134,7 +135,7 @@ func (cmd *helmUpdateCmd) Run() error { return err } for p := range gitFiles { - absPath, err := filepath.Abs(filepath.Join(rootPath, p)) + absPath, err := filepath.Abs(filepath.Join(cwd, p)) if err != nil { return err } From 87978f3772aa7c0810b61acc77a88c2f8dd6936a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 14 Nov 2022 14:38:13 +0100 Subject: [PATCH 0538/2268] feat: Better status reporting for helm-pull/helm-update --- cmd/kluctl/commands/cmd_helm_pull.go | 21 +++++++++++++++++---- cmd/kluctl/commands/cmd_helm_update.go | 6 +++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 74adaf8cf..dc6f251de 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -35,16 +35,29 @@ func (cmd *helmPullCmd) Run() error { err = filepath.WalkDir(cwd, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { - s := status.Start(cliCtx, "Pulling for %s", p) + statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) + if err != nil { + return err + } + + s := status.Start(cliCtx, "%s: Pulling Chart %s", statusPrefix) chart, err := deployment.NewHelmChart(p) if err != nil { - s.FailedWithMessage(err.Error()) + s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return err } + chartName, err := chart.GetChartName() + if err != nil { + s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) + return err + } + + s.Update("%s: Pulling Chart %s with version %s", statusPrefix, chartName, *chart.Config.ChartVersion) + creds := cmd.HelmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) if chart.Config.CredentialsId != nil && creds == nil { - err := fmt.Errorf("no credentials provided for %s", p) + err := fmt.Errorf("%s: no credentials provided", statusPrefix) s.FailedWithMessage(err.Error()) return err } @@ -52,7 +65,7 @@ func (cmd *helmPullCmd) Run() error { err = chart.Pull(cliCtx) if err != nil { - s.FailedWithMessage(err.Error()) + s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return err } s.Success() diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 9210c9e38..0f629a099 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -37,7 +37,11 @@ func (cmd *helmUpdateCmd) Run() error { err = filepath.WalkDir(cwd, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { - statusPrefix := filepath.Base(filepath.Dir(p)) + statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) + if err != nil { + return err + } + s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) defer s.Failed() From b7ef0e513bce388523e2bb2b0f120559487fb92c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 14 Nov 2022 14:48:30 +0100 Subject: [PATCH 0539/2268] feat: Paralellize helm-pull --- cmd/kluctl/commands/cmd_helm_pull.go | 99 +++++++++++++++++----------- pkg/utils/limited_routine.go | 36 ++++++++++ 2 files changed, 98 insertions(+), 37 deletions(-) create mode 100644 pkg/utils/limited_routine.go diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index dc6f251de..87d7cb732 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -2,13 +2,17 @@ package commands import ( "fmt" + "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/utils" + "golang.org/x/sync/semaphore" "io/fs" "os" "path/filepath" + "sync" ) type helmPullCmd struct { @@ -32,50 +36,71 @@ func (cmd *helmPullCmd) Run() error { return err } + var errs *multierror.Error + var wg sync.WaitGroup + var mutex sync.Mutex + sem := semaphore.NewWeighted(8) + err = filepath.WalkDir(cwd, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) - if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { - statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) - if err != nil { - return err - } - - s := status.Start(cliCtx, "%s: Pulling Chart %s", statusPrefix) - chart, err := deployment.NewHelmChart(p) - if err != nil { - s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) - return err - } - - chartName, err := chart.GetChartName() - if err != nil { - s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) - return err - } - - s.Update("%s: Pulling Chart %s with version %s", statusPrefix, chartName, *chart.Config.ChartVersion) - - creds := cmd.HelmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) - if chart.Config.CredentialsId != nil && creds == nil { - err := fmt.Errorf("%s: no credentials provided", statusPrefix) - s.FailedWithMessage(err.Error()) - return err - } - chart.SetCredentials(creds) - - err = chart.Pull(cliCtx) - if err != nil { - s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) - return err - } - s.Success() + if fname != "helm-chart.yml" && fname != "helm-chart.yaml" { + return nil } + + wg.Add(1) + utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, func() error { + defer wg.Done() + return doPull(gitRootPath, p, cmd.HelmCredentials) + }) + return nil }) - + wg.Wait() if err != nil { + errs = multierror.Append(errs, err) + } + + if errs.ErrorOrNil() != nil { return fmt.Errorf("command failed") } - return err + return nil +} + +func doPull(gitRootPath string, p string, helmCredentials args.HelmCredentials) error { + statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) + if err != nil { + return err + } + + s := status.Start(cliCtx, "%s: Pulling Chart %s", statusPrefix) + doError := func(err error) error { + s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) + return err + } + + chart, err := deployment.NewHelmChart(p) + if err != nil { + return doError(err) + } + + chartName, err := chart.GetChartName() + if err != nil { + return doError(err) + } + + s.Update("%s: Pulling Chart %s with version %s", statusPrefix, chartName, *chart.Config.ChartVersion) + + creds := helmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) + if chart.Config.CredentialsId != nil && creds == nil { + return doError(fmt.Errorf("no credentials provided")) + } + chart.SetCredentials(creds) + + err = chart.Pull(cliCtx) + if err != nil { + return doError(err) + } + s.Success() + return nil } diff --git a/pkg/utils/limited_routine.go b/pkg/utils/limited_routine.go new file mode 100644 index 000000000..7653bfc26 --- /dev/null +++ b/pkg/utils/limited_routine.go @@ -0,0 +1,36 @@ +package utils + +import ( + "context" + "github.com/hashicorp/go-multierror" + "golang.org/x/sync/semaphore" + "sync" +) + +func GoLimited(ctx context.Context, sem *semaphore.Weighted, fn func(), errCb func(err error)) { + go func() { + err := sem.Acquire(ctx, 1) + if err != nil { + if errCb != nil { + errCb(err) + } + return + } + defer sem.Release(1) + fn() + }() +} + +func GoLimitedMultiError(ctx context.Context, sem *semaphore.Weighted, merr **multierror.Error, mutex *sync.Mutex, fn func() error) { + errCb := func(err error) { + mutex.Lock() + defer mutex.Unlock() + *merr = multierror.Append(*merr, err) + } + GoLimited(ctx, sem, func() { + err := fn() + if err != nil { + errCb(err) + } + }, errCb) +} From c2ee8f1931849752ef11a76f6c52c33c840fd91f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 14 Nov 2022 14:49:37 +0100 Subject: [PATCH 0540/2268] refactor: Eaerly bailout --- cmd/kluctl/commands/cmd_helm_update.go | 177 +++++++++++++------------ 1 file changed, 89 insertions(+), 88 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 0f629a099..42e71ac6d 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -36,129 +36,130 @@ func (cmd *helmUpdateCmd) Run() error { err = filepath.WalkDir(cwd, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) - if fname == "helm-chart.yml" || fname == "helm-chart.yaml" { - statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) + if fname != "helm-chart.yml" && fname != "helm-chart.yaml" { + return nil + } + statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) + if err != nil { + return err + } + + s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) + defer s.Failed() + + chart, err := deployment.NewHelmChart(p) + if err != nil { + s.Update("%s: Error while loading helm-chart.yaml: %v", statusPrefix, err) + return err + } + + creds := cmd.HelmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) + if chart.Config.CredentialsId != nil && creds == nil { + err := fmt.Errorf("%s: No credentials provided", statusPrefix) + s.FailedWithMessage(err.Error()) + return err + } + chart.SetCredentials(creds) + + newVersion, updated, err := chart.CheckUpdate() + if err != nil { + return err + } + if !updated { + s.Update("%s: Version %s is already up-to-date.", statusPrefix, *chart.Config.ChartVersion) + s.Success() + return nil + } + msg := fmt.Sprintf("%s: Chart has new version %s available. Old version is %s.", statusPrefix, newVersion, *chart.Config.ChartVersion) + if chart.Config.SkipUpdate { + msg += " skipUpdate is set to true." + } + s.Update(msg) + + if !cmd.Upgrade { + s.Success() + } else { + if chart.Config.SkipUpdate { + s.Update("%s: NOT upgrading chart as skipUpdate was set to true", statusPrefix) + s.Success() + return nil + } + + oldVersion := *chart.Config.ChartVersion + chart.Config.ChartVersion = &newVersion + err = chart.Save() if err != nil { return err } - s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) - defer s.Failed() + chartsDir := filepath.Join(filepath.Dir(p), "charts") - chart, err := deployment.NewHelmChart(p) + // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later + // know what got deleted + gitFiles := make(map[string]bool) + gitFiles[p] = true + err = filepath.WalkDir(chartsDir, func(p string, d fs.DirEntry, err error) error { + if !d.IsDir() { + gitFiles[p] = true + } + return nil + }) if err != nil { - s.Update("%s: Error while loading helm-chart.yaml: %v", statusPrefix, err) return err } - creds := cmd.HelmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) - if chart.Config.CredentialsId != nil && creds == nil { - err := fmt.Errorf("%s: No credentials provided", statusPrefix) - s.FailedWithMessage(err.Error()) - return err - } - chart.SetCredentials(creds) + s.Update("%s: Pulling new version", statusPrefix) + defer s.Failed() - newVersion, updated, err := chart.CheckUpdate() + err = chart.Pull(cliCtx) if err != nil { return err } - if !updated { - s.Update("%s: Version %s is already up-to-date.", statusPrefix, *chart.Config.ChartVersion) - s.Success() - return nil - } - msg := fmt.Sprintf("%s: Chart has new version %s available. Old version is %s.", statusPrefix, newVersion, *chart.Config.ChartVersion) - if chart.Config.SkipUpdate { - msg += " skipUpdate is set to true." - } - s.Update(msg) - if !cmd.Upgrade { - s.Success() - } else { - if chart.Config.SkipUpdate { - s.Update("%s: NOT upgrading chart as skipUpdate was set to true", statusPrefix) - s.Success() - return nil - } - - oldVersion := *chart.Config.ChartVersion - chart.Config.ChartVersion = &newVersion - err = chart.Save() - if err != nil { - return err + // and now list all files again to catch all new files + err = filepath.WalkDir(chartsDir, func(p string, d fs.DirEntry, err error) error { + if !d.IsDir() { + gitFiles[p] = true } + return nil + }) + if err != nil { + return err + } - chartsDir := filepath.Join(filepath.Dir(p), "charts") - - // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later - // know what got deleted - gitFiles := make(map[string]bool) - gitFiles[p] = true - err = filepath.WalkDir(chartsDir, func(p string, d fs.DirEntry, err error) error { - if !d.IsDir() { - gitFiles[p] = true - } - return nil - }) - if err != nil { - return err - } + if cmd.Commit { + commitMsg := fmt.Sprintf("Updated helm chart %s from %s to %s", filepath.Dir(p), oldVersion, newVersion) - s.Update("%s: Pulling new version", statusPrefix) - defer s.Failed() + s.Update(fmt.Sprintf("%s: Updating chart from %s to %s", statusPrefix, oldVersion, newVersion)) - err = chart.Pull(cliCtx) + r, err := git.PlainOpen(gitRootPath) if err != nil { return err } - - // and now list all files again to catch all new files - err = filepath.WalkDir(chartsDir, func(p string, d fs.DirEntry, err error) error { - if !d.IsDir() { - gitFiles[p] = true - } - return nil - }) + wt, err := r.Worktree() if err != nil { return err } - - if cmd.Commit { - commitMsg := fmt.Sprintf("Updated helm chart %s from %s to %s", filepath.Dir(p), oldVersion, newVersion) - - s.Update(fmt.Sprintf("%s: Updating chart from %s to %s", statusPrefix, oldVersion, newVersion)) - - r, err := git.PlainOpen(gitRootPath) + for p := range gitFiles { + absPath, err := filepath.Abs(filepath.Join(cwd, p)) if err != nil { return err } - wt, err := r.Worktree() + relToGit, err := filepath.Rel(gitRootPath, absPath) if err != nil { return err } - for p := range gitFiles { - absPath, err := filepath.Abs(filepath.Join(cwd, p)) - if err != nil { - return err - } - relToGit, err := filepath.Rel(gitRootPath, absPath) - if err != nil { - return err - } - _, err = wt.Add(relToGit) - if err != nil { - return err - } - } - _, err = wt.Commit(commitMsg, &git.CommitOptions{}) + _, err = wt.Add(relToGit) if err != nil { return err } } - s.Success() + _, err = wt.Commit(commitMsg, &git.CommitOptions{}) + if err != nil { + return err + } } + s.Success() } return nil }) From 9e7bcf7c4869a29bde28f399fdffa601c5177301 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 14 Nov 2022 15:34:49 +0100 Subject: [PATCH 0541/2268] feat: Paralellize helm-update --- cmd/kluctl/commands/cmd_helm_pull.go | 13 +- cmd/kluctl/commands/cmd_helm_update.go | 296 ++++++++++++++++--------- pkg/deployment/helm_chart.go | 44 ++-- 3 files changed, 221 insertions(+), 132 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 87d7cb732..e4c510993 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -50,7 +50,14 @@ func (cmd *helmPullCmd) Run() error { wg.Add(1) utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, func() error { defer wg.Done() - return doPull(gitRootPath, p, cmd.HelmCredentials) + s := status.Start(cliCtx, "%s: Pulling Chart") + defer s.Failed() + err := doPull(gitRootPath, p, cmd.HelmCredentials, s) + if err != nil { + return err + } + s.Success() + return nil }) return nil @@ -67,13 +74,12 @@ func (cmd *helmPullCmd) Run() error { return nil } -func doPull(gitRootPath string, p string, helmCredentials args.HelmCredentials) error { +func doPull(gitRootPath string, p string, helmCredentials args.HelmCredentials, s *status.StatusContext) error { statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) if err != nil { return err } - s := status.Start(cliCtx, "%s: Pulling Chart %s", statusPrefix) doError := func(err error) error { s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return err @@ -101,6 +107,5 @@ func doPull(gitRootPath string, p string, helmCredentials args.HelmCredentials) if err != nil { return doError(err) } - s.Success() return nil } diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 42e71ac6d..d35bdb496 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -3,13 +3,17 @@ package commands import ( "fmt" "github.com/go-git/go-git/v5" + "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/utils" + "golang.org/x/sync/semaphore" "io/fs" "os" "path/filepath" + "sync" ) type helmUpdateCmd struct { @@ -34,134 +38,214 @@ func (cmd *helmUpdateCmd) Run() error { return err } + var errs *multierror.Error + var wg sync.WaitGroup + var mutex sync.Mutex + sem := semaphore.NewWeighted(8) + + type updatedChart struct { + chart *deployment.HelmChart + newVersion string + oldVersion string + pullSuccess bool + } + var updatedCharts []*updatedChart + err = filepath.WalkDir(cwd, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname != "helm-chart.yml" && fname != "helm-chart.yaml" { return nil } - statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) - if err != nil { - return err - } - s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) - defer s.Failed() + wg.Add(1) + utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, func() error { + defer wg.Done() - chart, err := deployment.NewHelmChart(p) - if err != nil { - s.Update("%s: Error while loading helm-chart.yaml: %v", statusPrefix, err) - return err - } + chart, newVersion, updated, err := cmd.doCheckUpdate(gitRootPath, p) + if err != nil { + return err + } - creds := cmd.HelmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) - if chart.Config.CredentialsId != nil && creds == nil { - err := fmt.Errorf("%s: No credentials provided", statusPrefix) - s.FailedWithMessage(err.Error()) - return err - } - chart.SetCredentials(creds) + mutex.Lock() + defer mutex.Unlock() - newVersion, updated, err := chart.CheckUpdate() - if err != nil { - return err - } - if !updated { - s.Update("%s: Version %s is already up-to-date.", statusPrefix, *chart.Config.ChartVersion) - s.Success() + if updated { + updatedCharts = append(updatedCharts, &updatedChart{ + chart: chart, + newVersion: newVersion, + oldVersion: *chart.Config.ChartVersion, + }) + } return nil - } + }) + return nil + }) + wg.Wait() + if err != nil { + errs = multierror.Append(errs, err) + return errs.ErrorOrNil() + } + + if !cmd.Upgrade { + return errs.ErrorOrNil() + } + + for _, uc := range updatedCharts { + uc := uc + + wg.Add(1) + utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, func() error { + defer wg.Done() + + err := cmd.pullAndCommitChart(gitRootPath, uc.chart, uc.oldVersion, uc.newVersion, &mutex) + if err != nil { + return err + } + return nil + }) + } + wg.Wait() + + if !cmd.Commit { + return errs.ErrorOrNil() + } + + return errs.ErrorOrNil() +} + +func (cmd *helmUpdateCmd) doCheckUpdate(gitRootPath string, p string) (*deployment.HelmChart, string, bool, error) { + statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) + if err != nil { + return nil, "", false, err + } + + s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) + doError := func(err error) (*deployment.HelmChart, string, bool, error) { + s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) + return nil, "", false, err + } + + chart, err := deployment.NewHelmChart(p) + if err != nil { + return doError(err) + } + + creds := cmd.HelmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) + if chart.Config.CredentialsId != nil && creds == nil { + return doError(fmt.Errorf("no credentials provided")) + } + chart.SetCredentials(creds) + + newVersion, updated, err := chart.CheckUpdate() + if err != nil { + return doError(err) + } + if !updated { + s.Update("%s: Version %s is already up-to-date.", statusPrefix, *chart.Config.ChartVersion) + } else { msg := fmt.Sprintf("%s: Chart has new version %s available. Old version is %s.", statusPrefix, newVersion, *chart.Config.ChartVersion) if chart.Config.SkipUpdate { msg += " skipUpdate is set to true." } s.Update(msg) + } + s.Success() - if !cmd.Upgrade { - s.Success() - } else { - if chart.Config.SkipUpdate { - s.Update("%s: NOT upgrading chart as skipUpdate was set to true", statusPrefix) - s.Success() - return nil - } - - oldVersion := *chart.Config.ChartVersion - chart.Config.ChartVersion = &newVersion - err = chart.Save() - if err != nil { - return err - } + return chart, newVersion, updated, nil +} - chartsDir := filepath.Join(filepath.Dir(p), "charts") - - // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later - // know what got deleted - gitFiles := make(map[string]bool) - gitFiles[p] = true - err = filepath.WalkDir(chartsDir, func(p string, d fs.DirEntry, err error) error { - if !d.IsDir() { - gitFiles[p] = true - } - return nil - }) - if err != nil { - return err - } +func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployment.HelmChart, oldVersion string, newVersion string, mutex *sync.Mutex) error { + statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(chart.ConfigFile)) + if err != nil { + return err + } - s.Update("%s: Pulling new version", statusPrefix) - defer s.Failed() + s := status.Start(cliCtx, "%s: Pulling Chart", statusPrefix) + defer s.Failed() - err = chart.Pull(cliCtx) - if err != nil { - return err - } + chart.Config.ChartVersion = &newVersion + err = chart.Save() + if err != nil { + return err + } - // and now list all files again to catch all new files - err = filepath.WalkDir(chartsDir, func(p string, d fs.DirEntry, err error) error { - if !d.IsDir() { - gitFiles[p] = true - } - return nil - }) - if err != nil { - return err - } + chartsDir, err := chart.GetChartDir() + if err != nil { + return err + } - if cmd.Commit { - commitMsg := fmt.Sprintf("Updated helm chart %s from %s to %s", filepath.Dir(p), oldVersion, newVersion) - - s.Update(fmt.Sprintf("%s: Updating chart from %s to %s", statusPrefix, oldVersion, newVersion)) - - r, err := git.PlainOpen(gitRootPath) - if err != nil { - return err - } - wt, err := r.Worktree() - if err != nil { - return err - } - for p := range gitFiles { - absPath, err := filepath.Abs(filepath.Join(cwd, p)) - if err != nil { - return err - } - relToGit, err := filepath.Rel(gitRootPath, absPath) - if err != nil { - return err - } - _, err = wt.Add(relToGit) - if err != nil { - return err - } - } - _, err = wt.Commit(commitMsg, &git.CommitOptions{}) - if err != nil { - return err - } - } - s.Success() + // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later + // know what got deleted + oldFiles := map[string]bool{} + err = filepath.WalkDir(chartsDir, func(p string, d fs.DirEntry, err error) error { + if d.IsDir() { + return nil + } + relToGit, err := filepath.Rel(gitRootPath, p) + if err != nil { + return err } + oldFiles[relToGit] = true return nil }) - return err + if err != nil { + return err + } + + err = doPull(gitRootPath, chart.ConfigFile, cmd.HelmCredentials, s) + if err != nil { + return err + } + + var toAdd []string + relToGit, err := filepath.Rel(gitRootPath, chart.ConfigFile) + if err != nil { + return err + } + toAdd = append(toAdd, relToGit) + + relToGit, err = filepath.Rel(gitRootPath, chartsDir) + if err != nil { + return err + } + toAdd = append(toAdd, relToGit) + + // figure out what got deleted + for p, _ := range oldFiles { + if !utils.IsFile(filepath.Join(gitRootPath, p)) { + toAdd = append(toAdd, p) + } + } + + s.Update("%s: Committing chart", statusPrefix) + + mutex.Lock() + defer mutex.Unlock() + + r, err := git.PlainOpen(gitRootPath) + if err != nil { + return err + } + wt, err := r.Worktree() + if err != nil { + return err + } + + for _, p := range toAdd { + _, err = wt.Add(p) + if err != nil { + return err + } + } + + commitMsg := fmt.Sprintf("Updated helm chart %s from %s to %s", statusPrefix, oldVersion, newVersion) + _, err = wt.Commit(commitMsg, &git.CommitOptions{}) + if err != nil { + return err + } + + s.Update("%s: Committed helm chart with version %s", statusPrefix, newVersion) + s.Success() + + return nil } diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index b7930f0f8..f5b8fcdc7 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -30,27 +30,27 @@ import ( "strings" ) -type helmChart struct { - configFile string +type HelmChart struct { + ConfigFile string Config *types.HelmChartConfig credentials *repo.Entry } -func NewHelmChart(configFile string) (*helmChart, error) { +func NewHelmChart(configFile string) (*HelmChart, error) { var config types.HelmChartConfig err := yaml.ReadYamlFile(configFile, &config) if err != nil { return nil, err } - hc := &helmChart{ - configFile: configFile, + hc := &HelmChart{ + ConfigFile: configFile, Config: &config, } return hc, nil } -func (c *helmChart) GetChartName() (string, error) { +func (c *HelmChart) GetChartName() (string, error) { if c.Config.Repo != nil && registry.IsOCI(*c.Config.Repo) { s := strings.Split(*c.Config.Repo, "/") chartName := s[len(s)-1] @@ -65,18 +65,18 @@ func (c *helmChart) GetChartName() (string, error) { return *c.Config.ChartName, nil } -func (c *helmChart) GetChartDir() (string, error) { +func (c *HelmChart) GetChartDir() (string, error) { chartName, err := c.GetChartName() if err != nil { return "", err } - dir := filepath.Dir(c.configFile) + dir := filepath.Dir(c.ConfigFile) targetDir := filepath.Join(dir, "charts") return securejoin.SecureJoin(targetDir, chartName) } -func (c *helmChart) GetOutputPath() string { +func (c *HelmChart) GetOutputPath() string { output := "helm-rendered.yaml" if c.Config.Output != nil { output = *c.Config.Output @@ -84,12 +84,12 @@ func (c *helmChart) GetOutputPath() string { return output } -func (c *helmChart) GetFullOutputPath() (string, error) { - dir := filepath.Dir(c.configFile) +func (c *HelmChart) GetFullOutputPath() (string, error) { + dir := filepath.Dir(c.ConfigFile) return securejoin.SecureJoin(dir, c.GetOutputPath()) } -func (c *helmChart) buildHelmConfig(k *k8s.K8sCluster) (*action.Configuration, error) { +func (c *HelmChart) buildHelmConfig(k *k8s.K8sCluster) (*action.Configuration, error) { rc, err := registry.NewClient() if err != nil { return nil, err @@ -101,7 +101,7 @@ func (c *helmChart) buildHelmConfig(k *k8s.K8sCluster) (*action.Configuration, e }, nil } -func (c *helmChart) Pull(ctx context.Context) error { +func (c *HelmChart) Pull(ctx context.Context) error { chartName, err := c.GetChartName() if err != nil { return err @@ -112,7 +112,7 @@ func (c *helmChart) Pull(ctx context.Context) error { return err } - targetDir := filepath.Join(filepath.Dir(c.configFile), "charts") + targetDir := filepath.Join(filepath.Dir(c.ConfigFile), "charts") _ = os.RemoveAll(chartDir) cfg, err := c.buildHelmConfig(nil) @@ -154,7 +154,7 @@ func (c *helmChart) Pull(ctx context.Context) error { return nil } -func (c *helmChart) CheckUpdate() (string, bool, error) { +func (c *HelmChart) CheckUpdate() (string, bool, error) { if c.Config.Repo != nil && registry.IsOCI(*c.Config.Repo) { return "", false, nil } @@ -203,7 +203,7 @@ func (c *helmChart) CheckUpdate() (string, bool, error) { return latestVersion, updated, nil } -func (c *helmChart) Render(ctx context.Context, k *k8s.K8sCluster) error { +func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster) error { chartName, err := c.GetChartName() if err != nil { return err @@ -215,7 +215,7 @@ func (c *helmChart) Render(ctx context.Context, k *k8s.K8sCluster) error { return nil } -func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { +func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { chartDir, err := c.GetChartDir() if err != nil { return err @@ -224,7 +224,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { if err != nil { return err } - valuesPath := yaml.FixPathExt(filepath.Join(filepath.Dir(c.configFile), "helm-values.yml")) + valuesPath := yaml.FixPathExt(filepath.Join(filepath.Dir(c.ConfigFile), "helm-values.yml")) var gvs []schema.GroupVersion if k != nil { @@ -345,7 +345,7 @@ func (c *helmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { return nil } -func (c *helmChart) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, error) { +func (c *HelmChart) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, error) { var parsed []*uo.UnstructuredObject duplicatesRemoved, err := yaml.RemoveDuplicateFields(strings.NewReader(s)) @@ -367,7 +367,7 @@ func (c *helmChart) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, return parsed, nil } -func (c *helmChart) SetCredentials(credentials *repo.Entry) { +func (c *HelmChart) SetCredentials(credentials *repo.Entry) { c.credentials = credentials } @@ -388,6 +388,6 @@ func isTestHook(h *release.Hook) bool { return false } -func (c *helmChart) Save() error { - return yaml.WriteYamlFile(c.configFile, c.Config) +func (c *HelmChart) Save() error { + return yaml.WriteYamlFile(c.ConfigFile, c.Config) } From 41271471fb25026fe1120997c1202437636c9b0f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 14 Nov 2022 23:54:33 +0100 Subject: [PATCH 0542/2268] feat: Allow to use Helm without pre-pulling --- pkg/deployment/helm_chart.go | 129 +++++++++++++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 6 deletions(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index f5b8fcdc7..f259e6a31 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -2,6 +2,8 @@ package deployment import ( "context" + "crypto/sha256" + "encoding/hex" "fmt" securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -12,6 +14,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/pkg/errors" + "github.com/rogpeppe/go-internal/lockedfile" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" @@ -28,6 +31,7 @@ import ( "regexp" "sort" "strings" + "time" ) type HelmChart struct { @@ -101,19 +105,96 @@ func (c *HelmChart) buildHelmConfig(k *k8s.K8sCluster) (*action.Configuration, e }, nil } +func (c *HelmChart) checkNeedsPull(chartDir string, isTmp bool) (bool, bool, string, error) { + if !utils.IsDirectory(chartDir) { + return true, false, "", nil + } + + chartYamlPath := yaml.FixPathExt(filepath.Join(chartDir, "Chart.yaml")) + st, err := os.Stat(chartYamlPath) + if err != nil { + if os.IsNotExist(err) { + return true, false, "", nil + } + return false, false, "", err + } + if isTmp && time.Now().Sub(st.ModTime()) >= time.Hour*24 { + // MacOS will delete tmp files after 3 days, so lets be safe and re-pull every day + return true, false, "", nil + } + + chartYaml, err := uo.FromFile(chartYamlPath) + if err != nil { + return false, false, "", err + } + + version, _, _ := chartYaml.GetNestedString("version") + if version != *c.Config.ChartVersion { + return true, true, version, nil + } + return false, false, "", nil +} + func (c *HelmChart) Pull(ctx context.Context) error { - chartName, err := c.GetChartName() + chartDir, err := c.GetChartDir() if err != nil { return err } + return c.doPull(ctx, chartDir) +} - chartDir, err := c.GetChartDir() +func (c *HelmChart) pullTmpChart(ctx context.Context) (string, error) { + chartName, err := c.GetChartName() + if err != nil { + return "", err + } + + hash := sha256.New() + _, _ = fmt.Fprintf(hash, "%s\n", *c.Config.Repo) + _, _ = fmt.Fprintf(hash, "%s\n", chartName) + _, _ = fmt.Fprintf(hash, "%s\n", *c.Config.ChartVersion) + h := hex.EncodeToString(hash.Sum(nil)) + tmpDir := filepath.Join(utils.GetTmpBaseDir(), "helm-charts") + _ = os.MkdirAll(tmpDir, 0o700) + tmpDir = filepath.Join(tmpDir, fmt.Sprintf("%s-%s", chartName, h)) + + lockFile := tmpDir + ".lock" + lock, err := lockedfile.Create(lockFile) + if err != nil { + return "", err + } + defer lock.Close() + + needsPull, _, _, err := c.checkNeedsPull(tmpDir, true) + if err != nil { + return "", err + } + if !needsPull { + return tmpDir, nil + } + err = c.doPull(ctx, tmpDir) + if err != nil { + return "", err + } + return tmpDir, nil +} + +func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { + chartName, err := c.GetChartName() if err != nil { return err } - targetDir := filepath.Join(filepath.Dir(c.ConfigFile), "charts") _ = os.RemoveAll(chartDir) + _ = os.MkdirAll(filepath.Dir(chartDir), 0o700) + + tmpDir, err := os.MkdirTemp(utils.GetTmpBaseDir(), "helm-pull-") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + tmpChartDir := filepath.Join(tmpDir, chartName) cfg, err := c.buildHelmConfig(nil) if err != nil { @@ -122,7 +203,7 @@ func (c *HelmChart) Pull(ctx context.Context) error { a := action.NewPullWithOpts(action.WithConfig(cfg)) a.Settings = cli.New() a.Untar = true - a.DestDir = targetDir + a.DestDir = tmpDir a.Version = *c.Config.ChartVersion if c.credentials != nil { @@ -142,15 +223,22 @@ func (c *HelmChart) Pull(ctx context.Context) error { a.RepoURL = *c.Config.Repo out, err = a.Run(chartName) } + if err != nil { + return err + } + // a bug in the Pull command causes this directory to be created by accident - _ = os.RemoveAll(chartDir + fmt.Sprintf("-%s.tar.gz", a.Version)) - _ = os.RemoveAll(chartDir + fmt.Sprintf("-%s.tgz", a.Version)) + _ = os.RemoveAll(tmpChartDir + fmt.Sprintf("-%s.tar.gz", a.Version)) + _ = os.RemoveAll(tmpChartDir + fmt.Sprintf("-%s.tgz", a.Version)) if out != "" { status.PlainText(ctx, out) } + + err = os.Rename(tmpChartDir, chartDir) if err != nil { return err } + return nil } @@ -216,10 +304,39 @@ func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster) error { } func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { + chartName, err := c.GetChartName() + if err != nil { + return err + } + chartDir, err := c.GetChartDir() if err != nil { return err } + + needsPull, versionChanged, prePulledVersion, err := c.checkNeedsPull(chartDir, false) + if err != nil { + return err + } + if needsPull { + if versionChanged { + return fmt.Errorf("pre-pulled Helm Chart %s need to be pulled (call 'kluctl helm-pull'). "+ + "Desired version is %s while pre-pulled version is %s", chartName, *c.Config.ChartVersion, prePulledVersion) + } else { + status.Warning(ctx, "Warning, need to pull Helm Chart %s with version %s. "+ + "Please consider pre-pulling it with 'kluctl helm-pull'", chartName, *c.Config.ChartVersion) + } + + s := status.Start(ctx, "Pulling Helm Chart %s with version %s", chartName, *c.Config.ChartVersion) + defer s.Failed() + + chartDir, err = c.pullTmpChart(ctx) + if err != nil { + return err + } + s.Success() + } + outputPath, err := c.GetFullOutputPath() if err != nil { return err From 5cfde34fd9dc29fa7412a54221aadbe56fbd3c4f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 10:27:16 +0100 Subject: [PATCH 0543/2268] fix: Don't swallow errors when using simple status handler --- pkg/status/status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/status/status.go b/pkg/status/status.go index 50dedcdd2..2bf1ec85a 100644 --- a/pkg/status/status.go +++ b/pkg/status/status.go @@ -201,7 +201,7 @@ func (s *StatusContext) FailedWithMessage(msg string, args ...any) { if s.finished { return } - s.Update(msg, args...) + s.UpdateAndInfoFallback(msg, args...) s.Failed() } From 752653b6fc74f23e9f05bab94df2dfbaca872687 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 10:27:50 +0100 Subject: [PATCH 0544/2268] fix: Show prefix in helmPullCmd.Run() --- cmd/kluctl/commands/cmd_helm_pull.go | 16 ++++++++-------- cmd/kluctl/commands/cmd_helm_update.go | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index e4c510993..c4f9ff8f4 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -47,12 +47,17 @@ func (cmd *helmPullCmd) Run() error { return nil } + statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) + if err != nil { + return err + } + wg.Add(1) utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, func() error { defer wg.Done() - s := status.Start(cliCtx, "%s: Pulling Chart") + s := status.Start(cliCtx, "%s: Pulling Chart", statusPrefix) defer s.Failed() - err := doPull(gitRootPath, p, cmd.HelmCredentials, s) + err := doPull(statusPrefix, p, cmd.HelmCredentials, s) if err != nil { return err } @@ -74,12 +79,7 @@ func (cmd *helmPullCmd) Run() error { return nil } -func doPull(gitRootPath string, p string, helmCredentials args.HelmCredentials, s *status.StatusContext) error { - statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) - if err != nil { - return err - } - +func doPull(statusPrefix string, p string, helmCredentials args.HelmCredentials, s *status.StatusContext) error { doError := func(err error) error { s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return err diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index d35bdb496..1d5f696a8 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -192,7 +192,7 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployme return err } - err = doPull(gitRootPath, chart.ConfigFile, cmd.HelmCredentials, s) + err = doPull(statusPrefix, chart.ConfigFile, cmd.HelmCredentials, s) if err != nil { return err } From 03f9a74ac736c5e099e4759703db655eddb16a21 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 14:34:24 +0100 Subject: [PATCH 0545/2268] tests: Fix incomplete stdout/stderr reading from sub-processes --- e2e/utils.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/e2e/utils.go b/e2e/utils.go index 7e7ac0d26..f8a20f9ad 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -13,6 +13,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "os/exec" "reflect" + "sync" "testing" ) @@ -69,7 +70,9 @@ func runHelper(t *testing.T, cmd *exec.Cmd) (string, string, error) { return "", "", err } + var wg sync.WaitGroup stdReader := func(testLogPrefix string, buf io.StringWriter, pipe io.Reader) { + defer wg.Done() scanner := bufio.NewScanner(pipe) for scanner.Scan() { l := scanner.Text() @@ -81,9 +84,15 @@ func runHelper(t *testing.T, cmd *exec.Cmd) (string, string, error) { stdoutBuf := bytes.NewBuffer(nil) stderrBuf := bytes.NewBuffer(nil) + wg.Add(2) go stdReader("stdout: ", stdoutBuf, stdoutPipe) go stdReader("stderr: ", stderrBuf, stderrPipe) - err = cmd.Run() + err = cmd.Start() + if err != nil { + return "", "", err + } + wg.Wait() + err = cmd.Wait() return stdoutBuf.String(), stderrBuf.String(), err } From 91689d19493c4a987a7e987682839ac697ecb061 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 14:34:54 +0100 Subject: [PATCH 0546/2268] fix: Also print warnings/errors with simple status handler --- cmd/kluctl/commands/cmd_helm_update.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 1d5f696a8..6c1ed737a 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -141,13 +141,13 @@ func (cmd *helmUpdateCmd) doCheckUpdate(gitRootPath string, p string) (*deployme return doError(err) } if !updated { - s.Update("%s: Version %s is already up-to-date.", statusPrefix, *chart.Config.ChartVersion) + s.UpdateAndInfoFallback("%s: Version %s is already up-to-date.", statusPrefix, *chart.Config.ChartVersion) } else { msg := fmt.Sprintf("%s: Chart has new version %s available. Old version is %s.", statusPrefix, newVersion, *chart.Config.ChartVersion) if chart.Config.SkipUpdate { msg += " skipUpdate is set to true." } - s.Update(msg) + s.UpdateAndInfoFallback(msg) } s.Success() From 3fa462a8683dc69def02319afd3ce7362a58b549 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 14:35:14 +0100 Subject: [PATCH 0547/2268] fix: Properly skip chart updates if skipUpdate is true --- cmd/kluctl/commands/cmd_helm_update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 6c1ed737a..ae560adab 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -69,7 +69,7 @@ func (cmd *helmUpdateCmd) Run() error { mutex.Lock() defer mutex.Unlock() - if updated { + if !chart.Config.SkipUpdate && updated { updatedCharts = append(updatedCharts, &updatedChart{ chart: chart, newVersion: newVersion, From 4ab482756681ad79b0427ab8423397a45062fb60 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 14:35:37 +0100 Subject: [PATCH 0548/2268] fix: Use temporary cache when downloading Helm index files --- pkg/deployment/helm_chart.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index f259e6a31..a7cf18b55 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -265,6 +265,12 @@ func (c *HelmChart) CheckUpdate() (string, bool, error) { return "", false, err } + r.CachePath, err = os.MkdirTemp(utils.GetTmpBaseDir(), "helm-check-update-") + if err != nil { + return "", false, err + } + defer os.RemoveAll(r.CachePath) + indexFile, err := r.DownloadIndexFile() if err != nil { return "", false, err From f698dd97accb8470227a0effa4950b2351251d9e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 14:35:47 +0100 Subject: [PATCH 0549/2268] tests: Implement Helm tests --- e2e/helm_test.go | 395 +++++++++++++++++++ e2e/test-helm-chart/.helmignore | 23 ++ e2e/test-helm-chart/Chart.yaml | 6 + e2e/test-helm-chart/resources.go | 8 + e2e/test-helm-chart/templates/_helpers.tpl | 62 +++ e2e/test-helm-chart/templates/configmap.yaml | 10 + e2e/test-helm-chart/values.yaml | 3 + internal/test-utils/git_server.go | 7 +- 8 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 e2e/helm_test.go create mode 100644 e2e/test-helm-chart/.helmignore create mode 100644 e2e/test-helm-chart/Chart.yaml create mode 100644 e2e/test-helm-chart/resources.go create mode 100644 e2e/test-helm-chart/templates/_helpers.tpl create mode 100644 e2e/test-helm-chart/templates/configmap.yaml create mode 100644 e2e/test-helm-chart/values.yaml diff --git a/e2e/helm_test.go b/e2e/helm_test.go new file mode 100644 index 000000000..a0a73552a --- /dev/null +++ b/e2e/helm_test.go @@ -0,0 +1,395 @@ +package e2e + +import ( + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" + test_resources "github.com/kluctl/kluctl/v2/e2e/test-helm-chart" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "github.com/stretchr/testify/assert" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/cli/values" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/repo" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "sort" + "testing" +) + +func createHelmPackage(t *testing.T, name string, version string) string { + tmpDir := t.TempDir() + err := utils.FsCopyDir(test_resources.HelmChartFS, ".", tmpDir) + if err != nil { + t.Fatal(err) + } + + c, err := uo.FromFile(filepath.Join(tmpDir, "Chart.yaml")) + if err != nil { + t.Fatal(err) + } + + _ = c.SetNestedField(name, "name") + _ = c.SetNestedField(version, "version") + + err = yaml.WriteYamlFile(filepath.Join(tmpDir, "Chart.yaml"), c) + if err != nil { + t.Fatal(err) + } + + settings := cli.New() + client := action.NewPackage() + client.Destination = tmpDir + valueOpts := &values.Options{} + p := getter.All(settings) + vals, err := valueOpts.MergeValues(p) + retName, err := client.Run(tmpDir, vals) + if err != nil { + t.Fatal(err) + } + + return retName +} + +type repoChart struct { + chartName string + version string +} + +func createHelmRepo(t *testing.T, charts []repoChart) string { + tmpDir := t.TempDir() + + for _, c := range charts { + tgz := createHelmPackage(t, c.chartName, c.version) + _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) + } + + s := httptest.NewServer(http.FileServer(http.FS(os.DirFS(tmpDir)))) + t.Cleanup(s.Close) + + i, err := repo.IndexDirectory(tmpDir, s.URL) + if err != nil { + t.Fatal(err) + } + + i.SortEntries() + err = i.WriteFile(filepath.Join(tmpDir, "index.yaml"), 0644) + if err != nil { + t.Fatal(err) + } + + return s.URL +} + +func addHelmDeployment(p *testProject, dir string, repoUrl string, chartName, version string, releaseName string, namespace string, values map[string]any) { + + p.addKustomizeDeployment(dir, []kustomizeResource{ + {name: "helm-rendered.yaml"}, + }, nil) + + p.updateYaml(filepath.Join(dir, "helm-chart.yaml"), func(o *uo.UnstructuredObject) error { + *o = *uo.FromMap(map[string]interface{}{ + "helmChart": map[string]any{ + "repo": repoUrl, + "chartName": chartName, + "chartVersion": version, + "releaseName": releaseName, + "namespace": namespace, + }, + }) + return nil + }, "") + + if values != nil { + p.updateYaml(filepath.Join(dir, "helm-values.yaml"), func(o *uo.UnstructuredObject) error { + *o = *uo.FromMap(values) + return nil + }, "") + } +} + +func TestHelmNoPrePull(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k) + + createNamespace(t, k, p.testSlug()) + + repoUrl := createHelmRepo(p.t, []repoChart{ + {chartName: "test-chart1", version: "0.1.0"}, + }) + + p.updateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") +} + +func TestHelmPrePull(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k) + + createNamespace(t, k, p.testSlug()) + + repoUrl := createHelmRepo(p.t, []repoChart{ + {chartName: "test-chart1", version: "0.1.0"}, + {chartName: "test-chart2", version: "0.1.0"}, + }) + + p.updateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) + + p.KluctlMust("helm-pull") + assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") + + addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.testSlug(), nil) + + p.KluctlMust("helm-pull") + assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm2/charts/test-chart2/Chart.yaml")) + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.testSlug(), "test-helm2-test-chart2") +} + +func TestHelmManualUpgrade(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k) + + createNamespace(t, k, p.testSlug()) + + repoUrl := createHelmRepo(p.t, []repoChart{ + {chartName: "test-chart1", version: "0.1.0"}, + {chartName: "test-chart1", version: "0.2.0"}, + }) + + p.updateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) + + p.KluctlMust("helm-pull") + assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) + p.KluctlMust("deploy", "--yes", "-t", "test") + cm := assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") + v, _, _ := cm.GetNestedString("data", "version") + assert.Equal(t, "0.1.0", v) + + p.updateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField("0.2.0", "helmChart", "chartVersion") + return nil + }, "") + + p.KluctlMust("helm-pull") + p.KluctlMust("deploy", "--yes", "-t", "test") + cm = assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") + v, _, _ = cm.GetNestedString("data", "version") + assert.Equal(t, "0.2.0", v) +} + +func testHelmUpdate(t *testing.T, upgrade bool, commit bool) { + t.Parallel() + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k) + + createNamespace(t, k, p.testSlug()) + + repoUrl := createHelmRepo(p.t, []repoChart{ + {chartName: "test-chart1", version: "0.1.0"}, + {chartName: "test-chart1", version: "0.2.0"}, + {chartName: "test-chart2", version: "0.1.0"}, + {chartName: "test-chart2", version: "0.3.0"}, + }) + + p.updateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) + addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.testSlug(), nil) + addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.testSlug(), nil) + + p.updateYaml("helm3/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipUpdate") + return nil + }, "") + + p.KluctlMust("helm-pull") + assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) + assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm2/charts/test-chart2/Chart.yaml")) + assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm3/charts/test-chart1/Chart.yaml")) + + args := []string{"helm-update"} + if upgrade { + args = append(args, "--upgrade") + } + if commit { + args = append(args, "--commit") + } + + _, stderr := p.KluctlMust(args...) + assert.Contains(t, stderr, "helm1: Chart has new version 0.2.0 available.") + assert.Contains(t, stderr, "helm2: Chart has new version 0.3.0 available.") + assert.Contains(t, stderr, "helm3: Chart has new version 0.2.0 available. Old version is 0.1.0. skipUpdate is set to true.") + + c1, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) + assert.NoError(t, err) + c2, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm2/charts/test-chart2/Chart.yaml")) + assert.NoError(t, err) + c3, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm3/charts/test-chart1/Chart.yaml")) + assert.NoError(t, err) + + v1, _, _ := c1.GetNestedString("version") + v2, _, _ := c2.GetNestedString("version") + v3, _, _ := c3.GetNestedString("version") + if upgrade { + assert.Equal(t, "0.2.0", v1) + assert.Equal(t, "0.3.0", v2) + assert.Equal(t, "0.1.0", v3) + } else { + assert.Equal(t, "0.1.0", v1) + assert.Equal(t, "0.1.0", v2) + assert.Equal(t, "0.1.0", v3) + } + + if commit { + r := p.gitServer.GetGitRepo(p.getKluctlProjectRepo()) + + commits, err := r.Log(&git.LogOptions{}) + assert.NoError(t, err) + var commitList []object.Commit + err = commits.ForEach(func(commit *object.Commit) error { + commitList = append(commitList, *commit) + return nil + }) + assert.NoError(t, err) + + commitList = commitList[0:2] + sort.Slice(commitList, func(i, j int) bool { + return commitList[i].Message < commitList[j].Message + }) + + assert.Equal(t, "Updated helm chart helm1 from 0.1.0 to 0.2.0", commitList[0].Message) + assert.Equal(t, "Updated helm chart helm2 from 0.1.0 to 0.3.0", commitList[1].Message) + } +} + +func TestHelmUpdate(t *testing.T) { + testHelmUpdate(t, false, false) +} + +func TestHelmUpdateAndUpgrade(t *testing.T) { + testHelmUpdate(t, true, false) +} + +func TestHelmUpdateAndUpgradeAndCommit(t *testing.T) { + testHelmUpdate(t, true, true) +} + +func TestHelmValues(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k) + + createNamespace(t, k, p.testSlug()) + + repoUrl := createHelmRepo(p.t, []repoChart{ + {chartName: "test-chart1", version: "0.1.0"}, + {chartName: "test-chart2", version: "0.1.0"}, + }) + + values1 := map[string]any{ + "data": map[string]any{ + "a": "x1", + "b": "y1", + }, + } + values2 := map[string]any{ + "data": map[string]any{ + "a": "x2", + "b": "y2", + }, + } + values3 := map[string]any{ + "data": map[string]any{ + "a": "{{ args.a }}", + "b": "{{ args.b }}", + }, + } + + p.updateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), values1) + addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.testSlug(), values2) + addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.testSlug(), values3) + + p.KluctlMust("helm-pull") + p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") + + cm1 := assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") + cm2 := assertConfigMapExists(t, k, p.testSlug(), "test-helm2-test-chart2") + cm3 := assertConfigMapExists(t, k, p.testSlug(), "test-helm3-test-chart1") + + assert.Equal(t, map[string]any{ + "a": "x1", + "b": "y1", + "version": "0.1.0", + }, cm1.Object["data"]) + assert.Equal(t, map[string]any{ + "a": "x2", + "b": "y2", + "version": "0.1.0", + }, cm2.Object["data"]) + assert.Equal(t, map[string]any{ + "a": "a", + "b": "b", + "version": "0.1.0", + }, cm3.Object["data"]) +} + +func TestHelmTemplateChartYaml(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := &testProject{} + p.init(t, k) + + createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.testSlug()+"-a") + createNamespace(t, k, p.testSlug()+"-b") + + repoUrl := createHelmRepo(p.t, []repoChart{ + {chartName: "test-chart1", version: "0.1.0"}, + {chartName: "test-chart2", version: "0.1.0"}, + }) + + p.updateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm-{{ args.a }}", p.testSlug(), nil) + addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm-{{ args.b }}", p.testSlug(), nil) + addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.testSlug()+"-{{ args.a }}", nil) + addHelmDeployment(p, "helm4", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.testSlug()+"-{{ args.b }}", nil) + + p.KluctlMust("helm-pull") + p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") + + assertConfigMapExists(t, k, p.testSlug(), "test-helm-a-test-chart1") + assertConfigMapExists(t, k, p.testSlug(), "test-helm-b-test-chart2") + assertConfigMapExists(t, k, p.testSlug()+"-a", "test-helm-ns-test-chart1") + assertConfigMapExists(t, k, p.testSlug()+"-b", "test-helm-ns-test-chart1") +} diff --git a/e2e/test-helm-chart/.helmignore b/e2e/test-helm-chart/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/e2e/test-helm-chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/e2e/test-helm-chart/Chart.yaml b/e2e/test-helm-chart/Chart.yaml new file mode 100644 index 000000000..0f3baa0c1 --- /dev/null +++ b/e2e/test-helm-chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: test-helm-chart +description: A Helm chart for Kubernetes +type: application +version: 0.1.0 +appVersion: "0.1.0" diff --git a/e2e/test-helm-chart/resources.go b/e2e/test-helm-chart/resources.go new file mode 100644 index 000000000..2ad97a675 --- /dev/null +++ b/e2e/test-helm-chart/resources.go @@ -0,0 +1,8 @@ +package test_resources + +import ( + "embed" +) + +//go:embed all:* +var HelmChartFS embed.FS diff --git a/e2e/test-helm-chart/templates/_helpers.tpl b/e2e/test-helm-chart/templates/_helpers.tpl new file mode 100644 index 000000000..bcbf57e17 --- /dev/null +++ b/e2e/test-helm-chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "test-helm-chart.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "test-helm-chart.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "test-helm-chart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "test-helm-chart.labels" -}} +helm.sh/chart: {{ include "test-helm-chart.chart" . }} +{{ include "test-helm-chart.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "test-helm-chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "test-helm-chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "test-helm-chart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "test-helm-chart.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/e2e/test-helm-chart/templates/configmap.yaml b/e2e/test-helm-chart/templates/configmap.yaml new file mode 100644 index 000000000..fe63edb28 --- /dev/null +++ b/e2e/test-helm-chart/templates/configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "test-helm-chart.fullname" . }} + labels: + {{- include "test-helm-chart.labels" . | nindent 4 }} +data: + a: {{ .Values.data.a }} + b: {{ .Values.data.b }} + version: {{ .Chart.Version }} diff --git a/e2e/test-helm-chart/values.yaml b/e2e/test-helm-chart/values.yaml new file mode 100644 index 000000000..9d4b13a77 --- /dev/null +++ b/e2e/test-helm-chart/values.yaml @@ -0,0 +1,3 @@ +data: + a: v1 + b: v2 diff --git a/internal/test-utils/git_server.go b/internal/test-utils/git_server.go index 070e4c98e..f539dac91 100644 --- a/internal/test-utils/git_server.go +++ b/internal/test-utils/git_server.go @@ -243,11 +243,16 @@ func (p *GitServer) LocalRepoDir(repo string) string { return filepath.Join(p.baseDir, repo) } -func (p *GitServer) GetWorktree(repo string) *git.Worktree { +func (p *GitServer) GetGitRepo(repo string) *git.Repository { r, err := git.PlainOpen(p.LocalRepoDir(repo)) if err != nil { p.t.Fatal(err) } + return r +} + +func (p *GitServer) GetWorktree(repo string) *git.Worktree { + r := p.GetGitRepo(repo) wt, err := r.Worktree() if err != nil { p.t.Fatal(err) From bae9fe76d8e558911af350c2eaa96fa9e37af5bf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 14:44:32 +0100 Subject: [PATCH 0550/2268] docs: Update helm.md to mention that pre-pulling is optional --- docs/reference/deployments/helm.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index 11794ea7d..bc49ddf0d 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -16,11 +16,17 @@ kluctl offers a simple-to-use Helm integration, which allows you to reuse many c The integration is split into 2 parts/steps/layers. The first is the management and pulling of the Helm Charts, while the second part handles configuration/customization and deployment of the chart. -Pulled Helm Charts are meant to be added to version control to ensure proper speed and consistency. +It is recommended to pre-pull Helm Charts with [`kluctl helm-pull`](../commands/helm-pull.md), which will store the +pulled charts near the `helm-chart.yaml` file (inside the `charts` sub-directory). It is however also possible (but not +recommended) to skip the pre-pulling phase and let kluctl pull Charts on-demand. + +When pre-pulling Helm Charts, you can also add the resulting Chart contents into version control. This is actually +recommended as it ensures that the deployment will always behave the same. It also allows pull-request based reviews +on third-party Helm Charts. ## How it works -Helm charts are not directly deployed via Helm. Instead, kluctl renders the Helm Chart into a single file and then +Helm charts are not directly installed via Helm. Instead, kluctl renders the Helm Chart into a single file and then hands over the rendered yaml to [kustomize](https://kustomize.io/). Rendering is done in combination with a provided `helm-values.yaml`, which contains the necessary values to configure the Helm Chart. @@ -82,7 +88,7 @@ resource. If `output` is omitted, the default value `helm-rendered.yaml` is used The url to the Helm repository where the Helm Chart is located. You can use hub.helm.sh to search for repositories and charts and then use the repos found there. -oci based repositories are also supported, for example: +OCI based repositories are also supported, for example: ```yaml helmChart: repo: oci://r.myreg.io/mycharts/pepper From 90bf499d8b81dba8ac89787f356336835d27fd3c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 16:14:35 +0100 Subject: [PATCH 0551/2268] fix: Fix false-positive cache hits when scheme changes --- pkg/registries/registries.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index a280438bf..48556c1e8 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -367,7 +367,7 @@ func (rh *RegistryHelper) writeCachedResponse(key string, data []byte) { } func (rh *RegistryHelper) RoundTripCached(req *http.Request, extraKey string, onNew func(res *http.Response) error) (*http.Response, error) { - key := fmt.Sprintf("%s\n%s\n%s\n", req.Host, req.URL.Path, extraKey) + key := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n", req.URL.Scheme, req.URL.Host, req.Host, req.URL.Path, extraKey) key = utils.Sha256String(key) isNew := false From 3447518f585d24e7b2e409fe67840ae883c4121e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 16:14:51 +0100 Subject: [PATCH 0552/2268] feat: Implement helm-update for OCI charts --- cmd/kluctl/commands/cmd_helm_update.go | 2 +- pkg/deployment/helm_chart.go | 29 ++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index ae560adab..4dca7ca47 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -136,7 +136,7 @@ func (cmd *helmUpdateCmd) doCheckUpdate(gitRootPath string, p string) (*deployme } chart.SetCredentials(creds) - newVersion, updated, err := chart.CheckUpdate() + newVersion, updated, err := chart.CheckUpdate(cliCtx) if err != nil { return doError(err) } diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index a7cf18b55..fb3b53926 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -7,6 +7,7 @@ import ( "fmt" securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/registries" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -242,10 +243,34 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { return nil } -func (c *HelmChart) CheckUpdate() (string, bool, error) { +func (c *HelmChart) CheckUpdate(ctx context.Context) (string, bool, error) { if c.Config.Repo != nil && registry.IsOCI(*c.Config.Repo) { - return "", false, nil + return c.checkUpdateOciRepo(ctx) } + return c.checkUpdateHelmRepo() +} + +func (c *HelmChart) checkUpdateOciRepo(ctx context.Context) (string, bool, error) { + rh := registries.NewRegistryHelper(ctx) + + imageName := strings.TrimPrefix(*c.Config.Repo, "oci://") + tags, err := rh.ListImageTags(imageName) + if err != nil { + return "", false, err + } + + var ls versions.LooseVersionSlice + for _, x := range tags { + ls = append(ls, versions.LooseVersion(x)) + } + sort.Stable(ls) + latestVersion := string(ls[len(ls)-1]) + + updated := latestVersion != *c.Config.ChartVersion + return latestVersion, updated, nil +} + +func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { chartName, err := c.GetChartName() if err != nil { return "", false, err From 2a3b86b91d1e05e016d1cc6545ac547e879f8239 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 16:15:00 +0100 Subject: [PATCH 0553/2268] tests: Test OCI charts --- e2e/helm_test.go | 118 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 16 deletions(-) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index a0a73552a..e4195f7f7 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -3,6 +3,7 @@ package e2e import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" + "github.com/google/go-containerregistry/pkg/registry" test_resources "github.com/kluctl/kluctl/v2/e2e/test-helm-chart" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -12,12 +13,16 @@ import ( "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/pusher" + registry2 "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/repo" + "helm.sh/helm/v3/pkg/uploader" "net/http" "net/http/httptest" "os" "path/filepath" "sort" + "strings" "testing" ) @@ -85,7 +90,50 @@ func createHelmRepo(t *testing.T, charts []repoChart) string { return s.URL } +func createOciRepo(t *testing.T, charts []repoChart) string { + tmpDir := t.TempDir() + + ociRegistry := registry.New() + ociServer := httptest.NewServer(ociRegistry) + + t.Cleanup(ociServer.Close) + + ociUrl := strings.ReplaceAll(ociServer.URL, "http://", "oci://") + + var out strings.Builder + settings := cli.New() + c := uploader.ChartUploader{ + Out: &out, + Pushers: pusher.All(settings), + Options: []pusher.Option{}, + } + + for _, chart := range charts { + tgz := createHelmPackage(t, chart.chartName, chart.version) + _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) + + err := c.UploadTo(tgz, ociUrl) + if err != nil { + t.Fatal(err) + } + } + + return ociUrl +} + +func createHelmOrOciRepo(t *testing.T, charts []repoChart, oci bool) string { + if oci { + return createOciRepo(t, charts) + } else { + return createHelmRepo(t, charts) + } +} + func addHelmDeployment(p *testProject, dir string, repoUrl string, chartName, version string, releaseName string, namespace string, values map[string]any) { + if registry2.IsOCI(repoUrl) { + repoUrl += "/" + chartName + chartName = "" + } p.addKustomizeDeployment(dir, []kustomizeResource{ {name: "helm-rendered.yaml"}, @@ -95,12 +143,14 @@ func addHelmDeployment(p *testProject, dir string, repoUrl string, chartName, ve *o = *uo.FromMap(map[string]interface{}{ "helmChart": map[string]any{ "repo": repoUrl, - "chartName": chartName, "chartVersion": version, "releaseName": releaseName, "namespace": namespace, }, }) + if chartName != "" { + _ = o.SetNestedField(chartName, "helmChart", "chartName") + } return nil }, "") @@ -112,7 +162,7 @@ func addHelmDeployment(p *testProject, dir string, repoUrl string, chartName, ve } } -func TestHelmNoPrePull(t *testing.T) { +func testHelmNoPrePull(t *testing.T, oci bool) { t.Parallel() k := defaultCluster1 @@ -122,9 +172,9 @@ func TestHelmNoPrePull(t *testing.T) { createNamespace(t, k, p.testSlug()) - repoUrl := createHelmRepo(p.t, []repoChart{ + repoUrl := createHelmOrOciRepo(p.t, []repoChart{ {chartName: "test-chart1", version: "0.1.0"}, - }) + }, oci) p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) @@ -132,7 +182,15 @@ func TestHelmNoPrePull(t *testing.T) { assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") } -func TestHelmPrePull(t *testing.T) { +func TestHelmNoPrePull(t *testing.T) { + testHelmNoPrePull(t, false) +} + +func TestHelmNoPrePullOci(t *testing.T) { + testHelmNoPrePull(t, true) +} + +func testHelmPrePull(t *testing.T, oci bool) { t.Parallel() k := defaultCluster1 @@ -142,10 +200,10 @@ func TestHelmPrePull(t *testing.T) { createNamespace(t, k, p.testSlug()) - repoUrl := createHelmRepo(p.t, []repoChart{ + repoUrl := createHelmOrOciRepo(p.t, []repoChart{ {chartName: "test-chart1", version: "0.1.0"}, {chartName: "test-chart2", version: "0.1.0"}, - }) + }, oci) p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) @@ -163,7 +221,15 @@ func TestHelmPrePull(t *testing.T) { assertConfigMapExists(t, k, p.testSlug(), "test-helm2-test-chart2") } -func TestHelmManualUpgrade(t *testing.T) { +func TestHelmPrePull(t *testing.T) { + testHelmPrePull(t, false) +} + +func TestHelmPrePullOci(t *testing.T) { + testHelmPrePull(t, true) +} + +func testHelmManualUpgrade(t *testing.T, oci bool) { t.Parallel() k := defaultCluster1 @@ -173,10 +239,10 @@ func TestHelmManualUpgrade(t *testing.T) { createNamespace(t, k, p.testSlug()) - repoUrl := createHelmRepo(p.t, []repoChart{ + repoUrl := createHelmOrOciRepo(p.t, []repoChart{ {chartName: "test-chart1", version: "0.1.0"}, {chartName: "test-chart1", version: "0.2.0"}, - }) + }, oci) p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) @@ -200,7 +266,15 @@ func TestHelmManualUpgrade(t *testing.T) { assert.Equal(t, "0.2.0", v) } -func testHelmUpdate(t *testing.T, upgrade bool, commit bool) { +func TestHelmManualUpgrade(t *testing.T) { + testHelmManualUpgrade(t, false) +} + +func TestHelmManualUpgradeOci(t *testing.T) { + testHelmManualUpgrade(t, true) +} + +func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { t.Parallel() k := defaultCluster1 @@ -210,12 +284,12 @@ func testHelmUpdate(t *testing.T, upgrade bool, commit bool) { createNamespace(t, k, p.testSlug()) - repoUrl := createHelmRepo(p.t, []repoChart{ + repoUrl := createHelmOrOciRepo(p.t, []repoChart{ {chartName: "test-chart1", version: "0.1.0"}, {chartName: "test-chart1", version: "0.2.0"}, {chartName: "test-chart2", version: "0.1.0"}, {chartName: "test-chart2", version: "0.3.0"}, - }) + }, oci) p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) @@ -288,15 +362,27 @@ func testHelmUpdate(t *testing.T, upgrade bool, commit bool) { } func TestHelmUpdate(t *testing.T) { - testHelmUpdate(t, false, false) + testHelmUpdate(t, false, false, false) +} + +func TestHelmUpdateOci(t *testing.T) { + testHelmUpdate(t, true, false, false) } func TestHelmUpdateAndUpgrade(t *testing.T) { - testHelmUpdate(t, true, false) + testHelmUpdate(t, false, true, false) +} + +func TestHelmUpdateAndUpgradeOci(t *testing.T) { + testHelmUpdate(t, true, true, false) } func TestHelmUpdateAndUpgradeAndCommit(t *testing.T) { - testHelmUpdate(t, true, true) + testHelmUpdate(t, false, true, true) +} + +func TestHelmUpdateAndUpgradeAndCommitOci(t *testing.T) { + testHelmUpdate(t, true, true, true) } func TestHelmValues(t *testing.T) { From ea71790483532b9665fa3a81de24d25a8abb556d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 16:24:43 +0100 Subject: [PATCH 0554/2268] feat: Add --interactive/-i mode for helm-update --- cmd/kluctl/commands/cmd_helm_update.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 4dca7ca47..638d4792c 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -21,6 +21,8 @@ type helmUpdateCmd struct { Upgrade bool `group:"misc" help:"Write new versions into helm-chart.yaml and perform helm-pull afterwards"` Commit bool `group:"misc" help:"Create a git commit for every updated chart"` + + Interactive bool `group:"misc" short:"i" help:"Ask for every Helm Chart if it should be upgraded."` } func (cmd *helmUpdateCmd) Help() string { @@ -44,6 +46,7 @@ func (cmd *helmUpdateCmd) Run() error { sem := semaphore.NewWeighted(8) type updatedChart struct { + path string chart *deployment.HelmChart newVersion string oldVersion string @@ -71,6 +74,7 @@ func (cmd *helmUpdateCmd) Run() error { if !chart.Config.SkipUpdate && updated { updatedCharts = append(updatedCharts, &updatedChart{ + path: p, chart: chart, newVersion: newVersion, oldVersion: *chart.Config.ChartVersion, @@ -90,6 +94,10 @@ func (cmd *helmUpdateCmd) Run() error { return errs.ErrorOrNil() } + if cmd.Interactive { + sem = semaphore.NewWeighted(1) + } + for _, uc := range updatedCharts { uc := uc @@ -97,6 +105,14 @@ func (cmd *helmUpdateCmd) Run() error { utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, func() error { defer wg.Done() + if cmd.Interactive { + statusPrefix, _ := filepath.Rel(gitRootPath, filepath.Dir(uc.path)) + chartName, _ := uc.chart.GetChartName() + if !status.AskForConfirmation(cliCtx, fmt.Sprintf("%s: Do you want to upgrade Chart %s from version %s to %s?", statusPrefix, chartName, uc.oldVersion, uc.newVersion)) { + return nil + } + } + err := cmd.pullAndCommitChart(gitRootPath, uc.chart, uc.oldVersion, uc.newVersion, &mutex) if err != nil { return err From 73e65fff1cbafefafdd5bb77a601fd9005d0fd18 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 16:43:21 +0100 Subject: [PATCH 0555/2268] refactor: Put rendering of Helm charts into own function --- pkg/deployment/deployment_collection.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 0a379cffa..d8449cd77 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -131,10 +131,17 @@ func (c *DeploymentCollection) RenderDeployments() error { return utils.NewErrorListOrNil(errors) } s.Success() + return nil +} - s = status.Start(c.ctx.Ctx, "Rendering Helm Charts") +func (c *DeploymentCollection) renderHelmCharts() error { + s := status.Start(c.ctx.Ctx, "Rendering Helm Charts") defer s.Failed() + var wg sync.WaitGroup + var mutex sync.Mutex + var errors []error + for _, d := range c.Deployments { d := d wg.Add(1) @@ -271,6 +278,10 @@ func (c *DeploymentCollection) Prepare() error { if err != nil { return err } + err = c.renderHelmCharts() + if err != nil { + return err + } err = c.resolveSealedSecrets() if err != nil { return err From f34daed0c744b93f310f8101641ca1fd5555de11 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 16:47:09 +0100 Subject: [PATCH 0556/2268] feat: Allow to pass Helm credentials in all relevant commands --- cmd/kluctl/args/helm_credentials.go | 17 +++++---- cmd/kluctl/commands/cmd_delete.go | 2 + cmd/kluctl/commands/cmd_deploy.go | 2 + cmd/kluctl/commands/cmd_diff.go | 2 + cmd/kluctl/commands/cmd_helm_pull.go | 8 +--- cmd/kluctl/commands/cmd_helm_update.go | 6 +-- cmd/kluctl/commands/cmd_list_images.go | 2 + cmd/kluctl/commands/cmd_poke_images.go | 2 + cmd/kluctl/commands/cmd_prune.go | 2 + cmd/kluctl/commands/cmd_render.go | 2 + cmd/kluctl/commands/cmd_seal.go | 2 + cmd/kluctl/commands/cmd_validate.go | 2 + cmd/kluctl/commands/utils.go | 2 + pkg/deployment/deployment_item.go | 2 + pkg/deployment/helm_chart.go | 51 ++++++++++++++++++-------- pkg/deployment/shared_context.go | 9 +++-- pkg/kluctl_project/target_context.go | 2 + 17 files changed, 77 insertions(+), 38 deletions(-) diff --git a/cmd/kluctl/args/helm_credentials.go b/cmd/kluctl/args/helm_credentials.go index 47cd25feb..451ff7ef2 100644 --- a/cmd/kluctl/args/helm_credentials.go +++ b/cmd/kluctl/args/helm_credentials.go @@ -8,10 +8,10 @@ import ( ) type HelmCredentials struct { - Username []string `group:"misc" help:"Specify username to use for Helm Repository authentication. Must be in the form --username=:, where must match the id specified in the helm-chart.yaml."` - Password []string `group:"misc" help:"Specify password to use for Helm Repository authentication. Must be in the form --password=:, where must match the id specified in the helm-chart.yaml."` - KeyFile []string `group:"misc" help:"Specify client certificate to use for Helm Repository authentication. Must be in the form --key-file=:, where must match the id specified in the helm-chart.yaml."` - InsecureSkipTlsVerify []string `group:"misc" help:"Controls skipping of TLS verification. Must be in the form --insecure-skip-tls-verify=, where must match the id specified in the helm-chart.yaml."` + HelmUsername []string `group:"misc" help:"Specify username to use for Helm Repository authentication. Must be in the form --helm-username=:, where must match the id specified in the helm-chart.yaml."` + HelmPassword []string `group:"misc" help:"Specify password to use for Helm Repository authentication. Must be in the form --helm-password=:, where must match the id specified in the helm-chart.yaml."` + HelmKeyFile []string `group:"misc" help:"Specify client certificate to use for Helm Repository authentication. Must be in the form --helm-key-file=:, where must match the id specified in the helm-chart.yaml."` + HelmInsecureSkipTlsVerify []string `group:"misc" help:"Controls skipping of TLS verification. Must be in the form --helm-insecure-skip-tls-verify=, where must match the id specified in the helm-chart.yaml."` } func (c *HelmCredentials) FindCredentials(repoUrl string, credentialsId *string) *repo.Entry { @@ -28,28 +28,29 @@ func (c *HelmCredentials) FindCredentials(repoUrl string, credentialsId *string) } var e repo.Entry - for _, x := range c.Username { + for _, x := range c.HelmUsername { if v, ok := splitIdAndValue(x); ok { e.Username = v } } - for _, x := range c.Password { + for _, x := range c.HelmPassword { if v, ok := splitIdAndValue(x); ok { e.Password = v } } - for _, x := range c.KeyFile { + for _, x := range c.HelmKeyFile { if v, ok := splitIdAndValue(x); ok { e.KeyFile = v } } - for _, x := range c.InsecureSkipTlsVerify { + for _, x := range c.HelmInsecureSkipTlsVerify { if x == *credentialsId { e.InsecureSkipTLSverify = true } } if e != (repo.Entry{}) { + e.URL = repoUrl return &e } } diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 368ccd554..ecfc97814 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -20,6 +20,7 @@ type deleteCmd struct { args.ArgsFlags args.ImageFlags args.InclusionFlags + args.HelmCredentials args.YesFlags args.DryRunFlags args.OutputFormatFlags @@ -43,6 +44,7 @@ func (cmd *deleteCmd) Run() error { argsFlags: cmd.ArgsFlags, imageFlags: cmd.ImageFlags, inclusionFlags: cmd.InclusionFlags, + helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 0e57f6e75..9b62a7aa8 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -14,6 +14,7 @@ type deployCmd struct { args.ArgsFlags args.ImageFlags args.InclusionFlags + args.HelmCredentials args.YesFlags args.DryRunFlags args.ForceApplyFlags @@ -40,6 +41,7 @@ func (cmd *deployCmd) Run() error { argsFlags: cmd.ArgsFlags, imageFlags: cmd.ImageFlags, inclusionFlags: cmd.InclusionFlags, + helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index bc6fcc211..d18becfe5 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -12,6 +12,7 @@ type diffCmd struct { args.ArgsFlags args.InclusionFlags args.ImageFlags + args.HelmCredentials args.ForceApplyFlags args.ReplaceOnErrorFlags args.IgnoreFlags @@ -33,6 +34,7 @@ func (cmd *diffCmd) Run() error { argsFlags: cmd.ArgsFlags, imageFlags: cmd.ImageFlags, inclusionFlags: cmd.InclusionFlags, + helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index c4f9ff8f4..42e5da324 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -90,6 +90,8 @@ func doPull(statusPrefix string, p string, helmCredentials args.HelmCredentials, return doError(err) } + chart.SetCredentials(&helmCredentials) + chartName, err := chart.GetChartName() if err != nil { return doError(err) @@ -97,12 +99,6 @@ func doPull(statusPrefix string, p string, helmCredentials args.HelmCredentials, s.Update("%s: Pulling Chart %s with version %s", statusPrefix, chartName, *chart.Config.ChartVersion) - creds := helmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) - if chart.Config.CredentialsId != nil && creds == nil { - return doError(fmt.Errorf("no credentials provided")) - } - chart.SetCredentials(creds) - err = chart.Pull(cliCtx) if err != nil { return doError(err) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 638d4792c..10c8a412c 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -146,11 +146,7 @@ func (cmd *helmUpdateCmd) doCheckUpdate(gitRootPath string, p string) (*deployme return doError(err) } - creds := cmd.HelmCredentials.FindCredentials(*chart.Config.Repo, chart.Config.CredentialsId) - if chart.Config.CredentialsId != nil && creds == nil { - return doError(fmt.Errorf("no credentials provided")) - } - chart.SetCredentials(creds) + chart.SetCredentials(&cmd.HelmCredentials) newVersion, updated, err := chart.CheckUpdate(cliCtx) if err != nil { diff --git a/cmd/kluctl/commands/cmd_list_images.go b/cmd/kluctl/commands/cmd_list_images.go index 4ec7d3543..371310ed1 100644 --- a/cmd/kluctl/commands/cmd_list_images.go +++ b/cmd/kluctl/commands/cmd_list_images.go @@ -11,6 +11,7 @@ type listImagesCmd struct { args.ArgsFlags args.ImageFlags args.InclusionFlags + args.HelmCredentials args.OutputFlags args.RenderOutputDirFlags @@ -32,6 +33,7 @@ func (cmd *listImagesCmd) Run() error { argsFlags: cmd.ArgsFlags, imageFlags: cmd.ImageFlags, inclusionFlags: cmd.InclusionFlags, + helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, offlineKubernetes: cmd.OfflineKubernetes, } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index e08e152c8..b28c2f369 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -13,6 +13,7 @@ type pokeImagesCmd struct { args.ArgsFlags args.ImageFlags args.InclusionFlags + args.HelmCredentials args.YesFlags args.DryRunFlags args.OutputFormatFlags @@ -32,6 +33,7 @@ func (cmd *pokeImagesCmd) Run() error { argsFlags: cmd.ArgsFlags, imageFlags: cmd.ImageFlags, inclusionFlags: cmd.InclusionFlags, + helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 3610aed88..a9197b22f 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -12,6 +12,7 @@ type pruneCmd struct { args.ArgsFlags args.ImageFlags args.InclusionFlags + args.HelmCredentials args.YesFlags args.DryRunFlags args.OutputFormatFlags @@ -33,6 +34,7 @@ func (cmd *pruneCmd) Run() error { argsFlags: cmd.ArgsFlags, imageFlags: cmd.ImageFlags, inclusionFlags: cmd.InclusionFlags, + helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 52327efdd..f3fcf8b5c 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -14,6 +14,7 @@ type renderCmd struct { args.TargetFlags args.ArgsFlags args.ImageFlags + args.HelmCredentials args.RenderOutputDirFlags OfflineKubernetes bool `group:"misc" help:"Run render in offline mode, meaning that it will not try to connect the target cluster"` @@ -41,6 +42,7 @@ func (cmd *renderCmd) Run() error { targetFlags: cmd.TargetFlags, argsFlags: cmd.ArgsFlags, imageFlags: cmd.ImageFlags, + helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, offlineKubernetes: cmd.OfflineKubernetes, } diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index e2388b317..e1b2dce24 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -17,6 +17,7 @@ import ( type sealCmd struct { args.ProjectFlags args.TargetFlags + args.HelmCredentials ForceReseal bool `group:"misc" help:"Lets kluctl ignore secret hashes found in already sealed secrets and thus forces resealing of those."` CertFile string `group:"misc" help:"Use the given certificate for sealing instead of requesting it from the sealed-secrets controller"` @@ -44,6 +45,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, + helmCredentials: cmd.HelmCredentials, forSeal: true, offlineKubernetes: cmd.OfflineKubernetes, } diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index b34d90a0d..4558cd76f 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -13,6 +13,7 @@ type validateCmd struct { args.TargetFlags args.ArgsFlags args.InclusionFlags + args.HelmCredentials args.OutputFlags args.RenderOutputDirFlags @@ -33,6 +34,7 @@ func (cmd *validateCmd) Run() error { targetFlags: cmd.TargetFlags, argsFlags: cmd.ArgsFlags, inclusionFlags: cmd.InclusionFlags, + helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index cd67132ad..eab25b232 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -85,6 +85,7 @@ type projectTargetCommandArgs struct { argsFlags args.ArgsFlags imageFlags args.ImageFlags inclusionFlags args.InclusionFlags + helmCredentials args.HelmCredentials dryRunArgs *args.DryRunFlags renderOutputDirFlags args.RenderOutputDirFlags @@ -162,6 +163,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm ForSeal: args.forSeal, Images: images, Inclusion: inclusion, + HelmCredentials: &args.helmCredentials, RenderOutputDir: renderOutputDir, } diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 69cdb586f..084035ba4 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -198,6 +198,8 @@ func (di *DeploymentItem) renderHelmCharts() error { return err } + chart.SetCredentials(di.ctx.HelmCredentials) + ky, err := di.readKustomizationYaml(subDir) if err == nil && ky != nil { resources, _, _ := ky.GetNestedStringList("resources") diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index fb3b53926..214a1ed1c 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -35,10 +35,15 @@ import ( "time" ) +type HelmCredentialsProvider interface { + FindCredentials(repoUrl string, credentialsId *string) *repo.Entry +} + type HelmChart struct { - ConfigFile string - Config *types.HelmChartConfig - credentials *repo.Entry + ConfigFile string + Config *types.HelmChartConfig + + credentials HelmCredentialsProvider } func NewHelmChart(configFile string) (*HelmChart, error) { @@ -207,14 +212,21 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { a.DestDir = tmpDir a.Version = *c.Config.ChartVersion - if c.credentials != nil { - a.Username = c.credentials.Username - a.Password = c.credentials.Password - a.CertFile = c.credentials.CertFile - a.CaFile = c.credentials.CAFile - a.KeyFile = c.credentials.KeyFile - a.InsecureSkipTLSverify = c.credentials.InsecureSkipTLSverify - a.PassCredentialsAll = c.credentials.PassCredentialsAll + if c.Config.CredentialsId != nil { + if c.credentials == nil { + return fmt.Errorf("no credentials provider") + } + creds := c.credentials.FindCredentials(*c.Config.Repo, c.Config.CredentialsId) + if creds == nil { + return fmt.Errorf("no credentials provided for Chart %s", chartName) + } + a.Username = creds.Username + a.Password = creds.Password + a.CertFile = creds.CertFile + a.CaFile = creds.CAFile + a.KeyFile = creds.KeyFile + a.InsecureSkipTLSverify = creds.InsecureSkipTLSverify + a.PassCredentialsAll = creds.PassCredentialsAll } var out string @@ -278,8 +290,17 @@ func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { var latestVersion string settings := cli.New() - e := c.credentials - if e == nil { + + var e *repo.Entry + if c.Config.CredentialsId != nil { + if c.credentials == nil { + return "", false, fmt.Errorf("no credentials provider") + } + e = c.credentials.FindCredentials(*c.Config.Repo, c.Config.CredentialsId) + if e == nil { + return "", false, fmt.Errorf("no credentials provided for Chart %s", chartName) + } + } else { e = &repo.Entry{ URL: *c.Config.Repo, } @@ -515,8 +536,8 @@ func (c *HelmChart) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, return parsed, nil } -func (c *HelmChart) SetCredentials(credentials *repo.Entry) { - c.credentials = credentials +func (c *HelmChart) SetCredentials(p HelmCredentialsProvider) { + c.credentials = p } func checkIfInstallable(ch *chart.Chart) error { diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index 3d4e3ca50..4851cbcd9 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -8,10 +8,11 @@ import ( ) type SharedContext struct { - Ctx context.Context - K *k8s.K8sCluster - RP *repocache.GitRepoCache - VarsLoader *vars.VarsLoader + Ctx context.Context + K *k8s.K8sCluster + RP *repocache.GitRepoCache + VarsLoader *vars.VarsLoader + HelmCredentials HelmCredentialsProvider RenderDir string SealedSecretsDir string diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 3a47c0642..9af4f6c1c 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -36,6 +36,7 @@ type TargetContextParams struct { ForSeal bool Images *deployment.Images Inclusion *utils.Inclusion + HelmCredentials deployment.HelmCredentialsProvider RenderOutputDir string } @@ -104,6 +105,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe K: k, RP: p.RP, VarsLoader: varsLoader, + HelmCredentials: params.HelmCredentials, RenderDir: params.RenderOutputDir, SealedSecretsDir: p.sealedSecretsDir, DefaultSealedSecretsOutputPattern: target.Name, From 155fc8e94670a9942abda9c8de52c034ec06a424 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 16:25:44 +0100 Subject: [PATCH 0557/2268] docs: Run replace-commands-help --- docs/reference/commands/delete.md | 33 ++++++++++++---- docs/reference/commands/deploy.md | 54 ++++++++++++++++++-------- docs/reference/commands/diff.md | 43 +++++++++++++------- docs/reference/commands/helm-update.md | 34 +++++++++------- docs/reference/commands/list-images.md | 27 ++++++++++--- docs/reference/commands/poke-images.md | 31 +++++++++++---- docs/reference/commands/prune.md | 31 +++++++++++---- docs/reference/commands/render.md | 25 +++++++++--- docs/reference/commands/validate.md | 27 ++++++++++--- 9 files changed, 221 insertions(+), 84 deletions(-) diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index 7dd91db7c..264e4c553 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -34,14 +34,31 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - -l, --delete-by-label stringArray Override the labels used to find objects for deletion. - --dry-run Performs all kubernetes API calls in dry-run mode. - -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format - can either be 'text' or 'yaml'. Can be specified multiple times. The actual - format for yaml is currently not documented and subject to change. - --render-output-dir string Specifies the target directory to render the project into. If omitted, a - temporary directory is used. - -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + -l, --delete-by-label stringArray Override the labels used to find objects for deletion. + --dry-run Performs all kubernetes API calls in dry-run mode. + --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --helm-insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --helm-key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --helm-key-file=:, where + must match the id specified in the helm-chart.yaml. + --helm-password stringArray Specify password to use for Helm Repository authentication. + Must be in the form + --helm-password=:, where + must match the id specified in the helm-chart.yaml. + --helm-username stringArray Specify username to use for Helm Repository authentication. + Must be in the form + --helm-username=:, where + must match the id specified in the helm-chart.yaml. + -o, --output-format stringArray Specify output format and target file, in the format + 'format=path'. Format can either be 'text' or 'yaml'. Can be + specified multiple times. The actual format for yaml is + currently not documented and subject to change. + --render-output-dir string Specifies the target directory to render the project into. If + omitted, a temporary directory is used. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you + would answer 'yes'. ``` diff --git a/docs/reference/commands/deploy.md b/docs/reference/commands/deploy.md index 73ce93477..044715d7b 100644 --- a/docs/reference/commands/deploy.md +++ b/docs/reference/commands/deploy.md @@ -32,23 +32,43 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - --abort-on-error Abort deploying when an error occurs instead of trying the remaining deployments - --dry-run Performs all kubernetes API calls in dry-run mode. - --force-apply Force conflict resolution when applying. See documentation for details - --force-replace-on-error Same as --replace-on-error, but also try to delete and re-create objects. See - documentation for more details. - --no-wait Don't wait for objects readiness' - -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format - can either be 'text' or 'yaml'. Can be specified multiple times. The actual - format for yaml is currently not documented and subject to change. - --readiness-timeout duration Maximum time to wait for object readiness. The timeout is meant per-object. - Timeouts are in the duration format (1s, 1m, 1h, ...). If not specified, a - default timeout of 5m is used. (default 5m0s) - --render-output-dir string Specifies the target directory to render the project into. If omitted, a - temporary directory is used. - --replace-on-error When patching an object fails, try to replace it. See documentation for more - details. - -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + --abort-on-error Abort deploying when an error occurs instead of trying the + remaining deployments + --dry-run Performs all kubernetes API calls in dry-run mode. + --force-apply Force conflict resolution when applying. See documentation for + details + --force-replace-on-error Same as --replace-on-error, but also try to delete and + re-create objects. See documentation for more details. + --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --helm-insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --helm-key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --helm-key-file=:, where + must match the id specified in the helm-chart.yaml. + --helm-password stringArray Specify password to use for Helm Repository authentication. + Must be in the form + --helm-password=:, where + must match the id specified in the helm-chart.yaml. + --helm-username stringArray Specify username to use for Helm Repository authentication. + Must be in the form + --helm-username=:, where + must match the id specified in the helm-chart.yaml. + --no-wait Don't wait for objects readiness' + -o, --output-format stringArray Specify output format and target file, in the format + 'format=path'. Format can either be 'text' or 'yaml'. Can be + specified multiple times. The actual format for yaml is + currently not documented and subject to change. + --readiness-timeout duration Maximum time to wait for object readiness. The timeout is + meant per-object. Timeouts are in the duration format (1s, 1m, + 1h, ...). If not specified, a default timeout of 5m is used. + (default 5m0s) + --render-output-dir string Specifies the target directory to render the project into. If + omitted, a temporary directory is used. + --replace-on-error When patching an object fails, try to replace it. See + documentation for more details. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you + would answer 'yes'. ``` diff --git a/docs/reference/commands/diff.md b/docs/reference/commands/diff.md index 320833839..bb047a7c4 100644 --- a/docs/reference/commands/diff.md +++ b/docs/reference/commands/diff.md @@ -33,19 +33,36 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - --force-apply Force conflict resolution when applying. See documentation for details - --force-replace-on-error Same as --replace-on-error, but also try to delete and re-create objects. See - documentation for more details. - --ignore-annotations Ignores changes in annotations when diffing - --ignore-labels Ignores changes in labels when diffing - --ignore-tags Ignores changes in tags when diffing - -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can - either be 'text' or 'yaml'. Can be specified multiple times. The actual format - for yaml is currently not documented and subject to change. - --render-output-dir string Specifies the target directory to render the project into. If omitted, a - temporary directory is used. - --replace-on-error When patching an object fails, try to replace it. See documentation for more - details. + --force-apply Force conflict resolution when applying. See documentation for + details + --force-replace-on-error Same as --replace-on-error, but also try to delete and + re-create objects. See documentation for more details. + --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --helm-insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --helm-key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --helm-key-file=:, where + must match the id specified in the helm-chart.yaml. + --helm-password stringArray Specify password to use for Helm Repository authentication. + Must be in the form + --helm-password=:, where + must match the id specified in the helm-chart.yaml. + --helm-username stringArray Specify username to use for Helm Repository authentication. + Must be in the form + --helm-username=:, where + must match the id specified in the helm-chart.yaml. + --ignore-annotations Ignores changes in annotations when diffing + --ignore-labels Ignores changes in labels when diffing + --ignore-tags Ignores changes in tags when diffing + -o, --output-format stringArray Specify output format and target file, in the format + 'format=path'. Format can either be 'text' or 'yaml'. Can be + specified multiple times. The actual format for yaml is + currently not documented and subject to change. + --render-output-dir string Specifies the target directory to render the project into. If + omitted, a temporary directory is used. + --replace-on-error When patching an object fails, try to replace it. See + documentation for more details. ``` diff --git a/docs/reference/commands/helm-update.md b/docs/reference/commands/helm-update.md index f9e679928..0e671b5aa 100644 --- a/docs/reference/commands/helm-update.md +++ b/docs/reference/commands/helm-update.md @@ -28,21 +28,25 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - --commit Create a git commit for every updated chart - --insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form - --insecure-skip-tls-verify=, where - must match the id specified in the helm-chart.yaml. - --key-file stringArray Specify client certificate to use for Helm Repository - authentication. Must be in the form - --key-file=:, where must match - the id specified in the helm-chart.yaml. - --password stringArray Specify password to use for Helm Repository authentication. Must be - in the form --password=:, where - must match the id specified in the helm-chart.yaml. - --upgrade Write new versions into helm-chart.yaml and perform helm-pull afterwards - --username stringArray Specify username to use for Helm Repository authentication. Must be - in the form --username=:, where - must match the id specified in the helm-chart.yaml. + --commit Create a git commit for every updated chart + --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --helm-insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --helm-key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --helm-key-file=:, where + must match the id specified in the helm-chart.yaml. + --helm-password stringArray Specify password to use for Helm Repository authentication. + Must be in the form + --helm-password=:, where + must match the id specified in the helm-chart.yaml. + --helm-username stringArray Specify username to use for Helm Repository authentication. + Must be in the form + --helm-username=:, where + must match the id specified in the helm-chart.yaml. + -i, --interactive Ask for every Helm Chart if it should be upgraded. + --upgrade Write new versions into helm-chart.yaml and perform helm-pull + afterwards ``` \ No newline at end of file diff --git a/docs/reference/commands/list-images.md b/docs/reference/commands/list-images.md index e4f6f7e2f..e90fe6624 100644 --- a/docs/reference/commands/list-images.md +++ b/docs/reference/commands/list-images.md @@ -33,12 +33,27 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - --offline-kubernetes Run list-images in offline mode, meaning that it will not try to connect the - target cluster - -o, --output stringArray Specify output target file. Can be specified multiple times - --render-output-dir string Specifies the target directory to render the project into. If omitted, a - temporary directory is used. - --simple Output a simplified version of the images list + --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --helm-insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --helm-key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --helm-key-file=:, where + must match the id specified in the helm-chart.yaml. + --helm-password stringArray Specify password to use for Helm Repository authentication. + Must be in the form + --helm-password=:, where + must match the id specified in the helm-chart.yaml. + --helm-username stringArray Specify username to use for Helm Repository authentication. + Must be in the form + --helm-username=:, where + must match the id specified in the helm-chart.yaml. + --offline-kubernetes Run list-images in offline mode, meaning that it will not try + to connect the target cluster + -o, --output stringArray Specify output target file. Can be specified multiple times + --render-output-dir string Specifies the target directory to render the project into. If + omitted, a temporary directory is used. + --simple Output a simplified version of the images list ``` diff --git a/docs/reference/commands/poke-images.md b/docs/reference/commands/poke-images.md index 8c3ba5ca8..4d16e9589 100644 --- a/docs/reference/commands/poke-images.md +++ b/docs/reference/commands/poke-images.md @@ -32,13 +32,30 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - --dry-run Performs all kubernetes API calls in dry-run mode. - -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can - either be 'text' or 'yaml'. Can be specified multiple times. The actual format - for yaml is currently not documented and subject to change. - --render-output-dir string Specifies the target directory to render the project into. If omitted, a - temporary directory is used. - -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + --dry-run Performs all kubernetes API calls in dry-run mode. + --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --helm-insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --helm-key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --helm-key-file=:, where + must match the id specified in the helm-chart.yaml. + --helm-password stringArray Specify password to use for Helm Repository authentication. + Must be in the form + --helm-password=:, where + must match the id specified in the helm-chart.yaml. + --helm-username stringArray Specify username to use for Helm Repository authentication. + Must be in the form + --helm-username=:, where + must match the id specified in the helm-chart.yaml. + -o, --output-format stringArray Specify output format and target file, in the format + 'format=path'. Format can either be 'text' or 'yaml'. Can be + specified multiple times. The actual format for yaml is + currently not documented and subject to change. + --render-output-dir string Specifies the target directory to render the project into. If + omitted, a temporary directory is used. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you + would answer 'yes'. ``` \ No newline at end of file diff --git a/docs/reference/commands/prune.md b/docs/reference/commands/prune.md index c33800f8c..bef1d7c4b 100644 --- a/docs/reference/commands/prune.md +++ b/docs/reference/commands/prune.md @@ -28,13 +28,30 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - --dry-run Performs all kubernetes API calls in dry-run mode. - -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can - either be 'text' or 'yaml'. Can be specified multiple times. The actual format - for yaml is currently not documented and subject to change. - --render-output-dir string Specifies the target directory to render the project into. If omitted, a - temporary directory is used. - -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + --dry-run Performs all kubernetes API calls in dry-run mode. + --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --helm-insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --helm-key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --helm-key-file=:, where + must match the id specified in the helm-chart.yaml. + --helm-password stringArray Specify password to use for Helm Repository authentication. + Must be in the form + --helm-password=:, where + must match the id specified in the helm-chart.yaml. + --helm-username stringArray Specify username to use for Helm Repository authentication. + Must be in the form + --helm-username=:, where + must match the id specified in the helm-chart.yaml. + -o, --output-format stringArray Specify output format and target file, in the format + 'format=path'. Format can either be 'text' or 'yaml'. Can be + specified multiple times. The actual format for yaml is + currently not documented and subject to change. + --render-output-dir string Specifies the target directory to render the project into. If + omitted, a temporary directory is used. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you + would answer 'yes'. ``` diff --git a/docs/reference/commands/render.md b/docs/reference/commands/render.md index 2fb25364c..f27882a06 100644 --- a/docs/reference/commands/render.md +++ b/docs/reference/commands/render.md @@ -30,11 +30,26 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - --offline-kubernetes Run render in offline mode, meaning that it will not try to connect the target - cluster - --print-all Write all rendered manifests to stdout - --render-output-dir string Specifies the target directory to render the project into. If omitted, a - temporary directory is used. + --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --helm-insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --helm-key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --helm-key-file=:, where + must match the id specified in the helm-chart.yaml. + --helm-password stringArray Specify password to use for Helm Repository authentication. + Must be in the form + --helm-password=:, where + must match the id specified in the helm-chart.yaml. + --helm-username stringArray Specify username to use for Helm Repository authentication. + Must be in the form + --helm-username=:, where + must match the id specified in the helm-chart.yaml. + --offline-kubernetes Run render in offline mode, meaning that it will not try to + connect the target cluster + --print-all Write all rendered manifests to stdout + --render-output-dir string Specifies the target directory to render the project into. If + omitted, a temporary directory is used. ``` \ No newline at end of file diff --git a/docs/reference/commands/validate.md b/docs/reference/commands/validate.md index 987592774..77a0abb98 100644 --- a/docs/reference/commands/validate.md +++ b/docs/reference/commands/validate.md @@ -31,12 +31,27 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - -o, --output stringArray Specify output target file. Can be specified multiple times - --render-output-dir string Specifies the target directory to render the project into. If omitted, a - temporary directory is used. - --sleep duration Sleep duration between validation attempts (default 5s) - --wait duration Wait for the given amount of time until the deployment validates - --warnings-as-errors Consider warnings as failures + --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form + --helm-insecure-skip-tls-verify=, where + must match the id specified in the helm-chart.yaml. + --helm-key-file stringArray Specify client certificate to use for Helm Repository + authentication. Must be in the form + --helm-key-file=:, where + must match the id specified in the helm-chart.yaml. + --helm-password stringArray Specify password to use for Helm Repository authentication. + Must be in the form + --helm-password=:, where + must match the id specified in the helm-chart.yaml. + --helm-username stringArray Specify username to use for Helm Repository authentication. + Must be in the form + --helm-username=:, where + must match the id specified in the helm-chart.yaml. + -o, --output stringArray Specify output target file. Can be specified multiple times + --render-output-dir string Specifies the target directory to render the project into. If + omitted, a temporary directory is used. + --sleep duration Sleep duration between validation attempts (default 5s) + --wait duration Wait for the given amount of time until the deployment validates + --warnings-as-errors Consider warnings as failures ``` From bdff6953602d92e53a1ca890de9f64d878e5c809 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 21:58:28 +0100 Subject: [PATCH 0558/2268] fix: Fix panic in case of empty env vars --- pkg/utils/env.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/env.go b/pkg/utils/env.go index b78f7dd39..a4b2845aa 100644 --- a/pkg/utils/env.go +++ b/pkg/utils/env.go @@ -42,7 +42,7 @@ func parseEnv(prefix string, withIndex bool, withSuffix bool) map[int]map[string for _, e := range os.Environ() { eq := strings.Index(e, "=") if eq == -1 { - panic(fmt.Sprintf("unexpected env var %s", e)) + continue } n := e[:eq] v := e[eq+1:] From b07ae7bb6f13e1e5d123ff432ba1a0235870acd4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 21:59:09 +0100 Subject: [PATCH 0559/2268] tests: Use fresh base tmp dir for each test --- e2e/project.go | 1 + pkg/utils/utils.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/e2e/project.go b/e2e/project.go index 41fd915eb..02e0b0135 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -347,6 +347,7 @@ func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { // this will cause the init() function from call_kluctl_hack.go to invoke the kluctl root command and then exit env = append(env, "CALL_KLUCTL=true") + env = append(env, fmt.Sprintf("KLUCTL_BASE_TMP_DIR=%s", p.t.TempDir())) p.t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index b28b992eb..8796755a9 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -25,6 +25,12 @@ func GetTmpBaseDir() string { } func createTmpBaseDir() { + envTmpDir := os.Getenv("KLUCTL_BASE_TMP_DIR") + if envTmpDir != "" { + tmpBaseDir = envTmpDir + return + } + dir := filepath.Join(os.TempDir(), "kluctl-workdir") ensureDir(dir, 0o777, true) From d6e2d34ecadd10ff352af59f4c5852610c31e2d6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 21:59:28 +0100 Subject: [PATCH 0560/2268] tests: Fix testSlug() for sub tests --- e2e/project.go | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e/project.go b/e2e/project.go index 02e0b0135..bcf57d051 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -54,6 +54,7 @@ func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster) { func (p *testProject) testSlug() string { n := p.t.Name() n = xstrings.ToKebabCase(n) + n = strings.ReplaceAll(n, "/", "-") return n } From 90b3762ebcd44e19dc7ada9eda7554709d5dd177 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 22:09:46 +0100 Subject: [PATCH 0561/2268] refactor: Pre-calculate chartName and chartDir --- cmd/kluctl/commands/cmd_helm_pull.go | 7 +- cmd/kluctl/commands/cmd_helm_update.go | 13 +--- pkg/deployment/helm_chart.go | 103 ++++++++++--------------- 3 files changed, 45 insertions(+), 78 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 42e5da324..bc55d6865 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -92,12 +92,7 @@ func doPull(statusPrefix string, p string, helmCredentials args.HelmCredentials, chart.SetCredentials(&helmCredentials) - chartName, err := chart.GetChartName() - if err != nil { - return doError(err) - } - - s.Update("%s: Pulling Chart %s with version %s", statusPrefix, chartName, *chart.Config.ChartVersion) + s.Update("%s: Pulling Chart %s with version %s", statusPrefix, chart.GetChartName(), *chart.Config.ChartVersion) err = chart.Pull(cliCtx) if err != nil { diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 10c8a412c..30e4a5626 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -107,8 +107,8 @@ func (cmd *helmUpdateCmd) Run() error { if cmd.Interactive { statusPrefix, _ := filepath.Rel(gitRootPath, filepath.Dir(uc.path)) - chartName, _ := uc.chart.GetChartName() - if !status.AskForConfirmation(cliCtx, fmt.Sprintf("%s: Do you want to upgrade Chart %s from version %s to %s?", statusPrefix, chartName, uc.oldVersion, uc.newVersion)) { + if !status.AskForConfirmation(cliCtx, fmt.Sprintf("%s: Do you want to upgrade Chart %s from version %s to %s?", + statusPrefix, uc.chart.GetChartName(), uc.oldVersion, uc.newVersion)) { return nil } } @@ -181,15 +181,10 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployme return err } - chartsDir, err := chart.GetChartDir() - if err != nil { - return err - } - // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later // know what got deleted oldFiles := map[string]bool{} - err = filepath.WalkDir(chartsDir, func(p string, d fs.DirEntry, err error) error { + err = filepath.WalkDir(chart.GetChartDir(), func(p string, d fs.DirEntry, err error) error { if d.IsDir() { return nil } @@ -216,7 +211,7 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployme } toAdd = append(toAdd, relToGit) - relToGit, err = filepath.Rel(gitRootPath, chartsDir) + relToGit, err = filepath.Rel(gitRootPath, chart.GetChartDir()) if err != nil { return err } diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 214a1ed1c..cf9a62766 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -44,6 +44,9 @@ type HelmChart struct { Config *types.HelmChartConfig credentials HelmCredentialsProvider + + chartName string + chartDir string } func NewHelmChart(configFile string) (*HelmChart, error) { @@ -57,33 +60,37 @@ func NewHelmChart(configFile string) (*HelmChart, error) { ConfigFile: configFile, Config: &config, } - return hc, nil -} -func (c *HelmChart) GetChartName() (string, error) { - if c.Config.Repo != nil && registry.IsOCI(*c.Config.Repo) { - s := strings.Split(*c.Config.Repo, "/") + if hc.Config.Repo != nil && registry.IsOCI(*hc.Config.Repo) { + s := strings.Split(*hc.Config.Repo, "/") chartName := s[len(s)-1] if m, _ := regexp.MatchString(`[a-zA-Z_-]+`, chartName); !m { - return "", fmt.Errorf("invalid oci chart url: %s", *c.Config.Repo) + return nil, fmt.Errorf("invalid oci chart url: %s", *hc.Config.Repo) } - return chartName, nil - } - if c.Config.ChartName == nil { - return "", fmt.Errorf("chartName is missing in helm-chart.yml") + hc.chartName = chartName + } else if hc.Config.ChartName == nil { + return nil, fmt.Errorf("chartName is missing in helm-chart.yml") + } else { + hc.chartName = *hc.Config.ChartName } - return *c.Config.ChartName, nil -} -func (c *HelmChart) GetChartDir() (string, error) { - chartName, err := c.GetChartName() + dir := filepath.Dir(configFile) + chartDir := filepath.Join(dir, "charts") + chartDir, err = securejoin.SecureJoin(chartDir, hc.chartName) if err != nil { - return "", err + return nil, err } + hc.chartDir = chartDir - dir := filepath.Dir(c.ConfigFile) - targetDir := filepath.Join(dir, "charts") - return securejoin.SecureJoin(targetDir, chartName) + return hc, nil +} + +func (c *HelmChart) GetChartName() string { + return c.chartName +} + +func (c *HelmChart) GetChartDir() string { + return c.chartDir } func (c *HelmChart) GetOutputPath() string { @@ -142,27 +149,18 @@ func (c *HelmChart) checkNeedsPull(chartDir string, isTmp bool) (bool, bool, str } func (c *HelmChart) Pull(ctx context.Context) error { - chartDir, err := c.GetChartDir() - if err != nil { - return err - } - return c.doPull(ctx, chartDir) + return c.doPull(ctx, c.chartDir) } func (c *HelmChart) pullTmpChart(ctx context.Context) (string, error) { - chartName, err := c.GetChartName() - if err != nil { - return "", err - } - hash := sha256.New() _, _ = fmt.Fprintf(hash, "%s\n", *c.Config.Repo) - _, _ = fmt.Fprintf(hash, "%s\n", chartName) + _, _ = fmt.Fprintf(hash, "%s\n", c.chartName) _, _ = fmt.Fprintf(hash, "%s\n", *c.Config.ChartVersion) h := hex.EncodeToString(hash.Sum(nil)) tmpDir := filepath.Join(utils.GetTmpBaseDir(), "helm-charts") _ = os.MkdirAll(tmpDir, 0o700) - tmpDir = filepath.Join(tmpDir, fmt.Sprintf("%s-%s", chartName, h)) + tmpDir = filepath.Join(tmpDir, fmt.Sprintf("%s-%s", c.chartName, h)) lockFile := tmpDir + ".lock" lock, err := lockedfile.Create(lockFile) @@ -186,11 +184,6 @@ func (c *HelmChart) pullTmpChart(ctx context.Context) (string, error) { } func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { - chartName, err := c.GetChartName() - if err != nil { - return err - } - _ = os.RemoveAll(chartDir) _ = os.MkdirAll(filepath.Dir(chartDir), 0o700) @@ -200,7 +193,7 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { } defer os.RemoveAll(tmpDir) - tmpChartDir := filepath.Join(tmpDir, chartName) + tmpChartDir := filepath.Join(tmpDir, c.chartName) cfg, err := c.buildHelmConfig(nil) if err != nil { @@ -218,7 +211,7 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { } creds := c.credentials.FindCredentials(*c.Config.Repo, c.Config.CredentialsId) if creds == nil { - return fmt.Errorf("no credentials provided for Chart %s", chartName) + return fmt.Errorf("no credentials provided for Chart %s", c.chartName) } a.Username = creds.Username a.Password = creds.Password @@ -234,7 +227,7 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { out, err = a.Run(*c.Config.Repo) } else { a.RepoURL = *c.Config.Repo - out, err = a.Run(chartName) + out, err = a.Run(c.chartName) } if err != nil { return err @@ -283,10 +276,6 @@ func (c *HelmChart) checkUpdateOciRepo(ctx context.Context) (string, bool, error } func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { - chartName, err := c.GetChartName() - if err != nil { - return "", false, err - } var latestVersion string settings := cli.New() @@ -298,7 +287,7 @@ func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { } e = c.credentials.FindCredentials(*c.Config.Repo, c.Config.CredentialsId) if e == nil { - return "", false, fmt.Errorf("no credentials provided for Chart %s", chartName) + return "", false, fmt.Errorf("no credentials provided for Chart %s", c.chartName) } } else { e = &repo.Entry{ @@ -327,9 +316,9 @@ func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { return "", false, err } - indexEntry, ok := index.Entries[chartName] + indexEntry, ok := index.Entries[c.chartName] if !ok || len(indexEntry) == 0 { - return "", false, fmt.Errorf("helm chart %s not found in repo index", chartName) + return "", false, fmt.Errorf("helm chart %s not found in repo index", c.chartName) } var ls versions.LooseVersionSlice @@ -344,27 +333,15 @@ func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { } func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster) error { - chartName, err := c.GetChartName() + err := c.doRender(ctx, k) if err != nil { - return err - } - err = c.doRender(ctx, k) - if err != nil { - return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", chartName, c.Config.ReleaseName, err) + return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", c.chartName, c.Config.ReleaseName, err) } return nil } func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { - chartName, err := c.GetChartName() - if err != nil { - return err - } - - chartDir, err := c.GetChartDir() - if err != nil { - return err - } + chartDir := c.chartDir needsPull, versionChanged, prePulledVersion, err := c.checkNeedsPull(chartDir, false) if err != nil { @@ -373,13 +350,13 @@ func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { if needsPull { if versionChanged { return fmt.Errorf("pre-pulled Helm Chart %s need to be pulled (call 'kluctl helm-pull'). "+ - "Desired version is %s while pre-pulled version is %s", chartName, *c.Config.ChartVersion, prePulledVersion) + "Desired version is %s while pre-pulled version is %s", c.chartName, *c.Config.ChartVersion, prePulledVersion) } else { status.Warning(ctx, "Warning, need to pull Helm Chart %s with version %s. "+ - "Please consider pre-pulling it with 'kluctl helm-pull'", chartName, *c.Config.ChartVersion) + "Please consider pre-pulling it with 'kluctl helm-pull'", c.chartName, *c.Config.ChartVersion) } - s := status.Start(ctx, "Pulling Helm Chart %s with version %s", chartName, *c.Config.ChartVersion) + s := status.Start(ctx, "Pulling Helm Chart %s with version %s", c.chartName, *c.Config.ChartVersion) defer s.Failed() chartDir, err = c.pullTmpChart(ctx) From 9f9865f81ec8d1b73f35830643caa556169bc5eb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 22:17:49 +0100 Subject: [PATCH 0562/2268] fix: Disable OCI credentials for now --- pkg/deployment/helm_chart.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index cf9a62766..1b53132c0 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -206,6 +206,10 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { a.Version = *c.Config.ChartVersion if c.Config.CredentialsId != nil { + if registry.IsOCI(*c.Config.Repo) { + return fmt.Errorf("OCI charts can currently only be authenticated via registry login and not via cli arguments") + } + if c.credentials == nil { return fmt.Errorf("no credentials provider") } From 78dde16ff1d7508707a6da106a7466067cb0f871 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 22:37:55 +0100 Subject: [PATCH 0563/2268] tests: Add tests for Helm credentials --- e2e/helm_test.go | 189 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 133 insertions(+), 56 deletions(-) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index e4195f7f7..780ea9c2b 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -19,6 +19,7 @@ import ( "helm.sh/helm/v3/pkg/uploader" "net/http" "net/http/httptest" + "net/url" "os" "path/filepath" "sort" @@ -65,7 +66,7 @@ type repoChart struct { version string } -func createHelmRepo(t *testing.T, charts []repoChart) string { +func createHelmRepo(t *testing.T, charts []repoChart, password string) string { tmpDir := t.TempDir() for _, c := range charts { @@ -73,7 +74,18 @@ func createHelmRepo(t *testing.T, charts []repoChart) string { _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) } - s := httptest.NewServer(http.FileServer(http.FS(os.DirFS(tmpDir)))) + fs := http.FileServer(http.FS(os.DirFS(tmpDir))) + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if password != "" { + _, p, ok := r.BasicAuth() + if !ok || p != password { + http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) + return + } + } + fs.ServeHTTP(w, r) + })) + t.Cleanup(s.Close) i, err := repo.IndexDirectory(tmpDir, s.URL) @@ -90,15 +102,30 @@ func createHelmRepo(t *testing.T, charts []repoChart) string { return s.URL } -func createOciRepo(t *testing.T, charts []repoChart) string { +func createOciRepo(t *testing.T, charts []repoChart, password string) string { tmpDir := t.TempDir() ociRegistry := registry.New() - ociServer := httptest.NewServer(ociRegistry) + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if password != "" { + _, p, ok := r.BasicAuth() + if !ok { + w.Header().Add("WWW-Authenticate", "Basic") + http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) + return + } + if !ok || p != password { + http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) + return + } + } + ociRegistry.ServeHTTP(w, r) + })) - t.Cleanup(ociServer.Close) + t.Cleanup(s.Close) - ociUrl := strings.ReplaceAll(ociServer.URL, "http://", "oci://") + ociUrl := strings.ReplaceAll(s.URL, "http://", "oci://") + ociUrl2, _ := url.Parse(ociUrl) var out strings.Builder settings := cli.New() @@ -108,6 +135,20 @@ func createOciRepo(t *testing.T, charts []repoChart) string { Options: []pusher.Option{}, } + var registryClient *registry2.Client + if password != "" { + var err error + registryClient, err = registry2.NewClient() + if err != nil { + t.Fatal(err) + } + err = registryClient.Login(ociUrl2.Host, registry2.LoginOptBasicAuth("test-user", password), registry2.LoginOptInsecure(true)) + if err != nil { + t.Fatal(err) + } + c.Options = append(c.Options, pusher.WithRegistryClient(registryClient)) + } + for _, chart := range charts { tgz := createHelmPackage(t, chart.chartName, chart.version) _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) @@ -118,14 +159,18 @@ func createOciRepo(t *testing.T, charts []repoChart) string { } } + if registryClient != nil { + registryClient.Logout(ociUrl2.Host) + } + return ociUrl } -func createHelmOrOciRepo(t *testing.T, charts []repoChart, oci bool) string { +func createHelmOrOciRepo(t *testing.T, charts []repoChart, oci bool, password string) string { if oci { - return createOciRepo(t, charts) + return createOciRepo(t, charts, password) } else { - return createHelmRepo(t, charts) + return createHelmRepo(t, charts, password) } } @@ -162,35 +207,16 @@ func addHelmDeployment(p *testProject, dir string, repoUrl string, chartName, ve } } -func testHelmNoPrePull(t *testing.T, oci bool) { - t.Parallel() - - k := defaultCluster1 - - p := &testProject{} - p.init(t, k) - - createNamespace(t, k, p.testSlug()) - - repoUrl := createHelmOrOciRepo(p.t, []repoChart{ - {chartName: "test-chart1", version: "0.1.0"}, - }, oci) - - p.updateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) - p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") +type testCase struct { + name string + oci bool + testAuth bool + credsId string + extraArgs []string + expectedError string } -func TestHelmNoPrePull(t *testing.T) { - testHelmNoPrePull(t, false) -} - -func TestHelmNoPrePullOci(t *testing.T) { - testHelmNoPrePull(t, true) -} - -func testHelmPrePull(t *testing.T, oci bool) { +func testHelmPull(t *testing.T, tc testCase, prePull bool) { t.Parallel() k := defaultCluster1 @@ -200,33 +226,84 @@ func testHelmPrePull(t *testing.T, oci bool) { createNamespace(t, k, p.testSlug()) + password := "" + if tc.testAuth { + password = "secret-password" + } + repoUrl := createHelmOrOciRepo(p.t, []repoChart{ {chartName: "test-chart1", version: "0.1.0"}, - {chartName: "test-chart2", version: "0.1.0"}, - }, oci) + }, tc.oci, password) p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) - p.KluctlMust("helm-pull") - assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) - p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") + if tc.testAuth { + if tc.credsId != "" { + p.updateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(tc.credsId, "helmChart", "credentialsId") + return nil + }, "") + } + } - addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.testSlug(), nil) + if prePull { + args := []string{"helm-pull"} + args = append(args, tc.extraArgs...) + + _, stderr, err := p.Kluctl(args...) + if tc.expectedError != "" { + assert.Error(t, err) + assert.Contains(t, stderr, tc.expectedError) + return + } else { + assert.NoError(t, err) + assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) + } + } - p.KluctlMust("helm-pull") - assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm2/charts/test-chart2/Chart.yaml")) - p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.testSlug(), "test-helm2-test-chart2") + args := []string{"deploy", "--yes", "-t", "test"} + args = append(args, tc.extraArgs...) + _, stderr, err := p.Kluctl(args...) + prePullWarning := "Warning, need to pull Helm Chart test-chart1 with version 0.1.0." + if prePull { + assert.NotContains(t, stderr, prePullWarning) + } else { + assert.Contains(t, stderr, prePullWarning) + } + if tc.expectedError != "" { + assert.Error(t, err) + assert.Contains(t, stderr, tc.expectedError) + } else { + assert.NoError(t, err) + assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") + } } -func TestHelmPrePull(t *testing.T) { - testHelmPrePull(t, false) -} +func TestHelmPull(t *testing.T) { + tests := []testCase{ + {name: "helm-no-creds"}, + {name: "helm-creds-missing", oci: false, testAuth: true, credsId: "test-creds", + expectedError: "no credentials provided for Chart test-chart1"}, + {name: "helm-creds-invalid", oci: false, testAuth: true, credsId: "test-creds", + extraArgs: []string{"--helm-username=test-creds:user", "--helm-password=test-creds:invalid"}, + expectedError: "401 Unauthorized"}, + {name: "helm-creds-valid", oci: false, testAuth: true, credsId: "test-creds", + extraArgs: []string{"--helm-username=test-creds:user", "--helm-password=test-creds:secret-password"}}, + {name: "oci", oci: true}, + {name: "oci-creds-fail", oci: true, testAuth: true, credsId: "test-creds", + extraArgs: []string{"--helm-username=test-creds:user", "--helm-password=test-creds:secret-password"}, + expectedError: "OCI charts can currently only be authenticated via registry login and not via cli arguments"}, + } -func TestHelmPrePullOci(t *testing.T) { - testHelmPrePull(t, true) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testHelmPull(t, tc, false) + }) + t.Run(tc.name+"-prepull", func(t *testing.T) { + testHelmPull(t, tc, true) + }) + } } func testHelmManualUpgrade(t *testing.T, oci bool) { @@ -242,7 +319,7 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { repoUrl := createHelmOrOciRepo(p.t, []repoChart{ {chartName: "test-chart1", version: "0.1.0"}, {chartName: "test-chart1", version: "0.2.0"}, - }, oci) + }, oci, "") p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) @@ -289,7 +366,7 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { {chartName: "test-chart1", version: "0.2.0"}, {chartName: "test-chart2", version: "0.1.0"}, {chartName: "test-chart2", version: "0.3.0"}, - }, oci) + }, oci, "") p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) @@ -398,7 +475,7 @@ func TestHelmValues(t *testing.T) { repoUrl := createHelmRepo(p.t, []repoChart{ {chartName: "test-chart1", version: "0.1.0"}, {chartName: "test-chart2", version: "0.1.0"}, - }) + }, "") values1 := map[string]any{ "data": map[string]any{ @@ -463,7 +540,7 @@ func TestHelmTemplateChartYaml(t *testing.T) { repoUrl := createHelmRepo(p.t, []repoChart{ {chartName: "test-chart1", version: "0.1.0"}, {chartName: "test-chart2", version: "0.1.0"}, - }) + }, "") p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm-{{ args.a }}", p.testSlug(), nil) From 46eea9002af77fdd539d9c2470fdd354214102ae Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 22:38:25 +0100 Subject: [PATCH 0564/2268] chore: Run go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9c9e527f2..8110b5859 100644 --- a/go.mod +++ b/go.mod @@ -59,6 +59,7 @@ require ( require ( github.com/go-logr/logr v1.2.3 + github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.3.2 go.mozilla.org/sops/v3 v3.7.3 sigs.k8s.io/controller-runtime v0.13.1 @@ -142,7 +143,6 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.3.1 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.4.5 // indirect github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect From a7f87d0444a56d51c45cd3c7c65250be7eef30c6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 23:29:11 +0100 Subject: [PATCH 0565/2268] ci: Run cross-compile in own workflow and only on main --- .github/workflows/cross-compile.yaml | 36 ++++++++++++++++++++++++++++ .github/workflows/tests.yml | 28 ---------------------- 2 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/cross-compile.yaml diff --git a/.github/workflows/cross-compile.yaml b/.github/workflows/cross-compile.yaml new file mode 100644 index 000000000..556c864d7 --- /dev/null +++ b/.github/workflows/cross-compile.yaml @@ -0,0 +1,36 @@ +name: cross-compile + +on: + push: + branches: + - main + +jobs: + cross-compile: + runs-on: ubuntu-20.04 + if: github.event_name != 'pull_request' + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-go@v3 + with: + go-version: '1.19' + - uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: cross-compile-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} + restore-keys: | + cross-compile-go-${{ runner.os }}- + - name: Build kluctl (linux) + run: | + make build GOARCH=amd64 GOOS=linux + - name: Build kluctl (darwin) + run: | + make build GOARCH=amd64 GOOS=darwin + - name: Build kluctl (windows) + run: | + make build GOARCH=amd64 GOOS=windows diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2731beb35..0b99db18d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,34 +40,6 @@ jobs: exit 1 fi - cross-compile: - runs-on: ubuntu-20.04 - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - uses: actions/setup-go@v3 - with: - go-version: '1.19' - - uses: actions/cache@v3 - with: - path: | - ~/go/pkg/mod - ~/.cache/go-build - key: cross-compile-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - cross-compile-go-${{ runner.os }}- - - name: Build kluctl (linux) - run: | - make build GOARCH=amd64 GOOS=linux - - name: Build kluctl (darwin) - run: | - make build GOARCH=amd64 GOOS=darwin - - name: Build kluctl (windows) - run: | - make build GOARCH=amd64 GOOS=windows - tests: strategy: matrix: From c974123e5d3ec3c2c47008cd90663fdd1cc7c79b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 23:33:52 +0100 Subject: [PATCH 0566/2268] ci: In pull requests, only run e2e tests for linux --- .github/workflows/tests.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0b99db18d..08e319e0b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,11 +45,11 @@ jobs: matrix: include: - os: ubuntu-20.04 - binary-suffix: linux-amd64 + run_on_pull_requests: true - os: macos-11 - binary-suffix: darwin-amd64 + run_on_pull_requests: false - os: windows-2019 - binary-suffix: windows-amd64 + run_on_pull_requests: false os: [ubuntu-20.04, macos-11, windows-2019] fail-fast: false runs-on: ${{ matrix.os }} @@ -67,15 +67,17 @@ jobs: key: tests-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} restore-keys: | tests-go-${{ runner.os }}- - - name: setup-envtest - shell: bash - run: | - make install-envtest - name: Run unit tests shell: bash run: | make test-unit + - name: setup-envtest + if: matrix.run_on_pull_requests || github.event_name != 'pull_request' + shell: bash + run: | + make install-envtest - name: Run e2e tests + if: matrix.run_on_pull_requests || github.event_name != 'pull_request' shell: bash run: | make test-e2e From 6c898b13ad0cbafad02f3d282709c9f732f42f9f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 15 Nov 2022 23:41:24 +0100 Subject: [PATCH 0567/2268] ci: Wipe cache --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 08e319e0b..92f166bb7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,7 +26,7 @@ jobs: ~/.cache/go-build key: docs-check-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} restore-keys: | - docs-check-go-${{ runner.os }}- + docs1-check-go-${{ runner.os }}- - name: Check links on changed files run: | make markdown-link-check @@ -64,9 +64,9 @@ jobs: path: | ~/go/pkg/mod ~/.cache/go-build - key: tests-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} + key: tests1-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} restore-keys: | - tests-go-${{ runner.os }}- + tests1-go-${{ runner.os }}- - name: Run unit tests shell: bash run: | From 603e0716fafee9254e35bfda1e77e98c643419e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 23:02:37 +0000 Subject: [PATCH 0568/2268] chore(deps): Bump golang.org/x/crypto from 0.1.0 to 0.2.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.1.0 to 0.2.0. - [Release notes](https://github.com/golang/crypto/releases) - [Commits](https://github.com/golang/crypto/compare/v0.1.0...v0.2.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8110b5859..30a4da448 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/stretchr/testify v1.8.1 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 - golang.org/x/crypto v0.1.0 + golang.org/x/crypto v0.2.0 golang.org/x/net v0.2.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.2.0 diff --git a/go.sum b/go.sum index c7ca5c92f..28eb9796f 100644 --- a/go.sum +++ b/go.sum @@ -870,8 +870,8 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= +golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 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= From 7028b084e1dd01049a7cc368657e7ab2436bf836 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 16 Nov 2022 20:38:32 +0100 Subject: [PATCH 0569/2268] fix: Fix "invalid cross-device link" errors when pulling charts inside docker --- pkg/deployment/helm_chart.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 1b53132c0..0d9b4d4cb 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -184,10 +184,14 @@ func (c *HelmChart) pullTmpChart(ctx context.Context) (string, error) { } func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { + baseDir := filepath.Dir(chartDir) + _ = os.RemoveAll(chartDir) - _ = os.MkdirAll(filepath.Dir(chartDir), 0o700) + _ = os.MkdirAll(baseDir, 0o700) - tmpDir, err := os.MkdirTemp(utils.GetTmpBaseDir(), "helm-pull-") + // need to use the same filesystem/volume that we later os.Rename the final pull chart to, as otherwise + // the rename operation will lead to errors + tmpDir, err := os.MkdirTemp(baseDir, c.chartName+"-pull-") if err != nil { return err } From f811edec55f506a07d1c158b8594a55c06f74b6d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 16 Nov 2022 21:00:21 +0100 Subject: [PATCH 0570/2268] docs: Fix reference to long removed deleteByLabels --- docs/reference/commands/delete.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index 264e4c553..e67295aab 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -17,7 +17,7 @@ Delete a target (or parts of it) from the corresponding cluster Objects are located based on 'commonLabels', configured in 'deployment.yaml' WARNING: This command will also delete objects which are not part of your deployment -project (anymore). It really only decides based on the 'deleteByLabel' labels and does NOT +project (anymore). It really only decides based on the 'commonLabels' labels and does NOT take the local target/state into account! From 90d5b0f5a86d37e20356e1ee28f2480f46453cd1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 16 Nov 2022 21:04:28 +0100 Subject: [PATCH 0571/2268] docs: Move args documentation into .kluctl.yaml documentation --- docs/reference/commands/delete.md | 2 +- docs/reference/deployments/deployment-yml.md | 45 ------------------ docs/reference/kluctl-project/README.md | 50 +++++++++++++++++++- 3 files changed, 49 insertions(+), 48 deletions(-) diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index e67295aab..264e4c553 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -17,7 +17,7 @@ Delete a target (or parts of it) from the corresponding cluster Objects are located based on 'commonLabels', configured in 'deployment.yaml' WARNING: This command will also delete objects which are not part of your deployment -project (anymore). It really only decides based on the 'commonLabels' labels and does NOT +project (anymore). It really only decides based on the 'deleteByLabel' labels and does NOT take the local target/state into account! diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index 7a0be122b..a8ff6252f 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -24,9 +24,6 @@ deployments: commonLabels: my.prefix/target: "{{ target.name }}" my.prefix/deployment-project: my-deployment-project - -args: -- name: environment ``` The following sub-chapters describe the available fields in the `deployment.yaml` @@ -248,48 +245,6 @@ A list of common tags which are applied to all kustomize deployments and sub-dep See [tags](./tags.md) for more details. -## args -A list of arguments that can or must be passed to most kluctl operations. Each of these arguments is then available -in templating via the global `args` object. Only the root `deployment.yaml` can contain such argument definitions. - -An example looks like this: -```yaml -deployments: - - path: nginx - -args: - - name: environment - - name: enable_debug - default: false - - name: complex_arg - default: - my: - nested1: arg1 - nested2: arg2 -``` - -These arguments can then be used in templating, e.g. by using `{{ args.environment }}`. - -When calling kluctl, most of the commands will then require you to specify at least `-a environment=xxx` and optionally -`-a enable_debug=true` - -The following sub chapters describe the fields for argument entries. - -### name -The name of the argument. - -### default -If specified, the argument becomes optional and will use the given value as default when not specified. - -The default value can be an arbitrary yaml value, meaning that it can also be a nested dictionary. In that case, passing -args in nested form will only set the nested value. With the above example of `complex_arg`, running: - -``` -kluctl deploy -t my-target -a my.nested1=override` -``` - -will only modify the value below `my.nested1` and keep the value of `my.nested2`. - ## ignoreForDiff A list of objects and fields to ignore while performing diffs. Consider the following example: diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index ba14d1b65..e48f33f47 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -35,13 +35,59 @@ targets: context: prod.example.com args: environment_name: prod + +args: + - name: environment ``` ## Allowed fields -Please check the following sub-sections of this section to see which fields are allowed at the root level of `.kluctl.yaml`. +### targets + +Please check the [targets](./targets) sub-section for details. + +### args + +A list of arguments that can or must be passed to most kluctl operations. Each of these arguments is then available +in templating via the global `args` object. + +An example looks like this: +```yaml +targets: +... + +args: + - name: environment + - name: enable_debug + default: false + - name: complex_arg + default: + my: + nested1: arg1 + nested2: arg2 +``` + +These arguments can then be used in templating, e.g. by using `{{ args.environment }}`. + +When calling kluctl, most of the commands will then require you to specify at least `-a environment=xxx` and optionally +`-a enable_debug=true` + +The following sub chapters describe the fields for argument entries. + +#### name +The name of the argument. + +#### default +If specified, the argument becomes optional and will use the given value as default when not specified. + +The default value can be an arbitrary yaml value, meaning that it can also be a nested dictionary. In that case, passing +args in nested form will only set the nested value. With the above example of `complex_arg`, running: + +``` +kluctl deploy -t my-target -a my.nested1=override` +``` -1. [targets](./targets) +will only modify the value below `my.nested1` and keep the value of `my.nested2`. ## Using Kluctl without .kluctl.yaml From 1c8c8abd2247d69096b361b39729c481de617476 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 22:16:53 +0000 Subject: [PATCH 0572/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.122 to 1.44.139 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.122 to 1.44.139. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.122...v1.44.139) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 30a4da448..39c3fbede 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.122 + github.com/aws/aws-sdk-go v1.44.139 github.com/bitnami-labs/sealed-secrets v0.19.1 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index 28eb9796f..97ed5720e 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo= -github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.139 h1:Mj/OZBy9RTbzJ8pfgK6rOL8xgUEAIn8pfIN6qWFtpAk= +github.com/aws/aws-sdk-go v1.44.139/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= @@ -819,6 +819,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= @@ -907,6 +908,7 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -954,6 +956,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -983,6 +987,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1054,14 +1059,18 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1134,6 +1143,7 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 4025358e28d4650f43516896bcfed9e472735bdc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 22:17:31 +0000 Subject: [PATCH 0573/2268] chore(deps): Bump golang.org/x/crypto from 0.2.0 to 0.3.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.2.0 to 0.3.0. - [Release notes](https://github.com/golang/crypto/releases) - [Commits](https://github.com/golang/crypto/compare/v0.2.0...v0.3.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 30a4da448..db5547ad2 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/stretchr/testify v1.8.1 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 - golang.org/x/crypto v0.2.0 + golang.org/x/crypto v0.3.0 golang.org/x/net v0.2.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.2.0 diff --git a/go.sum b/go.sum index 28eb9796f..842a149d9 100644 --- a/go.sum +++ b/go.sum @@ -870,8 +870,8 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= -golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 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= From 1b0bf313ec8b7cbae5b2bf8fea9528b302ce10f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 22:17:37 +0000 Subject: [PATCH 0574/2268] chore(deps): Bump github.com/huandu/xstrings from 1.3.2 to 1.3.3 Bumps [github.com/huandu/xstrings](https://github.com/huandu/xstrings) from 1.3.2 to 1.3.3. - [Release notes](https://github.com/huandu/xstrings/releases) - [Commits](https://github.com/huandu/xstrings/compare/v1.3.2...v1.3.3) --- updated-dependencies: - dependency-name: github.com/huandu/xstrings dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 30a4da448..2d60b5875 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 - github.com/huandu/xstrings v1.3.2 + github.com/huandu/xstrings v1.3.3 go.mozilla.org/sops/v3 v3.7.3 sigs.k8s.io/controller-runtime v0.13.1 ) diff --git a/go.sum b/go.sum index 28eb9796f..a8fae1877 100644 --- a/go.sum +++ b/go.sum @@ -484,8 +484,9 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 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.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= From 47ac7e4a41d63fed3832d2f676550287fb5cbd98 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 17 Nov 2022 11:48:42 +0100 Subject: [PATCH 0575/2268] docs: Make kustomize-deployment2 a simple-deployment --- docs/reference/deployments/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/reference/deployments/README.md b/docs/reference/deployments/README.md index 19145cca4..36c4f42d7 100644 --- a/docs/reference/deployments/README.md +++ b/docs/reference/deployments/README.md @@ -49,7 +49,6 @@ Some visualized files/directories have links attached, follow them to get more i |-- sub-deployment/ | |-- deployment.yaml | |-- kustomize-deployment2/ - | | |-- kustomization.yaml | | |-- resource1.yaml | | `-- ... | |-- kustomize-deployment3/ From 84ca198a97957d6dd58110facd807042cf6308df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 22:10:48 +0000 Subject: [PATCH 0576/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.139 to 1.44.140 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.139 to 1.44.140. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.139...v1.44.140) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c1e9d2c67..a8ff4d321 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.139 + github.com/aws/aws-sdk-go v1.44.140 github.com/bitnami-labs/sealed-secrets v0.19.1 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index b9fdb39d8..b5bd674c7 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.139 h1:Mj/OZBy9RTbzJ8pfgK6rOL8xgUEAIn8pfIN6qWFtpAk= -github.com/aws/aws-sdk-go v1.44.139/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.140 h1:6MxVSiAORc6AG+oh6401TEgWHb1ZzFL8y6+eBLoJtdU= +github.com/aws/aws-sdk-go v1.44.140/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From 0579d250447bfafa0e31a4136351561e60515da2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Nov 2022 22:53:40 +0000 Subject: [PATCH 0577/2268] chore(deps): Bump github.com/bitnami-labs/sealed-secrets Bumps [github.com/bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) from 0.19.1 to 0.19.2. - [Release notes](https://github.com/bitnami-labs/sealed-secrets/releases) - [Changelog](https://github.com/bitnami-labs/sealed-secrets/blob/main/RELEASE-NOTES.md) - [Commits](https://github.com/bitnami-labs/sealed-secrets/compare/v0.19.1...v0.19.2) --- updated-dependencies: - dependency-name: github.com/bitnami-labs/sealed-secrets dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index a8ff4d321..8f2d91554 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/aws/aws-sdk-go v1.44.140 - github.com/bitnami-labs/sealed-secrets v0.19.1 + github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/fluxcd/pkg/kustomize v0.8.0 @@ -193,7 +193,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect diff --git a/go.sum b/go.sum index b5bd674c7..77ae9e7db 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.19.1 h1:ZNLcVtTXRf7VkyNzyhe9omlwNYI0OHDteTbIHsQI7Ug= -github.com/bitnami-labs/sealed-secrets v0.19.1/go.mod h1:5UcsiOdOoviJUtXY1GNSeFa8zezbBY+j9pQAA2RtKyw= +github.com/bitnami-labs/sealed-secrets v0.19.2 h1:0NpJCAhfDTkZ2u3PJaRWSynxlg55ssHxPQNn8T5JNPU= +github.com/bitnami-labs/sealed-secrets v0.19.2/go.mod h1:KjTgHdD22gpjERjT1rA8yi4P4EkccMhRR2JW6Ctfu6g= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -654,8 +654,8 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= +github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls= +github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= @@ -694,8 +694,8 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +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= From 87c683f52efa37b10c2b573162ffc6c10cff0a44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 19 Nov 2022 19:27:14 +0000 Subject: [PATCH 0578/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.140 to 1.44.142 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.140 to 1.44.142. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.140...v1.44.142) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8f2d91554..9b323e838 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.140 + github.com/aws/aws-sdk-go v1.44.142 github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index 77ae9e7db..2b2ef6427 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.140 h1:6MxVSiAORc6AG+oh6401TEgWHb1ZzFL8y6+eBLoJtdU= -github.com/aws/aws-sdk-go v1.44.140/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.142 h1:KZ1/FDwCSft1DuNllFaBtWpcG0CW2NgQjvOrE1TdlXE= +github.com/aws/aws-sdk-go v1.44.142/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From cc88ac875c01265a183e5b0e2e8a07d7287cbbe4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 21 Nov 2022 13:29:38 +0100 Subject: [PATCH 0579/2268] fix: Fix race condition due to use of GoLimitedMultiError --- cmd/kluctl/commands/cmd_helm_pull.go | 4 +--- cmd/kluctl/commands/cmd_helm_update.go | 10 ++-------- pkg/utils/limited_routine.go | 12 +++++++++--- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index bc55d6865..af62259f1 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -52,9 +52,7 @@ func (cmd *helmPullCmd) Run() error { return err } - wg.Add(1) - utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, func() error { - defer wg.Done() + utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, &wg, func() error { s := status.Start(cliCtx, "%s: Pulling Chart", statusPrefix) defer s.Failed() err := doPull(statusPrefix, p, cmd.HelmCredentials, s) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 30e4a5626..7d6fc14f0 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -60,10 +60,7 @@ func (cmd *helmUpdateCmd) Run() error { return nil } - wg.Add(1) - utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, func() error { - defer wg.Done() - + utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, &wg, func() error { chart, newVersion, updated, err := cmd.doCheckUpdate(gitRootPath, p) if err != nil { return err @@ -101,10 +98,7 @@ func (cmd *helmUpdateCmd) Run() error { for _, uc := range updatedCharts { uc := uc - wg.Add(1) - utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, func() error { - defer wg.Done() - + utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, &wg, func() error { if cmd.Interactive { statusPrefix, _ := filepath.Rel(gitRootPath, filepath.Dir(uc.path)) if !status.AskForConfirmation(cliCtx, fmt.Sprintf("%s: Do you want to upgrade Chart %s from version %s to %s?", diff --git a/pkg/utils/limited_routine.go b/pkg/utils/limited_routine.go index 7653bfc26..f38a1bdde 100644 --- a/pkg/utils/limited_routine.go +++ b/pkg/utils/limited_routine.go @@ -7,8 +7,14 @@ import ( "sync" ) -func GoLimited(ctx context.Context, sem *semaphore.Weighted, fn func(), errCb func(err error)) { +func GoLimited(ctx context.Context, sem *semaphore.Weighted, wg *sync.WaitGroup, fn func(), errCb func(err error)) { + if wg != nil { + wg.Add(1) + } go func() { + if wg != nil { + defer wg.Done() + } err := sem.Acquire(ctx, 1) if err != nil { if errCb != nil { @@ -21,13 +27,13 @@ func GoLimited(ctx context.Context, sem *semaphore.Weighted, fn func(), errCb fu }() } -func GoLimitedMultiError(ctx context.Context, sem *semaphore.Weighted, merr **multierror.Error, mutex *sync.Mutex, fn func() error) { +func GoLimitedMultiError(ctx context.Context, sem *semaphore.Weighted, merr **multierror.Error, mutex *sync.Mutex, wg *sync.WaitGroup, fn func() error) { errCb := func(err error) { mutex.Lock() defer mutex.Unlock() *merr = multierror.Append(*merr, err) } - GoLimited(ctx, sem, func() { + GoLimited(ctx, sem, wg, func() { err := fn() if err != nil { errCb(err) From e2c9361f34a2e8d4661fb2ffd00c82907bba796c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Nov 2022 22:48:37 +0000 Subject: [PATCH 0580/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.142 to 1.44.144 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.142 to 1.44.144. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.142...v1.44.144) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9b323e838..57851c7ab 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.142 + github.com/aws/aws-sdk-go v1.44.144 github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index 2b2ef6427..aa745abe8 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.142 h1:KZ1/FDwCSft1DuNllFaBtWpcG0CW2NgQjvOrE1TdlXE= -github.com/aws/aws-sdk-go v1.44.142/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.144 h1:mMWdnYL8HZsobrQe1mwvQ18Xt8UbOVhWgipjuma5Mkg= +github.com/aws/aws-sdk-go v1.44.144/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From ebba705357714fefeeeba390ccb012865924e17e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Nov 2022 22:07:33 +0000 Subject: [PATCH 0581/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.144 to 1.44.145 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.144 to 1.44.145. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.144...v1.44.145) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 57851c7ab..7108d7e82 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.144 + github.com/aws/aws-sdk-go v1.44.145 github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index aa745abe8..89ad58cea 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.144 h1:mMWdnYL8HZsobrQe1mwvQ18Xt8UbOVhWgipjuma5Mkg= -github.com/aws/aws-sdk-go v1.44.144/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.145 h1:KMVRrIyjBsNz3xGPuHIRnhIuKlb5h3Ii5e5jbi3cgnc= +github.com/aws/aws-sdk-go v1.44.145/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From d5ddbfa41f102882146b297a02ad5c26926aad2e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 25 Nov 2022 10:53:45 +0100 Subject: [PATCH 0582/2268] feat: Allow to pass SOPS decrypter from outside This is the preparation for SOPS support in the flux-kluctl-controller --- pkg/deployment/deployment_item.go | 31 +-------------- pkg/deployment/shared_context.go | 2 + pkg/kluctl_project/load.go | 16 +++++--- pkg/kluctl_project/project.go | 6 ++- pkg/kluctl_project/project_load.go | 4 +- pkg/kluctl_project/target_context.go | 3 +- pkg/sops/sops_decryptor.go | 21 +++++++++++ pkg/sops/utils.go | 56 ++++++++++++++++++++++++++++ pkg/vars/vars_loader.go | 27 +++++++------- pkg/vars/vars_loader_test.go | 3 +- pkg/yaml/yaml.go | 4 -- 11 files changed, 116 insertions(+), 57 deletions(-) create mode 100644 pkg/sops/sops_decryptor.go create mode 100644 pkg/sops/utils.go diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 084035ba4..d6d1ad573 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -3,14 +3,12 @@ package deployment import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" - "go.mozilla.org/sops/v3" - "go.mozilla.org/sops/v3/cmd/sops/formats" - "go.mozilla.org/sops/v3/decrypt" "io/fs" "k8s.io/apimachinery/pkg/runtime/schema" "os" @@ -490,31 +488,6 @@ func (di *DeploymentItem) prepareKustomizationYaml() (*uo.UnstructuredObject, er return ky, nil } -func (di *DeploymentItem) decryptSopsFile(p string) error { - file, err := os.ReadFile(p) - if err != nil { - return fmt.Errorf("failed to read file %s: %w", p, err) - } - - if !yaml.IsMaybeSopsFile(file) { - return nil - } - - decrypted, err := decrypt.DataWithFormat(file, formats.FormatForPath(p)) - if err == sops.MetadataNotFound { - // not encrypted, so bail out - return nil - } else if err != nil { - return fmt.Errorf("failed to decrypt file %s: %w", p, err) - } - - err = os.WriteFile(p, decrypted, 0o600) - if err != nil { - return fmt.Errorf("failed to save decrypted file %s: %w", p, err) - } - return nil -} - func (di *DeploymentItem) buildKustomize() error { if di.dir == nil { return nil @@ -535,7 +508,7 @@ func (di *DeploymentItem) buildKustomize() error { for _, r := range resources { p := filepath.Join(di.RenderedDir, r) if utils.IsFile(p) { - err = di.decryptSopsFile(p) + err = sops.MaybeDecryptFile(di.ctx.SopsDecrypter, p) if err != nil { return err } diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index 4851cbcd9..f5334c195 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -4,6 +4,7 @@ import ( "context" "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/vars" ) @@ -11,6 +12,7 @@ type SharedContext struct { Ctx context.Context K *k8s.K8sCluster RP *repocache.GitRepoCache + SopsDecrypter sops.SopsDecrypter VarsLoader *vars.VarsLoader HelmCredentials HelmCredentialsProvider diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index d5d8ec704..d477429b8 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -3,6 +3,7 @@ package kluctl_project import ( "context" "github.com/kluctl/go-jinja2" + "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/status" ) @@ -11,11 +12,16 @@ func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir s defer status.Trace(ctx, "leave LoadKluctlProject") p := &LoadedKluctlProject{ - ctx: ctx, - loadArgs: args, - TmpDir: tmpDir, - J2: j2, - RP: args.RP, + ctx: ctx, + loadArgs: args, + TmpDir: tmpDir, + J2: j2, + RP: args.RP, + SopsDecrypter: args.SopsDecrypter, + } + + if p.SopsDecrypter == nil { + p.SopsDecrypter = &sops.LocalSopsDecrypter{} } err := p.loadKluctlProject() diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 7d36b0872..1701abfc2 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/git/repocache" + "github.com/kluctl/kluctl/v2/pkg/sops" types2 "github.com/kluctl/kluctl/v2/pkg/types" ) @@ -23,8 +24,9 @@ type LoadedKluctlProject struct { Config types2.KluctlProject DynamicTargets []*types2.DynamicTarget - J2 *jinja2.Jinja2 - RP *repocache.GitRepoCache + J2 *jinja2.Jinja2 + RP *repocache.GitRepoCache + SopsDecrypter sops.SopsDecrypter } func (c *LoadedKluctlProject) FindBaseTarget(name string) (*types2.Target, error) { diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index c4a2b20b9..dfe0cbe2d 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -2,6 +2,7 @@ package kluctl_project import ( "github.com/kluctl/kluctl/v2/pkg/git/repocache" + "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -15,7 +16,8 @@ type LoadKluctlProjectArgs struct { ProjectDir string ProjectConfig string - RP *repocache.GitRepoCache + SopsDecrypter sops.SopsDecrypter + RP *repocache.GitRepoCache ClientConfigGetter func(context *string) (*rest.Config, *api.Config, error) } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 9af4f6c1c..952c44d85 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -91,7 +91,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe s.Success() } - varsLoader := vars.NewVarsLoader(ctx, k, p.RP, aws.NewClientFactory()) + varsLoader := vars.NewVarsLoader(ctx, k, p.SopsDecrypter, p.RP, aws.NewClientFactory()) if params.ForSeal { err = p.loadSecrets(target, varsCtx, varsLoader) @@ -104,6 +104,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe Ctx: ctx, K: k, RP: p.RP, + SopsDecrypter: p.SopsDecrypter, VarsLoader: varsLoader, HelmCredentials: params.HelmCredentials, RenderDir: params.RenderOutputDir, diff --git a/pkg/sops/sops_decryptor.go b/pkg/sops/sops_decryptor.go new file mode 100644 index 000000000..474d9872d --- /dev/null +++ b/pkg/sops/sops_decryptor.go @@ -0,0 +1,21 @@ +package sops + +import ( + "fmt" + "go.mozilla.org/sops/v3/cmd/sops/formats" + "go.mozilla.org/sops/v3/decrypt" +) + +type SopsDecrypter interface { + SopsDecryptWithFormat(data []byte, inputFormat, outputFormat formats.Format) ([]byte, error) +} + +type LocalSopsDecrypter struct { +} + +func (_ LocalSopsDecrypter) SopsDecryptWithFormat(data []byte, inputFormat, outputFormat formats.Format) ([]byte, error) { + if inputFormat != outputFormat { + return nil, fmt.Errorf("inputFormat and outputFormat must be equal") + } + return decrypt.DataWithFormat(data, inputFormat) +} diff --git a/pkg/sops/utils.go b/pkg/sops/utils.go new file mode 100644 index 000000000..39a1d85b2 --- /dev/null +++ b/pkg/sops/utils.go @@ -0,0 +1,56 @@ +package sops + +import ( + "bytes" + "errors" + "fmt" + "go.mozilla.org/sops/v3" + "go.mozilla.org/sops/v3/cmd/sops/formats" + "os" +) + +func IsMaybeSopsFile(s []byte) bool { + return bytes.Index(s, []byte("sops")) != -1 +} + +func MaybeDecrypt(decrypter SopsDecrypter, encrypted []byte, inputFormat, outputFormat formats.Format) ([]byte, bool, error) { + if decrypter == nil { + return encrypted, false, nil + } + + if !IsMaybeSopsFile(encrypted) { + return encrypted, false, nil + } + + d, err := decrypter.SopsDecryptWithFormat(encrypted, inputFormat, outputFormat) + if err != nil { + if errors.Is(err, sops.MetadataNotFound) { + return encrypted, false, nil + } + return nil, false, err + } + return d, true, nil +} + +func MaybeDecryptFile(decrypter SopsDecrypter, path string) error { + format := formats.FormatForPath(path) + + file, err := os.ReadFile(path) + if err != nil { + return fmt.Errorf("failed to read file %s: %w", path, err) + } + + decrypted, encrypted, err := MaybeDecrypt(decrypter, file, format, format) + if err != nil { + return fmt.Errorf("failed to decrypt file %s: %w", path, err) + } + if !encrypted { + return nil + } + + err = os.WriteFile(path, decrypted, 0o600) + if err != nil { + return fmt.Errorf("failed to save decrypted file %s: %w", path, err) + } + return nil +} diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 0dd5e66eb..226a5d5e3 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -14,9 +15,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/vars/aws" "github.com/kluctl/kluctl/v2/pkg/vars/vault" "github.com/kluctl/kluctl/v2/pkg/yaml" - "go.mozilla.org/sops/v3" "go.mozilla.org/sops/v3/cmd/sops/formats" - "go.mozilla.org/sops/v3/decrypt" "k8s.io/apimachinery/pkg/runtime/schema" "os" "strings" @@ -28,18 +27,20 @@ type usernamePassword struct { } type VarsLoader struct { - ctx context.Context - k *k8s.K8sCluster - rp *repocache.GitRepoCache - aws aws.AwsClientFactory + ctx context.Context + k *k8s.K8sCluster + sops sops.SopsDecrypter + rp *repocache.GitRepoCache + aws aws.AwsClientFactory credentialsCache map[string]usernamePassword } -func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, rp *repocache.GitRepoCache, aws aws.AwsClientFactory) *VarsLoader { +func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, sops sops.SopsDecrypter, rp *repocache.GitRepoCache, aws aws.AwsClientFactory) *VarsLoader { return &VarsLoader{ ctx: ctx, k: k, + sops: sops, rp: rp, aws: aws, credentialsCache: map[string]usernamePassword{}, @@ -110,14 +111,12 @@ func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, searchDirs []string return fmt.Errorf("failed to render vars file %s: %w", path, err) } - if yaml.IsMaybeSopsFile([]byte(rendered)) { - decrypted, err := decrypt.DataWithFormat([]byte(rendered), formats.FormatForPath(path)) - if err != nil && err != sops.MetadataNotFound { - return fmt.Errorf("failed to decrypt vars file %s: %w", path, err) - } else if err == nil { - rendered = string(decrypted) - } + format := formats.FormatForPath(path) + decrypted, _, err := sops.MaybeDecrypt(v.sops, []byte(rendered), format, format) + if err != nil { + return fmt.Errorf("failed to decrypt vars file %s: %w", path, err) } + rendered = string(decrypted) newVars := uo.New() err = yaml.ReadYamlString(rendered, newVars) diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 95db5f20f..95fff9e9a 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -10,6 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git/repocache" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -56,7 +57,7 @@ func testVarsLoader(t *testing.T, test func(vl *VarsLoader, vc *VarsCtx, aws *aw grc := newRP(t) fakeAws := aws.NewFakeClientFactory() - vl := NewVarsLoader(context.TODO(), k, grc, fakeAws) + vl := NewVarsLoader(context.TODO(), k, &sops.LocalSopsDecrypter{}, grc, fakeAws) vc := NewVarsCtx(newJinja2Must(t)) test(vl, vc, fakeAws) diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 0dde23fad..89610c94a 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -285,7 +285,3 @@ func Exists(p string) bool { p = FixPathExt(p) return utils.Exists(p) } - -func IsMaybeSopsFile(s []byte) bool { - return bytes.Index(s, []byte("sops")) != -1 -} From 9b8f27efec2a535500466c9e2d71174d76ab855a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 25 Nov 2022 15:58:30 +0100 Subject: [PATCH 0583/2268] fix: Fix simulation of dryRun creation into fresh namespaces The ApplyUtil only tracks applied objects of the current deployment item but not objects that were previously applied by other instances. This causes namespaces to be considered "non-existent" when not created in the same item. This is fixed by globally tracking created namespaces. --- pkg/deployment/utils/apply_utils.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 440b83e76..2bee3e50d 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -46,7 +46,8 @@ type ApplyUtil struct { deletedHookObjects map[k8s2.ObjectRef]bool mutex sync.Mutex - abortSignal *atomic.Value + abortSignal *atomic.Value + allNamespaces *sync.Map ru *RemoteObjectUtils k *k8s.K8sCluster @@ -65,6 +66,11 @@ type ApplyDeploymentsUtil struct { abortSignal atomic.Value + // Used to track all created namespaces + // All ApplyUtil instances write to this in parallel and we ignore that order might be unstable + // This is only used to simulate dryRun apply into new namespaces + allNamespaces sync.Map + resultsMutex sync.Mutex results []*ApplyUtil } @@ -94,6 +100,7 @@ func (ad *ApplyDeploymentsUtil) NewApplyUtil(ctx context.Context, statusCtx *sta deletedObjects: map[k8s2.ObjectRef]bool{}, deletedHookObjects: map[k8s2.ObjectRef]bool{}, abortSignal: &ad.abortSignal, + allNamespaces: &ad.allNamespaces, ru: ad.ru, k: ad.k, o: ad.o, @@ -297,8 +304,7 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo x = x.Clone() x.SetK8sName(fmt.Sprintf("%s-%s", ref.Name, utils.RandomString(8))) } else if a.o.DryRun && remoteNamespace == nil && ref.Namespace != "" { - nsRef := k8s2.NewObjectRef("", "v1", "Namespace", ref.Namespace, "") - if _, ok := a.appliedObjects[nsRef]; ok { + if _, ok := a.allNamespaces.Load(ref.Namespace); ok { // The namespace does not really exist, but would have been created if dryRun would be false. // So let's pretend we deploy it to the default namespace with a dummy name usesDummyName = true @@ -318,6 +324,9 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo _ = r.ReplaceValues(tmpName, ref.Name) r.SetK8sNamespace(ref.Namespace) } + if r != nil && ref.GVK.GroupKind().String() == "Namespace" { + a.allNamespaces.Store(ref.Name, r) + } a.handleApiWarnings(ref, apiWarnings) if err == nil { a.handleResult(r, hook) From 121881e88e6c1a1b31a4e16ebe93803fa0d612f9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 10:38:46 +0100 Subject: [PATCH 0584/2268] fix: Make check for overwritten fields managers more strict Otherwise field managers that for example contain "kluctl" somewhere in-between are overwritten as well. Noticed this while working on the flux-kluctl-controller. --- pkg/diff/managed_fields.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index 1a5a36673..9fa5d1d69 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -18,11 +18,11 @@ type LostOwnership struct { var forceApplyFieldAnnotationRegex = regexp.MustCompile(`^kluctl.io/force-apply-field(-\d*)?$`) var overwriteAllowedManagers = []*regexp.Regexp{ - regexp.MustCompile("kluctl"), - regexp.MustCompile("kubectl"), - regexp.MustCompile("kubectl-.*"), - regexp.MustCompile("rancher"), - regexp.MustCompile("k9s"), + regexp.MustCompile("^kluctl$"), + regexp.MustCompile("^kubectl$"), + regexp.MustCompile("^kubectl-.*$"), + regexp.MustCompile("^rancher$"), + regexp.MustCompile("^k9s$"), } func checkListItemMatch(o interface{}, pathElement fieldpath.PathElement, index int) (bool, error) { From d7aac60ee0aede00b325cb2bdf4a9cc462e41d19 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 15:59:02 +0100 Subject: [PATCH 0585/2268] tests: Move test-utils package below e2e --- e2e/default_clusters.go | 2 +- e2e/flux_test.go | 2 +- e2e/hooks_test.go | 2 +- e2e/inclusion_test.go | 2 +- e2e/project.go | 2 +- e2e/seal_test.go | 2 +- {internal => e2e}/test-utils/envtest_cluster.go | 0 {internal => e2e}/test-utils/envtest_cluster_callback.go | 0 {internal => e2e}/test-utils/git_server.go | 0 e2e/test_resources/resources.go | 2 +- e2e/utils.go | 2 +- pkg/vars/vars_loader_test.go | 2 +- 12 files changed, 9 insertions(+), 9 deletions(-) rename {internal => e2e}/test-utils/envtest_cluster.go (100%) rename {internal => e2e}/test-utils/envtest_cluster_callback.go (100%) rename {internal => e2e}/test-utils/git_server.go (100%) diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 3ef14c5c8..3baff99ef 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -1,8 +1,8 @@ package e2e import ( + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/e2e/test_resources" - test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "k8s.io/apimachinery/pkg/runtime/schema" "sync" ) diff --git a/e2e/flux_test.go b/e2e/flux_test.go index b796e792d..5cd90eec7 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -3,6 +3,7 @@ package e2e import ( "context" "fmt" + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -10,7 +11,6 @@ import ( "testing" "github.com/kluctl/kluctl/v2/e2e/test_resources" - test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/stretchr/testify/assert" ) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 863ba6af3..663c8fb22 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -3,6 +3,7 @@ package e2e import ( "context" "fmt" + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -10,7 +11,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "testing" - test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index a50564854..84e61ad8e 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -1,7 +1,7 @@ package e2e import ( - "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/e2e/test-utils" corev1 "k8s.io/api/core/v1" "path/filepath" "reflect" diff --git a/e2e/project.go b/e2e/project.go index bcf57d051..1443b4901 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -5,7 +5,7 @@ import ( "github.com/go-git/go-git/v5" "github.com/huandu/xstrings" "github.com/imdario/mergo" - test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/client-go/tools/clientcmd" diff --git a/e2e/seal_test.go b/e2e/seal_test.go index c5b9268bf..c172f060b 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -6,8 +6,8 @@ import ( "encoding/pem" "fmt" "github.com/bitnami-labs/sealed-secrets/pkg/crypto" + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/e2e/test_resources" - "github.com/kluctl/kluctl/v2/internal/test-utils" "github.com/kluctl/kluctl/v2/pkg/seal" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" diff --git a/internal/test-utils/envtest_cluster.go b/e2e/test-utils/envtest_cluster.go similarity index 100% rename from internal/test-utils/envtest_cluster.go rename to e2e/test-utils/envtest_cluster.go diff --git a/internal/test-utils/envtest_cluster_callback.go b/e2e/test-utils/envtest_cluster_callback.go similarity index 100% rename from internal/test-utils/envtest_cluster_callback.go rename to e2e/test-utils/envtest_cluster_callback.go diff --git a/internal/test-utils/git_server.go b/e2e/test-utils/git_server.go similarity index 100% rename from internal/test-utils/git_server.go rename to e2e/test-utils/git_server.go diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go index f374d2ee0..fa1754bff 100644 --- a/e2e/test_resources/resources.go +++ b/e2e/test_resources/resources.go @@ -3,7 +3,7 @@ package test_resources import ( "context" "embed" - test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" diff --git a/e2e/utils.go b/e2e/utils.go index f8a20f9ad..bd8eb8e63 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -4,7 +4,7 @@ import ( "bufio" "bytes" "context" - test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "io" v1 "k8s.io/api/core/v1" diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 95fff9e9a..9b46900c8 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -4,7 +4,7 @@ import ( "context" git2 "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" - test_utils "github.com/kluctl/kluctl/v2/internal/test-utils" + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/repocache" From 95f6242dceb3360aaf9edac964097267702082bb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 16:25:55 +0100 Subject: [PATCH 0586/2268] tests: Move Helm Repo utils into test-utils --- e2e/helm_test.go | 194 +++--------------------------- e2e/test-utils/helm_repo_utils.go | 163 +++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 174 deletions(-) create mode 100644 e2e/test-utils/helm_repo_utils.go diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 780ea9c2b..e5d5b9952 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -3,174 +3,20 @@ package e2e import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" - "github.com/google/go-containerregistry/pkg/registry" - test_resources "github.com/kluctl/kluctl/v2/e2e/test-helm-chart" - "github.com/kluctl/kluctl/v2/pkg/utils" + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/stretchr/testify/assert" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/cli/values" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/pusher" registry2 "helm.sh/helm/v3/pkg/registry" - "helm.sh/helm/v3/pkg/repo" - "helm.sh/helm/v3/pkg/uploader" - "net/http" - "net/http/httptest" - "net/url" - "os" "path/filepath" "sort" - "strings" "testing" ) -func createHelmPackage(t *testing.T, name string, version string) string { - tmpDir := t.TempDir() - err := utils.FsCopyDir(test_resources.HelmChartFS, ".", tmpDir) - if err != nil { - t.Fatal(err) - } - - c, err := uo.FromFile(filepath.Join(tmpDir, "Chart.yaml")) - if err != nil { - t.Fatal(err) - } - - _ = c.SetNestedField(name, "name") - _ = c.SetNestedField(version, "version") - - err = yaml.WriteYamlFile(filepath.Join(tmpDir, "Chart.yaml"), c) - if err != nil { - t.Fatal(err) - } - - settings := cli.New() - client := action.NewPackage() - client.Destination = tmpDir - valueOpts := &values.Options{} - p := getter.All(settings) - vals, err := valueOpts.MergeValues(p) - retName, err := client.Run(tmpDir, vals) - if err != nil { - t.Fatal(err) - } - - return retName -} - -type repoChart struct { - chartName string - version string -} - -func createHelmRepo(t *testing.T, charts []repoChart, password string) string { - tmpDir := t.TempDir() - - for _, c := range charts { - tgz := createHelmPackage(t, c.chartName, c.version) - _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) - } - - fs := http.FileServer(http.FS(os.DirFS(tmpDir))) - s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if password != "" { - _, p, ok := r.BasicAuth() - if !ok || p != password { - http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) - return - } - } - fs.ServeHTTP(w, r) - })) - - t.Cleanup(s.Close) - - i, err := repo.IndexDirectory(tmpDir, s.URL) - if err != nil { - t.Fatal(err) - } - - i.SortEntries() - err = i.WriteFile(filepath.Join(tmpDir, "index.yaml"), 0644) - if err != nil { - t.Fatal(err) - } - - return s.URL -} - -func createOciRepo(t *testing.T, charts []repoChart, password string) string { - tmpDir := t.TempDir() - - ociRegistry := registry.New() - s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if password != "" { - _, p, ok := r.BasicAuth() - if !ok { - w.Header().Add("WWW-Authenticate", "Basic") - http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) - return - } - if !ok || p != password { - http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) - return - } - } - ociRegistry.ServeHTTP(w, r) - })) - - t.Cleanup(s.Close) - - ociUrl := strings.ReplaceAll(s.URL, "http://", "oci://") - ociUrl2, _ := url.Parse(ociUrl) - - var out strings.Builder - settings := cli.New() - c := uploader.ChartUploader{ - Out: &out, - Pushers: pusher.All(settings), - Options: []pusher.Option{}, - } - - var registryClient *registry2.Client - if password != "" { - var err error - registryClient, err = registry2.NewClient() - if err != nil { - t.Fatal(err) - } - err = registryClient.Login(ociUrl2.Host, registry2.LoginOptBasicAuth("test-user", password), registry2.LoginOptInsecure(true)) - if err != nil { - t.Fatal(err) - } - c.Options = append(c.Options, pusher.WithRegistryClient(registryClient)) - } - - for _, chart := range charts { - tgz := createHelmPackage(t, chart.chartName, chart.version) - _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) - - err := c.UploadTo(tgz, ociUrl) - if err != nil { - t.Fatal(err) - } - } - - if registryClient != nil { - registryClient.Logout(ociUrl2.Host) - } - - return ociUrl -} - -func createHelmOrOciRepo(t *testing.T, charts []repoChart, oci bool, password string) string { +func createHelmOrOciRepo(t *testing.T, charts []test_utils.RepoChart, oci bool, password string) string { if oci { - return createOciRepo(t, charts, password) + return test_utils.CreateOciRepo(t, charts, password) } else { - return createHelmRepo(t, charts, password) + return test_utils.CreateHelmRepo(t, charts, password) } } @@ -231,8 +77,8 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { password = "secret-password" } - repoUrl := createHelmOrOciRepo(p.t, []repoChart{ - {chartName: "test-chart1", version: "0.1.0"}, + repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, }, tc.oci, password) p.updateTarget("test", nil) @@ -316,9 +162,9 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { createNamespace(t, k, p.testSlug()) - repoUrl := createHelmOrOciRepo(p.t, []repoChart{ - {chartName: "test-chart1", version: "0.1.0"}, - {chartName: "test-chart1", version: "0.2.0"}, + repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, + {ChartName: "test-chart1", Version: "0.2.0"}, }, oci, "") p.updateTarget("test", nil) @@ -361,11 +207,11 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { createNamespace(t, k, p.testSlug()) - repoUrl := createHelmOrOciRepo(p.t, []repoChart{ - {chartName: "test-chart1", version: "0.1.0"}, - {chartName: "test-chart1", version: "0.2.0"}, - {chartName: "test-chart2", version: "0.1.0"}, - {chartName: "test-chart2", version: "0.3.0"}, + repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, + {ChartName: "test-chart1", Version: "0.2.0"}, + {ChartName: "test-chart2", Version: "0.1.0"}, + {ChartName: "test-chart2", Version: "0.3.0"}, }, oci, "") p.updateTarget("test", nil) @@ -472,9 +318,9 @@ func TestHelmValues(t *testing.T) { createNamespace(t, k, p.testSlug()) - repoUrl := createHelmRepo(p.t, []repoChart{ - {chartName: "test-chart1", version: "0.1.0"}, - {chartName: "test-chart2", version: "0.1.0"}, + repoUrl := test_utils.CreateHelmRepo(p.t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, + {ChartName: "test-chart2", Version: "0.1.0"}, }, "") values1 := map[string]any{ @@ -537,9 +383,9 @@ func TestHelmTemplateChartYaml(t *testing.T) { createNamespace(t, k, p.testSlug()+"-a") createNamespace(t, k, p.testSlug()+"-b") - repoUrl := createHelmRepo(p.t, []repoChart{ - {chartName: "test-chart1", version: "0.1.0"}, - {chartName: "test-chart2", version: "0.1.0"}, + repoUrl := test_utils.CreateHelmRepo(p.t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, + {ChartName: "test-chart2", Version: "0.1.0"}, }, "") p.updateTarget("test", nil) diff --git a/e2e/test-utils/helm_repo_utils.go b/e2e/test-utils/helm_repo_utils.go new file mode 100644 index 000000000..afe54a276 --- /dev/null +++ b/e2e/test-utils/helm_repo_utils.go @@ -0,0 +1,163 @@ +package test_utils + +import ( + "github.com/google/go-containerregistry/pkg/registry" + test_resources "github.com/kluctl/kluctl/v2/e2e/test-helm-chart" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/cli/values" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/pusher" + registry2 "helm.sh/helm/v3/pkg/registry" + "helm.sh/helm/v3/pkg/repo" + "helm.sh/helm/v3/pkg/uploader" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "strings" + "testing" +) + +func createHelmPackage(t *testing.T, name string, version string) string { + tmpDir := t.TempDir() + err := utils.FsCopyDir(test_resources.HelmChartFS, ".", tmpDir) + if err != nil { + t.Fatal(err) + } + + c, err := uo.FromFile(filepath.Join(tmpDir, "Chart.yaml")) + if err != nil { + t.Fatal(err) + } + + _ = c.SetNestedField(name, "name") + _ = c.SetNestedField(version, "version") + + err = yaml.WriteYamlFile(filepath.Join(tmpDir, "Chart.yaml"), c) + if err != nil { + t.Fatal(err) + } + + settings := cli.New() + client := action.NewPackage() + client.Destination = tmpDir + valueOpts := &values.Options{} + p := getter.All(settings) + vals, err := valueOpts.MergeValues(p) + retName, err := client.Run(tmpDir, vals) + if err != nil { + t.Fatal(err) + } + + return retName +} + +type RepoChart struct { + ChartName string + Version string +} + +func CreateHelmRepo(t *testing.T, charts []RepoChart, password string) string { + tmpDir := t.TempDir() + + for _, c := range charts { + tgz := createHelmPackage(t, c.ChartName, c.Version) + _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) + } + + fs := http.FileServer(http.FS(os.DirFS(tmpDir))) + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if password != "" { + _, p, ok := r.BasicAuth() + if !ok || p != password { + http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) + return + } + } + fs.ServeHTTP(w, r) + })) + + t.Cleanup(s.Close) + + i, err := repo.IndexDirectory(tmpDir, s.URL) + if err != nil { + t.Fatal(err) + } + + i.SortEntries() + err = i.WriteFile(filepath.Join(tmpDir, "index.yaml"), 0644) + if err != nil { + t.Fatal(err) + } + + return s.URL +} + +func CreateOciRepo(t *testing.T, charts []RepoChart, password string) string { + tmpDir := t.TempDir() + + ociRegistry := registry.New() + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if password != "" { + _, p, ok := r.BasicAuth() + if !ok { + w.Header().Add("WWW-Authenticate", "Basic") + http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) + return + } + if !ok || p != password { + http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) + return + } + } + ociRegistry.ServeHTTP(w, r) + })) + + t.Cleanup(s.Close) + + ociUrl := strings.ReplaceAll(s.URL, "http://", "oci://") + ociUrl2, _ := url.Parse(ociUrl) + + var out strings.Builder + settings := cli.New() + c := uploader.ChartUploader{ + Out: &out, + Pushers: pusher.All(settings), + Options: []pusher.Option{}, + } + + var registryClient *registry2.Client + if password != "" { + var err error + registryClient, err = registry2.NewClient() + if err != nil { + t.Fatal(err) + } + err = registryClient.Login(ociUrl2.Host, registry2.LoginOptBasicAuth("test-user", password), registry2.LoginOptInsecure(true)) + if err != nil { + t.Fatal(err) + } + c.Options = append(c.Options, pusher.WithRegistryClient(registryClient)) + } + + for _, chart := range charts { + tgz := createHelmPackage(t, chart.ChartName, chart.Version) + _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) + + err := c.UploadTo(tgz, ociUrl) + if err != nil { + t.Fatal(err) + } + } + + if registryClient != nil { + registryClient.Logout(ociUrl2.Host) + } + + return ociUrl +} From 8e531507d95cfd7ca922c6057b4a7ed65818a0c3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 16:33:48 +0100 Subject: [PATCH 0587/2268] tests: Also pass username to test Helm repos --- e2e/helm_test.go | 24 +++++++++++++----------- e2e/test-utils/helm_repo_utils.go | 16 ++++++++-------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index e5d5b9952..8b9adf1c5 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -12,11 +12,11 @@ import ( "testing" ) -func createHelmOrOciRepo(t *testing.T, charts []test_utils.RepoChart, oci bool, password string) string { +func createHelmOrOciRepo(t *testing.T, charts []test_utils.RepoChart, oci bool, user string, password string) string { if oci { - return test_utils.CreateOciRepo(t, charts, password) + return test_utils.CreateOciRepo(t, charts, user, password) } else { - return test_utils.CreateHelmRepo(t, charts, password) + return test_utils.CreateHelmRepo(t, charts, user, password) } } @@ -72,14 +72,16 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { createNamespace(t, k, p.testSlug()) + user := "" password := "" if tc.testAuth { + user = "test-user" password = "secret-password" } repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, - }, tc.oci, password) + }, tc.oci, user, password) p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) @@ -132,13 +134,13 @@ func TestHelmPull(t *testing.T) { {name: "helm-creds-missing", oci: false, testAuth: true, credsId: "test-creds", expectedError: "no credentials provided for Chart test-chart1"}, {name: "helm-creds-invalid", oci: false, testAuth: true, credsId: "test-creds", - extraArgs: []string{"--helm-username=test-creds:user", "--helm-password=test-creds:invalid"}, + extraArgs: []string{"--helm-username=test-creds:test-user", "--helm-password=test-creds:invalid"}, expectedError: "401 Unauthorized"}, {name: "helm-creds-valid", oci: false, testAuth: true, credsId: "test-creds", - extraArgs: []string{"--helm-username=test-creds:user", "--helm-password=test-creds:secret-password"}}, + extraArgs: []string{"--helm-username=test-creds:test-user", "--helm-password=test-creds:secret-password"}}, {name: "oci", oci: true}, {name: "oci-creds-fail", oci: true, testAuth: true, credsId: "test-creds", - extraArgs: []string{"--helm-username=test-creds:user", "--helm-password=test-creds:secret-password"}, + extraArgs: []string{"--helm-username=test-creds:test-user", "--helm-password=test-creds:secret-password"}, expectedError: "OCI charts can currently only be authenticated via registry login and not via cli arguments"}, } @@ -165,7 +167,7 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, {ChartName: "test-chart1", Version: "0.2.0"}, - }, oci, "") + }, oci, "", "") p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) @@ -212,7 +214,7 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { {ChartName: "test-chart1", Version: "0.2.0"}, {ChartName: "test-chart2", Version: "0.1.0"}, {ChartName: "test-chart2", Version: "0.3.0"}, - }, oci, "") + }, oci, "", "") p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) @@ -321,7 +323,7 @@ func TestHelmValues(t *testing.T) { repoUrl := test_utils.CreateHelmRepo(p.t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, {ChartName: "test-chart2", Version: "0.1.0"}, - }, "") + }, "", "") values1 := map[string]any{ "data": map[string]any{ @@ -386,7 +388,7 @@ func TestHelmTemplateChartYaml(t *testing.T) { repoUrl := test_utils.CreateHelmRepo(p.t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, {ChartName: "test-chart2", Version: "0.1.0"}, - }, "") + }, "", "") p.updateTarget("test", nil) addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm-{{ args.a }}", p.testSlug(), nil) diff --git a/e2e/test-utils/helm_repo_utils.go b/e2e/test-utils/helm_repo_utils.go index afe54a276..631b2a677 100644 --- a/e2e/test-utils/helm_repo_utils.go +++ b/e2e/test-utils/helm_repo_utils.go @@ -62,7 +62,7 @@ type RepoChart struct { Version string } -func CreateHelmRepo(t *testing.T, charts []RepoChart, password string) string { +func CreateHelmRepo(t *testing.T, charts []RepoChart, username string, password string) string { tmpDir := t.TempDir() for _, c := range charts { @@ -73,8 +73,8 @@ func CreateHelmRepo(t *testing.T, charts []RepoChart, password string) string { fs := http.FileServer(http.FS(os.DirFS(tmpDir))) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if password != "" { - _, p, ok := r.BasicAuth() - if !ok || p != password { + u, p, ok := r.BasicAuth() + if !ok || u != username || p != password { http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) return } @@ -98,19 +98,19 @@ func CreateHelmRepo(t *testing.T, charts []RepoChart, password string) string { return s.URL } -func CreateOciRepo(t *testing.T, charts []RepoChart, password string) string { +func CreateOciRepo(t *testing.T, charts []RepoChart, username string, password string) string { tmpDir := t.TempDir() ociRegistry := registry.New() s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if password != "" { - _, p, ok := r.BasicAuth() + if username != "" || password != "" { + u, p, ok := r.BasicAuth() if !ok { w.Header().Add("WWW-Authenticate", "Basic") http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) return } - if !ok || p != password { + if u != username || p != password { http.Error(w, "Auth header was incorrect", http.StatusUnauthorized) return } @@ -138,7 +138,7 @@ func CreateOciRepo(t *testing.T, charts []RepoChart, password string) string { if err != nil { t.Fatal(err) } - err = registryClient.Login(ociUrl2.Host, registry2.LoginOptBasicAuth("test-user", password), registry2.LoginOptInsecure(true)) + err = registryClient.Login(ociUrl2.Host, registry2.LoginOptBasicAuth(username, password), registry2.LoginOptInsecure(true)) if err != nil { t.Fatal(err) } From 203ceed998e0d3a62fb235130d13790e31c450b8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 16:48:16 +0100 Subject: [PATCH 0588/2268] tests: Get rid of redundant name in hooks tests --- e2e/hooks_test.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 663c8fb22..c94827e4d 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -92,7 +92,7 @@ func (s *hooksTestContext) addConfigMap(dir string, opts resourceOpts) { }) } -func prepareHookTestProject(t *testing.T, name string, hook string, hookDeletionPolicy string) *hooksTestContext { +func prepareHookTestProject(t *testing.T, hook string, hookDeletionPolicy string) *hooksTestContext { s := &hooksTestContext{ t: t, k: defaultCluster2, // use cluster2 as it has webhooks setup @@ -133,21 +133,21 @@ func (s *hooksTestContext) ensureHookNotExecuted() { func TestHooksPreDeployInitial(t *testing.T) { t.Parallel() - s := prepareHookTestProject(t, "pre-deploy-initial", "pre-deploy-initial", "") + s := prepareHookTestProject(t, "pre-deploy-initial", "") s.ensureHookExecuted("hook1", "cm1") s.ensureHookExecuted("cm1") } func TestHooksPostDeployInitial(t *testing.T) { t.Parallel() - s := prepareHookTestProject(t, "post-deploy-initial", "post-deploy-initial", "") + s := prepareHookTestProject(t, "post-deploy-initial", "") s.ensureHookExecuted("cm1", "hook1") s.ensureHookExecuted("cm1") } func TestHooksPreDeployUpgrade(t *testing.T) { t.Parallel() - s := prepareHookTestProject(t, "pre-deploy-upgrade", "pre-deploy-upgrade", "") + s := prepareHookTestProject(t, "pre-deploy-upgrade", "") s.ensureHookExecuted("cm1") s.ensureHookExecuted("hook1", "cm1") s.ensureHookExecuted("hook1", "cm1") @@ -155,58 +155,58 @@ func TestHooksPreDeployUpgrade(t *testing.T) { func TestHooksPostDeployUpgrade(t *testing.T) { t.Parallel() - s := prepareHookTestProject(t, "post-deploy-upgrade", "post-deploy-upgrade", "") + s := prepareHookTestProject(t, "post-deploy-upgrade", "") s.ensureHookExecuted("cm1") s.ensureHookExecuted("cm1", "hook1") s.ensureHookExecuted("cm1", "hook1") } -func doTestHooksPreDeploy(t *testing.T, name string, hooks string) { - s := prepareHookTestProject(t, name, hooks, "") +func doTestHooksPreDeploy(t *testing.T, hooks string) { + s := prepareHookTestProject(t, hooks, "") s.ensureHookExecuted("hook1", "cm1") s.ensureHookExecuted("hook1", "cm1") } -func doTestHooksPostDeploy(t *testing.T, name string, hooks string) { - s := prepareHookTestProject(t, name, hooks, "") +func doTestHooksPostDeploy(t *testing.T, hooks string) { + s := prepareHookTestProject(t, hooks, "") s.ensureHookExecuted("cm1", "hook1") s.ensureHookExecuted("cm1", "hook1") } -func doTestHooksPrePostDeploy(t *testing.T, name string, hooks string) { - s := prepareHookTestProject(t, name, hooks, "") +func doTestHooksPrePostDeploy(t *testing.T, hooks string) { + s := prepareHookTestProject(t, hooks, "") s.ensureHookExecuted("hook1", "cm1", "hook1") s.ensureHookExecuted("hook1", "cm1", "hook1") } func TestHooksPreDeploy(t *testing.T) { t.Parallel() - doTestHooksPreDeploy(t, "pre-deploy", "pre-deploy") + doTestHooksPreDeploy(t, "pre-deploy") } func TestHooksPreDeploy2(t *testing.T) { t.Parallel() // same as pre-deploy - doTestHooksPreDeploy(t, "pre-deploy2", "pre-deploy-initial,pre-deploy-upgrade") + doTestHooksPreDeploy(t, "pre-deploy-initial,pre-deploy-upgrade") } func TestHooksPostDeploy(t *testing.T) { t.Parallel() - doTestHooksPostDeploy(t, "post-deploy", "post-deploy") + doTestHooksPostDeploy(t, "post-deploy") } func TestHooksPostDeploy2(t *testing.T) { t.Parallel() // same as post-deploy - doTestHooksPostDeploy(t, "post-deploy2", "post-deploy-initial,post-deploy-upgrade") + doTestHooksPostDeploy(t, "post-deploy-initial,post-deploy-upgrade") } func TestHooksPrePostDeploy(t *testing.T) { t.Parallel() - doTestHooksPrePostDeploy(t, "pre-post-deploy", "pre-deploy,post-deploy") + doTestHooksPrePostDeploy(t, "pre-deploy,post-deploy") } func TestHooksPrePostDeploy2(t *testing.T) { t.Parallel() - doTestHooksPrePostDeploy(t, "pre-post-deploy2", "pre-deploy-initial,pre-deploy-upgrade,post-deploy-initial,post-deploy-upgrade") + doTestHooksPrePostDeploy(t, "pre-deploy-initial,pre-deploy-upgrade,post-deploy-initial,post-deploy-upgrade") } From 30e646f1020a66c43dd613975825852330c7d1b3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 16:50:44 +0100 Subject: [PATCH 0589/2268] tests: Implement and use NewTestProject --- e2e/args_test.go | 9 ++---- e2e/contexts_test.go | 5 ++- e2e/deployment_items_test.go | 6 ++-- e2e/flux_test.go | 3 +- e2e/helm_test.go | 17 ++++------ e2e/hooks_test.go | 5 ++- e2e/inclusion_test.go | 7 ++-- e2e/no_target_test.go | 5 ++- e2e/project.go | 62 ++++++++++++++++++++---------------- e2e/seal_test.go | 11 +++---- e2e/sops_test.go | 6 ++-- e2e/utils_resources.go | 4 +-- 12 files changed, 64 insertions(+), 76 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index 05e38fe97..17e772568 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -12,8 +12,7 @@ func testArgs(t *testing.T, deprecated bool) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) @@ -136,8 +135,7 @@ func TestArgsFromEnv(t *testing.T) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) @@ -170,8 +168,7 @@ func TestArgsFromEnvAndCli(t *testing.T) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index a6e2a0898..55296bac2 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -6,9 +6,8 @@ import ( "testing" ) -func prepareContextTest(t *testing.T) *testProject { - p := &testProject{} - p.init(t, defaultCluster1) +func prepareContextTest(t *testing.T) *TestProject { + p := NewTestProject(t, defaultCluster1) p.mergeKubeconfig(defaultCluster2) createNamespace(t, defaultCluster1, p.testSlug()) diff --git a/e2e/deployment_items_test.go b/e2e/deployment_items_test.go index a9fa3c5b6..23d4c0bad 100644 --- a/e2e/deployment_items_test.go +++ b/e2e/deployment_items_test.go @@ -10,8 +10,7 @@ func TestKustomize(t *testing.T) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) @@ -39,8 +38,7 @@ func TestGeneratedKustomize(t *testing.T) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) diff --git a/e2e/flux_test.go b/e2e/flux_test.go index 5cd90eec7..b9f49d496 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -36,8 +36,7 @@ func TestFluxCommands(t *testing.T) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) var wg sync.WaitGroup wg.Add(2) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 8b9adf1c5..cb4a588f5 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -20,7 +20,7 @@ func createHelmOrOciRepo(t *testing.T, charts []test_utils.RepoChart, oci bool, } } -func addHelmDeployment(p *testProject, dir string, repoUrl string, chartName, version string, releaseName string, namespace string, values map[string]any) { +func addHelmDeployment(p *TestProject, dir string, repoUrl string, chartName, version string, releaseName string, namespace string, values map[string]any) { if registry2.IsOCI(repoUrl) { repoUrl += "/" + chartName chartName = "" @@ -67,8 +67,7 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) @@ -159,8 +158,7 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) @@ -204,8 +202,7 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) @@ -315,8 +312,7 @@ func TestHelmValues(t *testing.T) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) @@ -378,8 +374,7 @@ func TestHelmTemplateChartYaml(t *testing.T) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) createNamespace(t, k, p.testSlug()+"-a") diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index c94827e4d..54df6986e 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -18,7 +18,7 @@ type hooksTestContext struct { t *testing.T k *test_utils.EnvTestCluster - p *testProject + p *TestProject seenConfigMaps []string @@ -96,11 +96,10 @@ func prepareHookTestProject(t *testing.T, hook string, hookDeletionPolicy string s := &hooksTestContext{ t: t, k: defaultCluster2, // use cluster2 as it has webhooks setup - p: &testProject{}, } s.setupWebhook() - s.p.init(t, s.k) + s.p = NewTestProject(t, s.k) t.Cleanup(func() { s.removeWebhook() }) diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 84e61ad8e..1a212cc4d 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -8,10 +8,9 @@ import ( "testing" ) -func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*testProject, *test_utils.EnvTestCluster) { +func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*TestProject, *test_utils.EnvTestCluster) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) @@ -41,7 +40,7 @@ func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*testProject, return p, k } -func assertExistsHelper(t *testing.T, p *testProject, k *test_utils.EnvTestCluster, shouldExists map[string]bool, add []string, remove []string) { +func assertExistsHelper(t *testing.T, p *TestProject, k *test_utils.EnvTestCluster, shouldExists map[string]bool, add []string, remove []string) { for _, x := range add { shouldExists[x] = true } diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index 01cdfe738..433df873d 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -7,9 +7,8 @@ import ( "testing" ) -func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *testProject { - p := &testProject{} - p.init(t, defaultCluster1) +func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *TestProject { + p := NewTestProject(t, defaultCluster1) p.mergeKubeconfig(defaultCluster2) createNamespace(t, defaultCluster1, p.testSlug()) diff --git a/e2e/project.go b/e2e/project.go index 1443b4901..afbb0fc0c 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -18,7 +18,7 @@ import ( "testing" ) -type testProject struct { +type TestProject struct { t *testing.T extraEnv []string @@ -27,8 +27,11 @@ type testProject struct { gitServer *test_utils.GitServer } -func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster) { - p.t = t +func NewTestProject(t *testing.T, k *test_utils.EnvTestCluster) *TestProject { + p := &TestProject{ + t: t, + } + p.gitServer = test_utils.NewGitServer(t) p.gitServer.GitInit(p.getKluctlProjectRepo()) @@ -48,17 +51,20 @@ func (p *testProject) init(t *testing.T, k *test_utils.EnvTestCluster) { t.Cleanup(func() { os.Remove(p.mergedKubeconfig) }) - p.mergeKubeconfig(k) + if k != nil { + p.mergeKubeconfig(k) + } + return p } -func (p *testProject) testSlug() string { +func (p *TestProject) testSlug() string { n := p.t.Name() n = xstrings.ToKebabCase(n) n = strings.ReplaceAll(n, "/", "-") return n } -func (p *testProject) mergeKubeconfig(k *test_utils.EnvTestCluster) { +func (p *TestProject) mergeKubeconfig(k *test_utils.EnvTestCluster) { p.updateMergedKubeconfig(func(config *clientcmdapi.Config) { nkcfg, err := clientcmd.Load(k.Kubeconfig) if err != nil { @@ -72,7 +78,7 @@ func (p *testProject) mergeKubeconfig(k *test_utils.EnvTestCluster) { }) } -func (p *testProject) updateMergedKubeconfig(cb func(config *clientcmdapi.Config)) { +func (p *TestProject) updateMergedKubeconfig(cb func(config *clientcmdapi.Config)) { mkcfg, err := clientcmd.LoadFromFile(p.mergedKubeconfig) if err != nil { p.t.Fatal(err) @@ -86,11 +92,11 @@ func (p *testProject) updateMergedKubeconfig(cb func(config *clientcmdapi.Config } } -func (p *testProject) updateKluctlYaml(update func(o *uo.UnstructuredObject) error) { +func (p *TestProject) updateKluctlYaml(update func(o *uo.UnstructuredObject) error) { p.updateYaml(".kluctl.yml", update, "") } -func (p *testProject) updateDeploymentYaml(dir string, update func(o *uo.UnstructuredObject) error) { +func (p *TestProject) updateDeploymentYaml(dir string, update func(o *uo.UnstructuredObject) error) { p.updateYaml(filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { if dir == "." { o.SetNestedField(p.testSlug(), "commonLabels", "project_name") @@ -99,17 +105,17 @@ func (p *testProject) updateDeploymentYaml(dir string, update func(o *uo.Unstruc }, "") } -func (p *testProject) updateYaml(path string, update func(o *uo.UnstructuredObject) error, message string) { +func (p *TestProject) updateYaml(path string, update func(o *uo.UnstructuredObject) error, message string) { p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), path, func(o *uo.UnstructuredObject) error { return update(o) }, message) } -func (p *testProject) updateFile(path string, update func(f string) (string, error), message string) { +func (p *TestProject) updateFile(path string, update func(f string) (string, error), message string) { p.gitServer.UpdateFile(p.getKluctlProjectRepo(), path, update, message) } -func (p *testProject) getDeploymentYaml(dir string) *uo.UnstructuredObject { +func (p *TestProject) getDeploymentYaml(dir string) *uo.UnstructuredObject { o, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, "deployment.yml")) if err != nil { p.t.Fatal(err) @@ -117,7 +123,7 @@ func (p *testProject) getDeploymentYaml(dir string) *uo.UnstructuredObject { return o } -func (p *testProject) listDeploymentItemPathes(dir string, fullPath bool) []string { +func (p *TestProject) listDeploymentItemPathes(dir string, fullPath bool) []string { var ret []string o := p.getDeploymentYaml(dir) l, _, err := o.GetNestedObjectList("deployments") @@ -141,7 +147,7 @@ func (p *testProject) listDeploymentItemPathes(dir string, fullPath bool) []stri return ret } -func (p *testProject) updateKustomizeDeployment(dir string, update func(o *uo.UnstructuredObject, wt *git.Worktree) error) { +func (p *TestProject) updateKustomizeDeployment(dir string, update func(o *uo.UnstructuredObject, wt *git.Worktree) error) { wt := p.gitServer.GetWorktree(p.getKluctlProjectRepo()) pth := filepath.Join(dir, "kustomization.yml") @@ -150,15 +156,15 @@ func (p *testProject) updateKustomizeDeployment(dir string, update func(o *uo.Un }, fmt.Sprintf("Update kustomization.yml for %s", dir)) } -func (p *testProject) updateTarget(name string, cb func(target *uo.UnstructuredObject)) { +func (p *TestProject) updateTarget(name string, cb func(target *uo.UnstructuredObject)) { p.updateNamedListItem(uo.KeyPath{"targets"}, name, cb) } -func (p *testProject) updateSecretSet(name string, cb func(secretSet *uo.UnstructuredObject)) { +func (p *TestProject) updateSecretSet(name string, cb func(secretSet *uo.UnstructuredObject)) { p.updateNamedListItem(uo.KeyPath{"secretsConfig", "secretSets"}, name, cb) } -func (p *testProject) updateNamedListItem(path uo.KeyPath, name string, cb func(item *uo.UnstructuredObject)) { +func (p *TestProject) updateNamedListItem(path uo.KeyPath, name string, cb func(item *uo.UnstructuredObject)) { if cb == nil { cb = func(target *uo.UnstructuredObject) {} } @@ -188,7 +194,7 @@ func (p *testProject) updateNamedListItem(path uo.KeyPath, name string, cb func( }) } -func (p *testProject) updateDeploymentItems(dir string, update func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject) { +func (p *TestProject) updateDeploymentItems(dir string, update func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject) { p.updateDeploymentYaml(dir, func(o *uo.UnstructuredObject) error { items, _, _ := o.GetNestedObjectList("deployments") items = update(items) @@ -196,7 +202,7 @@ func (p *testProject) updateDeploymentItems(dir string, update func(items []*uo. }) } -func (p *testProject) addDeploymentItem(dir string, item *uo.UnstructuredObject) { +func (p *TestProject) addDeploymentItem(dir string, item *uo.UnstructuredObject) { p.updateDeploymentItems(dir, func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { for _, x := range items { if reflect.DeepEqual(x, item) { @@ -208,7 +214,7 @@ func (p *testProject) addDeploymentItem(dir string, item *uo.UnstructuredObject) }) } -func (p *testProject) addDeploymentInclude(dir string, includePath string, tags []string) { +func (p *TestProject) addDeploymentInclude(dir string, includePath string, tags []string) { n := uo.FromMap(map[string]interface{}{ "include": includePath, }) @@ -218,7 +224,7 @@ func (p *testProject) addDeploymentInclude(dir string, includePath string, tags p.addDeploymentItem(dir, n) } -func (p *testProject) addDeploymentIncludes(dir string) { +func (p *TestProject) addDeploymentIncludes(dir string) { var pp []string for _, x := range strings.Split(dir, "/") { if x != "." { @@ -228,7 +234,7 @@ func (p *testProject) addDeploymentIncludes(dir string) { } } -func (p *testProject) addKustomizeDeployment(dir string, resources []kustomizeResource, tags []string) { +func (p *TestProject) addKustomizeDeployment(dir string, resources []kustomizeResource, tags []string) { deploymentDir := filepath.Dir(dir) if deploymentDir != "" { p.addDeploymentIncludes(deploymentDir) @@ -262,7 +268,7 @@ func (p *testProject) addKustomizeDeployment(dir string, resources []kustomizeRe }) } -func (p *testProject) convertInterfaceToList(x interface{}) []interface{} { +func (p *TestProject) convertInterfaceToList(x interface{}) []interface{} { var ret []interface{} if l, ok := x.([]interface{}); ok { return l @@ -288,7 +294,7 @@ type kustomizeResource struct { content interface{} } -func (p *testProject) addKustomizeResources(dir string, resources []kustomizeResource) { +func (p *TestProject) addKustomizeResources(dir string, resources []kustomizeResource) { p.updateKustomizeDeployment(dir, func(o *uo.UnstructuredObject, wt *git.Worktree) error { l, _, _ := o.GetNestedList("resources") for _, r := range resources { @@ -314,7 +320,7 @@ func (p *testProject) addKustomizeResources(dir string, resources []kustomizeRes }) } -func (p *testProject) deleteKustomizeDeployment(dir string) { +func (p *TestProject) deleteKustomizeDeployment(dir string) { deploymentDir := filepath.Dir(dir) p.updateDeploymentItems(deploymentDir, func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { var newItems []*uo.UnstructuredObject @@ -329,11 +335,11 @@ func (p *testProject) deleteKustomizeDeployment(dir string) { }) } -func (p *testProject) getKluctlProjectRepo() string { +func (p *TestProject) getKluctlProjectRepo() string { return "kluctl-project" } -func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { +func (p *TestProject) Kluctl(argsIn ...string) (string, string, error) { var args []string args = append(args, argsIn...) args = append(args, "--no-update-check") @@ -365,7 +371,7 @@ func (p *testProject) Kluctl(argsIn ...string) (string, string, error) { return stdout, stderr, err } -func (p *testProject) KluctlMust(argsIn ...string) (string, string) { +func (p *TestProject) KluctlMust(argsIn ...string) (string, string) { stdout, stderr, err := p.Kluctl(argsIn...) if err != nil { p.t.Logf(stderr) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index c172f060b..bf64c78a8 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -85,7 +85,7 @@ func startCertServer() (*certServer, error) { return &cs, nil } -func addProxyVars(p *testProject) { +func addProxyVars(p *TestProject) { f := func(idx int, k *test_utils.EnvTestCluster, cs *certServer) { p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_API_HOST=%s", idx, k.RESTConfig().Host)) p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_NAMESPACE=%s", idx, "kube-system")) @@ -97,9 +97,8 @@ func addProxyVars(p *testProject) { f(1, defaultCluster2, certServer2) } -func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *testProject { - p := &testProject{} - p.init(t, k) +func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *TestProject { + p := NewTestProject(t, k) if proxy { addProxyVars(p) @@ -115,13 +114,13 @@ func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[str return p } -func addSecretsSet(p *testProject, name string, varsSources []*uo.UnstructuredObject) { +func addSecretsSet(p *TestProject, name string, varsSources []*uo.UnstructuredObject) { p.updateSecretSet(name, func(secretSet *uo.UnstructuredObject) { _ = secretSet.SetNestedField(varsSources, "vars") }) } -func addSecretsSetToTarget(p *testProject, targetName string, secretSetName string) { +func addSecretsSetToTarget(p *TestProject, targetName string, secretSetName string) { p.updateTarget(targetName, func(target *uo.UnstructuredObject) { l, _, _ := target.GetNestedList("sealingConfig", "secretSets") l = append(l, secretSetName) diff --git a/e2e/sops_test.go b/e2e/sops_test.go index ee9af770a..c7e3f127f 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -13,8 +13,7 @@ func TestSopsVars(t *testing.T) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) @@ -54,8 +53,7 @@ func TestSopsResources(t *testing.T) { k := defaultCluster1 - p := &testProject{} - p.init(t, k) + p := NewTestProject(t, k) createNamespace(t, k, p.testSlug()) diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 073b246c5..9c8f03fae 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -51,14 +51,14 @@ func createSecretObject(data map[string]string, opts resourceOpts) *uo.Unstructu return o } -func addConfigMapDeployment(p *testProject, dir string, data map[string]string, opts resourceOpts) { +func addConfigMapDeployment(p *TestProject, dir string, data map[string]string, opts resourceOpts) { o := createConfigMapObject(data, opts) p.addKustomizeDeployment(dir, []kustomizeResource{ {fmt.Sprintf("configmap-%s.yml", opts.name), "", o}, }, opts.tags) } -func addSecretDeployment(p *testProject, dir string, data map[string]string, opts resourceOpts) { +func addSecretDeployment(p *TestProject, dir string, data map[string]string, opts resourceOpts) { o := createSecretObject(data, opts) fname := fmt.Sprintf("secret-%s.yml", opts.name) p.addKustomizeDeployment(dir, []kustomizeResource{ From 82b5844237f3c39a05680f341d78700712415bda Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 16:51:51 +0100 Subject: [PATCH 0590/2268] tests: Update flux-kluctl-controller CRDs --- e2e/test_resources/kluctl-crds.yaml | 1221 +++------------------------ 1 file changed, 98 insertions(+), 1123 deletions(-) diff --git a/e2e/test_resources/kluctl-crds.yaml b/e2e/test_resources/kluctl-crds.yaml index d16c0d9ea..83b9cffcc 100644 --- a/e2e/test_resources/kluctl-crds.yaml +++ b/e2e/test_resources/kluctl-crds.yaml @@ -51,7 +51,6 @@ spec: metadata: type: object spec: - description: KluctlDeploymentSpec defines the desired state of KluctlDeployment properties: abortOnError: default: false @@ -60,34 +59,42 @@ spec: when calling kluctl. type: boolean args: - additionalProperties: - type: string - description: Args specifies dynamic target args. Only arguments defined - by 'dynamicArgs' of the target are allowed. + description: Args specifies dynamic target args. + type: object + x-kubernetes-preserve-unknown-fields: true + context: + description: If specified, overrides the context to be used. This + will effectively make kluctl ignore the context specified in the + target. + type: string + decryption: + description: Decrypt Kubernetes secrets before applying them on the + cluster. + properties: + provider: + description: Provider is the name of the decryption engine. + enum: + - sops + type: string + secretRef: + description: The secret name containing the private OpenPGP keys + used for decryption. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider type: object - dependsOn: - description: DependsOn may contain a meta.NamespacedObjectReference - slice with references to resources that must be ready before this - kluctl project can be deployed. - items: - description: NamespacedObjectReference contains enough information - to locate the referenced Kubernetes resource object in any namespace. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - name - type: object - type: array deployInterval: description: DeployInterval specifies the interval at which to deploy - the KluctlDeployment. This is independent of the 'Interval' value, - which only causes deployments if some deployment objects have changed. + the KluctlDeployment. It defaults to the Interval value, meaning + that it will re-deploy on every reconciliation. If you set DeployInterval + to a different value, + pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ type: string deployMode: default: full-deploy @@ -96,6 +103,14 @@ spec: - full-deploy - poke-images type: string + deployOnChanges: + default: true + description: DeployOnChanges will cause a re-deployment whenever the + rendered resources change in the deployment. This check is performed + on every reconciliation. This means that a deployment will be triggered + even before the DeployInterval has passed in case something has + changed in the rendered resources. + type: boolean dryRun: default: false description: DryRun instructs kluctl to run everything in dry-run @@ -195,13 +210,16 @@ spec: type: array interval: description: The interval at which to reconcile the KluctlDeployment. + By default, the controller will re-deploy and validate the deployment + on each reconciliation. To override this behavior, change the DeployInterval + and/or ValidateInterval values. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ type: string kubeConfig: description: The KubeConfig for deploying to the target cluster. Specifies the kubeconfig to be used when invoking kluctl. Contexts in this kubeconfig must match the context found in the kluctl target. As - an alternative, RenameContexts can be used to fix non-matching context - names. + an alternative, specify the context to be used via 'context' properties: secretRef: description: SecretRef holds the name of a secret that contains @@ -283,8 +301,9 @@ spec: type: boolean retryInterval: description: The interval at which to retry a previously failed reconciliation. - When not specified, the controller uses the KluctlDeploymentSpec.Interval - value to retry failures. + When not specified, the controller uses the Interval value to retry + failures. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ type: string serviceAccountName: description: The name of the Kubernetes service account to use while @@ -297,20 +316,18 @@ spec: kluctl project. properties: apiVersion: - description: API version of the referent. + description: API version of the referent, if not specified the + Kubernetes preferred version will be used. type: string kind: description: Kind of the referent. - enum: - - GitRepository - - Bucket type: string name: description: Name of the referent. type: string namespace: - description: Namespace of the referent, defaults to the namespace - of the Kubernetes resource object that contains the reference. + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. type: string required: - kind @@ -322,28 +339,43 @@ spec: Defaults to false. type: boolean target: - description: Target specifies the kluctl target to deploy + description: Target specifies the kluctl target to deploy. If not + specified, an empty target is used that has no name and no context. + Use 'TargetName' and 'Context' to specify the name and context in + that case. + maxLength: 63 + minLength: 1 + type: string + targetNameOverride: + description: TargetNameOverride sets or overrides the target name. + This is especially useful when deployment without a target. maxLength: 63 minLength: 1 type: string timeout: description: Timeout for all operations. Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ type: string updateImages: default: false description: UpdateImages instructs kluctl to update dynamic images. Equivalent to using '-u' when calling kluctl. type: boolean + validate: + default: true + description: Validate enables validation after deploying + type: boolean validateInterval: - default: 5m description: ValidateInterval specifies the interval at which to validate the KluctlDeployment. Validation is performed the same way as with - 'kluctl validate -t '. Defaults to 1m. + 'kluctl validate -t '. Defaults to the same value as specified + in Interval. Validate is also performed whenever a deployment is + performed, independent of the value of ValidateInterval + pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ type: string required: - interval - sourceRef - - target type: object status: description: KluctlDeploymentStatus defines the observed state of KluctlDeployment @@ -360,13 +392,14 @@ spec: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: description: lastTransitionTime is the last time the condition @@ -436,253 +469,26 @@ spec: objectsHash: description: ObjectsHash is the hash of all rendered objects type: string - result: - properties: - changedObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - deletedObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - errors: - items: - properties: - error: - type: string - ref: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - required: - - error - - ref - type: object - type: array - hookObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - newObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - orphanObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - seenImages: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - registryImage: - type: string - resultImage: - type: string - versionFilter: - type: string - required: - - image - - resultImage - type: object - type: array - warnings: - items: - properties: - error: - type: string - ref: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - required: - - error - - ref - type: object - type: array - type: object + rawResult: + type: string revision: description: Revision is the source revision. Please note that kluctl projects have dependent git repositories which are not considered in the source revision type: string - targetName: - description: TargetName is the name of the target + target: + type: string + targetNameOverride: type: string time: description: AttemptedAt is the time when the attempt was performed format: date-time type: string required: - - targetName - time type: object + lastHandledDeployAt: + type: string lastHandledReconcileAt: description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can @@ -696,251 +502,22 @@ spec: objectsHash: description: ObjectsHash is the hash of all rendered objects type: string - result: - properties: - changedObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - deletedObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - errors: - items: - properties: - error: - type: string - ref: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - required: - - error - - ref - type: object - type: array - hookObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - newObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - orphanObjects: - items: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - type: array - seenImages: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - registryImage: - type: string - resultImage: - type: string - versionFilter: - type: string - required: - - image - - resultImage - type: object - type: array - warnings: - items: - properties: - error: - type: string - ref: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - required: - - error - - ref - type: object - type: array - type: object + rawResult: + type: string revision: description: Revision is the source revision. Please note that kluctl projects have dependent git repositories which are not considered in the source revision type: string - targetName: - description: TargetName is the name of the target + target: + type: string + targetNameOverride: type: string time: description: AttemptedAt is the time when the attempt was performed format: date-time type: string required: - - targetName - time type: object lastValidateResult: @@ -952,632 +529,30 @@ spec: objectsHash: description: ObjectsHash is the hash of all rendered objects type: string - result: - properties: - errors: - items: - properties: - error: - type: string - ref: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - required: - - error - - ref - type: object - type: array - ready: - type: boolean - results: - items: - properties: - annotation: - type: string - message: - type: string - ref: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - required: - - annotation - - message - - ref - type: object - type: array - warnings: - items: - properties: - error: - type: string - ref: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - required: - - error - - ref - type: object - type: array - required: - - ready - type: object + rawResult: + type: string revision: description: Revision is the source revision. Please note that kluctl projects have dependent git repositories which are not considered in the source revision type: string - targetName: - description: TargetName is the name of the target + target: + type: string + targetNameOverride: type: string time: description: AttemptedAt is the time when the attempt was performed format: date-time type: string required: - - error - - targetName - time type: object observedGeneration: description: ObservedGeneration is the last reconciled generation. format: int64 type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: kluctlmultideployments.flux.kluctl.io -spec: - group: flux.kluctl.io - names: - kind: KluctlMultiDeployment - listKind: KluctlMultiDeploymentList - plural: kluctlmultideployments - singular: kluctlmultideployment - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.targetPattern - name: Pattern - type: string - - jsonPath: .status.targetCount - name: Targets - type: integer - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: KluctlMultiDeployment is the Schema for the kluctlmultideployments - API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: KluctlMultiDeploymentSpec defines the desired state of KluctlMultiDeployment - properties: - dependsOn: - description: DependsOn may contain a meta.NamespacedObjectReference - slice with references to resources that must be ready before this - kluctl project can be deployed. - items: - description: NamespacedObjectReference contains enough information - to locate the referenced Kubernetes resource object in any namespace. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - name - type: object - type: array - interval: - description: The interval at which to reconcile the KluctlDeployment. - type: string - path: - description: Path to the directory containing the .kluctl.yaml file, - or the Defaults to 'None', which translates to the root path of - the SourceRef. - type: string - retryInterval: - description: The interval at which to retry a previously failed reconciliation. - When not specified, the controller uses the KluctlDeploymentSpec.Interval - value to retry failures. - type: string - sourceRef: - description: Reference of the source where the kluctl project is. - The authentication secrets from the source are also used to authenticate - dependent git repositories which are cloned while deploying the - kluctl project. - properties: - apiVersion: - description: API version of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, defaults to the namespace - of the Kubernetes resource object that contains the reference. - type: string - required: - - kind - - name - type: object - suspend: - description: This flag tells the controller to suspend subsequent - kluctl executions, it does not apply to already started executions. - Defaults to false. - type: boolean - targetPattern: - description: TargetPattern is the regex pattern used to match targets + rawTarget: type: string - template: - description: Template is the object template used to create KluctlDeploymet - objects - properties: - spec: - description: Spec is the KluctlDeployment spec to be used as a - template - properties: - abortOnError: - default: false - description: ForceReplaceOnError instructs kluctl to abort - deployments immediately when something fails. Equivalent - to using '--abort-on-error' when calling kluctl. - type: boolean - args: - additionalProperties: - type: string - description: Args specifies dynamic target args. Only arguments - defined by 'dynamicArgs' of the target are allowed. - type: object - deployInterval: - description: DeployInterval specifies the interval at which - to deploy the KluctlDeployment. This is independent of the - 'Interval' value, which only causes deployments if some - deployment objects have changed. - type: string - deployMode: - default: full-deploy - description: DeployMode specifies what deploy mode should - be used - enum: - - full-deploy - - poke-images - type: string - dryRun: - default: false - description: DryRun instructs kluctl to run everything in - dry-run mode. Equivalent to using '--dry-run' when calling - kluctl. - type: boolean - excludeDeploymentDirs: - description: ExcludeDeploymentDirs instructs kluctl to exclude - deployments with the given dir. Equivalent to using '--exclude-deployment-dir' - when calling kluctl. - items: - type: string - type: array - excludeTags: - description: ExcludeTags instructs kluctl to exclude deployments - with given tags. Equivalent to using '--exclude-tag' when - calling kluctl. - items: - type: string - type: array - forceApply: - default: false - description: ForceApply instructs kluctl to force-apply in - case of SSA conflicts. Equivalent to using '--force-apply' - when calling kluctl. - type: boolean - forceReplaceOnError: - default: false - description: ForceReplaceOnError instructs kluctl to force-replace - resources in case a normal replace fails. Equivalent to - using '--force-replace-on-error' when calling kluctl. - type: boolean - images: - description: Images contains a list of fixed image overrides. - Equivalent to using '--fixed-images-file' when calling kluctl. - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - description: ObjectRef contains the information necessary - to locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - registryImage: - type: string - resultImage: - type: string - versionFilter: - type: string - required: - - image - - resultImage - type: object - type: array - includeDeploymentDirs: - description: IncludeDeploymentDirs instructs kluctl to only - include deployments with the given dir. Equivalent to using - '--include-deployment-dir' when calling kluctl. - items: - type: string - type: array - includeTags: - description: IncludeTags instructs kluctl to only include - deployments with given tags. Equivalent to using '--include-tag' - when calling kluctl. - items: - type: string - type: array - interval: - description: The interval at which to reconcile the KluctlDeployment. - type: string - kubeConfig: - description: The KubeConfig for deploying to the target cluster. - Specifies the kubeconfig to be used when invoking kluctl. - Contexts in this kubeconfig must match the context found - in the kluctl target. As an alternative, RenameContexts - can be used to fix non-matching context names. - properties: - secretRef: - description: SecretRef holds the name of a secret that - contains a key with the kubeconfig file as the value. - If no key is set, the key will default to 'value'. The - secret must be in the same namespace as the Kustomization. - It is recommended that the kubeconfig is self-contained, - and the secret is regularly updated if credentials such - as a cloud-access-token expire. Cloud specific `cmd-path` - auth helpers will not function without adding binaries - and credentials to the Pod that is responsible for reconciling - the KluctlDeployment. - properties: - key: - description: Key in the Secret, when not specified - an implementation-specific default key is used. - type: string - name: - description: Name of the Secret. - type: string - required: - - name - type: object - type: object - noWait: - default: false - description: NoWait instructs kluctl to not wait for any resources - to become ready, including hooks. Equivalent to using '--no-wait' - when calling kluctl. - type: boolean - prune: - default: false - description: Prune enables pruning after deploying. - type: boolean - registrySecrets: - description: RegistrySecrets is a list of secret references - to be used for image registry authentication. The secrets - must either have ".dockerconfigjson" included or "registry", - "username" and "password". Additionally, "caFile" and "insecure" - can be specified. - items: - description: LocalObjectReference contains enough information - to locate the referenced Kubernetes resource object. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - type: array - renameContexts: - description: RenameContexts specifies a list of context rename - operations. This is useful when the kluctl target's context - does not match with the contexts found in the kubeconfig - while deploying. This is the case when using kubeconfigs - generated from service accounts, in which case the context - name is always "default". - items: - description: RenameContext specifies a single rename of - a context - properties: - newContext: - description: NewContext is the new name of the context - type: string - oldContext: - description: OldContext is the name of the context to - be renamed - type: string - required: - - newContext - - oldContext - type: object - type: array - replaceOnError: - default: false - description: ReplaceOnError instructs kluctl to replace resources - on error. Equivalent to using '--replace-on-error' when - calling kluctl. - type: boolean - retryInterval: - description: The interval at which to retry a previously failed - reconciliation. When not specified, the controller uses - the KluctlDeploymentSpec.Interval value to retry failures. - type: string - serviceAccountName: - description: The name of the Kubernetes service account to - use while deploying. If not specified, the default service - account is used. - type: string - timeout: - description: Timeout for all operations. Defaults to 'Interval' - duration. - type: string - updateImages: - default: false - description: UpdateImages instructs kluctl to update dynamic - images. Equivalent to using '-u' when calling kluctl. - type: boolean - validateInterval: - default: 5m - description: ValidateInterval specifies the interval at which - to validate the KluctlDeployment. Validation is performed - the same way as with 'kluctl validate -t '. Defaults - to 1m. - type: string - required: - - interval - type: object - required: - - spec - type: object - timeout: - description: Timeout for all operations. Defaults to 'Interval' duration. - type: string - required: - - interval - - sourceRef - - targetPattern - - template - type: object - status: - description: KluctlMultiDeploymentStatus defines the observed state of - KluctlMultiDeployment - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation - attempt. - type: string - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - targetCount: - description: TargetCount is the number of targets detected - type: integer - targets: - description: Targets is the list of detected targets - items: - description: KluctlMultiDeploymentTargetStatus describes the status - of a single target - properties: - kluctlDeploymentName: - description: KluctlDeploymentName is the name of the generated - KluctlDeployment object - type: string - name: - description: Name is the name of the detected target - type: string - required: - - kluctlDeploymentName - - name - type: object - type: array type: object type: object served: true From c5454557f845548588cbd005839bdc8020ca211a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 16:54:53 +0100 Subject: [PATCH 0591/2268] tests: Make multiple functions of TestProject public --- e2e/args_test.go | 42 +++++++++--------- e2e/contexts_test.go | 58 ++++++++++++------------ e2e/deployment_items_test.go | 38 ++++++++-------- e2e/helm_test.go | 80 ++++++++++++++++----------------- e2e/hooks_test.go | 18 ++++---- e2e/inclusion_test.go | 50 ++++++++++----------- e2e/no_target_test.go | 20 ++++----- e2e/project.go | 86 ++++++++++++++++++------------------ e2e/seal_test.go | 44 +++++++++--------- e2e/sops_test.go | 26 +++++------ e2e/utils_resources.go | 4 +- 11 files changed, 233 insertions(+), 233 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index 17e772568..32443a740 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -14,9 +14,9 @@ func testArgs(t *testing.T, deprecated bool) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) - p.updateTarget("test", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test", func(target *uo.UnstructuredObject) { }) args := []any{ @@ -36,12 +36,12 @@ func testArgs(t *testing.T, deprecated bool) { } if deprecated { - p.updateDeploymentYaml(".", func(o *uo.UnstructuredObject) error { + p.UpdateDeploymentYaml(".", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField(args, "args") return nil }) } else { - p.updateKluctlYaml(func(o *uo.UnstructuredObject) error { + p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { _ = o.SetNestedField(args, "args") return nil }) @@ -54,36 +54,36 @@ func testArgs(t *testing.T, deprecated bool) { "d": "{{ args.d | to_json }}", }, resourceOpts{ name: "cm", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a") - cm := k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm := k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "default", "data", "b") assertNestedFieldEquals(t, cm, "na", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") - cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "na", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c") - cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "c", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", "-ad.nested=d") - cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, `{"nested": "d"}`, "data", "d") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", `-ad={"nested": "d2"}`) - cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, `{"nested": "d2"}`, "data", "d") tmpFile, err := os.CreateTemp("", "") @@ -97,7 +97,7 @@ nested: `) p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b", "-ac=c", fmt.Sprintf(`-ad=@%s`, tmpFile.Name())) - cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "d3"}}`, "data", "d") _ = tmpFile.Truncate(0) @@ -111,7 +111,7 @@ d: `) p.KluctlMust("deploy", "--yes", "-t", "test", fmt.Sprintf(`--args-from-file=%s`, tmpFile.Name())) - cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, "a2", "data", "a") assertNestedFieldEquals(t, cm, "default", "data", "b") assertNestedFieldEquals(t, cm, "c2", "data", "c") @@ -137,9 +137,9 @@ func TestArgsFromEnv(t *testing.T) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) - p.updateTarget("test", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test", func(target *uo.UnstructuredObject) { }) addConfigMapDeployment(p, "cm", map[string]string{ @@ -150,11 +150,11 @@ func TestArgsFromEnv(t *testing.T) { "e": `{{ args.e }}`, }, resourceOpts{ name: "cm", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test") - cm := k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm := k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "c"}}`, "data", "c") @@ -170,9 +170,9 @@ func TestArgsFromEnvAndCli(t *testing.T) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) - p.updateTarget("test", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test", func(target *uo.UnstructuredObject) { }) addConfigMapDeployment(p, "cm", map[string]string{ @@ -181,18 +181,18 @@ func TestArgsFromEnvAndCli(t *testing.T) { "c": `{{ args.c }}`, }, resourceOpts{ name: "cm", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "b=b") - cm := k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm := k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "c", "data", "c") // make sure the CLI overrides values from env p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "b=b", "-a", "c=c2") - cm = k.MustGetCoreV1(t, "configmaps", p.testSlug(), "cm") + cm = k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, "a", "data", "a") assertNestedFieldEquals(t, cm, "b", "data", "b") assertNestedFieldEquals(t, cm, "c2", "data", "c") diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index 55296bac2..1f3c5ee45 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -8,14 +8,14 @@ import ( func prepareContextTest(t *testing.T) *TestProject { p := NewTestProject(t, defaultCluster1) - p.mergeKubeconfig(defaultCluster2) + p.MergeKubeconfig(defaultCluster2) - createNamespace(t, defaultCluster1, p.testSlug()) - createNamespace(t, defaultCluster2, p.testSlug()) + createNamespace(t, defaultCluster1, p.TestSlug()) + createNamespace(t, defaultCluster2, p.TestSlug()) addConfigMapDeployment(p, "cm", nil, resourceOpts{ name: "cm", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) return p @@ -26,20 +26,20 @@ func TestContextCurrent(t *testing.T) { p := prepareContextTest(t) - p.updateTarget("test1", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test1", func(target *uo.UnstructuredObject) { // no context set, assume the current one is used }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") - assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster1, p.TestSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.TestSlug(), "cm") - p.updateMergedKubeconfig(func(config *api.Config) { + p.UpdateMergedKubeconfig(func(config *api.Config) { config.CurrentContext = defaultCluster2.Context }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster2, p.TestSlug(), "cm") } func TestContext1(t *testing.T) { @@ -47,13 +47,13 @@ func TestContext1(t *testing.T) { p := prepareContextTest(t) - p.updateTarget("test1", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") - assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster1, p.TestSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.TestSlug(), "cm") } func TestContext2(t *testing.T) { @@ -61,13 +61,13 @@ func TestContext2(t *testing.T) { p := prepareContextTest(t) - p.updateTarget("test1", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") - assertConfigMapNotExists(t, defaultCluster1, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster2, p.TestSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster1, p.TestSlug(), "cm") } func TestContext1And2(t *testing.T) { @@ -75,19 +75,19 @@ func TestContext1And2(t *testing.T) { p := prepareContextTest(t) - p.updateTarget("test1", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") }) - p.updateTarget("test2", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test2", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") - assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster1, p.TestSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.TestSlug(), "cm") p.KluctlMust("deploy", "--yes", "-t", "test2") - assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster2, p.TestSlug(), "cm") } func TestContextSwitch(t *testing.T) { @@ -95,20 +95,20 @@ func TestContextSwitch(t *testing.T) { p := prepareContextTest(t) - p.updateTarget("test1", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") - assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster1, p.TestSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.TestSlug(), "cm") - p.updateTarget("test1", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster2.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster2, p.TestSlug(), "cm") } func TestContextOverride(t *testing.T) { @@ -116,14 +116,14 @@ func TestContextOverride(t *testing.T) { p := prepareContextTest(t) - p.updateTarget("test1", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test1", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") }) p.KluctlMust("deploy", "--yes", "-t", "test1") - assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") - assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster1, p.TestSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.TestSlug(), "cm") p.KluctlMust("deploy", "--yes", "-t", "test1", "--context", defaultCluster2.Context) - assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") + assertConfigMapExists(t, defaultCluster2, p.TestSlug(), "cm") } diff --git a/e2e/deployment_items_test.go b/e2e/deployment_items_test.go index 23d4c0bad..122204df4 100644 --- a/e2e/deployment_items_test.go +++ b/e2e/deployment_items_test.go @@ -12,25 +12,25 @@ func TestKustomize(t *testing.T) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) - p.updateTarget("test", nil) + p.UpdateTarget("test", nil) addConfigMapDeployment(p, "cm", nil, resourceOpts{ name: "cm", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.testSlug(), "cm") + assertConfigMapExists(t, k, p.TestSlug(), "cm") addConfigMapDeployment(p, "cm2", nil, resourceOpts{ name: "cm2", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) p.KluctlMust("deploy", "--yes", "-t", "test", "--dry-run") - assertConfigMapNotExists(t, k, p.testSlug(), "cm2") + assertConfigMapNotExists(t, k, p.TestSlug(), "cm2") p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.testSlug(), "cm2") + assertConfigMapExists(t, k, p.TestSlug(), "cm2") } func TestGeneratedKustomize(t *testing.T) { @@ -40,11 +40,11 @@ func TestGeneratedKustomize(t *testing.T) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) - p.updateTarget("test", nil) + p.UpdateTarget("test", nil) - p.updateDeploymentYaml("", func(o *uo.UnstructuredObject) error { + p.UpdateDeploymentYaml("", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField([]any{ map[string]any{ "path": "generated-kustomize", @@ -52,30 +52,30 @@ func TestGeneratedKustomize(t *testing.T) { }, "deployments") return nil }) - p.updateYaml("generated-kustomize/cm1.yaml", func(o *uo.UnstructuredObject) error { + p.UpdateYaml("generated-kustomize/cm1.yaml", func(o *uo.UnstructuredObject) error { *o = *createConfigMapObject(nil, resourceOpts{ name: "cm1", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) return nil }, "") - p.updateYaml("generated-kustomize/cm2.yaml", func(o *uo.UnstructuredObject) error { + p.UpdateYaml("generated-kustomize/cm2.yaml", func(o *uo.UnstructuredObject) error { *o = *createConfigMapObject(nil, resourceOpts{ name: "cm2", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) return nil }, "") - p.updateYaml("generated-kustomize/cm3._yaml", func(o *uo.UnstructuredObject) error { + p.UpdateYaml("generated-kustomize/cm3._yaml", func(o *uo.UnstructuredObject) error { *o = *createConfigMapObject(nil, resourceOpts{ name: "cm3", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) return nil }, "") p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapExists(t, k, p.testSlug(), "cm1") - assertConfigMapExists(t, k, p.testSlug(), "cm2") - assertConfigMapNotExists(t, k, p.testSlug(), "cm3") + assertConfigMapExists(t, k, p.TestSlug(), "cm1") + assertConfigMapExists(t, k, p.TestSlug(), "cm2") + assertConfigMapNotExists(t, k, p.TestSlug(), "cm3") } diff --git a/e2e/helm_test.go b/e2e/helm_test.go index cb4a588f5..f973570be 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -26,11 +26,11 @@ func addHelmDeployment(p *TestProject, dir string, repoUrl string, chartName, ve chartName = "" } - p.addKustomizeDeployment(dir, []kustomizeResource{ + p.AddKustomizeDeployment(dir, []kustomizeResource{ {name: "helm-rendered.yaml"}, }, nil) - p.updateYaml(filepath.Join(dir, "helm-chart.yaml"), func(o *uo.UnstructuredObject) error { + p.UpdateYaml(filepath.Join(dir, "helm-chart.yaml"), func(o *uo.UnstructuredObject) error { *o = *uo.FromMap(map[string]interface{}{ "helmChart": map[string]any{ "repo": repoUrl, @@ -46,7 +46,7 @@ func addHelmDeployment(p *TestProject, dir string, repoUrl string, chartName, ve }, "") if values != nil { - p.updateYaml(filepath.Join(dir, "helm-values.yaml"), func(o *uo.UnstructuredObject) error { + p.UpdateYaml(filepath.Join(dir, "helm-values.yaml"), func(o *uo.UnstructuredObject) error { *o = *uo.FromMap(values) return nil }, "") @@ -69,7 +69,7 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) user := "" password := "" @@ -82,12 +82,12 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { {ChartName: "test-chart1", Version: "0.1.0"}, }, tc.oci, user, password) - p.updateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) + p.UpdateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) if tc.testAuth { if tc.credsId != "" { - p.updateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + p.UpdateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField(tc.credsId, "helmChart", "credentialsId") return nil }, "") @@ -123,7 +123,7 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { assert.Contains(t, stderr, tc.expectedError) } else { assert.NoError(t, err) - assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") + assertConfigMapExists(t, k, p.TestSlug(), "test-helm1-test-chart1") } } @@ -160,31 +160,31 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, {ChartName: "test-chart1", Version: "0.2.0"}, }, oci, "", "") - p.updateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) + p.UpdateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) p.KluctlMust("helm-pull") assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) p.KluctlMust("deploy", "--yes", "-t", "test") - cm := assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") + cm := assertConfigMapExists(t, k, p.TestSlug(), "test-helm1-test-chart1") v, _, _ := cm.GetNestedString("data", "version") assert.Equal(t, "0.1.0", v) - p.updateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + p.UpdateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField("0.2.0", "helmChart", "chartVersion") return nil }, "") p.KluctlMust("helm-pull") p.KluctlMust("deploy", "--yes", "-t", "test") - cm = assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") + cm = assertConfigMapExists(t, k, p.TestSlug(), "test-helm1-test-chart1") v, _, _ = cm.GetNestedString("data", "version") assert.Equal(t, "0.2.0", v) } @@ -204,7 +204,7 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, @@ -213,12 +213,12 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { {ChartName: "test-chart2", Version: "0.3.0"}, }, oci, "", "") - p.updateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), nil) - addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.testSlug(), nil) - addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.testSlug(), nil) + p.UpdateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) + addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.TestSlug(), nil) + addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.TestSlug(), nil) - p.updateYaml("helm3/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + p.UpdateYaml("helm3/helm-chart.yaml", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField(true, "helmChart", "skipUpdate") return nil }, "") @@ -314,7 +314,7 @@ func TestHelmValues(t *testing.T) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) repoUrl := test_utils.CreateHelmRepo(p.t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, @@ -340,17 +340,17 @@ func TestHelmValues(t *testing.T) { }, } - p.updateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.testSlug(), values1) - addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.testSlug(), values2) - addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.testSlug(), values3) + p.UpdateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), values1) + addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.TestSlug(), values2) + addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.TestSlug(), values3) p.KluctlMust("helm-pull") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") - cm1 := assertConfigMapExists(t, k, p.testSlug(), "test-helm1-test-chart1") - cm2 := assertConfigMapExists(t, k, p.testSlug(), "test-helm2-test-chart2") - cm3 := assertConfigMapExists(t, k, p.testSlug(), "test-helm3-test-chart1") + cm1 := assertConfigMapExists(t, k, p.TestSlug(), "test-helm1-test-chart1") + cm2 := assertConfigMapExists(t, k, p.TestSlug(), "test-helm2-test-chart2") + cm3 := assertConfigMapExists(t, k, p.TestSlug(), "test-helm3-test-chart1") assert.Equal(t, map[string]any{ "a": "x1", @@ -376,26 +376,26 @@ func TestHelmTemplateChartYaml(t *testing.T) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) - createNamespace(t, k, p.testSlug()+"-a") - createNamespace(t, k, p.testSlug()+"-b") + createNamespace(t, k, p.TestSlug()) + createNamespace(t, k, p.TestSlug()+"-a") + createNamespace(t, k, p.TestSlug()+"-b") repoUrl := test_utils.CreateHelmRepo(p.t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, {ChartName: "test-chart2", Version: "0.1.0"}, }, "", "") - p.updateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm-{{ args.a }}", p.testSlug(), nil) - addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm-{{ args.b }}", p.testSlug(), nil) - addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.testSlug()+"-{{ args.a }}", nil) - addHelmDeployment(p, "helm4", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.testSlug()+"-{{ args.b }}", nil) + p.UpdateTarget("test", nil) + addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm-{{ args.a }}", p.TestSlug(), nil) + addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm-{{ args.b }}", p.TestSlug(), nil) + addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.TestSlug()+"-{{ args.a }}", nil) + addHelmDeployment(p, "helm4", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.TestSlug()+"-{{ args.b }}", nil) p.KluctlMust("helm-pull") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") - assertConfigMapExists(t, k, p.testSlug(), "test-helm-a-test-chart1") - assertConfigMapExists(t, k, p.testSlug(), "test-helm-b-test-chart2") - assertConfigMapExists(t, k, p.testSlug()+"-a", "test-helm-ns-test-chart1") - assertConfigMapExists(t, k, p.testSlug()+"-b", "test-helm-ns-test-chart1") + assertConfigMapExists(t, k, p.TestSlug(), "test-helm-a-test-chart1") + assertConfigMapExists(t, k, p.TestSlug(), "test-helm-b-test-chart2") + assertConfigMapExists(t, k, p.TestSlug()+"-a", "test-helm-ns-test-chart1") + assertConfigMapExists(t, k, p.TestSlug()+"-b", "test-helm-ns-test-chart1") } diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 54df6986e..e9e4ae433 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -36,7 +36,7 @@ func (s *hooksTestContext) removeWebhook() { } func (s *hooksTestContext) handleConfigmap(request admission.Request) { - if s.p.testSlug() != request.Namespace { + if s.p.TestSlug() != request.Namespace { return } @@ -87,7 +87,7 @@ func (s *hooksTestContext) addConfigMap(dir string, opts resourceOpts) { o.SetK8sGVKs("", "v1", "ConfigMap") mergeMetadata(o, opts) o.SetNestedField(map[string]interface{}{}, "data") - s.p.addKustomizeResources(dir, []kustomizeResource{ + s.p.AddKustomizeResources(dir, []kustomizeResource{ {fmt.Sprintf("%s.yml", opts.name), "", o}, }) } @@ -104,14 +104,14 @@ func prepareHookTestProject(t *testing.T, hook string, hookDeletionPolicy string s.removeWebhook() }) - createNamespace(s.t, s.k, s.p.testSlug()) + createNamespace(s.t, s.k, s.p.TestSlug()) - s.p.updateTarget("test", nil) + s.p.UpdateTarget("test", nil) - s.p.addKustomizeDeployment("hook", nil, nil) + s.p.AddKustomizeDeployment("hook", nil, nil) - s.addConfigMap("hook", resourceOpts{name: "cm1", namespace: s.p.testSlug()}) - s.addHookConfigMap("hook", resourceOpts{name: "hook1", namespace: s.p.testSlug()}, false, hook, hookDeletionPolicy) + s.addConfigMap("hook", resourceOpts{name: "cm1", namespace: s.p.TestSlug()}) + s.addHookConfigMap("hook", resourceOpts{name: "hook1", namespace: s.p.TestSlug()}, false, hook, hookDeletionPolicy) return s } @@ -124,10 +124,10 @@ func (s *hooksTestContext) ensureHookExecuted(expectedCms ...string) { func (s *hooksTestContext) ensureHookNotExecuted() { _ = s.k.DynamicClient.Resource(corev1.SchemeGroupVersion.WithResource("configmaps")). - Namespace(s.p.testSlug()). + Namespace(s.p.TestSlug()). Delete(context.Background(), "cm1", metav1.DeleteOptions{}) s.p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapNotExists(s.t, s.k, s.p.testSlug(), "cm1") + assertConfigMapNotExists(s.t, s.k, s.p.TestSlug(), "cm1") } func TestHooksPreDeployInitial(t *testing.T) { diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 1a212cc4d..f8e196651 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -12,29 +12,29 @@ func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*TestProject, k := defaultCluster1 p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) - p.updateTarget("test", nil) + p.UpdateTarget("test", nil) - addConfigMapDeployment(p, "cm1", nil, resourceOpts{name: "cm1", namespace: p.testSlug()}) - addConfigMapDeployment(p, "cm2", nil, resourceOpts{name: "cm2", namespace: p.testSlug()}) - addConfigMapDeployment(p, "cm3", nil, resourceOpts{name: "cm3", namespace: p.testSlug(), tags: []string{"tag1", "tag2"}}) - addConfigMapDeployment(p, "cm4", nil, resourceOpts{name: "cm4", namespace: p.testSlug(), tags: []string{"tag1", "tag3"}}) - addConfigMapDeployment(p, "cm5", nil, resourceOpts{name: "cm5", namespace: p.testSlug(), tags: []string{"tag1", "tag4"}}) - addConfigMapDeployment(p, "cm6", nil, resourceOpts{name: "cm6", namespace: p.testSlug(), tags: []string{"tag1", "tag5"}}) - addConfigMapDeployment(p, "cm7", nil, resourceOpts{name: "cm7", namespace: p.testSlug(), tags: []string{"tag1", "tag6"}}) + addConfigMapDeployment(p, "cm1", nil, resourceOpts{name: "cm1", namespace: p.TestSlug()}) + addConfigMapDeployment(p, "cm2", nil, resourceOpts{name: "cm2", namespace: p.TestSlug()}) + addConfigMapDeployment(p, "cm3", nil, resourceOpts{name: "cm3", namespace: p.TestSlug(), tags: []string{"tag1", "tag2"}}) + addConfigMapDeployment(p, "cm4", nil, resourceOpts{name: "cm4", namespace: p.TestSlug(), tags: []string{"tag1", "tag3"}}) + addConfigMapDeployment(p, "cm5", nil, resourceOpts{name: "cm5", namespace: p.TestSlug(), tags: []string{"tag1", "tag4"}}) + addConfigMapDeployment(p, "cm6", nil, resourceOpts{name: "cm6", namespace: p.TestSlug(), tags: []string{"tag1", "tag5"}}) + addConfigMapDeployment(p, "cm7", nil, resourceOpts{name: "cm7", namespace: p.TestSlug(), tags: []string{"tag1", "tag6"}}) if withIncludes { - p.addDeploymentInclude(".", "include1", nil) - addConfigMapDeployment(p, "include1/icm1", nil, resourceOpts{name: "icm1", namespace: p.testSlug(), tags: []string{"itag1", "itag2"}}) + p.AddDeploymentInclude(".", "include1", nil) + addConfigMapDeployment(p, "include1/icm1", nil, resourceOpts{name: "icm1", namespace: p.TestSlug(), tags: []string{"itag1", "itag2"}}) - p.addDeploymentInclude(".", "include2", nil) - addConfigMapDeployment(p, "include2/icm2", nil, resourceOpts{name: "icm2", namespace: p.testSlug()}) - addConfigMapDeployment(p, "include2/icm3", nil, resourceOpts{name: "icm3", namespace: p.testSlug(), tags: []string{"itag3", "itag4"}}) + p.AddDeploymentInclude(".", "include2", nil) + addConfigMapDeployment(p, "include2/icm2", nil, resourceOpts{name: "icm2", namespace: p.TestSlug()}) + addConfigMapDeployment(p, "include2/icm3", nil, resourceOpts{name: "icm3", namespace: p.TestSlug(), tags: []string{"itag3", "itag4"}}) - p.addDeploymentInclude(".", "include3", []string{"itag5"}) - addConfigMapDeployment(p, "include3/icm4", nil, resourceOpts{name: "icm4", namespace: p.testSlug()}) - addConfigMapDeployment(p, "include3/icm5", nil, resourceOpts{name: "icm5", namespace: p.testSlug(), tags: []string{"itag5", "itag6"}}) + p.AddDeploymentInclude(".", "include3", []string{"itag5"}) + addConfigMapDeployment(p, "include3/icm4", nil, resourceOpts{name: "icm4", namespace: p.TestSlug()}) + addConfigMapDeployment(p, "include3/icm5", nil, resourceOpts{name: "icm5", namespace: p.TestSlug(), tags: []string{"itag5", "itag6"}}) } return p, k @@ -49,7 +49,7 @@ func assertExistsHelper(t *testing.T, p *TestProject, k *test_utils.EnvTestClust delete(shouldExists, x) } } - items, err := k.List(corev1.SchemeGroupVersion.WithResource("configmaps"), p.testSlug(), map[string]string{"project_name": p.testSlug()}) + items, err := k.List(corev1.SchemeGroupVersion.WithResource("configmaps"), p.TestSlug(), map[string]string{"project_name": p.TestSlug()}) if err != nil { t.Fatal(err) } @@ -160,7 +160,7 @@ func TestInclusionDeploymentDirs(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test", "--exclude-deployment-dir", "include3/icm5") var a []string - for _, x := range p.listDeploymentItemPathes(".", false) { + for _, x := range p.ListDeploymentItemPathes(".", false) { if x != "icm5" { a = append(a, filepath.Base(x)) } @@ -180,20 +180,20 @@ func TestInclusionPrune(t *testing.T) { doAssertExists(nil, nil) p.KluctlMust("deploy", "--yes", "-t", "test") - doAssertExists(p.listDeploymentItemPathes(".", false), nil) + doAssertExists(p.ListDeploymentItemPathes(".", false), nil) - p.deleteKustomizeDeployment("cm1") + p.DeleteKustomizeDeployment("cm1") p.KluctlMust("prune", "--yes", "-t", "test", "-I", "non-existent-tag") doAssertExists(nil, nil) p.KluctlMust("prune", "--yes", "-t", "test", "-I", "cm1") doAssertExists(nil, []string{"cm1"}) - p.deleteKustomizeDeployment("cm2") + p.DeleteKustomizeDeployment("cm2") p.KluctlMust("prune", "--yes", "-t", "test", "-E", "cm2") doAssertExists(nil, nil) - p.deleteKustomizeDeployment("cm3") + p.DeleteKustomizeDeployment("cm3") p.KluctlMust("prune", "--yes", "-t", "test", "--exclude-deployment-dir", "cm3") doAssertExists(nil, []string{"cm2"}) @@ -211,7 +211,7 @@ func TestInclusionDelete(t *testing.T) { } p.KluctlMust("deploy", "--yes", "-t", "test") - doAssertExists(p.listDeploymentItemPathes(".", false), nil) + doAssertExists(p.ListDeploymentItemPathes(".", false), nil) p.KluctlMust("delete", "--yes", "-t", "test", "-I", "non-existent-tag") doAssertExists(nil, nil) @@ -221,7 +221,7 @@ func TestInclusionDelete(t *testing.T) { p.KluctlMust("delete", "--yes", "-t", "test", "-E", "cm2") var a []string - for _, x := range p.listDeploymentItemPathes(".", false) { + for _, x := range p.ListDeploymentItemPathes(".", false) { if x != "cm1" && x != "cm2" { a = append(a, x) } diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index 433df873d..c3866d521 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -9,23 +9,23 @@ import ( func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *TestProject { p := NewTestProject(t, defaultCluster1) - p.mergeKubeconfig(defaultCluster2) + p.MergeKubeconfig(defaultCluster2) - createNamespace(t, defaultCluster1, p.testSlug()) - createNamespace(t, defaultCluster2, p.testSlug()) + createNamespace(t, defaultCluster1, p.TestSlug()) + createNamespace(t, defaultCluster2, p.TestSlug()) cm := createConfigMapObject(map[string]string{ "targetName": `{{ target.name }}`, "targetContext": `{{ target.context }}`, }, resourceOpts{ name: "cm", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) if withDeploymentYaml { - p.addKustomizeDeployment("cm", []kustomizeResource{{name: "cm.yaml", content: cm}}, nil) + p.AddKustomizeDeployment("cm", []kustomizeResource{{name: "cm.yaml", content: cm}}, nil) } else { - p.addKustomizeResources("", []kustomizeResource{{name: "cm.yaml", content: cm}}) + p.AddKustomizeResources("", []kustomizeResource{{name: "cm.yaml", content: cm}}) err := os.Remove(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "deployment.yml")) assert.NoError(t, err) } @@ -39,22 +39,22 @@ func testNoTarget(t *testing.T, withDeploymentYaml bool) { p := prepareNoTargetTest(t, withDeploymentYaml) p.KluctlMust("deploy", "--yes") - cm := assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") - assertConfigMapNotExists(t, defaultCluster2, p.testSlug(), "cm") + cm := assertConfigMapExists(t, defaultCluster1, p.TestSlug(), "cm") + assertConfigMapNotExists(t, defaultCluster2, p.TestSlug(), "cm") assert.Equal(t, map[string]any{ "targetName": "", "targetContext": defaultCluster1.Context, }, cm.Object["data"]) p.KluctlMust("deploy", "--yes", "-T", "override-name") - cm = assertConfigMapExists(t, defaultCluster1, p.testSlug(), "cm") + cm = assertConfigMapExists(t, defaultCluster1, p.TestSlug(), "cm") assert.Equal(t, map[string]any{ "targetName": "override-name", "targetContext": defaultCluster1.Context, }, cm.Object["data"]) p.KluctlMust("deploy", "--yes", "-T", "override-name", "--context", defaultCluster2.Context) - cm = assertConfigMapExists(t, defaultCluster2, p.testSlug(), "cm") + cm = assertConfigMapExists(t, defaultCluster2, p.TestSlug(), "cm") assert.Equal(t, map[string]any{ "targetName": "override-name", "targetContext": defaultCluster2.Context, diff --git a/e2e/project.go b/e2e/project.go index afbb0fc0c..762506d57 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -35,14 +35,14 @@ func NewTestProject(t *testing.T, k *test_utils.EnvTestCluster) *TestProject { p.gitServer = test_utils.NewGitServer(t) p.gitServer.GitInit(p.getKluctlProjectRepo()) - p.updateKluctlYaml(func(o *uo.UnstructuredObject) error { + p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { return nil }) - p.updateDeploymentYaml(".", func(c *uo.UnstructuredObject) error { + p.UpdateDeploymentYaml(".", func(c *uo.UnstructuredObject) error { return nil }) - tmpFile, err := os.CreateTemp("", p.testSlug()+"-kubeconfig-") + tmpFile, err := os.CreateTemp("", p.TestSlug()+"-kubeconfig-") if err != nil { t.Fatal(err) } @@ -52,20 +52,20 @@ func NewTestProject(t *testing.T, k *test_utils.EnvTestCluster) *TestProject { os.Remove(p.mergedKubeconfig) }) if k != nil { - p.mergeKubeconfig(k) + p.MergeKubeconfig(k) } return p } -func (p *TestProject) testSlug() string { +func (p *TestProject) TestSlug() string { n := p.t.Name() n = xstrings.ToKebabCase(n) n = strings.ReplaceAll(n, "/", "-") return n } -func (p *TestProject) mergeKubeconfig(k *test_utils.EnvTestCluster) { - p.updateMergedKubeconfig(func(config *clientcmdapi.Config) { +func (p *TestProject) MergeKubeconfig(k *test_utils.EnvTestCluster) { + p.UpdateMergedKubeconfig(func(config *clientcmdapi.Config) { nkcfg, err := clientcmd.Load(k.Kubeconfig) if err != nil { p.t.Fatal(err) @@ -78,7 +78,7 @@ func (p *TestProject) mergeKubeconfig(k *test_utils.EnvTestCluster) { }) } -func (p *TestProject) updateMergedKubeconfig(cb func(config *clientcmdapi.Config)) { +func (p *TestProject) UpdateMergedKubeconfig(cb func(config *clientcmdapi.Config)) { mkcfg, err := clientcmd.LoadFromFile(p.mergedKubeconfig) if err != nil { p.t.Fatal(err) @@ -92,30 +92,30 @@ func (p *TestProject) updateMergedKubeconfig(cb func(config *clientcmdapi.Config } } -func (p *TestProject) updateKluctlYaml(update func(o *uo.UnstructuredObject) error) { - p.updateYaml(".kluctl.yml", update, "") +func (p *TestProject) UpdateKluctlYaml(update func(o *uo.UnstructuredObject) error) { + p.UpdateYaml(".kluctl.yml", update, "") } -func (p *TestProject) updateDeploymentYaml(dir string, update func(o *uo.UnstructuredObject) error) { - p.updateYaml(filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { +func (p *TestProject) UpdateDeploymentYaml(dir string, update func(o *uo.UnstructuredObject) error) { + p.UpdateYaml(filepath.Join(dir, "deployment.yml"), func(o *uo.UnstructuredObject) error { if dir == "." { - o.SetNestedField(p.testSlug(), "commonLabels", "project_name") + o.SetNestedField(p.TestSlug(), "commonLabels", "project_name") } return update(o) }, "") } -func (p *TestProject) updateYaml(path string, update func(o *uo.UnstructuredObject) error, message string) { +func (p *TestProject) UpdateYaml(path string, update func(o *uo.UnstructuredObject) error, message string) { p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), path, func(o *uo.UnstructuredObject) error { return update(o) }, message) } -func (p *TestProject) updateFile(path string, update func(f string) (string, error), message string) { +func (p *TestProject) UpdateFile(path string, update func(f string) (string, error), message string) { p.gitServer.UpdateFile(p.getKluctlProjectRepo(), path, update, message) } -func (p *TestProject) getDeploymentYaml(dir string) *uo.UnstructuredObject { +func (p *TestProject) GetDeploymentYaml(dir string) *uo.UnstructuredObject { o, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, "deployment.yml")) if err != nil { p.t.Fatal(err) @@ -123,9 +123,9 @@ func (p *TestProject) getDeploymentYaml(dir string) *uo.UnstructuredObject { return o } -func (p *TestProject) listDeploymentItemPathes(dir string, fullPath bool) []string { +func (p *TestProject) ListDeploymentItemPathes(dir string, fullPath bool) []string { var ret []string - o := p.getDeploymentYaml(dir) + o := p.GetDeploymentYaml(dir) l, _, err := o.GetNestedObjectList("deployments") if err != nil { p.t.Fatal(err) @@ -141,35 +141,35 @@ func (p *TestProject) listDeploymentItemPathes(dir string, fullPath bool) []stri } pth, ok, _ = x.GetNestedString("include") if ok { - ret = append(ret, p.listDeploymentItemPathes(filepath.Join(dir, pth), fullPath)...) + ret = append(ret, p.ListDeploymentItemPathes(filepath.Join(dir, pth), fullPath)...) } } return ret } -func (p *TestProject) updateKustomizeDeployment(dir string, update func(o *uo.UnstructuredObject, wt *git.Worktree) error) { +func (p *TestProject) UpdateKustomizeDeployment(dir string, update func(o *uo.UnstructuredObject, wt *git.Worktree) error) { wt := p.gitServer.GetWorktree(p.getKluctlProjectRepo()) pth := filepath.Join(dir, "kustomization.yml") - p.updateYaml(pth, func(o *uo.UnstructuredObject) error { + p.UpdateYaml(pth, func(o *uo.UnstructuredObject) error { return update(o, wt) }, fmt.Sprintf("Update kustomization.yml for %s", dir)) } -func (p *TestProject) updateTarget(name string, cb func(target *uo.UnstructuredObject)) { - p.updateNamedListItem(uo.KeyPath{"targets"}, name, cb) +func (p *TestProject) UpdateTarget(name string, cb func(target *uo.UnstructuredObject)) { + p.UpdateNamedListItem(uo.KeyPath{"targets"}, name, cb) } func (p *TestProject) updateSecretSet(name string, cb func(secretSet *uo.UnstructuredObject)) { - p.updateNamedListItem(uo.KeyPath{"secretsConfig", "secretSets"}, name, cb) + p.UpdateNamedListItem(uo.KeyPath{"secretsConfig", "secretSets"}, name, cb) } -func (p *TestProject) updateNamedListItem(path uo.KeyPath, name string, cb func(item *uo.UnstructuredObject)) { +func (p *TestProject) UpdateNamedListItem(path uo.KeyPath, name string, cb func(item *uo.UnstructuredObject)) { if cb == nil { cb = func(target *uo.UnstructuredObject) {} } - p.updateKluctlYaml(func(o *uo.UnstructuredObject) error { + p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { l, _, _ := o.GetNestedObjectList(path...) var newList []*uo.UnstructuredObject found := false @@ -194,16 +194,16 @@ func (p *TestProject) updateNamedListItem(path uo.KeyPath, name string, cb func( }) } -func (p *TestProject) updateDeploymentItems(dir string, update func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject) { - p.updateDeploymentYaml(dir, func(o *uo.UnstructuredObject) error { +func (p *TestProject) UpdateDeploymentItems(dir string, update func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject) { + p.UpdateDeploymentYaml(dir, func(o *uo.UnstructuredObject) error { items, _, _ := o.GetNestedObjectList("deployments") items = update(items) return o.SetNestedField(items, "deployments") }) } -func (p *TestProject) addDeploymentItem(dir string, item *uo.UnstructuredObject) { - p.updateDeploymentItems(dir, func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { +func (p *TestProject) AddDeploymentItem(dir string, item *uo.UnstructuredObject) { + p.UpdateDeploymentItems(dir, func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { for _, x := range items { if reflect.DeepEqual(x, item) { return items @@ -214,30 +214,30 @@ func (p *TestProject) addDeploymentItem(dir string, item *uo.UnstructuredObject) }) } -func (p *TestProject) addDeploymentInclude(dir string, includePath string, tags []string) { +func (p *TestProject) AddDeploymentInclude(dir string, includePath string, tags []string) { n := uo.FromMap(map[string]interface{}{ "include": includePath, }) if len(tags) != 0 { n.SetNestedField(tags, "tags") } - p.addDeploymentItem(dir, n) + p.AddDeploymentItem(dir, n) } -func (p *TestProject) addDeploymentIncludes(dir string) { +func (p *TestProject) AddDeploymentIncludes(dir string) { var pp []string for _, x := range strings.Split(dir, "/") { if x != "." { - p.addDeploymentInclude(filepath.Join(pp...), x, nil) + p.AddDeploymentInclude(filepath.Join(pp...), x, nil) } pp = append(pp, x) } } -func (p *TestProject) addKustomizeDeployment(dir string, resources []kustomizeResource, tags []string) { +func (p *TestProject) AddKustomizeDeployment(dir string, resources []kustomizeResource, tags []string) { deploymentDir := filepath.Dir(dir) if deploymentDir != "" { - p.addDeploymentIncludes(deploymentDir) + p.AddDeploymentIncludes(deploymentDir) } absKustomizeDir := filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir) @@ -247,14 +247,14 @@ func (p *TestProject) addKustomizeDeployment(dir string, resources []kustomizeRe p.t.Fatal(err) } - p.updateKustomizeDeployment(dir, func(o *uo.UnstructuredObject, wt *git.Worktree) error { + p.UpdateKustomizeDeployment(dir, func(o *uo.UnstructuredObject, wt *git.Worktree) error { o.SetNestedField("kustomize.config.k8s.io/v1beta1", "apiVersion") o.SetNestedField("Kustomization", "kind") return nil }) - p.addKustomizeResources(dir, resources) - p.updateDeploymentYaml(deploymentDir, func(o *uo.UnstructuredObject) error { + p.AddKustomizeResources(dir, resources) + p.UpdateDeploymentYaml(deploymentDir, func(o *uo.UnstructuredObject) error { d, _, _ := o.GetNestedObjectList("deployments") n := uo.FromMap(map[string]interface{}{ "path": filepath.Base(dir), @@ -294,8 +294,8 @@ type kustomizeResource struct { content interface{} } -func (p *TestProject) addKustomizeResources(dir string, resources []kustomizeResource) { - p.updateKustomizeDeployment(dir, func(o *uo.UnstructuredObject, wt *git.Worktree) error { +func (p *TestProject) AddKustomizeResources(dir string, resources []kustomizeResource) { + p.UpdateKustomizeDeployment(dir, func(o *uo.UnstructuredObject, wt *git.Worktree) error { l, _, _ := o.GetNestedList("resources") for _, r := range resources { l = append(l, r.name) @@ -320,9 +320,9 @@ func (p *TestProject) addKustomizeResources(dir string, resources []kustomizeRes }) } -func (p *TestProject) deleteKustomizeDeployment(dir string) { +func (p *TestProject) DeleteKustomizeDeployment(dir string) { deploymentDir := filepath.Dir(dir) - p.updateDeploymentItems(deploymentDir, func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { + p.UpdateDeploymentItems(deploymentDir, func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { var newItems []*uo.UnstructuredObject for _, item := range items { pth, _, _ := item.GetNestedString("path") diff --git a/e2e/seal_test.go b/e2e/seal_test.go index bf64c78a8..441281150 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -104,12 +104,12 @@ func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[str addProxyVars(p) } - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) addSecretsSet(p, "test", varsSources) addSecretsSetToTarget(p, "test-target", "test") - addSecretDeployment(p, "secret-deployment", secrets, resourceOpts{name: "secret", namespace: p.testSlug()}) + addSecretDeployment(p, "secret-deployment", secrets, resourceOpts{name: "secret", namespace: p.TestSlug()}) return p } @@ -121,7 +121,7 @@ func addSecretsSet(p *TestProject, name string, varsSources []*uo.UnstructuredOb } func addSecretsSetToTarget(p *TestProject, targetName string, secretSetName string) { - p.updateTarget(targetName, func(target *uo.UnstructuredObject) { + p.UpdateTarget(targetName, func(target *uo.UnstructuredObject) { l, _, _ := target.GetNestedList("sealingConfig", "secretSets") l = append(l, secretSetName) _ = target.SetNestedField(l, "sealingConfig", "secretSets") @@ -182,7 +182,7 @@ func TestSeal_WithOperator(t *testing.T) { assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -230,7 +230,7 @@ func TestSeal_WithBootstrap(t *testing.T) { certHash, err := seal.HashPublicKey(cert) assert.NoError(t, err) - assertSealedSecret(t, k, p.testSlug(), "secret", certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret", certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -266,7 +266,7 @@ func TestSeal_MultipleVarSources(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -306,7 +306,7 @@ func TestSeal_MultipleSecretSets(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -341,12 +341,12 @@ func TestSeal_MultipleTargets(t *testing.T) { }) addSecretsSetToTarget(p, "test-target2", "test2") - p.mergeKubeconfig(defaultCluster2) - createNamespace(t, defaultCluster2, p.testSlug()) - p.updateTarget("test-target", func(target *uo.UnstructuredObject) { + p.MergeKubeconfig(defaultCluster2) + createNamespace(t, defaultCluster2, p.TestSlug()) + p.UpdateTarget("test-target", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") }) - p.updateTarget("test-target2", func(target *uo.UnstructuredObject) { + p.UpdateTarget("test-target2", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster2.Context, "context") }) @@ -360,11 +360,11 @@ func TestSeal_MultipleTargets(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") p.KluctlMust("deploy", "--yes", "-t", "test-target2") - assertSealedSecret(t, defaultCluster1, p.testSlug(), "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, defaultCluster1, p.TestSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) - assertSealedSecret(t, defaultCluster2, p.testSlug(), "secret", certServer2.certHash, map[string]string{ + assertSealedSecret(t, defaultCluster2, p.TestSlug(), "secret", certServer2.certHash, map[string]string{ "s1": "v3", "s2": "v4", }) @@ -392,7 +392,7 @@ func TestSeal_MultipleSecrets(t *testing.T) { }, }), }, true) - addSecretDeployment(p, "secret-deployment2", secret2, resourceOpts{name: "secret2", namespace: p.testSlug()}) + addSecretDeployment(p, "secret-deployment2", secret2, resourceOpts{name: "secret2", namespace: p.TestSlug()}) p.KluctlMust("seal", "-t", "test-target") @@ -402,10 +402,10 @@ func TestSeal_MultipleSecrets(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", }) - assertSealedSecret(t, k, p.testSlug(), "secret2", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret2", certServer1.certHash, map[string]string{ "s2": "v2", }) } @@ -433,7 +433,7 @@ func TestSeal_MultipleSecretsInOneFile(t *testing.T) { }), }, true) - secret2Text, _ := yaml.WriteYamlString(createSecretObject(secret2, resourceOpts{name: "secret2", namespace: p.testSlug()})) + secret2Text, _ := yaml.WriteYamlString(createSecretObject(secret2, resourceOpts{name: "secret2", namespace: p.TestSlug()})) p.gitServer.UpdateFile(p.getKluctlProjectRepo(), "secret-deployment/secret-secret.yml.sealme", func(f string) (string, error) { f += "---\n" @@ -448,10 +448,10 @@ func TestSeal_MultipleSecretsInOneFile(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", }) - assertSealedSecret(t, k, p.testSlug(), "secret2", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret2", certServer1.certHash, map[string]string{ "s2": "v2", }) } @@ -472,7 +472,7 @@ func TestSeal_File(t *testing.T) { }), }, true) - p.updateYaml("secret-values.yaml", func(o *uo.UnstructuredObject) error { + p.UpdateYaml("secret-values.yaml", func(o *uo.UnstructuredObject) error { *o = *uo.FromMap(map[string]interface{}{ "secrets": map[string]interface{}{ "s1": "v1", @@ -489,7 +489,7 @@ func TestSeal_File(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) @@ -544,7 +544,7 @@ func TestSeal_Vault(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test-target") - assertSealedSecret(t, k, p.testSlug(), "secret", certServer1.certHash, map[string]string{ + assertSealedSecret(t, k, p.TestSlug(), "secret", certServer1.certHash, map[string]string{ "s1": "v1", "s2": "v2", }) diff --git a/e2e/sops_test.go b/e2e/sops_test.go index c7e3f127f..0e4e6bbf4 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -15,17 +15,17 @@ func TestSopsVars(t *testing.T) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) - p.updateTarget("test", nil) + p.UpdateTarget("test", nil) addConfigMapDeployment(p, "cm", map[string]string{ "v1": "{{ test1.test2 }}", }, resourceOpts{ name: "cm", - namespace: p.testSlug(), + namespace: p.TestSlug(), }) - p.updateDeploymentYaml("", func(o *uo.UnstructuredObject) error { + p.UpdateDeploymentYaml("", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField([]map[string]any{ { "file": "encrypted-vars.yaml", @@ -34,14 +34,14 @@ func TestSopsVars(t *testing.T) { return nil }) - p.updateFile("encrypted-vars.yaml", func(f string) (string, error) { + p.UpdateFile("encrypted-vars.yaml", func(f string) (string, error) { b, _ := sops_test_resources.TestResources.ReadFile("test.yaml") return string(b), nil }, "") p.KluctlMust("deploy", "--yes", "-t", "test") - cm := assertConfigMapExists(t, k, p.testSlug(), "cm") + cm := assertConfigMapExists(t, k, p.TestSlug(), "cm") assertNestedFieldEquals(t, cm, map[string]any{ "v1": "42", }, "data") @@ -55,26 +55,26 @@ func TestSopsResources(t *testing.T) { p := NewTestProject(t, k) - createNamespace(t, k, p.testSlug()) + createNamespace(t, k, p.TestSlug()) - p.updateTarget("test", nil) - p.updateDeploymentYaml("", func(o *uo.UnstructuredObject) error { - _ = o.SetNestedField(p.testSlug(), "overrideNamespace") + p.UpdateTarget("test", nil) + p.UpdateDeploymentYaml("", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(p.TestSlug(), "overrideNamespace") return nil }) - p.addKustomizeDeployment("cm", []kustomizeResource{ + p.AddKustomizeDeployment("cm", []kustomizeResource{ {name: "encrypted-cm.yaml"}, }, nil) - p.updateFile("cm/encrypted-cm.yaml", func(f string) (string, error) { + p.UpdateFile("cm/encrypted-cm.yaml", func(f string) (string, error) { b, _ := sops_test_resources.TestResources.ReadFile("test-configmap.yaml") return string(b), nil }, "") p.KluctlMust("deploy", "--yes", "-t", "test") - cm := assertConfigMapExists(t, k, p.testSlug(), "encrypted-cm") + cm := assertConfigMapExists(t, k, p.TestSlug(), "encrypted-cm") assertNestedFieldEquals(t, cm, map[string]any{ "a": "b", }, "data") diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 9c8f03fae..4df587ec1 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -53,7 +53,7 @@ func createSecretObject(data map[string]string, opts resourceOpts) *uo.Unstructu func addConfigMapDeployment(p *TestProject, dir string, data map[string]string, opts resourceOpts) { o := createConfigMapObject(data, opts) - p.addKustomizeDeployment(dir, []kustomizeResource{ + p.AddKustomizeDeployment(dir, []kustomizeResource{ {fmt.Sprintf("configmap-%s.yml", opts.name), "", o}, }, opts.tags) } @@ -61,7 +61,7 @@ func addConfigMapDeployment(p *TestProject, dir string, data map[string]string, func addSecretDeployment(p *TestProject, dir string, data map[string]string, opts resourceOpts) { o := createSecretObject(data, opts) fname := fmt.Sprintf("secret-%s.yml", opts.name) - p.addKustomizeDeployment(dir, []kustomizeResource{ + p.AddKustomizeDeployment(dir, []kustomizeResource{ {fname, fname + ".sealme", o}, }, opts.tags) } From 65bfbf428501ecd375d7d697b9690e10223f841c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 16:55:55 +0100 Subject: [PATCH 0592/2268] tests: KustomizeResource public --- e2e/helm_test.go | 4 ++-- e2e/hooks_test.go | 2 +- e2e/no_target_test.go | 4 ++-- e2e/project.go | 22 +++++++++++----------- e2e/sops_test.go | 4 ++-- e2e/utils_resources.go | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index f973570be..786ae8487 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -26,8 +26,8 @@ func addHelmDeployment(p *TestProject, dir string, repoUrl string, chartName, ve chartName = "" } - p.AddKustomizeDeployment(dir, []kustomizeResource{ - {name: "helm-rendered.yaml"}, + p.AddKustomizeDeployment(dir, []KustomizeResource{ + {Name: "helm-rendered.yaml"}, }, nil) p.UpdateYaml(filepath.Join(dir, "helm-chart.yaml"), func(o *uo.UnstructuredObject) error { diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index e9e4ae433..2d57815bd 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -87,7 +87,7 @@ func (s *hooksTestContext) addConfigMap(dir string, opts resourceOpts) { o.SetK8sGVKs("", "v1", "ConfigMap") mergeMetadata(o, opts) o.SetNestedField(map[string]interface{}{}, "data") - s.p.AddKustomizeResources(dir, []kustomizeResource{ + s.p.AddKustomizeResources(dir, []KustomizeResource{ {fmt.Sprintf("%s.yml", opts.name), "", o}, }) } diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index c3866d521..367041be0 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -23,9 +23,9 @@ func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *TestProject { }) if withDeploymentYaml { - p.AddKustomizeDeployment("cm", []kustomizeResource{{name: "cm.yaml", content: cm}}, nil) + p.AddKustomizeDeployment("cm", []KustomizeResource{{Name: "cm.yaml", Content: cm}}, nil) } else { - p.AddKustomizeResources("", []kustomizeResource{{name: "cm.yaml", content: cm}}) + p.AddKustomizeResources("", []KustomizeResource{{Name: "cm.yaml", Content: cm}}) err := os.Remove(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "deployment.yml")) assert.NoError(t, err) } diff --git a/e2e/project.go b/e2e/project.go index 762506d57..25ee4be57 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -234,7 +234,7 @@ func (p *TestProject) AddDeploymentIncludes(dir string) { } } -func (p *TestProject) AddKustomizeDeployment(dir string, resources []kustomizeResource, tags []string) { +func (p *TestProject) AddKustomizeDeployment(dir string, resources []KustomizeResource, tags []string) { deploymentDir := filepath.Dir(dir) if deploymentDir != "" { p.AddDeploymentIncludes(deploymentDir) @@ -288,23 +288,23 @@ func (p *TestProject) convertInterfaceToList(x interface{}) []interface{} { return []interface{}{x} } -type kustomizeResource struct { - name string - fileName string - content interface{} +type KustomizeResource struct { + Name string + FileName string + Content interface{} } -func (p *TestProject) AddKustomizeResources(dir string, resources []kustomizeResource) { +func (p *TestProject) AddKustomizeResources(dir string, resources []KustomizeResource) { p.UpdateKustomizeDeployment(dir, func(o *uo.UnstructuredObject, wt *git.Worktree) error { l, _, _ := o.GetNestedList("resources") for _, r := range resources { - l = append(l, r.name) - fileName := r.fileName + l = append(l, r.Name) + fileName := r.FileName if fileName == "" { - fileName = r.name + fileName = r.Name } - if r.content != nil { - x := p.convertInterfaceToList(r.content) + if r.Content != nil { + x := p.convertInterfaceToList(r.Content) err := yaml.WriteYamlAllFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, fileName), x) if err != nil { return err diff --git a/e2e/sops_test.go b/e2e/sops_test.go index 0e4e6bbf4..726c29b1b 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -63,8 +63,8 @@ func TestSopsResources(t *testing.T) { return nil }) - p.AddKustomizeDeployment("cm", []kustomizeResource{ - {name: "encrypted-cm.yaml"}, + p.AddKustomizeDeployment("cm", []KustomizeResource{ + {Name: "encrypted-cm.yaml"}, }, nil) p.UpdateFile("cm/encrypted-cm.yaml", func(f string) (string, error) { diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 4df587ec1..83a5df3e8 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -53,7 +53,7 @@ func createSecretObject(data map[string]string, opts resourceOpts) *uo.Unstructu func addConfigMapDeployment(p *TestProject, dir string, data map[string]string, opts resourceOpts) { o := createConfigMapObject(data, opts) - p.AddKustomizeDeployment(dir, []kustomizeResource{ + p.AddKustomizeDeployment(dir, []KustomizeResource{ {fmt.Sprintf("configmap-%s.yml", opts.name), "", o}, }, opts.tags) } @@ -61,7 +61,7 @@ func addConfigMapDeployment(p *TestProject, dir string, data map[string]string, func addSecretDeployment(p *TestProject, dir string, data map[string]string, opts resourceOpts) { o := createSecretObject(data, opts) fname := fmt.Sprintf("secret-%s.yml", opts.name) - p.AddKustomizeDeployment(dir, []kustomizeResource{ + p.AddKustomizeDeployment(dir, []KustomizeResource{ {fname, fname + ".sealme", o}, }, opts.tags) } From d12955dd49a41904390aa3d0c916732ac7148f39 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 17:05:32 +0100 Subject: [PATCH 0593/2268] tests: Simplify TestProject to only support one git repo --- e2e/helm_test.go | 18 +++++++++--------- e2e/no_target_test.go | 2 +- e2e/project.go | 28 ++++++++++++++++++---------- e2e/seal_test.go | 20 ++++++++++---------- e2e/test-utils/git_server.go | 2 +- pkg/vars/vars_loader_test.go | 4 ++-- 6 files changed, 41 insertions(+), 33 deletions(-) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 786ae8487..0110c3689 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -105,7 +105,7 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { return } else { assert.NoError(t, err) - assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) + assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm1/charts/test-chart1/Chart.yaml")) } } @@ -171,7 +171,7 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) p.KluctlMust("helm-pull") - assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) + assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm1/charts/test-chart1/Chart.yaml")) p.KluctlMust("deploy", "--yes", "-t", "test") cm := assertConfigMapExists(t, k, p.TestSlug(), "test-helm1-test-chart1") v, _, _ := cm.GetNestedString("data", "version") @@ -224,9 +224,9 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { }, "") p.KluctlMust("helm-pull") - assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) - assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm2/charts/test-chart2/Chart.yaml")) - assert.FileExists(t, filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm3/charts/test-chart1/Chart.yaml")) + assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm1/charts/test-chart1/Chart.yaml")) + assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm2/charts/test-chart2/Chart.yaml")) + assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm3/charts/test-chart1/Chart.yaml")) args := []string{"helm-update"} if upgrade { @@ -241,11 +241,11 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { assert.Contains(t, stderr, "helm2: Chart has new version 0.3.0 available.") assert.Contains(t, stderr, "helm3: Chart has new version 0.2.0 available. Old version is 0.1.0. skipUpdate is set to true.") - c1, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm1/charts/test-chart1/Chart.yaml")) + c1, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), "helm1/charts/test-chart1/Chart.yaml")) assert.NoError(t, err) - c2, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm2/charts/test-chart2/Chart.yaml")) + c2, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), "helm2/charts/test-chart2/Chart.yaml")) assert.NoError(t, err) - c3, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "helm3/charts/test-chart1/Chart.yaml")) + c3, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), "helm3/charts/test-chart1/Chart.yaml")) assert.NoError(t, err) v1, _, _ := c1.GetNestedString("version") @@ -262,7 +262,7 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { } if commit { - r := p.gitServer.GetGitRepo(p.getKluctlProjectRepo()) + r := p.GetGitRepo() commits, err := r.Log(&git.LogOptions{}) assert.NoError(t, err) diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index 367041be0..5d92a5c6c 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -26,7 +26,7 @@ func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *TestProject { p.AddKustomizeDeployment("cm", []KustomizeResource{{Name: "cm.yaml", Content: cm}}, nil) } else { p.AddKustomizeResources("", []KustomizeResource{{Name: "cm.yaml", Content: cm}}) - err := os.Remove(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), "deployment.yml")) + err := os.Remove(filepath.Join(p.LocalRepoDir(), "deployment.yml")) assert.NoError(t, err) } diff --git a/e2e/project.go b/e2e/project.go index 25ee4be57..4e1a4a14d 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -33,7 +33,7 @@ func NewTestProject(t *testing.T, k *test_utils.EnvTestCluster) *TestProject { } p.gitServer = test_utils.NewGitServer(t) - p.gitServer.GitInit(p.getKluctlProjectRepo()) + p.gitServer.GitInit("kluctl-project") p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { return nil @@ -106,17 +106,17 @@ func (p *TestProject) UpdateDeploymentYaml(dir string, update func(o *uo.Unstruc } func (p *TestProject) UpdateYaml(path string, update func(o *uo.UnstructuredObject) error, message string) { - p.gitServer.UpdateYaml(p.getKluctlProjectRepo(), path, func(o *uo.UnstructuredObject) error { + p.gitServer.UpdateYaml("kluctl-project", path, func(o *uo.UnstructuredObject) error { return update(o) }, message) } func (p *TestProject) UpdateFile(path string, update func(f string) (string, error), message string) { - p.gitServer.UpdateFile(p.getKluctlProjectRepo(), path, update, message) + p.gitServer.UpdateFile("kluctl-project", path, update, message) } func (p *TestProject) GetDeploymentYaml(dir string) *uo.UnstructuredObject { - o, err := uo.FromFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, "deployment.yml")) + o, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), dir, "deployment.yml")) if err != nil { p.t.Fatal(err) } @@ -148,7 +148,7 @@ func (p *TestProject) ListDeploymentItemPathes(dir string, fullPath bool) []stri } func (p *TestProject) UpdateKustomizeDeployment(dir string, update func(o *uo.UnstructuredObject, wt *git.Worktree) error) { - wt := p.gitServer.GetWorktree(p.getKluctlProjectRepo()) + wt := p.gitServer.GetWorktree("kluctl-project") pth := filepath.Join(dir, "kustomization.yml") p.UpdateYaml(pth, func(o *uo.UnstructuredObject) error { @@ -240,7 +240,7 @@ func (p *TestProject) AddKustomizeDeployment(dir string, resources []KustomizeRe p.AddDeploymentIncludes(deploymentDir) } - absKustomizeDir := filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir) + absKustomizeDir := filepath.Join(p.LocalRepoDir(), dir) err := os.MkdirAll(absKustomizeDir, 0o700) if err != nil { @@ -305,7 +305,7 @@ func (p *TestProject) AddKustomizeResources(dir string, resources []KustomizeRes } if r.Content != nil { x := p.convertInterfaceToList(r.Content) - err := yaml.WriteYamlAllFile(filepath.Join(p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()), dir, fileName), x) + err := yaml.WriteYamlAllFile(filepath.Join(p.LocalRepoDir(), dir, fileName), x) if err != nil { return err } @@ -335,8 +335,16 @@ func (p *TestProject) DeleteKustomizeDeployment(dir string) { }) } -func (p *TestProject) getKluctlProjectRepo() string { - return "kluctl-project" +func (p *TestProject) GitUrl() string { + return p.gitServer.GitUrl("kluctl-project") +} + +func (p *TestProject) LocalRepoDir() string { + return p.gitServer.LocalRepoDir("kluctl-project") +} + +func (p *TestProject) GetGitRepo() *git.Repository { + return p.gitServer.GetGitRepo("kluctl-project") } func (p *TestProject) Kluctl(argsIn ...string) (string, string, error) { @@ -344,7 +352,7 @@ func (p *TestProject) Kluctl(argsIn ...string) (string, string, error) { args = append(args, argsIn...) args = append(args, "--no-update-check") - cwd := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + cwd := p.LocalRepoDir() args = append(args, "--debug") diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 441281150..d80be37b3 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -178,7 +178,7 @@ func TestSeal_WithOperator(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + sealedSecretsDir := p.LocalRepoDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -213,7 +213,7 @@ func TestSeal_WithBootstrap(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + sealedSecretsDir := p.LocalRepoDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) test_resources.ApplyYaml("sealed-secrets.yaml", k) @@ -261,7 +261,7 @@ func TestSeal_MultipleVarSources(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + sealedSecretsDir := p.LocalRepoDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -301,7 +301,7 @@ func TestSeal_MultipleSecretSets(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + sealedSecretsDir := p.LocalRepoDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -353,7 +353,7 @@ func TestSeal_MultipleTargets(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") p.KluctlMust("seal", "-t", "test-target2") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + sealedSecretsDir := p.LocalRepoDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target2/secret-secret.yml")) @@ -396,7 +396,7 @@ func TestSeal_MultipleSecrets(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + sealedSecretsDir := p.LocalRepoDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment2/test-target/secret-secret2.yml")) @@ -435,7 +435,7 @@ func TestSeal_MultipleSecretsInOneFile(t *testing.T) { secret2Text, _ := yaml.WriteYamlString(createSecretObject(secret2, resourceOpts{name: "secret2", namespace: p.TestSlug()})) - p.gitServer.UpdateFile(p.getKluctlProjectRepo(), "secret-deployment/secret-secret.yml.sealme", func(f string) (string, error) { + p.UpdateFile("secret-deployment/secret-secret.yml.sealme", func(f string) (string, error) { f += "---\n" f += secret2Text return f, nil @@ -443,7 +443,7 @@ func TestSeal_MultipleSecretsInOneFile(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + sealedSecretsDir := p.LocalRepoDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -484,7 +484,7 @@ func TestSeal_File(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + sealedSecretsDir := p.LocalRepoDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -539,7 +539,7 @@ func TestSeal_Vault(t *testing.T) { p.extraEnv = append(p.extraEnv, "VAULT_TOKEN=root") p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.gitServer.LocalRepoDir(p.getKluctlProjectRepo()) + sealedSecretsDir := p.LocalRepoDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") diff --git a/e2e/test-utils/git_server.go b/e2e/test-utils/git_server.go index f539dac91..6e45a3031 100644 --- a/e2e/test-utils/git_server.go +++ b/e2e/test-utils/git_server.go @@ -235,7 +235,7 @@ func (p *GitServer) convertInterfaceToList(x interface{}) []interface{} { return []interface{}{x} } -func (p *GitServer) LocalGitUrl(repo string) string { +func (p *GitServer) GitUrl(repo string) string { return fmt.Sprintf("http://localhost:%d/%s/.git", p.gitServerPort, repo) } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 9b46900c8..fc0d510e0 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -163,7 +163,7 @@ func TestVarsLoader_Git(t *testing.T) { }, "") testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { - url, _ := git_url.Parse(gs.LocalGitUrl("repo")) + url, _ := git_url.Parse(gs.GitUrl("repo")) err := vl.LoadVars(vc, &types.VarsSource{ Git: &types.VarsSourceGit{ Url: *url, @@ -199,7 +199,7 @@ func TestVarsLoader_GitBranch(t *testing.T) { assert.NoError(t, err) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { - url, _ := git_url.Parse(gs.LocalGitUrl("repo")) + url, _ := git_url.Parse(gs.GitUrl("repo")) err = vl.LoadVars(vc, &types.VarsSource{ Git: &types.VarsSourceGit{ Url: *url, From ef3c11d54670b529415dfc3ccb60e66e5ab6489d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 17:07:19 +0100 Subject: [PATCH 0594/2268] tests: Implement AddExtraEnv helper --- e2e/project.go | 4 ++++ e2e/seal_test.go | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/e2e/project.go b/e2e/project.go index 4e1a4a14d..8b4cc5364 100644 --- a/e2e/project.go +++ b/e2e/project.go @@ -92,6 +92,10 @@ func (p *TestProject) UpdateMergedKubeconfig(cb func(config *clientcmdapi.Config } } +func (p *TestProject) AddExtraEnv(e string) { + p.extraEnv = append(p.extraEnv, e) +} + func (p *TestProject) UpdateKluctlYaml(update func(o *uo.UnstructuredObject) error) { p.UpdateYaml(".kluctl.yml", update, "") } diff --git a/e2e/seal_test.go b/e2e/seal_test.go index d80be37b3..ab4b87d4c 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -87,11 +87,11 @@ func startCertServer() (*certServer, error) { func addProxyVars(p *TestProject) { f := func(idx int, k *test_utils.EnvTestCluster, cs *certServer) { - p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_API_HOST=%s", idx, k.RESTConfig().Host)) - p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_NAMESPACE=%s", idx, "kube-system")) - p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_NAME=%s", idx, "sealed-secrets-controller")) - p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_PORT=%s", idx, "http")) - p.extraEnv = append(p.extraEnv, fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_LOCAL_URL=%s", idx, cs.url)) + p.AddExtraEnv(fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_API_HOST=%s", idx, k.RESTConfig().Host)) + p.AddExtraEnv(fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_NAMESPACE=%s", idx, "kube-system")) + p.AddExtraEnv(fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_NAME=%s", idx, "sealed-secrets-controller")) + p.AddExtraEnv(fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_PORT=%s", idx, "http")) + p.AddExtraEnv(fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_LOCAL_URL=%s", idx, cs.url)) } f(0, defaultCluster1, certServer1) f(1, defaultCluster2, certServer2) @@ -536,7 +536,7 @@ func TestSeal_Vault(t *testing.T) { }), }, true) - p.extraEnv = append(p.extraEnv, "VAULT_TOKEN=root") + p.AddExtraEnv("VAULT_TOKEN=root") p.KluctlMust("seal", "-t", "test-target") sealedSecretsDir := p.LocalRepoDir() From fdeee82cafe249a6e25120631b8d0794a945de05 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 17:11:53 +0100 Subject: [PATCH 0595/2268] tests: Move TestProject into test-utils package --- e2e/args_test.go | 7 ++--- e2e/contexts_test.go | 5 ++-- e2e/deployment_items_test.go | 5 ++-- e2e/flux_test.go | 2 +- e2e/helm_test.go | 24 ++++++++--------- e2e/hooks_test.go | 6 ++--- e2e/inclusion_test.go | 6 ++--- e2e/no_target_test.go | 9 ++++--- e2e/seal_test.go | 12 ++++----- e2e/sops_test.go | 7 ++--- e2e/{ => test-utils}/project.go | 13 +++++---- e2e/test-utils/run_helper.go | 48 +++++++++++++++++++++++++++++++++ e2e/utils.go | 43 ----------------------------- e2e/utils_resources.go | 9 ++++--- 14 files changed, 103 insertions(+), 93 deletions(-) rename e2e/{ => test-utils}/project.go (96%) create mode 100644 e2e/test-utils/run_helper.go diff --git a/e2e/args_test.go b/e2e/args_test.go index 32443a740..2cfebb50e 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -2,6 +2,7 @@ package e2e import ( "fmt" + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "os" "testing" @@ -12,7 +13,7 @@ func testArgs(t *testing.T, deprecated bool) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) @@ -135,7 +136,7 @@ func TestArgsFromEnv(t *testing.T) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) @@ -168,7 +169,7 @@ func TestArgsFromEnvAndCli(t *testing.T) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index 1f3c5ee45..ef7a203b0 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -1,13 +1,14 @@ package e2e import ( + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/client-go/tools/clientcmd/api" "testing" ) -func prepareContextTest(t *testing.T) *TestProject { - p := NewTestProject(t, defaultCluster1) +func prepareContextTest(t *testing.T) *test_utils.TestProject { + p := test_utils.NewTestProject(t, defaultCluster1) p.MergeKubeconfig(defaultCluster2) createNamespace(t, defaultCluster1, p.TestSlug()) diff --git a/e2e/deployment_items_test.go b/e2e/deployment_items_test.go index 122204df4..2a346b8fe 100644 --- a/e2e/deployment_items_test.go +++ b/e2e/deployment_items_test.go @@ -1,6 +1,7 @@ package e2e import ( + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "testing" ) @@ -10,7 +11,7 @@ func TestKustomize(t *testing.T) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) @@ -38,7 +39,7 @@ func TestGeneratedKustomize(t *testing.T) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) diff --git a/e2e/flux_test.go b/e2e/flux_test.go index b9f49d496..c8e1979b2 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -36,7 +36,7 @@ func TestFluxCommands(t *testing.T) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) var wg sync.WaitGroup wg.Add(2) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 0110c3689..4efa1fc14 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -20,13 +20,13 @@ func createHelmOrOciRepo(t *testing.T, charts []test_utils.RepoChart, oci bool, } } -func addHelmDeployment(p *TestProject, dir string, repoUrl string, chartName, version string, releaseName string, namespace string, values map[string]any) { +func addHelmDeployment(p *test_utils.TestProject, dir string, repoUrl string, chartName, version string, releaseName string, namespace string, values map[string]any) { if registry2.IsOCI(repoUrl) { repoUrl += "/" + chartName chartName = "" } - p.AddKustomizeDeployment(dir, []KustomizeResource{ + p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ {Name: "helm-rendered.yaml"}, }, nil) @@ -67,7 +67,7 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) @@ -78,7 +78,7 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { password = "secret-password" } - repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ + repoUrl := createHelmOrOciRepo(t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, }, tc.oci, user, password) @@ -158,11 +158,11 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) - repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ + repoUrl := createHelmOrOciRepo(t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, {ChartName: "test-chart1", Version: "0.2.0"}, }, oci, "", "") @@ -202,11 +202,11 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) - repoUrl := createHelmOrOciRepo(p.t, []test_utils.RepoChart{ + repoUrl := createHelmOrOciRepo(t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, {ChartName: "test-chart1", Version: "0.2.0"}, {ChartName: "test-chart2", Version: "0.1.0"}, @@ -312,11 +312,11 @@ func TestHelmValues(t *testing.T) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) - repoUrl := test_utils.CreateHelmRepo(p.t, []test_utils.RepoChart{ + repoUrl := test_utils.CreateHelmRepo(t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, {ChartName: "test-chart2", Version: "0.1.0"}, }, "", "") @@ -374,13 +374,13 @@ func TestHelmTemplateChartYaml(t *testing.T) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) createNamespace(t, k, p.TestSlug()+"-a") createNamespace(t, k, p.TestSlug()+"-b") - repoUrl := test_utils.CreateHelmRepo(p.t, []test_utils.RepoChart{ + repoUrl := test_utils.CreateHelmRepo(t, []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, {ChartName: "test-chart2", Version: "0.1.0"}, }, "", "") diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 2d57815bd..766c43e9c 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -18,7 +18,7 @@ type hooksTestContext struct { t *testing.T k *test_utils.EnvTestCluster - p *TestProject + p *test_utils.TestProject seenConfigMaps []string @@ -87,7 +87,7 @@ func (s *hooksTestContext) addConfigMap(dir string, opts resourceOpts) { o.SetK8sGVKs("", "v1", "ConfigMap") mergeMetadata(o, opts) o.SetNestedField(map[string]interface{}{}, "data") - s.p.AddKustomizeResources(dir, []KustomizeResource{ + s.p.AddKustomizeResources(dir, []test_utils.KustomizeResource{ {fmt.Sprintf("%s.yml", opts.name), "", o}, }) } @@ -99,7 +99,7 @@ func prepareHookTestProject(t *testing.T, hook string, hookDeletionPolicy string } s.setupWebhook() - s.p = NewTestProject(t, s.k) + s.p = test_utils.NewTestProject(t, s.k) t.Cleanup(func() { s.removeWebhook() }) diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index f8e196651..08e6f5881 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -8,9 +8,9 @@ import ( "testing" ) -func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*TestProject, *test_utils.EnvTestCluster) { +func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*test_utils.TestProject, *test_utils.EnvTestCluster) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) @@ -40,7 +40,7 @@ func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*TestProject, return p, k } -func assertExistsHelper(t *testing.T, p *TestProject, k *test_utils.EnvTestCluster, shouldExists map[string]bool, add []string, remove []string) { +func assertExistsHelper(t *testing.T, p *test_utils.TestProject, k *test_utils.EnvTestCluster, shouldExists map[string]bool, add []string, remove []string) { for _, x := range add { shouldExists[x] = true } diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index 5d92a5c6c..caa7d217c 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -1,14 +1,15 @@ package e2e import ( + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/stretchr/testify/assert" "os" "path/filepath" "testing" ) -func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *TestProject { - p := NewTestProject(t, defaultCluster1) +func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *test_utils.TestProject { + p := test_utils.NewTestProject(t, defaultCluster1) p.MergeKubeconfig(defaultCluster2) createNamespace(t, defaultCluster1, p.TestSlug()) @@ -23,9 +24,9 @@ func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *TestProject { }) if withDeploymentYaml { - p.AddKustomizeDeployment("cm", []KustomizeResource{{Name: "cm.yaml", Content: cm}}, nil) + p.AddKustomizeDeployment("cm", []test_utils.KustomizeResource{{Name: "cm.yaml", Content: cm}}, nil) } else { - p.AddKustomizeResources("", []KustomizeResource{{Name: "cm.yaml", Content: cm}}) + p.AddKustomizeResources("", []test_utils.KustomizeResource{{Name: "cm.yaml", Content: cm}}) err := os.Remove(filepath.Join(p.LocalRepoDir(), "deployment.yml")) assert.NoError(t, err) } diff --git a/e2e/seal_test.go b/e2e/seal_test.go index ab4b87d4c..ecbf0b69f 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -85,7 +85,7 @@ func startCertServer() (*certServer, error) { return &cs, nil } -func addProxyVars(p *TestProject) { +func addProxyVars(p *test_utils.TestProject) { f := func(idx int, k *test_utils.EnvTestCluster, cs *certServer) { p.AddExtraEnv(fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_API_HOST=%s", idx, k.RESTConfig().Host)) p.AddExtraEnv(fmt.Sprintf("KLUCTL_K8S_SERVICE_PROXY_%d_SERVICE_NAMESPACE=%s", idx, "kube-system")) @@ -97,8 +97,8 @@ func addProxyVars(p *TestProject) { f(1, defaultCluster2, certServer2) } -func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *TestProject { - p := NewTestProject(t, k) +func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *test_utils.TestProject { + p := test_utils.NewTestProject(t, k) if proxy { addProxyVars(p) @@ -114,13 +114,13 @@ func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[str return p } -func addSecretsSet(p *TestProject, name string, varsSources []*uo.UnstructuredObject) { - p.updateSecretSet(name, func(secretSet *uo.UnstructuredObject) { +func addSecretsSet(p *test_utils.TestProject, name string, varsSources []*uo.UnstructuredObject) { + p.UpdateSecretSet(name, func(secretSet *uo.UnstructuredObject) { _ = secretSet.SetNestedField(varsSources, "vars") }) } -func addSecretsSetToTarget(p *TestProject, targetName string, secretSetName string) { +func addSecretsSetToTarget(p *test_utils.TestProject, targetName string, secretSetName string) { p.UpdateTarget(targetName, func(target *uo.UnstructuredObject) { l, _, _ := target.GetNestedList("sealingConfig", "secretSets") l = append(l, secretSetName) diff --git a/e2e/sops_test.go b/e2e/sops_test.go index 726c29b1b..22014d9a0 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -1,6 +1,7 @@ package e2e import ( + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars/sops_test_resources" "go.mozilla.org/sops/v3/age" @@ -13,7 +14,7 @@ func TestSopsVars(t *testing.T) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) @@ -53,7 +54,7 @@ func TestSopsResources(t *testing.T) { k := defaultCluster1 - p := NewTestProject(t, k) + p := test_utils.NewTestProject(t, k) createNamespace(t, k, p.TestSlug()) @@ -63,7 +64,7 @@ func TestSopsResources(t *testing.T) { return nil }) - p.AddKustomizeDeployment("cm", []KustomizeResource{ + p.AddKustomizeDeployment("cm", []test_utils.KustomizeResource{ {Name: "encrypted-cm.yaml"}, }, nil) diff --git a/e2e/project.go b/e2e/test-utils/project.go similarity index 96% rename from e2e/project.go rename to e2e/test-utils/project.go index 8b4cc5364..fdf05fc6b 100644 --- a/e2e/project.go +++ b/e2e/test-utils/project.go @@ -1,11 +1,10 @@ -package e2e +package test_utils import ( "fmt" "github.com/go-git/go-git/v5" "github.com/huandu/xstrings" "github.com/imdario/mergo" - "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/client-go/tools/clientcmd" @@ -24,15 +23,15 @@ type TestProject struct { mergedKubeconfig string - gitServer *test_utils.GitServer + gitServer *GitServer } -func NewTestProject(t *testing.T, k *test_utils.EnvTestCluster) *TestProject { +func NewTestProject(t *testing.T, k *EnvTestCluster) *TestProject { p := &TestProject{ t: t, } - p.gitServer = test_utils.NewGitServer(t) + p.gitServer = NewGitServer(t) p.gitServer.GitInit("kluctl-project") p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { @@ -64,7 +63,7 @@ func (p *TestProject) TestSlug() string { return n } -func (p *TestProject) MergeKubeconfig(k *test_utils.EnvTestCluster) { +func (p *TestProject) MergeKubeconfig(k *EnvTestCluster) { p.UpdateMergedKubeconfig(func(config *clientcmdapi.Config) { nkcfg, err := clientcmd.Load(k.Kubeconfig) if err != nil { @@ -164,7 +163,7 @@ func (p *TestProject) UpdateTarget(name string, cb func(target *uo.UnstructuredO p.UpdateNamedListItem(uo.KeyPath{"targets"}, name, cb) } -func (p *TestProject) updateSecretSet(name string, cb func(secretSet *uo.UnstructuredObject)) { +func (p *TestProject) UpdateSecretSet(name string, cb func(secretSet *uo.UnstructuredObject)) { p.UpdateNamedListItem(uo.KeyPath{"secretsConfig", "secretSets"}, name, cb) } diff --git a/e2e/test-utils/run_helper.go b/e2e/test-utils/run_helper.go new file mode 100644 index 000000000..face1f426 --- /dev/null +++ b/e2e/test-utils/run_helper.go @@ -0,0 +1,48 @@ +package test_utils + +import ( + "bufio" + "bytes" + "io" + "os/exec" + "sync" + "testing" +) + +func runHelper(t *testing.T, cmd *exec.Cmd) (string, string, error) { + stdoutPipe, err := cmd.StdoutPipe() + if err != nil { + return "", "", err + } + stderrPipe, err := cmd.StderrPipe() + if err != nil { + _ = stdoutPipe.Close() + return "", "", err + } + + var wg sync.WaitGroup + stdReader := func(testLogPrefix string, buf io.StringWriter, pipe io.Reader) { + defer wg.Done() + scanner := bufio.NewScanner(pipe) + for scanner.Scan() { + l := scanner.Text() + t.Log(testLogPrefix + l) + _, _ = buf.WriteString(l + "\n") + } + } + + stdoutBuf := bytes.NewBuffer(nil) + stderrBuf := bytes.NewBuffer(nil) + + wg.Add(2) + go stdReader("stdout: ", stdoutBuf, stdoutPipe) + go stdReader("stderr: ", stderrBuf, stderrPipe) + + err = cmd.Start() + if err != nil { + return "", "", err + } + wg.Wait() + err = cmd.Wait() + return stdoutBuf.String(), stderrBuf.String(), err +} diff --git a/e2e/utils.go b/e2e/utils.go index bd8eb8e63..5d4a234a0 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -1,19 +1,14 @@ package e2e import ( - "bufio" - "bytes" "context" "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "io" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "os/exec" "reflect" - "sync" "testing" ) @@ -58,41 +53,3 @@ func assertNestedFieldEquals(t *testing.T, o *uo.UnstructuredObject, expected in t.Fatalf("%v != %v", v, expected) } } - -func runHelper(t *testing.T, cmd *exec.Cmd) (string, string, error) { - stdoutPipe, err := cmd.StdoutPipe() - if err != nil { - return "", "", err - } - stderrPipe, err := cmd.StderrPipe() - if err != nil { - _ = stdoutPipe.Close() - return "", "", err - } - - var wg sync.WaitGroup - stdReader := func(testLogPrefix string, buf io.StringWriter, pipe io.Reader) { - defer wg.Done() - scanner := bufio.NewScanner(pipe) - for scanner.Scan() { - l := scanner.Text() - t.Log(testLogPrefix + l) - _, _ = buf.WriteString(l + "\n") - } - } - - stdoutBuf := bytes.NewBuffer(nil) - stderrBuf := bytes.NewBuffer(nil) - - wg.Add(2) - go stdReader("stdout: ", stdoutBuf, stdoutPipe) - go stdReader("stderr: ", stderrBuf, stderrPipe) - - err = cmd.Start() - if err != nil { - return "", "", err - } - wg.Wait() - err = cmd.Wait() - return stdoutBuf.String(), stderrBuf.String(), err -} diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 83a5df3e8..6d645d980 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -2,6 +2,7 @@ package e2e import ( "fmt" + "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) @@ -51,17 +52,17 @@ func createSecretObject(data map[string]string, opts resourceOpts) *uo.Unstructu return o } -func addConfigMapDeployment(p *TestProject, dir string, data map[string]string, opts resourceOpts) { +func addConfigMapDeployment(p *test_utils.TestProject, dir string, data map[string]string, opts resourceOpts) { o := createConfigMapObject(data, opts) - p.AddKustomizeDeployment(dir, []KustomizeResource{ + p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ {fmt.Sprintf("configmap-%s.yml", opts.name), "", o}, }, opts.tags) } -func addSecretDeployment(p *TestProject, dir string, data map[string]string, opts resourceOpts) { +func addSecretDeployment(p *test_utils.TestProject, dir string, data map[string]string, opts resourceOpts) { o := createSecretObject(data, opts) fname := fmt.Sprintf("secret-%s.yml", opts.name) - p.AddKustomizeDeployment(dir, []KustomizeResource{ + p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ {fname, fname + ".sealme", o}, }, opts.tags) } From 04bb23d138d6d4b7833f699a377ba9766172b421 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 20:58:13 +0100 Subject: [PATCH 0596/2268] tests: Move test-helm-chart into test-utils --- e2e/test-utils/helm_repo_utils.go | 2 +- e2e/{ => test-utils}/test-helm-chart/.helmignore | 0 e2e/{ => test-utils}/test-helm-chart/Chart.yaml | 0 e2e/{ => test-utils}/test-helm-chart/resources.go | 0 e2e/{ => test-utils}/test-helm-chart/templates/_helpers.tpl | 0 e2e/{ => test-utils}/test-helm-chart/templates/configmap.yaml | 0 e2e/{ => test-utils}/test-helm-chart/values.yaml | 0 7 files changed, 1 insertion(+), 1 deletion(-) rename e2e/{ => test-utils}/test-helm-chart/.helmignore (100%) rename e2e/{ => test-utils}/test-helm-chart/Chart.yaml (100%) rename e2e/{ => test-utils}/test-helm-chart/resources.go (100%) rename e2e/{ => test-utils}/test-helm-chart/templates/_helpers.tpl (100%) rename e2e/{ => test-utils}/test-helm-chart/templates/configmap.yaml (100%) rename e2e/{ => test-utils}/test-helm-chart/values.yaml (100%) diff --git a/e2e/test-utils/helm_repo_utils.go b/e2e/test-utils/helm_repo_utils.go index 631b2a677..fc5f422f2 100644 --- a/e2e/test-utils/helm_repo_utils.go +++ b/e2e/test-utils/helm_repo_utils.go @@ -2,7 +2,7 @@ package test_utils import ( "github.com/google/go-containerregistry/pkg/registry" - test_resources "github.com/kluctl/kluctl/v2/e2e/test-helm-chart" + "github.com/kluctl/kluctl/v2/e2e/test-utils/test-helm-chart" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" diff --git a/e2e/test-helm-chart/.helmignore b/e2e/test-utils/test-helm-chart/.helmignore similarity index 100% rename from e2e/test-helm-chart/.helmignore rename to e2e/test-utils/test-helm-chart/.helmignore diff --git a/e2e/test-helm-chart/Chart.yaml b/e2e/test-utils/test-helm-chart/Chart.yaml similarity index 100% rename from e2e/test-helm-chart/Chart.yaml rename to e2e/test-utils/test-helm-chart/Chart.yaml diff --git a/e2e/test-helm-chart/resources.go b/e2e/test-utils/test-helm-chart/resources.go similarity index 100% rename from e2e/test-helm-chart/resources.go rename to e2e/test-utils/test-helm-chart/resources.go diff --git a/e2e/test-helm-chart/templates/_helpers.tpl b/e2e/test-utils/test-helm-chart/templates/_helpers.tpl similarity index 100% rename from e2e/test-helm-chart/templates/_helpers.tpl rename to e2e/test-utils/test-helm-chart/templates/_helpers.tpl diff --git a/e2e/test-helm-chart/templates/configmap.yaml b/e2e/test-utils/test-helm-chart/templates/configmap.yaml similarity index 100% rename from e2e/test-helm-chart/templates/configmap.yaml rename to e2e/test-utils/test-helm-chart/templates/configmap.yaml diff --git a/e2e/test-helm-chart/values.yaml b/e2e/test-utils/test-helm-chart/values.yaml similarity index 100% rename from e2e/test-helm-chart/values.yaml rename to e2e/test-utils/test-helm-chart/values.yaml From 02181c5e80252fd1679f1f0dd9ebf5e3f0028083 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 23:04:58 +0100 Subject: [PATCH 0597/2268] tests: Move addHelmDeployment into TestProject --- e2e/helm_test.go | 58 ++++++++------------------------------- e2e/test-utils/project.go | 34 +++++++++++++++++++++++ 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 4efa1fc14..58b15625d 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -6,7 +6,6 @@ import ( test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" - registry2 "helm.sh/helm/v3/pkg/registry" "path/filepath" "sort" "testing" @@ -20,39 +19,6 @@ func createHelmOrOciRepo(t *testing.T, charts []test_utils.RepoChart, oci bool, } } -func addHelmDeployment(p *test_utils.TestProject, dir string, repoUrl string, chartName, version string, releaseName string, namespace string, values map[string]any) { - if registry2.IsOCI(repoUrl) { - repoUrl += "/" + chartName - chartName = "" - } - - p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ - {Name: "helm-rendered.yaml"}, - }, nil) - - p.UpdateYaml(filepath.Join(dir, "helm-chart.yaml"), func(o *uo.UnstructuredObject) error { - *o = *uo.FromMap(map[string]interface{}{ - "helmChart": map[string]any{ - "repo": repoUrl, - "chartVersion": version, - "releaseName": releaseName, - "namespace": namespace, - }, - }) - if chartName != "" { - _ = o.SetNestedField(chartName, "helmChart", "chartName") - } - return nil - }, "") - - if values != nil { - p.UpdateYaml(filepath.Join(dir, "helm-values.yaml"), func(o *uo.UnstructuredObject) error { - *o = *uo.FromMap(values) - return nil - }, "") - } -} - type testCase struct { name string oci bool @@ -83,7 +49,7 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { }, tc.oci, user, password) p.UpdateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) + p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) if tc.testAuth { if tc.credsId != "" { @@ -168,7 +134,7 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { }, oci, "", "") p.UpdateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) + p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) p.KluctlMust("helm-pull") assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm1/charts/test-chart1/Chart.yaml")) @@ -214,9 +180,9 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { }, oci, "", "") p.UpdateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) - addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.TestSlug(), nil) - addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.TestSlug(), nil) + p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) + p.AddHelmDeployment("helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.TestSlug(), nil) + p.AddHelmDeployment("helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.TestSlug(), nil) p.UpdateYaml("helm3/helm-chart.yaml", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField(true, "helmChart", "skipUpdate") @@ -341,9 +307,9 @@ func TestHelmValues(t *testing.T) { } p.UpdateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), values1) - addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.TestSlug(), values2) - addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.TestSlug(), values3) + p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), values1) + p.AddHelmDeployment("helm2", repoUrl, "test-chart2", "0.1.0", "test-helm2", p.TestSlug(), values2) + p.AddHelmDeployment("helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.TestSlug(), values3) p.KluctlMust("helm-pull") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") @@ -386,10 +352,10 @@ func TestHelmTemplateChartYaml(t *testing.T) { }, "", "") p.UpdateTarget("test", nil) - addHelmDeployment(p, "helm1", repoUrl, "test-chart1", "0.1.0", "test-helm-{{ args.a }}", p.TestSlug(), nil) - addHelmDeployment(p, "helm2", repoUrl, "test-chart2", "0.1.0", "test-helm-{{ args.b }}", p.TestSlug(), nil) - addHelmDeployment(p, "helm3", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.TestSlug()+"-{{ args.a }}", nil) - addHelmDeployment(p, "helm4", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.TestSlug()+"-{{ args.b }}", nil) + p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm-{{ args.a }}", p.TestSlug(), nil) + p.AddHelmDeployment("helm2", repoUrl, "test-chart2", "0.1.0", "test-helm-{{ args.b }}", p.TestSlug(), nil) + p.AddHelmDeployment("helm3", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.TestSlug()+"-{{ args.a }}", nil) + p.AddHelmDeployment("helm4", repoUrl, "test-chart1", "0.1.0", "test-helm-ns", p.TestSlug()+"-{{ args.b }}", nil) p.KluctlMust("helm-pull") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index fdf05fc6b..51183ffef 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -7,6 +7,7 @@ import ( "github.com/imdario/mergo" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" + registry2 "helm.sh/helm/v3/pkg/registry" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "os" @@ -271,6 +272,39 @@ func (p *TestProject) AddKustomizeDeployment(dir string, resources []KustomizeRe }) } +func (p *TestProject) AddHelmDeployment(dir string, repoUrl string, chartName, version string, releaseName string, namespace string, values map[string]any) { + if registry2.IsOCI(repoUrl) { + repoUrl += "/" + chartName + chartName = "" + } + + p.AddKustomizeDeployment(dir, []KustomizeResource{ + {Name: "helm-rendered.yaml"}, + }, nil) + + p.UpdateYaml(filepath.Join(dir, "helm-chart.yaml"), func(o *uo.UnstructuredObject) error { + *o = *uo.FromMap(map[string]interface{}{ + "helmChart": map[string]any{ + "repo": repoUrl, + "chartVersion": version, + "releaseName": releaseName, + "namespace": namespace, + }, + }) + if chartName != "" { + _ = o.SetNestedField(chartName, "helmChart", "chartName") + } + return nil + }, "") + + if values != nil { + p.UpdateYaml(filepath.Join(dir, "helm-values.yaml"), func(o *uo.UnstructuredObject) error { + *o = *uo.FromMap(values) + return nil + }, "") + } +} + func (p *TestProject) convertInterfaceToList(x interface{}) []interface{} { var ret []interface{} if l, ok := x.([]interface{}); ok { From 22d0278238d0751061a30b349f2b79999bd44a96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 22:13:00 +0000 Subject: [PATCH 0598/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.145 to 1.44.146 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.145 to 1.44.146. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.145...v1.44.146) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7108d7e82..7f5badd32 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.145 + github.com/aws/aws-sdk-go v1.44.146 github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index 89ad58cea..b83e8c3c7 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.145 h1:KMVRrIyjBsNz3xGPuHIRnhIuKlb5h3Ii5e5jbi3cgnc= -github.com/aws/aws-sdk-go v1.44.145/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.146 h1:7YdGgPxDPRJu/yYffzZp/H7yHzQ6AqmuNFZPYraaN8I= +github.com/aws/aws-sdk-go v1.44.146/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From 64abf6c5824c515c17b4594db3782c9b34797014 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 22:13:40 +0000 Subject: [PATCH 0599/2268] chore(deps): Bump github.com/Masterminds/semver/v3 from 3.1.1 to 3.2.0 Bumps [github.com/Masterminds/semver/v3](https://github.com/Masterminds/semver) from 3.1.1 to 3.2.0. - [Release notes](https://github.com/Masterminds/semver/releases) - [Changelog](https://github.com/Masterminds/semver/blob/master/CHANGELOG.md) - [Commits](https://github.com/Masterminds/semver/compare/v3.1.1...v3.2.0) --- updated-dependencies: - dependency-name: github.com/Masterminds/semver/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 7108d7e82..202de4dd6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e - github.com/Masterminds/semver/v3 v3.1.1 + github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/aws/aws-sdk-go v1.44.145 github.com/bitnami-labs/sealed-secrets v0.19.2 diff --git a/go.sum b/go.sum index 89ad58cea..c186d2345 100644 --- a/go.sum +++ b/go.sum @@ -88,8 +88,9 @@ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6 github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= From 81a8c7424eb7f2f37c19fe09a1dae0f1e6b2b9a0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 28 Nov 2022 23:07:37 +0100 Subject: [PATCH 0600/2268] fix: Also consider helm credentials when no credentialsId is provided --- e2e/helm_test.go | 2 +- pkg/deployment/helm_chart.go | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 58b15625d..94386b8f8 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -97,7 +97,7 @@ func TestHelmPull(t *testing.T) { tests := []testCase{ {name: "helm-no-creds"}, {name: "helm-creds-missing", oci: false, testAuth: true, credsId: "test-creds", - expectedError: "no credentials provided for Chart test-chart1"}, + expectedError: "401 Unauthorized"}, {name: "helm-creds-invalid", oci: false, testAuth: true, credsId: "test-creds", extraArgs: []string{"--helm-username=test-creds:test-user", "--helm-password=test-creds:invalid"}, expectedError: "401 Unauthorized"}, diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 0d9b4d4cb..dda52347a 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -213,21 +213,22 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { if registry.IsOCI(*c.Config.Repo) { return fmt.Errorf("OCI charts can currently only be authenticated via registry login and not via cli arguments") } - if c.credentials == nil { return fmt.Errorf("no credentials provider") } + } + + if c.credentials != nil { creds := c.credentials.FindCredentials(*c.Config.Repo, c.Config.CredentialsId) - if creds == nil { - return fmt.Errorf("no credentials provided for Chart %s", c.chartName) + if creds != nil { + a.Username = creds.Username + a.Password = creds.Password + a.CertFile = creds.CertFile + a.CaFile = creds.CAFile + a.KeyFile = creds.KeyFile + a.InsecureSkipTLSverify = creds.InsecureSkipTLSverify + a.PassCredentialsAll = creds.PassCredentialsAll } - a.Username = creds.Username - a.Password = creds.Password - a.CertFile = creds.CertFile - a.CaFile = creds.CAFile - a.KeyFile = creds.KeyFile - a.InsecureSkipTLSverify = creds.InsecureSkipTLSverify - a.PassCredentialsAll = creds.PassCredentialsAll } var out string From 511efdc4b08295230e067d0073d0b145be7fa070 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 22:07:31 +0000 Subject: [PATCH 0601/2268] chore(deps): Bump github.com/golang-jwt/jwt/v4 from 4.4.2 to 4.4.3 Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.2 to 4.4.3. - [Release notes](https://github.com/golang-jwt/jwt/releases) - [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md) - [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.2...v4.4.3) --- updated-dependencies: - dependency-name: github.com/golang-jwt/jwt/v4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index aa0533f25..3561dec7e 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect - github.com/golang-jwt/jwt/v4 v4.4.2 + github.com/golang-jwt/jwt/v4 v4.4.3 github.com/google/go-containerregistry v0.12.1 github.com/hashicorp/vault/api v1.8.2 github.com/hexops/gotextdiff v1.0.3 diff --git a/go.sum b/go.sum index 3acee807f..e633e02b6 100644 --- a/go.sum +++ b/go.sum @@ -316,8 +316,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= +github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= From fe837cbe6a7c8f73ef29c30b1082d1217c605b2f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 29 Nov 2022 20:12:56 +0100 Subject: [PATCH 0602/2268] refactor: Remove status handler dependency from git pkg --- pkg/git/mirrored_repo.go | 17 +++++------------ pkg/git/repocache/cache.go | 5 +++++ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index c3a08ff7d..14262b6fd 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -11,7 +11,6 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" _ "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" - "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/rogpeppe/go-internal/lockedfile" "os" @@ -105,7 +104,6 @@ func (g *MirroredGitRepo) Unlock() error { err := g.fileLock.Close() if err != nil { - status.Warning(g.ctx, "Unlock of %s failed: %v", g.fileLockPath, err) return err } g.fileLock = nil @@ -196,7 +194,7 @@ func (g *MirroredGitRepo) cleanupMirrorDir() error { return nil } -func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string) error { +func (g *MirroredGitRepo) update(repoDir string) error { r, err := git.PlainOpen(repoDir) if err != nil { return err @@ -284,10 +282,10 @@ func (g *MirroredGitRepo) update(s *status.StatusContext, repoDir string) error return nil } -func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext) error { +func (g *MirroredGitRepo) cloneOrUpdate() error { initMarker := filepath.Join(g.mirrorDir, ".cache2.init") if utils.IsFile(initMarker) { - return g.update(s, g.mirrorDir) + return g.update(g.mirrorDir) } err := g.cleanupMirrorDir() if err != nil { @@ -314,7 +312,7 @@ func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext) error { return err } - err = g.update(s, tmpMirrorDir) + err = g.update(tmpMirrorDir) if err != nil { return err } @@ -337,14 +335,11 @@ func (g *MirroredGitRepo) cloneOrUpdate(s *status.StatusContext) error { } func (g *MirroredGitRepo) Update() error { - s := status.Start(g.ctx, "Updating git cache for %s", g.url.String()) - err := g.cloneOrUpdate(s) + err := g.cloneOrUpdate() if err != nil { - s.FailedWithMessage(err.Error()) return err } g.hasUpdated = true - s.Success() return nil } @@ -353,8 +348,6 @@ func (g *MirroredGitRepo) CloneProjectByCommit(commit string, targetDir string) panic("tried to clone from a project that is not locked/updated") } - status.Trace(g.ctx, "Cloning git project: url='%s', commit='%s', target='%s'", g.url.String(), commit, targetDir) - err := PoorMansClone(g.mirrorDir, targetDir, &git.CheckoutOptions{Hash: plumbing.NewHash(commit)}) if err != nil { return fmt.Errorf("failed to clone %s from %s: %w", commit, g.url.String(), err) diff --git a/pkg/git/repocache/cache.go b/pkg/git/repocache/cache.go index 59e89ef6f..aa0cb0c91 100644 --- a/pkg/git/repocache/cache.go +++ b/pkg/git/repocache/cache.go @@ -118,10 +118,15 @@ func (e *CacheEntry) Update() error { if time.Now().Sub(e.mr.LastUpdateTime()) <= e.rp.updateInterval { e.mr.SetUpdated(true) } else { + url := e.mr.Url() + s := status.Start(e.rp.ctx, "Updating git cache for %s", url.String()) + defer s.Failed() err := e.mr.Update() if err != nil { + s.FailedWithMessage(err.Error()) return err } + s.Success() } } From baf324c82cf9a93437c8a2591442fd049a6ea7e8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 13:02:28 +0100 Subject: [PATCH 0603/2268] refactor: Get rid of status handler dependency in git package --- cmd/kluctl/commands/utils.go | 11 +++++++- pkg/git/auth/auth_provider.go | 13 ++++++--- pkg/git/auth/env_auth_provider.go | 10 ++++--- pkg/git/auth/git_credentials_file.go | 15 ++++++----- pkg/git/auth/list_auth_provider.go | 32 +++++++++++----------- pkg/git/auth/ssh_auth_provider.go | 38 +++++++++++++------------- pkg/git/auth/ssh_known_hosts.go | 15 +++++------ pkg/git/list_refs.go | 2 -- pkg/git/messages/message_callbacks.go | 39 +++++++++++++++++++++++++++ pkg/git/ssh-pool/ssh_pool.go | 4 +-- pkg/vars/vars_loader_test.go | 2 +- 11 files changed, 118 insertions(+), 63 deletions(-) create mode 100644 pkg/git/messages/message_callbacks.go diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index eab25b232..0a53757ad 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -8,6 +8,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/git/messages" "github.com/kluctl/kluctl/v2/pkg/git/repocache" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" @@ -60,7 +61,15 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b repoOverrides = append(repoOverrides, ro) } - rp := repocache.NewGitRepoCache(ctx, sshPool, auth.NewDefaultAuthProviders(), repoOverrides, projectFlags.GitCacheUpdateInterval) + messageCallbacks := &messages.MessageCallbacks{ + WarningFn: func(s string) { status.Warning(ctx, s) }, + TraceFn: func(s string) { status.Trace(ctx, s) }, + AskForPasswordFn: func(s string) (string, error) { return status.AskForPassword(ctx, s) }, + AskForConfirmationFn: func(s string) bool { return status.AskForConfirmation(ctx, s) }, + } + gitAuth := auth.NewDefaultAuthProviders(messageCallbacks) + + rp := repocache.NewGitRepoCache(ctx, sshPool, gitAuth, repoOverrides, projectFlags.GitCacheUpdateInterval) defer rp.Clear() loadArgs := kluctl_project.LoadKluctlProjectArgs{ diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 0eca67a60..0d77d2cf6 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -4,6 +4,7 @@ import ( "context" "github.com/go-git/go-git/v5/plumbing/transport" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/git/messages" "golang.org/x/crypto/ssh" ) @@ -41,10 +42,14 @@ func (a *GitAuthProviders) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) return AuthMethodAndCA{} } -func NewDefaultAuthProviders() *GitAuthProviders { +func NewDefaultAuthProviders(messageCallbacks *messages.MessageCallbacks) *GitAuthProviders { + if messageCallbacks == nil { + messageCallbacks = &messages.MessageCallbacks{} + } + a := &GitAuthProviders{} - a.RegisterAuthProvider(&GitEnvAuthProvider{}, true) - a.RegisterAuthProvider(&GitCredentialsFileAuthProvider{}, true) - a.RegisterAuthProvider(&GitSshAuthProvider{}, true) + a.RegisterAuthProvider(&GitEnvAuthProvider{MessageCallbacks: *messageCallbacks}, true) + a.RegisterAuthProvider(&GitCredentialsFileAuthProvider{MessageCallbacks: *messageCallbacks}, true) + a.RegisterAuthProvider(&GitSshAuthProvider{MessageCallbacks: *messageCallbacks}, true) return a } diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index 7f692b661..76a4a3c8d 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -2,13 +2,15 @@ package auth import ( "context" + "fmt" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/git/messages" "github.com/kluctl/kluctl/v2/pkg/utils" "os" ) type GitEnvAuthProvider struct { + MessageCallbacks messages.MessageCallbacks } func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { @@ -27,13 +29,13 @@ func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr ssh_key_path, _ := m["SSH_KEY"] - status.Trace(ctx, "GitEnvAuthProvider: adding entry host=%s, pathPrefix=%s, username=%s, ssh_key=%s", e.Host, e.PathPrefix, e.Username, ssh_key_path) + a.MessageCallbacks.Trace(fmt.Sprintf("GitEnvAuthProvider: adding entry host=%s, pathPrefix=%s, username=%s, ssh_key=%s", e.Host, e.PathPrefix, e.Username, ssh_key_path)) if ssh_key_path != "" { ssh_key_path = utils.ExpandPath(ssh_key_path) b, err := os.ReadFile(ssh_key_path) if err != nil { - status.Trace(ctx, "GitEnvAuthProvider: failed to read key %s: %v", ssh_key_path, err) + a.MessageCallbacks.Trace(fmt.Sprintf("GitEnvAuthProvider: failed to read key %s: %v", ssh_key_path, err)) } else { e.SshKey = b } @@ -43,7 +45,7 @@ func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr ca_bundle_path = utils.ExpandPath(ca_bundle_path) b, err := os.ReadFile(ca_bundle_path) if err != nil { - status.Trace(ctx, "GitEnvAuthProvider: failed to read ca bundle %s: %v", ca_bundle_path, err) + a.MessageCallbacks.Trace(fmt.Sprintf("GitEnvAuthProvider: failed to read ca bundle %s: %v", ca_bundle_path, err)) } else { e.CABundle = b } diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index 7ee6775c3..5d3ef3cfd 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -5,14 +5,15 @@ import ( "context" "github.com/go-git/go-git/v5/plumbing/transport/http" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/git/messages" giturls "github.com/whilp/git-urls" "os" "path/filepath" ) type GitCredentialsFileAuthProvider struct { + MessageCallbacks messages.MessageCallbacks } func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { @@ -22,21 +23,21 @@ func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl g home, err := os.UserHomeDir() if err != nil { - status.Warning(ctx, "Could not determine home directory: %v", err) + a.MessageCallbacks.Warning("Could not determine home directory: %v", err) return AuthMethodAndCA{} } - auth := a.tryBuildAuth(ctx, gitUrl, filepath.Join(home, ".git-credentials")) + auth := a.tryBuildAuth(gitUrl, filepath.Join(home, ".git-credentials")) if auth != nil { return *auth } if xdgHome, ok := os.LookupEnv("XDG_CONFIG_HOME"); ok && xdgHome != "" { - auth = a.tryBuildAuth(ctx, gitUrl, filepath.Join(xdgHome, ".git-credentials")) + auth = a.tryBuildAuth(gitUrl, filepath.Join(xdgHome, ".git-credentials")) if auth != nil { return *auth } } else { - auth = a.tryBuildAuth(ctx, gitUrl, filepath.Join(home, ".config/.git-credentials")) + auth = a.tryBuildAuth(gitUrl, filepath.Join(home, ".config/.git-credentials")) if auth != nil { return *auth } @@ -44,14 +45,14 @@ func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl g return AuthMethodAndCA{} } -func (a *GitCredentialsFileAuthProvider) tryBuildAuth(ctx context.Context, gitUrl git_url.GitUrl, gitCredentialsPath string) *AuthMethodAndCA { +func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl git_url.GitUrl, gitCredentialsPath string) *AuthMethodAndCA { if !utils.IsFile(gitCredentialsPath) { return nil } f, err := os.Open(gitCredentialsPath) if err != nil { - status.Warning(ctx, "Failed to open %s: %v", gitCredentialsPath, err) + a.MessageCallbacks.Warning("Failed to open %s: %v", gitCredentialsPath, err) return nil } defer f.Close() diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index 532734c1d..e11cb176c 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -4,12 +4,14 @@ import ( "context" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/plumbing/transport/ssh" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/git/messages" "strings" ) type ListAuthProvider struct { + MessageCallbacks messages.MessageCallbacks + entries []AuthEntry } @@ -30,10 +32,10 @@ func (a *ListAuthProvider) AddEntry(e AuthEntry) { } func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { - status.Trace(ctx, "ListAuthProvider: BuildAuth for %s", gitUrl.String()) - status.Trace(ctx, "ListAuthProvider: path=%s, username=%s, scheme=%s", gitUrl.Path, gitUrl.User.Username(), gitUrl.Scheme) + a.MessageCallbacks.Trace("ListAuthProvider: BuildAuth for %s", gitUrl.String()) + a.MessageCallbacks.Trace("ListAuthProvider: path=%s, username=%s, scheme=%s", gitUrl.Path, gitUrl.User.Username(), gitUrl.Scheme) for _, e := range a.entries { - status.Trace(ctx, "ListAuthProvider: try host=%s, pathPrefix=%s, username=%s", e.Host, e.PathPrefix, e.Username) + a.MessageCallbacks.Trace("ListAuthProvider: try host=%s, pathPrefix=%s, username=%s", e.Host, e.PathPrefix, e.Username) if e.Host != "*" && e.Host != gitUrl.Hostname() { continue @@ -69,29 +71,29 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) if gitUrl.IsSsh() { if e.SshKey == nil { - status.Trace(ctx, "ListAuthProvider: empty ssh key is not accepted") + a.MessageCallbacks.Trace("ListAuthProvider: empty ssh key is not accepted") continue } - status.Trace(ctx, "ListAuthProvider: using username+sshKey") - a, err := ssh.NewPublicKeys(username, e.SshKey, "") + a.MessageCallbacks.Trace("ListAuthProvider: using username+sshKey") + pk, err := ssh.NewPublicKeys(username, e.SshKey, "") if err != nil { - status.Trace(ctx, "ListAuthProvider: failed to parse private key: %v", err) + a.MessageCallbacks.Trace("ListAuthProvider: failed to parse private key: %v", err) } else { - a.HostKeyCallback = buildVerifyHostCallback(ctx, e.KnownHosts) + pk.HostKeyCallback = buildVerifyHostCallback(a.MessageCallbacks, e.KnownHosts) return AuthMethodAndCA{ - AuthMethod: a, + AuthMethod: pk, Hash: func() ([]byte, error) { - return buildHash(a.Signer) + return buildHash(pk.Signer) }, - ClientConfig: a.ClientConfig, + ClientConfig: pk.ClientConfig, } } } else { if e.Password == "" { - status.Trace(ctx, "ListAuthProvider: empty password is not accepted") + a.MessageCallbacks.Trace("ListAuthProvider: empty password is not accepted") continue } - status.Trace(ctx, "ListAuthProvider: using username+password") + a.MessageCallbacks.Trace("ListAuthProvider: using username+password") return AuthMethodAndCA{ AuthMethod: &http.BasicAuth{ Username: username, diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index c1be1b032..1098ef791 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -7,7 +7,7 @@ import ( "fmt" "github.com/kevinburke/ssh_config" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/git/messages" "github.com/kluctl/kluctl/v2/pkg/utils" sshagent "github.com/xanzy/ssh-agent" "golang.org/x/crypto/ssh" @@ -19,6 +19,8 @@ import ( ) type GitSshAuthProvider struct { + MessageCallbacks messages.MessageCallbacks + passphrases map[string][]byte passphrasesMutex sync.Mutex } @@ -44,7 +46,7 @@ func (a *sshDefaultIdentityAndAgent) ClientConfig() (*ssh.ClientConfig, error) { User: a.user, Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Signers)}, } - cc.HostKeyCallback = buildVerifyHostCallback(a.ctx, nil) + cc.HostKeyCallback = buildVerifyHostCallback(a.authProvider.MessageCallbacks, nil) return cc, nil } @@ -53,49 +55,49 @@ func (a *sshDefaultIdentityAndAgent) Signers() ([]ssh.Signer, error) { } func (a *sshDefaultIdentityAndAgent) addDefaultIdentity(gitUrl git_url.GitUrl) { - status.Trace(a.ctx, "trying to add default identity") + a.authProvider.MessageCallbacks.Trace("trying to add default identity") u, err := user.Current() if err != nil { - status.Trace(a.ctx, "No current user: %v", err) + a.authProvider.MessageCallbacks.Trace("No current user: %v", err) } else { path := filepath.Join(u.HomeDir, ".ssh", "id_rsa") signer, err := a.authProvider.readKey(a.ctx, path) if err != nil && !os.IsNotExist(err) { - status.Warning(a.ctx, "Failed to read default identity file for url %s: %v", gitUrl.String(), err) + a.authProvider.MessageCallbacks.Warning("Failed to read default identity file for url %s: %v", gitUrl.String(), err) } else if signer != nil { - status.Trace(a.ctx, "...added '%s' as default identity", path) + a.authProvider.MessageCallbacks.Trace("...added '%s' as default identity", path) a.signers = append(a.signers, signer) } } } func (a *sshDefaultIdentityAndAgent) addConfigIdentities(gitUrl git_url.GitUrl) { - status.Trace(a.ctx, "trying to add identities from ssh config") + a.authProvider.MessageCallbacks.Trace("trying to add identities from ssh config") for _, id := range ssh_config.GetAll(gitUrl.Hostname(), "IdentityFile") { expanded := utils.ExpandPath(id) - status.Trace(a.ctx, "...trying '%s' (expanded: '%s')", id, expanded) + a.authProvider.MessageCallbacks.Trace("...trying '%s' (expanded: '%s')", id, expanded) signer, err := a.authProvider.readKey(a.ctx, expanded) if err != nil && !os.IsNotExist(err) { - status.Warning(a.ctx, "Failed to read key %s for url %s: %v", id, gitUrl.String(), err) + a.authProvider.MessageCallbacks.Warning("Failed to read key %s for url %s: %v", id, gitUrl.String(), err) } else if err == nil { - status.Trace(a.ctx, "...added '%s' from ssh config", expanded) + a.authProvider.MessageCallbacks.Trace("...added '%s' from ssh config", expanded) a.signers = append(a.signers, signer) } } } func (a *sshDefaultIdentityAndAgent) addAgentIdentities(gitUrl git_url.GitUrl) { - status.Trace(a.ctx, "trying to add agent keys") + a.authProvider.MessageCallbacks.Trace("trying to add agent keys") agent, _, err := sshagent.New() if err != nil { - status.Warning(a.ctx, "Failed to connect to ssh agent for url %s: %v", gitUrl.String(), err) + a.authProvider.MessageCallbacks.Warning("Failed to connect to ssh agent for url %s: %v", gitUrl.String(), err) } else { signers, err := agent.Signers() if err != nil { - status.Warning(a.ctx, "Failed to get signers from ssh agent for url %s: %v", gitUrl.String(), err) + a.authProvider.MessageCallbacks.Warning("Failed to get signers from ssh agent for url %s: %v", gitUrl.String(), err) return } - status.Trace(a.ctx, "...added %d agent keys", len(signers)) + a.authProvider.MessageCallbacks.Trace("...added %d agent keys", len(signers)) a.signers = append(a.signers, signers...) } } @@ -157,7 +159,7 @@ func (k *deferredPassphraseKey) getPassphrase() ([]byte, error) { return passphrase, nil } - passphraseStr, err := status.AskForPassword(k.ctx, fmt.Sprintf("Enter passphrase for key '%s'", k.path)) + passphraseStr, err := k.a.MessageCallbacks.AskForPassword(fmt.Sprintf("Enter passphrase for key '%s'", k.path)) if err != nil { k.a.passphrases[k.path] = nil return nil, err @@ -171,21 +173,21 @@ func (k *deferredPassphraseKey) parse() { passphrase, err := k.getPassphrase() if err != nil { k.err = err - status.Warning(k.ctx, "Failed to parse key %s: %v", k.path, err) + k.a.MessageCallbacks.Warning("Failed to parse key %s: %v", k.path, err) return } pemBytes, err := os.ReadFile(k.path) if err != nil { k.err = err - status.Warning(k.ctx, "Failed to parse key %s: %v", k.path, err) + k.a.MessageCallbacks.Warning("Failed to parse key %s: %v", k.path, err) return } s, err := ssh.ParsePrivateKeyWithPassphrase(pemBytes, passphrase) if err != nil { k.err = err - status.Warning(k.ctx, "Failed to parse key %s: %v", k.path, err) + k.a.MessageCallbacks.Warning("Failed to parse key %s: %v", k.path, err) return } k.parsed = s diff --git a/pkg/git/auth/ssh_known_hosts.go b/pkg/git/auth/ssh_known_hosts.go index ec5df2966..0d0e9b713 100644 --- a/pkg/git/auth/ssh_known_hosts.go +++ b/pkg/git/auth/ssh_known_hosts.go @@ -1,10 +1,9 @@ package auth import ( - "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/git/auth/goph" - "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/git/messages" "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/crypto/ssh" "net" @@ -16,13 +15,13 @@ import ( var askHostMutex sync.Mutex -func buildVerifyHostCallback(ctx context.Context, knownHosts []byte) func(hostname string, remote net.Addr, key ssh.PublicKey) error { +func buildVerifyHostCallback(messageCallbacks messages.MessageCallbacks, knownHosts []byte) func(hostname string, remote net.Addr, key ssh.PublicKey) error { return func(hostname string, remote net.Addr, key ssh.PublicKey) error { - return verifyHost(ctx, hostname, remote, key, knownHosts) + return verifyHost(messageCallbacks, hostname, remote, key, knownHosts) } } -func verifyHost(ctx context.Context, host string, remote net.Addr, key ssh.PublicKey, knownHosts []byte) error { +func verifyHost(messageCallbacks messages.MessageCallbacks, host string, remote net.Addr, key ssh.PublicKey, knownHosts []byte) error { // Ensure only one prompt happens at a time askHostMutex.Lock() defer askHostMutex.Unlock() @@ -87,14 +86,14 @@ func verifyHost(ctx context.Context, host string, remote net.Addr, key ssh.Publi return fmt.Errorf("host not found and SSH_KNOWN_HOSTS has been set") } - if !askIsHostTrusted(ctx, host, key) { + if !askIsHostTrusted(messageCallbacks, host, key) { return fmt.Errorf("aborted") } return goph.AddKnownHost(host, remote, key, "") } -func askIsHostTrusted(ctx context.Context, host string, key ssh.PublicKey) bool { +func askIsHostTrusted(messageCallbacks messages.MessageCallbacks, host string, key ssh.PublicKey) bool { prompt := fmt.Sprintf("Unknown Host: %s\nFingerprint: %s\nWould you like to add it? ", host, ssh.FingerprintSHA256(key)) - return status.AskForConfirmation(ctx, prompt) + return messageCallbacks.AskForConfirmation(prompt) } diff --git a/pkg/git/list_refs.go b/pkg/git/list_refs.go index d529c952c..735234d6b 100644 --- a/pkg/git/list_refs.go +++ b/pkg/git/list_refs.go @@ -12,7 +12,6 @@ import ( auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" - "github.com/kluctl/kluctl/v2/pkg/status" "strconv" ) @@ -100,7 +99,6 @@ func ListRemoteRefs(ctx context.Context, url git_url.GitUrl, sshPool *ssh_pool.S if err == nil { return refs, nil } - status.Warning(ctx, "Fast listing of remote refs failed: %s", err.Error()) } return ListRemoteRefsSlow(ctx, url, auth) } diff --git a/pkg/git/messages/message_callbacks.go b/pkg/git/messages/message_callbacks.go new file mode 100644 index 000000000..e9d8715c4 --- /dev/null +++ b/pkg/git/messages/message_callbacks.go @@ -0,0 +1,39 @@ +package messages + +import "fmt" + +type MessageCallbacks struct { + WarningFn func(s string) + TraceFn func(s string) + AskForPasswordFn func(prompt string) (string, error) + AskForConfirmationFn func(prompt string) bool +} + +func (l MessageCallbacks) Warning(s string, args ...any) { + if l.WarningFn != nil { + l.WarningFn(fmt.Sprintf(s, args...)) + } +} + +func (l MessageCallbacks) Trace(s string, args ...any) { + if l.TraceFn != nil { + l.TraceFn(fmt.Sprintf(s, args...)) + } +} + +func (l MessageCallbacks) AskForPassword(prompt string) (string, error) { + if l.AskForPasswordFn != nil { + return l.AskForPasswordFn(prompt) + } + err := fmt.Errorf("AskForPasswordFn not provided, skipping prompt: %s", prompt) + l.Warning(err.Error()) + return "", err +} + +func (l MessageCallbacks) AskForConfirmation(prompt string) bool { + if l.AskForConfirmationFn != nil { + return l.AskForConfirmationFn(prompt) + } + l.Warning("Not a terminal, suppressed prompt: %s", prompt) + return false +} diff --git a/pkg/git/ssh-pool/ssh_pool.go b/pkg/git/ssh-pool/ssh_pool.go index fb60c220c..7b33a4f1c 100644 --- a/pkg/git/ssh-pool/ssh_pool.go +++ b/pkg/git/ssh-pool/ssh_pool.go @@ -7,7 +7,6 @@ import ( "encoding/hex" "fmt" "github.com/kluctl/kluctl/v2/pkg/git/auth" - "github.com/kluctl/kluctl/v2/pkg/status" "golang.org/x/crypto/ssh" "golang.org/x/net/proxy" "sync" @@ -78,7 +77,6 @@ func (p *SshPool) GetSession(ctx context.Context, host string, port int, auth au s, err := pe.pc.client.NewSession() if err != nil { - origErr := err _ = pe.pc.client.Close() pe.pc = nil if isNew { @@ -92,7 +90,7 @@ func (p *SshPool) GetSession(ctx context.Context, host string, port int, auth au p.pool.Delete(h) return nil, err } - status.Trace(ctx, "Successfully retries failed ssh connection. Old error: %s", origErr) + pe.pc = &poolClient{ time: time.Now(), client: client, diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index fc0d510e0..719315987 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -42,7 +42,7 @@ func newTestDir(t *testing.T) string { } func newRP(t *testing.T) *repocache.GitRepoCache { - grc := repocache.NewGitRepoCache(context.TODO(), &ssh_pool.SshPool{}, auth.NewDefaultAuthProviders(), nil, 0) + grc := repocache.NewGitRepoCache(context.TODO(), &ssh_pool.SshPool{}, auth.NewDefaultAuthProviders(nil), nil, 0) t.Cleanup(func() { grc.Clear() }) From 68f2c00951300c9c8695358345785d7e24a5c791 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 13:03:02 +0100 Subject: [PATCH 0604/2268] refactor: Make git env prefix dynamic --- cmd/kluctl/commands/utils.go | 2 +- pkg/git/auth/auth_provider.go | 4 ++-- pkg/git/auth/env_auth_provider.go | 4 +++- pkg/vars/vars_loader_test.go | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 0a53757ad..e3c827be0 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -67,7 +67,7 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b AskForPasswordFn: func(s string) (string, error) { return status.AskForPassword(ctx, s) }, AskForConfirmationFn: func(s string) bool { return status.AskForConfirmation(ctx, s) }, } - gitAuth := auth.NewDefaultAuthProviders(messageCallbacks) + gitAuth := auth.NewDefaultAuthProviders("KLUCTL_GIT", messageCallbacks) rp := repocache.NewGitRepoCache(ctx, sshPool, gitAuth, repoOverrides, projectFlags.GitCacheUpdateInterval) defer rp.Clear() diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 0d77d2cf6..19b2d418e 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -42,13 +42,13 @@ func (a *GitAuthProviders) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) return AuthMethodAndCA{} } -func NewDefaultAuthProviders(messageCallbacks *messages.MessageCallbacks) *GitAuthProviders { +func NewDefaultAuthProviders(envPrefix string, messageCallbacks *messages.MessageCallbacks) *GitAuthProviders { if messageCallbacks == nil { messageCallbacks = &messages.MessageCallbacks{} } a := &GitAuthProviders{} - a.RegisterAuthProvider(&GitEnvAuthProvider{MessageCallbacks: *messageCallbacks}, true) + a.RegisterAuthProvider(&GitEnvAuthProvider{MessageCallbacks: *messageCallbacks, Prefix: envPrefix}, true) a.RegisterAuthProvider(&GitCredentialsFileAuthProvider{MessageCallbacks: *messageCallbacks}, true) a.RegisterAuthProvider(&GitSshAuthProvider{MessageCallbacks: *messageCallbacks}, true) return a diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index 76a4a3c8d..7dfa737fb 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -11,12 +11,14 @@ import ( type GitEnvAuthProvider struct { MessageCallbacks messages.MessageCallbacks + + Prefix string } func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { var la ListAuthProvider - for _, m := range utils.ParseEnvConfigSets("KLUCTL_GIT") { + for _, m := range utils.ParseEnvConfigSets(a.Prefix) { e := AuthEntry{ Host: m["HOST"], PathPrefix: m["PATH_PREFIX"], diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 719315987..30d6ce755 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -42,7 +42,7 @@ func newTestDir(t *testing.T) string { } func newRP(t *testing.T) *repocache.GitRepoCache { - grc := repocache.NewGitRepoCache(context.TODO(), &ssh_pool.SshPool{}, auth.NewDefaultAuthProviders(nil), nil, 0) + grc := repocache.NewGitRepoCache(context.TODO(), &ssh_pool.SshPool{}, auth.NewDefaultAuthProviders("KLUCTL_GIT", nil), nil, 0) t.Cleanup(func() { grc.Clear() }) From 18083d897aeba5ba76c08485f78419a53dea5ee1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 13:04:21 +0100 Subject: [PATCH 0605/2268] refactor: Get rid of pkg/utils dependency from git package --- pkg/git/auth/env_auth_provider.go | 4 +-- pkg/git/auth/git_credentials_file.go | 4 +-- pkg/git/auth/ssh_auth_provider.go | 3 +-- pkg/git/auth/utils.go | 13 ++++++++++ pkg/git/mirrored_repo.go | 37 ++++++++++++++++++---------- pkg/git/repocache/cache.go | 2 +- pkg/git/utils.go | 5 ++-- 7 files changed, 46 insertions(+), 22 deletions(-) create mode 100644 pkg/git/auth/utils.go diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index 7dfa737fb..d88c54e89 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -34,7 +34,7 @@ func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr a.MessageCallbacks.Trace(fmt.Sprintf("GitEnvAuthProvider: adding entry host=%s, pathPrefix=%s, username=%s, ssh_key=%s", e.Host, e.PathPrefix, e.Username, ssh_key_path)) if ssh_key_path != "" { - ssh_key_path = utils.ExpandPath(ssh_key_path) + ssh_key_path = expandHomeDir(ssh_key_path) b, err := os.ReadFile(ssh_key_path) if err != nil { a.MessageCallbacks.Trace(fmt.Sprintf("GitEnvAuthProvider: failed to read key %s: %v", ssh_key_path, err)) @@ -44,7 +44,7 @@ func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr } ca_bundle_path := m["CA_BUNDLE"] if ca_bundle_path != "" { - ca_bundle_path = utils.ExpandPath(ca_bundle_path) + ca_bundle_path = expandHomeDir(ca_bundle_path) b, err := os.ReadFile(ca_bundle_path) if err != nil { a.MessageCallbacks.Trace(fmt.Sprintf("GitEnvAuthProvider: failed to read ca bundle %s: %v", ca_bundle_path, err)) diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index 5d3ef3cfd..68989ca85 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -5,7 +5,6 @@ import ( "context" "github.com/go-git/go-git/v5/plumbing/transport/http" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/git/messages" giturls "github.com/whilp/git-urls" "os" @@ -46,7 +45,8 @@ func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl g } func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl git_url.GitUrl, gitCredentialsPath string) *AuthMethodAndCA { - if !utils.IsFile(gitCredentialsPath) { + st, err := os.Stat(gitCredentialsPath) + if err != nil || !st.Mode().IsDir() { return nil } diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index 1098ef791..2e95cb864 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -8,7 +8,6 @@ import ( "github.com/kevinburke/ssh_config" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" - "github.com/kluctl/kluctl/v2/pkg/utils" sshagent "github.com/xanzy/ssh-agent" "golang.org/x/crypto/ssh" "io" @@ -74,7 +73,7 @@ func (a *sshDefaultIdentityAndAgent) addDefaultIdentity(gitUrl git_url.GitUrl) { func (a *sshDefaultIdentityAndAgent) addConfigIdentities(gitUrl git_url.GitUrl) { a.authProvider.MessageCallbacks.Trace("trying to add identities from ssh config") for _, id := range ssh_config.GetAll(gitUrl.Hostname(), "IdentityFile") { - expanded := utils.ExpandPath(id) + expanded := expandHomeDir(id) a.authProvider.MessageCallbacks.Trace("...trying '%s' (expanded: '%s')", id, expanded) signer, err := a.authProvider.readKey(a.ctx, expanded) if err != nil && !os.IsNotExist(err) { diff --git a/pkg/git/auth/utils.go b/pkg/git/auth/utils.go new file mode 100644 index 000000000..ff0c9e834 --- /dev/null +++ b/pkg/git/auth/utils.go @@ -0,0 +1,13 @@ +package auth + +import ( + "k8s.io/client-go/util/homedir" + "strings" +) + +func expandHomeDir(p string) string { + if strings.HasPrefix(p, "~/") { + p = homedir.HomeDir() + p[1:] + } + return p +} diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 14262b6fd..10e2e805c 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -2,6 +2,8 @@ package git import ( "context" + "crypto/sha256" + "encoding/hex" "fmt" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" @@ -11,7 +13,6 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" _ "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/rogpeppe/go-internal/lockedfile" "os" "path/filepath" @@ -20,8 +21,6 @@ import ( "time" ) -var cacheBaseDir = filepath.Join(utils.GetTmpBaseDir(), "git-cache") - var defaultFetch = []config.RefSpec{ "+refs/heads/*:refs/heads/*", "+refs/tags/*:refs/tags/*", @@ -30,6 +29,7 @@ var defaultFetch = []config.RefSpec{ type MirroredGitRepo struct { ctx context.Context + baseDir string sshPool *ssh_pool.SshPool authProviders *auth2.GitAuthProviders @@ -44,21 +44,25 @@ type MirroredGitRepo struct { mutex sync.Mutex } -func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl, sshPool *ssh_pool.SshPool, authProviders *auth2.GitAuthProviders) (*MirroredGitRepo, error) { +func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl, baseDir string, sshPool *ssh_pool.SshPool, authProviders *auth2.GitAuthProviders) (*MirroredGitRepo, error) { mirrorRepoName := buildMirrorRepoName(u) o := &MirroredGitRepo{ ctx: ctx, + baseDir: baseDir, sshPool: sshPool, authProviders: authProviders, url: u, - mirrorDir: filepath.Join(cacheBaseDir, mirrorRepoName), + mirrorDir: filepath.Join(baseDir, mirrorRepoName), } - if !utils.IsDirectory(o.mirrorDir) { - err := os.MkdirAll(o.mirrorDir, 0o700) + st, err := os.Stat(o.mirrorDir) + if err != nil { + err = os.MkdirAll(o.mirrorDir, 0o700) if err != nil { return nil, fmt.Errorf("failed to create mirror repo for %v: %w", u.String(), err) } + } else if !st.IsDir() { + return nil, fmt.Errorf("%s is not a directory", o.mirrorDir) } o.fileLockPath = filepath.Join(o.mirrorDir, ".cache.lock") @@ -179,7 +183,8 @@ func (g *MirroredGitRepo) buildRepositoryObject() (*git.Repository, error) { } func (g *MirroredGitRepo) cleanupMirrorDir() error { - if utils.IsDirectory(g.mirrorDir) { + st, err := os.Stat(g.mirrorDir) + if err == nil && st.IsDir() { files, err := os.ReadDir(g.mirrorDir) if err != nil { return err @@ -284,15 +289,16 @@ func (g *MirroredGitRepo) update(repoDir string) error { func (g *MirroredGitRepo) cloneOrUpdate() error { initMarker := filepath.Join(g.mirrorDir, ".cache2.init") - if utils.IsFile(initMarker) { + st, err := os.Stat(initMarker) + if err == nil && st.Mode().IsRegular() { return g.update(g.mirrorDir) } - err := g.cleanupMirrorDir() + err = g.cleanupMirrorDir() if err != nil { return err } - tmpMirrorDir, err := os.MkdirTemp(utils.GetTmpBaseDir(), "mirror-") + tmpMirrorDir, err := os.MkdirTemp(g.baseDir, "tmp-mirror-") if err != nil { return err } @@ -327,10 +333,11 @@ func (g *MirroredGitRepo) cloneOrUpdate() error { return err } } - err = utils.Touch(initMarker) + f, err := os.Create(initMarker) if err != nil { return err } + defer f.Close() return nil } @@ -383,12 +390,16 @@ func (g *MirroredGitRepo) GetGitTreeByCommit(commitHash string) (*object.Tree, e } func buildMirrorRepoName(u git_url.GitUrl) string { + h := sha256.New() + h.Write([]byte(u.String())) + h2 := hex.EncodeToString(h.Sum(nil)) + r := filepath.Base(u.Path) r = strings.ReplaceAll(r, "/", "-") r = strings.ReplaceAll(r, "\\", "-") if strings.HasSuffix(r, ".git") { r = r[:len(r)-len(".git")] } - r += "-" + utils.Sha256String(u.String())[:6] + r += "-" + h2[:6] return r } diff --git a/pkg/git/repocache/cache.go b/pkg/git/repocache/cache.go index aa0cb0c91..1aecfa179 100644 --- a/pkg/git/repocache/cache.go +++ b/pkg/git/repocache/cache.go @@ -86,7 +86,7 @@ func (rp *GitRepoCache) GetEntry(url git_url.GitUrl) (*CacheEntry, error) { e, ok := rp.repos[url.NormalizedRepoKey()] if !ok { - mr, err := git.NewMirroredGitRepo(rp.ctx, url, rp.sshPool, rp.authProviders) + mr, err := git.NewMirroredGitRepo(rp.ctx, url, filepath.Join(utils.GetTmpBaseDir(), "git-cache"), rp.sshPool, rp.authProviders) if err != nil { return nil, err } diff --git a/pkg/git/utils.go b/pkg/git/utils.go index 0c0bb72d0..f007fa3ee 100644 --- a/pkg/git/utils.go +++ b/pkg/git/utils.go @@ -3,7 +3,7 @@ package git import ( "fmt" "github.com/go-git/go-git/v5" - "github.com/kluctl/kluctl/v2/pkg/utils" + "os" "path/filepath" ) @@ -32,7 +32,8 @@ func DetectGitRepositoryRoot(path string) (string, error) { return "", err } for true { - if utils.Exists(filepath.Join(path, ".git")) { + st, err := os.Stat(filepath.Join(path, ".git")) + if err == nil && st.IsDir() { break } old := path From 580352ec507d2972c9b03c84bc7a5761511a4601 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 13:13:58 +0100 Subject: [PATCH 0606/2268] refactor: Remove custom Copy functions from utils and use otiai10/copy instead --- e2e/test-utils/helm_repo_utils.go | 20 ++++++-- e2e/test_resources/resources.go | 7 ++- go.mod | 1 + go.sum | 7 +++ pkg/git/poor_mans_clone.go | 14 +++--- pkg/git/repocache/cache.go | 3 +- pkg/utils/fs.go | 79 ------------------------------- 7 files changed, 37 insertions(+), 94 deletions(-) diff --git a/e2e/test-utils/helm_repo_utils.go b/e2e/test-utils/helm_repo_utils.go index fc5f422f2..aabe7835b 100644 --- a/e2e/test-utils/helm_repo_utils.go +++ b/e2e/test-utils/helm_repo_utils.go @@ -3,9 +3,9 @@ package test_utils import ( "github.com/google/go-containerregistry/pkg/registry" "github.com/kluctl/kluctl/v2/e2e/test-utils/test-helm-chart" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" + cp "github.com/otiai10/copy" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli/values" @@ -14,6 +14,7 @@ import ( registry2 "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/repo" "helm.sh/helm/v3/pkg/uploader" + "io/fs" "net/http" "net/http/httptest" "net/url" @@ -25,7 +26,18 @@ import ( func createHelmPackage(t *testing.T, name string, version string) string { tmpDir := t.TempDir() - err := utils.FsCopyDir(test_resources.HelmChartFS, ".", tmpDir) + + err := fs.WalkDir(test_resources.HelmChartFS, ".", func(path string, d fs.DirEntry, err error) error { + if d.IsDir() { + return os.MkdirAll(filepath.Join(tmpDir, path), 0o700) + } else { + b, err := test_resources.HelmChartFS.ReadFile(path) + if err != nil { + return err + } + return os.WriteFile(filepath.Join(tmpDir, path), b, 0o600) + } + }) if err != nil { t.Fatal(err) } @@ -67,7 +79,7 @@ func CreateHelmRepo(t *testing.T, charts []RepoChart, username string, password for _, c := range charts { tgz := createHelmPackage(t, c.ChartName, c.Version) - _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) + _ = cp.Copy(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) } fs := http.FileServer(http.FS(os.DirFS(tmpDir))) @@ -147,7 +159,7 @@ func CreateOciRepo(t *testing.T, charts []RepoChart, username string, password s for _, chart := range charts { tgz := createHelmPackage(t, chart.ChartName, chart.Version) - _ = utils.CopyFile(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) + _ = cp.Copy(tgz, filepath.Join(tmpDir, filepath.Base(tgz))) err := c.UploadTo(tgz, ociUrl) if err != nil { diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go index fa1754bff..23dad66c9 100644 --- a/e2e/test_resources/resources.go +++ b/e2e/test_resources/resources.go @@ -4,7 +4,6 @@ import ( "context" "embed" "github.com/kluctl/kluctl/v2/e2e/test-utils" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -28,7 +27,11 @@ func GetYamlTmpFile(name string) string { } tmpFile.Close() - err = utils.FsCopyFile(Yamls, name, tmpFile.Name()) + b, err := Yamls.ReadFile(name) + if err != nil { + panic(err) + } + err = os.WriteFile(tmpFile.Name(), b, 0o600) if err != nil { panic(err) } diff --git a/go.mod b/go.mod index aa0533f25..ae00085ce 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.3.3 + github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.3 sigs.k8s.io/controller-runtime v0.13.1 ) diff --git a/go.sum b/go.sum index 3acee807f..2a56447bd 100644 --- a/go.sum +++ b/go.sum @@ -663,6 +663,13 @@ github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7X github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4= +github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.4.0 h1:umwcf7gbpEwf7WFzqmWwSv0CzbeMsae2u9ZvpP8j2q4= +github.com/otiai10/mint v1.4.0/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= diff --git a/pkg/git/poor_mans_clone.go b/pkg/git/poor_mans_clone.go index f5f306924..975112564 100644 --- a/pkg/git/poor_mans_clone.go +++ b/pkg/git/poor_mans_clone.go @@ -3,7 +3,7 @@ package git import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" - "github.com/kluctl/kluctl/v2/pkg/utils" + cp "github.com/otiai10/copy" "os" "path/filepath" "runtime" @@ -32,15 +32,13 @@ func PoorMansClone(sourceDir string, targetDir string, coOptions *git.CheckoutOp if de.Name() == "objects" { err = os.Symlink(s, d) if err != nil && runtime.GOOS == "windows" { - // windows 10 does not support symlinks as unprivileged users... - err = utils.CopyDir(s, d) + // Windows 10 does not support symlinks as unprivileged users, so we revert to deep copying + err = cp.Copy(s, d, cp.Options{OnSymlink: func(src string) cp.SymlinkAction { + return cp.Deep + }}) } } else { - if de.IsDir() { - err = utils.CopyDir(s, d) - } else { - err = utils.CopyFile(s, d) - } + err = cp.Copy(s, d) } if err != nil { return err diff --git a/pkg/git/repocache/cache.go b/pkg/git/repocache/cache.go index 1aecfa179..88d20f53a 100644 --- a/pkg/git/repocache/cache.go +++ b/pkg/git/repocache/cache.go @@ -9,6 +9,7 @@ import ( ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" + cp "github.com/otiai10/copy" "os" "path" "path/filepath" @@ -235,7 +236,7 @@ func (e *CacheEntry) GetClonedDir(ref string) (string, git.CheckoutInfo, error) if foundRo != nil { u := e.mr.Url() status.WarningOnce(e.rp.ctx, fmt.Sprintf("git-override-%s|%s", foundRo.RepoKey, foundRo.Ref), "Overriding git repo %s with local directory %s", u.String(), foundRo.Override) - err = utils.CopyDir(foundRo.Override, p) + err = cp.Copy(foundRo.Override, p) if err != nil { return "", git.CheckoutInfo{}, err } diff --git a/pkg/utils/fs.go b/pkg/utils/fs.go index 0b25e1e4b..1c6909e00 100644 --- a/pkg/utils/fs.go +++ b/pkg/utils/fs.go @@ -2,11 +2,8 @@ package utils import ( "fmt" - "io" - "io/fs" "k8s.io/client-go/util/homedir" "os" - "path" "path/filepath" "strings" ) @@ -69,82 +66,6 @@ func Touch(path string) error { return f.Close() } -func CopyFile(src string, dst string) error { - if !IsFile(src) { - return fmt.Errorf("%s is not a regular file", src) - } - f, err := os.Open(src) - if err != nil { - return err - } - defer f.Close() - return CopyFileStream(f, dst) -} - -func FsCopyFile(srcFs fs.FS, src, dst string) error { - src = filepath.ToSlash(src) - source, err := srcFs.Open(src) - if err != nil { - return err - } - defer source.Close() - - sourceFileStat, err := source.Stat() - if err != nil { - return err - } - - if !sourceFileStat.Mode().IsRegular() { - return fmt.Errorf("%s is not a regular file", src) - } - - return CopyFileStream(source, dst) -} - -func CopyFileStream(src io.Reader, dst string) error { - destination, err := os.Create(dst) - if err != nil { - return err - } - defer destination.Close() - - _, err = io.Copy(destination, src) - return err -} - -func FsCopyDir(srcFs fs.FS, src string, dst string) error { - var err error - var fds []fs.DirEntry - - src = filepath.ToSlash(src) - - if fds, err = fs.ReadDir(srcFs, src); err != nil { - return err - } - if err = os.MkdirAll(dst, 0o700); err != nil { - return err - } - for _, fd := range fds { - srcfp := path.Join(src, fd.Name()) - dstfp := filepath.Join(dst, fd.Name()) - - if fd.IsDir() { - if err = FsCopyDir(srcFs, srcfp, dstfp); err != nil { - return err - } - } else { - if err = FsCopyFile(srcFs, srcfp, dstfp); err != nil { - return err - } - } - } - return nil -} - -func CopyDir(src string, dst string) error { - return FsCopyDir(os.DirFS(src), ".", dst) -} - func ExpandPath(p string) string { if strings.HasPrefix(p, "~/") { p = homedir.HomeDir() + p[1:] From a152db00b4cd887d624c444c843d4940ebe329cb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 13:15:56 +0100 Subject: [PATCH 0607/2268] refactor: Move repocache package out of git package --- cmd/kluctl/commands/utils.go | 2 +- pkg/deployment/shared_context.go | 2 +- pkg/kluctl_project/project.go | 2 +- pkg/kluctl_project/project_load.go | 2 +- pkg/{git => }/repocache/cache.go | 0 pkg/vars/vars_loader.go | 2 +- pkg/vars/vars_loader_test.go | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename pkg/{git => }/repocache/cache.go (100%) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index e3c827be0..221f97ac0 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -9,11 +9,11 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" - "github.com/kluctl/kluctl/v2/pkg/git/repocache" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/registries" + "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index f5334c195..be1492050 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -2,8 +2,8 @@ package deployment import ( "context" - "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/vars" ) diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 1701abfc2..0df49e01c 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -4,7 +4,7 @@ import ( "context" "fmt" "github.com/kluctl/go-jinja2" - "github.com/kluctl/kluctl/v2/pkg/git/repocache" + "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops" types2 "github.com/kluctl/kluctl/v2/pkg/types" ) diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index dfe0cbe2d..4c075fd23 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -1,7 +1,7 @@ package kluctl_project import ( - "github.com/kluctl/kluctl/v2/pkg/git/repocache" + "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" diff --git a/pkg/git/repocache/cache.go b/pkg/repocache/cache.go similarity index 100% rename from pkg/git/repocache/cache.go rename to pkg/repocache/cache.go diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 226a5d5e3..6ead52755 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -5,8 +5,8 @@ import ( "encoding/base64" "fmt" "github.com/kluctl/go-jinja2" - "github.com/kluctl/kluctl/v2/pkg/git/repocache" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 30d6ce755..be4b18fa8 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -7,9 +7,9 @@ import ( "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/git/repocache" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" From 1635e682aa09d22019e0bc5e440e9aa95ae62181 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 13:17:14 +0100 Subject: [PATCH 0608/2268] refactor: Rename GitServer to TestGitServer --- e2e/test-utils/project.go | 4 +-- .../{git_server.go => test_git_server.go} | 30 +++++++++---------- pkg/vars/vars_loader_test.go | 4 +-- 3 files changed, 19 insertions(+), 19 deletions(-) rename e2e/test-utils/{git_server.go => test_git_server.go} (80%) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index 51183ffef..d9a5a4bd3 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -24,7 +24,7 @@ type TestProject struct { mergedKubeconfig string - gitServer *GitServer + gitServer *TestGitServer } func NewTestProject(t *testing.T, k *EnvTestCluster) *TestProject { @@ -32,7 +32,7 @@ func NewTestProject(t *testing.T, k *EnvTestCluster) *TestProject { t: t, } - p.gitServer = NewGitServer(t) + p.gitServer = NewTestGitServer(t) p.gitServer.GitInit("kluctl-project") p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { diff --git a/e2e/test-utils/git_server.go b/e2e/test-utils/test_git_server.go similarity index 80% rename from e2e/test-utils/git_server.go rename to e2e/test-utils/test_git_server.go index 6e45a3031..dd163fafb 100644 --- a/e2e/test-utils/git_server.go +++ b/e2e/test-utils/test_git_server.go @@ -17,7 +17,7 @@ import ( "testing" ) -type GitServer struct { +type TestGitServer struct { t *testing.T baseDir string @@ -27,8 +27,8 @@ type GitServer struct { gitServerPort int } -func NewGitServer(t *testing.T) *GitServer { - p := &GitServer{ +func NewTestGitServer(t *testing.T) *TestGitServer { + p := &TestGitServer{ t: t, } @@ -47,7 +47,7 @@ func NewGitServer(t *testing.T) *GitServer { return p } -func (p *GitServer) initGitServer() { +func (p *TestGitServer) initGitServer() { p.gitServer = http_server.New(p.baseDir) p.gitHttpServer = &http.Server{ @@ -67,7 +67,7 @@ func (p *GitServer) initGitServer() { }() } -func (p *GitServer) Cleanup() { +func (p *TestGitServer) Cleanup() { if p.gitHttpServer != nil { _ = p.gitHttpServer.Shutdown(context.Background()) p.gitHttpServer = nil @@ -81,7 +81,7 @@ func (p *GitServer) Cleanup() { p.baseDir = "" } -func (p *GitServer) GitInit(repo string) { +func (p *TestGitServer) GitInit(repo string) { dir := p.LocalRepoDir(repo) err := os.MkdirAll(dir, 0o700) @@ -124,7 +124,7 @@ func (p *GitServer) GitInit(repo string) { } } -func (p *GitServer) CommitFiles(repo string, add []string, all bool, message string) { +func (p *TestGitServer) CommitFiles(repo string, add []string, all bool, message string) { r, err := git.PlainOpen(p.LocalRepoDir(repo)) if err != nil { p.t.Fatal(err) @@ -147,7 +147,7 @@ func (p *GitServer) CommitFiles(repo string, add []string, all bool, message str } } -func (p *GitServer) CommitYaml(repo string, pth string, message string, y *uo.UnstructuredObject) { +func (p *TestGitServer) CommitYaml(repo string, pth string, message string, y *uo.UnstructuredObject) { fullPath := filepath.Join(p.LocalRepoDir(repo), pth) dir, _ := filepath.Split(fullPath) @@ -168,7 +168,7 @@ func (p *GitServer) CommitYaml(repo string, pth string, message string, y *uo.Un p.CommitFiles(repo, []string{pth}, false, message) } -func (p *GitServer) UpdateFile(repo string, pth string, update func(f string) (string, error), message string) { +func (p *TestGitServer) UpdateFile(repo string, pth string, update func(f string) (string, error), message string) { fullPath := filepath.Join(p.LocalRepoDir(repo), pth) f := "" if utils.Exists(fullPath) { @@ -194,7 +194,7 @@ func (p *GitServer) UpdateFile(repo string, pth string, update func(f string) (s p.CommitFiles(repo, []string{pth}, false, message) } -func (p *GitServer) UpdateYaml(repo string, pth string, update func(o *uo.UnstructuredObject) error, message string) { +func (p *TestGitServer) UpdateYaml(repo string, pth string, update func(o *uo.UnstructuredObject) error, message string) { fullPath := filepath.Join(p.LocalRepoDir(repo), pth) o := uo.New() @@ -215,7 +215,7 @@ func (p *GitServer) UpdateYaml(repo string, pth string, update func(o *uo.Unstru p.CommitYaml(repo, pth, message, o) } -func (p *GitServer) convertInterfaceToList(x interface{}) []interface{} { +func (p *TestGitServer) convertInterfaceToList(x interface{}) []interface{} { var ret []interface{} if l, ok := x.([]interface{}); ok { return l @@ -235,15 +235,15 @@ func (p *GitServer) convertInterfaceToList(x interface{}) []interface{} { return []interface{}{x} } -func (p *GitServer) GitUrl(repo string) string { +func (p *TestGitServer) GitUrl(repo string) string { return fmt.Sprintf("http://localhost:%d/%s/.git", p.gitServerPort, repo) } -func (p *GitServer) LocalRepoDir(repo string) string { +func (p *TestGitServer) LocalRepoDir(repo string) string { return filepath.Join(p.baseDir, repo) } -func (p *GitServer) GetGitRepo(repo string) *git.Repository { +func (p *TestGitServer) GetGitRepo(repo string) *git.Repository { r, err := git.PlainOpen(p.LocalRepoDir(repo)) if err != nil { p.t.Fatal(err) @@ -251,7 +251,7 @@ func (p *GitServer) GetGitRepo(repo string) *git.Repository { return r } -func (p *GitServer) GetWorktree(repo string) *git.Worktree { +func (p *TestGitServer) GetWorktree(repo string) *git.Worktree { r := p.GetGitRepo(repo) wt, err := r.Worktree() if err != nil { diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index be4b18fa8..729970b94 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -155,7 +155,7 @@ func TestVarsLoader_FileWithLoadNotExists(t *testing.T) { } func TestVarsLoader_Git(t *testing.T) { - gs := test_utils.NewGitServer(t) + gs := test_utils.NewTestGitServer(t) gs.GitInit("repo") gs.UpdateYaml("repo", "test.yaml", func(o *uo.UnstructuredObject) error { *o = *uo.FromStringMust(`{"test1": {"test2": 42}}`) @@ -178,7 +178,7 @@ func TestVarsLoader_Git(t *testing.T) { } func TestVarsLoader_GitBranch(t *testing.T) { - gs := test_utils.NewGitServer(t) + gs := test_utils.NewTestGitServer(t) gs.GitInit("repo") wt := gs.GetWorktree("repo") From 1ed0c22ef21d87491d79d5c2c37e1adcd3463d5d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 13:17:57 +0100 Subject: [PATCH 0609/2268] refactor: Move TestGitServer into git package --- e2e/test-utils/project.go | 5 +++-- {e2e/test-utils => pkg/git}/test_git_server.go | 2 +- pkg/vars/vars_loader_test.go | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) rename {e2e/test-utils => pkg/git}/test_git_server.go (99%) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index d9a5a4bd3..b07fb529d 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -5,6 +5,7 @@ import ( "github.com/go-git/go-git/v5" "github.com/huandu/xstrings" "github.com/imdario/mergo" + git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" registry2 "helm.sh/helm/v3/pkg/registry" @@ -24,7 +25,7 @@ type TestProject struct { mergedKubeconfig string - gitServer *TestGitServer + gitServer *git2.TestGitServer } func NewTestProject(t *testing.T, k *EnvTestCluster) *TestProject { @@ -32,7 +33,7 @@ func NewTestProject(t *testing.T, k *EnvTestCluster) *TestProject { t: t, } - p.gitServer = NewTestGitServer(t) + p.gitServer = git2.NewTestGitServer(t) p.gitServer.GitInit("kluctl-project") p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { diff --git a/e2e/test-utils/test_git_server.go b/pkg/git/test_git_server.go similarity index 99% rename from e2e/test-utils/test_git_server.go rename to pkg/git/test_git_server.go index dd163fafb..2e7805c7c 100644 --- a/e2e/test-utils/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -1,4 +1,4 @@ -package test_utils +package git import ( "context" diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 729970b94..18e342465 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -4,7 +4,7 @@ import ( "context" git2 "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" - "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" @@ -155,7 +155,7 @@ func TestVarsLoader_FileWithLoadNotExists(t *testing.T) { } func TestVarsLoader_Git(t *testing.T) { - gs := test_utils.NewTestGitServer(t) + gs := git.NewTestGitServer(t) gs.GitInit("repo") gs.UpdateYaml("repo", "test.yaml", func(o *uo.UnstructuredObject) error { *o = *uo.FromStringMust(`{"test1": {"test2": 42}}`) @@ -178,7 +178,7 @@ func TestVarsLoader_Git(t *testing.T) { } func TestVarsLoader_GitBranch(t *testing.T) { - gs := test_utils.NewTestGitServer(t) + gs := git.NewTestGitServer(t) gs.GitInit("repo") wt := gs.GetWorktree("repo") From 4585fe5f9790a4f5dbe0511abf62af158e9bb731 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 15:30:04 +0100 Subject: [PATCH 0610/2268] refactor: Get rid of utils dependency in TestGitServer --- pkg/git/test_git_server.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index 2e7805c7c..5a1cf29b4 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/go-git/go-git/v5" http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "log" @@ -110,10 +109,11 @@ func (p *TestGitServer) GitInit(repo string) { if err != nil { p.t.Fatal(err) } - err = utils.Touch(filepath.Join(dir, ".dummy")) + f, err := os.Create(filepath.Join(dir, ".dummy")) if err != nil { p.t.Fatal(err) } + _ = f.Close() _, err = wt.Add(".dummy") if err != nil { p.t.Fatal(err) @@ -171,7 +171,7 @@ func (p *TestGitServer) CommitYaml(repo string, pth string, message string, y *u func (p *TestGitServer) UpdateFile(repo string, pth string, update func(f string) (string, error), message string) { fullPath := filepath.Join(p.LocalRepoDir(repo), pth) f := "" - if utils.Exists(fullPath) { + if _, err := os.Stat(fullPath); err == nil { b, err := os.ReadFile(fullPath) if err != nil { p.t.Fatal(err) @@ -198,7 +198,7 @@ func (p *TestGitServer) UpdateYaml(repo string, pth string, update func(o *uo.Un fullPath := filepath.Join(p.LocalRepoDir(repo), pth) o := uo.New() - if utils.Exists(fullPath) { + if _, err := os.Stat(fullPath); err == nil { err := yaml.ReadYamlFile(fullPath, o) if err != nil { p.t.Fatal(err) From 0c450715d1c2c1363408113771862d7824a00808 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 15:27:31 +0100 Subject: [PATCH 0611/2268] refactor: Get rid of uo dependency in TestGitServer --- pkg/git/test_git_server.go | 54 ++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index 5a1cf29b4..fcbd28883 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -4,9 +4,9 @@ import ( "context" "fmt" "github.com/go-git/go-git/v5" + "github.com/jinzhu/copier" http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" + "gopkg.in/yaml.v3" "log" "net" "net/http" @@ -147,7 +147,7 @@ func (p *TestGitServer) CommitFiles(repo string, add []string, all bool, message } } -func (p *TestGitServer) CommitYaml(repo string, pth string, message string, y *uo.UnstructuredObject) { +func (p *TestGitServer) CommitYaml(repo string, pth string, message string, o map[string]any) { fullPath := filepath.Join(p.LocalRepoDir(repo), pth) dir, _ := filepath.Split(fullPath) @@ -158,7 +158,11 @@ func (p *TestGitServer) CommitYaml(repo string, pth string, message string, y *u } } - err := yaml.WriteYamlFile(fullPath, y) + b, err := yaml.Marshal(o) + if err != nil { + p.t.Fatal(err) + } + err = os.WriteFile(fullPath, b, 0o600) if err != nil { p.t.Fatal(err) } @@ -194,18 +198,30 @@ func (p *TestGitServer) UpdateFile(repo string, pth string, update func(f string p.CommitFiles(repo, []string{pth}, false, message) } -func (p *TestGitServer) UpdateYaml(repo string, pth string, update func(o *uo.UnstructuredObject) error, message string) { +func (p *TestGitServer) UpdateYaml(repo string, pth string, update func(o *map[string]any) error, message string) { fullPath := filepath.Join(p.LocalRepoDir(repo), pth) - o := uo.New() + var o map[string]any if _, err := os.Stat(fullPath); err == nil { - err := yaml.ReadYamlFile(fullPath, o) + b, err := os.ReadFile(fullPath) + if err != nil { + p.t.Fatal(err) + } + err = yaml.Unmarshal(b, &o) if err != nil { p.t.Fatal(err) } + } else { + o = map[string]any{} } - orig := o.Clone() - err := update(o) + + var orig map[string]any + err := copier.CopyWithOption(&orig, &o, copier.Option{DeepCopy: true}) + if err != nil { + p.t.Fatal(err) + } + + err = update(&o) if err != nil { p.t.Fatal(err) } @@ -215,26 +231,6 @@ func (p *TestGitServer) UpdateYaml(repo string, pth string, update func(o *uo.Un p.CommitYaml(repo, pth, message, o) } -func (p *TestGitServer) convertInterfaceToList(x interface{}) []interface{} { - var ret []interface{} - if l, ok := x.([]interface{}); ok { - return l - } - if l, ok := x.([]*uo.UnstructuredObject); ok { - for _, y := range l { - ret = append(ret, y) - } - return ret - } - if l, ok := x.([]map[string]interface{}); ok { - for _, y := range l { - ret = append(ret, y) - } - return ret - } - return []interface{}{x} -} - func (p *TestGitServer) GitUrl(repo string) string { return fmt.Sprintf("http://localhost:%d/%s/.git", p.gitServerPort, repo) } From 667937f7995f666fd360ef612067608cf01e1e55 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 22:04:29 +0100 Subject: [PATCH 0612/2268] refactor: Remove dependency to UnstructuredObject in git package --- e2e/test-utils/project.go | 11 +++++++++-- pkg/git/test_git_server.go | 4 ++-- pkg/vars/vars_loader_test.go | 12 ++++++++---- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index b07fb529d..f52e77e80 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -5,6 +5,7 @@ import ( "github.com/go-git/go-git/v5" "github.com/huandu/xstrings" "github.com/imdario/mergo" + "github.com/jinzhu/copier" git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -111,8 +112,14 @@ func (p *TestProject) UpdateDeploymentYaml(dir string, update func(o *uo.Unstruc } func (p *TestProject) UpdateYaml(path string, update func(o *uo.UnstructuredObject) error, message string) { - p.gitServer.UpdateYaml("kluctl-project", path, func(o *uo.UnstructuredObject) error { - return update(o) + p.gitServer.UpdateYaml("kluctl-project", path, func(o map[string]any) error { + u := uo.FromMap(o) + err := update(u) + if err != nil { + return err + } + _ = copier.CopyWithOption(&o, &u.Object, copier.Option{DeepCopy: true}) + return nil }, message) } diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index fcbd28883..e7661ab9e 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -198,7 +198,7 @@ func (p *TestGitServer) UpdateFile(repo string, pth string, update func(f string p.CommitFiles(repo, []string{pth}, false, message) } -func (p *TestGitServer) UpdateYaml(repo string, pth string, update func(o *map[string]any) error, message string) { +func (p *TestGitServer) UpdateYaml(repo string, pth string, update func(o map[string]any) error, message string) { fullPath := filepath.Join(p.LocalRepoDir(repo), pth) var o map[string]any @@ -221,7 +221,7 @@ func (p *TestGitServer) UpdateYaml(repo string, pth string, update func(o *map[s p.t.Fatal(err) } - err = update(&o) + err = update(o) if err != nil { p.t.Fatal(err) } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 18e342465..ecf2e006e 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -157,8 +157,10 @@ func TestVarsLoader_FileWithLoadNotExists(t *testing.T) { func TestVarsLoader_Git(t *testing.T) { gs := git.NewTestGitServer(t) gs.GitInit("repo") - gs.UpdateYaml("repo", "test.yaml", func(o *uo.UnstructuredObject) error { - *o = *uo.FromStringMust(`{"test1": {"test2": 42}}`) + gs.UpdateYaml("repo", "test.yaml", func(o map[string]any) error { + o["test1"] = map[string]any{ + "test2": 42, + } return nil }, "") @@ -188,8 +190,10 @@ func TestVarsLoader_GitBranch(t *testing.T) { }) assert.NoError(t, err) - gs.UpdateYaml("repo", "test.yaml", func(o *uo.UnstructuredObject) error { - *o = *uo.FromStringMust(`{"test1": {"test2": 42}}`) + gs.UpdateYaml("repo", "test.yaml", func(o map[string]any) error { + o["test1"] = map[string]any{ + "test2": 42, + } return nil }, "") From 2853468ed9c5a93e1e3ac23f602f2ad40b30f21e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 30 Nov 2022 22:09:52 +0100 Subject: [PATCH 0613/2268] refactor: Remove dependency to utils in git package --- pkg/git/auth/ssh_known_hosts.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/git/auth/ssh_known_hosts.go b/pkg/git/auth/ssh_known_hosts.go index 0d0e9b713..9e8017040 100644 --- a/pkg/git/auth/ssh_known_hosts.go +++ b/pkg/git/auth/ssh_known_hosts.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/git/auth/goph" "github.com/kluctl/kluctl/v2/pkg/git/messages" - "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/crypto/ssh" "net" "os" @@ -47,7 +46,7 @@ func verifyHost(messageCallbacks messages.MessageCallbacks, host string, remote } f := filepath.Join(home, ".ssh", "known_hosts") - if !utils.Exists(filepath.Dir(f)) { + if _, err := os.Stat(filepath.Dir(f)); err != nil { err = os.MkdirAll(filepath.Dir(f), 0o700) if err != nil { return err @@ -58,7 +57,7 @@ func verifyHost(messageCallbacks messages.MessageCallbacks, host string, remote allowAdd = true } } else { - tmpFile, err := os.CreateTemp(utils.GetTmpBaseDir(), "known_hosts-") + tmpFile, err := os.CreateTemp("", "known_hosts-") if err != nil { return err } From 2ceb76ee809ebffc4b24f713d6db95095aafb994 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Dec 2022 22:09:20 +0000 Subject: [PATCH 0614/2268] chore(deps): Bump github.com/huandu/xstrings from 1.3.3 to 1.4.0 Bumps [github.com/huandu/xstrings](https://github.com/huandu/xstrings) from 1.3.3 to 1.4.0. - [Release notes](https://github.com/huandu/xstrings/releases) - [Commits](https://github.com/huandu/xstrings/compare/v1.3.3...v1.4.0) --- updated-dependencies: - dependency-name: github.com/huandu/xstrings dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ae00085ce..5e425d6db 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 - github.com/huandu/xstrings v1.3.3 + github.com/huandu/xstrings v1.4.0 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.3 sigs.k8s.io/controller-runtime v0.13.1 diff --git a/go.sum b/go.sum index 2a56447bd..6216b20a0 100644 --- a/go.sum +++ b/go.sum @@ -486,8 +486,8 @@ github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 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.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= From d72f1746c7d0082a26668a8fdfaf37d854bb9095 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Dec 2022 22:08:44 +0000 Subject: [PATCH 0615/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.146 to 1.44.154 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.146 to 1.44.154. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.146...v1.44.154) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ae00085ce..981c37ff5 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.146 + github.com/aws/aws-sdk-go v1.44.154 github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index 2a56447bd..561253b46 100644 --- a/go.sum +++ b/go.sum @@ -131,8 +131,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.146 h1:7YdGgPxDPRJu/yYffzZp/H7yHzQ6AqmuNFZPYraaN8I= -github.com/aws/aws-sdk-go v1.44.146/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.154 h1:2TaEC8JIM/t7Fu+FBmdOBK5jFUAVL07tbpfJsLuVo3Q= +github.com/aws/aws-sdk-go v1.44.154/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From 58ca81510ee22a60f152fae2fd3c722e362ecca6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Dec 2022 11:16:47 +0100 Subject: [PATCH 0616/2268] refactor: Use to github.com/fluxcd/pkg/kustomize This removes the need to use a copy of SecureBuildKustomization inside Kluctl. --- go.mod | 24 ++++++------ go.sum | 44 ++++++++++++---------- pkg/deployment/deployment_item.go | 3 +- pkg/utils/kustomize.go | 62 ------------------------------- 4 files changed, 39 insertions(+), 94 deletions(-) delete mode 100644 pkg/utils/kustomize.go diff --git a/go.mod b/go.mod index 3cef08b7c..8870ed9e9 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible - github.com/fluxcd/pkg/kustomize v0.8.0 + github.com/fluxcd/pkg/kustomize v0.11.0 github.com/go-git/go-git/v5 v5.4.2 github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect @@ -48,11 +48,11 @@ require ( gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.2 k8s.io/api v0.25.4 - k8s.io/apiextensions-apiserver v0.25.3 + k8s.io/apiextensions-apiserver v0.25.4 k8s.io/apimachinery v0.25.4 k8s.io/client-go v0.25.4 k8s.io/klog/v2 v2.80.1 - sigs.k8s.io/kustomize/api v0.12.1 + sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) @@ -108,12 +108,14 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/drone/envsubst v1.0.3 // indirect + github.com/emicklei/go-restful/v3 v3.10.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect + github.com/fluxcd/pkg/apis/kustomize v0.7.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect @@ -216,11 +218,11 @@ require ( go.etcd.io/etcd/api/v3 v3.5.5 // indirect go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a // indirect go.opencensus.io v0.23.0 // indirect - go.starlark.net v0.0.0-20221020143700-22309ac47eac // indirect + go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.6.0 // indirect - golang.org/x/oauth2 v0.1.0 // indirect - golang.org/x/time v0.1.0 // indirect + golang.org/x/oauth2 v0.2.0 // indirect + golang.org/x/time v0.2.0 // indirect golang.org/x/tools v0.2.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/api v0.102.0 // indirect @@ -233,12 +235,12 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiserver v0.25.3 // indirect + k8s.io/apiserver v0.25.4 // indirect k8s.io/cli-runtime v0.25.3 // indirect - k8s.io/component-base v0.25.3 // indirect - k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + k8s.io/component-base v0.25.4 // indirect + k8s.io/kube-openapi v0.0.0-20221110221610-a28e98eb7c70 // indirect k8s.io/kubectl v0.25.3 // indirect - k8s.io/utils v0.0.0-20221012122500-cfd413dd9e85 // indirect + k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect oras.land/oras-go v1.2.1 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index f91f3c8d1..cbe4b45be 100644 --- a/go.sum +++ b/go.sum @@ -212,9 +212,11 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= +github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -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/emicklei/go-restful/v3 v3.10.0 h1:X4gma4HM7hFm6WMeAsTfqA0GOfdNoCzBIkHGoRLGXuM= +github.com/emicklei/go-restful/v3 v3.10.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= @@ -240,8 +242,10 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fluxcd/pkg/kustomize v0.8.0 h1:8AdEvp6y38ISZzoi0H82Si5zkmLXClbeX10W7HevB00= -github.com/fluxcd/pkg/kustomize v0.8.0/go.mod h1:zGtCZF6V3hMWcf46SqrQc10fS9yUlKzi2UcFUeabDAE= +github.com/fluxcd/pkg/apis/kustomize v0.7.0 h1:X2htBmJ91nGYv4d93gin665MFWKNGiNwUiZ08/Zz0hY= +github.com/fluxcd/pkg/apis/kustomize v0.7.0/go.mod h1:Mu+KdktsEKWA4l/33CZdY5lB4hz51mqfcLzBZSwAqVg= +github.com/fluxcd/pkg/kustomize v0.11.0 h1:zseS9LRUuzhP/7KamccmsOgYpJAdhqtsf+2wN/CHF3I= +github.com/fluxcd/pkg/kustomize v0.11.0/go.mod h1:awHID4OKe2/WAfTFg4u0fURXZPUkrIslSZNSPX9MEFQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -851,8 +855,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.starlark.net v0.0.0-20221020143700-22309ac47eac h1:gBO5Qfcw5V9404yzsu2FEIsxK/u2mBNTNogK0uIoVhk= -go.starlark.net v0.0.0-20221020143700-22309ac47eac/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= +go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 h1:5/KzhcSqd4UgY51l17r7C5g/JiE6DRw1Vq7VJfQHuMc= +go.starlark.net v0.0.0-20221028183056-acb66ad56dd2/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= 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= @@ -983,8 +987,8 @@ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= -golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= +golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU= +golang.org/x/oauth2 v0.2.0/go.mod h1:Cwn6afJ8jrQwYMxQDTpISoXmXW9I6qF6vDeuuoX3Ibs= 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= @@ -1096,8 +1100,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= +golang.org/x/time v0.2.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= @@ -1328,26 +1332,26 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs= k8s.io/api v0.25.4/go.mod h1:IG2+RzyPQLllQxnhzD8KQNEu4c4YvyDTpSMztf4A0OQ= -k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= -k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= +k8s.io/apiextensions-apiserver v0.25.4 h1:7hu9pF+xikxQuQZ7/30z/qxIPZc2J1lFElPtr7f+B6U= +k8s.io/apiextensions-apiserver v0.25.4/go.mod h1:bkSGki5YBoZWdn5pWtNIdGvDrrsRWlmnvl9a+tAw5vQ= k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= -k8s.io/apiserver v0.25.3 h1:m7+xGuG5+KYAnEsqaFtDyWMkmMMEOFYlu+NlWv5qSBI= -k8s.io/apiserver v0.25.3/go.mod h1:9bT47iM2fzRuhICJpM/RcQR9sqDDfZ7Yw60h0p3JW08= +k8s.io/apiserver v0.25.4 h1:/3TwZcgLqX7wUxq7TtXOUqXeBTwXIblVMQdhR5XZ7yo= +k8s.io/apiserver v0.25.4/go.mod h1:rPcm567XxjOnnd7jedDUnGJGmDGAo+cT6H7QHAN+xV0= k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= k8s.io/cli-runtime v0.25.3/go.mod h1:InHHsjkyW5hQsILJGpGjeruiDZT/R0OkROQgD6GzxO4= k8s.io/client-go v0.25.4 h1:3RNRDffAkNU56M/a7gUfXaEzdhZlYhoW8dgViGy5fn8= k8s.io/client-go v0.25.4/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw= -k8s.io/component-base v0.25.3 h1:UrsxciGdrCY03ULT1h/S/gXFCOPnLhUVwSyx+hM/zq4= -k8s.io/component-base v0.25.3/go.mod h1:WYoS8L+IlTZgU7rhAl5Ctpw0WdMxDfCC5dkxcEFa/TI= +k8s.io/component-base v0.25.4 h1:n1bjg9Yt+G1C0WnIDJmg2fo6wbEU1UGMRiQSjmj7hNQ= +k8s.io/component-base v0.25.4/go.mod h1:nnZJU8OP13PJEm6/p5V2ztgX2oyteIaAGKGMYb2L2cY= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -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/kube-openapi v0.0.0-20221110221610-a28e98eb7c70 h1:zfqQc1V6/ZgGpvrOVvr62OjiqQX4lZjfznK34NQwkqw= +k8s.io/kube-openapi v0.0.0-20221110221610-a28e98eb7c70/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= -k8s.io/utils v0.0.0-20221012122500-cfd413dd9e85 h1:cTdVh7LYu82xeClmfzGtgyspNh6UxpwLWGi8R4sspNo= -k8s.io/utils v0.0.0-20221012122500-cfd413dd9e85/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 h1:GfD9OzL11kvZN5iArC6oTS7RTj7oJOIfnislxYlqTj8= +k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.1 h1:/VcGS8FUy3eEXLl/1vC4QypLHwrfSmgW7ygsoklqKK8= oras.land/oras-go v1.2.1/go.mod h1:3N11Z5E3c4ZzOjroCl1RtAdB4yNAYl7A27j2SVf913A= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index d6d1ad573..94d23ddd9 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -2,6 +2,7 @@ package deployment import ( "fmt" + "github.com/fluxcd/pkg/kustomize" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/status" @@ -515,7 +516,7 @@ func (di *DeploymentItem) buildKustomize() error { } } - rm, err := utils.SecureBuildKustomization(di.RenderedSourceRootDir, di.RenderedDir, true) + rm, err := kustomize.SecureBuild(di.RenderedSourceRootDir, di.RenderedDir, true) if err != nil { return err } diff --git a/pkg/utils/kustomize.go b/pkg/utils/kustomize.go deleted file mode 100644 index 44a92258a..000000000 --- a/pkg/utils/kustomize.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Code in this file is copied from https://github.com/fluxcd/kustomize-controller/blob/main/controllers/kustomization_generator.go -*/ - -package utils - -import ( - "fmt" - securefs "github.com/fluxcd/pkg/kustomize/filesys" - "sigs.k8s.io/kustomize/api/filesys" - "sigs.k8s.io/kustomize/api/krusty" - "sigs.k8s.io/kustomize/api/resmap" - kustypes "sigs.k8s.io/kustomize/api/types" - "sync" -) - -// TODO: remove mutex when kustomize fixes the concurrent map read/write panic -var kustomizeBuildMutex sync.Mutex - -// SecureBuildKustomization wraps krusty.MakeKustomizer with the following settings: -// - secure on-disk FS denying operations outside root -// - load files from outside the kustomization dir path -// (but not outside root) -// - disable plugins except for the builtin ones -func SecureBuildKustomization(root, dirPath string, allowRemoteBases bool) (_ resmap.ResMap, err error) { - var fs filesys.FileSystem - - // Create secure FS for root with or without remote base support - if allowRemoteBases { - fs, err = securefs.MakeFsOnDiskSecureBuild(root) - if err != nil { - return nil, err - } - } else { - fs, err = securefs.MakeFsOnDiskSecure(root) - if err != nil { - return nil, err - } - } - - // Temporary workaround for concurrent map read and map write bug - // https://github.com/kubernetes-sigs/kustomize/issues/3659 - kustomizeBuildMutex.Lock() - defer kustomizeBuildMutex.Unlock() - - // Kustomize tends to panic in unpredicted ways due to (accidental) - // invalid object data; recover when this happens to ensure continuity of - // operations - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("recovered from kustomize build panic: %v", r) - } - }() - - buildOptions := &krusty.Options{ - LoadRestrictions: kustypes.LoadRestrictionsNone, - PluginConfig: kustypes.DisabledPluginConfig(), - } - - k := krusty.MakeKustomizer(buildOptions) - return k.Run(fs, dirPath) -} From c9972318ac1a66b7e7430140f7554fb22abf6716 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Dec 2022 11:42:25 +0100 Subject: [PATCH 0617/2268] fix: Use locked files to write/read registry cache files --- pkg/registries/registries.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 48556c1e8..81b1156e8 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -15,6 +15,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/rogpeppe/go-internal/lockedfile" "io" "net/http" "net/http/httputil" @@ -324,7 +325,13 @@ func (rh *RegistryHelper) readCachedResponse(key string) []byte { return nil } - b, err := os.ReadFile(cachePath) + f, err := lockedfile.OpenFile(cachePath, os.O_RDONLY, 0) + if err != nil { + status.Warning(rh.ctx, "readCachedResponse failed: %v", err) + return nil + } + b, err := io.ReadAll(f) + _ = f.Close() if err != nil { return nil } @@ -346,20 +353,23 @@ func (rh *RegistryHelper) readCachedResponse(key string) []byte { func (rh *RegistryHelper) writeCachedResponse(key string, data []byte) { cachePath := rh.getCachePath(key) - if !utils.Exists(filepath.Dir(cachePath)) { - err := os.MkdirAll(filepath.Dir(cachePath), 0o700) + cacheDir := filepath.Dir(cachePath) + if !utils.Exists(cacheDir) { + err := os.MkdirAll(cacheDir, 0o700) if err != nil { status.Warning(rh.ctx, "writeCachedResponse failed: %v", err) return } } - err := os.WriteFile(cachePath+".tmp", data, 0o600) + f, err := lockedfile.OpenFile(cachePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600) if err != nil { status.Warning(rh.ctx, "writeCachedResponse failed: %v", err) return } - err = os.Rename(cachePath+".tmp", cachePath) + defer f.Close() + + _, err = f.Write(data) if err != nil { status.Warning(rh.ctx, "writeCachedResponse failed: %v", err) return From 215ab6df1ace4bad8c4241773ac84c0717e86a98 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Dec 2022 14:04:12 +0100 Subject: [PATCH 0618/2268] feat: Support SOPS in helm-files.yaml --- e2e/sops_test.go | 34 +++++++++++++++++++ pkg/deployment/deployment_item.go | 2 +- pkg/deployment/helm_chart.go | 14 +++++--- pkg/sops/utils.go | 18 ++++++++-- pkg/vars/sops_test_resources/README.md | 1 + pkg/vars/sops_test_resources/helm-values.yaml | 23 +++++++++++++ 6 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 pkg/vars/sops_test_resources/helm-values.yaml diff --git a/e2e/sops_test.go b/e2e/sops_test.go index 22014d9a0..6b4a1a1a2 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -4,6 +4,7 @@ import ( "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars/sops_test_resources" + "github.com/stretchr/testify/assert" "go.mozilla.org/sops/v3/age" "testing" ) @@ -80,3 +81,36 @@ func TestSopsResources(t *testing.T) { "a": "b", }, "data") } + +func TestSopsHelmValues(t *testing.T) { + key, _ := sops_test_resources.TestResources.ReadFile("test-key.txt") + t.Setenv(age.SopsAgeKeyEnv, string(key)) + + k := defaultCluster1 + + p := test_utils.NewTestProject(t, k) + + createNamespace(t, k, p.TestSlug()) + + repoUrl := test_utils.CreateHelmRepo(t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, + }, "", "") + + valuesBytes, err := sops_test_resources.TestResources.ReadFile("helm-values.yaml") + assert.NoError(t, err) + values1, err := uo.FromString(string(valuesBytes)) + assert.NoError(t, err) + + p.UpdateTarget("test", nil) + p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), values1.Object) + + p.KluctlMust("deploy", "--yes", "-t", "test") + + cm1 := assertConfigMapExists(t, k, p.TestSlug(), "test-helm1-test-chart1") + + assert.Equal(t, map[string]any{ + "a": "secret1", + "b": "secret2", + "version": "0.1.0", + }, cm1.Object["data"]) +} diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 94d23ddd9..f6caec641 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -214,7 +214,7 @@ func (di *DeploymentItem) renderHelmCharts() error { } } - return chart.Render(di.ctx.Ctx, di.ctx.K) + return chart.Render(di.ctx.Ctx, di.ctx.K, di.ctx.SopsDecrypter) }) if err != nil { return err diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index dda52347a..584fe3a72 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -8,6 +8,7 @@ import ( securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/registries" + "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -341,15 +342,15 @@ func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { return latestVersion, updated, nil } -func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster) error { - err := c.doRender(ctx, k) +func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster, sopsDecrypter sops.SopsDecrypter) error { + err := c.doRender(ctx, k, sopsDecrypter) if err != nil { return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", c.chartName, c.Config.ReleaseName, err) } return nil } -func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { +func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster, sopsDecrypter sops.SopsDecrypter) error { chartDir := c.chartDir needsPull, versionChanged, prePulledVersion, err := c.checkNeedsPull(chartDir, false) @@ -397,7 +398,12 @@ func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster) error { valueOpts := values.Options{} if utils.Exists(valuesPath) { - valueOpts.ValueFiles = append(valueOpts.ValueFiles, valuesPath) + tmpValues, err := sops.MaybeDecryptFileToTmp(sopsDecrypter, valuesPath) + if err != nil { + return err + } + defer os.Remove(tmpValues) + valueOpts.ValueFiles = append(valueOpts.ValueFiles, tmpValues) } var kubeVersion *chartutil.KubeVersion diff --git a/pkg/sops/utils.go b/pkg/sops/utils.go index 39a1d85b2..7a1250d5e 100644 --- a/pkg/sops/utils.go +++ b/pkg/sops/utils.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils" "go.mozilla.org/sops/v3" "go.mozilla.org/sops/v3/cmd/sops/formats" "os" @@ -33,6 +34,10 @@ func MaybeDecrypt(decrypter SopsDecrypter, encrypted []byte, inputFormat, output } func MaybeDecryptFile(decrypter SopsDecrypter, path string) error { + return MaybeDecryptFileTo(decrypter, path, path) +} + +func MaybeDecryptFileTo(decrypter SopsDecrypter, path string, to string) error { format := formats.FormatForPath(path) file, err := os.ReadFile(path) @@ -44,13 +49,22 @@ func MaybeDecryptFile(decrypter SopsDecrypter, path string) error { if err != nil { return fmt.Errorf("failed to decrypt file %s: %w", path, err) } - if !encrypted { + if !encrypted && path == to { return nil } - err = os.WriteFile(path, decrypted, 0o600) + err = os.WriteFile(to, decrypted, 0o600) if err != nil { return fmt.Errorf("failed to save decrypted file %s: %w", path, err) } return nil } + +func MaybeDecryptFileToTmp(decrypter SopsDecrypter, path string) (string, error) { + tmp, err := os.CreateTemp(utils.GetTmpBaseDir(), "sops-decrypt-") + if err != nil { + return "", err + } + _ = tmp.Close() + return tmp.Name(), MaybeDecryptFileTo(decrypter, path, tmp.Name()) +} diff --git a/pkg/vars/sops_test_resources/README.md b/pkg/vars/sops_test_resources/README.md index f25a10769..6a5cfbd52 100644 --- a/pkg/vars/sops_test_resources/README.md +++ b/pkg/vars/sops_test_resources/README.md @@ -2,4 +2,5 @@ To edit the test.yaml file, run: ```sh SOPS_AGE_KEY_FILE=$(pwd)/test-key.txt sops test.yaml SOPS_AGE_KEY_FILE=$(pwd)/test-key.txt sops test-configmap.yaml +SOPS_AGE_KEY_FILE=$(pwd)/test-key.txt sops helm-values.yaml ``` diff --git a/pkg/vars/sops_test_resources/helm-values.yaml b/pkg/vars/sops_test_resources/helm-values.yaml new file mode 100644 index 000000000..06aca28b4 --- /dev/null +++ b/pkg/vars/sops_test_resources/helm-values.yaml @@ -0,0 +1,23 @@ +data: + a: ENC[AES256_GCM,data:72uviUAptw==,iv:jRc24qIQwkbhGAp2zj6qiaoiunMFYiQdJEW8tC6nCOw=,tag:LA4hUT++fqgT5ZgKTM6LFA==,type:str] + b: ENC[AES256_GCM,data:M7AlzSVTxg==,iv:j2yFEUIc5OwWmhVFzljZDmPEII/0jzr42nBiplXZG1Y=,tag:1w91ofjr1nOF8rU7gH8ZGQ==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1q69g6x9jcz7lgnrgdxemystmhec4e8cxlzz45x0tt6t7dddp2ppsnkdxe7 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSRzZhejBCdmdYdHc5dEYw + UGduYk1neGlYeXNQenRSYmQrV0FpTkcwRTNFCk1panJJTmsyejdzNGtkTFJhWVVn + bXhkaU0xZlAwbFo2UG5QdkpnajlZZzQKLS0tIHRzRkRtWEhzL1R1R2VoM0NFMk5s + NzlGRU0zK29IV3FuSWxIS2ZXb1FFMEEKQKXp5xfIRQ94uQ0Z6QkNYrnqF6LjW/XT + HihRLtBxL4Yuj/3bhImk+3PqCh8fdCjPmN13NURUxeW9q6fWwZWekA== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2022-12-07T12:53:07Z" + mac: ENC[AES256_GCM,data:tQFvNENKLBCmIO5De4QVj3GDpXq2haYpK7dlrvcYZ9S0Aw5l92HB3MHi3RYtxDH6ZqC1HbwKGEcFmINRUAA98+g6htJ3XV+yfjuL09v0W3Kosf6FbkEpKvfrbD+Z2nGrQ6+IdGAH/TPICK1D2MtJ7Q9DHd+98STxmcZ4npyiu8g=,iv:bteoE24FA+h+BnbRKoH9FbAT3qk3p8Mh6oqUBGg7NRU=,tag:Al6hWq7fuI8eXOddoCwdNg==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.7.3 From 97e622973dbc12759ec60d36bf96296054d5b543 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Dec 2022 14:33:11 +0100 Subject: [PATCH 0619/2268] feat: Implement annotations to mark objects/fields to be ignored on conflict --- .../deployments/annotations/all-resources.md | 10 ++++ pkg/diff/managed_fields.go | 52 +++++++++++++------ 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/docs/reference/deployments/annotations/all-resources.md b/docs/reference/deployments/annotations/all-resources.md index aa517a46c..9340ebdbf 100644 --- a/docs/reference/deployments/annotations/all-resources.md +++ b/docs/reference/deployments/annotations/all-resources.md @@ -32,6 +32,16 @@ fields will be overwritten in case of field manager conflicts. If more than one field needs to be specified, add `-xxx` to the annotation key, where `xxx` is an arbitrary number. +### kluctl.io/ignore-conflicts +If set to "true", the whole all fields of the object are going to be ignored when conflicts arise. +This effectively disables the warnings that are shown when field ownership is lost. + +### kluctl.io/ignore-conflicts-field +Specifies a [JSON Path](https://goessner.net/articles/JsonPath/) for fields that should be ignored when conflicts arise. +This effectively disables the warnings that are shown when field ownership is lost. + +If more than one field needs to be specified, add `-xxx` to the annotation key, where `xxx` is an arbitrary number. + ## Control deletion/pruning The following annotations control how delete/prune is behaving. diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index 9fa5d1d69..e2b4e6797 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -2,6 +2,7 @@ package diff import ( "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "reflect" @@ -17,6 +18,7 @@ type LostOwnership struct { } var forceApplyFieldAnnotationRegex = regexp.MustCompile(`^kluctl.io/force-apply-field(-\d*)?$`) +var ignoreConflictsFieldAnnotationRegex = regexp.MustCompile(`^kluctl.io/ignore-conflicts-field(-\d*)?$`) var overwriteAllowedManagers = []*regexp.Regexp{ regexp.MustCompile("^kluctl$"), regexp.MustCompile("^kubectl$"), @@ -98,6 +100,24 @@ func convertToKeyList(remote *uo.UnstructuredObject, path fieldpath.Path) (uo.Ke return ret, true, nil } +func collectFields(o *uo.UnstructuredObject, regex *regexp.Regexp) (map[string]bool, error) { + result := map[string]bool{} + for _, v := range o.GetK8sAnnotationsWithRegex(regex) { + j, err := uo.NewMyJsonPath(v) + if err != nil { + return nil, err + } + fields, err := j.ListMatchingFields(o) + if err != nil { + return nil, err + } + for _, f := range fields { + result[f.ToJsonPath()] = true + } + } + return result, nil +} + func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.UnstructuredObject, conflictStatus metav1.Status) (*uo.UnstructuredObject, []LostOwnership, error) { managedFields := remote.GetK8sManagedFields() @@ -154,24 +174,19 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr ret := local.Clone() - forceApplyAll := false + forceApplyAll := utils.ParseBoolOrFalse(local.GetK8sAnnotation("kluctl.io/force-apply")) + ignoreConflictsAll := utils.ParseBoolOrFalse(local.GetK8sAnnotation("kluctl.io/ignore-conflicts")) if x := local.GetK8sAnnotation("kluctl.io/force-apply"); x != nil { forceApplyAll, _ = strconv.ParseBool(*x) } - forceApplyFields := make(map[string]bool) - for _, v := range local.GetK8sAnnotationsWithRegex(forceApplyFieldAnnotationRegex) { - j, err := uo.NewMyJsonPath(v) - if err != nil { - return nil, nil, err - } - fields, err := j.ListMatchingFields(ret) - if err != nil { - return nil, nil, err - } - for _, f := range fields { - forceApplyFields[f.ToJsonPath()] = true - } + forceApplyFields, err := collectFields(ret, forceApplyFieldAnnotationRegex) + if err != nil { + return nil, nil, err + } + ignoreConflictFields, err := collectFields(ret, ignoreConflictsFieldAnnotationRegex) + if err != nil { + return nil, nil, err } var lostOwnership []LostOwnership @@ -215,6 +230,7 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr } overwrite := true + ignoreConflict := ignoreConflictsAll if !forceApplyAll { for _, mfn := range mf.managers { found := false @@ -236,6 +252,12 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr overwrite = true } } + if _, ok := ignoreConflictFields[localKeyPath.ToJsonPath()]; ok { + ignoreConflict = true + } + if _, ok := ignoreConflictFields[remoteKeyPath.ToJsonPath()]; ok { + ignoreConflict = true + } if !overwrite { j, err := uo.NewMyJsonPath(localKeyPath.ToJsonPath()) @@ -247,7 +269,7 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr return nil, nil, err } - if !reflect.DeepEqual(localValue, remoteValue) { + if !reflect.DeepEqual(localValue, remoteValue) && !ignoreConflict { lostOwnership = append(lostOwnership, LostOwnership{ Field: cause.Field, Message: cause.Message, From 794c94fc3eff185efcd4957c76a9e6172c64eb2e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Dec 2022 15:50:14 +0100 Subject: [PATCH 0620/2268] tests: Implement tests for conflict resolution --- pkg/diff/managed_fields_test.go | 192 ++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 pkg/diff/managed_fields_test.go diff --git a/pkg/diff/managed_fields_test.go b/pkg/diff/managed_fields_test.go new file mode 100644 index 000000000..3c6fcd96e --- /dev/null +++ b/pkg/diff/managed_fields_test.go @@ -0,0 +1,192 @@ +package diff + +import ( + "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "testing" +) + +func TestResolveFieldManagerConflicts(t *testing.T) { + type testCase struct { + name string + remote *uo.UnstructuredObject + local *uo.UnstructuredObject + status metav1.Status + result *uo.UnstructuredObject + lost []LostOwnership + anns map[string]string + } + + type fieldInfo struct { + name string + value string + manager string + } + + buildConfigMap := func(fields ...fieldInfo) *uo.UnstructuredObject { + o := uo.FromMap(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]any{ + "name": "name", + "namespace": "namespace", + }, + "data": map[string]any{}, + }) + pathesByManagers := map[string][]fieldpath.Path{} + for _, fi := range fields { + _ = o.SetNestedField(fi.value, "data", fi.name) + if fi.manager != "" { + pathesByManagers[fi.manager] = append(pathesByManagers[fi.manager], fieldpath.MakePathOrDie("data", fi.name)) + } + } + var managedFields []any + for manager, pbm := range pathesByManagers { + fs := fieldpath.NewSet(pbm...) + json, _ := fs.ToJSON() + fsY, _ := uo.FromString(string(json)) + managedFields = append(managedFields, map[string]interface{}{ + "apiVersion": "v1", + "fieldsType": "FieldsV1", + "manager": manager, + "operation": "Apply", + "time": "dummy", + "fieldsV1": fsY.Object, + }) + } + _ = o.SetNestedField(managedFields, "metadata", "managedFields") + return o + } + + buildConflicts := func(fields ...string) metav1.Status { + var s metav1.Status + s.Details = &metav1.StatusDetails{} + for _, f := range fields { + s.Details.Causes = append(s.Details.Causes, metav1.StatusCause{ + Type: metav1.CauseTypeFieldManagerConflict, + Field: fmt.Sprintf(".data.%s", f), + }) + } + return s + } + + buildLost := func(fields ...string) []LostOwnership { + var l []LostOwnership + for _, f := range fields { + l = append(l, LostOwnership{ + Field: fmt.Sprintf(".data.%s", f), + Message: "", + }) + } + return l + } + + buildAnnotations := func(kvs ...string) map[string]string { + ret := map[string]string{} + for i := 0; i < len(kvs); i += 2 { + ret[kvs[i]] = kvs[i+1] + } + return ret + } + + tests := []testCase{ + { + name: "overwrite-kubectl", + remote: buildConfigMap(fieldInfo{"d1", "v1", "kubectl"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}), + status: buildConflicts("d1"), + result: buildConfigMap(fieldInfo{"d1", "x", "m1"}), + }, + { + name: "lost-field", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}), + status: buildConflicts("d1"), + result: buildConfigMap(fieldInfo{"d2", "x", "m1"}), + lost: buildLost("d1"), + // also test non-matching fields here + anns: buildAnnotations("kluctl.io/force-apply-field", "data.d3"), + }, + { + name: "force-apply-object", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + lost: buildLost(), + anns: buildAnnotations("kluctl.io/force-apply", "true"), + }, + { + name: "force-apply-field", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}), + lost: buildLost("d3"), + anns: buildAnnotations("kluctl.io/force-apply-field", "data.d1"), + }, + { + name: "force-apply-field-xxx", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + lost: buildLost("d1"), + anns: buildAnnotations("kluctl.io/force-apply-field-123", "data.d3"), + }, + { + name: "ignore-conflicts", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d2", "x", "m1"}), + lost: buildLost(), + anns: buildAnnotations("kluctl.io/ignore-conflicts", "true"), + }, + { + name: "ignore-conflicts-field", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d2", "x", "m1"}), + lost: buildLost("d3"), + anns: buildAnnotations("kluctl.io/ignore-conflicts-field", "data.d1"), + }, + { + name: "ignore-conflicts-field-xxx", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d2", "x", "m1"}), + lost: buildLost("d1"), + anns: buildAnnotations("kluctl.io/ignore-conflicts-field-123", "data.d3"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.local != nil { + _ = tc.local.RemoveNestedField("metadata", "managedFields") + } + if tc.result != nil { + _ = tc.result.RemoveNestedField("metadata", "managedFields") + } + for k, v := range tc.anns { + if tc.local != nil { + tc.local.SetK8sAnnotation(k, v) + } + if tc.result != nil { + tc.result.SetK8sAnnotation(k, v) + } + } + + r, l, err := ResolveFieldManagerConflicts(tc.local, tc.remote, tc.status) + assert.NoError(t, err) + assert.Equal(t, r, tc.result) + assert.Equal(t, l, tc.lost) + }) + } +} From aa7b47c81cdaedd6cf40353158e9d71a2aee3d9f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Dec 2022 17:12:33 +0100 Subject: [PATCH 0621/2268] fix: Fix potential nil pointer access in pullAndCommitChart --- cmd/kluctl/commands/cmd_helm_update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 7d6fc14f0..e831c591f 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -179,7 +179,7 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployme // know what got deleted oldFiles := map[string]bool{} err = filepath.WalkDir(chart.GetChartDir(), func(p string, d fs.DirEntry, err error) error { - if d.IsDir() { + if d == nil || d.IsDir() { return nil } relToGit, err := filepath.Rel(gitRootPath, p) From 967ec16f3fb3ddf9cbe1a726620ed64ece8fc935 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Dec 2022 17:15:16 +0100 Subject: [PATCH 0622/2268] feat: Use github.com/Masterminds/semver for Helm version sorting --- pkg/deployment/helm_chart.go | 47 +++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 584fe3a72..5308e9431 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "github.com/Masterminds/semver/v3" securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/registries" @@ -13,7 +14,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/pkg/errors" "github.com/rogpeppe/go-internal/lockedfile" @@ -57,6 +57,11 @@ func NewHelmChart(configFile string) (*HelmChart, error) { return nil, err } + _, err = semver.NewVersion(*config.ChartVersion) + if err != nil { + return nil, fmt.Errorf("invalid chart version '%s': %w", *config.ChartVersion, err) + } + hc := &HelmChart{ ConfigFile: configFile, Config: &config, @@ -274,20 +279,16 @@ func (c *HelmChart) checkUpdateOciRepo(ctx context.Context) (string, bool, error return "", false, err } - var ls versions.LooseVersionSlice - for _, x := range tags { - ls = append(ls, versions.LooseVersion(x)) + latestVersion, err := c.findLatestVersion(tags) + if err != nil { + return "", false, err } - sort.Stable(ls) - latestVersion := string(ls[len(ls)-1]) updated := latestVersion != *c.Config.ChartVersion return latestVersion, updated, nil } func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { - var latestVersion string - settings := cli.New() var e *repo.Entry @@ -331,17 +332,39 @@ func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { return "", false, fmt.Errorf("helm chart %s not found in repo index", c.chartName) } - var ls versions.LooseVersionSlice + versions := make([]string, 0, indexEntry.Len()) for _, x := range indexEntry { - ls = append(ls, versions.LooseVersion(x.Version)) + versions = append(versions, x.Version) + } + + latestVersion, err := c.findLatestVersion(versions) + if err != nil { + return "", false, err } - sort.Stable(ls) - latestVersion = string(ls[len(ls)-1]) updated := latestVersion != *c.Config.ChartVersion return latestVersion, updated, nil } +func (c *HelmChart) findLatestVersion(inputVersions []string) (string, error) { + var versions semver.Collection + for _, x := range inputVersions { + v, err := semver.NewVersion(x) + if err != nil { + continue + } + + versions = append(versions, v) + } + if len(versions) == 0 { + return "", fmt.Errorf("no version found") + } + + sort.Stable(versions) + latestVersion := versions[len(versions)-1].Original() + return latestVersion, nil +} + func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster, sopsDecrypter sops.SopsDecrypter) error { err := c.doRender(ctx, k, sopsDecrypter) if err != nil { From 5e84b0f426e00f4ced5c40d5b8145c9a6510dce2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Dec 2022 17:15:33 +0100 Subject: [PATCH 0623/2268] feat: Implement updateConstraints for helm-chart.yaml --- docs/reference/deployments/helm.md | 11 +++++- e2e/helm_test.go | 63 ++++++++++++++++++++++++++++++ e2e/test-utils/project.go | 8 +++- pkg/deployment/helm_chart.go | 23 ++++++++++- pkg/types/helm_chart.go | 19 ++++----- 5 files changed, 111 insertions(+), 13 deletions(-) diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index bc49ddf0d..ee2c4ec21 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -68,6 +68,7 @@ helmChart: repo: https://charts.bitnami.com/bitnami chartName: redis chartVersion: 12.1.1 + updateConstraints: ~12.1.0 skipUpdate: false releaseName: redis-cache namespace: "{{ my.jinja2.var }}" @@ -102,7 +103,15 @@ helmChart: The name of the chart that can be found in the repository. ### chartVersion -The version of the chart. +The version of the chart. Must be a valid semantic version. + +### updateConstraints +Specifies version constraints to be used when running [helm-update](../commands/helm-update.md). See +[Checking Version Constraints](https://github.com/Masterminds/semver#checking-version-constraints) for details on the +supported syntax. + +If omitted, Kluctl will filter out pre-releases by default. Use a `updateConstraings` like `~1.2.3-0` to enable +pre-releases. ### skipUpdate Skip this Helm Chart when the [helm-update](../commands/helm-update.md) command is called. diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 94386b8f8..4ac15022b 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -273,6 +273,69 @@ func TestHelmUpdateAndUpgradeAndCommitOci(t *testing.T) { testHelmUpdate(t, true, true, true) } +func testHelmUpdateConstraints(t *testing.T, oci bool) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t, k) + + createNamespace(t, k, p.TestSlug()) + + repoUrl := createHelmOrOciRepo(t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, + {ChartName: "test-chart1", Version: "0.1.1"}, + {ChartName: "test-chart1", Version: "0.2.0"}, + {ChartName: "test-chart1", Version: "1.1.0"}, + {ChartName: "test-chart1", Version: "1.1.1"}, + {ChartName: "test-chart1", Version: "1.2.1"}, + }, oci, "", "") + + p.UpdateTarget("test", nil) + p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) + p.AddHelmDeployment("helm2", repoUrl, "test-chart1", "0.1.0", "test-helm2", p.TestSlug(), nil) + p.AddHelmDeployment("helm3", repoUrl, "test-chart1", "0.1.0", "test-helm3", p.TestSlug(), nil) + + p.UpdateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField("~0.1.0", "helmChart", "updateConstraints") + return nil + }, "") + p.UpdateYaml("helm2/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField("~0.2.0", "helmChart", "updateConstraints") + return nil + }, "") + p.UpdateYaml("helm3/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField("~1.2.0", "helmChart", "updateConstraints") + return nil + }, "") + + args := []string{"helm-update", "--upgrade"} + + _, stderr := p.KluctlMust(args...) + assert.Contains(t, stderr, "helm1: Chart has new version 0.1.1 available.") + assert.Contains(t, stderr, "helm2: Chart has new version 0.2.0 available.") + assert.Contains(t, stderr, "helm3: Chart has new version 1.2.1 available.") + + c1 := p.GetYaml("helm1/helm-chart.yaml") + c2 := p.GetYaml("helm2/helm-chart.yaml") + c3 := p.GetYaml("helm3/helm-chart.yaml") + + v1, _, _ := c1.GetNestedString("helmChart", "chartVersion") + v2, _, _ := c2.GetNestedString("helmChart", "chartVersion") + v3, _, _ := c3.GetNestedString("helmChart", "chartVersion") + assert.Equal(t, "0.1.1", v1) + assert.Equal(t, "0.2.0", v2) + assert.Equal(t, "1.2.1", v3) +} + +func TestHelmUpdateConstraints(t *testing.T) { + testHelmUpdateConstraints(t, false) +} + +func TestHelmUpdateConstraintsOci(t *testing.T) { + testHelmUpdateConstraints(t, true) +} + func TestHelmValues(t *testing.T) { t.Parallel() diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index f52e77e80..ffc225c62 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -127,14 +127,18 @@ func (p *TestProject) UpdateFile(path string, update func(f string) (string, err p.gitServer.UpdateFile("kluctl-project", path, update, message) } -func (p *TestProject) GetDeploymentYaml(dir string) *uo.UnstructuredObject { - o, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), dir, "deployment.yml")) +func (p *TestProject) GetYaml(path string) *uo.UnstructuredObject { + o, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), path)) if err != nil { p.t.Fatal(err) } return o } +func (p *TestProject) GetDeploymentYaml(dir string) *uo.UnstructuredObject { + return p.GetYaml(filepath.Join(dir, "deployment.yml")) +} + func (p *TestProject) ListDeploymentItemPathes(dir string, fullPath bool) []string { var ret []string o := p.GetDeploymentYaml(dir) diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index 5308e9431..bd5f27fdf 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -347,6 +347,15 @@ func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { } func (c *HelmChart) findLatestVersion(inputVersions []string) (string, error) { + var err error + var updateConstraints *semver.Constraints + if c.Config.UpdateConstraints != nil { + updateConstraints, err = semver.NewConstraint(*c.Config.UpdateConstraints) + if err != nil { + return "", fmt.Errorf("invalid update constraints '%s': %w", *c.Config.UpdateConstraints, err) + } + } + var versions semver.Collection for _, x := range inputVersions { v, err := semver.NewVersion(x) @@ -354,10 +363,22 @@ func (c *HelmChart) findLatestVersion(inputVersions []string) (string, error) { continue } + if updateConstraints == nil { + if v.Prerelease() != "" { + // we don't allow pre-releases by default. To allow pre-releases, use 1.0.0-0 as constraint + continue + } + } else if !updateConstraints.Check(v) { + continue + } versions = append(versions, v) } if len(versions) == 0 { - return "", fmt.Errorf("no version found") + if c.Config.UpdateConstraints == nil { + return "", fmt.Errorf("no version found") + } else { + return "", fmt.Errorf("no version found that satisfies constraints '%s'", *c.Config.UpdateConstraints) + } } sort.Stable(versions) diff --git a/pkg/types/helm_chart.go b/pkg/types/helm_chart.go index d5e9e4445..7cf645597 100644 --- a/pkg/types/helm_chart.go +++ b/pkg/types/helm_chart.go @@ -1,15 +1,16 @@ package types type HelmChartConfig2 struct { - Repo *string `yaml:"repo" validate:"required"` - CredentialsId *string `yaml:"credentialsId,omitempty"` - ChartName *string `yaml:"chartName,omitempty"` - ChartVersion *string `yaml:"chartVersion" validate:"required"` - ReleaseName string `yaml:"releaseName" validate:"required"` - Namespace *string `yaml:"namespace,omitempty"` - Output *string `yaml:"output,omitempty"` - SkipCRDs bool `yaml:"skipCRDs,omitempty"` - SkipUpdate bool `yaml:"skipUpdate,omitempty"` + Repo *string `yaml:"repo" validate:"required"` + CredentialsId *string `yaml:"credentialsId,omitempty"` + ChartName *string `yaml:"chartName,omitempty"` + ChartVersion *string `yaml:"chartVersion" validate:"required"` + UpdateConstraints *string `yaml:"updateConstraints"` + ReleaseName string `yaml:"releaseName" validate:"required"` + Namespace *string `yaml:"namespace,omitempty"` + Output *string `yaml:"output,omitempty"` + SkipCRDs bool `yaml:"skipCRDs,omitempty"` + SkipUpdate bool `yaml:"skipUpdate,omitempty"` } type HelmChartConfig struct { From 64f602eb37ff9e2b42ab6410c724319dd5539359 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Dec 2022 22:14:05 +0000 Subject: [PATCH 0624/2268] chore(deps): Bump golang.org/x/net from 0.2.0 to 0.4.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.2.0 to 0.4.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.2.0...v0.4.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 15 ++++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index b3ba13049..df37fc236 100644 --- a/go.mod +++ b/go.mod @@ -39,11 +39,11 @@ require ( github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 golang.org/x/crypto v0.3.0 - golang.org/x/net v0.2.0 + golang.org/x/net v0.4.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.2.0 - golang.org/x/term v0.2.0 - golang.org/x/text v0.4.0 + golang.org/x/sys v0.3.0 + golang.org/x/term v0.3.0 + golang.org/x/text v0.5.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.2 diff --git a/go.sum b/go.sum index 83e4ab2bc..6a0a94efe 100644 --- a/go.sum +++ b/go.sum @@ -971,8 +971,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 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= @@ -1078,14 +1078,14 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc 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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= 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= @@ -1095,8 +1095,9 @@ 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.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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= From 17c8fd9074f27c02bb4464ea88f48ad1e42f6389 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Dec 2022 23:18:05 +0100 Subject: [PATCH 0625/2268] chore: Add more info when pulling charts --- cmd/kluctl/commands/cmd_helm_pull.go | 2 +- cmd/kluctl/commands/cmd_helm_update.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index af62259f1..fba329f84 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -90,7 +90,7 @@ func doPull(statusPrefix string, p string, helmCredentials args.HelmCredentials, chart.SetCredentials(&helmCredentials) - s.Update("%s: Pulling Chart %s with version %s", statusPrefix, chart.GetChartName(), *chart.Config.ChartVersion) + s.UpdateAndInfoFallback("%s: Pulling Chart %s with version %s", statusPrefix, chart.GetChartName(), *chart.Config.ChartVersion) err = chart.Pull(cliCtx) if err != nil { diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index e831c591f..47edfeae9 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -218,7 +218,7 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployme } } - s.Update("%s: Committing chart", statusPrefix) + s.UpdateAndInfoFallback("%s: Committing chart", statusPrefix) mutex.Lock() defer mutex.Unlock() @@ -235,14 +235,14 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployme for _, p := range toAdd { _, err = wt.Add(p) if err != nil { - return err + return fmt.Errorf("failed to add %s to git index: %w", p, err) } } commitMsg := fmt.Sprintf("Updated helm chart %s from %s to %s", statusPrefix, oldVersion, newVersion) _, err = wt.Commit(commitMsg, &git.CommitOptions{}) if err != nil { - return err + return fmt.Errorf("failed to commit: %w", err) } s.Update("%s: Committed helm chart with version %s", statusPrefix, newVersion) From 18e686a7a594889fe45a75dfd94f3c149a71270b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Dec 2022 09:51:38 +0100 Subject: [PATCH 0626/2268] fix: Fix sporadically occuring "no such file or directly" errors See the inline comment for details. --- cmd/kluctl/commands/cmd_helm_update.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 47edfeae9..2b9fad76b 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -11,9 +11,11 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/sync/semaphore" "io/fs" + "math/rand" "os" "path/filepath" "sync" + "time" ) type helmUpdateCmd struct { @@ -233,7 +235,19 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployme } for _, p := range toAdd { - _, err = wt.Add(p) + // we have to retry a few times as Add() might fail with "no such file or directly" + // This is because it internally tries to get the git status, which fails if files are added/deleted in + // parallel by another goroutine (we're pulling in parallel). We're guarding the repo via the mutex from above + // so this is actually safe. + for i := 0; i < 10; i++ { + _, err = wt.Add(p) + if err == nil || !os.IsNotExist(err) { + break + } + // let's have some randomness in waiting time to ensure we don't run into the same problem again and again + s := time.Duration(rand.Intn(10) + 10) + time.Sleep(s * time.Millisecond) + } if err != nil { return fmt.Errorf("failed to add %s to git index: %w", p, err) } From 006eba70d60e5bddc948fed6905f1b94bf822de8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Dec 2022 09:58:00 +0100 Subject: [PATCH 0627/2268] refactor: Move --offline-kubernetes flag into OfflineKubernetesFlags --- cmd/kluctl/args/misc.go | 4 ++++ cmd/kluctl/commands/cmd_list_images.go | 4 ++-- cmd/kluctl/commands/cmd_render.go | 4 ++-- cmd/kluctl/commands/cmd_seal.go | 6 +++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cmd/kluctl/args/misc.go b/cmd/kluctl/args/misc.go index 196f52872..7b2540c12 100644 --- a/cmd/kluctl/args/misc.go +++ b/cmd/kluctl/args/misc.go @@ -8,6 +8,10 @@ type YesFlags struct { Yes bool `group:"misc" short:"y" help:"Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'."` } +type OfflineKubernetesFlags struct { + OfflineKubernetes bool `group:"misc" help:"Run command in offline mode, meaning that it will not try to connect the target cluster"` +} + type DryRunFlags struct { DryRun bool `group:"misc" help:"Performs all kubernetes API calls in dry-run mode."` } diff --git a/cmd/kluctl/commands/cmd_list_images.go b/cmd/kluctl/commands/cmd_list_images.go index 371310ed1..477a757c2 100644 --- a/cmd/kluctl/commands/cmd_list_images.go +++ b/cmd/kluctl/commands/cmd_list_images.go @@ -14,9 +14,9 @@ type listImagesCmd struct { args.HelmCredentials args.OutputFlags args.RenderOutputDirFlags + args.OfflineKubernetesFlags - OfflineKubernetes bool `group:"misc" help:"Run list-images in offline mode, meaning that it will not try to connect the target cluster"` - Simple bool `group:"misc" help:"Output a simplified version of the images list"` + Simple bool `group:"misc" help:"Output a simplified version of the images list"` } func (cmd *listImagesCmd) Help() string { diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index f3fcf8b5c..9a82dc4ce 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -16,9 +16,9 @@ type renderCmd struct { args.ImageFlags args.HelmCredentials args.RenderOutputDirFlags + args.OfflineKubernetesFlags - OfflineKubernetes bool `group:"misc" help:"Run render in offline mode, meaning that it will not try to connect the target cluster"` - PrintAll bool `group:"misc" help:"Write all rendered manifests to stdout"` + PrintAll bool `group:"misc" help:"Write all rendered manifests to stdout"` } func (cmd *renderCmd) Help() string { diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index e1b2dce24..679d5697d 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -18,10 +18,10 @@ type sealCmd struct { args.ProjectFlags args.TargetFlags args.HelmCredentials + args.OfflineKubernetesFlags - ForceReseal bool `group:"misc" help:"Lets kluctl ignore secret hashes found in already sealed secrets and thus forces resealing of those."` - CertFile string `group:"misc" help:"Use the given certificate for sealing instead of requesting it from the sealed-secrets controller"` - OfflineKubernetes bool `group:"misc" help:"Run seal in offline mode, meaning that it will not try to connect the target cluster"` + ForceReseal bool `group:"misc" help:"Lets kluctl ignore secret hashes found in already sealed secrets and thus forces resealing of those."` + CertFile string `group:"misc" help:"Use the given certificate for sealing instead of requesting it from the sealed-secrets controller"` } func (cmd *sealCmd) Help() string { From df99219670320b1510f4fac0b275438d71e55f75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:33:27 +0000 Subject: [PATCH 0628/2268] chore(deps): Bump golang.org/x/crypto from 0.3.0 to 0.4.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.3.0 to 0.4.0. - [Release notes](https://github.com/golang/crypto/releases) - [Commits](https://github.com/golang/crypto/compare/v0.3.0...v0.4.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index df37fc236..ab79f6f8b 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/stretchr/testify v1.8.1 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 - golang.org/x/crypto v0.3.0 + golang.org/x/crypto v0.4.0 golang.org/x/net v0.4.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.3.0 diff --git a/go.sum b/go.sum index 6a0a94efe..4b69beba1 100644 --- a/go.sum +++ b/go.sum @@ -884,8 +884,8 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= 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= From 17ba8b1c31d283de49701e7d8da979f6323e1183 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Dec 2022 10:07:39 +0100 Subject: [PATCH 0629/2268] feat: Implement --kubernetes-version to be used with --offline-kubernetes --- cmd/kluctl/args/misc.go | 3 ++- cmd/kluctl/commands/cmd_list_images.go | 1 + cmd/kluctl/commands/cmd_render.go | 1 + cmd/kluctl/commands/cmd_seal.go | 1 + cmd/kluctl/commands/utils.go | 2 ++ pkg/deployment/deployment_item.go | 2 +- pkg/deployment/helm_chart.go | 12 +++++++++--- pkg/deployment/shared_context.go | 1 + pkg/kluctl_project/target_context.go | 2 ++ 9 files changed, 20 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/args/misc.go b/cmd/kluctl/args/misc.go index 7b2540c12..52b7b6f4c 100644 --- a/cmd/kluctl/args/misc.go +++ b/cmd/kluctl/args/misc.go @@ -9,7 +9,8 @@ type YesFlags struct { } type OfflineKubernetesFlags struct { - OfflineKubernetes bool `group:"misc" help:"Run command in offline mode, meaning that it will not try to connect the target cluster"` + OfflineKubernetes bool `group:"misc" help:"Run command in offline mode, meaning that it will not try to connect the target cluster"` + KubernetesVersion string `group:"misc" help:"Specify the Kubernetes version that will be assumed. This will also override the kubeVersion used when rendering Helm Charts."` } type DryRunFlags struct { diff --git a/cmd/kluctl/commands/cmd_list_images.go b/cmd/kluctl/commands/cmd_list_images.go index 477a757c2..748d2805e 100644 --- a/cmd/kluctl/commands/cmd_list_images.go +++ b/cmd/kluctl/commands/cmd_list_images.go @@ -36,6 +36,7 @@ func (cmd *listImagesCmd) Run() error { helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, offlineKubernetes: cmd.OfflineKubernetes, + kubernetesVersion: cmd.KubernetesVersion, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { result := types.FixedImagesConfig{ diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 9a82dc4ce..d7c5ff244 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -45,6 +45,7 @@ func (cmd *renderCmd) Run() error { helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, offlineKubernetes: cmd.OfflineKubernetes, + kubernetesVersion: cmd.KubernetesVersion, } return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { if cmd.PrintAll { diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 679d5697d..fc31ec63f 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -48,6 +48,7 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L helmCredentials: cmd.HelmCredentials, forSeal: true, offlineKubernetes: cmd.OfflineKubernetes, + kubernetesVersion: cmd.KubernetesVersion, } ptArgs.targetFlags.Target = targetName diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 221f97ac0..5715f93e4 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -101,6 +101,7 @@ type projectTargetCommandArgs struct { forSeal bool forCompletion bool offlineKubernetes bool + kubernetesVersion string } type commandCtx struct { @@ -167,6 +168,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm TargetNameOverride: args.targetFlags.TargetNameOverride, ContextOverride: args.targetFlags.Context, OfflineK8s: args.offlineKubernetes, + K8sVersion: args.kubernetesVersion, DryRun: args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, ExternalArgs: optionArgs2, ForSeal: args.forSeal, diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index f6caec641..bbd9665b6 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -214,7 +214,7 @@ func (di *DeploymentItem) renderHelmCharts() error { } } - return chart.Render(di.ctx.Ctx, di.ctx.K, di.ctx.SopsDecrypter) + return chart.Render(di.ctx.Ctx, di.ctx.K, di.ctx.K8sVersion, di.ctx.SopsDecrypter) }) if err != nil { return err diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index bd5f27fdf..d21426da2 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -386,15 +386,15 @@ func (c *HelmChart) findLatestVersion(inputVersions []string) (string, error) { return latestVersion, nil } -func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster, sopsDecrypter sops.SopsDecrypter) error { - err := c.doRender(ctx, k, sopsDecrypter) +func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { + err := c.doRender(ctx, k, k8sVersion, sopsDecrypter) if err != nil { return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", c.chartName, c.Config.ReleaseName, err) } return nil } -func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster, sopsDecrypter sops.SopsDecrypter) error { +func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { chartDir := c.chartDir needsPull, versionChanged, prePulledVersion, err := c.checkNeedsPull(chartDir, false) @@ -457,6 +457,12 @@ func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster, sopsDecrypt return err } } + if k8sVersion != "" { + kubeVersion, err = chartutil.ParseKubeVersion(k8sVersion) + if err != nil { + return err + } + } namespace := "default" if c.Config.Namespace != nil { diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index be1492050..7fce849ce 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -11,6 +11,7 @@ import ( type SharedContext struct { Ctx context.Context K *k8s.K8sCluster + K8sVersion string RP *repocache.GitRepoCache SopsDecrypter sops.SopsDecrypter VarsLoader *vars.VarsLoader diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 952c44d85..980585382 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -31,6 +31,7 @@ type TargetContextParams struct { TargetNameOverride string ContextOverride string OfflineK8s bool + K8sVersion string DryRun bool ExternalArgs *uo.UnstructuredObject ForSeal bool @@ -103,6 +104,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe dctx := deployment.SharedContext{ Ctx: ctx, K: k, + K8sVersion: params.K8sVersion, RP: p.RP, SopsDecrypter: p.SopsDecrypter, VarsLoader: varsLoader, From da1647e2b7122ba148d27661e32cd4b1f7d34a1b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Dec 2022 10:20:07 +0100 Subject: [PATCH 0630/2268] tests: Add tests for --kubernetes-version --- e2e/helm_test.go | 58 ++++++++++++++++--- e2e/sops_test.go | 7 ++- e2e/test-utils/envtest_cluster.go | 12 ++++ .../test-helm-chart/templates/configmap.yaml | 1 + 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 4ac15022b..cedfa3a60 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -382,19 +382,22 @@ func TestHelmValues(t *testing.T) { cm3 := assertConfigMapExists(t, k, p.TestSlug(), "test-helm3-test-chart1") assert.Equal(t, map[string]any{ - "a": "x1", - "b": "y1", - "version": "0.1.0", + "a": "x1", + "b": "y1", + "version": "0.1.0", + "kubeVersion": k.ServerVersion.String(), }, cm1.Object["data"]) assert.Equal(t, map[string]any{ - "a": "x2", - "b": "y2", - "version": "0.1.0", + "a": "x2", + "b": "y2", + "version": "0.1.0", + "kubeVersion": k.ServerVersion.String(), }, cm2.Object["data"]) assert.Equal(t, map[string]any{ - "a": "a", - "b": "b", - "version": "0.1.0", + "a": "a", + "b": "b", + "version": "0.1.0", + "kubeVersion": k.ServerVersion.String(), }, cm3.Object["data"]) } @@ -428,3 +431,40 @@ func TestHelmTemplateChartYaml(t *testing.T) { assertConfigMapExists(t, k, p.TestSlug()+"-a", "test-helm-ns-test-chart1") assertConfigMapExists(t, k, p.TestSlug()+"-b", "test-helm-ns-test-chart1") } + +func TestHelmRenderOfflineKubernetes(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t, k) + + createNamespace(t, k, p.TestSlug()) + + repoUrl := test_utils.CreateHelmRepo(t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, + }, "", "") + + p.UpdateTarget("test", nil) + p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) + + stdout, _ := p.KluctlMust("render", "--print-all", "--offline-kubernetes", "-t", "test") + cm1 := uo.FromStringMust(stdout) + + assert.Equal(t, map[string]any{ + "a": "v1", + "b": "v2", + "version": "0.1.0", + "kubeVersion": "v1.20.0", + }, cm1.Object["data"]) + + stdout, _ = p.KluctlMust("render", "--print-all", "--offline-kubernetes", "--kubernetes-version", "1.22.1", "-t", "test") + cm1 = uo.FromStringMust(stdout) + + assert.Equal(t, map[string]any{ + "a": "v1", + "b": "v2", + "version": "0.1.0", + "kubeVersion": "v1.22.1", + }, cm1.Object["data"]) +} diff --git a/e2e/sops_test.go b/e2e/sops_test.go index 6b4a1a1a2..da65ece12 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -109,8 +109,9 @@ func TestSopsHelmValues(t *testing.T) { cm1 := assertConfigMapExists(t, k, p.TestSlug(), "test-helm1-test-chart1") assert.Equal(t, map[string]any{ - "a": "secret1", - "b": "secret2", - "version": "0.1.0", + "a": "secret1", + "b": "secret2", + "version": "0.1.0", + "kubeVersion": k.ServerVersion.String(), }, cm1.Object["data"]) } diff --git a/e2e/test-utils/envtest_cluster.go b/e2e/test-utils/envtest_cluster.go index c32b82665..df6893c7d 100644 --- a/e2e/test-utils/envtest_cluster.go +++ b/e2e/test-utils/envtest_cluster.go @@ -7,6 +7,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/version" + "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "k8s.io/client-go/util/flowcontrol" @@ -28,6 +30,7 @@ type EnvTestCluster struct { HttpClient *http.Client DynamicClient dynamic.Interface + ServerVersion *version.Info callbackServer webhook.Server callbackServerStop context.CancelFunc @@ -85,6 +88,15 @@ func (k *EnvTestCluster) Start() error { } k.DynamicClient = dynamicClient + discoveryClient, err := discovery.NewDiscoveryClientForConfigAndClient(k.config, client) + if err != nil { + return err + } + k.ServerVersion, err = discoveryClient.ServerVersion() + if err != nil { + return err + } + return nil } diff --git a/e2e/test-utils/test-helm-chart/templates/configmap.yaml b/e2e/test-utils/test-helm-chart/templates/configmap.yaml index fe63edb28..25adf10ac 100644 --- a/e2e/test-utils/test-helm-chart/templates/configmap.yaml +++ b/e2e/test-utils/test-helm-chart/templates/configmap.yaml @@ -8,3 +8,4 @@ data: a: {{ .Values.data.a }} b: {{ .Values.data.b }} version: {{ .Chart.Version }} + kubeVersion: {{ .Capabilities.KubeVersion }} From f25e9812e4538b92470de55d0814a0a86265b45e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Dec 2022 10:34:46 +0100 Subject: [PATCH 0631/2268] docs: Run make replace-commands-help --- docs/reference/commands/list-images.md | 6 ++++-- docs/reference/commands/render.md | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/reference/commands/list-images.md b/docs/reference/commands/list-images.md index e90fe6624..125b79770 100644 --- a/docs/reference/commands/list-images.md +++ b/docs/reference/commands/list-images.md @@ -48,8 +48,10 @@ Misc arguments: Must be in the form --helm-username=:, where must match the id specified in the helm-chart.yaml. - --offline-kubernetes Run list-images in offline mode, meaning that it will not try - to connect the target cluster + --kubernetes-version string Specify the Kubernetes version that will be assumed. This will + also override the kubeVersion used when rendering Helm Charts. + --offline-kubernetes Run command in offline mode, meaning that it will not try to + connect the target cluster -o, --output stringArray Specify output target file. Can be specified multiple times --render-output-dir string Specifies the target directory to render the project into. If omitted, a temporary directory is used. diff --git a/docs/reference/commands/render.md b/docs/reference/commands/render.md index f27882a06..3556e40c0 100644 --- a/docs/reference/commands/render.md +++ b/docs/reference/commands/render.md @@ -45,7 +45,9 @@ Misc arguments: Must be in the form --helm-username=:, where must match the id specified in the helm-chart.yaml. - --offline-kubernetes Run render in offline mode, meaning that it will not try to + --kubernetes-version string Specify the Kubernetes version that will be assumed. This will + also override the kubeVersion used when rendering Helm Charts. + --offline-kubernetes Run command in offline mode, meaning that it will not try to connect the target cluster --print-all Write all rendered manifests to stdout --render-output-dir string Specifies the target directory to render the project into. If From 5a5112f9957acd247467111c31c2041fe6dbd272 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 10:34:43 +0100 Subject: [PATCH 0632/2268] refactor: Don't use global cliCtx anymore and pass ctx around where needed --- .../commands/cmd_check_image_updates.go | 17 +++---- cmd/kluctl/commands/cmd_delete.go | 12 ++--- cmd/kluctl/commands/cmd_deploy.go | 31 +++++++------ cmd/kluctl/commands/cmd_diff.go | 11 ++--- cmd/kluctl/commands/cmd_flux_reconcile.go | 10 ++--- cmd/kluctl/commands/cmd_flux_resume.go | 4 +- cmd/kluctl/commands/cmd_flux_suspend.go | 4 +- cmd/kluctl/commands/cmd_helm_pull.go | 13 +++--- cmd/kluctl/commands/cmd_helm_update.go | 25 ++++++----- cmd/kluctl/commands/cmd_list_images.go | 9 ++-- cmd/kluctl/commands/cmd_list_targets.go | 6 +-- cmd/kluctl/commands/cmd_poke_images.go | 13 +++--- cmd/kluctl/commands/cmd_prune.go | 17 +++---- cmd/kluctl/commands/cmd_render.go | 11 ++--- cmd/kluctl/commands/cmd_seal.go | 34 +++++++------- cmd/kluctl/commands/cmd_validate.go | 11 ++--- cmd/kluctl/commands/cmd_version.go | 3 +- cmd/kluctl/commands/cobra_utils.go | 5 ++- cmd/kluctl/commands/command_result.go | 13 +++--- cmd/kluctl/commands/completion.go | 44 ++++++++++--------- cmd/kluctl/commands/root.go | 8 +++- cmd/kluctl/commands/utils.go | 12 ++--- 22 files changed, 167 insertions(+), 146 deletions(-) diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index 14be5ede7..a21df32c3 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/registries" "github.com/kluctl/kluctl/v2/pkg/status" @@ -22,20 +23,20 @@ func (cmd *checkImageUpdatesCmd) Help() string { return `This is based on a best effort approach and might give many false-positives.` } -func (cmd *checkImageUpdatesCmd) Run() error { +func (cmd *checkImageUpdatesCmd) Run(ctx context.Context) error { ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - return runCheckImageUpdates(ctx) + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { + return runCheckImageUpdates(cmdCtx) }) } -func runCheckImageUpdates(ctx *commandCtx) error { - renderedImages := ctx.targetCtx.DeploymentCollection.FindRenderedImages() +func runCheckImageUpdates(cmdCtx *commandCtx) error { + renderedImages := cmdCtx.targetCtx.DeploymentCollection.FindRenderedImages() - rh := registries.NewRegistryHelper(ctx.ctx) + rh := registries.NewRegistryHelper(cmdCtx.ctx) imageTags := make(map[string]interface{}) var mutex sync.Mutex @@ -76,7 +77,7 @@ func runCheckImageUpdates(ctx *commandCtx) error { for _, image := range images { s := strings.SplitN(image, ":", 2) if len(s) == 1 { - status.Warning(ctx.ctx, "%s: Ignoring image %s as it doesn't specify a tag", ref.String(), image) + status.Warning(cmdCtx.ctx, "%s: Ignoring image %s as it doesn't specify a tag", ref.String(), image) continue } repo := s[0] @@ -84,7 +85,7 @@ func runCheckImageUpdates(ctx *commandCtx) error { repoTags, _ := imageTags[repo].([]string) err, _ := imageTags[repo].(error) if err != nil { - status.Warning(ctx.ctx, "%s: Failed to list tags for %s. %v", ref.String(), repo, err) + status.Warning(cmdCtx.ctx, "%s: Failed to list tags for %s. %v", ref.String(), repo, err) continue } diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index ecfc97814..48a09b2c7 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -37,7 +37,7 @@ project (anymore). It really only decides based on the 'deleteByLabel' labels an take the local target/state into account!` } -func (cmd *deleteCmd) Run() error { +func (cmd *deleteCmd) Run(ctx context.Context) error { ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, @@ -48,8 +48,8 @@ func (cmd *deleteCmd) Run() error { dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - cmd2 := commands.NewDeleteCommand(ctx.targetCtx.DeploymentCollection) + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { + cmd2 := commands.NewDeleteCommand(cmdCtx.targetCtx.DeploymentCollection) deleteByLabels, err := deployment.ParseArgs(cmd.DeleteByLabel) if err != nil { @@ -58,15 +58,15 @@ func (cmd *deleteCmd) Run() error { cmd2.OverrideDeleteByLabels = deleteByLabels - objects, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) + objects, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) if err != nil { return err } - result, err := confirmedDeleteObjects(ctx.ctx, ctx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) + result, err := confirmedDeleteObjects(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) if err != nil { return err } - err = outputCommandResult(cmd.OutputFormat, result) + err = outputCommandResult(ctx, cmd.OutputFormat, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 9b62a7aa8..f819935db 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" @@ -34,7 +35,7 @@ It will also output a list of prunable objects (without actually deleting them). ` } -func (cmd *deployCmd) Run() error { +func (cmd *deployCmd) Run(ctx context.Context) error { ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, @@ -45,16 +46,16 @@ func (cmd *deployCmd) Run() error { dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - return cmd.runCmdDeploy(ctx) + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { + return cmd.runCmdDeploy(cmdCtx) }) } -func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { - status.Trace(ctx.ctx, "enter runCmdDeploy") - defer status.Trace(ctx.ctx, "leave runCmdDeploy") +func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { + status.Trace(cmdCtx.ctx, "enter runCmdDeploy") + defer status.Trace(cmdCtx.ctx, "leave runCmdDeploy") - cmd2 := commands.NewDeployCommand(ctx.targetCtx.DeploymentCollection) + cmd2 := commands.NewDeployCommand(cmdCtx.targetCtx.DeploymentCollection) cmd2.ForceApply = cmd.ForceApply cmd2.ReplaceOnError = cmd.ReplaceOnError cmd2.ForceReplaceOnError = cmd.ForceReplaceOnError @@ -62,16 +63,18 @@ func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { cmd2.ReadinessTimeout = cmd.ReadinessTimeout cmd2.NoWait = cmd.NoWait - cb := cmd.diffResultCb + cb := func(diffResult *types.CommandResult) error { + return cmd.diffResultCb(cmdCtx.ctx, diffResult) + } if cmd.Yes || cmd.DryRun { cb = nil } - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K, cb) + result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, cb) if err != nil { return err } - err = outputCommandResult(cmd.OutputFormat, result) + err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormat, result) if err != nil { return err } @@ -81,8 +84,8 @@ func (cmd *deployCmd) runCmdDeploy(ctx *commandCtx) error { return nil } -func (cmd *deployCmd) diffResultCb(diffResult *types.CommandResult) error { - err := outputCommandResult(nil, diffResult) +func (cmd *deployCmd) diffResultCb(ctx context.Context, diffResult *types.CommandResult) error { + err := outputCommandResult(ctx, nil, diffResult) if err != nil { return err } @@ -90,11 +93,11 @@ func (cmd *deployCmd) diffResultCb(diffResult *types.CommandResult) error { return nil } if len(diffResult.Errors) != 0 { - if !status.AskForConfirmation(cliCtx, "The diff resulted in errors, do you still want to proceed?") { + if !status.AskForConfirmation(ctx, "The diff resulted in errors, do you still want to proceed?") { return fmt.Errorf("aborted") } } else { - if !status.AskForConfirmation(cliCtx, "The diff succeeded, do you want to proceed?") { + if !status.AskForConfirmation(ctx, "The diff succeeded, do you want to proceed?") { return fmt.Errorf("aborted") } } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index d18becfe5..bb8fcce19 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" @@ -27,7 +28,7 @@ is currently not documented and prone to changes. After the diff is performed, the command will also search for prunable objects and list them.` } -func (cmd *diffCmd) Run() error { +func (cmd *diffCmd) Run(ctx context.Context) error { ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, @@ -37,19 +38,19 @@ func (cmd *diffCmd) Run() error { helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - cmd2 := commands.NewDiffCommand(ctx.targetCtx.DeploymentCollection) + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { + cmd2 := commands.NewDiffCommand(cmdCtx.targetCtx.DeploymentCollection) cmd2.ForceApply = cmd.ForceApply cmd2.ReplaceOnError = cmd.ReplaceOnError cmd2.ForceReplaceOnError = cmd.ForceReplaceOnError cmd2.IgnoreTags = cmd.IgnoreTags cmd2.IgnoreLabels = cmd.IgnoreLabels cmd2.IgnoreAnnotations = cmd.IgnoreAnnotations - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) + result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) if err != nil { return err } - err = outputCommandResult(cmd.OutputFormat, result) + err = outputCommandResult(ctx, cmd.OutputFormat, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_flux_reconcile.go b/cmd/kluctl/commands/cmd_flux_reconcile.go index a6ed024e2..8e4668024 100644 --- a/cmd/kluctl/commands/cmd_flux_reconcile.go +++ b/cmd/kluctl/commands/cmd_flux_reconcile.go @@ -16,7 +16,7 @@ type fluxReconcileCmd struct { args.KluctlDeploymentFlags } -func (cmd *fluxReconcileCmd) Run() error { +func (cmd *fluxReconcileCmd) Run(ctx context.Context) error { var ( sourceNamespace string sourceName string @@ -61,7 +61,7 @@ func (cmd *fluxReconcileCmd) Run() error { } ref2 := k8s2.ObjectRef{GVK: args.GitRepositoryGVK, Name: sourceName, Namespace: sourceNamespace} - s := status.Start(cliCtx, "Annotating Source %s in %s namespace", sourceName, sourceNamespace) + s := status.Start(ctx, "Annotating Source %s in %s namespace", sourceName, sourceNamespace) defer s.Failed() _, _, err = k.PatchObjectWithJsonPatch(ref2, patch, k8s.PatchOptions{}) @@ -70,7 +70,7 @@ func (cmd *fluxReconcileCmd) Run() error { } s.Success() - s = status.Start(cliCtx, "Waiting for Source %s to finish reconciliation", sourceName) + s = status.Start(ctx, "Waiting for Source %s to finish reconciliation", sourceName) if !noWait { ready, err := WaitForReady(k, ref2) @@ -83,7 +83,7 @@ func (cmd *fluxReconcileCmd) Run() error { s.Success() } - s := status.Start(cliCtx, "Annotating KluctlDeployment %s in %s namespace", kd, ns) + s := status.Start(ctx, "Annotating KluctlDeployment %s in %s namespace", kd, ns) defer s.Failed() _, _, err = k.PatchObjectWithJsonPatch(ref, patch, k8s.PatchOptions{}) @@ -92,7 +92,7 @@ func (cmd *fluxReconcileCmd) Run() error { } s.Success() - s = status.Start(cliCtx, "Waiting for KluctlDeployment %s in %s namespace to finish reconciliation", kd, ns) + s = status.Start(ctx, "Waiting for KluctlDeployment %s in %s namespace to finish reconciliation", kd, ns) if !noWait { ready, err := WaitForReady(k, ref) diff --git a/cmd/kluctl/commands/cmd_flux_resume.go b/cmd/kluctl/commands/cmd_flux_resume.go index 8b7aaafc8..3b90f1219 100644 --- a/cmd/kluctl/commands/cmd_flux_resume.go +++ b/cmd/kluctl/commands/cmd_flux_resume.go @@ -14,7 +14,7 @@ type fluxResumeCmd struct { } // TODO add reconciliation after resume -func (cmd *fluxResumeCmd) Run() error { +func (cmd *fluxResumeCmd) Run(ctx context.Context) error { ns := cmd.KluctlDeploymentFlags.Namespace kd := cmd.KluctlDeploymentFlags.KluctlDeployment @@ -35,7 +35,7 @@ func (cmd *fluxResumeCmd) Run() error { Value: false, }} - s := status.Start(cliCtx, "Resuming KluctlDeployment %s in %s namespace", kd, ns) + s := status.Start(ctx, "Resuming KluctlDeployment %s in %s namespace", kd, ns) defer s.Failed() _, _, err = k.PatchObjectWithJsonPatch(ref, patch, k8s.PatchOptions{}) diff --git a/cmd/kluctl/commands/cmd_flux_suspend.go b/cmd/kluctl/commands/cmd_flux_suspend.go index 6e5422fb6..d06212dbe 100644 --- a/cmd/kluctl/commands/cmd_flux_suspend.go +++ b/cmd/kluctl/commands/cmd_flux_suspend.go @@ -13,7 +13,7 @@ type fluxSuspendCmd struct { args.KluctlDeploymentFlags } -func (cmd *fluxSuspendCmd) Run() error { +func (cmd *fluxSuspendCmd) Run(ctx context.Context) error { ns := cmd.KluctlDeploymentFlags.Namespace kd := cmd.KluctlDeploymentFlags.KluctlDeployment @@ -33,7 +33,7 @@ func (cmd *fluxSuspendCmd) Run() error { Value: true, }} - s := status.Start(cliCtx, "Suspending KluctlDeployment %s in %s namespace", kd, ns) + s := status.Start(ctx, "Suspending KluctlDeployment %s in %s namespace", kd, ns) defer s.Failed() _, _, err = k.PatchObjectWithJsonPatch(ref, patch, k8s.PatchOptions{}) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index fba329f84..4ee57ec2e 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" @@ -25,7 +26,7 @@ func (cmd *helmPullCmd) Help() string { pulling is only needed when really required (e.g. when the chart version changes).` } -func (cmd *helmPullCmd) Run() error { +func (cmd *helmPullCmd) Run(ctx context.Context) error { cwd, err := os.Getwd() if err != nil { return err @@ -52,10 +53,10 @@ func (cmd *helmPullCmd) Run() error { return err } - utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, &wg, func() error { - s := status.Start(cliCtx, "%s: Pulling Chart", statusPrefix) + utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { + s := status.Start(ctx, "%s: Pulling Chart", statusPrefix) defer s.Failed() - err := doPull(statusPrefix, p, cmd.HelmCredentials, s) + err := doPull(ctx, statusPrefix, p, cmd.HelmCredentials, s) if err != nil { return err } @@ -77,7 +78,7 @@ func (cmd *helmPullCmd) Run() error { return nil } -func doPull(statusPrefix string, p string, helmCredentials args.HelmCredentials, s *status.StatusContext) error { +func doPull(ctx context.Context, statusPrefix string, p string, helmCredentials args.HelmCredentials, s *status.StatusContext) error { doError := func(err error) error { s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return err @@ -92,7 +93,7 @@ func doPull(statusPrefix string, p string, helmCredentials args.HelmCredentials, s.UpdateAndInfoFallback("%s: Pulling Chart %s with version %s", statusPrefix, chart.GetChartName(), *chart.Config.ChartVersion) - err = chart.Pull(cliCtx) + err = chart.Pull(ctx) if err != nil { return doError(err) } diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 2b9fad76b..beecb5d0e 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/go-git/go-git/v5" "github.com/hashicorp/go-multierror" @@ -31,7 +32,7 @@ func (cmd *helmUpdateCmd) Help() string { return `Optionally performs the actual upgrade and/or add a commit to version control.` } -func (cmd *helmUpdateCmd) Run() error { +func (cmd *helmUpdateCmd) Run(ctx context.Context) error { cwd, err := os.Getwd() if err != nil { return err @@ -62,8 +63,8 @@ func (cmd *helmUpdateCmd) Run() error { return nil } - utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, &wg, func() error { - chart, newVersion, updated, err := cmd.doCheckUpdate(gitRootPath, p) + utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { + chart, newVersion, updated, err := cmd.doCheckUpdate(ctx, gitRootPath, p) if err != nil { return err } @@ -100,16 +101,16 @@ func (cmd *helmUpdateCmd) Run() error { for _, uc := range updatedCharts { uc := uc - utils.GoLimitedMultiError(cliCtx, sem, &errs, &mutex, &wg, func() error { + utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { if cmd.Interactive { statusPrefix, _ := filepath.Rel(gitRootPath, filepath.Dir(uc.path)) - if !status.AskForConfirmation(cliCtx, fmt.Sprintf("%s: Do you want to upgrade Chart %s from version %s to %s?", + if !status.AskForConfirmation(ctx, fmt.Sprintf("%s: Do you want to upgrade Chart %s from version %s to %s?", statusPrefix, uc.chart.GetChartName(), uc.oldVersion, uc.newVersion)) { return nil } } - err := cmd.pullAndCommitChart(gitRootPath, uc.chart, uc.oldVersion, uc.newVersion, &mutex) + err := cmd.pullAndCommitChart(ctx, gitRootPath, uc.chart, uc.oldVersion, uc.newVersion, &mutex) if err != nil { return err } @@ -125,13 +126,13 @@ func (cmd *helmUpdateCmd) Run() error { return errs.ErrorOrNil() } -func (cmd *helmUpdateCmd) doCheckUpdate(gitRootPath string, p string) (*deployment.HelmChart, string, bool, error) { +func (cmd *helmUpdateCmd) doCheckUpdate(ctx context.Context, gitRootPath string, p string) (*deployment.HelmChart, string, bool, error) { statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) if err != nil { return nil, "", false, err } - s := status.Start(cliCtx, "%s: Checking for updates", statusPrefix) + s := status.Start(ctx, "%s: Checking for updates", statusPrefix) doError := func(err error) (*deployment.HelmChart, string, bool, error) { s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return nil, "", false, err @@ -144,7 +145,7 @@ func (cmd *helmUpdateCmd) doCheckUpdate(gitRootPath string, p string) (*deployme chart.SetCredentials(&cmd.HelmCredentials) - newVersion, updated, err := chart.CheckUpdate(cliCtx) + newVersion, updated, err := chart.CheckUpdate(ctx) if err != nil { return doError(err) } @@ -162,13 +163,13 @@ func (cmd *helmUpdateCmd) doCheckUpdate(gitRootPath string, p string) (*deployme return chart, newVersion, updated, nil } -func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployment.HelmChart, oldVersion string, newVersion string, mutex *sync.Mutex) error { +func (cmd *helmUpdateCmd) pullAndCommitChart(ctx context.Context, gitRootPath string, chart *deployment.HelmChart, oldVersion string, newVersion string, mutex *sync.Mutex) error { statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(chart.ConfigFile)) if err != nil { return err } - s := status.Start(cliCtx, "%s: Pulling Chart", statusPrefix) + s := status.Start(ctx, "%s: Pulling Chart", statusPrefix) defer s.Failed() chart.Config.ChartVersion = &newVersion @@ -195,7 +196,7 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(gitRootPath string, chart *deployme return err } - err = doPull(statusPrefix, chart.ConfigFile, cmd.HelmCredentials, s) + err = doPull(ctx, statusPrefix, chart.ConfigFile, cmd.HelmCredentials, s) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_list_images.go b/cmd/kluctl/commands/cmd_list_images.go index 748d2805e..7e1f78e83 100644 --- a/cmd/kluctl/commands/cmd_list_images.go +++ b/cmd/kluctl/commands/cmd_list_images.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/types" ) @@ -26,7 +27,7 @@ If fixed images ('-f/--fixed-image') are provided, these are also taken into acc as described in the deploy command.` } -func (cmd *listImagesCmd) Run() error { +func (cmd *listImagesCmd) Run(ctx context.Context) error { ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, @@ -38,10 +39,10 @@ func (cmd *listImagesCmd) Run() error { offlineKubernetes: cmd.OfflineKubernetes, kubernetesVersion: cmd.KubernetesVersion, } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { result := types.FixedImagesConfig{ - Images: ctx.images.SeenImages(cmd.Simple), + Images: cmdCtx.images.SeenImages(cmd.Simple), } - return outputYamlResult(cmd.Output, result, false) + return outputYamlResult(ctx, cmd.Output, result, false) }) } diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index f8a6b5626..91eacf231 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -16,12 +16,12 @@ func (cmd *listTargetsCmd) Help() string { return `Outputs a yaml list with all target, including dynamic targets` } -func (cmd *listTargetsCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { +func (cmd *listTargetsCmd) Run(ctx context.Context) error { + return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var result []*types.Target for _, t := range p.DynamicTargets { result = append(result, t.Target) } - return outputYamlResult(cmd.Output, result, false) + return outputYamlResult(ctx, cmd.Output, result, false) }) } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index b28c2f369..7492acc74 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" @@ -26,7 +27,7 @@ deploying the target. Only images used in combination with 'images.get_image(... replaced` } -func (cmd *pokeImagesCmd) Run() error { +func (cmd *pokeImagesCmd) Run(ctx context.Context) error { ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, @@ -37,20 +38,20 @@ func (cmd *pokeImagesCmd) Run() error { dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { - if !status.AskForConfirmation(cliCtx, fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", ctx.targetCtx.ClusterContext)) { + if !status.AskForConfirmation(ctx, fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", cmdCtx.targetCtx.ClusterContext)) { return fmt.Errorf("aborted") } } - cmd2 := commands.NewPokeImagesCommand(ctx.targetCtx.DeploymentCollection) + cmd2 := commands.NewPokeImagesCommand(cmdCtx.targetCtx.DeploymentCollection) - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) + result, err := cmd2.Run(ctx, cmdCtx.targetCtx.SharedContext.K) if err != nil { return err } - err = outputCommandResult(cmd.OutputFormat, result) + err = outputCommandResult(ctx, cmd.OutputFormat, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index a9197b22f..13516a1b4 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" @@ -27,7 +28,7 @@ func (cmd *pruneCmd) Help() string { 3. Remove all objects from the list of 1. that are part of the list in 2.` } -func (cmd *pruneCmd) Run() error { +func (cmd *pruneCmd) Run(ctx context.Context) error { ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, @@ -38,22 +39,22 @@ func (cmd *pruneCmd) Run() error { dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { - return cmd.runCmdPrune(ctx) + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { + return cmd.runCmdPrune(cmdCtx) }) } -func (cmd *pruneCmd) runCmdPrune(ctx *commandCtx) error { - cmd2 := commands.NewPruneCommand(ctx.targetCtx.DeploymentCollection) - objects, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) +func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { + cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.DeploymentCollection) + objects, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) if err != nil { return err } - result, err := confirmedDeleteObjects(ctx.ctx, ctx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) + result, err := confirmedDeleteObjects(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) if err != nil { return err } - err = outputCommandResult(cmd.OutputFormat, result) + err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormat, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index d7c5ff244..16c0bc2c1 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -26,7 +27,7 @@ func (cmd *renderCmd) Help() string { a temporary directory or a specified directory.` } -func (cmd *renderCmd) Run() error { +func (cmd *renderCmd) Run(ctx context.Context) error { isTmp := false if cmd.RenderOutputDir == "" { p, err := ioutil.TempDir(utils.GetTmpBaseDir(), "rendered-") @@ -47,10 +48,10 @@ func (cmd *renderCmd) Run() error { offlineKubernetes: cmd.OfflineKubernetes, kubernetesVersion: cmd.KubernetesVersion, } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { if cmd.PrintAll { var all []any - for _, d := range ctx.targetCtx.DeploymentCollection.Deployments { + for _, d := range cmdCtx.targetCtx.DeploymentCollection.Deployments { for _, o := range d.Objects { all = append(all, o) } @@ -58,10 +59,10 @@ func (cmd *renderCmd) Run() error { if isTmp { defer os.RemoveAll(cmd.RenderOutputDir) } - status.Flush(ctx.ctx) + status.Flush(cmdCtx.ctx) return yaml.WriteYamlAllStream(os.Stdout, all) } else { - status.Info(ctx.ctx, "Rendered into %s", ctx.targetCtx.SharedContext.RenderDir) + status.Info(cmdCtx.ctx, "Rendered into %s", cmdCtx.targetCtx.SharedContext.RenderDir) } return nil }) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index fc31ec63f..18fd57647 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -53,30 +53,30 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L ptArgs.targetFlags.Target = targetName // pass forSeal=True so that .sealme files are rendered as well - return withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { - err := ctx.targetCtx.DeploymentCollection.RenderDeployments() + return withProjectTargetCommandContext(ctx, ptArgs, p, func(cmdCtx *commandCtx) error { + err := cmdCtx.targetCtx.DeploymentCollection.RenderDeployments() if err != nil { return doFail(err) } - cert, err := cmd.loadCert(ctx) + cert, err := cmd.loadCert(cmdCtx) if err != nil { return doFail(err) } - sealer, err := seal.NewSealer(ctx.ctx, cert, cmd.ForceReseal) + sealer, err := seal.NewSealer(cmdCtx.ctx, cert, cmd.ForceReseal) if err != nil { return doFail(err) } outputPattern := targetName - if ctx.targetCtx.DeploymentProject.Config.SealedSecrets != nil && ctx.targetCtx.DeploymentProject.Config.SealedSecrets.OutputPattern != nil { + if cmdCtx.targetCtx.DeploymentProject.Config.SealedSecrets != nil && cmdCtx.targetCtx.DeploymentProject.Config.SealedSecrets.OutputPattern != nil { // the outputPattern is rendered already at this point, meaning that for example // '{{ cluster.name }}/{{ target.name }}' will already be rendered to 'my-cluster/my-target' - outputPattern = *ctx.targetCtx.DeploymentProject.Config.SealedSecrets.OutputPattern + outputPattern = *cmdCtx.targetCtx.DeploymentProject.Config.SealedSecrets.OutputPattern } - cmd2 := commands.NewSealCommand(ctx.targetCtx.DeploymentCollection, outputPattern, ctx.targetCtx.SharedContext.RenderDir, ctx.targetCtx.SharedContext.SealedSecretsDir) + cmd2 := commands.NewSealCommand(cmdCtx.targetCtx.DeploymentCollection, outputPattern, cmdCtx.targetCtx.SharedContext.RenderDir, cmdCtx.targetCtx.SharedContext.SealedSecretsDir) err = cmd2.Run(sealer) if err != nil { @@ -87,13 +87,13 @@ func (cmd *sealCmd) runCmdSealForTarget(ctx context.Context, p *kluctl_project.L }) } -func (cmd *sealCmd) loadCert(ctx *commandCtx) (*x509.Certificate, error) { - sealingConfig := ctx.targetCtx.Target.SealingConfig +func (cmd *sealCmd) loadCert(cmdCtx *commandCtx) (*x509.Certificate, error) { + sealingConfig := cmdCtx.targetCtx.Target.SealingConfig var certFile string if sealingConfig != nil && sealingConfig.CertFile != nil { - path, err := securejoin.SecureJoin(ctx.targetCtx.KluctlProject.ProjectDir, *sealingConfig.CertFile) + path, err := securejoin.SecureJoin(cmdCtx.targetCtx.KluctlProject.ProjectDir, *sealingConfig.CertFile) if err != nil { return nil, err } @@ -102,7 +102,7 @@ func (cmd *sealCmd) loadCert(ctx *commandCtx) (*x509.Certificate, error) { if cmd.CertFile != "" { if certFile != "" { - status.Info(ctx.ctx, "Overriding certFile from target with certFile argument") + status.Info(cmdCtx.ctx, "Overriding certFile from target with certFile argument") } certFile = cmd.CertFile } @@ -118,11 +118,11 @@ func (cmd *sealCmd) loadCert(ctx *commandCtx) (*x509.Certificate, error) { } return cert, nil } else { - if ctx.targetCtx.SharedContext.K == nil { + if cmdCtx.targetCtx.SharedContext.K == nil { return nil, fmt.Errorf("must specify certFile when sealing in offline mode") } - secretsConfig := ctx.targetCtx.KluctlProject.Config.SecretsConfig + secretsConfig := cmdCtx.targetCtx.KluctlProject.Config.SecretsConfig var sealedSecretsConfig *types.GlobalSealedSecretsConfig if secretsConfig != nil { sealedSecretsConfig = secretsConfig.SealedSecrets @@ -140,13 +140,13 @@ func (cmd *sealCmd) loadCert(ctx *commandCtx) (*x509.Certificate, error) { } if sealedSecretsConfig == nil || sealedSecretsConfig.Bootstrap == nil || *sealedSecretsConfig.Bootstrap { - err := seal.BootstrapSealedSecrets(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace) + err := seal.BootstrapSealedSecrets(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, sealedSecretsNamespace) if err != nil { return nil, err } } - cert, err := seal.FetchCert(ctx.ctx, ctx.targetCtx.SharedContext.K, sealedSecretsNamespace, sealedSecretsControllerName) + cert, err := seal.FetchCert(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, sealedSecretsNamespace, sealedSecretsControllerName) if err != nil { return nil, err } @@ -154,8 +154,8 @@ func (cmd *sealCmd) loadCert(ctx *commandCtx) (*x509.Certificate, error) { } } -func (cmd *sealCmd) Run() error { - return withKluctlProjectFromArgs(cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { +func (cmd *sealCmd) Run(ctx context.Context) error { + return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { hadError := false baseTargets := make(map[string]bool) diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index 4558cd76f..101af905f 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" @@ -28,7 +29,7 @@ func (cmd *validateCmd) Help() string { TODO: This needs to be better documented!` } -func (cmd *validateCmd) Run() error { +func (cmd *validateCmd) Run(ctx context.Context) error { ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, @@ -37,17 +38,17 @@ func (cmd *validateCmd) Run() error { helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, } - return withProjectCommandContext(ptArgs, func(ctx *commandCtx) error { + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { startTime := time.Now() - cmd2 := commands.NewValidateCommand(ctx.ctx, ctx.targetCtx.DeploymentCollection) + cmd2 := commands.NewValidateCommand(cmdCtx.ctx, cmdCtx.targetCtx.DeploymentCollection) for true { - result, err := cmd2.Run(ctx.ctx, ctx.targetCtx.SharedContext.K) + result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) if err != nil { return err } failed := len(result.Errors) != 0 || (cmd.WarningsAsErrors && len(result.Warnings) != 0) - err = outputValidateResult(cmd.Output, result) + err = outputValidateResult(ctx, cmd.Output, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_version.go b/cmd/kluctl/commands/cmd_version.go index 98da1b398..b7683a784 100644 --- a/cmd/kluctl/commands/cmd_version.go +++ b/cmd/kluctl/commands/cmd_version.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/kluctl/kluctl/v2/pkg/version" "os" ) @@ -8,7 +9,7 @@ import ( type versionCmd struct { } -func (cmd *versionCmd) Run() error { +func (cmd *versionCmd) Run(ctx context.Context) error { _, err := os.Stdout.WriteString(version.GetVersion() + "\n") return err } diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index 5982f04bf..5ebf48f10 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -1,6 +1,7 @@ package commands import ( + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/term" @@ -20,7 +21,7 @@ type helpProvider interface { } type runProvider interface { - Run() error + Run(ctx context.Context) error } type rootCommand struct { @@ -68,7 +69,7 @@ func (c *rootCommand) buildCobraCmd(parent *commandAndGroups, cmdStruct interfac runP, ok := cmdStruct.(runProvider) if ok { cg.cmd.RunE = func(cmd *cobra.Command, args []string) error { - return runP.Run() + return runP.Run(cmd.Context()) } } diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index b5702f1c7..cdf7f0753 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -2,6 +2,7 @@ package commands import ( "bytes" + "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" @@ -196,24 +197,24 @@ func outputHelper(output []string, cb func(format string) (string, error)) error return nil } -func outputCommandResult(output []string, cr *types.CommandResult) error { - status.Flush(cliCtx) +func outputCommandResult(ctx context.Context, output []string, cr *types.CommandResult) error { + status.Flush(ctx) return outputHelper(output, func(format string) (string, error) { return formatCommandResult(cr, format) }) } -func outputValidateResult(output []string, vr *types.ValidateResult) error { - status.Flush(cliCtx) +func outputValidateResult(ctx context.Context, output []string, vr *types.ValidateResult) error { + status.Flush(ctx) return outputHelper(output, func(format string) (string, error) { return formatValidateResult(vr, format) }) } -func outputYamlResult(output []string, result interface{}, multiDoc bool) error { - status.Flush(cliCtx) +func outputYamlResult(ctx context.Context, output []string, result interface{}, multiDoc bool) error { + status.Flush(ctx) if len(output) == 0 { output = []string{"-"} diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 7830d90e4..32ffab84f 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -20,13 +20,15 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err inclusionFlags := v.FieldByName("InclusionFlags") imageFlags := v.FieldByName("ImageFlags") + ctx := context.Background() + if projectFlags.IsValid() && targetFlags.IsValid() { - _ = ccmd.RegisterFlagCompletionFunc("target", buildTargetCompletionFunc(projectFlags.Addr().Interface().(*args.ProjectFlags))) + _ = ccmd.RegisterFlagCompletionFunc("target", buildTargetCompletionFunc(ctx, projectFlags.Addr().Interface().(*args.ProjectFlags))) } if projectFlags.IsValid() && inclusionFlags.IsValid() { - tagsFunc := buildInclusionCompletionFunc(cmdStruct, false) - dirsFunc := buildInclusionCompletionFunc(cmdStruct, true) + tagsFunc := buildInclusionCompletionFunc(ctx, cmdStruct, false) + dirsFunc := buildInclusionCompletionFunc(ctx, cmdStruct, true) _ = ccmd.RegisterFlagCompletionFunc("include-tag", tagsFunc) _ = ccmd.RegisterFlagCompletionFunc("exclude-tag", tagsFunc) _ = ccmd.RegisterFlagCompletionFunc("include-deployment-dir", dirsFunc) @@ -34,31 +36,31 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err } if imageFlags.IsValid() { - _ = ccmd.RegisterFlagCompletionFunc("fixed-image", buildImagesCompletionFunc(cmdStruct)) + _ = ccmd.RegisterFlagCompletionFunc("fixed-image", buildImagesCompletionFunc(ctx, cmdStruct)) } return nil } -func withProjectForCompletion(projectArgs *args.ProjectFlags, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { +func withProjectForCompletion(ctx context.Context, projectArgs *args.ProjectFlags, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { // let's not update git caches too often projectArgs.GitCacheUpdateInterval = time.Second * 60 - return withKluctlProjectFromArgs(*projectArgs, false, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(ctx, *projectArgs, false, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return cb(ctx, p) }) } -func buildTargetCompletionFunc(projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func buildTargetCompletionFunc(ctx context.Context, projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var ret []string - err := withProjectForCompletion(projectArgs, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(ctx, projectArgs, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { for _, t := range p.DynamicTargets { ret = append(ret, t.Target.Name) } return nil }) if err != nil { - status.Error(cliCtx, err.Error()) + status.Error(ctx, err.Error()) return nil, cobra.ShellCompDirectiveError } return ret, cobra.ShellCompDirectiveDefault @@ -89,7 +91,7 @@ func buildAutocompleteProjectTargetCommandArgs(cmdStruct interface{}) projectTar return ptArgs } -func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func buildInclusionCompletionFunc(ctx context.Context, cmdStruct interface{}, forDirs bool) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { ptArgs := buildAutocompleteProjectTargetCommandArgs(cmdStruct) @@ -97,7 +99,7 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd var deploymentItemDirs utils.OrderedMap var mutex sync.Mutex - err := withProjectForCompletion(&ptArgs.projectFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(ctx, &ptArgs.projectFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { for _, t := range p.DynamicTargets { @@ -113,10 +115,10 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd ptArgs.targetFlags.Target = t wg.Add(1) go func() { - _ = withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { + _ = withProjectTargetCommandContext(ctx, ptArgs, p, func(cmdCtx *commandCtx) error { mutex.Lock() defer mutex.Unlock() - for _, di := range ctx.targetCtx.DeploymentCollection.Deployments { + for _, di := range cmdCtx.targetCtx.DeploymentCollection.Deployments { tags.Merge(di.Tags) deploymentItemDirs.Set(di.RelToSourceItemDir, true) } @@ -129,7 +131,7 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd return nil }) if err != nil { - status.Error(cliCtx, err.Error()) + status.Error(ctx, err.Error()) return nil, cobra.ShellCompDirectiveError } if forDirs { @@ -140,7 +142,7 @@ func buildInclusionCompletionFunc(cmdStruct interface{}, forDirs bool) func(cmd } } -func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func buildImagesCompletionFunc(ctx context.Context, cmdStruct interface{}) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { ptArgs := buildAutocompleteProjectTargetCommandArgs(cmdStruct) @@ -151,7 +153,7 @@ func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, a var images utils.OrderedMap var mutex sync.Mutex - err := withProjectForCompletion(&ptArgs.projectFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(ctx, &ptArgs.projectFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { for _, t := range p.DynamicTargets { @@ -167,15 +169,15 @@ func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, a ptArgs.targetFlags.Target = t wg.Add(1) go func() { - _ = withProjectTargetCommandContext(ctx, ptArgs, p, func(ctx *commandCtx) error { - err := ctx.targetCtx.DeploymentCollection.Prepare() + _ = withProjectTargetCommandContext(ctx, ptArgs, p, func(cmdCtx *commandCtx) error { + err := cmdCtx.targetCtx.DeploymentCollection.Prepare() if err != nil { - status.Error(cliCtx, err.Error()) + status.Error(ctx, err.Error()) } mutex.Lock() defer mutex.Unlock() - for _, si := range ctx.images.SeenImages(false) { + for _, si := range cmdCtx.images.SeenImages(false) { str := si.Image if si.Namespace != nil { str += ":" + *si.Namespace @@ -197,7 +199,7 @@ func buildImagesCompletionFunc(cmdStruct interface{}) func(cmd *cobra.Command, a return nil }) if err != nil { - status.Error(cliCtx, err.Error()) + status.Error(ctx, err.Error()) return nil, cobra.ShellCompDirectiveError } return images.ListKeys(), cobra.ShellCompDirectiveNoSpace diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index d864584f0..203e06405 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -197,7 +197,7 @@ func (c *cli) preRun() error { return nil } -func (c *cli) Run() error { +func (c *cli) Run(ctx context.Context) error { return nil } @@ -237,7 +237,11 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif if err != nil { return err } - return root.preRun() + err = root.preRun() + for c := cmd; c != nil; c = c.Parent() { + c.SetContext(cliCtx) + } + return err } initViper() diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 5715f93e4..f3184db23 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -24,7 +24,7 @@ import ( "strings" ) -func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { +func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { tmpDir, err := os.MkdirTemp(utils.GetTmpBaseDir(), "project-") if err != nil { return fmt.Errorf("creating temporary project directory failed: %w", err) @@ -44,10 +44,10 @@ func withKluctlProjectFromArgs(projectFlags args.ProjectFlags, strictTemplates b repoRoot, err := git.DetectGitRepositoryRoot(cwd) if err != nil { - status.Warning(cliCtx, "Failed to detect git project root. This might cause follow-up errors") + status.Warning(ctx, "Failed to detect git project root. This might cause follow-up errors") } - ctx, cancel := context.WithTimeout(cliCtx, projectFlags.Timeout) + ctx, cancel := context.WithTimeout(ctx, projectFlags.Timeout) defer cancel() sshPool := &ssh_pool.SshPool{} @@ -110,13 +110,13 @@ type commandCtx struct { images *deployment.Images } -func withProjectCommandContext(args projectTargetCommandArgs, cb func(ctx *commandCtx) error) error { - return withKluctlProjectFromArgs(args.projectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { +func withProjectCommandContext(ctx context.Context, args projectTargetCommandArgs, cb func(cmdCtx *commandCtx) error) error { + return withKluctlProjectFromArgs(ctx, args.projectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return withProjectTargetCommandContext(ctx, args, p, cb) }) } -func withProjectTargetCommandContext(ctx context.Context, args projectTargetCommandArgs, p *kluctl_project.LoadedKluctlProject, cb func(ctx *commandCtx) error) error { +func withProjectTargetCommandContext(ctx context.Context, args projectTargetCommandArgs, p *kluctl_project.LoadedKluctlProject, cb func(cmdCtx *commandCtx) error) error { rh := registries.NewRegistryHelper(ctx) err := rh.ParseAuthEntriesFromEnv() if err != nil { From 4834099b8eecc52384568c0fb66c67543252d064 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 10:50:38 +0100 Subject: [PATCH 0633/2268] chore: Reformat replace-commands-help/main.go --- internal/replace-commands-help/main.go | 282 ++++++++++++------------- 1 file changed, 141 insertions(+), 141 deletions(-) diff --git a/internal/replace-commands-help/main.go b/internal/replace-commands-help/main.go index 6d4e781fb..a29bc8764 100644 --- a/internal/replace-commands-help/main.go +++ b/internal/replace-commands-help/main.go @@ -1,92 +1,92 @@ package main import ( - "flag" - "fmt" - "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" - "io/fs" - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "reflect" - "regexp" - "strings" - "syscall" + "flag" + "fmt" + "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" + "io/fs" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "reflect" + "regexp" + "strings" + "syscall" ) var docsDir = flag.String("docs-dir", "", "Path to documentation") type section struct { - start int - end int - command string - section string - code bool + start int + end int + command string + section string + code bool } func main() { - _ = syscall.Setenv("COLUMNS", "120") - - if os.Getenv("CALL_KLUCTL") == "true" { - kluctlMain() - return - } - - flag.Parse() - - filepath.WalkDir(*docsDir, func(path string, d fs.DirEntry, err error) error { - if !strings.HasSuffix(path, ".md") { - return nil - } - processFile(path) - return nil - }) + _ = syscall.Setenv("COLUMNS", "120") + + if os.Getenv("CALL_KLUCTL") == "true" { + kluctlMain() + return + } + + flag.Parse() + + filepath.WalkDir(*docsDir, func(path string, d fs.DirEntry, err error) error { + if !strings.HasSuffix(path, ".md") { + return nil + } + processFile(path) + return nil + }) } func kluctlMain() { - commands.Execute() + commands.Execute() } func processFile(path string) { - text, err := ioutil.ReadFile(path) - if err != nil { - log.Fatal(err) - } - lines := strings.Split(string(text), "\n") - - var newLines []string - pos := 0 - for true { - s := findNextSection(lines, pos) - if s == nil { - newLines = append(newLines, lines[pos:]...) - break - } - - newLines = append(newLines, lines[pos:s.start+1]...) - - s2 := getHelpSection(s.command, s.section) - - if s.code { - newLines = append(newLines, "```") - } - newLines = append(newLines, s2...) - if s.code { - newLines = append(newLines, "```") - } - - newLines = append(newLines, lines[s.end]) - pos = s.end + 1 - } - - if !reflect.DeepEqual(lines, newLines) { - err = ioutil.WriteFile(path, []byte(strings.Join(newLines, "\n")), 0o600) - if err != nil { - log.Fatal(err) - } - } + text, err := ioutil.ReadFile(path) + if err != nil { + log.Fatal(err) + } + lines := strings.Split(string(text), "\n") + + var newLines []string + pos := 0 + for true { + s := findNextSection(lines, pos) + if s == nil { + newLines = append(newLines, lines[pos:]...) + break + } + + newLines = append(newLines, lines[pos:s.start+1]...) + + s2 := getHelpSection(s.command, s.section) + + if s.code { + newLines = append(newLines, "```") + } + newLines = append(newLines, s2...) + if s.code { + newLines = append(newLines, "```") + } + + newLines = append(newLines, lines[s.end]) + pos = s.end + 1 + } + + if !reflect.DeepEqual(lines, newLines) { + err = ioutil.WriteFile(path, []byte(strings.Join(newLines, "\n")), 0o600) + if err != nil { + log.Fatal(err) + } + } } var beginPattern = regexp.MustCompile(``) @@ -94,79 +94,79 @@ var endPattern = regexp.MustCompile(``) func findNextSection(lines []string, start int) *section { - for i := start; i < len(lines); i++ { - m := beginPattern.FindSubmatch([]byte(lines[i])) - if m == nil { - continue - } - - var s section - s.start = i - s.command = string(m[1]) - s.section = string(m[2]) - s.code = string(m[3]) == "true" - - for j := i + 1; j < len(lines); j++ { - m = endPattern.FindSubmatch([]byte(lines[j])) - if m == nil { - continue - } - s.end = j - return &s - } - } - return nil + for i := start; i < len(lines); i++ { + m := beginPattern.FindSubmatch([]byte(lines[i])) + if m == nil { + continue + } + + var s section + s.start = i + s.command = string(m[1]) + s.section = string(m[2]) + s.code = string(m[3]) == "true" + + for j := i + 1; j < len(lines); j++ { + m = endPattern.FindSubmatch([]byte(lines[j])) + if m == nil { + continue + } + s.end = j + return &s + } + } + return nil } func countIndent(str string) int { - for i := 0; i < len(str); i++ { - if str[i] != ' ' { - return i - } - } - return 0 + for i := 0; i < len(str); i++ { + if str[i] != ' ' { + return i + } + } + return 0 } func getHelpSection(command string, section string) []string { - log.Printf("Getting section '%s' from command '%s'", section, command) - - exe, err := os.Executable() - if err != nil { - log.Fatal(err) - } - - helpCmd := exec.Command(exe, command, "--help") - helpCmd.Env = os.Environ() - helpCmd.Env = append(helpCmd.Env, "CALL_KLUCTL=true") - - out, err := helpCmd.CombinedOutput() - if err != nil { - log.Fatal(err) - } - - lines := strings.Split(string(out), "\n") - - sectionStart := -1 - for i := 0; i < len(lines); i++ { - indent := countIndent(lines[i]) - if strings.HasPrefix(lines[i][indent:], fmt.Sprintf("%s:", section)) { - sectionStart = i - break - } - } - if sectionStart == -1 { - log.Fatalf("Section %s not found in command %s", section, command) - } - - var ret []string - ret = append(ret, lines[sectionStart]) - for i := sectionStart + 1; i < len(lines); i++ { - indent := countIndent(lines[i]) - if len(lines[i]) != 0 && indent == 0 && lines[i][len(lines[i])-1] == ':' { - // new section has started - break - } - ret = append(ret, lines[i]) - } - return ret + log.Printf("Getting section '%s' from command '%s'", section, command) + + exe, err := os.Executable() + if err != nil { + log.Fatal(err) + } + + helpCmd := exec.Command(exe, command, "--help") + helpCmd.Env = os.Environ() + helpCmd.Env = append(helpCmd.Env, "CALL_KLUCTL=true") + + out, err := helpCmd.CombinedOutput() + if err != nil { + log.Fatal(err) + } + + lines := strings.Split(string(out), "\n") + + sectionStart := -1 + for i := 0; i < len(lines); i++ { + indent := countIndent(lines[i]) + if strings.HasPrefix(lines[i][indent:], fmt.Sprintf("%s:", section)) { + sectionStart = i + break + } + } + if sectionStart == -1 { + log.Fatalf("Section %s not found in command %s", section, command) + } + + var ret []string + ret = append(ret, lines[sectionStart]) + for i := sectionStart + 1; i < len(lines); i++ { + indent := countIndent(lines[i]) + if len(lines[i]) != 0 && indent == 0 && lines[i][len(lines[i])-1] == ':' { + // new section has started + break + } + ret = append(ret, lines[i]) + } + return ret } From 77d5c6ee5d8d13ed008de5e73c2345e97fe14ec8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 10:51:55 +0100 Subject: [PATCH 0634/2268] refactor: Split Execute() into into Main() and Execute() --- cmd/kluctl/commands/root.go | 152 +++++++++++++++---------- e2e/call_kluctl_hack.go | 2 +- internal/replace-commands-help/main.go | 2 +- main.go | 2 +- 4 files changed, 94 insertions(+), 64 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 203e06405..0af94bdfa 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -42,12 +42,16 @@ import ( const latestReleaseUrl = "https://api.github.com/repos/kluctl/kluctl/releases/latest" -type cli struct { +type GlobalFlags struct { Debug bool `group:"global" help:"Enable debug logging"` NoUpdateCheck bool `group:"global" help:"Disable update check on startup"` NoColor bool `group:"global" help:"Disable colored output"` CpuProfile string `group:"global" help:"Enable CPU profiling and write the result to the given path"` +} + +type cli struct { + GlobalFlags CheckImageUpdates checkImageUpdatesCmd `cmd:"" help:"Render deployment and check if any images have new tags available"` Delete deleteCmd `cmd:"" help:"Delete a target (or parts of it) from the corresponding cluster"` @@ -76,30 +80,35 @@ var flagGroups = []groupInfo{ {group: "flux", title: "Flux arguments:", description: "EXPERIMENTAL: Subcommands for interaction with flux-kluctl-controller"}, } -var cliCtx = context.Background() -var didSetupStatusHandler bool - -func setupStatusHandler(debug bool, noColor bool) { - didSetupStatusHandler = true - - origStderr := os.Stderr +var origStderr = os.Stderr +func initStatusHandler(ctx context.Context, debug bool, noColor bool) context.Context { // we must determine isTerminal before we override os.Stderr - isTerminal := isatty.IsTerminal(os.Stderr.Fd()) + isTerminal := isatty.IsTerminal(origStderr.Fd()) var sh status.StatusHandler - if !debug && isatty.IsTerminal(os.Stderr.Fd()) { - sh = status.NewMultiLineStatusHandler(cliCtx, os.Stderr, isTerminal, !noColor, false) + if !debug && isatty.IsTerminal(origStderr.Fd()) { + sh = status.NewMultiLineStatusHandler(ctx, origStderr, isTerminal, !noColor, false) } else { sh = status.NewSimpleStatusHandler(func(message string) { _, _ = fmt.Fprintf(origStderr, "%s\n", message) }, isTerminal, false) } sh.SetTrace(debug) - cliCtx = status.NewContext(cliCtx, sh) + ctx = status.NewContext(ctx, sh) + return ctx +} + +func redirectLogsAndStderr(ctxGetter func() context.Context) { + f := func(line string) { + status.Info(ctxGetter(), line) + } + + lr1 := status.NewLineRedirector(f) + lr2 := status.NewLineRedirector(f) klog.LogToStderr(false) - klog.SetOutput(status.NewLineRedirector(sh.Info)) - log.SetOutput(status.NewLineRedirector(sh.Info)) + klog.SetOutput(lr1) + log.SetOutput(lr2) pr, pw, err := os.Pipe() if err != nil { @@ -107,7 +116,7 @@ func setupStatusHandler(debug bool, noColor bool) { } go func() { - x := status.NewLineRedirector(sh.Info) + x := status.NewLineRedirector(f) _, _ = io.Copy(x, pr) }() @@ -135,10 +144,7 @@ type VersionCheckState struct { LastVersionCheck time.Time `yaml:"lastVersionCheck"` } -func (c *cli) checkNewVersion() { - if c.NoUpdateCheck { - return - } +func checkNewVersion(ctx context.Context) { if version.GetVersion() == "0.0.0" { return } @@ -155,7 +161,7 @@ func (c *cli) checkNewVersion() { versionCheckState.LastVersionCheck = time.Now() _ = yaml.WriteYamlFile(versionCheckPath, &versionCheckState) - s := status.Start(cliCtx, "Checking for new kluctl version") + s := status.Start(ctx, "Checking for new kluctl version") defer s.Failed() r, err := http.Get(latestReleaseUrl) @@ -187,36 +193,70 @@ func (c *cli) checkNewVersion() { s.Success() } -func (c *cli) preRun() error { - err := setupProfiling(c.CpuProfile) - if err != nil { - return err - } - setupStatusHandler(c.Debug, c.NoColor) - c.checkNewVersion() - return nil -} - func (c *cli) Run(ctx context.Context) error { return nil } -func initViper() { +func initViper(ctx context.Context) { viper.SetConfigName("config") viper.SetConfigType("yaml") viper.AddConfigPath("/etc/kluctl/") viper.AddConfigPath("$HOME/.kluctl") if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - status.Error(cliCtx, err.Error()) + status.Error(ctx, err.Error()) os.Exit(1) } } } -func Execute() { +func Main() { colorable.EnableColorsStdout(nil) + ctx := context.Background() + + ctx = initStatusHandler(ctx, false, true) + redirectLogsAndStderr(func() context.Context { + // ctx might be replaced later in preRun() of Execute() + return ctx + }) + + initViper(ctx) + err := Execute(ctx, os.Args[1:], func(ctxIn context.Context, cmd *cobra.Command, flags GlobalFlags) (context.Context, error) { + err := copyViperValuesToCobraCmd(cmd) + if err != nil { + return ctx, err + } + err = setupProfiling(flags.CpuProfile) + if err != nil { + return ctx, err + } + oldSh := status.FromContext(ctxIn) + if oldSh != nil { + oldSh.Stop() + } + ctx = initStatusHandler(ctxIn, flags.Debug, flags.NoColor) + if !flags.NoUpdateCheck { + checkNewVersion(ctx) + } + return ctx, nil + }) + + if cpuProfileFile != nil { + pprof.StopCPUProfile() + _ = cpuProfileFile.Close() + cpuProfileFile = nil + } + + sh := status.FromContext(ctx) + sh.Stop() + + if err != nil { + os.Exit(1) + } +} + +func Execute(ctx context.Context, args []string, preRun func(ctx context.Context, rootCmd *cobra.Command, flags GlobalFlags) (context.Context, error)) error { root := cli{} rootCmd, err := buildRootCobraCmd(&root, "kluctl", "Deploy and manage complex deployments on Kubernetes", @@ -225,44 +265,34 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif flagGroups) if err != nil { - panic(err) + return err } + rootCmd.SetContext(ctx) + rootCmd.SetArgs(args) rootCmd.Version = version.GetVersion() rootCmd.SilenceUsage = true rootCmd.SilenceErrors = true rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { - err := copyViperValuesToCobraCmd(cmd) - if err != nil { - return err - } - err = root.preRun() - for c := cmd; c != nil; c = c.Parent() { - c.SetContext(cliCtx) + if preRun != nil { + ctx, err = preRun(ctx, cmd, root.GlobalFlags) + if ctx != nil { + for c := cmd; c != nil; c = c.Parent() { + c.SetContext(ctx) + } + } + if err != nil { + return err + } } - return err - } - - initViper() - - err = rootCmd.ExecuteContext(cliCtx) - if !didSetupStatusHandler { - setupStatusHandler(false, true) + return nil } - if cpuProfileFile != nil { - pprof.StopCPUProfile() - _ = cpuProfileFile.Close() - cpuProfileFile = nil - } - - sh := status.FromContext(cliCtx) - + err = rootCmd.Execute() if err != nil { - status.Error(cliCtx, "%s", err.Error()) - sh.Stop() - os.Exit(1) + status.Error(ctx, err.Error()) + return err } - sh.Stop() + return nil } diff --git a/e2e/call_kluctl_hack.go b/e2e/call_kluctl_hack.go index 6da895464..fa5268d30 100644 --- a/e2e/call_kluctl_hack.go +++ b/e2e/call_kluctl_hack.go @@ -13,7 +13,7 @@ func init() { // We use the Golang's initializing mechanism to run kluctl even though the test executable was invoked // This is clearly a hack, but it avoids the requirement to have a kluctl executable pre-built if isCallKluctlHack() { - commands.Execute() + commands.Main() os.Exit(0) } } diff --git a/internal/replace-commands-help/main.go b/internal/replace-commands-help/main.go index a29bc8764..2f47f1c08 100644 --- a/internal/replace-commands-help/main.go +++ b/internal/replace-commands-help/main.go @@ -46,7 +46,7 @@ func main() { } func kluctlMain() { - commands.Execute() + commands.Main() } func processFile(path string) { diff --git a/main.go b/main.go index f6ca27150..d7b055670 100644 --- a/main.go +++ b/main.go @@ -24,5 +24,5 @@ var version = "0.0.0" func main() { version2.SetVersion(version) - commands.Execute() + commands.Main() } From 779a97a1df9877cf7e8fe0090d9d40d1150bb674 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 11:26:54 +0100 Subject: [PATCH 0635/2268] refactor: Allow to override tmp base dir via context --- cmd/kluctl/commands/cmd_flux_reconcile.go | 2 +- cmd/kluctl/commands/cmd_flux_resume.go | 2 +- cmd/kluctl/commands/cmd_flux_suspend.go | 2 +- cmd/kluctl/commands/cmd_render.go | 2 +- cmd/kluctl/commands/root.go | 2 +- cmd/kluctl/commands/utils.go | 2 +- pkg/deployment/helm_chart.go | 10 +-- pkg/k8s/client_factory.go | 11 ++- pkg/kluctl_project/target_context.go | 2 +- pkg/registries/registries.go | 2 +- pkg/repocache/cache.go | 4 +- pkg/sops/utils.go | 5 +- pkg/utils/tmpdir.go | 98 +++++++++++++++++++++++ pkg/utils/utils.go | 74 ----------------- 14 files changed, 123 insertions(+), 95 deletions(-) create mode 100644 pkg/utils/tmpdir.go diff --git a/cmd/kluctl/commands/cmd_flux_reconcile.go b/cmd/kluctl/commands/cmd_flux_reconcile.go index 8e4668024..91e345112 100644 --- a/cmd/kluctl/commands/cmd_flux_reconcile.go +++ b/cmd/kluctl/commands/cmd_flux_reconcile.go @@ -27,7 +27,7 @@ func (cmd *fluxReconcileCmd) Run(ctx context.Context) error { noWait := cmd.KluctlDeploymentFlags.NoWait timestamp := time.Now().Format(time.RFC3339) - cf, err := k8s.NewClientFactoryFromDefaultConfig(nil) + cf, err := k8s.NewClientFactoryFromDefaultConfig(ctx, nil) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_flux_resume.go b/cmd/kluctl/commands/cmd_flux_resume.go index 3b90f1219..d2fbc7d69 100644 --- a/cmd/kluctl/commands/cmd_flux_resume.go +++ b/cmd/kluctl/commands/cmd_flux_resume.go @@ -18,7 +18,7 @@ func (cmd *fluxResumeCmd) Run(ctx context.Context) error { ns := cmd.KluctlDeploymentFlags.Namespace kd := cmd.KluctlDeploymentFlags.KluctlDeployment - cf, err := k8s.NewClientFactoryFromDefaultConfig(nil) + cf, err := k8s.NewClientFactoryFromDefaultConfig(ctx, nil) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_flux_suspend.go b/cmd/kluctl/commands/cmd_flux_suspend.go index d06212dbe..dc90d9c2b 100644 --- a/cmd/kluctl/commands/cmd_flux_suspend.go +++ b/cmd/kluctl/commands/cmd_flux_suspend.go @@ -17,7 +17,7 @@ func (cmd *fluxSuspendCmd) Run(ctx context.Context) error { ns := cmd.KluctlDeploymentFlags.Namespace kd := cmd.KluctlDeploymentFlags.KluctlDeployment - cf, err := k8s.NewClientFactoryFromDefaultConfig(nil) + cf, err := k8s.NewClientFactoryFromDefaultConfig(ctx, nil) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 16c0bc2c1..27851b332 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -30,7 +30,7 @@ a temporary directory or a specified directory.` func (cmd *renderCmd) Run(ctx context.Context) error { isTmp := false if cmd.RenderOutputDir == "" { - p, err := ioutil.TempDir(utils.GetTmpBaseDir(), "rendered-") + p, err := ioutil.TempDir(utils.GetTmpBaseDir(ctx), "rendered-") if err != nil { return err } diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 0af94bdfa..2c6d696f1 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -149,7 +149,7 @@ func checkNewVersion(ctx context.Context) { return } - versionCheckPath := filepath.Join(utils.GetTmpBaseDir(), "version_check.yaml") + versionCheckPath := filepath.Join(utils.GetTmpBaseDir(ctx), "version_check.yaml") var versionCheckState VersionCheckState err := yaml.ReadYamlFile(versionCheckPath, &versionCheckState) if err == nil { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index f3184db23..bfe63cb17 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -25,7 +25,7 @@ import ( ) func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { - tmpDir, err := os.MkdirTemp(utils.GetTmpBaseDir(), "project-") + tmpDir, err := os.MkdirTemp(utils.GetTmpBaseDir(ctx), "project-") if err != nil { return fmt.Errorf("creating temporary project directory failed: %w", err) } diff --git a/pkg/deployment/helm_chart.go b/pkg/deployment/helm_chart.go index d21426da2..cf1cb825c 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/deployment/helm_chart.go @@ -164,7 +164,7 @@ func (c *HelmChart) pullTmpChart(ctx context.Context) (string, error) { _, _ = fmt.Fprintf(hash, "%s\n", c.chartName) _, _ = fmt.Fprintf(hash, "%s\n", *c.Config.ChartVersion) h := hex.EncodeToString(hash.Sum(nil)) - tmpDir := filepath.Join(utils.GetTmpBaseDir(), "helm-charts") + tmpDir := filepath.Join(utils.GetTmpBaseDir(ctx), "helm-charts") _ = os.MkdirAll(tmpDir, 0o700) tmpDir = filepath.Join(tmpDir, fmt.Sprintf("%s-%s", c.chartName, h)) @@ -267,7 +267,7 @@ func (c *HelmChart) CheckUpdate(ctx context.Context) (string, bool, error) { if c.Config.Repo != nil && registry.IsOCI(*c.Config.Repo) { return c.checkUpdateOciRepo(ctx) } - return c.checkUpdateHelmRepo() + return c.checkUpdateHelmRepo(ctx) } func (c *HelmChart) checkUpdateOciRepo(ctx context.Context) (string, bool, error) { @@ -288,7 +288,7 @@ func (c *HelmChart) checkUpdateOciRepo(ctx context.Context) (string, bool, error return latestVersion, updated, nil } -func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { +func (c *HelmChart) checkUpdateHelmRepo(ctx context.Context) (string, bool, error) { settings := cli.New() var e *repo.Entry @@ -311,7 +311,7 @@ func (c *HelmChart) checkUpdateHelmRepo() (string, bool, error) { return "", false, err } - r.CachePath, err = os.MkdirTemp(utils.GetTmpBaseDir(), "helm-check-update-") + r.CachePath, err = os.MkdirTemp(utils.GetTmpBaseDir(ctx), "helm-check-update-") if err != nil { return "", false, err } @@ -442,7 +442,7 @@ func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion valueOpts := values.Options{} if utils.Exists(valuesPath) { - tmpValues, err := sops.MaybeDecryptFileToTmp(sopsDecrypter, valuesPath) + tmpValues, err := sops.MaybeDecryptFileToTmp(ctx, sopsDecrypter, valuesPath) if err != nil { return err } diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go index 1c9e451df..f5e1f4b7a 100644 --- a/pkg/k8s/client_factory.go +++ b/pkg/k8s/client_factory.go @@ -1,6 +1,7 @@ package k8s import ( + "context" "github.com/kluctl/kluctl/v2/pkg/utils" "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/disk" @@ -26,6 +27,7 @@ type ClientFactory interface { } type realClientFactory struct { + ctx context.Context config *rest.Config httpClient *http.Client } @@ -43,7 +45,7 @@ func (r *realClientFactory) DiscoveryClient() (discovery.DiscoveryInterface, err if err != nil { return nil, err } - discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(), "kube-cache/discovery", apiHost.Hostname()) + discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(r.ctx), "kube-cache/discovery", apiHost.Hostname()) discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(r.config), discoveryCacheDir, "", time.Hour*24) if err != nil { return nil, err @@ -67,7 +69,7 @@ func (r *realClientFactory) CloseIdleConnections() { r.httpClient.CloseIdleConnections() } -func NewClientFactory(configIn *rest.Config) (ClientFactory, error) { +func NewClientFactory(ctx context.Context, configIn *rest.Config) (ClientFactory, error) { restConfig := rest.CopyConfig(configIn) restConfig.QPS = 10 restConfig.Burst = 20 @@ -78,12 +80,13 @@ func NewClientFactory(configIn *rest.Config) (ClientFactory, error) { } return &realClientFactory{ + ctx: ctx, config: restConfig, httpClient: httpClient, }, nil } -func NewClientFactoryFromDefaultConfig(context *string) (ClientFactory, error) { +func NewClientFactoryFromDefaultConfig(ctx context.Context, context *string) (ClientFactory, error) { configOverrides := &clientcmd.ConfigOverrides{} if context != nil { configOverrides.CurrentContext = *context @@ -96,5 +99,5 @@ func NewClientFactoryFromDefaultConfig(context *string) (ClientFactory, error) { return nil, err } - return NewClientFactory(config) + return NewClientFactory(ctx, config) } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 980585382..bacf2d7d5 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -80,7 +80,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe var k *k8s.K8sCluster if clientConfig != nil { s := status.Start(ctx, fmt.Sprintf("Initializing k8s client")) - clientFactory, err := k8s.NewClientFactory(clientConfig) + clientFactory, err := k8s.NewClientFactory(ctx, clientConfig) if err != nil { return nil, err } diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 81b1156e8..ffda2e3f8 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -284,7 +284,7 @@ func (rh *RegistryHelper) realmFromRequest(req *http.Request) string { } func (rh *RegistryHelper) getCachePath(key string) string { - return filepath.Join(utils.GetTmpBaseDir(), "registries-cache", key[0:2], key[2:4], key) + return filepath.Join(utils.GetTmpBaseDir(rh.ctx), "registries-cache", key[0:2], key[2:4], key) } func (rh *RegistryHelper) checkInvalidToken(resBody []byte) bool { diff --git a/pkg/repocache/cache.go b/pkg/repocache/cache.go index 88d20f53a..9d9e70b32 100644 --- a/pkg/repocache/cache.go +++ b/pkg/repocache/cache.go @@ -87,7 +87,7 @@ func (rp *GitRepoCache) GetEntry(url git_url.GitUrl) (*CacheEntry, error) { e, ok := rp.repos[url.NormalizedRepoKey()] if !ok { - mr, err := git.NewMirroredGitRepo(rp.ctx, url, filepath.Join(utils.GetTmpBaseDir(), "git-cache"), rp.sshPool, rp.authProviders) + mr, err := git.NewMirroredGitRepo(rp.ctx, url, filepath.Join(utils.GetTmpBaseDir(rp.ctx), "git-cache"), rp.sshPool, rp.authProviders) if err != nil { return nil, err } @@ -198,7 +198,7 @@ func (e *CacheEntry) GetClonedDir(ref string) (string, git.CheckoutInfo, error) return "", git.CheckoutInfo{}, err } - tmpDir := filepath.Join(utils.GetTmpBaseDir(), "git-cloned") + tmpDir := filepath.Join(utils.GetTmpBaseDir(e.rp.ctx), "git-cloned") err = os.MkdirAll(tmpDir, 0700) if err != nil { return "", git.CheckoutInfo{}, err diff --git a/pkg/sops/utils.go b/pkg/sops/utils.go index 7a1250d5e..5b05e20ca 100644 --- a/pkg/sops/utils.go +++ b/pkg/sops/utils.go @@ -2,6 +2,7 @@ package sops import ( "bytes" + "context" "errors" "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -60,8 +61,8 @@ func MaybeDecryptFileTo(decrypter SopsDecrypter, path string, to string) error { return nil } -func MaybeDecryptFileToTmp(decrypter SopsDecrypter, path string) (string, error) { - tmp, err := os.CreateTemp(utils.GetTmpBaseDir(), "sops-decrypt-") +func MaybeDecryptFileToTmp(ctx context.Context, decrypter SopsDecrypter, path string) (string, error) { + tmp, err := os.CreateTemp(utils.GetTmpBaseDir(ctx), "sops-decrypt-") if err != nil { return "", err } diff --git a/pkg/utils/tmpdir.go b/pkg/utils/tmpdir.go new file mode 100644 index 000000000..76d684f8a --- /dev/null +++ b/pkg/utils/tmpdir.go @@ -0,0 +1,98 @@ +package utils + +import ( + "context" + "fmt" + "io/fs" + "os" + "os/user" + "path/filepath" + "runtime" + "sync" +) + +type tmpBaseDirKey int +type tmpBaseDirValue struct { + dir string + initOnce sync.Once +} + +var tmpBaseDirKeyInst tmpBaseDirKey +var tmpBaseDirValueDefault = &tmpBaseDirValue{ + dir: getDefaultTmpBaseDir(), +} + +func WithTmpBaseDir(ctx context.Context, tmpBaseDir string) context.Context { + return context.WithValue(ctx, tmpBaseDirKeyInst, &tmpBaseDirValue{ + dir: tmpBaseDir, + }) +} + +func GetTmpBaseDir(ctx context.Context) string { + v := ctx.Value(tmpBaseDirKeyInst) + if v == nil { + v = tmpBaseDirValueDefault + } + v2 := v.(*tmpBaseDirValue) + v2.initOnce.Do(func() { + v2.dir = createTmpBaseDir(v2.dir) + }) + return v2.dir +} + +func getDefaultTmpBaseDir() string { + dir := os.Getenv("KLUCTL_BASE_TMP_DIR") + if dir != "" { + return dir + } + + return filepath.Join(os.TempDir(), "kluctl-workdir") +} + +func createTmpBaseDir(dir string) string { + ensureDir(dir, 0o777, true) + + // every user gets its own tmp dir + if runtime.GOOS == "windows" { + u, err := user.Current() + if err != nil { + panic(err) + } + dir = filepath.Join(dir, u.Uid) + } else { + uid := os.Getuid() + dir = filepath.Join(dir, fmt.Sprintf("%d", uid)) + } + + ensureDir(dir, 0o700, true) + return dir +} + +func ensureDir(path string, perm fs.FileMode, allowCreate bool) { + if Exists(path) { + st, err := os.Lstat(path) + if err != nil { + panic(err) + } + if !st.IsDir() { + panic(fmt.Sprintf("%s is not a directory", path)) + } + if st.Mode().Perm() != perm { + err = os.Chmod(path, perm) + if err != nil { + panic(err) + } + } + } else if !allowCreate { + panic(fmt.Sprintf("failed to ensure directory %s", path)) + } else { + err := os.Mkdir(path, perm) + if err != nil { + if os.IsExist(err) { + ensureDir(path, perm, false) + } else { + panic(err) + } + } + } +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 8796755a9..97b9d781d 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -3,84 +3,10 @@ package utils import ( "crypto/sha256" "encoding/hex" - "fmt" "github.com/jinzhu/copier" - "io/fs" - "os" - "os/user" - "path/filepath" - "runtime" "strconv" - "sync" ) -var createTmpBaseDirOnce sync.Once -var tmpBaseDir string - -func GetTmpBaseDir() string { - createTmpBaseDirOnce.Do(func() { - createTmpBaseDir() - }) - return tmpBaseDir -} - -func createTmpBaseDir() { - envTmpDir := os.Getenv("KLUCTL_BASE_TMP_DIR") - if envTmpDir != "" { - tmpBaseDir = envTmpDir - return - } - - dir := filepath.Join(os.TempDir(), "kluctl-workdir") - - ensureDir(dir, 0o777, true) - - // every user gets its own tmp dir - if runtime.GOOS == "windows" { - u, err := user.Current() - if err != nil { - panic(err) - } - dir = filepath.Join(dir, u.Uid) - } else { - uid := os.Getuid() - dir = filepath.Join(dir, fmt.Sprintf("%d", uid)) - } - - ensureDir(dir, 0o700, true) - - tmpBaseDir = dir -} - -func ensureDir(path string, perm fs.FileMode, allowCreate bool) { - if Exists(path) { - st, err := os.Lstat(path) - if err != nil { - panic(err) - } - if !st.IsDir() { - panic(fmt.Sprintf("%s is not a directory", path)) - } - if st.Mode().Perm() != perm { - err = os.Chmod(path, perm) - if err != nil { - panic(err) - } - } - } else if !allowCreate { - panic(fmt.Sprintf("failed to ensure directory %s", path)) - } else { - err := os.Mkdir(path, perm) - if err != nil { - if os.IsExist(err) { - ensureDir(path, perm, false) - } else { - panic(err) - } - } - } -} - func Sha256String(data string) string { return Sha256Bytes([]byte(data)) } From 8849b1f19d5f5ce394db03280eca87f5d5b9b45d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 11:55:53 +0100 Subject: [PATCH 0636/2268] tests: Use global KUBECONFIG instead of per-project mergedKubeconfig --- e2e/args_test.go | 6 ++-- e2e/contexts_test.go | 10 ++---- e2e/default_clusters.go | 63 ++++++++++++++++++++++++++++++++++++ e2e/deployment_items_test.go | 4 +-- e2e/flux_test.go | 2 +- e2e/helm_test.go | 14 ++++---- e2e/hooks_test.go | 6 ++-- e2e/inclusion_test.go | 2 +- e2e/no_target_test.go | 3 +- e2e/seal_test.go | 3 +- e2e/sops_test.go | 6 ++-- e2e/test-utils/project.go | 48 +-------------------------- 12 files changed, 89 insertions(+), 78 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index 2cfebb50e..b52578096 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -13,7 +13,7 @@ func testArgs(t *testing.T, deprecated bool) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -136,7 +136,7 @@ func TestArgsFromEnv(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -169,7 +169,7 @@ func TestArgsFromEnvAndCli(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) diff --git a/e2e/contexts_test.go b/e2e/contexts_test.go index ef7a203b0..e5c6a73a9 100644 --- a/e2e/contexts_test.go +++ b/e2e/contexts_test.go @@ -3,13 +3,11 @@ package e2e import ( "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "k8s.io/client-go/tools/clientcmd/api" "testing" ) func prepareContextTest(t *testing.T) *test_utils.TestProject { - p := test_utils.NewTestProject(t, defaultCluster1) - p.MergeKubeconfig(defaultCluster2) + p := test_utils.NewTestProject(t) createNamespace(t, defaultCluster1, p.TestSlug()) createNamespace(t, defaultCluster2, p.TestSlug()) @@ -23,8 +21,6 @@ func prepareContextTest(t *testing.T) *test_utils.TestProject { } func TestContextCurrent(t *testing.T) { - t.Parallel() - p := prepareContextTest(t) p.UpdateTarget("test1", func(target *uo.UnstructuredObject) { @@ -35,9 +31,7 @@ func TestContextCurrent(t *testing.T) { assertConfigMapExists(t, defaultCluster1, p.TestSlug(), "cm") assertConfigMapNotExists(t, defaultCluster2, p.TestSlug(), "cm") - p.UpdateMergedKubeconfig(func(config *api.Config) { - config.CurrentContext = defaultCluster2.Context - }) + setMergedKubeconfigContext(t, defaultCluster2.Context) p.KluctlMust("deploy", "--yes", "-t", "test1") assertConfigMapExists(t, defaultCluster2, p.TestSlug(), "cm") diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 3baff99ef..0d5479d27 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -1,14 +1,21 @@ package e2e +import "C" import ( + "github.com/imdario/mergo" "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/e2e/test_resources" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/tools/clientcmd" + "os" + "runtime" "sync" + "testing" ) var defaultCluster1 = test_utils.CreateEnvTestCluster("cluster1") var defaultCluster2 = test_utils.CreateEnvTestCluster("cluster2") +var mergedKubeconfig string func init() { if isCallKluctlHack() { @@ -37,4 +44,60 @@ func init() { test_resources.ApplyYaml("sealed-secrets.yaml", defaultCluster2) }() wg.Wait() + + tmpKubeconfig, err := os.CreateTemp("", "") + if err != nil { + panic(err) + } + _ = tmpKubeconfig.Close() + runtime.SetFinalizer(defaultCluster1, func(_ *test_utils.EnvTestCluster) { + _ = os.Remove(tmpKubeconfig.Name()) + }) + + mergeKubeconfig(tmpKubeconfig.Name(), defaultCluster1.Kubeconfig) + mergeKubeconfig(tmpKubeconfig.Name(), defaultCluster2.Kubeconfig) + + mergedKubeconfig = tmpKubeconfig.Name() + _ = os.Setenv("KUBECONFIG", mergedKubeconfig) +} + +func mergeKubeconfig(path string, kubeconfig []byte) { + mkcfg, err := clientcmd.LoadFromFile(path) + if err != nil { + panic(err) + } + + nkcfg, err := clientcmd.Load(kubeconfig) + if err != nil { + panic(err) + } + + err = mergo.Merge(mkcfg, nkcfg) + if err != nil { + panic(err) + } + + err = clientcmd.WriteToFile(*mkcfg, path) + if err != nil { + panic(err) + } +} + +func setMergedKubeconfigContext(t *testing.T, newContext string) { + tmpKubeconfig, err := os.CreateTemp(t.TempDir(), "kubeconfig-") + if err != nil { + t.Fatal(err) + } + _ = tmpKubeconfig.Close() + + kcfg, err := clientcmd.LoadFromFile(mergedKubeconfig) + if err != nil { + t.Fatal(err) + } + kcfg.CurrentContext = newContext + err = clientcmd.WriteToFile(*kcfg, tmpKubeconfig.Name()) + if err != nil { + t.Fatal(err) + } + t.Setenv("KUBECONFIG", tmpKubeconfig.Name()) } diff --git a/e2e/deployment_items_test.go b/e2e/deployment_items_test.go index 2a346b8fe..71e93a34a 100644 --- a/e2e/deployment_items_test.go +++ b/e2e/deployment_items_test.go @@ -11,7 +11,7 @@ func TestKustomize(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -39,7 +39,7 @@ func TestGeneratedKustomize(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) diff --git a/e2e/flux_test.go b/e2e/flux_test.go index c8e1979b2..8854487b7 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -36,7 +36,7 @@ func TestFluxCommands(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) var wg sync.WaitGroup wg.Add(2) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index cedfa3a60..3fe9dad20 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -33,7 +33,7 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -124,7 +124,7 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -168,7 +168,7 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -278,7 +278,7 @@ func testHelmUpdateConstraints(t *testing.T, oci bool) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -341,7 +341,7 @@ func TestHelmValues(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -406,7 +406,7 @@ func TestHelmTemplateChartYaml(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) createNamespace(t, k, p.TestSlug()+"-a") @@ -437,7 +437,7 @@ func TestHelmRenderOfflineKubernetes(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 766c43e9c..e32f1c55f 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -99,14 +99,16 @@ func prepareHookTestProject(t *testing.T, hook string, hookDeletionPolicy string } s.setupWebhook() - s.p = test_utils.NewTestProject(t, s.k) + s.p = test_utils.NewTestProject(t) t.Cleanup(func() { s.removeWebhook() }) createNamespace(s.t, s.k, s.p.TestSlug()) - s.p.UpdateTarget("test", nil) + s.p.UpdateTarget("test", func(target *uo.UnstructuredObject) { + _ = target.SetNestedField(s.k.Context, "context") + }) s.p.AddKustomizeDeployment("hook", nil, nil) diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 08e6f5881..84b427c97 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -10,7 +10,7 @@ import ( func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*test_utils.TestProject, *test_utils.EnvTestCluster) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index caa7d217c..1451d10d8 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -9,8 +9,7 @@ import ( ) func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *test_utils.TestProject { - p := test_utils.NewTestProject(t, defaultCluster1) - p.MergeKubeconfig(defaultCluster2) + p := test_utils.NewTestProject(t) createNamespace(t, defaultCluster1, p.TestSlug()) createNamespace(t, defaultCluster2, p.TestSlug()) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index ecbf0b69f..49c060feb 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -98,7 +98,7 @@ func addProxyVars(p *test_utils.TestProject) { } func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *test_utils.TestProject { - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) if proxy { addProxyVars(p) @@ -341,7 +341,6 @@ func TestSeal_MultipleTargets(t *testing.T) { }) addSecretsSetToTarget(p, "test-target2", "test2") - p.MergeKubeconfig(defaultCluster2) createNamespace(t, defaultCluster2, p.TestSlug()) p.UpdateTarget("test-target", func(target *uo.UnstructuredObject) { _ = target.SetNestedField(defaultCluster1.Context, "context") diff --git a/e2e/sops_test.go b/e2e/sops_test.go index da65ece12..598885272 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -15,7 +15,7 @@ func TestSopsVars(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -55,7 +55,7 @@ func TestSopsResources(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) @@ -88,7 +88,7 @@ func TestSopsHelmValues(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t, k) + p := test_utils.NewTestProject(t) createNamespace(t, k, p.TestSlug()) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index ffc225c62..e3951f616 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -4,14 +4,11 @@ import ( "fmt" "github.com/go-git/go-git/v5" "github.com/huandu/xstrings" - "github.com/imdario/mergo" "github.com/jinzhu/copier" git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" registry2 "helm.sh/helm/v3/pkg/registry" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "os" "os/exec" "path/filepath" @@ -24,12 +21,10 @@ type TestProject struct { t *testing.T extraEnv []string - mergedKubeconfig string - gitServer *git2.TestGitServer } -func NewTestProject(t *testing.T, k *EnvTestCluster) *TestProject { +func NewTestProject(t *testing.T) *TestProject { p := &TestProject{ t: t, } @@ -43,19 +38,6 @@ func NewTestProject(t *testing.T, k *EnvTestCluster) *TestProject { p.UpdateDeploymentYaml(".", func(c *uo.UnstructuredObject) error { return nil }) - - tmpFile, err := os.CreateTemp("", p.TestSlug()+"-kubeconfig-") - if err != nil { - t.Fatal(err) - } - _ = tmpFile.Close() - p.mergedKubeconfig = tmpFile.Name() - t.Cleanup(func() { - os.Remove(p.mergedKubeconfig) - }) - if k != nil { - p.MergeKubeconfig(k) - } return p } @@ -66,34 +48,6 @@ func (p *TestProject) TestSlug() string { return n } -func (p *TestProject) MergeKubeconfig(k *EnvTestCluster) { - p.UpdateMergedKubeconfig(func(config *clientcmdapi.Config) { - nkcfg, err := clientcmd.Load(k.Kubeconfig) - if err != nil { - p.t.Fatal(err) - } - - err = mergo.Merge(config, nkcfg) - if err != nil { - p.t.Fatal(err) - } - }) -} - -func (p *TestProject) UpdateMergedKubeconfig(cb func(config *clientcmdapi.Config)) { - mkcfg, err := clientcmd.LoadFromFile(p.mergedKubeconfig) - if err != nil { - p.t.Fatal(err) - } - - cb(mkcfg) - - err = clientcmd.WriteToFile(*mkcfg, p.mergedKubeconfig) - if err != nil { - p.t.Fatal(err) - } -} - func (p *TestProject) AddExtraEnv(e string) { p.extraEnv = append(p.extraEnv, e) } From e427b01612a6b1c02ca7a134117c2b457e300633 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 13:23:23 +0100 Subject: [PATCH 0637/2268] refactor: Allow to override stdout/stderr in commands --- .../commands/cmd_check_image_updates.go | 3 +- cmd/kluctl/commands/cmd_delete.go | 5 +- cmd/kluctl/commands/cmd_render.go | 2 +- cmd/kluctl/commands/cmd_validate.go | 3 +- cmd/kluctl/commands/cmd_version.go | 3 +- cmd/kluctl/commands/command_result.go | 15 +++--- cmd/kluctl/commands/override_std_streams.go | 53 +++++++++++++++++++ 7 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 cmd/kluctl/commands/override_std_streams.go diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index a21df32c3..64471dd3b 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -7,7 +7,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/versions" - "os" "regexp" "sort" "strings" @@ -130,6 +129,6 @@ func runCheckImageUpdates(cmdCtx *commandCtx) error { } table.SortRows(1) - _, _ = os.Stdout.WriteString(table.Render([]int{60})) + _, _ = getStdout(cmdCtx.ctx).WriteString(table.Render([]int{60})) return nil } diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 48a09b2c7..b034c6c4e 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -11,7 +11,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "os" ) type deleteCmd struct { @@ -79,9 +78,9 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { func confirmedDeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, dryRun bool, forceYes bool) (*types.CommandResult, error) { if len(refs) != 0 { - _, _ = os.Stderr.WriteString("The following objects will be deleted:\n") + _, _ = getStderr(ctx).WriteString("The following objects will be deleted:\n") for _, ref := range refs { - _, _ = os.Stderr.WriteString(fmt.Sprintf(" %s\n", ref.String())) + _, _ = getStderr(ctx).WriteString(fmt.Sprintf(" %s\n", ref.String())) } if !forceYes && !dryRun { if !status.AskForConfirmation(ctx, fmt.Sprintf("Do you really want to delete %d objects?", len(refs))) { diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 27851b332..9c4152129 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -60,7 +60,7 @@ func (cmd *renderCmd) Run(ctx context.Context) error { defer os.RemoveAll(cmd.RenderOutputDir) } status.Flush(cmdCtx.ctx) - return yaml.WriteYamlAllStream(os.Stdout, all) + return yaml.WriteYamlAllStream(getStdout(ctx), all) } else { status.Info(cmdCtx.ctx, "Rendered into %s", cmdCtx.targetCtx.SharedContext.RenderDir) } diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index 101af905f..26b43da2f 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" - "os" "time" ) @@ -54,7 +53,7 @@ func (cmd *validateCmd) Run(ctx context.Context) error { } if !failed { - _, _ = os.Stderr.WriteString("Validation succeeded\n") + _, _ = getStderr(ctx).WriteString("Validation succeeded\n") return nil } diff --git a/cmd/kluctl/commands/cmd_version.go b/cmd/kluctl/commands/cmd_version.go index b7683a784..c28a5b37e 100644 --- a/cmd/kluctl/commands/cmd_version.go +++ b/cmd/kluctl/commands/cmd_version.go @@ -3,13 +3,12 @@ package commands import ( "context" "github.com/kluctl/kluctl/v2/pkg/version" - "os" ) type versionCmd struct { } func (cmd *versionCmd) Run(ctx context.Context) error { - _, err := os.Stdout.WriteString(version.GetVersion() + "\n") + _, err := getStdout(ctx).WriteString(version.GetVersion() + "\n") return err } diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index cdf7f0753..f9dd12408 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -173,7 +173,7 @@ func formatValidateResult(vr *types.ValidateResult, format string) (string, erro } } -func outputHelper(output []string, cb func(format string) (string, error)) error { +func outputHelper(ctx context.Context, output []string, cb func(format string) (string, error)) error { if len(output) == 0 { output = []string{"text"} } @@ -189,7 +189,7 @@ func outputHelper(output []string, cb func(format string) (string, error)) error return err } - err = outputResult(path, r) + err = outputResult(ctx, path, r) if err != nil { return err } @@ -200,7 +200,7 @@ func outputHelper(output []string, cb func(format string) (string, error)) error func outputCommandResult(ctx context.Context, output []string, cr *types.CommandResult) error { status.Flush(ctx) - return outputHelper(output, func(format string) (string, error) { + return outputHelper(ctx, output, func(format string) (string, error) { return formatCommandResult(cr, format) }) } @@ -208,7 +208,7 @@ func outputCommandResult(ctx context.Context, output []string, cr *types.Command func outputValidateResult(ctx context.Context, output []string, vr *types.ValidateResult) error { status.Flush(ctx) - return outputHelper(output, func(format string) (string, error) { + return outputHelper(ctx, output, func(format string) (string, error) { return formatValidateResult(vr, format) }) } @@ -238,7 +238,7 @@ func outputYamlResult(ctx context.Context, output []string, result interface{}, s = x } for _, path := range output { - err := outputResult(&path, s) + err := outputResult(ctx, &path, s) if err != nil { return err } @@ -246,8 +246,9 @@ func outputYamlResult(ctx context.Context, output []string, result interface{}, return nil } -func outputResult(f *string, result string) error { - w := os.Stdout +func outputResult(ctx context.Context, f *string, result string) error { + var w io.Writer + w = getStdout(ctx) if f != nil && *f != "-" { f, err := os.Create(*f) if err != nil { diff --git a/cmd/kluctl/commands/override_std_streams.go b/cmd/kluctl/commands/override_std_streams.go new file mode 100644 index 000000000..ab2573f72 --- /dev/null +++ b/cmd/kluctl/commands/override_std_streams.go @@ -0,0 +1,53 @@ +package commands + +import ( + "context" + "io" + "os" +) + +type overrideStdStreamsKey int +type overrideStdStreamsValue struct { + stdout io.Writer + stderr io.Writer +} + +var overrideStdStreamsKeyInst overrideStdStreamsKey + +func WithStdStreams(ctx context.Context, stdout io.Writer, stderr io.Writer) context.Context { + return context.WithValue(ctx, overrideStdStreamsKeyInst, &overrideStdStreamsValue{ + stdout: stdout, + stderr: stderr, + }) +} + +func getStdStreams(ctx context.Context) (io.Writer, io.Writer) { + v := ctx.Value(overrideStdStreamsKeyInst) + if v == nil { + return os.Stdout, os.Stderr + } + v2 := v.(*overrideStdStreamsValue) + return v2.stdout, v2.stderr +} + +type stringWriter struct { + w io.Writer +} + +func (w *stringWriter) WriteString(s string) (n int, err error) { + return w.w.Write([]byte(s)) +} + +func (w *stringWriter) Write(b []byte) (n int, err error) { + return w.w.Write(b) +} + +func getStdout(ctx context.Context) *stringWriter { + stdout, _ := getStdStreams(ctx) + return &stringWriter{stdout} +} + +func getStderr(ctx context.Context) *stringWriter { + _, stderr := getStdStreams(ctx) + return &stringWriter{stderr} +} From d496a45ccdb50b7ff884a11d28d365aa3d16e49c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 13:46:52 +0100 Subject: [PATCH 0638/2268] feat: Implement --project-dir argument --- cmd/kluctl/args/project.go | 22 +++++++++++++++++++++- cmd/kluctl/commands/cmd_helm_pull.go | 8 ++++---- cmd/kluctl/commands/cmd_helm_update.go | 7 ++++--- cmd/kluctl/commands/utils.go | 6 +++--- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index f60f27482..eff3ec1f0 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -1,8 +1,28 @@ package args -import "time" +import ( + "os" + "time" +) + +type ProjectDir struct { + ProjectDir existingDirType `group:"project" help:"Specify the project directory. Defaults to the current working directory."` +} + +func (a ProjectDir) GetProjectDir() (string, error) { + if a.ProjectDir != "" { + return a.ProjectDir.String(), nil + } + cwd, err := os.Getwd() + if err != nil { + return "", err + } + return cwd, nil +} type ProjectFlags struct { + ProjectDir + ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 4ee57ec2e..ab2d7bf66 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -11,12 +11,12 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/sync/semaphore" "io/fs" - "os" "path/filepath" "sync" ) type helmPullCmd struct { + args.ProjectDir args.HelmCredentials } @@ -27,12 +27,12 @@ pulling is only needed when really required (e.g. when the chart version changes } func (cmd *helmPullCmd) Run(ctx context.Context) error { - cwd, err := os.Getwd() + projectDir, err := cmd.ProjectDir.GetProjectDir() if err != nil { return err } - gitRootPath, err := git2.DetectGitRepositoryRoot(cwd) + gitRootPath, err := git2.DetectGitRepositoryRoot(projectDir) if err != nil { return err } @@ -42,7 +42,7 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { var mutex sync.Mutex sem := semaphore.NewWeighted(8) - err = filepath.WalkDir(cwd, func(p string, d fs.DirEntry, err error) error { + err = filepath.WalkDir(projectDir, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname != "helm-chart.yml" && fname != "helm-chart.yaml" { return nil diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index beecb5d0e..225caec92 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -20,6 +20,7 @@ import ( ) type helmUpdateCmd struct { + args.ProjectDir args.HelmCredentials Upgrade bool `group:"misc" help:"Write new versions into helm-chart.yaml and perform helm-pull afterwards"` @@ -33,12 +34,12 @@ func (cmd *helmUpdateCmd) Help() string { } func (cmd *helmUpdateCmd) Run(ctx context.Context) error { - cwd, err := os.Getwd() + projectDir, err := cmd.ProjectDir.GetProjectDir() if err != nil { return err } - gitRootPath, err := git2.DetectGitRepositoryRoot(cwd) + gitRootPath, err := git2.DetectGitRepositoryRoot(projectDir) if err != nil { return err } @@ -57,7 +58,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { } var updatedCharts []*updatedChart - err = filepath.WalkDir(cwd, func(p string, d fs.DirEntry, err error) error { + err = filepath.WalkDir(projectDir, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) if fname != "helm-chart.yml" && fname != "helm-chart.yaml" { return nil diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index bfe63cb17..a209d2829 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -37,12 +37,12 @@ func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFla } defer j2.Close() - cwd, err := os.Getwd() + projectDir, err := projectFlags.ProjectDir.GetProjectDir() if err != nil { return err } - repoRoot, err := git.DetectGitRepositoryRoot(cwd) + repoRoot, err := git.DetectGitRepositoryRoot(projectDir) if err != nil { status.Warning(ctx, "Failed to detect git project root. This might cause follow-up errors") } @@ -74,7 +74,7 @@ func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFla loadArgs := kluctl_project.LoadKluctlProjectArgs{ RepoRoot: repoRoot, - ProjectDir: cwd, + ProjectDir: projectDir, ProjectConfig: projectFlags.ProjectConfig.String(), RP: rp, ClientConfigGetter: clientConfigGetter(forCompletion), From 986c5d62a62549a9b5e64c172db42c895c8bb894 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 13:47:17 +0100 Subject: [PATCH 0639/2268] fix: Don't allow term.GetWidth() to return 0 --- pkg/utils/term/term_size.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/term/term_size.go b/pkg/utils/term/term_size.go index 706f09180..0c210fd72 100644 --- a/pkg/utils/term/term_size.go +++ b/pkg/utils/term/term_size.go @@ -15,7 +15,7 @@ func GetWidth() int { } } w, _, err := GetSize(int(origStdout.Fd())) - if err != nil { + if err != nil || w == 0 { return 80 } return w From a71636b61c5280498da3bf4bb25b6383bf62f813 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 14:42:19 +0100 Subject: [PATCH 0640/2268] tests: Execute kluctl via commands.Execute() instead of spawning a process --- e2e/args_test.go | 20 +++++------ e2e/flux_test.go | 2 +- e2e/seal_test.go | 2 +- e2e/sops_test.go | 24 ++++++++----- e2e/test-utils/project.go | 73 +++++++++++++++++++++++++++++++++++---- 5 files changed, 93 insertions(+), 28 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index b52578096..d754bf3f6 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -128,15 +128,14 @@ func TestArgs(t *testing.T) { } func TestArgsFromEnv(t *testing.T) { - t.Setenv("KLUCTL_ARG", "a=a") - t.Setenv("KLUCTL_ARG_1", "b=b") - t.Setenv("KLUCTL_ARG_2", `c={"nested":{"nested2":"c"}}`) - t.Setenv("KLUCTL_ARG_3", "d=true") - t.Setenv("KLUCTL_ARG_4", "e='true'") - k := defaultCluster1 - p := test_utils.NewTestProject(t) + p := test_utils.NewTestProject(t, test_utils.WithUseProcess(true)) + p.AddExtraEnv("KLUCTL_ARG=a=a") + p.AddExtraEnv("KLUCTL_ARG_1=b=b") + p.AddExtraEnv(`KLUCTL_ARG_2=c={"nested":{"nested2":"c"}}`) + p.AddExtraEnv("KLUCTL_ARG_3=d=true") + p.AddExtraEnv("KLUCTL_ARG_4=e='true'") createNamespace(t, k, p.TestSlug()) @@ -164,12 +163,11 @@ func TestArgsFromEnv(t *testing.T) { } func TestArgsFromEnvAndCli(t *testing.T) { - t.Setenv("KLUCTL_ARG_1", "a=a") - t.Setenv("KLUCTL_ARG_2", "c=c") - k := defaultCluster1 - p := test_utils.NewTestProject(t) + p := test_utils.NewTestProject(t, test_utils.WithUseProcess(true)) + p.AddExtraEnv("KLUCTL_ARG_1=a=a") + p.AddExtraEnv("KLUCTL_ARG_2=c=c") createNamespace(t, k, p.TestSlug()) diff --git a/e2e/flux_test.go b/e2e/flux_test.go index 8854487b7..5967270c4 100644 --- a/e2e/flux_test.go +++ b/e2e/flux_test.go @@ -36,7 +36,7 @@ func TestFluxCommands(t *testing.T) { k := defaultCluster1 - p := test_utils.NewTestProject(t) + p := test_utils.NewTestProject(t, test_utils.WithUseProcess(true)) var wg sync.WaitGroup wg.Add(2) diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 49c060feb..2308ec103 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -98,7 +98,7 @@ func addProxyVars(p *test_utils.TestProject) { } func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[string]string, varsSources []*uo.UnstructuredObject, proxy bool) *test_utils.TestProject { - p := test_utils.NewTestProject(t) + p := test_utils.NewTestProject(t, test_utils.WithUseProcess(true)) if proxy { addProxyVars(p) diff --git a/e2e/sops_test.go b/e2e/sops_test.go index 598885272..2ea1d9ac7 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -1,6 +1,7 @@ package e2e import ( + "fmt" "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars/sops_test_resources" @@ -9,13 +10,18 @@ import ( "testing" ) -func TestSopsVars(t *testing.T) { +func setSopsKey(p *test_utils.TestProject) { key, _ := sops_test_resources.TestResources.ReadFile("test-key.txt") - t.Setenv(age.SopsAgeKeyEnv, string(key)) + p.AddExtraEnv(fmt.Sprintf("%s=%s", age.SopsAgeKeyEnv, string(key))) +} + +func TestSopsVars(t *testing.T) { + t.Parallel() k := defaultCluster1 - p := test_utils.NewTestProject(t) + p := test_utils.NewTestProject(t, test_utils.WithUseProcess(true)) + setSopsKey(p) createNamespace(t, k, p.TestSlug()) @@ -50,12 +56,12 @@ func TestSopsVars(t *testing.T) { } func TestSopsResources(t *testing.T) { - key, _ := sops_test_resources.TestResources.ReadFile("test-key.txt") - t.Setenv(age.SopsAgeKeyEnv, string(key)) + t.Parallel() k := defaultCluster1 - p := test_utils.NewTestProject(t) + p := test_utils.NewTestProject(t, test_utils.WithUseProcess(true)) + setSopsKey(p) createNamespace(t, k, p.TestSlug()) @@ -83,12 +89,12 @@ func TestSopsResources(t *testing.T) { } func TestSopsHelmValues(t *testing.T) { - key, _ := sops_test_resources.TestResources.ReadFile("test-key.txt") - t.Setenv(age.SopsAgeKeyEnv, string(key)) + t.Parallel() k := defaultCluster1 - p := test_utils.NewTestProject(t) + p := test_utils.NewTestProject(t, test_utils.WithUseProcess(true)) + setSopsKey(p) createNamespace(t, k, p.TestSlug()) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index e3951f616..0d3b79415 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -1,11 +1,16 @@ package test_utils import ( + "bytes" + "context" "fmt" "github.com/go-git/go-git/v5" "github.com/huandu/xstrings" "github.com/jinzhu/copier" + "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" git2 "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" registry2 "helm.sh/helm/v3/pkg/registry" @@ -18,17 +23,31 @@ import ( ) type TestProject struct { - t *testing.T - extraEnv []string + t *testing.T + + extraEnv []string + useProcess bool gitServer *git2.TestGitServer } -func NewTestProject(t *testing.T) *TestProject { +type TestProjectOption func(p *TestProject) + +func WithUseProcess(useProcess bool) TestProjectOption { + return func(p *TestProject) { + p.useProcess = useProcess + } +} + +func NewTestProject(t *testing.T, opts ...TestProjectOption) *TestProject { p := &TestProject{ t: t, } + for _, o := range opts { + o(p) + } + p.gitServer = git2.NewTestGitServer(t) p.gitServer.GitInit("kluctl-project") @@ -350,7 +369,7 @@ func (p *TestProject) GetGitRepo() *git.Repository { return p.gitServer.GetGitRepo("kluctl-project") } -func (p *TestProject) Kluctl(argsIn ...string) (string, string, error) { +func (p *TestProject) KluctlProcess(argsIn ...string) (string, string, error) { var args []string args = append(args, argsIn...) args = append(args, "--no-update-check") @@ -361,7 +380,6 @@ func (p *TestProject) Kluctl(argsIn ...string) (string, string, error) { env := os.Environ() env = append(env, p.extraEnv...) - env = append(env, fmt.Sprintf("KUBECONFIG=%s", p.mergedKubeconfig)) // this will cause the init() function from call_kluctl_hack.go to invoke the kluctl root command and then exit env = append(env, "CALL_KLUCTL=true") @@ -382,10 +400,53 @@ func (p *TestProject) Kluctl(argsIn ...string) (string, string, error) { return stdout, stderr, err } +func (p *TestProject) KluctlProcessMust(argsIn ...string) (string, string) { + stdout, stderr, err := p.KluctlProcess(argsIn...) + if err != nil { + p.t.Logf(stderr) + p.t.Fatal(fmt.Errorf("kluctl failed: %w", err)) + } + return stdout, stderr +} + +func (p *TestProject) KluctlExecute(argsIn ...string) (string, string, error) { + if len(p.extraEnv) != 0 { + p.t.Fatal("extraEnv is only supported in KluctlProcess(...)") + } + + var args []string + args = append(args, "--project-dir", p.LocalRepoDir()) + args = append(args, argsIn...) + + p.t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) + + stdout := bytes.NewBuffer(nil) + stderr := bytes.NewBuffer(nil) + + ctx := context.Background() + ctx = utils.WithTmpBaseDir(ctx, p.t.TempDir()) + ctx = commands.WithStdStreams(ctx, stdout, stderr) + sh := status.NewSimpleStatusHandler(func(message string) { + p.t.Log(message) + stderr.WriteString(message + "\n") + }, false, true) + defer sh.Stop() + ctx = status.NewContext(ctx, sh) + err := commands.Execute(ctx, args, nil) + return stdout.String(), stderr.String(), err +} + +func (p *TestProject) Kluctl(argsIn ...string) (string, string, error) { + if p.useProcess { + return p.KluctlProcess(argsIn...) + } else { + return p.KluctlExecute(argsIn...) + } +} + func (p *TestProject) KluctlMust(argsIn ...string) (string, string) { stdout, stderr, err := p.Kluctl(argsIn...) if err != nil { - p.t.Logf(stderr) p.t.Fatal(fmt.Errorf("kluctl failed: %w", err)) } return stdout, stderr From 687d718794b1f7008f1117dd56b9e97c6b5e88c3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 15:52:50 +0100 Subject: [PATCH 0641/2268] tests: Fix potential crash in webhook handler --- e2e/hooks_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index e32f1c55f..122dc018a 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -96,10 +96,9 @@ func prepareHookTestProject(t *testing.T, hook string, hookDeletionPolicy string s := &hooksTestContext{ t: t, k: defaultCluster2, // use cluster2 as it has webhooks setup + p: test_utils.NewTestProject(t), } s.setupWebhook() - - s.p = test_utils.NewTestProject(t) t.Cleanup(func() { s.removeWebhook() }) From 8328ad819370c05b3c5bdaaae0cfea2ce5c37920 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 15:53:25 +0100 Subject: [PATCH 0642/2268] tests: Ignore config map updates when they don't belong to the current run --- e2e/hooks_test.go | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 122dc018a..504559c23 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -1,14 +1,12 @@ package e2e import ( - "context" "fmt" "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "sync" "testing" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -20,7 +18,9 @@ type hooksTestContext struct { p *test_utils.TestProject + m sync.Mutex seenConfigMaps []string + runCount int whh *test_utils.CallbackHandlerEntry } @@ -39,6 +39,8 @@ func (s *hooksTestContext) handleConfigmap(request admission.Request) { if s.p.TestSlug() != request.Namespace { return } + s.m.Lock() + defer s.m.Unlock() x, err := uo.FromString(string(request.Object.Raw)) if err != nil { @@ -52,6 +54,10 @@ func (s *hooksTestContext) handleConfigmap(request admission.Request) { if err != nil { s.t.Fatal(err) } + runCount := x.GetK8sLabel("tests.kluctl.io/runCount") + if runCount == nil || *runCount != fmt.Sprintf("%d", s.runCount) { + return + } s.t.Logf("handleConfigmap: op=%s, name=%s/%s, generation=%d, uid=%s", request.Operation, request.Namespace, request.Name, generation, uid) @@ -59,6 +65,8 @@ func (s *hooksTestContext) handleConfigmap(request admission.Request) { } func (s *hooksTestContext) clearSeenConfigmaps() { + s.m.Lock() + defer s.m.Unlock() s.t.Logf("clearSeenConfigmaps: %v", s.seenConfigMaps) s.seenConfigMaps = nil } @@ -117,20 +125,22 @@ func prepareHookTestProject(t *testing.T, hook string, hookDeletionPolicy string return s } +func (s *hooksTestContext) incRunCount() { + s.runCount++ + s.t.Logf("incRunCount: %d", s.runCount) + s.p.UpdateDeploymentYaml(".", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(fmt.Sprintf("%d", s.runCount), "commonLabels", "tests.kluctl.io/runCount") + return nil + }) +} + func (s *hooksTestContext) ensureHookExecuted(expectedCms ...string) { s.clearSeenConfigmaps() + s.incRunCount() s.p.KluctlMust("deploy", "--yes", "-t", "test") assert.Equal(s.t, expectedCms, s.seenConfigMaps) } -func (s *hooksTestContext) ensureHookNotExecuted() { - _ = s.k.DynamicClient.Resource(corev1.SchemeGroupVersion.WithResource("configmaps")). - Namespace(s.p.TestSlug()). - Delete(context.Background(), "cm1", metav1.DeleteOptions{}) - s.p.KluctlMust("deploy", "--yes", "-t", "test") - assertConfigMapNotExists(s.t, s.k, s.p.TestSlug(), "cm1") -} - func TestHooksPreDeployInitial(t *testing.T) { t.Parallel() s := prepareHookTestProject(t, "pre-deploy-initial", "") From b3a72803d3653eed220fb74f585e2a8a71a0897b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 16:03:13 +0100 Subject: [PATCH 0643/2268] docs: Run make replace-commands-help --- docs/reference/commands/common-arguments.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index aedafc415..0f3150151 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -62,6 +62,7 @@ Project arguments: 'github.com:my-org/my-repo:my-branch=/local/path/to/override' -c, --project-config existingfile Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml + --project-dir existingdir Specify the project directory. Defaults to the current working directory. -t, --target string Target name to run command for. Target must exist in .kluctl.yaml. -T, --target-name-override string Overrides the target name. If -t is used at the same time, then the target will be looked up based on -t and then renamed to the From f704069fcab9070242670027936a152dc85326d4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 16:14:28 +0100 Subject: [PATCH 0644/2268] refactor: More uses og GetK8sAnnotation/GetK8sLabel and ParseBoolOrFalse --- pkg/deployment/deployment_item.go | 8 ++++++-- pkg/deployment/utils/delete_utils.go | 9 +++------ pkg/diff/normalize.go | 5 ++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index bbd9665b6..94f005e5c 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -578,8 +578,12 @@ func (di *DeploymentItem) postprocessObjects(images *Images) error { } // Set common labels/annotations - o.SetK8sLabels(uo.CopyMergeStrMap(o.GetK8sLabels(), commonLabels)) - o.SetK8sAnnotations(uo.CopyMergeStrMap(o.GetK8sAnnotations(), commonAnnotations)) + for n, v := range commonLabels { + o.SetK8sLabel(n, v) + } + for n, v := range commonAnnotations { + o.SetK8sAnnotation(n, v) + } // Resolve image placeholders err := images.ResolvePlaceholders(di.ctx.K, o, di.RelRenderedDir, di.Tags.ListKeys()) diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index dabdf569e..f4cec36cc 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -5,11 +5,11 @@ import ( "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "golang.org/x/sync/semaphore" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "strconv" "sync" ) @@ -73,13 +73,11 @@ func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, continue } - annotations := o.GetK8sAnnotations() ownerRefs := o.GetK8sOwnerReferences() managedFields := o.GetK8sManagedFields() // exclude when explicitly requested - skipDelete, err := strconv.ParseBool(annotations["kluctl.io/skip-delete"]) - if err == nil && skipDelete { + if utils.ParseBoolOrFalse(o.GetK8sAnnotation("kluctl.io/skip-delete")) { continue } @@ -114,8 +112,7 @@ func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, // exclude resources which have the 'kluctl.io/skip-delete-if-tags' annotation set if inclusionHasTags { - skipDeleteIfTags, err := strconv.ParseBool(annotations["kluctl.io/skip-delete-if-tags"]) - if err == nil && skipDeleteIfTags { + if utils.ParseBoolOrFalse(o.GetK8sAnnotation("kluctl.io/skip-delete-if-tags")) { continue } } diff --git a/pkg/diff/normalize.go b/pkg/diff/normalize.go index dd69cf073..5123a347f 100644 --- a/pkg/diff/normalize.go +++ b/pkg/diff/normalize.go @@ -3,9 +3,9 @@ package diff import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "regexp" - "strconv" "strings" ) @@ -168,8 +168,7 @@ func NormalizeObject(o_ *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreFo } } - ignoreAll, _ := strconv.ParseBool(localObject.GetK8sAnnotations()["kluctl.io/ignore-diff"]) - if ignoreAll { + if utils.ParseBoolOrFalse(localObject.GetK8sAnnotation("kluctl.io/ignore-diff")) { // Return empty object so that diffs will always be empty return &uo.UnstructuredObject{Object: map[string]interface{}{}} } From e4da1f800f5869eacfd463c99f3224dea9732e6b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 16:20:56 +0100 Subject: [PATCH 0645/2268] feat: Add compatibility code for helm.sh/resource-policy annotation --- pkg/deployment/utils/delete_utils.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index f4cec36cc..babaa5a4b 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -81,6 +81,14 @@ func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, continue } + helmResourcePolicy := o.GetK8sAnnotation("helm.sh/resource-policy") + if helmResourcePolicy != nil && *helmResourcePolicy == "keep" { + // warning, this is currently not how Helm handles it. Helm will only respect annotations set in the Chart + // itself, while Kluctl will also respect it when manually set on the live resource + // See: https://github.com/helm/helm/issues/8132 + continue + } + // exclude objects which are owned by some other object if len(ownerRefs) != 0 { continue From 0cc442d698975b728946edd7d5e45564bf77f5b7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 16:23:47 +0100 Subject: [PATCH 0646/2268] tests: Fix race condition --- e2e/test-utils/project.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index 0d3b79415..9a87be0f1 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -19,6 +19,7 @@ import ( "path/filepath" "reflect" "strings" + "sync" "testing" ) @@ -420,6 +421,7 @@ func (p *TestProject) KluctlExecute(argsIn ...string) (string, string, error) { p.t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) + var m sync.Mutex stdout := bytes.NewBuffer(nil) stderr := bytes.NewBuffer(nil) @@ -427,6 +429,8 @@ func (p *TestProject) KluctlExecute(argsIn ...string) (string, string, error) { ctx = utils.WithTmpBaseDir(ctx, p.t.TempDir()) ctx = commands.WithStdStreams(ctx, stdout, stderr) sh := status.NewSimpleStatusHandler(func(message string) { + m.Lock() + defer m.Unlock() p.t.Log(message) stderr.WriteString(message + "\n") }, false, true) From 7cf5d1ac4df9f772d8aa538abbc747f5e593878f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 17:06:57 +0100 Subject: [PATCH 0647/2268] feat: Respect inclusion flags while collecting deployments --- docs/reference/deployments/deployment-yml.md | 2 ++ pkg/deployment/commands/poke_images.go | 3 --- pkg/deployment/commands/validate.go | 3 --- pkg/deployment/deployment_collection.go | 13 +++++++++---- pkg/deployment/deployment_item.go | 9 ++------- pkg/deployment/utils/apply_utils.go | 5 ----- pkg/deployment/utils/diff_utils.go | 4 ---- 7 files changed, 13 insertions(+), 26 deletions(-) diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index a8ff6252f..73e0fb3a5 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -183,6 +183,8 @@ deployments: - path: kustomizeDeployment2 ``` +Please note that `alwaysDeploy` will also cause [kluctl render](../commands/render.md) to always render the resources. + ### skipDeleteIfTags Forces exclusion of a deployment whenever inclusion/exclusion tags are specified via command line. See [Deleting with tag inclusion/exclusion](./tags.md#deploying-with-tag-inclusionexclusion) for details. diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index a06bf48a2..3bcaaef36 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -35,9 +35,6 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type allObjects := make(map[k8s2.ObjectRef]*uo.UnstructuredObject) for _, d := range cmd.c.Deployments { - if !d.CheckInclusionForDeploy() { - continue - } for _, o := range d.Objects { allObjects[o.GetK8sRef()] = o } diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 1b4f20e89..a5781f855 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -38,9 +38,6 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types. ad := utils2.NewApplyDeploymentsUtil(ctx, cmd.dew, cmd.c.Deployments, cmd.ru, k, &utils2.ApplyUtilOptions{}) for _, d := range cmd.c.Deployments { - if !d.CheckInclusionForDeploy() { - continue - } au := ad.NewApplyUtil(ctx, nil) h := utils2.NewHooksUtil(au) for _, o := range d.Objects { diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index d8449cd77..c37a9fc9d 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -34,11 +34,16 @@ func NewDeploymentCollection(ctx SharedContext, project *DeploymentProject, imag } indexes := make(map[string]int) - deployments, err := dc.collectDeployments(project, indexes) + deployments, err := dc.collectAllDeployments(project, indexes) if err != nil { return nil, err } - dc.Deployments = deployments + dc.Deployments = make([]*DeploymentItem, 0, len(deployments)) + for _, d := range deployments { + if d.CheckInclusionForDeploy() { + dc.Deployments = append(dc.Deployments, d) + } + } return dc, nil } @@ -75,7 +80,7 @@ func findDeploymentItemIndex(project *DeploymentProject, pth *string, indexes ma return index, dir2 } -func (c *DeploymentCollection) collectDeployments(project *DeploymentProject, indexes map[string]int) ([]*DeploymentItem, error) { +func (c *DeploymentCollection) collectAllDeployments(project *DeploymentProject, indexes map[string]int) ([]*DeploymentItem, error) { var ret []*DeploymentItem for i, diConfig := range project.Config.Deployments { @@ -84,7 +89,7 @@ func (c *DeploymentCollection) collectDeployments(project *DeploymentProject, in if !ok { panic(fmt.Sprintf("Did not find find index %d in project.includes", i)) } - ret2, err := c.collectDeployments(includedProject, indexes) + ret2, err := c.collectAllDeployments(includedProject, indexes) if err != nil { return nil, err } diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index bbd9665b6..6776bb7db 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -367,16 +367,11 @@ func (di *DeploymentItem) CheckInclusionForDeploy() bool { if di.Config.AlwaysDeploy { return true } - values := di.buildInclusionEntries() - return di.Inclusion.CheckIncluded(values, false) -} - -func (di *DeploymentItem) checkInclusionForDelete() bool { - if di.Inclusion == nil { + if di.Config.Barrier { return true } values := di.buildInclusionEntries() - return di.Inclusion.CheckIncluded(values, di.Config.SkipDeleteIfTags) + return di.Inclusion.CheckIncluded(values, false) } func (di *DeploymentItem) readKustomizationYaml(subDir string) (*uo.UnstructuredObject, error) { diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 2bee3e50d..87767ad19 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -475,11 +475,6 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { // +1 to ensure that we don't prematurely complete the bar (which would happen as we don't count for waiting) total := len(applyObjects) + len(preHooks) + len(postHooks) + 1 a.sctx.SetTotal(total) - if !d.CheckInclusionForDeploy() { - a.sctx.UpdateAndInfoFallback("Skipped") - a.sctx.Warning() - return - } if len(toDelete) != 0 { a.sctx.InfoFallback("Deleting %d objects", len(toDelete)) diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index 3690ecdd8..77b1e31ad 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -42,10 +42,6 @@ func (u *diffUtil) Diff() { u.calcRemoteObjectsForDiff() for _, d := range u.deployments { - if !d.CheckInclusionForDeploy() { - continue - } - ignoreForDiffs := d.Project.GetIgnoreForDiffs(u.IgnoreTags, u.IgnoreLabels, u.IgnoreAnnotations) for _, o := range d.Objects { o := o From 553d8ea76cd019446421ae18274d7873d4e5829e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 17:07:38 +0100 Subject: [PATCH 0648/2268] feat: Allow inclusion flags on render command --- cmd/kluctl/commands/cmd_render.go | 2 ++ docs/reference/commands/render.md | 1 + 2 files changed, 3 insertions(+) diff --git a/cmd/kluctl/commands/cmd_render.go b/cmd/kluctl/commands/cmd_render.go index 9c4152129..f8e252804 100644 --- a/cmd/kluctl/commands/cmd_render.go +++ b/cmd/kluctl/commands/cmd_render.go @@ -15,6 +15,7 @@ type renderCmd struct { args.TargetFlags args.ArgsFlags args.ImageFlags + args.InclusionFlags args.HelmCredentials args.RenderOutputDirFlags args.OfflineKubernetesFlags @@ -43,6 +44,7 @@ func (cmd *renderCmd) Run(ctx context.Context) error { targetFlags: cmd.TargetFlags, argsFlags: cmd.ArgsFlags, imageFlags: cmd.ImageFlags, + inclusionFlags: cmd.InclusionFlags, helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, offlineKubernetes: cmd.OfflineKubernetes, diff --git a/docs/reference/commands/render.md b/docs/reference/commands/render.md index 3556e40c0..5681e0f57 100644 --- a/docs/reference/commands/render.md +++ b/docs/reference/commands/render.md @@ -23,6 +23,7 @@ a temporary directory or a specified directory. The following sets of arguments are available: 1. [project arguments](./common-arguments.md#project-arguments) 1. [image arguments](./common-arguments.md#image-arguments) +1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) In addition, the following arguments are available: From 0275419164233600a55c940017695b872eb5313d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Dec 2022 17:57:37 +0100 Subject: [PATCH 0649/2268] feat: Allow to ignore missing variable sources --- docs/reference/templating/variable-sources.md | 5 + pkg/types/vars_source.go | 5 + pkg/vars/aws/fake_clientfactory.go | 3 +- pkg/vars/vars_loader.go | 66 ++++++--- pkg/vars/vars_loader_http.go | 11 +- pkg/vars/vars_loader_test.go | 132 +++++++++++++++++- pkg/vars/vault/secrets.go | 11 +- 7 files changed, 204 insertions(+), 29 deletions(-) diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index 2f4a4137f..43c03e071 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -27,11 +27,16 @@ following example. vars: - file: vars1.yaml - file: vars2.yaml +- file: optional-vars.yaml + ignoreMissing: true ``` `vars2.yaml` can now use variables that are defined in `vars1.yaml`. At all times, variables defined by parents of the current sub-deployment project can be used in the current vars file. +Each variable source can have the optional field `ignoreMissing` set to `true`, causing Kluctl to ignore if the source +can not be found. + Different types of vars entries are possible: ### file diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index af8a235cc..c32578469 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -54,6 +54,8 @@ type VarsSourceVault struct { } type VarsSource struct { + IgnoreMissing *bool `yaml:"ignoreMissing,omitempty"` + Values *uo.UnstructuredObject `yaml:"values,omitempty"` File *string `yaml:"file,omitempty"` Git *VarsSourceGit `yaml:"git,omitempty"` @@ -71,6 +73,9 @@ func ValidateVarsSource(sl validator.StructLevel) { count := 0 v := reflect.ValueOf(s) for i := 0; i < v.NumField(); i++ { + if v.Type().Field(i).Name == "IgnoreMissing" { + continue + } if !v.Field(i).IsNil() { count += 1 } diff --git a/pkg/vars/aws/fake_clientfactory.go b/pkg/vars/aws/fake_clientfactory.go index 219dc1259..d06d68fd5 100644 --- a/pkg/vars/aws/fake_clientfactory.go +++ b/pkg/vars/aws/fake_clientfactory.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" ) @@ -27,7 +28,7 @@ func (f *FakeAwsClientFactory) GetSecretValue(in *secretsmanager.GetSecretValueI }, nil } - return nil, fmt.Errorf("secret %s not found", *in.SecretId) + return nil, awserr.New(secretsmanager.ErrCodeResourceNotFoundException, fmt.Sprintf("secret %s not found", *in.SecretId), nil) } func (f *FakeAwsClientFactory) SecretsManagerClient(profile *string, region *string) (secretsmanageriface.SecretsManagerAPI, error) { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 6ead52755..40e0bea86 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -3,7 +3,10 @@ package vars import ( "context" "encoding/base64" + errors2 "errors" "fmt" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/repocache" @@ -16,6 +19,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/vars/vault" "github.com/kluctl/kluctl/v2/pkg/yaml" "go.mozilla.org/sops/v3/cmd/sops/formats" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" "os" "strings" @@ -74,25 +78,30 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear return err } + ignoreMissing := false + if source.IgnoreMissing != nil { + ignoreMissing = *source.IgnoreMissing + } + if source.Values != nil { v.mergeVars(varsCtx, source.Values, rootKey) return nil } else if source.File != nil { - return v.loadFile(varsCtx, *source.File, searchDirs, rootKey) + return v.loadFile(varsCtx, *source.File, ignoreMissing, searchDirs, rootKey) } else if source.Git != nil { - return v.loadGit(varsCtx, source.Git, rootKey) + return v.loadGit(varsCtx, source.Git, ignoreMissing, rootKey) } else if source.ClusterConfigMap != nil { - return v.loadFromK8sObject(varsCtx, *source.ClusterConfigMap, "ConfigMap", source.ClusterConfigMap.Key, rootKey, false) + return v.loadFromK8sObject(varsCtx, *source.ClusterConfigMap, "ConfigMap", source.ClusterConfigMap.Key, rootKey, ignoreMissing, false) } else if source.ClusterSecret != nil { - return v.loadFromK8sObject(varsCtx, *source.ClusterSecret, "Secret", source.ClusterSecret.Key, rootKey, true) + return v.loadFromK8sObject(varsCtx, *source.ClusterSecret, "Secret", source.ClusterSecret.Key, rootKey, ignoreMissing, true) } else if source.SystemEnvVars != nil { - return v.loadSystemEnvs(varsCtx, &source, rootKey) + return v.loadSystemEnvs(varsCtx, &source, ignoreMissing, rootKey) } else if source.Http != nil { - return v.loadHttp(varsCtx, &source, rootKey) + return v.loadHttp(varsCtx, &source, ignoreMissing, rootKey) } else if source.AwsSecretsManager != nil { - return v.loadAwsSecretsManager(varsCtx, &source, rootKey) + return v.loadAwsSecretsManager(varsCtx, &source, ignoreMissing, rootKey) } else if source.Vault != nil { - return v.loadVault(varsCtx, &source, rootKey) + return v.loadVault(varsCtx, &source, ignoreMissing, rootKey) } return fmt.Errorf("invalid vars source") } @@ -105,7 +114,11 @@ func (v *VarsLoader) mergeVars(varsCtx *VarsCtx, newVars *uo.UnstructuredObject, } } -func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, searchDirs []string, rootKey string) error { +func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, ignoreMissing bool, searchDirs []string, rootKey string) error { + if ignoreMissing && !utils.Exists(path) { + return nil + } + rendered, err := varsCtx.RenderFile(path, searchDirs) if err != nil { return fmt.Errorf("failed to render vars file %s: %w", path, err) @@ -139,7 +152,7 @@ func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, searchDirs []string return nil } -func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { +func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool, rootKey string) error { newVars := uo.New() err := source.SystemEnvVars.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { envName, ok := it.Value().(string) @@ -164,6 +177,9 @@ func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, envValueStr = `""` } } else { + if ignoreMissing { + return nil + } return fmt.Errorf("environment variable %s not found for %s", envName, it.KeyPath().ToJsonPath()) } @@ -186,27 +202,39 @@ func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, return nil } -func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { +func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool, rootKey string) error { if v.aws == nil { return fmt.Errorf("no AWS client factory provided") } secret, err := aws.GetAwsSecretsManagerSecret(v.aws, source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) if err != nil { + var aerr awserr.Error + if errors2.As(err, &aerr) { + if ignoreMissing && aerr.Code() == secretsmanager.ErrCodeResourceNotFoundException { + return nil + } + } return err } return v.loadFromString(varsCtx, secret, "awsSecretsManager", rootKey) } -func (v *VarsLoader) loadVault(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { +func (v *VarsLoader) loadVault(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool, rootKey string) error { secret, err := vault.GetSecret(source.Vault.Address, source.Vault.Path) if err != nil { return err } - return v.loadFromString(varsCtx, secret, "vault", rootKey) + if secret == nil { + if ignoreMissing { + return nil + } + return fmt.Errorf("the specified vault secret was not found") + } + return v.loadFromString(varsCtx, *secret, "vault", rootKey) } -func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, rootKey string) error { +func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, ignoreMissing bool, rootKey string) error { ge, err := v.rp.GetEntry(gitFile.Url) if err != nil { return err @@ -217,10 +245,10 @@ func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, roo return fmt.Errorf("failed to load vars from git repository %s: %w", gitFile.Url.String(), err) } - return v.loadFile(varsCtx, gitFile.Path, []string{clonedDir}, rootKey) + return v.loadFile(varsCtx, gitFile.Path, ignoreMissing, []string{clonedDir}, rootKey) } -func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, key string, rootKey string, base64Decode bool) error { +func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, key string, rootKey string, ignoreMissing bool, base64Decode bool) error { if v.k == nil { return fmt.Errorf("loading vars from cluster is disabled") } @@ -231,6 +259,9 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo if varsSource.Name != "" { o, _, err = v.k.GetSingleObject(k8s2.NewObjectRef("", "v1", kind, varsSource.Name, varsSource.Namespace)) if err != nil { + if ignoreMissing && errors.IsNotFound(err) { + return nil + } return err } } else { @@ -243,6 +274,9 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo return err } if len(objs) == 0 { + if ignoreMissing { + return nil + } return fmt.Errorf("no object found with labels %v", varsSource.Labels) } if len(objs) > 1 { diff --git a/pkg/vars/vars_loader_http.go b/pkg/vars/vars_loader_http.go index 2f70cd260..7de4ca0a2 100644 --- a/pkg/vars/vars_loader_http.go +++ b/pkg/vars/vars_loader_http.go @@ -14,7 +14,7 @@ import ( "strings" ) -func (v *VarsLoader) doHttp(httpSource *types.VarsSourceHttp, username string, password string) (*http.Response, string, error) { +func (v *VarsLoader) doHttp(httpSource *types.VarsSourceHttp, ignoreMissing bool, username string, password string) (*http.Response, string, error) { client := &http.Client{ Transport: ntlmssp.Negotiator{ RoundTripper: &http.Transport{ @@ -64,8 +64,8 @@ func (v *VarsLoader) doHttp(httpSource *types.VarsSourceHttp, username string, p return resp, string(respBody), nil } -func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, rootKey string) error { - resp, respBody, err := v.doHttp(source.Http, "", "") +func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool, rootKey string) error { + resp, respBody, err := v.doHttp(source.Http, ignoreMissing, "", "") if err != nil && resp != nil && resp.StatusCode == http.StatusUnauthorized { chgs := challenge.ResponseChallenges(resp) if len(chgs) == 0 { @@ -95,11 +95,14 @@ func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, rootKe v.credentialsCache[credsKey] = creds } - resp, respBody, err = v.doHttp(source.Http, creds.username, creds.password) + resp, respBody, err = v.doHttp(source.Http, ignoreMissing, creds.username, creds.password) if err != nil { return err } } else if err != nil { + if ignoreMissing && resp != nil && resp.StatusCode == http.StatusNotFound { + return nil + } return err } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index ecf2e006e..b4d4a0f38 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -27,6 +27,7 @@ import ( "net/url" "os" "path/filepath" + "strings" "testing" ) @@ -88,6 +89,15 @@ func TestVarsLoader_File(t *testing.T) { v, _, _ := vc.Vars.GetNestedInt("test1", "test2") assert.Equal(t, int64(42), v) }) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + b := true + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + File: utils.StrPtr("test-missing.yaml"), + }, []string{d}, "") + assert.NoError(t, err) + }) } func TestVarsLoader_SopsFile(t *testing.T) { @@ -177,6 +187,19 @@ func TestVarsLoader_Git(t *testing.T) { v, _, _ := vc.Vars.GetNestedInt("test1", "test2") assert.Equal(t, int64(42), v) }) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + url, _ := git_url.Parse(gs.GitUrl("repo")) + b := true + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + Git: &types.VarsSourceGit{ + Url: *url, + Path: "test-missing.yaml", + }, + }, nil, "") + assert.NoError(t, err) + }) } func TestVarsLoader_GitBranch(t *testing.T) { @@ -259,6 +282,19 @@ func TestVarsLoader_ClusterConfigMap(t *testing.T) { }, nil, "") assert.EqualError(t, err, "key vars1 not found in ns/ConfigMap/cm on cluster") }, &cm) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + b := true + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + ClusterConfigMap: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "cm-missing", + Namespace: "ns", + Key: "vars", + }, + }, nil, "") + assert.NoError(t, err) + }, &cm) } func TestVarsLoader_ClusterSecret(t *testing.T) { @@ -302,6 +338,19 @@ func TestVarsLoader_ClusterSecret(t *testing.T) { }, nil, "") assert.EqualError(t, err, "key vars1 not found in ns/Secret/s on cluster") }, &secret) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + b := true + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + ClusterSecret: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "s-missing", + Namespace: "ns", + Key: "vars", + }, + }, nil, "") + assert.NoError(t, err) + }, &secret) } func TestVarsLoader_K8sObjectLabels(t *testing.T) { @@ -345,6 +394,19 @@ func TestVarsLoader_K8sObjectLabels(t *testing.T) { v, _, _ := vc.Vars.GetNestedInt("test3", "test4") assert.Equal(t, int64(43), v) }, &cm1, &cm2) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + b := true + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + ClusterConfigMap: &types.VarsSourceClusterConfigMapOrSecret{ + Labels: map[string]string{"label2": "value-missing"}, + Namespace: "ns", + Key: "vars", + }, + }, nil, "") + assert.NoError(t, err) + }, &cm1, &cm2) } func TestVarsLoader_SystemEnv(t *testing.T) { @@ -398,17 +460,38 @@ func TestVarsLoader_SystemEnv(t *testing.T) { }, nil, "") assert.EqualError(t, err, "environment variable TEST5 not found for test5") }) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + b := true + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + SystemEnvVars: uo.FromMap(map[string]interface{}{ + "test1": "TEST-MISSING", + }), + }, nil, "") + assert.NoError(t, err) + + _, ok, _ := vc.Vars.GetNestedField("test1") + assert.False(t, ok) + }) } func TestVarsLoader_Http_GET(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, _ = w.Write([]byte(`{"test1": {"test2": 42}}`)) + if strings.HasSuffix(r.URL.Path, "/ok") { + _, _ = w.Write([]byte(`{"test1": {"test2": 42}}`)) + } else if strings.HasSuffix(r.URL.Path, "/error") { + http.Error(w, "error", http.StatusInternalServerError) + } else { + http.NotFound(w, r) + } })) defer ts.Close() - u, _ := url.Parse(ts.URL) - testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + u, _ := url.Parse(ts.URL) + u.Path += "/ok" + err := vl.LoadVars(vc, &types.VarsSource{ Http: &types.VarsSourceHttp{ Url: types.YamlUrl{URL: *u}, @@ -419,6 +502,33 @@ func TestVarsLoader_Http_GET(t *testing.T) { v, _, _ := vc.Vars.GetNestedInt("test1", "test2") assert.Equal(t, int64(42), v) }) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + b := true + u, _ := url.Parse(ts.URL) + u.Path += "/missing" + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + Http: &types.VarsSourceHttp{ + Url: types.YamlUrl{URL: *u}, + }, + }, nil, "") + assert.NoError(t, err) + }) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + b := true + u, _ := url.Parse(ts.URL) + u.Path += "/error" + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + Http: &types.VarsSourceHttp{ + Url: types.YamlUrl{URL: *u}, + }, + }, nil, "") + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed with status code 500") + }) } func TestVarsLoader_Http_POST(t *testing.T) { @@ -543,4 +653,20 @@ func TestVarsLoader_AwsSecretsManager(t *testing.T) { v, _, _ := vc.Vars.GetNestedInt("test1", "test2") assert.Equal(t, int64(42), v) }) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + aws.Secrets = map[string]string{ + "secret": `{"test1": {"test2": 42}}`, + } + + b := true + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + AwsSecretsManager: &types.VarsSourceAwsSecretsManager{ + SecretName: "missing", + Region: utils.StrPtr("eu-central1"), + }, + }, nil, "") + assert.NoError(t, err) + }) } diff --git a/pkg/vars/vault/secrets.go b/pkg/vars/vault/secrets.go index 49f04618f..7d4fb9012 100644 --- a/pkg/vars/vault/secrets.go +++ b/pkg/vars/vault/secrets.go @@ -13,19 +13,20 @@ var httpClient = &http.Client{ Timeout: 15 * time.Second, } -func GetSecret(server string, path string) (string, error) { +func GetSecret(server string, path string) (*string, error) { client, err := api.NewClient(&api.Config{Address: server, HttpClient: httpClient}) if err != nil { - return "", fmt.Errorf("failed to create vault %s client", server) + return nil, fmt.Errorf("failed to create vault %s client", server) } secret, err := client.Logical().Read(path) if err != nil { - return "", fmt.Errorf("reading from vault failed: %v", err) + return nil, fmt.Errorf("reading from vault failed: %v", err) } if secret == nil || secret.Data == nil { - return "", fmt.Errorf("the specified vault secret was not found") + return nil, nil } data, _ := secret.Data["data"].(map[string]interface{}) jsonData, _ := json.Marshal(data) - return string(jsonData), nil + ret := string(jsonData) + return &ret, nil } From 63182574f360610bceebaa86cd0dad9a303f3b65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 22:08:14 +0000 Subject: [PATCH 0650/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.154 to 1.44.157 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.154 to 1.44.157. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.154...v1.44.157) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ab79f6f8b..957530805 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.154 + github.com/aws/aws-sdk-go v1.44.157 github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index 4b69beba1..c627a02b2 100644 --- a/go.sum +++ b/go.sum @@ -131,8 +131,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.154 h1:2TaEC8JIM/t7Fu+FBmdOBK5jFUAVL07tbpfJsLuVo3Q= -github.com/aws/aws-sdk-go v1.44.154/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.157 h1:JVBPpEWC8+yA7CbfAuTl/ZFFlHS3yoqWFqxFyTCISwg= +github.com/aws/aws-sdk-go v1.44.157/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From aae82d0e28b2eaad9028aeef489d59eccd5b8d58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 22:08:33 +0000 Subject: [PATCH 0651/2268] chore(deps): Bump k8s.io/api from 0.25.4 to 0.26.0 Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.25.4 to 0.26.0. - [Release notes](https://github.com/kubernetes/api/releases) - [Commits](https://github.com/kubernetes/api/compare/v0.25.4...v0.26.0) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ab79f6f8b..79fcb8c5e 100644 --- a/go.mod +++ b/go.mod @@ -47,9 +47,9 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.2 - k8s.io/api v0.25.4 + k8s.io/api v0.26.0 k8s.io/apiextensions-apiserver v0.25.4 - k8s.io/apimachinery v0.25.4 + k8s.io/apimachinery v0.26.0 k8s.io/client-go v0.25.4 k8s.io/klog/v2 v2.80.1 sigs.k8s.io/kustomize/api v0.12.1 // indirect diff --git a/go.sum b/go.sum index 4b69beba1..9b23f2e91 100644 --- a/go.sum +++ b/go.sum @@ -1331,12 +1331,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs= -k8s.io/api v0.25.4/go.mod h1:IG2+RzyPQLllQxnhzD8KQNEu4c4YvyDTpSMztf4A0OQ= +k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= +k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= k8s.io/apiextensions-apiserver v0.25.4 h1:7hu9pF+xikxQuQZ7/30z/qxIPZc2J1lFElPtr7f+B6U= k8s.io/apiextensions-apiserver v0.25.4/go.mod h1:bkSGki5YBoZWdn5pWtNIdGvDrrsRWlmnvl9a+tAw5vQ= -k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= -k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= +k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= k8s.io/apiserver v0.25.4 h1:/3TwZcgLqX7wUxq7TtXOUqXeBTwXIblVMQdhR5XZ7yo= k8s.io/apiserver v0.25.4/go.mod h1:rPcm567XxjOnnd7jedDUnGJGmDGAo+cT6H7QHAN+xV0= k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= From 096659c65d305a78f3ddbc2e956f4907eba642c2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 10 Dec 2022 00:50:36 +0100 Subject: [PATCH 0652/2268] tests: Actually collect and return stdout --- e2e/test-utils/project.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index 9a87be0f1..441d8250c 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -422,22 +422,28 @@ func (p *TestProject) KluctlExecute(argsIn ...string) (string, string, error) { p.t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) var m sync.Mutex - stdout := bytes.NewBuffer(nil) - stderr := bytes.NewBuffer(nil) + stdoutBuf := bytes.NewBuffer(nil) + stdout := status.NewLineRedirector(func(line string) { + m.Lock() + defer m.Unlock() + p.t.Log(line) + stdoutBuf.WriteString(line + "\n") + }) + stderrBuf := bytes.NewBuffer(nil) ctx := context.Background() ctx = utils.WithTmpBaseDir(ctx, p.t.TempDir()) - ctx = commands.WithStdStreams(ctx, stdout, stderr) + ctx = commands.WithStdStreams(ctx, stdout, stderrBuf) sh := status.NewSimpleStatusHandler(func(message string) { m.Lock() defer m.Unlock() p.t.Log(message) - stderr.WriteString(message + "\n") + stderrBuf.WriteString(message + "\n") }, false, true) defer sh.Stop() ctx = status.NewContext(ctx, sh) err := commands.Execute(ctx, args, nil) - return stdout.String(), stderr.String(), err + return stdoutBuf.String(), stderrBuf.String(), err } func (p *TestProject) Kluctl(argsIn ...string) (string, string, error) { From 9f7d5cc8937f46e420340e48aeabbdae357820ff Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 10 Dec 2022 00:51:17 +0100 Subject: [PATCH 0653/2268] feat: Don't force-replace objects marked with kluctl.io/skip-delete --- e2e/skip_delete_test.go | 132 +++++++++++++++++++++++++++ e2e/utils.go | 10 ++ pkg/deployment/utils/apply_utils.go | 16 +++- pkg/deployment/utils/delete_utils.go | 26 ++++-- 4 files changed, 171 insertions(+), 13 deletions(-) create mode 100644 e2e/skip_delete_test.go diff --git a/e2e/skip_delete_test.go b/e2e/skip_delete_test.go new file mode 100644 index 000000000..ba7802e2e --- /dev/null +++ b/e2e/skip_delete_test.go @@ -0,0 +1,132 @@ +package e2e + +import ( + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/stretchr/testify/assert" + "strings" + "testing" +) + +func TestSkipDelete(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + addConfigMapDeployment(p, "cm1", map[string]string{}, resourceOpts{ + name: "cm1", + namespace: p.TestSlug(), + }) + addConfigMapDeployment(p, "cm2", map[string]string{}, resourceOpts{ + name: "cm2", + namespace: p.TestSlug(), + }) + addConfigMapDeployment(p, "cm3", map[string]string{}, resourceOpts{ + name: "cm3", + namespace: p.TestSlug(), + annotations: map[string]string{ + "kluctl.io/skip-delete": "true", + }, + }) + addConfigMapDeployment(p, "cm4", map[string]string{}, resourceOpts{ + name: "cm4", + namespace: p.TestSlug(), + annotations: map[string]string{ + "helm.sh/resource-policy": "keep", + }, + }) + + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.TestSlug(), "cm1") + cm2 := assertConfigMapExists(t, k, p.TestSlug(), "cm2") + assertConfigMapExists(t, k, p.TestSlug(), "cm3") + assertConfigMapExists(t, k, p.TestSlug(), "cm4") + + cm2.SetK8sAnnotation("kluctl.io/skip-delete", "true") + updateObject(t, k, cm2) + + p.KluctlMust("delete", "--yes", "-t", "test") + assertConfigMapNotExists(t, k, p.TestSlug(), "cm1") + cm2 = assertConfigMapExists(t, k, p.TestSlug(), "cm2") + assertConfigMapExists(t, k, p.TestSlug(), "cm3") + assertConfigMapExists(t, k, p.TestSlug(), "cm4") + + p.KluctlMust("deploy", "--yes", "-t", "test") + cm1 := assertConfigMapExists(t, k, p.TestSlug(), "cm1") + cm1.SetK8sAnnotation("kluctl.io/skip-delete", "true") + cm2.SetK8sAnnotation("kluctl.io/skip-delete", "false") + updateObject(t, k, cm1) + updateObject(t, k, cm2) + p.DeleteKustomizeDeployment("cm1") + p.DeleteKustomizeDeployment("cm2") + p.DeleteKustomizeDeployment("cm3") + p.KluctlMust("prune", "--yes", "-t", "test") + cm1 = assertConfigMapExists(t, k, p.TestSlug(), "cm1") + assertConfigMapNotExists(t, k, p.TestSlug(), "cm2") + assertConfigMapExists(t, k, p.TestSlug(), "cm3") + assertConfigMapExists(t, k, p.TestSlug(), "cm4") +} + +func TestForceReplaceSkipDelete(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + addConfigMapDeployment(p, "cm1", map[string]string{ + "k1": "v1", + }, resourceOpts{ + name: "cm1", + namespace: p.TestSlug(), + annotations: map[string]string{ + "kluctl.io/skip-delete": "true", + }, + }) + + p.KluctlMust("deploy", "--yes", "-t", "test") + cm1 := assertConfigMapExists(t, k, p.TestSlug(), "cm1") + assert.Equal(t, map[string]any{ + "k1": "v1", + }, cm1.Object["data"]) + + p.UpdateYaml("cm1/configmap-cm1.yml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField("v2", "data", "k1") + return nil + }, "") + + p.KluctlMust("deploy", "--yes", "-t", "test") + cm1 = assertConfigMapExists(t, k, p.TestSlug(), "cm1") + assert.Equal(t, map[string]any{ + "k1": "v2", + }, cm1.Object["data"]) + + invalidLabel := "invalid_label_" + strings.Repeat("x", 63) + p.UpdateYaml("cm1/configmap-cm1.yml", func(o *uo.UnstructuredObject) error { + o.SetK8sLabel(invalidLabel, "invalid_label") + return nil + }, "") + stdout, _, err := p.Kluctl("deploy", "--yes", "-t", "test") + assert.Error(t, err) + assert.Contains(t, stdout, "invalid: metadata.labels") + // make sure it did not try to replace it + assertConfigMapExists(t, k, p.TestSlug(), "cm1") + + stdout, stderr, err := p.Kluctl("deploy", "--yes", "-t", "test", "--force-replace-on-error") + assert.Error(t, err) + assert.Contains(t, stdout, "retrying with replace instead of patch") + assert.Contains(t, stderr, "skipped forced replace") + assert.Contains(t, stdout, "invalid: metadata.labels") + // make sure it did not try to replace it + assertConfigMapExists(t, k, p.TestSlug(), "cm1") +} diff --git a/e2e/utils.go b/e2e/utils.go index 5d4a234a0..1e3b6c61b 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -4,10 +4,12 @@ import ( "context" "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" "reflect" "testing" ) @@ -53,3 +55,11 @@ func assertNestedFieldEquals(t *testing.T, o *uo.UnstructuredObject, expected in t.Fatalf("%v != %v", v, expected) } } + +func updateObject(t *testing.T, k *test_utils.EnvTestCluster, o *uo.UnstructuredObject) { + _, err := k.DynamicClient.Resource(schema.GroupVersionResource{ + Version: "v1", + Resource: "configmaps", + }).Namespace(o.GetK8sNamespace()).Update(context.Background(), o.ToUnstructured(), metav1.UpdateOptions{}) + assert.NoError(t, err) +} diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 2bee3e50d..78bc51836 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -180,7 +180,7 @@ func (a *ApplyUtil) DeleteObject(ref k8s2.ObjectRef, hook bool) bool { return true } -func (a *ApplyUtil) retryApplyForceReplace(x *uo.UnstructuredObject, hook bool, applyError error) { +func (a *ApplyUtil) retryApplyForceReplace(x *uo.UnstructuredObject, hook bool, remoteObject *uo.UnstructuredObject, applyError error) { ref := x.GetK8sRef() if !a.o.ForceReplaceOnError { @@ -188,6 +188,16 @@ func (a *ApplyUtil) retryApplyForceReplace(x *uo.UnstructuredObject, hook bool, return } + skipDelete := isSkipDelete(x) + if remoteObject != nil { + skipDelete = skipDelete || isSkipDelete(remoteObject) + } + if skipDelete { + status.Warning(a.ctx, "skipped forced replace of %s", ref.String()) + a.HandleError(ref, applyError) + return + } + warn := fmt.Errorf("patching %s failed, retrying by deleting and re-applying", ref.String()) a.HandleWarning(ref, warn) status.Warning(a.ctx, warn.Error()) @@ -235,7 +245,7 @@ func (a *ApplyUtil) retryApplyWithReplace(x *uo.UnstructuredObject, hook bool, r r, apiWarnings, err := a.k.UpdateObject(x, o) a.handleApiWarnings(ref, apiWarnings) if err != nil { - a.retryApplyForceReplace(x, hook, err) + a.retryApplyForceReplace(x, hook, remoteObject, err) return } a.handleResult(r, hook) @@ -334,8 +344,6 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo a.HandleError(ref, err) } else if errors.IsConflict(err) { a.retryApplyWithConflicts(x, hook, remoteObject, err) - } else if errors.IsInternalError(err) { - a.HandleError(ref, err) } else { a.retryApplyWithReplace(x, hook, remoteObject, err) } diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index babaa5a4b..d79f5d229 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -47,6 +47,22 @@ func objectRefForExclusion(k *k8s.K8sCluster, ref k8s2.ObjectRef) k8s2.ObjectRef return ref } +func isSkipDelete(o *uo.UnstructuredObject) bool { + if utils.ParseBoolOrFalse(o.GetK8sAnnotation("kluctl.io/skip-delete")) { + return true + } + + helmResourcePolicy := o.GetK8sAnnotation("helm.sh/resource-policy") + if helmResourcePolicy != nil && *helmResourcePolicy == "keep" { + // warning, this is currently not how Helm handles it. Helm will only respect annotations set in the Chart + // itself, while Kluctl will also respect it when manually set on the live resource + // See: https://github.com/helm/helm/issues/8132 + return true + } + + return false +} + func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, apiFilter []string, inclusionHasTags bool, excludedObjects map[k8s2.ObjectRef]bool) ([]*uo.UnstructuredObject, error) { filterFunc := func(ar *v1.APIResource) bool { if len(apiFilter) == 0 { @@ -77,15 +93,7 @@ func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, managedFields := o.GetK8sManagedFields() // exclude when explicitly requested - if utils.ParseBoolOrFalse(o.GetK8sAnnotation("kluctl.io/skip-delete")) { - continue - } - - helmResourcePolicy := o.GetK8sAnnotation("helm.sh/resource-policy") - if helmResourcePolicy != nil && *helmResourcePolicy == "keep" { - // warning, this is currently not how Helm handles it. Helm will only respect annotations set in the Chart - // itself, while Kluctl will also respect it when manually set on the live resource - // See: https://github.com/helm/helm/issues/8132 + if isSkipDelete(o) { continue } From 5f0338566fb26048e198b9c99acd388c95994fde Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 10 Dec 2022 01:01:54 +0100 Subject: [PATCH 0654/2268] chore: Upgrade go-git to v5.5.0 --- go.mod | 6 ++-- go.sum | 51 +++++++++++++++--------------- pkg/git/auth/list_auth_provider.go | 12 +++++-- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index ab79f6f8b..e3b62184a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/fluxcd/pkg/kustomize v0.11.0 - github.com/go-git/go-git/v5 v5.4.2 + github.com/go-git/go-git/v5 v5.5.0 github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 @@ -88,7 +88,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad // indirect + github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect @@ -195,6 +195,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect + github.com/pjbgf/sha1cd v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -206,6 +207,7 @@ require ( github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect + github.com/skeema/knownhosts v1.1.0 // indirect github.com/spf13/afero v1.9.2 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index 4b69beba1..7eb91241c 100644 --- a/go.sum +++ b/go.sum @@ -96,17 +96,14 @@ github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmy github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad h1:QeeqI2zxxgZVe11UrYFXXx6gVxPVF40ygekjBzEg4XY= -github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= +github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= +github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= @@ -117,8 +114,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy 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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -217,7 +214,6 @@ github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9 github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.10.0 h1:X4gma4HM7hFm6WMeAsTfqA0GOfdNoCzBIkHGoRLGXuM= github.com/emicklei/go-restful/v3 v3.10.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -246,25 +242,23 @@ github.com/fluxcd/pkg/apis/kustomize v0.7.0 h1:X2htBmJ91nGYv4d93gin665MFWKNGiNwU github.com/fluxcd/pkg/apis/kustomize v0.7.0/go.mod h1:Mu+KdktsEKWA4l/33CZdY5lB4hz51mqfcLzBZSwAqVg= github.com/fluxcd/pkg/kustomize v0.11.0 h1:zseS9LRUuzhP/7KamccmsOgYpJAdhqtsf+2wN/CHF3I= github.com/fluxcd/pkg/kustomize v0.11.0/go.mod h1:awHID4OKe2/WAfTFg4u0fURXZPUkrIslSZNSPX9MEFQ= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= -github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= -github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= -github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= +github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= +github.com/go-git/go-git/v5 v5.5.0 h1:StO/ASRvk1Pp74tr7XQ0pQwKlCFignzzTF/NLKdQzUE= +github.com/go-git/go-git/v5 v5.5.0/go.mod h1:g456XI30HAdt7GQtIf8JR6GDAdULGaR4KtfFtQa0uTg= 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= @@ -495,7 +489,6 @@ github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq 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.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 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= @@ -531,7 +524,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -687,6 +679,8 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pjbgf/sha1cd v0.2.0 h1:gIsJVwjbRviE4gydidGztxH1IlJQoYBcCrwG4Dz8wvM= +github.com/pjbgf/sha1cd v0.2.0/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= 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= @@ -761,13 +755,14 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= +github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -814,7 +809,7 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaU github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 h1:3kHD8uZqiYes9JHdd3FzuyUbG10g9Bp9EOfqkSHIhg4= github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -866,7 +861,6 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -876,14 +870,16 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -962,7 +958,6 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -970,7 +965,9 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1018,7 +1015,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1053,12 +1049,10 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/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-20210502180810-71e4cd670f79/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= @@ -1075,15 +1069,20 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/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-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index e11cb176c..44e75c477 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -6,6 +6,7 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport/ssh" "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" + ssh2 "golang.org/x/crypto/ssh" "strings" ) @@ -79,13 +80,20 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) if err != nil { a.MessageCallbacks.Trace("ListAuthProvider: failed to parse private key: %v", err) } else { - pk.HostKeyCallback = buildVerifyHostCallback(a.MessageCallbacks, e.KnownHosts) + hostKeyCallback := buildVerifyHostCallback(a.MessageCallbacks, e.KnownHosts) return AuthMethodAndCA{ AuthMethod: pk, Hash: func() ([]byte, error) { return buildHash(pk.Signer) }, - ClientConfig: pk.ClientConfig, + ClientConfig: func() (*ssh2.ClientConfig, error) { + ccfg, err := pk.ClientConfig() + if err != nil { + return nil, err + } + ccfg.HostKeyCallback = hostKeyCallback + return ccfg, nil + }, } } } else { From c9aa3d1628b8687165e20c26e4a642428fe5aa02 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 10 Dec 2022 01:07:59 +0100 Subject: [PATCH 0655/2268] tests: Fix race condition --- e2e/test-utils/project.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index 441d8250c..b97497f98 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -440,9 +440,16 @@ func (p *TestProject) KluctlExecute(argsIn ...string) (string, string, error) { p.t.Log(message) stderrBuf.WriteString(message + "\n") }, false, true) - defer sh.Stop() + defer func() { + if sh != nil { + sh.Stop() + } + }() ctx = status.NewContext(ctx, sh) err := commands.Execute(ctx, args, nil) + sh.Stop() + sh = nil + _ = stdout.Close() return stdoutBuf.String(), stderrBuf.String(), err } From 0e712d3c1fa009abf8fc9f3c3fb130c6cb727115 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 10 Dec 2022 01:21:30 +0100 Subject: [PATCH 0656/2268] fix: Wait for lineRedirector to finish when calling Close() --- pkg/status/utils.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pkg/status/utils.go b/pkg/status/utils.go index cc5002217..4930ddf2f 100644 --- a/pkg/status/utils.go +++ b/pkg/status/utils.go @@ -5,8 +5,23 @@ import ( "io" ) +type lineRedirector struct { + io.WriteCloser + done chan bool +} + +func (lr *lineRedirector) Close() error { + err := lr.WriteCloser.Close() + if err != nil { + return err + } + <-lr.done + return nil +} + func NewLineRedirector(cb func(line string)) io.WriteCloser { r, w := io.Pipe() + done := make(chan bool, 1) go func() { br := bufio.NewReader(r) @@ -18,9 +33,10 @@ func NewLineRedirector(cb func(line string)) io.WriteCloser { } cb(msg) } + done <- true }() - return w + return &lineRedirector{w, done} } type replaceRReader struct { From 56eb1e32c5403f6155dc2c806950388d76c19f54 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 10 Dec 2022 01:29:25 +0100 Subject: [PATCH 0657/2268] fix: Upgrade github.com/pjbgf/sha1cd to 0.2.3 to fix !cgo compilation --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 49328dda2..484ebe921 100644 --- a/go.mod +++ b/go.mod @@ -195,7 +195,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect - github.com/pjbgf/sha1cd v0.2.0 // indirect + github.com/pjbgf/sha1cd v0.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect diff --git a/go.sum b/go.sum index 07e98a652..5ce647007 100644 --- a/go.sum +++ b/go.sum @@ -679,8 +679,9 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pjbgf/sha1cd v0.2.0 h1:gIsJVwjbRviE4gydidGztxH1IlJQoYBcCrwG4Dz8wvM= github.com/pjbgf/sha1cd v0.2.0/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= +github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= +github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= 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= From c534527a165594e11acaef426f8a387c39e278bf Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 10 Dec 2022 21:57:26 +0100 Subject: [PATCH 0658/2268] fix: Fix potential crash in readCachedResponse --- pkg/registries/registries.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index ffda2e3f8..b372b06b6 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -337,6 +337,10 @@ func (rh *RegistryHelper) readCachedResponse(key string) []byte { } res, err := http.ReadResponse(bufio.NewReader(bytes.NewReader(b)), nil) + if err != nil { + status.Warning(rh.ctx, "readCachedResponse failed: %v", err) + return nil + } if strings.HasPrefix(res.Header.Get("Content-Type"), "application/json") { jb, err := io.ReadAll(res.Body) From 85888a3370ba4a05e32dd37a0113dbfd3fa29cea Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Dec 2022 09:23:06 +0100 Subject: [PATCH 0659/2268] refactor: Move HelmChart into own package --- cmd/kluctl/commands/cmd_helm_pull.go | 4 ++-- cmd/kluctl/commands/cmd_helm_update.go | 12 ++++++------ pkg/deployment/deployment_item.go | 5 +++-- pkg/deployment/shared_context.go | 3 ++- pkg/{deployment => helm}/helm_chart.go | 2 +- pkg/kluctl_project/target_context.go | 3 ++- 6 files changed, 16 insertions(+), 13 deletions(-) rename pkg/{deployment => helm}/helm_chart.go (99%) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index ab2d7bf66..1dd11609c 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/deployment" git2 "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/sync/semaphore" @@ -84,7 +84,7 @@ func doPull(ctx context.Context, statusPrefix string, p string, helmCredentials return err } - chart, err := deployment.NewHelmChart(p) + chart, err := helm.NewHelmChart(p) if err != nil { return doError(err) } diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 225caec92..daafebf19 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -6,8 +6,8 @@ import ( "github.com/go-git/go-git/v5" "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/deployment" git2 "github.com/kluctl/kluctl/v2/pkg/git" + "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/sync/semaphore" @@ -51,7 +51,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { type updatedChart struct { path string - chart *deployment.HelmChart + chart *helm.HelmChart newVersion string oldVersion string pullSuccess bool @@ -127,19 +127,19 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { return errs.ErrorOrNil() } -func (cmd *helmUpdateCmd) doCheckUpdate(ctx context.Context, gitRootPath string, p string) (*deployment.HelmChart, string, bool, error) { +func (cmd *helmUpdateCmd) doCheckUpdate(ctx context.Context, gitRootPath string, p string) (*helm.HelmChart, string, bool, error) { statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) if err != nil { return nil, "", false, err } s := status.Start(ctx, "%s: Checking for updates", statusPrefix) - doError := func(err error) (*deployment.HelmChart, string, bool, error) { + doError := func(err error) (*helm.HelmChart, string, bool, error) { s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return nil, "", false, err } - chart, err := deployment.NewHelmChart(p) + chart, err := helm.NewHelmChart(p) if err != nil { return doError(err) } @@ -164,7 +164,7 @@ func (cmd *helmUpdateCmd) doCheckUpdate(ctx context.Context, gitRootPath string, return chart, newVersion, updated, nil } -func (cmd *helmUpdateCmd) pullAndCommitChart(ctx context.Context, gitRootPath string, chart *deployment.HelmChart, oldVersion string, newVersion string, mutex *sync.Mutex) error { +func (cmd *helmUpdateCmd) pullAndCommitChart(ctx context.Context, gitRootPath string, chart *helm.HelmChart, oldVersion string, newVersion string, mutex *sync.Mutex) error { statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(chart.ConfigFile)) if err != nil { return err diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 9051825d0..b63c7d704 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -3,6 +3,7 @@ package deployment import ( "fmt" "github.com/fluxcd/pkg/kustomize" + "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/status" @@ -192,7 +193,7 @@ func (di *DeploymentItem) renderHelmCharts() error { return err } - chart, err := NewHelmChart(p) + chart, err := helm.NewHelmChart(p) if err != nil { return err } @@ -417,7 +418,7 @@ func (di *DeploymentItem) generateKustomizationYaml(subDir string) (*uo.Unstruct if di.isHelmValuesYaml(de.Name()) { continue } else if di.isHelmChartYaml(de.Name()) { - c, err := NewHelmChart(filepath.Join(di.RenderedDir, subDir, de.Name())) + c, err := helm.NewHelmChart(filepath.Join(di.RenderedDir, subDir, de.Name())) if err != nil { return nil, err } diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index 7fce849ce..dec1c4571 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -2,6 +2,7 @@ package deployment import ( "context" + "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops" @@ -15,7 +16,7 @@ type SharedContext struct { RP *repocache.GitRepoCache SopsDecrypter sops.SopsDecrypter VarsLoader *vars.VarsLoader - HelmCredentials HelmCredentialsProvider + HelmCredentials helm.HelmCredentialsProvider RenderDir string SealedSecretsDir string diff --git a/pkg/deployment/helm_chart.go b/pkg/helm/helm_chart.go similarity index 99% rename from pkg/deployment/helm_chart.go rename to pkg/helm/helm_chart.go index cf1cb825c..5c79c616e 100644 --- a/pkg/deployment/helm_chart.go +++ b/pkg/helm/helm_chart.go @@ -1,4 +1,4 @@ -package deployment +package helm import ( "context" diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index bacf2d7d5..f07436014 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" @@ -37,7 +38,7 @@ type TargetContextParams struct { ForSeal bool Images *deployment.Images Inclusion *utils.Inclusion - HelmCredentials deployment.HelmCredentialsProvider + HelmCredentials helm.HelmCredentialsProvider RenderOutputDir string } From cc8b1ebf9149d0d6e17242b696b095354ed8681a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Dec 2022 09:28:50 +0100 Subject: [PATCH 0660/2268] fix: Add omitempty to UpdateConstraints --- pkg/types/helm_chart.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/types/helm_chart.go b/pkg/types/helm_chart.go index 7cf645597..9ebc00118 100644 --- a/pkg/types/helm_chart.go +++ b/pkg/types/helm_chart.go @@ -5,7 +5,7 @@ type HelmChartConfig2 struct { CredentialsId *string `yaml:"credentialsId,omitempty"` ChartName *string `yaml:"chartName,omitempty"` ChartVersion *string `yaml:"chartVersion" validate:"required"` - UpdateConstraints *string `yaml:"updateConstraints"` + UpdateConstraints *string `yaml:"updateConstraints,omitempty"` ReleaseName string `yaml:"releaseName" validate:"required"` Namespace *string `yaml:"namespace,omitempty"` Output *string `yaml:"output,omitempty"` From ca06a858884a9bee5deeb004d202d07a68bf89f2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Dec 2022 09:36:30 +0100 Subject: [PATCH 0661/2268] feat: Store pre-pulled helm charts in central .helm-charts dir --- cmd/kluctl/commands/cmd_helm_pull.go | 97 ++++++--- cmd/kluctl/commands/cmd_helm_update.go | 275 +++++++++++++++++-------- pkg/deployment/deployment_item.go | 4 +- pkg/deployment/shared_context.go | 1 + pkg/helm/helm_chart.go | 125 ++++++++--- pkg/kluctl_project/project.go | 1 + pkg/kluctl_project/project_load.go | 1 + pkg/kluctl_project/target_context.go | 1 + 8 files changed, 356 insertions(+), 149 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 1dd11609c..cb9ac562a 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -5,12 +5,13 @@ import ( "fmt" "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/yaml" "golang.org/x/sync/semaphore" "io/fs" + "os" "path/filepath" "sync" ) @@ -32,15 +33,14 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { return err } - gitRootPath, err := git2.DetectGitRepositoryRoot(projectDir) - if err != nil { - return err + if !yaml.Exists(filepath.Join(projectDir, ".kluctl.yaml")) { + return fmt.Errorf("helm-pull can only be used on the root of a Kluctl project that must have a .kluctl.yaml file") } - var errs *multierror.Error - var wg sync.WaitGroup - var mutex sync.Mutex - sem := semaphore.NewWeighted(8) + baseChartsDir := filepath.Join(projectDir, ".helm-charts") + + chartsToPull := map[string]*helm.HelmChart{} + cleanupDirs := map[string][]string{} err = filepath.WalkDir(projectDir, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) @@ -48,24 +48,46 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { return nil } - statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) + chart, err := helm.NewHelmChart(baseChartsDir, p) if err != nil { return err } + chart.SetCredentials(&cmd.HelmCredentials) + + cleanupDir := chart.GetChartDir(false) + cleanupDirs[cleanupDir] = append(cleanupDirs[cleanupDir], *chart.Config.ChartVersion) + + if _, ok := chartsToPull[chart.GetChartDir(true)]; ok { + return nil + } + + chartsToPull[chart.GetChartDir(true)] = chart + return nil + }) + + var errs *multierror.Error + var wg sync.WaitGroup + var mutex sync.Mutex + sem := semaphore.NewWeighted(8) + + for _, chart := range chartsToPull { + chart := chart + statusPrefix := chart.GetChartName() + utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { - s := status.Start(ctx, "%s: Pulling Chart", statusPrefix) + s := status.Start(ctx, "%s: Pulling Chart with version %s", statusPrefix, *chart.Config.ChartVersion) defer s.Failed() - err := doPull(ctx, statusPrefix, p, cmd.HelmCredentials, s) + + err := chart.Pull(ctx) if err != nil { + s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return err } s.Success() return nil }) - - return nil - }) + } wg.Wait() if err != nil { errs = multierror.Append(errs, err) @@ -75,27 +97,34 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { return fmt.Errorf("command failed") } - return nil -} - -func doPull(ctx context.Context, statusPrefix string, p string, helmCredentials args.HelmCredentials, s *status.StatusContext) error { - doError := func(err error) error { - s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) - return err - } - - chart, err := helm.NewHelmChart(p) - if err != nil { - return doError(err) + for dir, versions := range cleanupDirs { + des, err := os.ReadDir(dir) + if err != nil { + return err + } + for _, de := range des { + if !de.IsDir() { + continue + } + found := false + for _, v := range versions { + if v == de.Name() { + found = true + break + } + } + if !found { + chartName := filepath.Base(dir) + s := status.Start(ctx, "%s: Removing unused version %s", chartName, de.Name()) + err = os.RemoveAll(filepath.Join(dir, de.Name())) + if err != nil { + s.Failed() + return err + } + s.Success() + } + } } - chart.SetCredentials(&helmCredentials) - - s.UpdateAndInfoFallback("%s: Pulling Chart %s with version %s", statusPrefix, chart.GetChartName(), *chart.Config.ChartVersion) - - err = chart.Pull(ctx) - if err != nil { - return doError(err) - } return nil } diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index daafebf19..98dd66f4c 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -10,6 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/yaml" "golang.org/x/sync/semaphore" "io/fs" "math/rand" @@ -33,30 +34,34 @@ func (cmd *helmUpdateCmd) Help() string { return `Optionally performs the actual upgrade and/or add a commit to version control.` } +type updatedChart struct { + charts []*helm.HelmChart + newVersion string +} + func (cmd *helmUpdateCmd) Run(ctx context.Context) error { projectDir, err := cmd.ProjectDir.GetProjectDir() if err != nil { return err } + if !yaml.Exists(filepath.Join(projectDir, ".kluctl.yaml")) { + return fmt.Errorf("helm-pull can only be used on the root of a Kluctl project that must have a .kluctl.yaml file") + } + gitRootPath, err := git2.DetectGitRepositoryRoot(projectDir) if err != nil { return err } + baseChartsDir := filepath.Join(projectDir, ".helm-charts") + var errs *multierror.Error var wg sync.WaitGroup var mutex sync.Mutex sem := semaphore.NewWeighted(8) - type updatedChart struct { - path string - chart *helm.HelmChart - newVersion string - oldVersion string - pullSuccess bool - } - var updatedCharts []*updatedChart + chartsToCheck := map[string]*updatedChart{} err = filepath.WalkDir(projectDir, func(p string, d fs.DirEntry, err error) error { fname := filepath.Base(p) @@ -64,8 +69,31 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { return nil } + chart, err := helm.NewHelmChart(baseChartsDir, p) + if err != nil { + return err + } + + chart.SetCredentials(&cmd.HelmCredentials) + + key := buildKey(chart, chart.Config.UpdateConstraints, nil) + if uc2, ok := chartsToCheck[key]; ok { + uc2.charts = append(uc2.charts, chart) + } else { + chartsToCheck[key] = &updatedChart{ + charts: []*helm.HelmChart{chart}, + } + } + return nil + }) + + toKeep := map[string]bool{} + chartsToPull := map[string]*updatedChart{} + + for _, uc := range chartsToCheck { + uc := uc utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { - chart, newVersion, updated, err := cmd.doCheckUpdate(ctx, gitRootPath, p) + newVersion, err := cmd.doQueryLatestVersion(ctx, uc.charts[0]) if err != nil { return err } @@ -73,22 +101,43 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { mutex.Lock() defer mutex.Unlock() - if !chart.Config.SkipUpdate && updated { - updatedCharts = append(updatedCharts, &updatedChart{ - path: p, - chart: chart, + key := buildKey(uc.charts[0], &newVersion, nil) + toKeep[key] = true + + uc2, ok := chartsToPull[key] + if !ok { + uc2 = &updatedChart{ newVersion: newVersion, - oldVersion: *chart.Config.ChartVersion, - }) + } + chartsToPull[key] = uc2 + } + + for _, chart := range uc.charts { + updated := newVersion != *chart.Config.ChartVersion + if updated && chart.Config.SkipUpdate { + status.Info(ctx, "%s: Skipping update to version %s", chart.GetChartName(), newVersion) + updated = false + } + if !updated { + toKeep[buildKey(chart, chart.Config.ChartVersion, nil)] = true + continue + } + if len(uc2.charts) == 0 { + status.Info(ctx, "%s: Chart has new version %s", chart.GetChartName(), newVersion) + } + uc2.charts = append(uc2.charts, chart) + } + + if len(uc2.charts) == 0 { + delete(chartsToPull, key) } + return nil }) - return nil - }) + } wg.Wait() - if err != nil { - errs = multierror.Append(errs, err) - return errs.ErrorOrNil() + if errs.ErrorOrNil() != nil { + return errs } if !cmd.Upgrade { @@ -99,19 +148,19 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { sem = semaphore.NewWeighted(1) } - for _, uc := range updatedCharts { + for _, uc := range chartsToPull { uc := uc utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { if cmd.Interactive { - statusPrefix, _ := filepath.Rel(gitRootPath, filepath.Dir(uc.path)) - if !status.AskForConfirmation(ctx, fmt.Sprintf("%s: Do you want to upgrade Chart %s from version %s to %s?", - statusPrefix, uc.chart.GetChartName(), uc.oldVersion, uc.newVersion)) { + statusPrefix := uc.charts[0].GetChartName() + if !status.AskForConfirmation(ctx, fmt.Sprintf("%s: Do you want to upgrade Chart %s to version %s?", + statusPrefix, uc.charts[0].GetChartName(), uc.newVersion)) { return nil } } - err := cmd.pullAndCommitChart(ctx, gitRootPath, uc.chart, uc.oldVersion, uc.newVersion, &mutex) + err := cmd.pullAndCommitCharts(ctx, gitRootPath, uc, toKeep, &mutex) if err != nil { return err } @@ -127,99 +176,155 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { return errs.ErrorOrNil() } -func (cmd *helmUpdateCmd) doCheckUpdate(ctx context.Context, gitRootPath string, p string) (*helm.HelmChart, string, bool, error) { - statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(p)) - if err != nil { - return nil, "", false, err +func buildKey(chart *helm.HelmChart, v1 *string, v2 *string) string { + key := chart.GetChartDir(false) + if v1 != nil { + key += " / " + *v1 } - - s := status.Start(ctx, "%s: Checking for updates", statusPrefix) - doError := func(err error) (*helm.HelmChart, string, bool, error) { - s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) - return nil, "", false, err + if v2 != nil { + key += " / " + *v2 } + return key +} - chart, err := helm.NewHelmChart(p) - if err != nil { - return doError(err) +func (cmd *helmUpdateCmd) doQueryLatestVersion(ctx context.Context, chart *helm.HelmChart) (string, error) { + statusPrefix := chart.GetChartName() + + statusText := fmt.Sprintf("%s: Querying latest version", statusPrefix) + if chart.Config.UpdateConstraints != nil { + statusText = fmt.Sprintf("%s: Querying latest version with constraints '%s'", statusPrefix, *chart.Config.UpdateConstraints) } - chart.SetCredentials(&cmd.HelmCredentials) + s := status.Start(ctx, statusText) + defer s.Failed() - newVersion, updated, err := chart.CheckUpdate(ctx) + doError := func(err error) (string, error) { + s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) + return "", err + } + + latestVersion, err := chart.QueryLatestVersion(ctx) if err != nil { return doError(err) } - if !updated { - s.UpdateAndInfoFallback("%s: Version %s is already up-to-date.", statusPrefix, *chart.Config.ChartVersion) - } else { - msg := fmt.Sprintf("%s: Chart has new version %s available. Old version is %s.", statusPrefix, newVersion, *chart.Config.ChartVersion) - if chart.Config.SkipUpdate { - msg += " skipUpdate is set to true." - } - s.UpdateAndInfoFallback(msg) - } s.Success() - return chart, newVersion, updated, nil + return latestVersion, nil } -func (cmd *helmUpdateCmd) pullAndCommitChart(ctx context.Context, gitRootPath string, chart *helm.HelmChart, oldVersion string, newVersion string, mutex *sync.Mutex) error { - statusPrefix, err := filepath.Rel(gitRootPath, filepath.Dir(chart.ConfigFile)) - if err != nil { - return err +func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]os.FileInfo) error { + err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, err error) error { + if d == nil || d.IsDir() { + return nil + } + relToGit, err := filepath.Rel(root, p) + if err != nil { + return err + } + if _, ok := m[relToGit]; ok { + return nil + } + st, err := d.Info() + if err != nil { + return err + } + m[relToGit] = st + return nil + }) + if os.IsNotExist(err) { + err = nil } + return err +} - s := status.Start(ctx, "%s: Pulling Chart", statusPrefix) +func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, gitRootPath string, uc *updatedChart, toKeep map[string]bool, mutex *sync.Mutex) error { + statusPrefix := uc.charts[0].GetChartName() + + s := status.Start(ctx, "%s: Pulling Chart with version %s", statusPrefix, uc.newVersion) defer s.Failed() - chart.Config.ChartVersion = &newVersion - err = chart.Save() - if err != nil { + doError := func(err error) error { + s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return err } + var toAdd []string + var oldVersions []string + var oldChartDirs []string + // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later // know what got deleted - oldFiles := map[string]bool{} - err = filepath.WalkDir(chart.GetChartDir(), func(p string, d fs.DirEntry, err error) error { - if d == nil || d.IsDir() { - return nil + files := map[string]os.FileInfo{} + + for _, chart := range uc.charts { + oldChartDirs = append(oldChartDirs, chart.GetChartDir(true)) + oldVersions = append(oldVersions, *chart.Config.ChartVersion) + + chart.Config.ChartVersion = &uc.newVersion + err := chart.Save() + if err != nil { + return doError(err) } - relToGit, err := filepath.Rel(gitRootPath, p) + + // add helm-chart.yaml + relToGit, err := filepath.Rel(gitRootPath, chart.ConfigFile) if err != nil { - return err + return doError(err) + } + toAdd = append(toAdd, relToGit) + + err = cmd.collectFiles(gitRootPath, chart.GetDeprecatedChartDir(), files) + if err != nil { + return doError(err) } - oldFiles[relToGit] = true - return nil - }) - if err != nil { - return err } - err = doPull(ctx, statusPrefix, chart.ConfigFile, cmd.HelmCredentials, s) + err := cmd.collectFiles(gitRootPath, uc.charts[0].GetChartDir(true), files) if err != nil { - return err + return doError(err) } - var toAdd []string - relToGit, err := filepath.Rel(gitRootPath, chart.ConfigFile) + err = uc.charts[0].Pull(ctx) if err != nil { - return err + return doError(err) } - toAdd = append(toAdd, relToGit) - relToGit, err = filepath.Rel(gitRootPath, chart.GetChartDir()) + relToGit, err := filepath.Rel(gitRootPath, uc.charts[0].GetChartDir(true)) if err != nil { - return err + return doError(err) } toAdd = append(toAdd, relToGit) + // delete old versions + for i, chart := range uc.charts { + deleteOldVersion := !toKeep[buildKey(chart, &oldVersions[i], nil)] + if deleteOldVersion { + err = os.RemoveAll(oldChartDirs[i]) + if err != nil { + return doError(err) + } + } + + err = os.RemoveAll(chart.GetDeprecatedChartDir()) + if err != nil { + return doError(err) + } + } + // figure out what got deleted - for p, _ := range oldFiles { - if !utils.IsFile(filepath.Join(gitRootPath, p)) { - toAdd = append(toAdd, p) + for p, oldSt := range files { + st, err := os.Lstat(filepath.Join(gitRootPath, p)) + if err != nil { + if os.IsNotExist(err) { + toAdd = append(toAdd, p) + continue + } + return doError(err) + } + if st.Mode() == oldSt.Mode() && st.ModTime() == oldSt.ModTime() && st.Size() == oldSt.Size() { + continue } + toAdd = append(toAdd, p) } s.UpdateAndInfoFallback("%s: Committing chart", statusPrefix) @@ -229,11 +334,11 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(ctx context.Context, gitRootPath st r, err := git.PlainOpen(gitRootPath) if err != nil { - return err + return doError(err) } wt, err := r.Worktree() if err != nil { - return err + return doError(err) } for _, p := range toAdd { @@ -251,17 +356,17 @@ func (cmd *helmUpdateCmd) pullAndCommitChart(ctx context.Context, gitRootPath st time.Sleep(s * time.Millisecond) } if err != nil { - return fmt.Errorf("failed to add %s to git index: %w", p, err) + return doError(fmt.Errorf("failed to add %s to git index: %w", p, err)) } } - commitMsg := fmt.Sprintf("Updated helm chart %s from %s to %s", statusPrefix, oldVersion, newVersion) + commitMsg := fmt.Sprintf("Updated helm chart %s to version %s", statusPrefix, uc.newVersion) _, err = wt.Commit(commitMsg, &git.CommitOptions{}) if err != nil { - return fmt.Errorf("failed to commit: %w", err) + return doError(fmt.Errorf("failed to commit: %w", err)) } - s.Update("%s: Committed helm chart with version %s", statusPrefix, newVersion) + s.Update("%s: Committed helm chart with version %s", statusPrefix, uc.newVersion) s.Success() return nil diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index b63c7d704..72688db84 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -193,7 +193,7 @@ func (di *DeploymentItem) renderHelmCharts() error { return err } - chart, err := helm.NewHelmChart(p) + chart, err := helm.NewHelmChart(di.ctx.HelmChartsDir, p) if err != nil { return err } @@ -418,7 +418,7 @@ func (di *DeploymentItem) generateKustomizationYaml(subDir string) (*uo.Unstruct if di.isHelmValuesYaml(de.Name()) { continue } else if di.isHelmChartYaml(de.Name()) { - c, err := helm.NewHelmChart(filepath.Join(di.RenderedDir, subDir, de.Name())) + c, err := helm.NewHelmChart(di.ctx.HelmChartsDir, filepath.Join(di.RenderedDir, subDir, de.Name())) if err != nil { return nil, err } diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index dec1c4571..49133fecb 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -21,4 +21,5 @@ type SharedContext struct { RenderDir string SealedSecretsDir string DefaultSealedSecretsOutputPattern string + HelmChartsDir string } diff --git a/pkg/helm/helm_chart.go b/pkg/helm/helm_chart.go index 5c79c616e..e6dfab130 100644 --- a/pkg/helm/helm_chart.go +++ b/pkg/helm/helm_chart.go @@ -28,6 +28,7 @@ import ( "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/repo" "k8s.io/apimachinery/pkg/runtime/schema" + "net/url" "os" "path/filepath" "regexp" @@ -46,11 +47,14 @@ type HelmChart struct { credentials HelmCredentialsProvider - chartName string - chartDir string + chartName string + chartDir string + deprecatedChartDir string + + versions []string } -func NewHelmChart(configFile string) (*HelmChart, error) { +func NewHelmChart(baseChartsDir string, configFile string) (*HelmChart, error) { var config types.HelmChartConfig err := yaml.ReadYamlFile(configFile, &config) if err != nil { @@ -86,19 +90,73 @@ func NewHelmChart(configFile string) (*HelmChart, error) { if err != nil { return nil, err } - hc.chartDir = chartDir + hc.deprecatedChartDir = chartDir + + hc.chartDir, err = hc.buildChartDir(baseChartsDir) + if err != nil { + return nil, err + } return hc, nil } +func (c *HelmChart) buildChartDir(baseChartsDir string) (string, error) { + u, err := url.Parse(*c.Config.Repo) + if err != nil { + return "", err + } + + scheme := "" + port := "" + switch { + case registry.IsOCI(*c.Config.Repo): + scheme = "oci" + case u.Scheme == "http": + scheme = "http" + if u.Port() != "80" { + port = u.Port() + } + case u.Scheme == "https": + scheme = "https" + if u.Port() != "443" { + port = u.Port() + } + default: + return "", fmt.Errorf("unsupported scheme in %s", u.String()) + } + if port != "" { + scheme += "_" + port + } + + dir := filepath.Join( + baseChartsDir, + fmt.Sprintf("%s_%s", scheme, strings.ToLower(u.Hostname())), + filepath.FromSlash(strings.ToLower(u.Path)), + c.chartName, + ) + err = utils.CheckInDir(baseChartsDir, dir) + if err != nil { + return "", err + } + + return dir, nil +} + func (c *HelmChart) GetChartName() string { return c.chartName } -func (c *HelmChart) GetChartDir() string { +func (c *HelmChart) GetChartDir(withVersion bool) string { + if withVersion { + return filepath.Join(c.chartDir, *c.Config.ChartVersion) + } return c.chartDir } +func (c *HelmChart) GetDeprecatedChartDir() string { + return c.deprecatedChartDir +} + func (c *HelmChart) GetOutputPath() string { output := "helm-rendered.yaml" if c.Config.Output != nil { @@ -155,7 +213,14 @@ func (c *HelmChart) checkNeedsPull(chartDir string, isTmp bool) (bool, bool, str } func (c *HelmChart) Pull(ctx context.Context) error { - return c.doPull(ctx, c.chartDir) + if utils.Exists(c.GetDeprecatedChartDir()) { + err := os.RemoveAll(c.GetDeprecatedChartDir()) + if err != nil { + return err + } + } + + return c.doPull(ctx, c.GetChartDir(true)) } func (c *HelmChart) pullTmpChart(ctx context.Context) (string, error) { @@ -165,7 +230,7 @@ func (c *HelmChart) pullTmpChart(ctx context.Context) (string, error) { _, _ = fmt.Fprintf(hash, "%s\n", *c.Config.ChartVersion) h := hex.EncodeToString(hash.Sum(nil)) tmpDir := filepath.Join(utils.GetTmpBaseDir(ctx), "helm-charts") - _ = os.MkdirAll(tmpDir, 0o700) + _ = os.MkdirAll(tmpDir, 0o755) tmpDir = filepath.Join(tmpDir, fmt.Sprintf("%s-%s", c.chartName, h)) lockFile := tmpDir + ".lock" @@ -193,7 +258,7 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { baseDir := filepath.Dir(chartDir) _ = os.RemoveAll(chartDir) - _ = os.MkdirAll(baseDir, 0o700) + _ = os.MkdirAll(baseDir, 0o755) // need to use the same filesystem/volume that we later os.Rename the final pull chart to, as otherwise // the rename operation will lead to errors @@ -263,42 +328,41 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { return nil } -func (c *HelmChart) CheckUpdate(ctx context.Context) (string, bool, error) { +func (c *HelmChart) QueryLatestVersion(ctx context.Context) (string, error) { if c.Config.Repo != nil && registry.IsOCI(*c.Config.Repo) { - return c.checkUpdateOciRepo(ctx) + return c.queryLatestVersionOci(ctx) } - return c.checkUpdateHelmRepo(ctx) + return c.queryLatestVersionHelmRepo(ctx) } -func (c *HelmChart) checkUpdateOciRepo(ctx context.Context) (string, bool, error) { +func (c *HelmChart) queryLatestVersionOci(ctx context.Context) (string, error) { rh := registries.NewRegistryHelper(ctx) imageName := strings.TrimPrefix(*c.Config.Repo, "oci://") tags, err := rh.ListImageTags(imageName) if err != nil { - return "", false, err + return "", err } latestVersion, err := c.findLatestVersion(tags) if err != nil { - return "", false, err + return "", err } - updated := latestVersion != *c.Config.ChartVersion - return latestVersion, updated, nil + return latestVersion, nil } -func (c *HelmChart) checkUpdateHelmRepo(ctx context.Context) (string, bool, error) { +func (c *HelmChart) queryLatestVersionHelmRepo(ctx context.Context) (string, error) { settings := cli.New() var e *repo.Entry if c.Config.CredentialsId != nil { if c.credentials == nil { - return "", false, fmt.Errorf("no credentials provider") + return "", fmt.Errorf("no credentials provider") } e = c.credentials.FindCredentials(*c.Config.Repo, c.Config.CredentialsId) if e == nil { - return "", false, fmt.Errorf("no credentials provided for Chart %s", c.chartName) + return "", fmt.Errorf("no credentials provided for Chart %s", c.chartName) } } else { e = &repo.Entry{ @@ -308,28 +372,28 @@ func (c *HelmChart) checkUpdateHelmRepo(ctx context.Context) (string, bool, erro r, err := repo.NewChartRepository(e, getter.All(settings)) if err != nil { - return "", false, err + return "", err } r.CachePath, err = os.MkdirTemp(utils.GetTmpBaseDir(ctx), "helm-check-update-") if err != nil { - return "", false, err + return "", err } defer os.RemoveAll(r.CachePath) indexFile, err := r.DownloadIndexFile() if err != nil { - return "", false, err + return "", err } index, err := repo.LoadIndexFile(indexFile) if err != nil { - return "", false, err + return "", err } indexEntry, ok := index.Entries[c.chartName] if !ok || len(indexEntry) == 0 { - return "", false, fmt.Errorf("helm chart %s not found in repo index", c.chartName) + return "", fmt.Errorf("helm chart %s not found in repo index", c.chartName) } versions := make([]string, 0, indexEntry.Len()) @@ -339,11 +403,10 @@ func (c *HelmChart) checkUpdateHelmRepo(ctx context.Context) (string, bool, erro latestVersion, err := c.findLatestVersion(versions) if err != nil { - return "", false, err + return "", err } - updated := latestVersion != *c.Config.ChartVersion - return latestVersion, updated, nil + return latestVersion, nil } func (c *HelmChart) findLatestVersion(inputVersions []string) (string, error) { @@ -387,6 +450,12 @@ func (c *HelmChart) findLatestVersion(inputVersions []string) (string, error) { } func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { + deprecatedChartDir := c.GetDeprecatedChartDir() + if utils.IsDirectory(deprecatedChartDir) { + status.Deprecation(ctx, "helm-charts-dir", "Your project has pre-pulled charts located beside the helm-chart.yaml, which is deprecated. "+ + "Please run 'kluctl helm-pull' on your project and ensure that the deprecated charts are removed! Future versions of kluctl will ignore these locations.") + } + err := c.doRender(ctx, k, k8sVersion, sopsDecrypter) if err != nil { return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", c.chartName, c.Config.ReleaseName, err) @@ -395,7 +464,7 @@ func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster, k8sVersion st } func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { - chartDir := c.chartDir + chartDir := c.deprecatedChartDir needsPull, versionChanged, prePulledVersion, err := c.checkNeedsPull(chartDir, false) if err != nil { diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 0df49e01c..d30936121 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -20,6 +20,7 @@ type LoadedKluctlProject struct { ProjectDir string sealedSecretsDir string + helmChartsDir string Config types2.KluctlProject DynamicTargets []*types2.DynamicTarget diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 4c075fd23..b809bdd68 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -61,6 +61,7 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { } c.sealedSecretsDir = filepath.Join(c.ProjectDir, ".sealed-secrets") + c.helmChartsDir = filepath.Join(c.ProjectDir, ".helm-charts") sealedSecretsUsed := false if c.Config.SecretsConfig != nil { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index f07436014..9ee3c53ec 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -113,6 +113,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe RenderDir: params.RenderOutputDir, SealedSecretsDir: p.sealedSecretsDir, DefaultSealedSecretsOutputPattern: target.Name, + HelmChartsDir: p.helmChartsDir, } d, err := deployment.NewDeploymentProject(dctx, varsCtx, deployment.NewSource(deploymentDir), ".", nil) From 85a993bad818ba841ebabadc335e1415608a052a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Dec 2022 11:18:03 +0100 Subject: [PATCH 0662/2268] refactor: Don't use pointers for Repo/ChartVersion/ChartName --- cmd/kluctl/commands/cmd_helm_pull.go | 4 +- cmd/kluctl/commands/cmd_helm_update.go | 8 ++-- pkg/helm/helm_chart.go | 54 +++++++++++++------------- pkg/types/helm_chart.go | 6 +-- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index cb9ac562a..18f53c464 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -56,7 +56,7 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { chart.SetCredentials(&cmd.HelmCredentials) cleanupDir := chart.GetChartDir(false) - cleanupDirs[cleanupDir] = append(cleanupDirs[cleanupDir], *chart.Config.ChartVersion) + cleanupDirs[cleanupDir] = append(cleanupDirs[cleanupDir], chart.Config.ChartVersion) if _, ok := chartsToPull[chart.GetChartDir(true)]; ok { return nil @@ -76,7 +76,7 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { statusPrefix := chart.GetChartName() utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { - s := status.Start(ctx, "%s: Pulling Chart with version %s", statusPrefix, *chart.Config.ChartVersion) + s := status.Start(ctx, "%s: Pulling Chart with version %s", statusPrefix, chart.Config.ChartVersion) defer s.Failed() err := chart.Pull(ctx) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 98dd66f4c..6d02499c4 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -113,13 +113,13 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { } for _, chart := range uc.charts { - updated := newVersion != *chart.Config.ChartVersion + updated := newVersion != chart.Config.ChartVersion if updated && chart.Config.SkipUpdate { status.Info(ctx, "%s: Skipping update to version %s", chart.GetChartName(), newVersion) updated = false } if !updated { - toKeep[buildKey(chart, chart.Config.ChartVersion, nil)] = true + toKeep[buildKey(chart, &chart.Config.ChartVersion, nil)] = true continue } if len(uc2.charts) == 0 { @@ -258,9 +258,9 @@ func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, gitRootPath s for _, chart := range uc.charts { oldChartDirs = append(oldChartDirs, chart.GetChartDir(true)) - oldVersions = append(oldVersions, *chart.Config.ChartVersion) + oldVersions = append(oldVersions, chart.Config.ChartVersion) - chart.Config.ChartVersion = &uc.newVersion + chart.Config.ChartVersion = uc.newVersion err := chart.Save() if err != nil { return doError(err) diff --git a/pkg/helm/helm_chart.go b/pkg/helm/helm_chart.go index e6dfab130..6c1f8e4dd 100644 --- a/pkg/helm/helm_chart.go +++ b/pkg/helm/helm_chart.go @@ -61,9 +61,9 @@ func NewHelmChart(baseChartsDir string, configFile string) (*HelmChart, error) { return nil, err } - _, err = semver.NewVersion(*config.ChartVersion) + _, err = semver.NewVersion(config.ChartVersion) if err != nil { - return nil, fmt.Errorf("invalid chart version '%s': %w", *config.ChartVersion, err) + return nil, fmt.Errorf("invalid chart version '%s': %w", config.ChartVersion, err) } hc := &HelmChart{ @@ -71,17 +71,17 @@ func NewHelmChart(baseChartsDir string, configFile string) (*HelmChart, error) { Config: &config, } - if hc.Config.Repo != nil && registry.IsOCI(*hc.Config.Repo) { - s := strings.Split(*hc.Config.Repo, "/") + if registry.IsOCI(hc.Config.Repo) { + s := strings.Split(hc.Config.Repo, "/") chartName := s[len(s)-1] if m, _ := regexp.MatchString(`[a-zA-Z_-]+`, chartName); !m { - return nil, fmt.Errorf("invalid oci chart url: %s", *hc.Config.Repo) + return nil, fmt.Errorf("invalid oci chart url: %s", hc.Config.Repo) } hc.chartName = chartName - } else if hc.Config.ChartName == nil { + } else if hc.Config.ChartName == "" { return nil, fmt.Errorf("chartName is missing in helm-chart.yml") } else { - hc.chartName = *hc.Config.ChartName + hc.chartName = hc.Config.ChartName } dir := filepath.Dir(configFile) @@ -101,7 +101,7 @@ func NewHelmChart(baseChartsDir string, configFile string) (*HelmChart, error) { } func (c *HelmChart) buildChartDir(baseChartsDir string) (string, error) { - u, err := url.Parse(*c.Config.Repo) + u, err := url.Parse(c.Config.Repo) if err != nil { return "", err } @@ -109,7 +109,7 @@ func (c *HelmChart) buildChartDir(baseChartsDir string) (string, error) { scheme := "" port := "" switch { - case registry.IsOCI(*c.Config.Repo): + case registry.IsOCI(c.Config.Repo): scheme = "oci" case u.Scheme == "http": scheme = "http" @@ -148,7 +148,7 @@ func (c *HelmChart) GetChartName() string { func (c *HelmChart) GetChartDir(withVersion bool) string { if withVersion { - return filepath.Join(c.chartDir, *c.Config.ChartVersion) + return filepath.Join(c.chartDir, c.Config.ChartVersion) } return c.chartDir } @@ -206,7 +206,7 @@ func (c *HelmChart) checkNeedsPull(chartDir string, isTmp bool) (bool, bool, str } version, _, _ := chartYaml.GetNestedString("version") - if version != *c.Config.ChartVersion { + if version != c.Config.ChartVersion { return true, true, version, nil } return false, false, "", nil @@ -225,9 +225,9 @@ func (c *HelmChart) Pull(ctx context.Context) error { func (c *HelmChart) pullTmpChart(ctx context.Context) (string, error) { hash := sha256.New() - _, _ = fmt.Fprintf(hash, "%s\n", *c.Config.Repo) + _, _ = fmt.Fprintf(hash, "%s\n", c.Config.Repo) _, _ = fmt.Fprintf(hash, "%s\n", c.chartName) - _, _ = fmt.Fprintf(hash, "%s\n", *c.Config.ChartVersion) + _, _ = fmt.Fprintf(hash, "%s\n", c.Config.ChartVersion) h := hex.EncodeToString(hash.Sum(nil)) tmpDir := filepath.Join(utils.GetTmpBaseDir(ctx), "helm-charts") _ = os.MkdirAll(tmpDir, 0o755) @@ -278,10 +278,10 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { a.Settings = cli.New() a.Untar = true a.DestDir = tmpDir - a.Version = *c.Config.ChartVersion + a.Version = c.Config.ChartVersion if c.Config.CredentialsId != nil { - if registry.IsOCI(*c.Config.Repo) { + if registry.IsOCI(c.Config.Repo) { return fmt.Errorf("OCI charts can currently only be authenticated via registry login and not via cli arguments") } if c.credentials == nil { @@ -290,7 +290,7 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { } if c.credentials != nil { - creds := c.credentials.FindCredentials(*c.Config.Repo, c.Config.CredentialsId) + creds := c.credentials.FindCredentials(c.Config.Repo, c.Config.CredentialsId) if creds != nil { a.Username = creds.Username a.Password = creds.Password @@ -303,10 +303,10 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { } var out string - if registry.IsOCI(*c.Config.Repo) { - out, err = a.Run(*c.Config.Repo) + if registry.IsOCI(c.Config.Repo) { + out, err = a.Run(c.Config.Repo) } else { - a.RepoURL = *c.Config.Repo + a.RepoURL = c.Config.Repo out, err = a.Run(c.chartName) } if err != nil { @@ -329,7 +329,7 @@ func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { } func (c *HelmChart) QueryLatestVersion(ctx context.Context) (string, error) { - if c.Config.Repo != nil && registry.IsOCI(*c.Config.Repo) { + if registry.IsOCI(c.Config.Repo) { return c.queryLatestVersionOci(ctx) } return c.queryLatestVersionHelmRepo(ctx) @@ -338,7 +338,7 @@ func (c *HelmChart) QueryLatestVersion(ctx context.Context) (string, error) { func (c *HelmChart) queryLatestVersionOci(ctx context.Context) (string, error) { rh := registries.NewRegistryHelper(ctx) - imageName := strings.TrimPrefix(*c.Config.Repo, "oci://") + imageName := strings.TrimPrefix(c.Config.Repo, "oci://") tags, err := rh.ListImageTags(imageName) if err != nil { return "", err @@ -360,13 +360,13 @@ func (c *HelmChart) queryLatestVersionHelmRepo(ctx context.Context) (string, err if c.credentials == nil { return "", fmt.Errorf("no credentials provider") } - e = c.credentials.FindCredentials(*c.Config.Repo, c.Config.CredentialsId) + e = c.credentials.FindCredentials(c.Config.Repo, c.Config.CredentialsId) if e == nil { return "", fmt.Errorf("no credentials provided for Chart %s", c.chartName) } } else { e = &repo.Entry{ - URL: *c.Config.Repo, + URL: c.Config.Repo, } } @@ -473,13 +473,13 @@ func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion if needsPull { if versionChanged { return fmt.Errorf("pre-pulled Helm Chart %s need to be pulled (call 'kluctl helm-pull'). "+ - "Desired version is %s while pre-pulled version is %s", c.chartName, *c.Config.ChartVersion, prePulledVersion) + "Desired version is %s while pre-pulled version is %s", c.chartName, c.Config.ChartVersion, prePulledVersion) } else { status.Warning(ctx, "Warning, need to pull Helm Chart %s with version %s. "+ - "Please consider pre-pulling it with 'kluctl helm-pull'", c.chartName, *c.Config.ChartVersion) + "Please consider pre-pulling it with 'kluctl helm-pull'", c.chartName, c.Config.ChartVersion) } - s := status.Start(ctx, "Pulling Helm Chart %s with version %s", c.chartName, *c.Config.ChartVersion) + s := status.Start(ctx, "Pulling Helm Chart %s with version %s", c.chartName, c.Config.ChartVersion) defer s.Failed() chartDir, err = c.pullTmpChart(ctx) @@ -572,7 +572,7 @@ func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion } if chartRequested.Metadata.Deprecated { - status.Warning(ctx, "Chart %s is deprecated", *c.Config.ChartName) + status.Warning(ctx, "Chart %s is deprecated", c.Config.ChartName) } rel, err := client.Run(chartRequested, vals) diff --git a/pkg/types/helm_chart.go b/pkg/types/helm_chart.go index 9ebc00118..11498426b 100644 --- a/pkg/types/helm_chart.go +++ b/pkg/types/helm_chart.go @@ -1,10 +1,10 @@ package types type HelmChartConfig2 struct { - Repo *string `yaml:"repo" validate:"required"` + Repo string `yaml:"repo" validate:"required"` CredentialsId *string `yaml:"credentialsId,omitempty"` - ChartName *string `yaml:"chartName,omitempty"` - ChartVersion *string `yaml:"chartVersion" validate:"required"` + ChartName string `yaml:"chartName,omitempty"` + ChartVersion string `yaml:"chartVersion" validate:"required"` UpdateConstraints *string `yaml:"updateConstraints,omitempty"` ReleaseName string `yaml:"releaseName" validate:"required"` Namespace *string `yaml:"namespace,omitempty"` From 979de607b9af5caf0ff3d04379f1470a38f66ecd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Dec 2022 16:22:17 +0100 Subject: [PATCH 0663/2268] feat: Reimplement helm support to allow better control of pre-pulled charts --- cmd/kluctl/commands/cmd_helm_pull.go | 145 +++--- cmd/kluctl/commands/cmd_helm_update.go | 408 +++++++-------- e2e/helm_test.go | 101 ++-- pkg/deployment/deployment_item.go | 12 +- pkg/helm/chart.go | 409 +++++++++++++++ pkg/helm/helm_chart.go | 673 ------------------------- pkg/helm/helm_release.go | 313 ++++++++++++ pkg/helm/pulled_chart.go | 65 +++ pkg/helm/utils.go | 19 + 9 files changed, 1159 insertions(+), 986 deletions(-) create mode 100644 pkg/helm/chart.go delete mode 100644 pkg/helm/helm_chart.go create mode 100644 pkg/helm/helm_release.go create mode 100644 pkg/helm/pulled_chart.go create mode 100644 pkg/helm/utils.go diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 18f53c464..ee8fc429d 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -39,92 +39,121 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { baseChartsDir := filepath.Join(projectDir, ".helm-charts") - chartsToPull := map[string]*helm.HelmChart{} - cleanupDirs := map[string][]string{} - - err = filepath.WalkDir(projectDir, func(p string, d fs.DirEntry, err error) error { - fname := filepath.Base(p) - if fname != "helm-chart.yml" && fname != "helm-chart.yaml" { - return nil - } - - chart, err := helm.NewHelmChart(baseChartsDir, p) - if err != nil { - return err - } - - chart.SetCredentials(&cmd.HelmCredentials) - - cleanupDir := chart.GetChartDir(false) - cleanupDirs[cleanupDir] = append(cleanupDirs[cleanupDir], chart.Config.ChartVersion) + releases, charts, err := loadHelmReleases(projectDir, baseChartsDir, &cmd.HelmCredentials) + if err != nil { + return err + } - if _, ok := chartsToPull[chart.GetChartDir(true)]; ok { - return nil + for _, hr := range releases { + if utils.Exists(hr.GetDeprecatedChartDir()) { + rel, err := filepath.Rel(projectDir, hr.GetDeprecatedChartDir()) + if err != nil { + return err + } + status.Info(ctx, "Removing deprecated charts dir %s", rel) + err = os.RemoveAll(hr.GetDeprecatedChartDir()) + if err != nil { + return err + } } - - chartsToPull[chart.GetChartDir(true)] = chart - return nil - }) + } var errs *multierror.Error var wg sync.WaitGroup var mutex sync.Mutex sem := semaphore.NewWeighted(8) - for _, chart := range chartsToPull { + for _, chart := range charts { chart := chart statusPrefix := chart.GetChartName() - utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { - s := status.Start(ctx, "%s: Pulling Chart with version %s", statusPrefix, chart.Config.ChartVersion) - defer s.Failed() - - err := chart.Pull(ctx) - if err != nil { - s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) - return err + versionsToPull := map[string]bool{} + for _, hr := range releases { + if hr.Chart == chart { + versionsToPull[hr.Config.ChartVersion] = true } - s.Success() - return nil - }) - } - wg.Wait() - if err != nil { - errs = multierror.Append(errs, err) - } - - if errs.ErrorOrNil() != nil { - return fmt.Errorf("command failed") - } + } - for dir, versions := range cleanupDirs { - des, err := os.ReadDir(dir) + cleanupDir, err := chart.BuildPulledChartDir(baseChartsDir, "") if err != nil { return err } + des, err := os.ReadDir(cleanupDir) + if err != nil && !os.IsNotExist(err) { + return err + } for _, de := range des { if !de.IsDir() { continue } - found := false - for _, v := range versions { - if v == de.Name() { - found = true - break + if _, ok := versionsToPull[de.Name()]; !ok { + status.Info(ctx, "Removing unused Chart with version %s", de.Name()) + err = os.RemoveAll(filepath.Join(cleanupDir, de.Name())) + if err != nil { + return err } } - if !found { - chartName := filepath.Base(dir) - s := status.Start(ctx, "%s: Removing unused version %s", chartName, de.Name()) - err = os.RemoveAll(filepath.Join(dir, de.Name())) + } + + for version, _ := range versionsToPull { + version := version + utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { + s := status.Start(ctx, "%s: Pulling Chart with version %s", statusPrefix, version) + defer s.Failed() + + _, err := chart.PullInProject(ctx, baseChartsDir, version) if err != nil { - s.Failed() + s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) return err } + s.Success() - } + return nil + }) } } + wg.Wait() + if err != nil { + errs = multierror.Append(errs, err) + } + + if errs.ErrorOrNil() != nil { + return fmt.Errorf("command failed") + } return nil } + +func loadHelmReleases(projectDir string, baseChartsDir string, credentialsProvider helm.HelmCredentialsProvider) ([]*helm.Release, []*helm.Chart, error) { + var releases []*helm.Release + chartsMap := make(map[string]*helm.Chart) + err := filepath.WalkDir(projectDir, func(p string, d fs.DirEntry, err error) error { + fname := filepath.Base(p) + if fname != "helm-chart.yml" && fname != "helm-chart.yaml" { + return nil + } + + hr, err := helm.NewRelease(p, baseChartsDir, credentialsProvider) + if err != nil { + return err + } + + releases = append(releases, hr) + chart := hr.Chart + key := fmt.Sprintf("%s / %s", chart.GetRepo(), chart.GetChartName()) + if x, ok := chartsMap[key]; !ok { + chartsMap[key] = chart + } else { + hr.Chart = x + } + return nil + }) + if err != nil { + return nil, nil, err + } + charts := make([]*helm.Chart, 0, len(chartsMap)) + for _, chart := range chartsMap { + charts = append(charts, chart) + } + return releases, charts, nil +} diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 6d02499c4..d40b274a6 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/format/index" "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" git2 "github.com/kluctl/kluctl/v2/pkg/git" @@ -13,11 +14,9 @@ import ( "github.com/kluctl/kluctl/v2/pkg/yaml" "golang.org/x/sync/semaphore" "io/fs" - "math/rand" "os" "path/filepath" "sync" - "time" ) type helmUpdateCmd struct { @@ -34,11 +33,6 @@ func (cmd *helmUpdateCmd) Help() string { return `Optionally performs the actual upgrade and/or add a commit to version control.` } -type updatedChart struct { - charts []*helm.HelmChart - newVersion string -} - func (cmd *helmUpdateCmd) Run(ctx context.Context) error { projectDir, err := cmd.ProjectDir.GetProjectDir() if err != nil { @@ -54,6 +48,26 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { return err } + if cmd.Commit { + g, err := git.PlainOpen(gitRootPath) + if err != nil { + return err + } + wt, err := g.Worktree() + if err != nil { + return err + } + gitStatus, err := wt.Status() + if err != nil { + return err + } + for _, s := range gitStatus { + if (s.Staging != git.Untracked && s.Staging != git.Unmodified) || (s.Worktree != git.Untracked && s.Worktree != git.Unmodified) { + return fmt.Errorf("--commit can only be used when the git worktree is unmodified") + } + } + } + baseChartsDir := filepath.Join(projectDir, ".helm-charts") var errs *multierror.Error @@ -61,158 +75,151 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { var mutex sync.Mutex sem := semaphore.NewWeighted(8) - chartsToCheck := map[string]*updatedChart{} - - err = filepath.WalkDir(projectDir, func(p string, d fs.DirEntry, err error) error { - fname := filepath.Base(p) - if fname != "helm-chart.yml" && fname != "helm-chart.yaml" { - return nil - } - - chart, err := helm.NewHelmChart(baseChartsDir, p) - if err != nil { - return err - } - - chart.SetCredentials(&cmd.HelmCredentials) + releases, charts, err := loadHelmReleases(projectDir, baseChartsDir, &cmd.HelmCredentials) + if err != nil { + return err + } - key := buildKey(chart, chart.Config.UpdateConstraints, nil) - if uc2, ok := chartsToCheck[key]; ok { - uc2.charts = append(uc2.charts, chart) - } else { - chartsToCheck[key] = &updatedChart{ - charts: []*helm.HelmChart{chart}, + for _, hr := range releases { + if utils.Exists(hr.GetDeprecatedChartDir()) { + relDir, err := filepath.Rel(projectDir, filepath.Dir(hr.ConfigFile)) + if err != nil { + return err } + status.Error(ctx, "%s: Project is using a pre-pulled Helm Chart that is next to the helm-chart.yaml, which is deprecated. "+ + "Updating is only possible after removing these. Use 'kluctl helm-pull' to remove all deprecated chart folders.", relDir) + return fmt.Errorf("detected deprecated chart folder") } - return nil - }) - - toKeep := map[string]bool{} - chartsToPull := map[string]*updatedChart{} + } - for _, uc := range chartsToCheck { - uc := uc + for _, chart := range charts { + chart := chart utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { - newVersion, err := cmd.doQueryLatestVersion(ctx, uc.charts[0]) + s := status.Start(ctx, "%s: Querying versions", chart.GetChartName()) + defer s.Failed() + err := chart.QueryVersions(ctx) if err != nil { + s.FailedWithMessage("%s: %s", chart.GetChartName(), err.Error()) return err } + s.Success() + return nil + }) + } + wg.Wait() + if errs.ErrorOrNil() != nil { + return errs + } - mutex.Lock() - defer mutex.Unlock() - - key := buildKey(uc.charts[0], &newVersion, nil) - toKeep[key] = true + for _, chart := range charts { + chart := chart - uc2, ok := chartsToPull[key] - if !ok { - uc2 = &updatedChart{ - newVersion: newVersion, - } - chartsToPull[key] = uc2 + versionsToPull := map[string]bool{} + for _, hr := range releases { + if hr.Chart != chart { + continue } + versionsToPull[hr.Config.ChartVersion] = true + } - for _, chart := range uc.charts { - updated := newVersion != chart.Config.ChartVersion - if updated && chart.Config.SkipUpdate { - status.Info(ctx, "%s: Skipping update to version %s", chart.GetChartName(), newVersion) - updated = false - } - if !updated { - toKeep[buildKey(chart, &chart.Config.ChartVersion, nil)] = true - continue - } - if len(uc2.charts) == 0 { - status.Info(ctx, "%s: Chart has new version %s", chart.GetChartName(), newVersion) + for version, _ := range versionsToPull { + version := version + utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { + s := status.Start(ctx, "%s: Downloading Chart with version %s into cache", chart.GetChartName(), version) + defer s.Failed() + _, err := chart.PullCached(ctx, version) + if err != nil { + s.FailedWithMessage("%s: %s", chart.GetChartName(), err.Error()) + return err } - uc2.charts = append(uc2.charts, chart) - } - - if len(uc2.charts) == 0 { - delete(chartsToPull, key) - } - - return nil - }) + s.Success() + return nil + }) + } } wg.Wait() if errs.ErrorOrNil() != nil { return errs } - if !cmd.Upgrade { - return errs.ErrorOrNil() + versionUseCounts := map[string]map[string]int{} + for _, hr := range releases { + key := fmt.Sprintf("%s / %s", hr.Chart.GetRepo(), hr.Chart.GetChartName()) + if _, ok := versionUseCounts[key]; !ok { + versionUseCounts[key] = map[string]int{} + } + versionUseCounts[key][hr.Config.ChartVersion]++ } - if cmd.Interactive { - sem = semaphore.NewWeighted(1) - } + for _, hr := range releases { + relDir, err := filepath.Rel(projectDir, filepath.Dir(hr.ConfigFile)) + if err != nil { + return err + } - for _, uc := range chartsToPull { - uc := uc + latestVersion, err := hr.Chart.GetLatestVersion(hr.Config.UpdateConstraints) + if err != nil { + return err + } + if hr.Config.ChartVersion == latestVersion { + continue + } - utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { - if cmd.Interactive { - statusPrefix := uc.charts[0].GetChartName() - if !status.AskForConfirmation(ctx, fmt.Sprintf("%s: Do you want to upgrade Chart %s to version %s?", - statusPrefix, uc.charts[0].GetChartName(), uc.newVersion)) { - return nil - } - } + if hr.Config.SkipUpdate { + status.Info(ctx, "%s: Skipped update to version %s", relDir, latestVersion) + continue + } - err := cmd.pullAndCommitCharts(ctx, gitRootPath, uc, toKeep, &mutex) - if err != nil { - return err - } - return nil - }) - } - wg.Wait() + status.Info(ctx, "%s: Chart has new version %s available", relDir, latestVersion) - if !cmd.Commit { - return errs.ErrorOrNil() - } + if !cmd.Upgrade { + continue + } - return errs.ErrorOrNil() -} + if cmd.Interactive { + if !status.AskForConfirmation(ctx, fmt.Sprintf("%s: Do you want to upgrade Chart %s to version %s?", + relDir, hr.Chart.GetChartName(), latestVersion)) { + continue + } + } -func buildKey(chart *helm.HelmChart, v1 *string, v2 *string) string { - key := chart.GetChartDir(false) - if v1 != nil { - key += " / " + *v1 - } - if v2 != nil { - key += " / " + *v2 - } - return key -} + oldVersion := hr.Config.ChartVersion + hr.Config.ChartVersion = latestVersion + err = hr.Save() + if err != nil { + return err + } -func (cmd *helmUpdateCmd) doQueryLatestVersion(ctx context.Context, chart *helm.HelmChart) (string, error) { - statusPrefix := chart.GetChartName() + status.Info(ctx, "%s: Updated Chart version to %s", relDir, latestVersion) - statusText := fmt.Sprintf("%s: Querying latest version", statusPrefix) - if chart.Config.UpdateConstraints != nil { - statusText = fmt.Sprintf("%s: Querying latest version with constraints '%s'", statusPrefix, *chart.Config.UpdateConstraints) - } + if !cmd.Commit { + continue + } - s := status.Start(ctx, statusText) - defer s.Failed() + key := fmt.Sprintf("%s / %s", hr.Chart.GetRepo(), hr.Chart.GetChartName()) + uv := versionUseCounts[key] + uv[oldVersion]-- + uv[latestVersion]++ - doError := func(err error) (string, error) { - s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) - return "", err - } + pullChart := false + deleteChart := false + if uv[latestVersion] == 1 { + pullChart = true + } + if uv[oldVersion] == 0 { + deleteChart = true + } - latestVersion, err := chart.QueryLatestVersion(ctx) - if err != nil { - return doError(err) + err = cmd.pullAndCommitCharts(ctx, projectDir, baseChartsDir, gitRootPath, hr, oldVersion, latestVersion, pullChart, deleteChart) + if err != nil { + return err + } } - s.Success() - return latestVersion, nil + return errs.ErrorOrNil() } -func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]os.FileInfo) error { +func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]bool) error { err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, err error) error { if d == nil || d.IsDir() { return nil @@ -224,11 +231,7 @@ func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]os. if _, ok := m[relToGit]; ok { return nil } - st, err := d.Info() - if err != nil { - return err - } - m[relToGit] = st + m[relToGit] = true return nil }) if os.IsNotExist(err) { @@ -237,136 +240,109 @@ func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]os. return err } -func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, gitRootPath string, uc *updatedChart, toKeep map[string]bool, mutex *sync.Mutex) error { - statusPrefix := uc.charts[0].GetChartName() +func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, projectDir string, baseChartsDir string, gitRootPath string, hr *helm.Release, oldVersion string, newVersion string, pullChart bool, deleteChart bool) error { + relDir, err := filepath.Rel(projectDir, filepath.Dir(hr.ConfigFile)) + if err != nil { + return err + } - s := status.Start(ctx, "%s: Pulling Chart with version %s", statusPrefix, uc.newVersion) + s := status.Start(ctx, "%s: Committing Chart with version %s", relDir, newVersion) defer s.Failed() doError := func(err error) error { - s.FailedWithMessage("%s: %s", statusPrefix, err.Error()) + s.FailedWithMessage("%s: %s", relDir, err.Error()) return err } - var toAdd []string - var oldVersions []string - var oldChartDirs []string - - // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later - // know what got deleted - files := map[string]os.FileInfo{} - - for _, chart := range uc.charts { - oldChartDirs = append(oldChartDirs, chart.GetChartDir(true)) - oldVersions = append(oldVersions, chart.Config.ChartVersion) - - chart.Config.ChartVersion = uc.newVersion - err := chart.Save() - if err != nil { - return doError(err) - } - - // add helm-chart.yaml - relToGit, err := filepath.Rel(gitRootPath, chart.ConfigFile) - if err != nil { - return doError(err) - } - toAdd = append(toAdd, relToGit) - - err = cmd.collectFiles(gitRootPath, chart.GetDeprecatedChartDir(), files) - if err != nil { - return doError(err) - } + r, err := git.PlainOpen(gitRootPath) + if err != nil { + return doError(err) } - - err := cmd.collectFiles(gitRootPath, uc.charts[0].GetChartDir(true), files) + wt, err := r.Worktree() if err != nil { return doError(err) } - err = uc.charts[0].Pull(ctx) + // add helm-chart.yaml + relToGit, err := filepath.Rel(gitRootPath, hr.ConfigFile) if err != nil { return doError(err) } - - relToGit, err := filepath.Rel(gitRootPath, uc.charts[0].GetChartDir(true)) + _, err = wt.Add(relToGit) if err != nil { return doError(err) } - toAdd = append(toAdd, relToGit) - // delete old versions - for i, chart := range uc.charts { - deleteOldVersion := !toKeep[buildKey(chart, &oldVersions[i], nil)] - if deleteOldVersion { - err = os.RemoveAll(oldChartDirs[i]) - if err != nil { - return doError(err) - } + if deleteChart { + chartDir, err := hr.Chart.BuildPulledChartDir(baseChartsDir, oldVersion) + if err != nil { + return doError(err) } - - err = os.RemoveAll(chart.GetDeprecatedChartDir()) + relChartDir, err := filepath.Rel(gitRootPath, chartDir) + if err != nil { + return doError(err) + } + _, err = wt.Remove(relChartDir) + if err != nil && err != index.ErrEntryNotFound { + return doError(err) + } + err = os.RemoveAll(chartDir) if err != nil { return doError(err) } } - // figure out what got deleted - for p, oldSt := range files { - st, err := os.Lstat(filepath.Join(gitRootPath, p)) + if pullChart { + chartDir, err := hr.Chart.BuildPulledChartDir(baseChartsDir, newVersion) if err != nil { - if os.IsNotExist(err) { - toAdd = append(toAdd, p) - continue - } return doError(err) } - if st.Mode() == oldSt.Mode() && st.ModTime() == oldSt.ModTime() && st.Size() == oldSt.Size() { - continue + relChartDir, err := filepath.Rel(gitRootPath, chartDir) + if err != nil { + return doError(err) } - toAdd = append(toAdd, p) - } - s.UpdateAndInfoFallback("%s: Committing chart", statusPrefix) + // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later + // know what got deleted + files := map[string]bool{} + err = cmd.collectFiles(gitRootPath, chartDir, files) + if err != nil { + return doError(err) + } - mutex.Lock() - defer mutex.Unlock() + _, err = hr.Chart.PullInProject(ctx, baseChartsDir, newVersion) + if err != nil { + return doError(err) + } - r, err := git.PlainOpen(gitRootPath) - if err != nil { - return doError(err) - } - wt, err := r.Worktree() - if err != nil { - return doError(err) - } + _, err = wt.Add(relChartDir) + if err != nil { + return doError(err) + } - for _, p := range toAdd { - // we have to retry a few times as Add() might fail with "no such file or directly" - // This is because it internally tries to get the git status, which fails if files are added/deleted in - // parallel by another goroutine (we're pulling in parallel). We're guarding the repo via the mutex from above - // so this is actually safe. - for i := 0; i < 10; i++ { - _, err = wt.Add(p) - if err == nil || !os.IsNotExist(err) { - break + // figure out what got deleted + for p := range files { + _, err := os.Lstat(filepath.Join(gitRootPath, p)) + if err != nil { + if os.IsNotExist(err) { + _, err = wt.Remove(p) + if err != nil { + return doError(err) + } + } else { + return doError(err) + } } - // let's have some randomness in waiting time to ensure we don't run into the same problem again and again - s := time.Duration(rand.Intn(10) + 10) - time.Sleep(s * time.Millisecond) - } - if err != nil { - return doError(fmt.Errorf("failed to add %s to git index: %w", p, err)) } } - commitMsg := fmt.Sprintf("Updated helm chart %s to version %s", statusPrefix, uc.newVersion) + commitMsg := fmt.Sprintf("Updated helm chart %s to version %s", relToGit, newVersion) _, err = wt.Commit(commitMsg, &git.CommitOptions{}) if err != nil { return doError(fmt.Errorf("failed to commit: %w", err)) } - s.Update("%s: Committed helm chart with version %s", statusPrefix, uc.newVersion) + s.Update("%s: Committed helm chart with version %s", relToGit, newVersion) s.Success() return nil diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 3fe9dad20..682163cca 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -1,11 +1,14 @@ package e2e import ( + "fmt" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" + "net/url" + "os" "path/filepath" "sort" "testing" @@ -71,18 +74,18 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { return } else { assert.NoError(t, err) - assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm1/charts/test-chart1/Chart.yaml")) + assert.FileExists(t, getChartFile(t, p, repoUrl, "test-chart1", "0.1.0")) } } args := []string{"deploy", "--yes", "-t", "test"} args = append(args, tc.extraArgs...) _, stderr, err := p.Kluctl(args...) - prePullWarning := "Warning, need to pull Helm Chart test-chart1 with version 0.1.0." + pullMessage := "Pulling Helm Chart test-chart1 with version 0.1.0" if prePull { - assert.NotContains(t, stderr, prePullWarning) + assert.NotContains(t, stderr, pullMessage) } else { - assert.Contains(t, stderr, prePullWarning) + assert.Contains(t, stderr, pullMessage) } if tc.expectedError != "" { assert.Error(t, err) @@ -137,7 +140,7 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) p.KluctlMust("helm-pull") - assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm1/charts/test-chart1/Chart.yaml")) + assert.FileExists(t, getChartFile(t, p, repoUrl, "test-chart1", "0.1.0")) p.KluctlMust("deploy", "--yes", "-t", "test") cm := assertConfigMapExists(t, k, p.TestSlug(), "test-helm1-test-chart1") v, _, _ := cm.GetNestedString("data", "version") @@ -149,6 +152,8 @@ func testHelmManualUpgrade(t *testing.T, oci bool) { }, "") p.KluctlMust("helm-pull") + assert.NoFileExists(t, getChartFile(t, p, repoUrl, "test-chart1", "0.1.0")) + assert.FileExists(t, getChartFile(t, p, repoUrl, "test-chart1", "0.2.0")) p.KluctlMust("deploy", "--yes", "-t", "test") cm = assertConfigMapExists(t, k, p.TestSlug(), "test-helm1-test-chart1") v, _, _ = cm.GetNestedString("data", "version") @@ -190,9 +195,8 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { }, "") p.KluctlMust("helm-pull") - assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm1/charts/test-chart1/Chart.yaml")) - assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm2/charts/test-chart2/Chart.yaml")) - assert.FileExists(t, filepath.Join(p.LocalRepoDir(), "helm3/charts/test-chart1/Chart.yaml")) + assert.FileExists(t, getChartFile(t, p, repoUrl, "test-chart1", "0.1.0")) + assert.FileExists(t, getChartFile(t, p, repoUrl, "test-chart2", "0.1.0")) args := []string{"helm-update"} if upgrade { @@ -203,28 +207,28 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { } _, stderr := p.KluctlMust(args...) - assert.Contains(t, stderr, "helm1: Chart has new version 0.2.0 available.") - assert.Contains(t, stderr, "helm2: Chart has new version 0.3.0 available.") - assert.Contains(t, stderr, "helm3: Chart has new version 0.2.0 available. Old version is 0.1.0. skipUpdate is set to true.") + assert.Contains(t, stderr, "helm1: Chart has new version 0.2.0 available") + assert.Contains(t, stderr, "helm2: Chart has new version 0.3.0 available") + assert.Contains(t, stderr, "helm3: Skipped update to version 0.2.0") - c1, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), "helm1/charts/test-chart1/Chart.yaml")) - assert.NoError(t, err) - c2, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), "helm2/charts/test-chart2/Chart.yaml")) - assert.NoError(t, err) - c3, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), "helm3/charts/test-chart1/Chart.yaml")) - assert.NoError(t, err) - - v1, _, _ := c1.GetNestedString("version") - v2, _, _ := c2.GetNestedString("version") - v3, _, _ := c3.GetNestedString("version") if upgrade { - assert.Equal(t, "0.2.0", v1) - assert.Equal(t, "0.3.0", v2) - assert.Equal(t, "0.1.0", v3) + assert.Contains(t, stderr, "helm1: Updated Chart version to 0.2.0") + assert.Contains(t, stderr, "helm2: Updated Chart version to 0.3.0") + } + if commit { + assert.Contains(t, stderr, "helm1: Committing Chart with version 0.2.0") + assert.Contains(t, stderr, "helm2: Committing Chart with version 0.3.0") + } + + pulledVersions1 := listChartVersions(t, p, repoUrl, "test-chart1") + pulledVersions2 := listChartVersions(t, p, repoUrl, "test-chart2") + + if commit { + assert.Equal(t, []string{"0.1.0", "0.2.0"}, pulledVersions1) + assert.Equal(t, []string{"0.3.0"}, pulledVersions2) } else { - assert.Equal(t, "0.1.0", v1) - assert.Equal(t, "0.1.0", v2) - assert.Equal(t, "0.1.0", v3) + assert.Equal(t, []string{"0.1.0"}, pulledVersions1) + assert.Equal(t, []string{"0.1.0"}, pulledVersions2) } if commit { @@ -244,8 +248,8 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { return commitList[i].Message < commitList[j].Message }) - assert.Equal(t, "Updated helm chart helm1 from 0.1.0 to 0.2.0", commitList[0].Message) - assert.Equal(t, "Updated helm chart helm2 from 0.1.0 to 0.3.0", commitList[1].Message) + assert.Equal(t, "Updated helm chart helm1/helm-chart.yaml to version 0.2.0", commitList[0].Message) + assert.Equal(t, "Updated helm chart helm2/helm-chart.yaml to version 0.3.0", commitList[1].Message) } } @@ -312,9 +316,9 @@ func testHelmUpdateConstraints(t *testing.T, oci bool) { args := []string{"helm-update", "--upgrade"} _, stderr := p.KluctlMust(args...) - assert.Contains(t, stderr, "helm1: Chart has new version 0.1.1 available.") - assert.Contains(t, stderr, "helm2: Chart has new version 0.2.0 available.") - assert.Contains(t, stderr, "helm3: Chart has new version 1.2.1 available.") + assert.Contains(t, stderr, "helm1: Chart has new version 0.1.1 available") + assert.Contains(t, stderr, "helm2: Chart has new version 0.2.0 available") + assert.Contains(t, stderr, "helm3: Chart has new version 1.2.1 available") c1 := p.GetYaml("helm1/helm-chart.yaml") c2 := p.GetYaml("helm2/helm-chart.yaml") @@ -468,3 +472,36 @@ func TestHelmRenderOfflineKubernetes(t *testing.T) { "kubeVersion": "v1.22.1", }, cm1.Object["data"]) } + +func getChartDir(t *testing.T, p *test_utils.TestProject, url2 string, chartName string, chartVersion string) string { + u, err := url.Parse(url2) + if err != nil { + t.Fatal(err) + } + var dir string + if u.Scheme == "oci" { + dir = filepath.Join(p.LocalRepoDir(), ".helm-charts", fmt.Sprintf("%s_%s", u.Scheme, u.Hostname()), chartName) + } else { + dir = filepath.Join(p.LocalRepoDir(), ".helm-charts", fmt.Sprintf("%s_%s_%s", u.Scheme, u.Port(), u.Hostname()), chartName) + } + if chartVersion != "" { + dir = filepath.Join(dir, chartVersion) + } + return dir +} + +func getChartFile(t *testing.T, p *test_utils.TestProject, url2 string, chartName string, chartVersion string) string { + return filepath.Join(getChartDir(t, p, url2, chartName, chartVersion), "Chart.yaml") +} + +func listChartVersions(t *testing.T, p *test_utils.TestProject, url2 string, chartName string) []string { + des, err := os.ReadDir(getChartDir(t, p, url2, chartName, "")) + assert.NoError(t, err) + + var versions []string + for _, de := range des { + versions = append(versions, de.Name()) + } + sort.Strings(versions) + return versions +} diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 72688db84..05df656bd 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -193,29 +193,27 @@ func (di *DeploymentItem) renderHelmCharts() error { return err } - chart, err := helm.NewHelmChart(di.ctx.HelmChartsDir, p) + hr, err := helm.NewRelease(p, di.ctx.HelmChartsDir, di.ctx.HelmCredentials) if err != nil { return err } - chart.SetCredentials(di.ctx.HelmCredentials) - ky, err := di.readKustomizationYaml(subDir) if err == nil && ky != nil { resources, _, _ := ky.GetNestedStringList("resources") found := false for _, r := range resources { - if r == chart.GetOutputPath() { + if r == hr.GetOutputPath() { found = true break } } if !found { - return fmt.Errorf("%s/kustomization.yaml does not include the rendered helm chart: %s", di.RelRenderedDir, chart.GetOutputPath()) + return fmt.Errorf("%s/kustomization.yaml does not include the rendered helm chart: %s", di.RelRenderedDir, hr.GetOutputPath()) } } - return chart.Render(di.ctx.Ctx, di.ctx.K, di.ctx.K8sVersion, di.ctx.SopsDecrypter) + return hr.Render(di.ctx.Ctx, di.ctx.K, di.ctx.K8sVersion, di.ctx.SopsDecrypter) }) if err != nil { return err @@ -418,7 +416,7 @@ func (di *DeploymentItem) generateKustomizationYaml(subDir string) (*uo.Unstruct if di.isHelmValuesYaml(de.Name()) { continue } else if di.isHelmChartYaml(de.Name()) { - c, err := helm.NewHelmChart(di.ctx.HelmChartsDir, filepath.Join(di.RenderedDir, subDir, de.Name())) + c, err := helm.NewRelease(filepath.Join(di.RenderedDir, subDir, de.Name()), di.ctx.HelmChartsDir, nil) if err != nil { return nil, err } diff --git a/pkg/helm/chart.go b/pkg/helm/chart.go new file mode 100644 index 000000000..c5c29e54b --- /dev/null +++ b/pkg/helm/chart.go @@ -0,0 +1,409 @@ +package helm + +import ( + "context" + "fmt" + "github.com/Masterminds/semver/v3" + "github.com/kluctl/kluctl/v2/pkg/registries" + "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/utils" + cp "github.com/otiai10/copy" + "github.com/rogpeppe/go-internal/lockedfile" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/registry" + "helm.sh/helm/v3/pkg/repo" + "net/url" + "os" + "path/filepath" + "regexp" + "sort" + "strings" +) + +type HelmCredentialsProvider interface { + FindCredentials(repoUrl string, credentialsId *string) *repo.Entry +} + +type Chart struct { + repo string + chartName string + + credentials HelmCredentialsProvider + credentialsId string + + versions []string +} + +func NewChart(repo string, chartName string, credentialsProvider HelmCredentialsProvider, credentialsId string) (*Chart, error) { + hc := &Chart{ + repo: repo, + credentials: credentialsProvider, + credentialsId: credentialsId, + } + + if repo == "" { + return nil, fmt.Errorf("repo is missing") + } + + if registry.IsOCI(repo) { + if chartName != "" { + return nil, fmt.Errorf("chartName can't be specified when using OCI repos") + } + s := strings.Split(repo, "/") + chartName := s[len(s)-1] + if m, _ := regexp.MatchString(`[a-zA-Z_-]+`, chartName); !m { + return nil, fmt.Errorf("invalid oci chart url: %s", repo) + } + hc.chartName = chartName + } else if chartName == "" { + return nil, fmt.Errorf("chartName is missing") + } else { + hc.chartName = chartName + } + + return hc, nil +} + +func (c *Chart) GetRepo() string { + return c.repo +} + +func (c *Chart) BuildPulledChartDir(baseDir string, version string) (string, error) { + u, err := url.Parse(c.repo) + if err != nil { + return "", err + } + + scheme := "" + port := "" + switch { + case u.Scheme == "oci": + scheme = "oci" + case u.Scheme == "http": + scheme = "http" + if u.Port() != "80" { + port = u.Port() + } + case u.Scheme == "https": + scheme = "https" + if u.Port() != "443" { + port = u.Port() + } + default: + return "", fmt.Errorf("unsupported scheme in %s", u.String()) + } + if port != "" { + scheme += "_" + port + } + + dir := filepath.Join( + baseDir, + fmt.Sprintf("%s_%s", scheme, strings.ToLower(u.Hostname())), + filepath.FromSlash(strings.ToLower(u.Path)), + ) + if u.Scheme != "oci" { + dir = filepath.Join(dir, c.chartName) + } + if version != "" { + dir = filepath.Join(dir, version) + } + err = utils.CheckInDir(baseDir, dir) + if err != nil { + return "", err + } + + return dir, nil +} + +func (c *Chart) GetChartName() string { + return c.chartName +} + +func (c *Chart) PullToTmp(ctx context.Context, version string) (*PulledChart, error) { + tmpPullDir, err := os.MkdirTemp(utils.GetTmpBaseDir(ctx), c.chartName+"-pull-") + if err != nil { + return nil, err + } + defer os.RemoveAll(tmpPullDir) + + cfg, err := buildHelmConfig(nil) + if err != nil { + return nil, err + } + a := action.NewPullWithOpts(action.WithConfig(cfg)) + a.Settings = cli.New() + a.Untar = true + a.DestDir = tmpPullDir + a.Version = version + + if c.credentialsId != "" { + if registry.IsOCI(c.repo) { + return nil, fmt.Errorf("OCI charts can currently only be authenticated via registry login and not via cli arguments") + } + if c.credentials == nil { + return nil, fmt.Errorf("no credentials provider") + } + } + + if c.credentials != nil { + p := &c.credentialsId + if c.credentialsId == "" { + p = nil + } + creds := c.credentials.FindCredentials(c.repo, p) + if creds != nil { + a.Username = creds.Username + a.Password = creds.Password + a.CertFile = creds.CertFile + a.CaFile = creds.CAFile + a.KeyFile = creds.KeyFile + a.InsecureSkipTLSverify = creds.InsecureSkipTLSverify + a.PassCredentialsAll = creds.PassCredentialsAll + } + } + + var out string + if registry.IsOCI(c.repo) { + out, err = a.Run(c.repo) + } else { + a.RepoURL = c.repo + out, err = a.Run(c.chartName) + } + if out != "" { + status.PlainText(ctx, out) + } + if err != nil { + return nil, err + } + + chartDir, err := os.MkdirTemp(utils.GetTmpBaseDir(ctx), c.chartName+"-pulled-") + if err != nil { + return nil, err + } + + // move chart + des, err := os.ReadDir(filepath.Join(tmpPullDir, c.chartName)) + if err != nil { + return nil, err + } + for _, de := range des { + err = os.Rename(filepath.Join(tmpPullDir, c.chartName, de.Name()), filepath.Join(chartDir, de.Name())) + if err != nil { + return nil, err + } + } + + return NewPulledChart(c, version, chartDir, true), nil +} + +func (c *Chart) Pull(ctx context.Context, pc *PulledChart) error { + newPulled, err := c.PullToTmp(ctx, pc.version) + if err != nil { + return err + } + defer os.RemoveAll(newPulled.dir) + + err = os.RemoveAll(pc.dir) + if err != nil { + return err + } + + _ = os.MkdirAll(filepath.Dir(pc.dir), 0o755) + + err = cp.Copy(newPulled.dir, pc.dir) + if err != nil { + return err + } + + return nil +} + +func (c *Chart) doPullCached(ctx context.Context, version string) (*PulledChart, *lockedfile.File, error) { + baseDir := filepath.Join(utils.GetTmpBaseDir(ctx), "helm-charts") + cacheDir, err := c.BuildPulledChartDir(baseDir, version) + _ = os.MkdirAll(cacheDir, 0o755) + + lock, err := lockedfile.Create(cacheDir + ".lock") + if err != nil { + return nil, nil, err + } + + cached := NewPulledChart(c, version, cacheDir, true) + needsPull, _, _, err := cached.CheckNeedsPull() + if err != nil { + _ = lock.Close() + return nil, nil, err + } + if !needsPull { + return cached, lock, nil + } + + err = c.Pull(ctx, cached) + if err != nil { + _ = lock.Close() + return nil, nil, err + } + + return cached, lock, nil +} + +func (c *Chart) PullCached(ctx context.Context, version string) (*PulledChart, error) { + pc, lock, err := c.doPullCached(ctx, version) + if err != nil { + return nil, err + } + _ = lock.Close() + return pc, nil +} + +func (c *Chart) PullInProject(ctx context.Context, baseDir string, version string) (*PulledChart, error) { + cachePc, lock, err := c.doPullCached(ctx, version) + if err != nil { + return nil, err + } + defer lock.Close() + + pc, err := c.GetPulledChart(baseDir, version) + if err != nil { + return nil, err + } + + err = os.RemoveAll(pc.dir) + if err != nil { + return nil, err + } + + err = cp.Copy(cachePc.dir, pc.dir) + if err != nil { + return nil, err + } + + return pc, nil +} + +func (c *Chart) GetPulledChart(baseDir string, version string) (*PulledChart, error) { + chartDir, err := c.BuildPulledChartDir(baseDir, version) + if err != nil { + return nil, err + } + return NewPulledChart(c, version, chartDir, false), nil +} + +func (c *Chart) QueryVersions(ctx context.Context) error { + if registry.IsOCI(c.repo) { + return c.queryVersionsOci(ctx) + } + return c.queryVersionsHelmRepo(ctx) +} + +func (c *Chart) queryVersionsOci(ctx context.Context) error { + rh := registries.NewRegistryHelper(ctx) + + imageName := strings.TrimPrefix(c.repo, "oci://") + tags, err := rh.ListImageTags(imageName) + if err != nil { + return err + } + + c.versions = tags + + return nil +} + +func (c *Chart) queryVersionsHelmRepo(ctx context.Context) error { + settings := cli.New() + + var e *repo.Entry + if c.credentialsId != "" { + if c.credentials == nil { + return fmt.Errorf("no credentials provider") + } + e = c.credentials.FindCredentials(c.repo, &c.credentialsId) + if e == nil { + return fmt.Errorf("no credentials provided for Chart %s", c.chartName) + } + } else { + e = &repo.Entry{ + URL: c.repo, + } + } + + r, err := repo.NewChartRepository(e, getter.All(settings)) + if err != nil { + return err + } + + r.CachePath, err = os.MkdirTemp(utils.GetTmpBaseDir(ctx), "helm-check-update-") + if err != nil { + return err + } + defer os.RemoveAll(r.CachePath) + + indexFile, err := r.DownloadIndexFile() + if err != nil { + return err + } + + index, err := repo.LoadIndexFile(indexFile) + if err != nil { + return err + } + + indexEntry, ok := index.Entries[c.chartName] + if !ok || len(indexEntry) == 0 { + return fmt.Errorf("helm chart %s not found in Repo index", c.chartName) + } + + c.versions = make([]string, 0, indexEntry.Len()) + for _, x := range indexEntry { + c.versions = append(c.versions, x.Version) + } + + return nil +} + +func (c *Chart) GetLatestVersion(constraints *string) (string, error) { + if len(c.versions) == 0 { + return "", fmt.Errorf("no versions found or queried") + } + + var err error + var updateConstraints *semver.Constraints + if constraints != nil { + updateConstraints, err = semver.NewConstraint(*constraints) + if err != nil { + return "", fmt.Errorf("invalid constraints '%s': %w", *constraints, err) + } + } + + var versions semver.Collection + for _, x := range c.versions { + v, err := semver.NewVersion(x) + if err != nil { + continue + } + + if updateConstraints == nil { + if v.Prerelease() != "" { + // we don't allow pre-releases by default. To allow pre-releases, use 1.0.0-0 as constraint + continue + } + } else if !updateConstraints.Check(v) { + continue + } + versions = append(versions, v) + } + if len(versions) == 0 { + if constraints == nil { + return "", fmt.Errorf("no version found") + } else { + return "", fmt.Errorf("no version found that satisfies constraints '%s'", *constraints) + } + } + + sort.Stable(versions) + latestVersion := versions[len(versions)-1].Original() + return latestVersion, nil +} diff --git a/pkg/helm/helm_chart.go b/pkg/helm/helm_chart.go deleted file mode 100644 index 6c1f8e4dd..000000000 --- a/pkg/helm/helm_chart.go +++ /dev/null @@ -1,673 +0,0 @@ -package helm - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "fmt" - "github.com/Masterminds/semver/v3" - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/registries" - "github.com/kluctl/kluctl/v2/pkg/sops" - "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" - "github.com/pkg/errors" - "github.com/rogpeppe/go-internal/lockedfile" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/cli/values" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/registry" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/repo" - "k8s.io/apimachinery/pkg/runtime/schema" - "net/url" - "os" - "path/filepath" - "regexp" - "sort" - "strings" - "time" -) - -type HelmCredentialsProvider interface { - FindCredentials(repoUrl string, credentialsId *string) *repo.Entry -} - -type HelmChart struct { - ConfigFile string - Config *types.HelmChartConfig - - credentials HelmCredentialsProvider - - chartName string - chartDir string - deprecatedChartDir string - - versions []string -} - -func NewHelmChart(baseChartsDir string, configFile string) (*HelmChart, error) { - var config types.HelmChartConfig - err := yaml.ReadYamlFile(configFile, &config) - if err != nil { - return nil, err - } - - _, err = semver.NewVersion(config.ChartVersion) - if err != nil { - return nil, fmt.Errorf("invalid chart version '%s': %w", config.ChartVersion, err) - } - - hc := &HelmChart{ - ConfigFile: configFile, - Config: &config, - } - - if registry.IsOCI(hc.Config.Repo) { - s := strings.Split(hc.Config.Repo, "/") - chartName := s[len(s)-1] - if m, _ := regexp.MatchString(`[a-zA-Z_-]+`, chartName); !m { - return nil, fmt.Errorf("invalid oci chart url: %s", hc.Config.Repo) - } - hc.chartName = chartName - } else if hc.Config.ChartName == "" { - return nil, fmt.Errorf("chartName is missing in helm-chart.yml") - } else { - hc.chartName = hc.Config.ChartName - } - - dir := filepath.Dir(configFile) - chartDir := filepath.Join(dir, "charts") - chartDir, err = securejoin.SecureJoin(chartDir, hc.chartName) - if err != nil { - return nil, err - } - hc.deprecatedChartDir = chartDir - - hc.chartDir, err = hc.buildChartDir(baseChartsDir) - if err != nil { - return nil, err - } - - return hc, nil -} - -func (c *HelmChart) buildChartDir(baseChartsDir string) (string, error) { - u, err := url.Parse(c.Config.Repo) - if err != nil { - return "", err - } - - scheme := "" - port := "" - switch { - case registry.IsOCI(c.Config.Repo): - scheme = "oci" - case u.Scheme == "http": - scheme = "http" - if u.Port() != "80" { - port = u.Port() - } - case u.Scheme == "https": - scheme = "https" - if u.Port() != "443" { - port = u.Port() - } - default: - return "", fmt.Errorf("unsupported scheme in %s", u.String()) - } - if port != "" { - scheme += "_" + port - } - - dir := filepath.Join( - baseChartsDir, - fmt.Sprintf("%s_%s", scheme, strings.ToLower(u.Hostname())), - filepath.FromSlash(strings.ToLower(u.Path)), - c.chartName, - ) - err = utils.CheckInDir(baseChartsDir, dir) - if err != nil { - return "", err - } - - return dir, nil -} - -func (c *HelmChart) GetChartName() string { - return c.chartName -} - -func (c *HelmChart) GetChartDir(withVersion bool) string { - if withVersion { - return filepath.Join(c.chartDir, c.Config.ChartVersion) - } - return c.chartDir -} - -func (c *HelmChart) GetDeprecatedChartDir() string { - return c.deprecatedChartDir -} - -func (c *HelmChart) GetOutputPath() string { - output := "helm-rendered.yaml" - if c.Config.Output != nil { - output = *c.Config.Output - } - return output -} - -func (c *HelmChart) GetFullOutputPath() (string, error) { - dir := filepath.Dir(c.ConfigFile) - return securejoin.SecureJoin(dir, c.GetOutputPath()) -} - -func (c *HelmChart) buildHelmConfig(k *k8s.K8sCluster) (*action.Configuration, error) { - rc, err := registry.NewClient() - if err != nil { - return nil, err - } - - return &action.Configuration{ - RESTClientGetter: k, - RegistryClient: rc, - }, nil -} - -func (c *HelmChart) checkNeedsPull(chartDir string, isTmp bool) (bool, bool, string, error) { - if !utils.IsDirectory(chartDir) { - return true, false, "", nil - } - - chartYamlPath := yaml.FixPathExt(filepath.Join(chartDir, "Chart.yaml")) - st, err := os.Stat(chartYamlPath) - if err != nil { - if os.IsNotExist(err) { - return true, false, "", nil - } - return false, false, "", err - } - if isTmp && time.Now().Sub(st.ModTime()) >= time.Hour*24 { - // MacOS will delete tmp files after 3 days, so lets be safe and re-pull every day - return true, false, "", nil - } - - chartYaml, err := uo.FromFile(chartYamlPath) - if err != nil { - return false, false, "", err - } - - version, _, _ := chartYaml.GetNestedString("version") - if version != c.Config.ChartVersion { - return true, true, version, nil - } - return false, false, "", nil -} - -func (c *HelmChart) Pull(ctx context.Context) error { - if utils.Exists(c.GetDeprecatedChartDir()) { - err := os.RemoveAll(c.GetDeprecatedChartDir()) - if err != nil { - return err - } - } - - return c.doPull(ctx, c.GetChartDir(true)) -} - -func (c *HelmChart) pullTmpChart(ctx context.Context) (string, error) { - hash := sha256.New() - _, _ = fmt.Fprintf(hash, "%s\n", c.Config.Repo) - _, _ = fmt.Fprintf(hash, "%s\n", c.chartName) - _, _ = fmt.Fprintf(hash, "%s\n", c.Config.ChartVersion) - h := hex.EncodeToString(hash.Sum(nil)) - tmpDir := filepath.Join(utils.GetTmpBaseDir(ctx), "helm-charts") - _ = os.MkdirAll(tmpDir, 0o755) - tmpDir = filepath.Join(tmpDir, fmt.Sprintf("%s-%s", c.chartName, h)) - - lockFile := tmpDir + ".lock" - lock, err := lockedfile.Create(lockFile) - if err != nil { - return "", err - } - defer lock.Close() - - needsPull, _, _, err := c.checkNeedsPull(tmpDir, true) - if err != nil { - return "", err - } - if !needsPull { - return tmpDir, nil - } - err = c.doPull(ctx, tmpDir) - if err != nil { - return "", err - } - return tmpDir, nil -} - -func (c *HelmChart) doPull(ctx context.Context, chartDir string) error { - baseDir := filepath.Dir(chartDir) - - _ = os.RemoveAll(chartDir) - _ = os.MkdirAll(baseDir, 0o755) - - // need to use the same filesystem/volume that we later os.Rename the final pull chart to, as otherwise - // the rename operation will lead to errors - tmpDir, err := os.MkdirTemp(baseDir, c.chartName+"-pull-") - if err != nil { - return err - } - defer os.RemoveAll(tmpDir) - - tmpChartDir := filepath.Join(tmpDir, c.chartName) - - cfg, err := c.buildHelmConfig(nil) - if err != nil { - return err - } - a := action.NewPullWithOpts(action.WithConfig(cfg)) - a.Settings = cli.New() - a.Untar = true - a.DestDir = tmpDir - a.Version = c.Config.ChartVersion - - if c.Config.CredentialsId != nil { - if registry.IsOCI(c.Config.Repo) { - return fmt.Errorf("OCI charts can currently only be authenticated via registry login and not via cli arguments") - } - if c.credentials == nil { - return fmt.Errorf("no credentials provider") - } - } - - if c.credentials != nil { - creds := c.credentials.FindCredentials(c.Config.Repo, c.Config.CredentialsId) - if creds != nil { - a.Username = creds.Username - a.Password = creds.Password - a.CertFile = creds.CertFile - a.CaFile = creds.CAFile - a.KeyFile = creds.KeyFile - a.InsecureSkipTLSverify = creds.InsecureSkipTLSverify - a.PassCredentialsAll = creds.PassCredentialsAll - } - } - - var out string - if registry.IsOCI(c.Config.Repo) { - out, err = a.Run(c.Config.Repo) - } else { - a.RepoURL = c.Config.Repo - out, err = a.Run(c.chartName) - } - if err != nil { - return err - } - - // a bug in the Pull command causes this directory to be created by accident - _ = os.RemoveAll(tmpChartDir + fmt.Sprintf("-%s.tar.gz", a.Version)) - _ = os.RemoveAll(tmpChartDir + fmt.Sprintf("-%s.tgz", a.Version)) - if out != "" { - status.PlainText(ctx, out) - } - - err = os.Rename(tmpChartDir, chartDir) - if err != nil { - return err - } - - return nil -} - -func (c *HelmChart) QueryLatestVersion(ctx context.Context) (string, error) { - if registry.IsOCI(c.Config.Repo) { - return c.queryLatestVersionOci(ctx) - } - return c.queryLatestVersionHelmRepo(ctx) -} - -func (c *HelmChart) queryLatestVersionOci(ctx context.Context) (string, error) { - rh := registries.NewRegistryHelper(ctx) - - imageName := strings.TrimPrefix(c.Config.Repo, "oci://") - tags, err := rh.ListImageTags(imageName) - if err != nil { - return "", err - } - - latestVersion, err := c.findLatestVersion(tags) - if err != nil { - return "", err - } - - return latestVersion, nil -} - -func (c *HelmChart) queryLatestVersionHelmRepo(ctx context.Context) (string, error) { - settings := cli.New() - - var e *repo.Entry - if c.Config.CredentialsId != nil { - if c.credentials == nil { - return "", fmt.Errorf("no credentials provider") - } - e = c.credentials.FindCredentials(c.Config.Repo, c.Config.CredentialsId) - if e == nil { - return "", fmt.Errorf("no credentials provided for Chart %s", c.chartName) - } - } else { - e = &repo.Entry{ - URL: c.Config.Repo, - } - } - - r, err := repo.NewChartRepository(e, getter.All(settings)) - if err != nil { - return "", err - } - - r.CachePath, err = os.MkdirTemp(utils.GetTmpBaseDir(ctx), "helm-check-update-") - if err != nil { - return "", err - } - defer os.RemoveAll(r.CachePath) - - indexFile, err := r.DownloadIndexFile() - if err != nil { - return "", err - } - - index, err := repo.LoadIndexFile(indexFile) - if err != nil { - return "", err - } - - indexEntry, ok := index.Entries[c.chartName] - if !ok || len(indexEntry) == 0 { - return "", fmt.Errorf("helm chart %s not found in repo index", c.chartName) - } - - versions := make([]string, 0, indexEntry.Len()) - for _, x := range indexEntry { - versions = append(versions, x.Version) - } - - latestVersion, err := c.findLatestVersion(versions) - if err != nil { - return "", err - } - - return latestVersion, nil -} - -func (c *HelmChart) findLatestVersion(inputVersions []string) (string, error) { - var err error - var updateConstraints *semver.Constraints - if c.Config.UpdateConstraints != nil { - updateConstraints, err = semver.NewConstraint(*c.Config.UpdateConstraints) - if err != nil { - return "", fmt.Errorf("invalid update constraints '%s': %w", *c.Config.UpdateConstraints, err) - } - } - - var versions semver.Collection - for _, x := range inputVersions { - v, err := semver.NewVersion(x) - if err != nil { - continue - } - - if updateConstraints == nil { - if v.Prerelease() != "" { - // we don't allow pre-releases by default. To allow pre-releases, use 1.0.0-0 as constraint - continue - } - } else if !updateConstraints.Check(v) { - continue - } - versions = append(versions, v) - } - if len(versions) == 0 { - if c.Config.UpdateConstraints == nil { - return "", fmt.Errorf("no version found") - } else { - return "", fmt.Errorf("no version found that satisfies constraints '%s'", *c.Config.UpdateConstraints) - } - } - - sort.Stable(versions) - latestVersion := versions[len(versions)-1].Original() - return latestVersion, nil -} - -func (c *HelmChart) Render(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { - deprecatedChartDir := c.GetDeprecatedChartDir() - if utils.IsDirectory(deprecatedChartDir) { - status.Deprecation(ctx, "helm-charts-dir", "Your project has pre-pulled charts located beside the helm-chart.yaml, which is deprecated. "+ - "Please run 'kluctl helm-pull' on your project and ensure that the deprecated charts are removed! Future versions of kluctl will ignore these locations.") - } - - err := c.doRender(ctx, k, k8sVersion, sopsDecrypter) - if err != nil { - return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", c.chartName, c.Config.ReleaseName, err) - } - return nil -} - -func (c *HelmChart) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { - chartDir := c.deprecatedChartDir - - needsPull, versionChanged, prePulledVersion, err := c.checkNeedsPull(chartDir, false) - if err != nil { - return err - } - if needsPull { - if versionChanged { - return fmt.Errorf("pre-pulled Helm Chart %s need to be pulled (call 'kluctl helm-pull'). "+ - "Desired version is %s while pre-pulled version is %s", c.chartName, c.Config.ChartVersion, prePulledVersion) - } else { - status.Warning(ctx, "Warning, need to pull Helm Chart %s with version %s. "+ - "Please consider pre-pulling it with 'kluctl helm-pull'", c.chartName, c.Config.ChartVersion) - } - - s := status.Start(ctx, "Pulling Helm Chart %s with version %s", c.chartName, c.Config.ChartVersion) - defer s.Failed() - - chartDir, err = c.pullTmpChart(ctx) - if err != nil { - return err - } - s.Success() - } - - outputPath, err := c.GetFullOutputPath() - if err != nil { - return err - } - valuesPath := yaml.FixPathExt(filepath.Join(filepath.Dir(c.ConfigFile), "helm-values.yml")) - - var gvs []schema.GroupVersion - if k != nil { - gvs, err = k.Resources.GetAllGroupVersions() - if err != nil { - return err - } - } - cfg, err := c.buildHelmConfig(k) - if err != nil { - return err - } - - settings := cli.New() - valueOpts := values.Options{} - - if utils.Exists(valuesPath) { - tmpValues, err := sops.MaybeDecryptFileToTmp(ctx, sopsDecrypter, valuesPath) - if err != nil { - return err - } - defer os.Remove(tmpValues) - valueOpts.ValueFiles = append(valueOpts.ValueFiles, tmpValues) - } - - var kubeVersion *chartutil.KubeVersion - if k != nil { - kubeVersion, err = chartutil.ParseKubeVersion(k.ServerVersion.String()) - if err != nil { - return err - } - } - if k8sVersion != "" { - kubeVersion, err = chartutil.ParseKubeVersion(k8sVersion) - if err != nil { - return err - } - } - - namespace := "default" - if c.Config.Namespace != nil { - namespace = *c.Config.Namespace - } - - client := action.NewInstall(cfg) - client.DryRun = true - client.Namespace = namespace - client.ReleaseName = c.Config.ReleaseName - client.Replace = true - client.ClientOnly = true - client.KubeVersion = kubeVersion - - if c.Config.SkipCRDs { - client.SkipCRDs = true - } else { - client.IncludeCRDs = true - } - for _, gv := range gvs { - client.APIVersions = append(client.APIVersions, gv.String()) - } - - p := getter.All(settings) - vals, err := valueOpts.MergeValues(p) - if err != nil { - return err - } - - // Check chart dependencies to make sure all are present in /charts - chartRequested, err := loader.Load(chartDir) - if err != nil { - return err - } - - if err := checkIfInstallable(chartRequested); err != nil { - return err - } - - if chartRequested.Metadata.Deprecated { - status.Warning(ctx, "Chart %s is deprecated", c.Config.ChartName) - } - - rel, err := client.Run(chartRequested, vals) - if err != nil { - return err - } - - parsed, err := c.parseRenderedManifests(rel.Manifest) - if err != nil { - return err - } - - if !client.DisableHooks { - for _, m := range rel.Hooks { - if isTestHook(m) { - continue - } - parsedHooks, err := c.parseRenderedManifests(m.Manifest) - if err != nil { - return err - } - parsed = append(parsed, parsedHooks...) - } - } - - var fixed []interface{} - for _, o := range parsed { - // "helm install" will deploy resources to the given namespace automatically, but "helm template" does not - // add the necessary namespace in the rendered resources - if k != nil { - err = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { - k.Resources.FixNamespace(o, namespace) - return nil - }) - if err != nil { - return err - } - } - fixed = append(fixed, o) - } - rendered, err := yaml.WriteYamlAllBytes(fixed) - if err != nil { - return err - } - - err = os.WriteFile(outputPath, rendered, 0o600) - if err != nil { - return err - } - return nil -} - -func (c *HelmChart) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, error) { - var parsed []*uo.UnstructuredObject - - duplicatesRemoved, err := yaml.RemoveDuplicateFields(strings.NewReader(s)) - if err != nil { - return nil, err - } - - m, err := yaml.ReadYamlAllBytes(duplicatesRemoved) - if err != nil { - return nil, err - } - for _, x := range m { - x2, ok := x.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("yaml object is not a map") - } - parsed = append(parsed, uo.FromMap(x2)) - } - return parsed, nil -} - -func (c *HelmChart) SetCredentials(p HelmCredentialsProvider) { - c.credentials = p -} - -func checkIfInstallable(ch *chart.Chart) error { - switch ch.Metadata.Type { - case "", "application": - return nil - } - return errors.Errorf("%s charts are not installable", ch.Metadata.Type) -} - -func isTestHook(h *release.Hook) bool { - for _, e := range h.Events { - if e == release.HookTest { - return true - } - } - return false -} - -func (c *HelmChart) Save() error { - return yaml.WriteYamlFile(c.ConfigFile, c.Config) -} diff --git a/pkg/helm/helm_release.go b/pkg/helm/helm_release.go new file mode 100644 index 000000000..885082193 --- /dev/null +++ b/pkg/helm/helm_release.go @@ -0,0 +1,313 @@ +package helm + +import ( + "context" + "fmt" + "github.com/Masterminds/semver/v3" + securejoin "github.com/cyphar/filepath-securejoin" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/sops" + "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/cli/values" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/release" + "k8s.io/apimachinery/pkg/runtime/schema" + "os" + "path/filepath" + "strings" +) + +type Release struct { + ConfigFile string + Config *types.HelmChartConfig + Chart *Chart + + baseChartsDir string +} + +func NewRelease(configFile string, baseChartsDir string, credentialsProvider HelmCredentialsProvider) (*Release, error) { + var config types.HelmChartConfig + err := yaml.ReadYamlFile(configFile, &config) + if err != nil { + return nil, err + } + + _, err = semver.NewVersion(config.ChartVersion) + if err != nil { + return nil, fmt.Errorf("invalid chart version '%s': %w", config.ChartVersion, err) + } + + credentialsId := "" + if config.CredentialsId != nil { + credentialsId = *config.CredentialsId + } + chart, err := NewChart(config.Repo, config.ChartName, credentialsProvider, credentialsId) + if err != nil { + return nil, err + } + + hr := &Release{ + ConfigFile: configFile, + Config: &config, + baseChartsDir: baseChartsDir, + Chart: chart, + } + + return hr, nil +} + +func (hr *Release) GetOutputPath() string { + output := "helm-rendered.yaml" + if hr.Config.Output != nil { + output = *hr.Config.Output + } + return output +} + +func (hr *Release) GetFullOutputPath() (string, error) { + dir := filepath.Dir(hr.ConfigFile) + return securejoin.SecureJoin(dir, hr.GetOutputPath()) +} + +func (hr *Release) Render(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { + err := hr.doRender(ctx, k, k8sVersion, sopsDecrypter) + if err != nil { + return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", hr.Chart.GetChartName(), hr.Config.ReleaseName, err) + } + return nil +} + +func (hr *Release) GetDeprecatedChartDir() string { + dir := filepath.Dir(hr.ConfigFile) + return filepath.Join(dir, "charts", hr.Chart.GetChartName()) +} + +func (hr *Release) getPulledChart(ctx context.Context) (*PulledChart, error) { + deprecatedPC := NewPulledChart(hr.Chart, hr.Config.ChartVersion, hr.GetDeprecatedChartDir(), false) + if deprecatedPC.CheckExists() { + status.Deprecation(ctx, "helm-charts-dir", "Your project has pre-pulled charts located next to the helm-chart.yaml, which is deprecated. "+ + "Please run 'kluctl helm-pull' on your project and ensure that the deprecated charts are removed! Future versions of kluctl will ignore these locations.") + return deprecatedPC, nil + } + pc, err := hr.Chart.GetPulledChart(hr.baseChartsDir, hr.Config.ChartVersion) + if err != nil { + return nil, err + } + + needsPull, versionChanged, prePulledVersion, err := pc.CheckNeedsPull() + if err != nil { + return nil, err + } + if needsPull { + if versionChanged { + return nil, fmt.Errorf("pre-pulled Helm Chart %s need to be pulled (call 'kluctl helm-pull'). "+ + "Desired version is %s while pre-pulled version is %s", hr.Chart.GetChartName(), hr.Config.ChartVersion, prePulledVersion) + } + + s := status.Start(ctx, "Pulling Helm Chart %s with version %s", hr.Chart.GetChartName(), hr.Config.ChartVersion) + defer s.Failed() + + pc, err = hr.Chart.PullCached(ctx, hr.Config.ChartVersion) + if err != nil { + return nil, err + } + s.Success() + } + + return pc, nil +} + +func (hr *Release) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { + pc, err := hr.getPulledChart(ctx) + if err != nil { + return err + } + + outputPath, err := hr.GetFullOutputPath() + if err != nil { + return err + } + valuesPath := yaml.FixPathExt(filepath.Join(filepath.Dir(hr.ConfigFile), "helm-values.yml")) + + var gvs []schema.GroupVersion + if k != nil { + gvs, err = k.Resources.GetAllGroupVersions() + if err != nil { + return err + } + } + cfg, err := buildHelmConfig(k) + if err != nil { + return err + } + + settings := cli.New() + valueOpts := values.Options{} + + if utils.Exists(valuesPath) { + tmpValues, err := sops.MaybeDecryptFileToTmp(ctx, sopsDecrypter, valuesPath) + if err != nil { + return err + } + defer os.Remove(tmpValues) + valueOpts.ValueFiles = append(valueOpts.ValueFiles, tmpValues) + } + + var kubeVersion *chartutil.KubeVersion + if k != nil { + kubeVersion, err = chartutil.ParseKubeVersion(k.ServerVersion.String()) + if err != nil { + return err + } + } + if k8sVersion != "" { + kubeVersion, err = chartutil.ParseKubeVersion(k8sVersion) + if err != nil { + return err + } + } + + namespace := "default" + if hr.Config.Namespace != nil { + namespace = *hr.Config.Namespace + } + + client := action.NewInstall(cfg) + client.DryRun = true + client.Namespace = namespace + client.ReleaseName = hr.Config.ReleaseName + client.Replace = true + client.ClientOnly = true + client.KubeVersion = kubeVersion + + if hr.Config.SkipCRDs { + client.SkipCRDs = true + } else { + client.IncludeCRDs = true + } + for _, gv := range gvs { + client.APIVersions = append(client.APIVersions, gv.String()) + } + + p := getter.All(settings) + vals, err := valueOpts.MergeValues(p) + if err != nil { + return err + } + + // Check chart dependencies to make sure all are present in /charts + chartRequested, err := loader.Load(pc.dir) + if err != nil { + return err + } + + if err := checkIfInstallable(chartRequested); err != nil { + return err + } + + if chartRequested.Metadata.Deprecated { + status.Warning(ctx, "Chart %s is deprecated", hr.Config.ChartName) + } + + rel, err := client.Run(chartRequested, vals) + if err != nil { + return err + } + + parsed, err := hr.parseRenderedManifests(rel.Manifest) + if err != nil { + return err + } + + if !client.DisableHooks { + for _, m := range rel.Hooks { + if isTestHook(m) { + continue + } + parsedHooks, err := hr.parseRenderedManifests(m.Manifest) + if err != nil { + return err + } + parsed = append(parsed, parsedHooks...) + } + } + + var fixed []interface{} + for _, o := range parsed { + // "helm install" will deploy resources to the given namespace automatically, but "helm template" does not + // add the necessary namespace in the rendered resources + if k != nil { + err = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { + k.Resources.FixNamespace(o, namespace) + return nil + }) + if err != nil { + return err + } + } + fixed = append(fixed, o) + } + rendered, err := yaml.WriteYamlAllBytes(fixed) + if err != nil { + return err + } + + err = os.WriteFile(outputPath, rendered, 0o600) + if err != nil { + return err + } + return nil +} + +func (hr *Release) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, error) { + var parsed []*uo.UnstructuredObject + + duplicatesRemoved, err := yaml.RemoveDuplicateFields(strings.NewReader(s)) + if err != nil { + return nil, err + } + + m, err := yaml.ReadYamlAllBytes(duplicatesRemoved) + if err != nil { + return nil, err + } + for _, x := range m { + x2, ok := x.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("yaml object is not a map") + } + parsed = append(parsed, uo.FromMap(x2)) + } + return parsed, nil +} + +func (hr *Release) Save() error { + return yaml.WriteYamlFile(hr.ConfigFile, hr.Config) +} + +func checkIfInstallable(ch *chart.Chart) error { + switch ch.Metadata.Type { + case "", "application": + return nil + } + return errors.Errorf("%s charts are not installable", ch.Metadata.Type) +} + +func isTestHook(h *release.Hook) bool { + for _, e := range h.Events { + if e == release.HookTest { + return true + } + } + return false +} diff --git a/pkg/helm/pulled_chart.go b/pkg/helm/pulled_chart.go new file mode 100644 index 000000000..d8c65a963 --- /dev/null +++ b/pkg/helm/pulled_chart.go @@ -0,0 +1,65 @@ +package helm + +import ( + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "os" + "path/filepath" + "time" +) + +type PulledChart struct { + chart *Chart + version string + dir string + isTmp bool +} + +func NewPulledChart(chart *Chart, version string, dir string, isTmp bool) *PulledChart { + pc := &PulledChart{ + chart: chart, + version: version, + dir: dir, + isTmp: isTmp, + } + return pc +} + +func (pc *PulledChart) CheckExists() bool { + if !utils.IsDirectory(pc.dir) { + return false + } + return true +} + +func (pc *PulledChart) CheckNeedsPull() (bool, bool, string, error) { + if !utils.IsDirectory(pc.dir) { + return true, false, "", nil + } + + chartYamlPath := yaml.FixPathExt(filepath.Join(pc.dir, "Chart.yaml")) + st, err := os.Stat(chartYamlPath) + if err != nil { + if os.IsNotExist(err) { + return true, false, "", nil + } + return false, false, "", err + } + + if pc.isTmp && time.Now().Sub(st.ModTime()) >= time.Hour*24 { + // MacOS will delete tmp files after 3 days, so lets be safe and re-pull every day + return true, false, "", nil + } + + chartYaml, err := uo.FromFile(chartYamlPath) + if err != nil { + return false, false, "", err + } + + version, _, _ := chartYaml.GetNestedString("version") + if version != pc.version { + return true, true, version, nil + } + return false, false, version, nil +} diff --git a/pkg/helm/utils.go b/pkg/helm/utils.go new file mode 100644 index 000000000..d02c70ef0 --- /dev/null +++ b/pkg/helm/utils.go @@ -0,0 +1,19 @@ +package helm + +import ( + "github.com/kluctl/kluctl/v2/pkg/k8s" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/registry" +) + +func buildHelmConfig(k *k8s.K8sCluster) (*action.Configuration, error) { + rc, err := registry.NewClient() + if err != nil { + return nil, err + } + + return &action.Configuration{ + RESTClientGetter: k, + RegistryClient: rc, + }, nil +} From 2e8b8b3187508c65a166549dd2f338080d63219f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Dec 2022 17:20:57 +0100 Subject: [PATCH 0664/2268] docs: Update docs in regard to pre-pulled charts location --- docs/reference/deployments/helm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index ee2c4ec21..f42fe69c3 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -17,7 +17,7 @@ The integration is split into 2 parts/steps/layers. The first is the management the second part handles configuration/customization and deployment of the chart. It is recommended to pre-pull Helm Charts with [`kluctl helm-pull`](../commands/helm-pull.md), which will store the -pulled charts near the `helm-chart.yaml` file (inside the `charts` sub-directory). It is however also possible (but not +pulled charts inside `.helm-charts` of the project directory. It is however also possible (but not recommended) to skip the pre-pulling phase and let kluctl pull Charts on-demand. When pre-pulling Helm Charts, you can also add the resulting Chart contents into version control. This is actually From 916db16b085fa09ea42a89db7e09d8ab4890474a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Dec 2022 17:26:41 +0100 Subject: [PATCH 0665/2268] fix: Honor --upgrade without --commit Fixes #158 --- cmd/kluctl/commands/cmd_helm_update.go | 74 ++++++++++++++------------ e2e/helm_test.go | 14 ++--- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index d40b274a6..22e0abc8b 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -192,10 +192,6 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { status.Info(ctx, "%s: Updated Chart version to %s", relDir, latestVersion) - if !cmd.Commit { - continue - } - key := fmt.Sprintf("%s / %s", hr.Chart.GetRepo(), hr.Chart.GetChartName()) uv := versionUseCounts[key] uv[oldVersion]-- @@ -246,7 +242,7 @@ func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, projectDir st return err } - s := status.Start(ctx, "%s: Committing Chart with version %s", relDir, newVersion) + s := status.Start(ctx, "%s: Upgrading Chart to version %s", relDir, newVersion) defer s.Failed() doError := func(err error) error { @@ -263,14 +259,16 @@ func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, projectDir st return doError(err) } - // add helm-chart.yaml - relToGit, err := filepath.Rel(gitRootPath, hr.ConfigFile) - if err != nil { - return doError(err) - } - _, err = wt.Add(relToGit) - if err != nil { - return doError(err) + if cmd.Commit { + // add helm-chart.yaml + relToGit, err := filepath.Rel(gitRootPath, hr.ConfigFile) + if err != nil { + return doError(err) + } + _, err = wt.Add(relToGit) + if err != nil { + return doError(err) + } } if deleteChart { @@ -282,9 +280,11 @@ func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, projectDir st if err != nil { return doError(err) } - _, err = wt.Remove(relChartDir) - if err != nil && err != index.ErrEntryNotFound { - return doError(err) + if cmd.Commit { + _, err = wt.Remove(relChartDir) + if err != nil && err != index.ErrEntryNotFound { + return doError(err) + } } err = os.RemoveAll(chartDir) if err != nil { @@ -315,34 +315,38 @@ func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, projectDir st return doError(err) } - _, err = wt.Add(relChartDir) - if err != nil { - return doError(err) - } - - // figure out what got deleted - for p := range files { - _, err := os.Lstat(filepath.Join(gitRootPath, p)) + if cmd.Commit { + _, err = wt.Add(relChartDir) if err != nil { - if os.IsNotExist(err) { - _, err = wt.Remove(p) - if err != nil { + return doError(err) + } + + // figure out what got deleted + for p := range files { + _, err := os.Lstat(filepath.Join(gitRootPath, p)) + if err != nil { + if os.IsNotExist(err) { + _, err = wt.Remove(p) + if err != nil { + return doError(err) + } + } else { return doError(err) } - } else { - return doError(err) } } } } - commitMsg := fmt.Sprintf("Updated helm chart %s to version %s", relToGit, newVersion) - _, err = wt.Commit(commitMsg, &git.CommitOptions{}) - if err != nil { - return doError(fmt.Errorf("failed to commit: %w", err)) - } + if cmd.Commit { + commitMsg := fmt.Sprintf("Updated helm chart %s to version %s", relDir, newVersion) + _, err = wt.Commit(commitMsg, &git.CommitOptions{}) + if err != nil { + return doError(fmt.Errorf("failed to commit: %w", err)) + } - s.Update("%s: Committed helm chart with version %s", relToGit, newVersion) + s.UpdateAndInfoFallback("%s: Committed helm chart with version %s", relDir, newVersion) + } s.Success() return nil diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 682163cca..03a6540bb 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -212,18 +212,18 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { assert.Contains(t, stderr, "helm3: Skipped update to version 0.2.0") if upgrade { - assert.Contains(t, stderr, "helm1: Updated Chart version to 0.2.0") - assert.Contains(t, stderr, "helm2: Updated Chart version to 0.3.0") + assert.Contains(t, stderr, "helm1: Upgrading Chart to version 0.2.0") + assert.Contains(t, stderr, "helm2: Upgrading Chart to version 0.3.0") } if commit { - assert.Contains(t, stderr, "helm1: Committing Chart with version 0.2.0") - assert.Contains(t, stderr, "helm2: Committing Chart with version 0.3.0") + assert.Contains(t, stderr, "helm1: Committed helm chart with version 0.2.0") + assert.Contains(t, stderr, "helm2: Committed helm chart with version 0.3.0") } pulledVersions1 := listChartVersions(t, p, repoUrl, "test-chart1") pulledVersions2 := listChartVersions(t, p, repoUrl, "test-chart2") - if commit { + if upgrade { assert.Equal(t, []string{"0.1.0", "0.2.0"}, pulledVersions1) assert.Equal(t, []string{"0.3.0"}, pulledVersions2) } else { @@ -248,8 +248,8 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { return commitList[i].Message < commitList[j].Message }) - assert.Equal(t, "Updated helm chart helm1/helm-chart.yaml to version 0.2.0", commitList[0].Message) - assert.Equal(t, "Updated helm chart helm2/helm-chart.yaml to version 0.3.0", commitList[1].Message) + assert.Equal(t, "Updated helm chart helm1 to version 0.2.0", commitList[0].Message) + assert.Equal(t, "Updated helm chart helm2 to version 0.3.0", commitList[1].Message) } } From eac6fff2eb89c01866ba36035ac41fb34f532852 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Dec 2022 17:39:45 +0100 Subject: [PATCH 0666/2268] chore: Better log output when pulling/ugprading --- cmd/kluctl/commands/cmd_helm_update.go | 8 ++++---- e2e/helm_test.go | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 22e0abc8b..1cc484d4c 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -170,7 +170,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { continue } - status.Info(ctx, "%s: Chart has new version %s available", relDir, latestVersion) + status.Info(ctx, "%s: Chart %s has new version %s available", relDir, hr.Chart.GetChartName(), latestVersion) if !cmd.Upgrade { continue @@ -242,7 +242,7 @@ func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, projectDir st return err } - s := status.Start(ctx, "%s: Upgrading Chart to version %s", relDir, newVersion) + s := status.Start(ctx, "%s: Upgrading Chart %s to version %s", relDir, hr.Chart.GetChartName(), newVersion) defer s.Failed() doError := func(err error) error { @@ -339,13 +339,13 @@ func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, projectDir st } if cmd.Commit { - commitMsg := fmt.Sprintf("Updated helm chart %s to version %s", relDir, newVersion) + commitMsg := fmt.Sprintf("Updated helm chart %s in %s to version %s", hr.Chart.GetChartName(), relDir, newVersion) _, err = wt.Commit(commitMsg, &git.CommitOptions{}) if err != nil { return doError(fmt.Errorf("failed to commit: %w", err)) } - s.UpdateAndInfoFallback("%s: Committed helm chart with version %s", relDir, newVersion) + s.UpdateAndInfoFallback("%s: Committed helm chart %s with version %s", relDir, hr.Chart.GetChartName(), newVersion) } s.Success() diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 03a6540bb..5b9a772d5 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -207,17 +207,17 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { } _, stderr := p.KluctlMust(args...) - assert.Contains(t, stderr, "helm1: Chart has new version 0.2.0 available") - assert.Contains(t, stderr, "helm2: Chart has new version 0.3.0 available") + assert.Contains(t, stderr, "helm1: Chart test-chart1 has new version 0.2.0 available") + assert.Contains(t, stderr, "helm2: Chart test-chart2 has new version 0.3.0 available") assert.Contains(t, stderr, "helm3: Skipped update to version 0.2.0") if upgrade { - assert.Contains(t, stderr, "helm1: Upgrading Chart to version 0.2.0") - assert.Contains(t, stderr, "helm2: Upgrading Chart to version 0.3.0") + assert.Contains(t, stderr, "helm1: Upgrading Chart test-chart1 to version 0.2.0") + assert.Contains(t, stderr, "helm2: Upgrading Chart test-chart2 to version 0.3.0") } if commit { - assert.Contains(t, stderr, "helm1: Committed helm chart with version 0.2.0") - assert.Contains(t, stderr, "helm2: Committed helm chart with version 0.3.0") + assert.Contains(t, stderr, "helm1: Committed helm chart test-chart1 with version 0.2.0") + assert.Contains(t, stderr, "helm2: Committed helm chart test-chart2 with version 0.3.0") } pulledVersions1 := listChartVersions(t, p, repoUrl, "test-chart1") @@ -248,8 +248,8 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { return commitList[i].Message < commitList[j].Message }) - assert.Equal(t, "Updated helm chart helm1 to version 0.2.0", commitList[0].Message) - assert.Equal(t, "Updated helm chart helm2 to version 0.3.0", commitList[1].Message) + assert.Equal(t, "Updated helm chart test-chart1 in helm1 to version 0.2.0", commitList[0].Message) + assert.Equal(t, "Updated helm chart test-chart2 in helm2 to version 0.3.0", commitList[1].Message) } } @@ -316,9 +316,9 @@ func testHelmUpdateConstraints(t *testing.T, oci bool) { args := []string{"helm-update", "--upgrade"} _, stderr := p.KluctlMust(args...) - assert.Contains(t, stderr, "helm1: Chart has new version 0.1.1 available") - assert.Contains(t, stderr, "helm2: Chart has new version 0.2.0 available") - assert.Contains(t, stderr, "helm3: Chart has new version 1.2.1 available") + assert.Contains(t, stderr, "helm1: Chart test-chart1 has new version 0.1.1 available") + assert.Contains(t, stderr, "helm2: Chart test-chart1 has new version 0.2.0 available") + assert.Contains(t, stderr, "helm3: Chart test-chart1 has new version 1.2.1 available") c1 := p.GetYaml("helm1/helm-chart.yaml") c2 := p.GetYaml("helm2/helm-chart.yaml") From c57d5419ff02f4a987e4d37ddcf41783cae7dc24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 22:08:18 +0000 Subject: [PATCH 0667/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.157 to 1.44.158 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.157 to 1.44.158. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.157...v1.44.158) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 484ebe921..cdc51572b 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.157 + github.com/aws/aws-sdk-go v1.44.158 github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index 5ce647007..8ccdc3d66 100644 --- a/go.sum +++ b/go.sum @@ -128,8 +128,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.157 h1:JVBPpEWC8+yA7CbfAuTl/ZFFlHS3yoqWFqxFyTCISwg= -github.com/aws/aws-sdk-go v1.44.157/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.158 h1:Q71ei9ijL3KuyQcLJA9TtuYy2gMLsLdVH5Q2ackBq3s= +github.com/aws/aws-sdk-go v1.44.158/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From 04c2a2833595e07a75e16f1b113ebd476591d25d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 22:08:37 +0000 Subject: [PATCH 0668/2268] chore(deps): Bump github.com/go-git/go-git/v5 from 5.5.0 to 5.5.1 Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.5.0 to 5.5.1. - [Release notes](https://github.com/go-git/go-git/releases) - [Commits](https://github.com/go-git/go-git/compare/v5.5.0...v5.5.1) --- updated-dependencies: - dependency-name: github.com/go-git/go-git/v5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 484ebe921..dae75ac18 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/fluxcd/pkg/kustomize v0.11.0 - github.com/go-git/go-git/v5 v5.5.0 + github.com/go-git/go-git/v5 v5.5.1 github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 @@ -37,7 +37,7 @@ require ( github.com/spf13/viper v1.14.0 github.com/stretchr/testify v1.8.1 github.com/whilp/git-urls v1.0.0 - github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 + github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.4.0 golang.org/x/net v0.4.0 golang.org/x/sync v0.1.0 diff --git a/go.sum b/go.sum index 5ce647007..838b0c7b2 100644 --- a/go.sum +++ b/go.sum @@ -257,8 +257,8 @@ github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Ai github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.5.0 h1:StO/ASRvk1Pp74tr7XQ0pQwKlCFignzzTF/NLKdQzUE= -github.com/go-git/go-git/v5 v5.5.0/go.mod h1:g456XI30HAdt7GQtIf8JR6GDAdULGaR4KtfFtQa0uTg= +github.com/go-git/go-git/v5 v5.5.1 h1:5vtv2TB5PM/gPM+EvsHJ16hJh4uAkdGcKilcwY7FYwo= +github.com/go-git/go-git/v5 v5.5.1/go.mod h1:uz5PQ3d0gz7mSgzZhSJToM6ALPaKCdSnl58/Xb5hzr8= 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= @@ -679,7 +679,6 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pjbgf/sha1cd v0.2.0/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -810,9 +809,8 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaU github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= -github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4 h1:3kHD8uZqiYes9JHdd3FzuyUbG10g9Bp9EOfqkSHIhg4= -github.com/xanzy/ssh-agent v0.3.3-0.20220920102508-0fa644ba07f4/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= From 690228f2b9eb9b07121c3ec37e59fb5140e71806 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 13 Dec 2022 12:13:53 +0100 Subject: [PATCH 0669/2268] fix: Use fluxcd fork of go-git See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility --- cmd/kluctl/commands/cmd_helm_update.go | 4 ++-- e2e/helm_test.go | 4 ++-- e2e/test-utils/project.go | 2 +- go.mod | 4 +++- go.sum | 31 +++++++++++++++++++++----- pkg/git/auth/auth_provider.go | 2 +- pkg/git/auth/git_credentials_file.go | 2 +- pkg/git/auth/list_auth_provider.go | 4 ++-- pkg/git/list_refs.go | 10 ++++----- pkg/git/mirrored_repo.go | 8 +++---- pkg/git/poor_mans_clone.go | 4 ++-- pkg/git/ssh-pool/hostport.go | 2 +- pkg/git/test_git_server.go | 2 +- pkg/git/utils.go | 2 +- pkg/vars/vars_loader_test.go | 4 ++-- 15 files changed, 53 insertions(+), 32 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 1cc484d4c..059c70233 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -3,8 +3,8 @@ package commands import ( "context" "fmt" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/format/index" + "github.com/fluxcd/go-git/v5" + "github.com/fluxcd/go-git/v5/plumbing/format/index" "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" git2 "github.com/kluctl/kluctl/v2/pkg/git" diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 5b9a772d5..cf33cf37c 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -2,8 +2,8 @@ package e2e import ( "fmt" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/object" + "github.com/fluxcd/go-git/v5" + "github.com/fluxcd/go-git/v5/plumbing/object" test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index b97497f98..3d0b891fb 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "fmt" - "github.com/go-git/go-git/v5" + "github.com/fluxcd/go-git/v5" "github.com/huandu/xstrings" "github.com/jinzhu/copier" "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" diff --git a/go.mod b/go.mod index d447c0d8a..b86ceba06 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,9 @@ require ( github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible + // See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility + github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df github.com/fluxcd/pkg/kustomize v0.11.0 - github.com/go-git/go-git/v5 v5.5.1 github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 @@ -120,6 +121,7 @@ require ( github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect + github.com/go-git/go-git/v5 v5.4.2 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect diff --git a/go.sum b/go.sum index bc25ba223..20b2f6dfc 100644 --- a/go.sum +++ b/go.sum @@ -96,12 +96,15 @@ github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmy github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= @@ -114,6 +117,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy 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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -214,6 +218,7 @@ github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9 github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.10.0 h1:X4gma4HM7hFm6WMeAsTfqA0GOfdNoCzBIkHGoRLGXuM= github.com/emicklei/go-restful/v3 v3.10.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -238,27 +243,33 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df h1:2BHXJp1PwX7D47Q2oaKDekn+BZVZCmxeCWNi+FyownE= +github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df/go.mod h1:raWgfUV7lDQVXp4QXUaeNNJkRVKz97UQuF+0kdY7Vmo= github.com/fluxcd/pkg/apis/kustomize v0.7.0 h1:X2htBmJ91nGYv4d93gin665MFWKNGiNwUiZ08/Zz0hY= github.com/fluxcd/pkg/apis/kustomize v0.7.0/go.mod h1:Mu+KdktsEKWA4l/33CZdY5lB4hz51mqfcLzBZSwAqVg= github.com/fluxcd/pkg/kustomize v0.11.0 h1:zseS9LRUuzhP/7KamccmsOgYpJAdhqtsf+2wN/CHF3I= github.com/fluxcd/pkg/kustomize v0.11.0/go.mod h1:awHID4OKe2/WAfTFg4u0fURXZPUkrIslSZNSPX9MEFQ= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.5.1 h1:5vtv2TB5PM/gPM+EvsHJ16hJh4uAkdGcKilcwY7FYwo= -github.com/go-git/go-git/v5 v5.5.1/go.mod h1:uz5PQ3d0gz7mSgzZhSJToM6ALPaKCdSnl58/Xb5hzr8= +github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= 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= @@ -489,6 +500,7 @@ github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq 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.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 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= @@ -524,6 +536,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -755,6 +768,7 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -809,6 +823,8 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaU github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -860,6 +876,7 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -869,6 +886,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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= @@ -878,7 +896,7 @@ golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -957,6 +975,7 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -966,7 +985,6 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1014,6 +1032,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1048,10 +1067,12 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/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-20210502180810-71e4cd670f79/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= @@ -1073,7 +1094,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1081,7 +1101,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 19b2d418e..002ea0d12 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -2,7 +2,7 @@ package auth import ( "context" - "github.com/go-git/go-git/v5/plumbing/transport" + "github.com/fluxcd/go-git/v5/plumbing/transport" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" "golang.org/x/crypto/ssh" diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index 68989ca85..eb1696075 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -3,7 +3,7 @@ package auth import ( "bufio" "context" - "github.com/go-git/go-git/v5/plumbing/transport/http" + "github.com/fluxcd/go-git/v5/plumbing/transport/http" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" giturls "github.com/whilp/git-urls" diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index 44e75c477..3d095e419 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -2,8 +2,8 @@ package auth import ( "context" - "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/fluxcd/go-git/v5/plumbing/transport/http" + "github.com/fluxcd/go-git/v5/plumbing/transport/ssh" "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" ssh2 "golang.org/x/crypto/ssh" diff --git a/pkg/git/list_refs.go b/pkg/git/list_refs.go index 735234d6b..a4a8bb7d5 100644 --- a/pkg/git/list_refs.go +++ b/pkg/git/list_refs.go @@ -4,11 +4,11 @@ import ( "bytes" "context" "fmt" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/plumbing/protocol/packp" - "github.com/go-git/go-git/v5/storage/memory" + "github.com/fluxcd/go-git/v5" + "github.com/fluxcd/go-git/v5/config" + "github.com/fluxcd/go-git/v5/plumbing" + "github.com/fluxcd/go-git/v5/plumbing/protocol/packp" + "github.com/fluxcd/go-git/v5/storage/memory" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 10e2e805c..e1642cdb9 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -5,10 +5,10 @@ import ( "crypto/sha256" "encoding/hex" "fmt" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/plumbing/object" + "github.com/fluxcd/go-git/v5" + "github.com/fluxcd/go-git/v5/config" + "github.com/fluxcd/go-git/v5/plumbing" + "github.com/fluxcd/go-git/v5/plumbing/object" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" _ "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" diff --git a/pkg/git/poor_mans_clone.go b/pkg/git/poor_mans_clone.go index 975112564..9804cab66 100644 --- a/pkg/git/poor_mans_clone.go +++ b/pkg/git/poor_mans_clone.go @@ -1,8 +1,8 @@ package git import ( - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/config" + "github.com/fluxcd/go-git/v5" + "github.com/fluxcd/go-git/v5/config" cp "github.com/otiai10/copy" "os" "path/filepath" diff --git a/pkg/git/ssh-pool/hostport.go b/pkg/git/ssh-pool/hostport.go index 4ca5c5cc7..d4e314ae0 100644 --- a/pkg/git/ssh-pool/hostport.go +++ b/pkg/git/ssh-pool/hostport.go @@ -2,7 +2,7 @@ package ssh_pool import ( "fmt" - "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/fluxcd/go-git/v5/plumbing/transport/ssh" "strconv" ) diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index e7661ab9e..a323901a5 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -3,7 +3,7 @@ package git import ( "context" "fmt" - "github.com/go-git/go-git/v5" + "github.com/fluxcd/go-git/v5" "github.com/jinzhu/copier" http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" "gopkg.in/yaml.v3" diff --git a/pkg/git/utils.go b/pkg/git/utils.go index f007fa3ee..121bca567 100644 --- a/pkg/git/utils.go +++ b/pkg/git/utils.go @@ -2,7 +2,7 @@ package git import ( "fmt" - "github.com/go-git/go-git/v5" + "github.com/fluxcd/go-git/v5" "os" "path/filepath" ) diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index b4d4a0f38..e7ecd8720 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -2,8 +2,8 @@ package vars import ( "context" - git2 "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing" + git2 "github.com/fluxcd/go-git/v5" + "github.com/fluxcd/go-git/v5/plumbing" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" From f51bf628ca4974f67d5428ea6ae1e0d4a6e5db30 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 13 Dec 2022 12:14:51 +0100 Subject: [PATCH 0670/2268] fix: Use wrapper around AuthMethod to override ClientConfig for HostKeyCallback --- pkg/git/auth/auth_provider.go | 13 +++++++++-- pkg/git/auth/list_auth_provider.go | 36 +++++++++++++++++++++--------- pkg/git/auth/ssh_auth_provider.go | 1 - pkg/git/ssh-pool/ssh_pool.go | 4 ++-- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 002ea0d12..18809899a 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -2,7 +2,9 @@ package auth import ( "context" + "fmt" "github.com/fluxcd/go-git/v5/plumbing/transport" + ssh2 "github.com/fluxcd/go-git/v5/plumbing/transport/ssh" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" "golang.org/x/crypto/ssh" @@ -12,8 +14,15 @@ type AuthMethodAndCA struct { AuthMethod transport.AuthMethod CABundle []byte - Hash func() ([]byte, error) - ClientConfig func() (*ssh.ClientConfig, error) + Hash func() ([]byte, error) +} + +func (a *AuthMethodAndCA) SshClientConfig() (*ssh.ClientConfig, error) { + gitSshAuth, ok := a.AuthMethod.(ssh2.AuthMethod) + if !ok { + return nil, fmt.Errorf("auth is not a git ssh.AuthMethod") + } + return gitSshAuth.ClientConfig() } type GitAuthProvider interface { diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index 3d095e419..c6876027e 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -28,6 +28,28 @@ type AuthEntry struct { CABundle []byte } +type KnownHostsWrapper struct { + authMethod ssh.AuthMethod + hostKeyCallback ssh2.HostKeyCallback +} + +func (w *KnownHostsWrapper) String() string { + return w.authMethod.String() +} + +func (w *KnownHostsWrapper) Name() string { + return w.authMethod.Name() +} + +func (w *KnownHostsWrapper) ClientConfig() (*ssh2.ClientConfig, error) { + ccfg, err := w.authMethod.ClientConfig() + if err != nil { + return nil, err + } + ccfg.HostKeyCallback = w.hostKeyCallback + return ccfg, nil +} + func (a *ListAuthProvider) AddEntry(e AuthEntry) { a.entries = append(a.entries, e) } @@ -80,20 +102,14 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) if err != nil { a.MessageCallbacks.Trace("ListAuthProvider: failed to parse private key: %v", err) } else { - hostKeyCallback := buildVerifyHostCallback(a.MessageCallbacks, e.KnownHosts) return AuthMethodAndCA{ - AuthMethod: pk, + AuthMethod: &KnownHostsWrapper{ + authMethod: pk, + hostKeyCallback: buildVerifyHostCallback(a.MessageCallbacks, e.KnownHosts), + }, Hash: func() ([]byte, error) { return buildHash(pk.Signer) }, - ClientConfig: func() (*ssh2.ClientConfig, error) { - ccfg, err := pk.ClientConfig() - if err != nil { - return nil, err - } - ccfg.HostKeyCallback = hostKeyCallback - return ccfg, nil - }, } } } else { diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index 2e95cb864..5af2d2c0d 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -130,7 +130,6 @@ func (a *GitSshAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr } return buildHashForList(signers) }, - ClientConfig: auth.ClientConfig, } } diff --git a/pkg/git/ssh-pool/ssh_pool.go b/pkg/git/ssh-pool/ssh_pool.go index 7b33a4f1c..010fec57a 100644 --- a/pkg/git/ssh-pool/ssh_pool.go +++ b/pkg/git/ssh-pool/ssh_pool.go @@ -139,7 +139,7 @@ func (p *SshPool) newClient(ctx context.Context, addr string, auth auth.AuthMeth return nil, err } - config, err := auth.ClientConfig() + config, err := auth.SshClientConfig() if err != nil { return nil, err } @@ -157,7 +157,7 @@ func (p *SshPool) buildHash(addr string, auth auth.AuthMethodAndCA) (string, err return "", fmt.Errorf("auth has no Hash") } - config, err := auth.ClientConfig() + config, err := auth.SshClientConfig() if err != nil { return "", err } From ba3608599b80a7ae039e54b8b7525382af2ee40e Mon Sep 17 00:00:00 2001 From: Eng Zer Jun Date: Wed, 14 Dec 2022 20:17:10 +0800 Subject: [PATCH 0671/2268] test: use `T.TempDir` to create temporary test directory This commit replaces `os.MkdirTemp` with `t.TempDir` in tests. The directory created by `t.TempDir` is automatically removed when the test and all its subtests complete. Prior to this commit, temporary directory created using `os.MkdirTemp` needs to be removed manually by calling `os.RemoveAll`, which is omitted in some tests. The error handling boilerplate e.g. defer func() { if err := os.RemoveAll(dir); err != nil { t.Fatal(err) } } is also tedious, but `t.TempDir` handles this for us nicely. Reference: https://pkg.go.dev/testing#T.TempDir Signed-off-by: Eng Zer Jun --- pkg/git/test_git_server.go | 22 +++++++-------------- pkg/vars/vars_loader_test.go | 38 +++++++++++++----------------------- 2 files changed, 21 insertions(+), 39 deletions(-) diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index a323901a5..7dd0a1614 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -3,10 +3,6 @@ package git import ( "context" "fmt" - "github.com/fluxcd/go-git/v5" - "github.com/jinzhu/copier" - http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" - "gopkg.in/yaml.v3" "log" "net" "net/http" @@ -14,6 +10,11 @@ import ( "path/filepath" "reflect" "testing" + + "github.com/fluxcd/go-git/v5" + "github.com/jinzhu/copier" + http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" + "gopkg.in/yaml.v3" ) type TestGitServer struct { @@ -28,15 +29,10 @@ type TestGitServer struct { func NewTestGitServer(t *testing.T) *TestGitServer { p := &TestGitServer{ - t: t, + t: t, + baseDir: t.TempDir(), } - baseDir, err := os.MkdirTemp(os.TempDir(), "kluctl-tests-") - if err != nil { - p.t.Fatal(err) - } - p.baseDir = baseDir - p.initGitServer() t.Cleanup(func() { @@ -73,10 +69,6 @@ func (p *TestGitServer) Cleanup() { p.gitServer = nil } - if p.baseDir == "" { - return - } - _ = os.RemoveAll(p.baseDir) p.baseDir = "" } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index e7ecd8720..a4a99b4e6 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -2,6 +2,15 @@ package vars import ( "context" + "io" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "strings" + "testing" + git2 "github.com/fluxcd/go-git/v5" "github.com/fluxcd/go-git/v5/plumbing" "github.com/kluctl/kluctl/v2/pkg/git" @@ -18,30 +27,11 @@ import ( "github.com/kluctl/kluctl/v2/pkg/vars/sops_test_resources" "github.com/stretchr/testify/assert" "go.mozilla.org/sops/v3/age" - "io" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "net/http" - "net/http/httptest" - "net/url" - "os" - "path/filepath" - "strings" - "testing" ) -func newTestDir(t *testing.T) string { - tmp, err := os.MkdirTemp("", "") - if err != nil { - t.Fatal(err) - } - t.Cleanup(func() { - _ = os.RemoveAll(tmp) - }) - return tmp -} - func newRP(t *testing.T) *repocache.GitRepoCache { grc := repocache.NewGitRepoCache(context.TODO(), &ssh_pool.SshPool{}, auth.NewDefaultAuthProviders("KLUCTL_GIT", nil), nil, 0) t.Cleanup(func() { @@ -77,7 +67,7 @@ func TestVarsLoader_Values(t *testing.T) { } func TestVarsLoader_File(t *testing.T) { - d := newTestDir(t) + d := t.TempDir() _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": 42}}`), 0o600) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { @@ -101,7 +91,7 @@ func TestVarsLoader_File(t *testing.T) { } func TestVarsLoader_SopsFile(t *testing.T) { - d := newTestDir(t) + d := t.TempDir() f, _ := sops_test_resources.TestResources.ReadFile("test.yaml") key, _ := sops_test_resources.TestResources.ReadFile("test-key.txt") _ = os.WriteFile(filepath.Join(d, "test.yaml"), f, 0o600) @@ -120,7 +110,7 @@ func TestVarsLoader_SopsFile(t *testing.T) { } func TestVarsLoader_FileWithLoad(t *testing.T) { - d := newTestDir(t) + d := t.TempDir() _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{ load_template("test2.txt") }}}}`), 0o600) _ = os.WriteFile(filepath.Join(d, "test2.txt"), []byte(`42`), 0o600) @@ -136,7 +126,7 @@ func TestVarsLoader_FileWithLoad(t *testing.T) { } func TestVarsLoader_FileWithLoadSubDir(t *testing.T) { - d := newTestDir(t) + d := t.TempDir() _ = os.Mkdir(filepath.Join(d, "subdir"), 0o700) _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{ load_template("test2.txt") }}}}`), 0o600) _ = os.WriteFile(filepath.Join(d, "subdir/test2.txt"), []byte(`42`), 0o600) @@ -153,7 +143,7 @@ func TestVarsLoader_FileWithLoadSubDir(t *testing.T) { } func TestVarsLoader_FileWithLoadNotExists(t *testing.T) { - d := newTestDir(t) + d := t.TempDir() _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": {{load_template("test3.txt")}}}}`), 0o600) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { From 9ee0b28807f87b2a9c953cdd1382648daeaadd47 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Dec 2022 17:47:18 +0100 Subject: [PATCH 0672/2268] docs: Set linkTitle to Kluctl --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 1ccac0e55..b1dab7f60 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,7 +2,7 @@ --- title: "Kluctl Documentation" -linkTitle: "Docs" +linkTitle: "Kluctl" description: "The missing glue to put together large Kubernetes deployments." taxonomyCloud: [] weight: 20 From 0fbb20d6d8ebe15efe7f8228e742061fba808490 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Dec 2022 17:51:31 +0100 Subject: [PATCH 0673/2268] docs: Remove Kluctl menu entry from hugo page --- docs/README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index b1dab7f60..9e921ca6b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,9 +6,6 @@ linkTitle: "Kluctl" description: "The missing glue to put together large Kubernetes deployments." taxonomyCloud: [] weight: 20 -menu: - main: - weight: 20 --- --> From dc3ebf970f517ad8342eaf11b067c0e292a782ff Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Dec 2022 22:59:09 +0100 Subject: [PATCH 0674/2268] feat: Obfuscate diffs of Secrets --- cmd/kluctl/args/misc.go | 1 + cmd/kluctl/commands/cmd_delete.go | 2 +- cmd/kluctl/commands/cmd_deploy.go | 8 +-- cmd/kluctl/commands/cmd_diff.go | 2 +- cmd/kluctl/commands/cmd_poke_images.go | 2 +- cmd/kluctl/commands/cmd_prune.go | 2 +- cmd/kluctl/commands/command_result.go | 10 +++- docs/reference/commands/delete.md | 1 + docs/reference/commands/deploy.md | 1 + docs/reference/commands/diff.md | 1 + docs/reference/commands/poke-images.md | 1 + docs/reference/commands/prune.md | 1 + e2e/obfuscate_test.go | 48 +++++++++++++++++ e2e/seal_test.go | 4 +- e2e/utils.go | 8 +++ e2e/utils_resources.go | 10 ++-- pkg/diff/diff.go | 73 ++++++++++++++++---------- pkg/diff/obfuscate.go | 42 +++++++++++++++ 18 files changed, 174 insertions(+), 43 deletions(-) create mode 100644 e2e/obfuscate_test.go create mode 100644 pkg/diff/obfuscate.go diff --git a/cmd/kluctl/args/misc.go b/cmd/kluctl/args/misc.go index 52b7b6f4c..513e71165 100644 --- a/cmd/kluctl/args/misc.go +++ b/cmd/kluctl/args/misc.go @@ -42,6 +42,7 @@ type AbortOnErrorFlags struct { type OutputFormatFlags struct { OutputFormat []string `group:"misc" short:"o" help:"Specify output format and target file, in the format 'format=path'. Format can either be 'text' or 'yaml'. Can be specified multiple times. The actual format for yaml is currently not documented and subject to change."` + NoObfuscate bool `group:"misc" help:"Disable obfuscation of sensitive/secret data"` } type OutputFlags struct { diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index b034c6c4e..ed20376b7 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -65,7 +65,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(ctx, cmd.OutputFormat, result) + err = outputCommandResult(ctx, cmd.OutputFormat, cmd.NoObfuscate, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index f819935db..624d6b6e3 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -64,7 +64,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { cmd2.NoWait = cmd.NoWait cb := func(diffResult *types.CommandResult) error { - return cmd.diffResultCb(cmdCtx.ctx, diffResult) + return cmd.diffResultCb(cmdCtx.ctx, cmd.NoObfuscate, diffResult) } if cmd.Yes || cmd.DryRun { cb = nil @@ -74,7 +74,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { if err != nil { return err } - err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormat, result) + err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormat, cmd.NoObfuscate, result) if err != nil { return err } @@ -84,8 +84,8 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { return nil } -func (cmd *deployCmd) diffResultCb(ctx context.Context, diffResult *types.CommandResult) error { - err := outputCommandResult(ctx, nil, diffResult) +func (cmd *deployCmd) diffResultCb(ctx context.Context, noObfuscate bool, diffResult *types.CommandResult) error { + err := outputCommandResult(ctx, nil, noObfuscate, diffResult) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index bb8fcce19..a32991d41 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -50,7 +50,7 @@ func (cmd *diffCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(ctx, cmd.OutputFormat, result) + err = outputCommandResult(ctx, cmd.OutputFormat, cmd.NoObfuscate, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 7492acc74..c1593e58e 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -51,7 +51,7 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(ctx, cmd.OutputFormat, result) + err = outputCommandResult(ctx, cmd.OutputFormat, cmd.NoObfuscate, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 13516a1b4..9c887eb1c 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -54,7 +54,7 @@ func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { if err != nil { return err } - err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormat, result) + err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormat, cmd.NoObfuscate, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index f9dd12408..dcf709fa1 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/diff" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" @@ -197,9 +198,16 @@ func outputHelper(ctx context.Context, output []string, cb func(format string) ( return nil } -func outputCommandResult(ctx context.Context, output []string, cr *types.CommandResult) error { +func outputCommandResult(ctx context.Context, output []string, noObfuscate bool, cr *types.CommandResult) error { status.Flush(ctx) + if !noObfuscate { + var obfuscator diff.Obfuscator + for _, c := range cr.ChangedObjects { + obfuscator.Obfuscate(c.Ref, c.Changes) + } + } + return outputHelper(ctx, output, func(format string) (string, error) { return formatCommandResult(cr, format) }) diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index 264e4c553..f75f0f77c 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -51,6 +51,7 @@ Misc arguments: Must be in the form --helm-username=:, where must match the id specified in the helm-chart.yaml. + --no-obfuscate Disable obfuscation of sensitive/secret data -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can either be 'text' or 'yaml'. Can be specified multiple times. The actual format for yaml is diff --git a/docs/reference/commands/deploy.md b/docs/reference/commands/deploy.md index 044715d7b..edc7d6544 100644 --- a/docs/reference/commands/deploy.md +++ b/docs/reference/commands/deploy.md @@ -54,6 +54,7 @@ Misc arguments: Must be in the form --helm-username=:, where must match the id specified in the helm-chart.yaml. + --no-obfuscate Disable obfuscation of sensitive/secret data --no-wait Don't wait for objects readiness' -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can either be 'text' or 'yaml'. Can be diff --git a/docs/reference/commands/diff.md b/docs/reference/commands/diff.md index bb047a7c4..4441498af 100644 --- a/docs/reference/commands/diff.md +++ b/docs/reference/commands/diff.md @@ -55,6 +55,7 @@ Misc arguments: --ignore-annotations Ignores changes in annotations when diffing --ignore-labels Ignores changes in labels when diffing --ignore-tags Ignores changes in tags when diffing + --no-obfuscate Disable obfuscation of sensitive/secret data -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can either be 'text' or 'yaml'. Can be specified multiple times. The actual format for yaml is diff --git a/docs/reference/commands/poke-images.md b/docs/reference/commands/poke-images.md index 4d16e9589..77a33c404 100644 --- a/docs/reference/commands/poke-images.md +++ b/docs/reference/commands/poke-images.md @@ -48,6 +48,7 @@ Misc arguments: Must be in the form --helm-username=:, where must match the id specified in the helm-chart.yaml. + --no-obfuscate Disable obfuscation of sensitive/secret data -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can either be 'text' or 'yaml'. Can be specified multiple times. The actual format for yaml is diff --git a/docs/reference/commands/prune.md b/docs/reference/commands/prune.md index bef1d7c4b..5bb327f3c 100644 --- a/docs/reference/commands/prune.md +++ b/docs/reference/commands/prune.md @@ -44,6 +44,7 @@ Misc arguments: Must be in the form --helm-username=:, where must match the id specified in the helm-chart.yaml. + --no-obfuscate Disable obfuscation of sensitive/secret data -o, --output-format stringArray Specify output format and target file, in the format 'format=path'. Format can either be 'text' or 'yaml'. Can be specified multiple times. The actual format for yaml is diff --git a/e2e/obfuscate_test.go b/e2e/obfuscate_test.go new file mode 100644 index 000000000..8f0d018e5 --- /dev/null +++ b/e2e/obfuscate_test.go @@ -0,0 +1,48 @@ +package e2e + +import ( + "encoding/base64" + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestObfuscateSecrets(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + addSecretDeployment(p, "secret", map[string]string{ + "secret": "secret_value_1", + }, resourceOpts{ + name: "secret", + namespace: p.TestSlug(), + }, false) + stdout, _ := p.KluctlMust("deploy", "--yes", "-t", "test") + assertSecretExists(t, k, p.TestSlug(), "secret") + assert.NotContains(t, stdout, "secret_value") + + p.UpdateYaml("secret/secret-secret.yml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField("secret_value_2", "stringData", "secret") + return nil + }, "") + stdout, _ = p.KluctlMust("deploy", "--yes", "-t", "test") + assert.NotContains(t, stdout, "secret_value") + assert.Contains(t, stdout, "***** (obfuscated)") + + p.UpdateYaml("secret/secret-secret.yml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField("secret_value_3", "stringData", "secret") + return nil + }, "") + stdout, _ = p.KluctlMust("deploy", "--yes", "-t", "test", "--no-obfuscate") + assert.Contains(t, stdout, "-"+base64.StdEncoding.EncodeToString([]byte("secret_value_2"))) + assert.Contains(t, stdout, "+"+base64.StdEncoding.EncodeToString([]byte("secret_value_3"))) + assert.NotContains(t, stdout, "***** (obfuscated)") +} diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 2308ec103..532088948 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -109,7 +109,7 @@ func prepareSealTest(t *testing.T, k *test_utils.EnvTestCluster, secrets map[str addSecretsSet(p, "test", varsSources) addSecretsSetToTarget(p, "test-target", "test") - addSecretDeployment(p, "secret-deployment", secrets, resourceOpts{name: "secret", namespace: p.TestSlug()}) + addSecretDeployment(p, "secret-deployment", secrets, resourceOpts{name: "secret", namespace: p.TestSlug()}, true) return p } @@ -391,7 +391,7 @@ func TestSeal_MultipleSecrets(t *testing.T) { }, }), }, true) - addSecretDeployment(p, "secret-deployment2", secret2, resourceOpts{name: "secret2", namespace: p.TestSlug()}) + addSecretDeployment(p, "secret-deployment2", secret2, resourceOpts{name: "secret2", namespace: p.TestSlug()}, true) p.KluctlMust("seal", "-t", "test-target") diff --git a/e2e/utils.go b/e2e/utils.go index 1e3b6c61b..270c273fa 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -43,6 +43,14 @@ func assertConfigMapNotExists(t *testing.T, k *test_utils.EnvTestCluster, namesp } } +func assertSecretExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, name string) *uo.UnstructuredObject { + x, err := k.Get(v1.SchemeGroupVersion.WithResource("secrets"), namespace, name) + if err != nil { + t.Fatalf("unexpected error '%v' while getting Secret %s/%s", err, namespace, name) + } + return x +} + func assertNestedFieldEquals(t *testing.T, o *uo.UnstructuredObject, expected interface{}, keys ...interface{}) { v, ok, err := o.GetNestedField(keys...) if err != nil { diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 6d645d980..4131374b7 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -45,7 +45,7 @@ func createConfigMapObject(data map[string]string, opts resourceOpts) *uo.Unstru } func createSecretObject(data map[string]string, opts resourceOpts) *uo.UnstructuredObject { - o := createCoreV1Object("ConfigMap", opts) + o := createCoreV1Object("Secret", opts) if data != nil { o.SetNestedField(data, "stringData") } @@ -59,10 +59,14 @@ func addConfigMapDeployment(p *test_utils.TestProject, dir string, data map[stri }, opts.tags) } -func addSecretDeployment(p *test_utils.TestProject, dir string, data map[string]string, opts resourceOpts) { +func addSecretDeployment(p *test_utils.TestProject, dir string, data map[string]string, opts resourceOpts, sealme bool) { + sealmeExt := "" + if sealme { + sealmeExt = ".sealme" + } o := createSecretObject(data, opts) fname := fmt.Sprintf("secret-%s.yml", opts.name) p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ - {fname, fname + ".sealme", o}, + {fname, fname + sealmeExt, o}, }, opts.tags) } diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index 5781b9312..f9f1a1c55 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -54,6 +54,10 @@ func Diff(oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) ([ if err != nil { return nil, err } + err = updateUnifiedDiff(c2) + if err != nil { + return nil, err + } changes = append(changes, *c2) } @@ -65,59 +69,70 @@ func Diff(oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) ([ func convertChange(c diff2.Change, oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) (*types.Change, error) { switch c.Type { case "create": - ud, err := buildUnifiedDiff(notPresent, c.To, false) - if err != nil { - return nil, err - } p, err := convertPath(c.Path, newObject.Object) if err != nil { return nil, err } return &types.Change{ - Type: "insert", - JsonPath: p, - NewValue: c.To, - UnifiedDiff: ud, + Type: "insert", + JsonPath: p, + NewValue: c.To, }, nil case "delete": - ud, err := buildUnifiedDiff(c.From, notPresent, false) - if err != nil { - return nil, err - } p, err := convertPath(c.Path, oldObject.Object) if err != nil { return nil, err } return &types.Change{ - Type: "delete", - JsonPath: p, - OldValue: c.From, - UnifiedDiff: ud, + Type: "delete", + JsonPath: p, + OldValue: c.From, }, nil case "update": - showType := false - if reflect.TypeOf(c.From) != reflect.TypeOf(c.To) { - showType = true - } - ud, err := buildUnifiedDiff(c.From, c.To, showType) - if err != nil { - return nil, err - } p, err := convertPath(c.Path, newObject.Object) if err != nil { return nil, err } return &types.Change{ - Type: "update", - JsonPath: p, - NewValue: c.To, - OldValue: c.From, - UnifiedDiff: ud, + Type: "update", + JsonPath: p, + NewValue: c.To, + OldValue: c.From, }, nil } return nil, fmt.Errorf("unknown change type %s", c.Type) } +func updateUnifiedDiff(change *types.Change) error { + switch change.Type { + case "insert": + ud, err := buildUnifiedDiff(notPresent, change.NewValue, false) + if err != nil { + return err + } + change.UnifiedDiff = ud + case "delete": + ud, err := buildUnifiedDiff(change.OldValue, notPresent, false) + if err != nil { + return err + } + change.UnifiedDiff = ud + case "update": + showType := false + if reflect.TypeOf(change.OldValue) != reflect.TypeOf(change.NewValue) { + showType = true + } + ud, err := buildUnifiedDiff(change.OldValue, change.NewValue, showType) + if err != nil { + return err + } + change.UnifiedDiff = ud + default: + return fmt.Errorf("unknown change type %s", change.Type) + } + return nil +} + func stableSortChanges(changes []types.Change) { changesStrs := make([]string, len(changes)) changesIndexes := make([]int, len(changes)) diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go new file mode 100644 index 000000000..710ca60c0 --- /dev/null +++ b/pkg/diff/obfuscate.go @@ -0,0 +1,42 @@ +package diff + +import ( + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "k8s.io/apimachinery/pkg/runtime/schema" + "strings" +) + +var secretGvk = schema.GroupKind{Group: "", Kind: "Secret"} + +type Obfuscator struct { +} + +func (o *Obfuscator) Obfuscate(ref k8s.ObjectRef, changes []types.Change) { + if ref.GVK.GroupKind() == secretGvk { + o.obfuscateSecret(ref, changes) + } +} + +func (o *Obfuscator) obfuscateSecret(ref k8s.ObjectRef, changes []types.Change) { + for i, _ := range changes { + c := &changes[i] + if strings.HasPrefix(c.JsonPath, "data.") || strings.HasPrefix(c.JsonPath, "stringData.") { + if c.NewValue != nil { + c.NewValue = "*****a" + } + if c.OldValue != nil { + c.OldValue = "*****b" + } + _ = updateUnifiedDiff(c) + if c.NewValue != nil { + c.NewValue = "*****" + c.UnifiedDiff = strings.ReplaceAll(c.UnifiedDiff, "*****a", "***** (obfuscated)") + } + if c.OldValue != nil { + c.OldValue = "*****" + c.UnifiedDiff = strings.ReplaceAll(c.UnifiedDiff, "*****b", "***** (obfuscated)") + } + } + } +} From 7554f43670154b128c25183c1fb6f23143f1204d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Dec 2022 21:12:34 +0100 Subject: [PATCH 0675/2268] feat: Remove templateExcludes from deployment projects --- pkg/deployment/deployment_item.go | 5 ----- pkg/types/deployment.go | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 05df656bd..49d2e650f 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -6,7 +6,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/sops" - "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -125,10 +124,6 @@ func (di *DeploymentItem) render(forSeal bool) error { var excludePatterns []string excludePatterns = append(excludePatterns, "**/.git") - if len(di.Project.Config.TemplateExcludes) != 0 { - status.Deprecation(di.ctx.Ctx, "template-excludes", "'templateExcludes' are deprecated, use .templateignore files instead.") - } - excludePatterns = append(excludePatterns, di.Project.Config.TemplateExcludes...) err = filepath.WalkDir(*di.dir, func(p string, d fs.DirEntry, err error) error { if d.IsDir() { relDir, err := filepath.Rel(*di.dir, p) diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index eee074fda..cefcf4b59 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -94,8 +94,7 @@ type DeploymentProjectConfig struct { OverrideNamespace *string `yaml:"overrideNamespace,omitempty"` Tags []string `yaml:"tags,omitempty"` - IgnoreForDiff []*IgnoreForDiffItemConfig `yaml:"ignoreForDiff,omitempty"` - TemplateExcludes []string `yaml:"templateExcludes,omitempty"` + IgnoreForDiff []*IgnoreForDiffItemConfig `yaml:"ignoreForDiff,omitempty"` } func init() { From 3794ed68bcc9e90510004dacc3ee4d27fc901c6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 22:08:10 +0000 Subject: [PATCH 0676/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.158 to 1.44.160 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.158 to 1.44.160. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.158...v1.44.160) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b86ceba06..5f69d1c24 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.158 + github.com/aws/aws-sdk-go v1.44.160 github.com/bitnami-labs/sealed-secrets v0.19.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index 20b2f6dfc..434f6f811 100644 --- a/go.sum +++ b/go.sum @@ -132,8 +132,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.158 h1:Q71ei9ijL3KuyQcLJA9TtuYy2gMLsLdVH5Q2ackBq3s= -github.com/aws/aws-sdk-go v1.44.158/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.160 h1:F41sWUel1CJ69ezoBGCg8sDyu9kyeKEpwmDrLXbCuyA= +github.com/aws/aws-sdk-go v1.44.160/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From e779ecf2a3a3b21ba43d14daf1330834c3c21202 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 22:08:40 +0000 Subject: [PATCH 0677/2268] chore(deps): Bump helm.sh/helm/v3 from 3.10.2 to 3.10.3 Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.10.2 to 3.10.3. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.10.2...v3.10.3) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b86ceba06..a5bddba73 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( golang.org/x/text v0.5.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.10.2 + helm.sh/helm/v3 v3.10.3 k8s.io/api v0.26.0 k8s.io/apiextensions-apiserver v0.25.4 k8s.io/apimachinery v0.26.0 diff --git a/go.sum b/go.sum index 20b2f6dfc..2c8e17b78 100644 --- a/go.sum +++ b/go.sum @@ -1339,8 +1339,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -helm.sh/helm/v3 v3.10.2 h1:2PmN9NgmqTn5pswfL5Kh2LxOKjkmh0hxKLe6/J0yUY4= -helm.sh/helm/v3 v3.10.2/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= +helm.sh/helm/v3 v3.10.3 h1:wL7IUZ7Zyukm5Kz0OUmIFZgKHuAgByCrUcJBtY0kDyw= +helm.sh/helm/v3 v3.10.3/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= 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= From e95e3e5908a967acdfb31cb16bbeccdc0ceafd35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 08:14:22 +0000 Subject: [PATCH 0678/2268] chore(deps): Bump sigs.k8s.io/controller-runtime from 0.13.1 to 0.14.0 Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.13.1 to 0.14.0. - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/master/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.13.1...v0.14.0) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 14 +++++++------- go.sum | 37 +++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 1f310f9c2..673227fa3 100644 --- a/go.mod +++ b/go.mod @@ -49,9 +49,9 @@ require ( gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.3 k8s.io/api v0.26.0 - k8s.io/apiextensions-apiserver v0.25.4 + k8s.io/apiextensions-apiserver v0.26.0 k8s.io/apimachinery v0.26.0 - k8s.io/client-go v0.25.4 + k8s.io/client-go v0.26.0 k8s.io/klog/v2 v2.80.1 sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/kustomize/kyaml v0.13.9 @@ -64,7 +64,7 @@ require ( github.com/huandu/xstrings v1.4.0 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.3 - sigs.k8s.io/controller-runtime v0.13.1 + sigs.k8s.io/controller-runtime v0.14.0 ) require ( @@ -226,7 +226,7 @@ require ( go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.6.0 // indirect golang.org/x/oauth2 v0.2.0 // indirect - golang.org/x/time v0.2.0 // indirect + golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.2.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/api v0.102.0 // indirect @@ -239,12 +239,12 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiserver v0.25.4 // indirect + k8s.io/apiserver v0.26.0 // indirect k8s.io/cli-runtime v0.25.3 // indirect - k8s.io/component-base v0.25.4 // indirect + k8s.io/component-base v0.26.0 // indirect k8s.io/kube-openapi v0.0.0-20221110221610-a28e98eb7c70 // indirect k8s.io/kubectl v0.25.3 // indirect - k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect + k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect oras.land/oras-go v1.2.1 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index b087c36fd..c865d24b6 100644 --- a/go.sum +++ b/go.sum @@ -241,7 +241,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df h1:2BHXJp1PwX7D47Q2oaKDekn+BZVZCmxeCWNi+FyownE= github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df/go.mod h1:raWgfUV7lDQVXp4QXUaeNNJkRVKz97UQuF+0kdY7Vmo= @@ -657,14 +657,12 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m 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/ohler55/ojg v1.14.5 h1:xCX2oyh/ZaoesbLH6fwVHStSJpk4o4eJs8ttXutzdg0= github.com/ohler55/ojg v1.14.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls= +github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -873,7 +871,7 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 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/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1118,8 +1116,8 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= -golang.org/x/time v0.2.0/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= @@ -1316,7 +1314,6 @@ 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/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -1350,33 +1347,33 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= -k8s.io/apiextensions-apiserver v0.25.4 h1:7hu9pF+xikxQuQZ7/30z/qxIPZc2J1lFElPtr7f+B6U= -k8s.io/apiextensions-apiserver v0.25.4/go.mod h1:bkSGki5YBoZWdn5pWtNIdGvDrrsRWlmnvl9a+tAw5vQ= +k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo= +k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ= k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= -k8s.io/apiserver v0.25.4 h1:/3TwZcgLqX7wUxq7TtXOUqXeBTwXIblVMQdhR5XZ7yo= -k8s.io/apiserver v0.25.4/go.mod h1:rPcm567XxjOnnd7jedDUnGJGmDGAo+cT6H7QHAN+xV0= +k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o= +k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84= k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= k8s.io/cli-runtime v0.25.3/go.mod h1:InHHsjkyW5hQsILJGpGjeruiDZT/R0OkROQgD6GzxO4= -k8s.io/client-go v0.25.4 h1:3RNRDffAkNU56M/a7gUfXaEzdhZlYhoW8dgViGy5fn8= -k8s.io/client-go v0.25.4/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw= -k8s.io/component-base v0.25.4 h1:n1bjg9Yt+G1C0WnIDJmg2fo6wbEU1UGMRiQSjmj7hNQ= -k8s.io/component-base v0.25.4/go.mod h1:nnZJU8OP13PJEm6/p5V2ztgX2oyteIaAGKGMYb2L2cY= +k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= +k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= +k8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs= +k8s.io/component-base v0.26.0/go.mod h1:lqHwlfV1/haa14F/Z5Zizk5QmzaVf23nQzCwVOQpfC8= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221110221610-a28e98eb7c70 h1:zfqQc1V6/ZgGpvrOVvr62OjiqQX4lZjfznK34NQwkqw= k8s.io/kube-openapi v0.0.0-20221110221610-a28e98eb7c70/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= -k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 h1:GfD9OzL11kvZN5iArC6oTS7RTj7oJOIfnislxYlqTj8= -k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.1 h1:/VcGS8FUy3eEXLl/1vC4QypLHwrfSmgW7ygsoklqKK8= oras.land/oras-go v1.2.1/go.mod h1:3N11Z5E3c4ZzOjroCl1RtAdB4yNAYl7A27j2SVf913A= 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/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= -sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= +sigs.k8s.io/controller-runtime v0.14.0 h1:ju2xsov5Ara6FoQuddg+az+rAxsUsTYn2IYyEKCTyDc= +sigs.k8s.io/controller-runtime v0.14.0/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= From 6da364cbf6cc86daabe844a4560d2e02fe9a62d6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Dec 2022 14:35:32 +0100 Subject: [PATCH 0679/2268] feat: Add support for local Helm Charts --- cmd/kluctl/commands/cmd_helm_pull.go | 11 ++++- docs/reference/deployments/helm.md | 6 +++ e2e/helm_test.go | 25 ++++++++++ e2e/test-utils/helm_repo_utils.go | 18 ++++--- e2e/test-utils/project.go | 8 +++- pkg/deployment/deployment_item.go | 8 ++-- pkg/helm/chart.go | 72 ++++++++++++++++++++++++++-- pkg/helm/helm_release.go | 36 ++++++++++++-- pkg/types/helm_chart.go | 47 +++++++++++++++++- 9 files changed, 207 insertions(+), 24 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index ee8fc429d..0931ea0b8 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -133,11 +133,20 @@ func loadHelmReleases(projectDir string, baseChartsDir string, credentialsProvid return nil } - hr, err := helm.NewRelease(p, baseChartsDir, credentialsProvider) + relDir, err := filepath.Rel(projectDir, filepath.Dir(p)) if err != nil { return err } + hr, err := helm.NewRelease(projectDir, relDir, p, baseChartsDir, credentialsProvider) + if err != nil { + return err + } + + if hr.Chart.IsLocalChart() { + return nil + } + releases = append(releases, hr) chart := hr.Chart key := fmt.Sprintf("%s / %s", chart.GetRepo(), chart.GetChartName()) diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index f42fe69c3..bc31615f6 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -99,6 +99,12 @@ helmChart: namespace: pepper ``` +### path +As alternative to `repo`, you can also specify `path`. The path must point to a local Helm Chart that is relative to the +`helm-chart.yaml`. The local Chart must reside in your Kluctl project. + +When `path` is specified, `repo`, `chartName`, `chartVersion` and `updateContrainsts` are not allowed. + ### chartName The name of the chart that can be found in the repository. diff --git a/e2e/helm_test.go b/e2e/helm_test.go index cf33cf37c..9d8f519cb 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -473,6 +473,31 @@ func TestHelmRenderOfflineKubernetes(t *testing.T) { }, cm1.Object["data"]) } +func TestHelmLocalChart(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + p.AddHelmDeployment("helm1", "../test-chart1", "", "", "test-helm-1", p.TestSlug(), nil) + p.AddHelmDeployment("helm2", "test-chart2", "", "", "test-helm-2", p.TestSlug(), nil) + + test_utils.CreateHelmDir(t, "test-chart1", "0.1.0", filepath.Join(p.LocalRepoDir(), "test-chart1")) + test_utils.CreateHelmDir(t, "test-chart2", "0.1.0", filepath.Join(p.LocalRepoDir(), "helm2/test-chart2")) + + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.TestSlug(), "test-helm-1-test-chart1") + assertConfigMapExists(t, k, p.TestSlug(), "test-helm-2-test-chart2") + + _, stderr := p.KluctlMust("helm-pull") + assert.NotContains(t, stderr, "test-chart1") + assert.NotContains(t, stderr, "test-chart2") +} + func getChartDir(t *testing.T, p *test_utils.TestProject, url2 string, chartName string, chartVersion string) string { u, err := url.Parse(url2) if err != nil { diff --git a/e2e/test-utils/helm_repo_utils.go b/e2e/test-utils/helm_repo_utils.go index aabe7835b..6bf45c0f5 100644 --- a/e2e/test-utils/helm_repo_utils.go +++ b/e2e/test-utils/helm_repo_utils.go @@ -24,25 +24,23 @@ import ( "testing" ) -func createHelmPackage(t *testing.T, name string, version string) string { - tmpDir := t.TempDir() - +func CreateHelmDir(t *testing.T, name string, version string, dest string) { err := fs.WalkDir(test_resources.HelmChartFS, ".", func(path string, d fs.DirEntry, err error) error { if d.IsDir() { - return os.MkdirAll(filepath.Join(tmpDir, path), 0o700) + return os.MkdirAll(filepath.Join(dest, path), 0o700) } else { b, err := test_resources.HelmChartFS.ReadFile(path) if err != nil { return err } - return os.WriteFile(filepath.Join(tmpDir, path), b, 0o600) + return os.WriteFile(filepath.Join(dest, path), b, 0o600) } }) if err != nil { t.Fatal(err) } - c, err := uo.FromFile(filepath.Join(tmpDir, "Chart.yaml")) + c, err := uo.FromFile(filepath.Join(dest, "Chart.yaml")) if err != nil { t.Fatal(err) } @@ -50,10 +48,16 @@ func createHelmPackage(t *testing.T, name string, version string) string { _ = c.SetNestedField(name, "name") _ = c.SetNestedField(version, "version") - err = yaml.WriteYamlFile(filepath.Join(tmpDir, "Chart.yaml"), c) + err = yaml.WriteYamlFile(filepath.Join(dest, "Chart.yaml"), c) if err != nil { t.Fatal(err) } +} + +func createHelmPackage(t *testing.T, name string, version string) string { + tmpDir := t.TempDir() + + CreateHelmDir(t, name, version, tmpDir) settings := cli.New() client := action.NewPackage() diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index 3d0b891fb..b824a5223 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -14,6 +14,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" registry2 "helm.sh/helm/v3/pkg/registry" + "net/url" "os" "os/exec" "path/filepath" @@ -259,7 +260,11 @@ func (p *TestProject) AddKustomizeDeployment(dir string, resources []KustomizeRe } func (p *TestProject) AddHelmDeployment(dir string, repoUrl string, chartName, version string, releaseName string, namespace string, values map[string]any) { - if registry2.IsOCI(repoUrl) { + localPath := "" + if u, err := url.Parse(repoUrl); err != nil || u.Host == "" { + localPath = repoUrl + repoUrl = "" + } else if registry2.IsOCI(repoUrl) { repoUrl += "/" + chartName chartName = "" } @@ -272,6 +277,7 @@ func (p *TestProject) AddHelmDeployment(dir string, repoUrl string, chartName, v *o = *uo.FromMap(map[string]interface{}{ "helmChart": map[string]any{ "repo": repoUrl, + "path": localPath, "chartVersion": version, "releaseName": releaseName, "namespace": namespace, diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 49d2e650f..bf5469beb 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -130,9 +130,9 @@ func (di *DeploymentItem) render(forSeal bool) error { if err != nil { return err } - if yaml.Exists(filepath.Join(p, "helm-chart.yml")) { + if yaml.Exists(filepath.Join(p, "Chart.yaml")) { // never try to render helm charts - ep := filepath.Join(relDir, "charts/**") + ep := fmt.Sprintf("%s/**", relDir) ep = filepath.ToSlash(ep) excludePatterns = append(excludePatterns, ep) return filepath.SkipDir @@ -188,7 +188,7 @@ func (di *DeploymentItem) renderHelmCharts() error { return err } - hr, err := helm.NewRelease(p, di.ctx.HelmChartsDir, di.ctx.HelmCredentials) + hr, err := helm.NewRelease(di.Project.source.dir, filepath.Join(di.RelToSourceItemDir, subDir), p, di.ctx.HelmChartsDir, di.ctx.HelmCredentials) if err != nil { return err } @@ -411,7 +411,7 @@ func (di *DeploymentItem) generateKustomizationYaml(subDir string) (*uo.Unstruct if di.isHelmValuesYaml(de.Name()) { continue } else if di.isHelmChartYaml(de.Name()) { - c, err := helm.NewRelease(filepath.Join(di.RenderedDir, subDir, de.Name()), di.ctx.HelmChartsDir, nil) + c, err := helm.NewRelease(di.Project.source.dir, filepath.Join(di.RelToSourceItemDir, subDir), filepath.Join(di.RenderedDir, subDir, de.Name()), di.ctx.HelmChartsDir, nil) if err != nil { return nil, err } diff --git a/pkg/helm/chart.go b/pkg/helm/chart.go index c5c29e54b..6ef7a4a64 100644 --- a/pkg/helm/chart.go +++ b/pkg/helm/chart.go @@ -7,6 +7,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/registries" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" cp "github.com/otiai10/copy" "github.com/rogpeppe/go-internal/lockedfile" "helm.sh/helm/v3/pkg/action" @@ -28,6 +30,7 @@ type HelmCredentialsProvider interface { type Chart struct { repo string + localPath string chartName string credentials HelmCredentialsProvider @@ -36,18 +39,32 @@ type Chart struct { versions []string } -func NewChart(repo string, chartName string, credentialsProvider HelmCredentialsProvider, credentialsId string) (*Chart, error) { +func NewChart(repo string, localPath string, chartName string, credentialsProvider HelmCredentialsProvider, credentialsId string) (*Chart, error) { hc := &Chart{ repo: repo, + localPath: localPath, credentials: credentialsProvider, credentialsId: credentialsId, } - if repo == "" { - return nil, fmt.Errorf("repo is missing") + if localPath == "" && repo == "" { + return nil, fmt.Errorf("repo and localPath are missing") } - if registry.IsOCI(repo) { + if hc.IsLocalChart() { + chartYaml, err := uo.FromFile(yaml.FixPathExt(filepath.Join(hc.localPath, "Chart.yaml"))) + if err != nil { + return nil, err + } + x, _, err := chartYaml.GetNestedString("name") + if err != nil { + return nil, err + } + if x == "" { + return nil, fmt.Errorf("invalid/empty chart name") + } + hc.chartName = x + } else if registry.IsOCI(repo) { if chartName != "" { return nil, fmt.Errorf("chartName can't be specified when using OCI repos") } @@ -70,6 +87,29 @@ func (c *Chart) GetRepo() string { return c.repo } +func (c *Chart) GetLocalPath() string { + return c.localPath +} + +func (c *Chart) IsLocalChart() bool { + return c.localPath != "" +} + +func (c *Chart) GetLocalChartVersion() (string, error) { + chartYaml, err := uo.FromFile(yaml.FixPathExt(filepath.Join(c.localPath, "Chart.yaml"))) + if err != nil { + return "", err + } + x, _, err := chartYaml.GetNestedString("version") + if err != nil { + return "", err + } + if x == "" { + return "", fmt.Errorf("invalid/empty chart version") + } + return x, nil +} + func (c *Chart) BuildPulledChartDir(baseDir string, version string) (string, error) { u, err := url.Parse(c.repo) if err != nil { @@ -122,6 +162,10 @@ func (c *Chart) GetChartName() string { } func (c *Chart) PullToTmp(ctx context.Context, version string) (*PulledChart, error) { + if c.IsLocalChart() { + return nil, fmt.Errorf("can not pull local charts") + } + tmpPullDir, err := os.MkdirTemp(utils.GetTmpBaseDir(ctx), c.chartName+"-pull-") if err != nil { return nil, err @@ -199,6 +243,10 @@ func (c *Chart) PullToTmp(ctx context.Context, version string) (*PulledChart, er } func (c *Chart) Pull(ctx context.Context, pc *PulledChart) error { + if c.IsLocalChart() { + return fmt.Errorf("can not pull local charts") + } + newPulled, err := c.PullToTmp(ctx, pc.version) if err != nil { return err @@ -250,6 +298,10 @@ func (c *Chart) doPullCached(ctx context.Context, version string) (*PulledChart, } func (c *Chart) PullCached(ctx context.Context, version string) (*PulledChart, error) { + if c.IsLocalChart() { + return nil, fmt.Errorf("can not pull local charts") + } + pc, lock, err := c.doPullCached(ctx, version) if err != nil { return nil, err @@ -259,6 +311,10 @@ func (c *Chart) PullCached(ctx context.Context, version string) (*PulledChart, e } func (c *Chart) PullInProject(ctx context.Context, baseDir string, version string) (*PulledChart, error) { + if c.IsLocalChart() { + return nil, fmt.Errorf("can not pull local charts") + } + cachePc, lock, err := c.doPullCached(ctx, version) if err != nil { return nil, err @@ -284,6 +340,10 @@ func (c *Chart) PullInProject(ctx context.Context, baseDir string, version strin } func (c *Chart) GetPulledChart(baseDir string, version string) (*PulledChart, error) { + if c.IsLocalChart() { + return nil, fmt.Errorf("can not pull local charts") + } + chartDir, err := c.BuildPulledChartDir(baseDir, version) if err != nil { return nil, err @@ -292,6 +352,10 @@ func (c *Chart) GetPulledChart(baseDir string, version string) (*PulledChart, er } func (c *Chart) QueryVersions(ctx context.Context) error { + if c.IsLocalChart() { + return fmt.Errorf("can not query versions for local charts") + } + if registry.IsOCI(c.repo) { return c.queryVersionsOci(ctx) } diff --git a/pkg/helm/helm_release.go b/pkg/helm/helm_release.go index 885082193..c4d8963e4 100644 --- a/pkg/helm/helm_release.go +++ b/pkg/helm/helm_release.go @@ -35,23 +35,41 @@ type Release struct { baseChartsDir string } -func NewRelease(configFile string, baseChartsDir string, credentialsProvider HelmCredentialsProvider) (*Release, error) { +func NewRelease(projectRoot string, relDirInProject string, configFile string, baseChartsDir string, credentialsProvider HelmCredentialsProvider) (*Release, error) { var config types.HelmChartConfig err := yaml.ReadYamlFile(configFile, &config) if err != nil { return nil, err } - _, err = semver.NewVersion(config.ChartVersion) - if err != nil { - return nil, fmt.Errorf("invalid chart version '%s': %w", config.ChartVersion, err) + if config.ChartVersion != "" { + _, err = semver.NewVersion(config.ChartVersion) + if err != nil { + return nil, fmt.Errorf("invalid chart version '%s': %w", config.ChartVersion, err) + } + } + + localPath := "" + if config.Path != "" { + if filepath.IsAbs(config.Path) { + return nil, fmt.Errorf("absolute path is not allowed in helm-chart.yaml") + } + localPath = filepath.Join(projectRoot, relDirInProject, config.Path) + localPath, err = filepath.Abs(localPath) + if err != nil { + return nil, err + } + err = utils.CheckInDir(projectRoot, localPath) + if err != nil { + return nil, err + } } credentialsId := "" if config.CredentialsId != nil { credentialsId = *config.CredentialsId } - chart, err := NewChart(config.Repo, config.ChartName, credentialsProvider, credentialsId) + chart, err := NewChart(config.Repo, localPath, config.ChartName, credentialsProvider, credentialsId) if err != nil { return nil, err } @@ -93,6 +111,14 @@ func (hr *Release) GetDeprecatedChartDir() string { } func (hr *Release) getPulledChart(ctx context.Context) (*PulledChart, error) { + if hr.Chart.IsLocalChart() { + version, err := hr.Chart.GetLocalChartVersion() + if err != nil { + return nil, err + } + return NewPulledChart(hr.Chart, version, hr.Chart.GetLocalPath(), false), nil + } + deprecatedPC := NewPulledChart(hr.Chart, hr.Config.ChartVersion, hr.GetDeprecatedChartDir(), false) if deprecatedPC.CheckExists() { status.Deprecation(ctx, "helm-charts-dir", "Your project has pre-pulled charts located next to the helm-chart.yaml, which is deprecated. "+ diff --git a/pkg/types/helm_chart.go b/pkg/types/helm_chart.go index 11498426b..e2c45601f 100644 --- a/pkg/types/helm_chart.go +++ b/pkg/types/helm_chart.go @@ -1,10 +1,17 @@ package types +import ( + "github.com/go-playground/validator/v10" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "helm.sh/helm/v3/pkg/registry" +) + type HelmChartConfig2 struct { - Repo string `yaml:"repo" validate:"required"` + Repo string `yaml:"repo,omitempty"` + Path string `yaml:"path,omitempty"` CredentialsId *string `yaml:"credentialsId,omitempty"` ChartName string `yaml:"chartName,omitempty"` - ChartVersion string `yaml:"chartVersion" validate:"required"` + ChartVersion string `yaml:"chartVersion,omitempty"` UpdateConstraints *string `yaml:"updateConstraints,omitempty"` ReleaseName string `yaml:"releaseName" validate:"required"` Namespace *string `yaml:"namespace,omitempty"` @@ -13,6 +20,42 @@ type HelmChartConfig2 struct { SkipUpdate bool `yaml:"skipUpdate,omitempty"` } +func ValidateHelmChartConfig2(sl validator.StructLevel) { + c := sl.Current().Interface().(HelmChartConfig2) + if c.Repo == "" && c.Path == "" { + sl.ReportError("self", "repo", "repo", "either repo or path must be specified", "") + } else if c.Repo != "" && c.Path != "" { + sl.ReportError("self", "repo", "repo", "only one of repo and path can be specified", "") + } else if c.Repo != "" { + if c.ChartVersion == "" { + sl.ReportError("self", "chartVersion", "chartVersion", "chartVersion must be specified when repo is specified", "") + } + if registry.IsOCI(c.Repo) { + if c.ChartName != "" { + sl.ReportError("self", "chartName", "chartName", "chartName can not be specified when repo is a OCI url", "") + } + } else { + if c.ChartName == "" { + sl.ReportError("self", "chartName", "chartName", "chartName must be specified when repo is normal Helm repo", "") + } + } + } else if c.Path != "" { + if c.ChartName != "" { + sl.ReportError("self", "chartName", "chartName", "chartName can not be specified for local Helm charts", "") + } + if c.ChartVersion != "" { + sl.ReportError("self", "chartVersion", "chartVersion", "chartVersion can not be specified for local Helm charts", "") + } + if c.UpdateConstraints != nil { + sl.ReportError("self", "updateConstraints", "updateConstraints", "updateConstraints can not be specified for local Helm charts", "") + } + } +} + type HelmChartConfig struct { HelmChartConfig2 `yaml:"helmChart" validate:"required"` } + +func init() { + yaml.Validator.RegisterStructValidation(ValidateHelmChartConfig2, HelmChartConfig2{}) +} From ef2f7271b9e5e7d6ddf5efdcf580895433503edd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Dec 2022 14:42:59 +0100 Subject: [PATCH 0680/2268] fix: Show help when kluctl is called without arguments --- cmd/kluctl/commands/root.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 2c6d696f1..8b7231230 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -18,6 +18,7 @@ package commands import ( "context" "fmt" + flag "github.com/spf13/pflag" "io" "log" "net/http" @@ -194,7 +195,7 @@ func checkNewVersion(ctx context.Context) { } func (c *cli) Run(ctx context.Context) error { - return nil + return flag.ErrHelp } func initViper(ctx context.Context) { From 11325b13aff0988c4f2d324539624dc4df6800fb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Dec 2022 15:01:07 +0100 Subject: [PATCH 0681/2268] fix: Fix help output of completion command --- cmd/kluctl/commands/cobra_utils.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index 5ebf48f10..e8e68f528 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -88,7 +88,7 @@ func (c *rootCommand) buildCobraCmd(parent *commandAndGroups, cmdStruct interfac } cg.cmd.SetHelpFunc(func(command *cobra.Command, i []string) { - c.helpFunc(cg) + c.helpFunc(cg, command) }) return cg, nil @@ -283,9 +283,7 @@ func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { return utils.NewErrorListOrNil(retErr) } -func (c *rootCommand) helpFunc(cg *commandAndGroups) { - cmd := cg.cmd - +func (c *rootCommand) helpFunc(cg *commandAndGroups, cmd *cobra.Command) { termWidth := term.GetWidth() h := "Usage: " From 876cf3fdfc3b5a8f22948e79e2e6fac2dbf6bce2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Dec 2022 15:05:07 +0100 Subject: [PATCH 0682/2268] fix: Actually install shell completions in brew tap --- .goreleaser.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 9570d3b69..43a80732f 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -115,8 +115,6 @@ brews: description: "kluctl" install: | bin.install "kluctl" - test: | - system "#{bin}/kluctl version" bash_output = Utils.safe_popen_read(bin/"kluctl", "completion", "bash") (bash_completion/"kluctl").write bash_output @@ -126,3 +124,5 @@ brews: fish_output = Utils.safe_popen_read(bin/"kluctl", "completion", "fish") (fish_completion/"kluctl.fish").write fish_output + test: | + system "#{bin}/kluctl version" From 99a8aabb23784f89ed73bde09b7938d6d863a1ab Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Dec 2022 15:08:12 +0100 Subject: [PATCH 0683/2268] fix: Flush status before printing version --- cmd/kluctl/commands/cmd_version.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/kluctl/commands/cmd_version.go b/cmd/kluctl/commands/cmd_version.go index c28a5b37e..3328a0203 100644 --- a/cmd/kluctl/commands/cmd_version.go +++ b/cmd/kluctl/commands/cmd_version.go @@ -2,6 +2,7 @@ package commands import ( "context" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/version" ) @@ -9,6 +10,7 @@ type versionCmd struct { } func (cmd *versionCmd) Run(ctx context.Context) error { + status.Flush(ctx) _, err := getStdout(ctx).WriteString(version.GetVersion() + "\n") return err } From c01a8bfb18ee1c96ab704f1004a11d347ec4062e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Dec 2022 22:32:54 +0100 Subject: [PATCH 0684/2268] chore: Run go get -u ./... --- go.mod | 92 ++++++++++----------- go.sum | 248 +++++++++++++++++++++++---------------------------------- 2 files changed, 146 insertions(+), 194 deletions(-) diff --git a/go.mod b/go.mod index 673227fa3..4065cbaa8 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,16 @@ module github.com/kluctl/kluctl/v2 go 1.19 require ( - github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.160 - github.com/bitnami-labs/sealed-secrets v0.19.2 + github.com/aws/aws-sdk-go v1.44.161 + github.com/bitnami-labs/sealed-secrets v0.19.3 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible // See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df - github.com/fluxcd/pkg/kustomize v0.11.0 + github.com/fluxcd/pkg/kustomize v0.12.0 github.com/go-playground/validator/v10 v10.11.1 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 @@ -22,8 +22,8 @@ require ( github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/kluctl/go-embed-python v0.0.0-3.10.7-20221017-1 - github.com/kluctl/go-jinja2 v0.0.0-20221017153334-80addb432305 + github.com/kluctl/go-embed-python v0.0.0-3.10.8-20221106-1 + github.com/kluctl/go-jinja2 v0.0.0-20221215083015-c3f906953ba1 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 @@ -68,16 +68,16 @@ require ( ) require ( - cloud.google.com/go/compute v1.12.1 // indirect - cloud.google.com/go/compute/metadata v0.2.1 // indirect + cloud.google.com/go/compute v1.14.0 // indirect + cloud.google.com/go/compute/metadata v0.2.2 // indirect filippo.io/age v1.0.0 // indirect - github.com/Azure/azure-sdk-for-go v63.3.0+incompatible // indirect + github.com/Azure/azure-sdk-for-go v67.1.0+incompatible // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.28 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect @@ -86,7 +86,7 @@ require ( github.com/BurntSushi/toml v1.2.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.2 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect @@ -97,20 +97,20 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/cloudflare/circl v1.2.0 // indirect - github.com/containerd/containerd v1.6.9 // indirect + github.com/cloudflare/circl v1.3.0 // indirect + github.com/containerd/containerd v1.6.13 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/docker/cli v20.10.20+incompatible // indirect - github.com/docker/docker v20.10.20+incompatible // indirect + github.com/docker/cli v20.10.21+incompatible // indirect + github.com/docker/docker v20.10.21+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/drone/envsubst v1.0.3 // indirect - github.com/emicklei/go-restful/v3 v3.10.0 // indirect + github.com/emicklei/go-restful/v3 v3.10.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect @@ -121,7 +121,7 @@ require ( github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect - github.com/go-git/go-git/v5 v5.4.2 // indirect + github.com/go-git/go-git/v5 v5.5.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect @@ -138,17 +138,17 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect - github.com/googleapis/gax-go/v2 v2.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect + github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.3.1 // indirect + github.com/hashicorp/go-hclog v1.4.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-plugin v1.4.5 // indirect + github.com/hashicorp/go-plugin v1.4.8 // indirect github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect @@ -159,24 +159,24 @@ require ( github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/vault/sdk v0.6.0 // indirect + github.com/hashicorp/vault/sdk v0.6.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.11 // indirect + github.com/klauspost/compress v1.15.13 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/lib/pq v1.10.7 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/magiconair/properties v1.8.6 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect @@ -184,7 +184,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect + github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect @@ -194,23 +194,23 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pjbgf/sha1cd v0.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/common v0.39.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/rivo/uniseg v0.4.2 // indirect + github.com/rivo/uniseg v0.4.3 // indirect github.com/rubenv/sql-migrate v1.2.0 // indirect - github.com/russross/blackfriday v1.6.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/skeema/knownhosts v1.1.0 // indirect - github.com/spf13/afero 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/subosito/gotenv v1.4.1 // indirect @@ -219,20 +219,20 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect - go.etcd.io/etcd/api/v3 v3.5.5 // indirect + go.etcd.io/etcd/api/v3 v3.5.6 // indirect go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a // indirect - go.opencensus.io v0.23.0 // indirect - go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 // indirect + go.opencensus.io v0.24.0 // indirect + go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/mod v0.6.0 // indirect - golang.org/x/oauth2 v0.2.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/oauth2 v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.2.0 // indirect + golang.org/x/tools v0.4.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/api v0.102.0 // indirect + google.golang.org/api v0.105.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect - google.golang.org/grpc v1.50.1 // indirect + google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 // indirect + google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect @@ -240,12 +240,12 @@ require ( gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect k8s.io/apiserver v0.26.0 // indirect - k8s.io/cli-runtime v0.25.3 // indirect + k8s.io/cli-runtime v0.26.0 // indirect k8s.io/component-base v0.26.0 // indirect - k8s.io/kube-openapi v0.0.0-20221110221610-a28e98eb7c70 // indirect - k8s.io/kubectl v0.25.3 // indirect + k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715 // indirect + k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect oras.land/oras-go v1.2.1 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index c865d24b6..54d211f55 100644 --- a/go.sum +++ b/go.sum @@ -20,19 +20,21 @@ cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPT cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= 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.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute/metadata v0.2.2 h1:aWKAjYaBaOSrpKl57+jnS/3fJRQnxL7TvR/u1VVbt6k= +cloud.google.com/go/compute/metadata v0.2.2/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= 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= @@ -46,8 +48,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc= filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8= -github.com/Azure/azure-sdk-for-go v63.3.0+incompatible h1:INepVujzUrmArRZjDLHbtER+FkvCoEwyRCXGqOlmDII= -github.com/Azure/azure-sdk-for-go v63.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v67.1.0+incompatible h1:oziYcaopbnIKfM69DL05wXdypiqfrUKdxUKrKpynJTw= +github.com/Azure/azure-sdk-for-go v67.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -60,8 +62,9 @@ github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxsh github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= @@ -75,8 +78,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= -github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -92,19 +95,16 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0 github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= +github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= @@ -116,8 +116,6 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy 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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -132,15 +130,15 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.160 h1:F41sWUel1CJ69ezoBGCg8sDyu9kyeKEpwmDrLXbCuyA= -github.com/aws/aws-sdk-go v1.44.160/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.161 h1:uZdZJ30mlbaU2wsrd/wzibrX01cbgKE2t486TtRjeHs= +github.com/aws/aws-sdk-go v1.44.161/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.19.2 h1:0NpJCAhfDTkZ2u3PJaRWSynxlg55ssHxPQNn8T5JNPU= -github.com/bitnami-labs/sealed-secrets v0.19.2/go.mod h1:KjTgHdD22gpjERjT1rA8yi4P4EkccMhRR2JW6Ctfu6g= +github.com/bitnami-labs/sealed-secrets v0.19.3 h1:D9v0fQt8JNDEoBU6HRKKJ20VhqvGUyaDrK1bl714i4I= +github.com/bitnami-labs/sealed-secrets v0.19.3/go.mod h1:G7Psbu6s3ed7GCO6ZjZ51zff0GGGSymLxFXb4QkJnRw= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -150,15 +148,14 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= 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.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/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/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -168,16 +165,16 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= -github.com/cloudflare/circl v1.2.0 h1:NheeISPSUcYftKlfrLuOo4T62FkmD4t4jviLfFFYaec= -github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8M+INXlMk= +github.com/cloudflare/circl v1.3.0 h1:Anq00jxDtoyX3+aCaYUZ0vXC5r4k4epberfWGDXV1zE= +github.com/cloudflare/circl v1.3.0/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw= 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/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/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= -github.com/containerd/containerd v1.6.9 h1:IN/r8DUes/B5lEGTNfIiUkfZBtIQJGx2ai703dV6lRA= -github.com/containerd/containerd v1.6.9/go.mod h1:XVicUvkxOrftE2Q1YWUXgZwkkAxwQYNOFzYWvfVfEfQ= +github.com/containerd/containerd v1.6.13 h1:7llWEzjLH/fao0f2lppn1L6NhjsvxqMdUQa2mgVCs+U= +github.com/containerd/containerd v1.6.13/go.mod h1:vDm+BbU+dD9uvuUlHr+KmcY0HKX8WDyI6dzJjNi4ee8= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -185,8 +182,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -196,12 +192,12 @@ github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27N github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= -github.com/docker/cli v20.10.20+incompatible h1:lWQbHSHUFs7KraSN2jOJK7zbMS2jNCHI4mt4xUFUVQ4= -github.com/docker/cli v20.10.20+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= +github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.20+incompatible h1:kH9tx6XO+359d+iAkumyKDc5Q1kOwPuAUaeri48nD6E= -github.com/docker/docker v20.10.20+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= +github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -216,9 +212,8 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -github.com/emicklei/go-restful/v3 v3.10.0 h1:X4gma4HM7hFm6WMeAsTfqA0GOfdNoCzBIkHGoRLGXuM= -github.com/emicklei/go-restful/v3 v3.10.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= +github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -247,29 +242,25 @@ github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df h1:2BHXJp1PwX7D47 github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df/go.mod h1:raWgfUV7lDQVXp4QXUaeNNJkRVKz97UQuF+0kdY7Vmo= github.com/fluxcd/pkg/apis/kustomize v0.7.0 h1:X2htBmJ91nGYv4d93gin665MFWKNGiNwUiZ08/Zz0hY= github.com/fluxcd/pkg/apis/kustomize v0.7.0/go.mod h1:Mu+KdktsEKWA4l/33CZdY5lB4hz51mqfcLzBZSwAqVg= -github.com/fluxcd/pkg/kustomize v0.11.0 h1:zseS9LRUuzhP/7KamccmsOgYpJAdhqtsf+2wN/CHF3I= -github.com/fluxcd/pkg/kustomize v0.11.0/go.mod h1:awHID4OKe2/WAfTFg4u0fURXZPUkrIslSZNSPX9MEFQ= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fluxcd/pkg/kustomize v0.12.0 h1:4MQdbP3M8NTIcr8TgNMxRCN+2xZoMWtCDDS3RiOT+8M= +github.com/fluxcd/pkg/kustomize v0.12.0/go.mod h1:awHID4OKe2/WAfTFg4u0fURXZPUkrIslSZNSPX9MEFQ= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= -github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/go-git/go-git/v5 v5.5.1 h1:5vtv2TB5PM/gPM+EvsHJ16hJh4uAkdGcKilcwY7FYwo= +github.com/go-git/go-git/v5 v5.5.1/go.mod h1:uz5PQ3d0gz7mSgzZhSJToM6ALPaKCdSnl58/Xb5hzr8= 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= @@ -278,12 +269,9 @@ github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= 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-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.0/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= @@ -409,12 +397,12 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= 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/gax-go/v2 v2.6.0 h1:SXk3ABtQYDT/OH8jAyvEOQ58mgawq5C4o/4/89qN2ZU= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -438,8 +426,8 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo= -github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= +github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -447,8 +435,8 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= -github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= +github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= @@ -485,8 +473,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/vault/api v1.8.2 h1:C7OL9YtOtwQbTKI9ogB0A1wffRbCN+rH/LLCHO3d8HM= github.com/hashicorp/vault/api v1.8.2/go.mod h1:ML8aYzBIhY5m1MD1B2Q0JV89cC85YVH4t5kBaZiyVaE= -github.com/hashicorp/vault/sdk v0.6.0 h1:6Z+In5DXHiUfZvIZdMx7e2loL1PPyDjA4bVh9ZTIAhs= -github.com/hashicorp/vault/sdk v0.6.0/go.mod h1:+DRpzoXIdMvKc88R4qxr+edwy/RvH5QK8itmxLiDHLc= +github.com/hashicorp/vault/sdk v0.6.1 h1:sjZC1z4j5Rh2GXYbkxn5BLK05S1p7+MhW4AgdUmgRUA= +github.com/hashicorp/vault/sdk v0.6.1/go.mod h1:Ck4JuAC6usTphfrrRJCRH+7/N7O2ozZzkm/fzQFt4uM= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -495,17 +483,18 @@ github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 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.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 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.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -521,11 +510,9 @@ github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= 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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/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= @@ -533,22 +520,19 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= -github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/kluctl/go-embed-python v0.0.0-3.10.7-20221017-1 h1:4XlPysiwaex+j7+r6pPA12WyuBM9Sy6gSoeKYTqxU9M= -github.com/kluctl/go-embed-python v0.0.0-3.10.7-20221017-1/go.mod h1:fuMI+yFFgrViXsRl+NIWQeuPDTvJtOk2pe0HoeujT54= -github.com/kluctl/go-jinja2 v0.0.0-20221017153334-80addb432305 h1:95WNlnGuRfLXgWoeBTDV0DhHwX26hUfnIzgiYTitsLk= -github.com/kluctl/go-jinja2 v0.0.0-20221017153334-80addb432305/go.mod h1:2G09vDjMA8VSWJT7Kox9EQi8l7DCkGnyBNcd7eomM1s= +github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0= +github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/kluctl/go-embed-python v0.0.0-3.10.8-20221106-1 h1:PXkdM8Z/Lh1+GIjXVG3dFs5enQoQ1eC7JXMz/Mo4R04= +github.com/kluctl/go-embed-python v0.0.0-3.10.8-20221106-1/go.mod h1:qVVSnYkq5NSgUuLzz4hTCXvVsRQyF/ic4U5v7DaiCGM= +github.com/kluctl/go-jinja2 v0.0.0-20221215083015-c3f906953ba1 h1:LwFIJE5cb6AAThM8FPbg589Y7zaToSrBCEQ1bxs8eiY= +github.com/kluctl/go-jinja2 v0.0.0-20221215083015-c3f906953ba1/go.mod h1:hkHz+qOoK67xFAM8M6OkvvXpEsYq8ZKQxkYkat6J058= 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/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= 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= @@ -574,8 +558,8 @@ github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +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.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -607,8 +591,8 @@ github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= -github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= @@ -639,8 +623,8 @@ github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQ github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 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= @@ -655,7 +639,6 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P 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/ohler55/ojg v1.14.5 h1:xCX2oyh/ZaoesbLH6fwVHStSJpk4o4eJs8ttXutzdg0= github.com/ohler55/ojg v1.14.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= @@ -683,8 +666,8 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pelletier/go-toml v1.9.3/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.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +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/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= @@ -708,9 +691,6 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 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= @@ -722,25 +702,19 @@ github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3d github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= 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.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -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.7.3/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/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= -github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 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= @@ -749,9 +723,8 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU= github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -766,9 +739,7 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= @@ -779,8 +750,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +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.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= @@ -789,7 +760,6 @@ github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 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.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= @@ -821,7 +791,6 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaU github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= @@ -846,8 +815,8 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= -go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= +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.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k= @@ -860,11 +829,12 @@ 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.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 h1:5/KzhcSqd4UgY51l17r7C5g/JiE6DRw1Vq7VJfQHuMc= -go.starlark.net v0.0.0-20221028183056-acb66ad56dd2/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= +go.starlark.net v0.0.0-20221205180719-3fd0dac74452 h1:JZtNuL6LPB+scU5yaQ6hqRlJFRiddZm2FwRt2AQqtHA= +go.starlark.net v0.0.0-20221205180719-3fd0dac74452/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= 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= @@ -874,7 +844,6 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -884,17 +853,16 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -933,8 +901,8 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -973,16 +941,13 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -997,10 +962,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU= -golang.org/x/oauth2 v0.2.0/go.mod h1:Cwn6afJ8jrQwYMxQDTpISoXmXW9I6qF6vDeuuoX3Ibs= +golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8= +golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= 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= @@ -1030,13 +993,11 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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= @@ -1050,8 +1011,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1065,14 +1024,11 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/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-20210502180810-71e4cd670f79/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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1080,9 +1036,6 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1092,6 +1045,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1099,6 +1053,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1130,7 +1085,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn 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= @@ -1173,13 +1127,12 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= 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= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= 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= @@ -1204,8 +1157,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.102.0 h1:JxJl2qQ85fRMPNvlZY/enexbxpCjLwGhZUtgfGeQ51I= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.105.0 h1:t6P9Jj+6XTn4U9I2wycQai6Q/Kz7iOT+QzjJ3G2V4x8= +google.golang.org/api v0.105.0/go.mod h1:qh7eD5FJks5+BcE+cjBIm6Gz8vioK7EHvnlniqXBnqI= 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= @@ -1258,8 +1211,8 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 h1:GEgb2jF5zxsFJpJfg9RoDDWm7tiwc/DDSTE2BtLUkXU= -google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70= +google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= 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= @@ -1282,8 +1235,8 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG 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.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= 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= @@ -1334,7 +1287,6 @@ 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 v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= helm.sh/helm/v3 v3.10.3 h1:wL7IUZ7Zyukm5Kz0OUmIFZgKHuAgByCrUcJBtY0kDyw= helm.sh/helm/v3 v3.10.3/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= @@ -1353,18 +1305,18 @@ k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o= k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84= -k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= -k8s.io/cli-runtime v0.25.3/go.mod h1:InHHsjkyW5hQsILJGpGjeruiDZT/R0OkROQgD6GzxO4= +k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= +k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= k8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs= k8s.io/component-base v0.26.0/go.mod h1:lqHwlfV1/haa14F/Z5Zizk5QmzaVf23nQzCwVOQpfC8= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20221110221610-a28e98eb7c70 h1:zfqQc1V6/ZgGpvrOVvr62OjiqQX4lZjfznK34NQwkqw= -k8s.io/kube-openapi v0.0.0-20221110221610-a28e98eb7c70/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= -k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= +k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715 h1:tBEbstoM+K0FiBV5KGAKQ0kuvf54v/hwpldiJt69w1s= +k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= +k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.1 h1:/VcGS8FUy3eEXLl/1vC4QypLHwrfSmgW7ygsoklqKK8= @@ -1374,8 +1326,8 @@ 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/controller-runtime v0.14.0 h1:ju2xsov5Ara6FoQuddg+az+rAxsUsTYn2IYyEKCTyDc= sigs.k8s.io/controller-runtime v0.14.0/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +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/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= From 347a2b46673a02b895cc272372458f67600ed054 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 16 Dec 2022 15:18:32 +0100 Subject: [PATCH 0685/2268] fix: Use correct .helm-charts dir when using git includes --- cmd/kluctl/commands/cmd_helm_update.go | 2 +- pkg/deployment/deployment_item.go | 19 +++++++++++++++---- pkg/deployment/shared_context.go | 1 - pkg/kluctl_project/project.go | 1 - pkg/kluctl_project/project_load.go | 1 - pkg/kluctl_project/target_context.go | 1 - 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 059c70233..fd4856c1b 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -40,7 +40,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { } if !yaml.Exists(filepath.Join(projectDir, ".kluctl.yaml")) { - return fmt.Errorf("helm-pull can only be used on the root of a Kluctl project that must have a .kluctl.yaml file") + return fmt.Errorf("helm-update can only be used on the root of a Kluctl project that must have a .kluctl.yaml file") } gitRootPath, err := git2.DetectGitRepositoryRoot(projectDir) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index bf5469beb..a9ac22883 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -173,6 +173,17 @@ func (di *DeploymentItem) isHelmValuesYaml(p string) bool { return file == "helm-values.yml" || file == "helm-values.yaml" } +func (di *DeploymentItem) newHelmRelease(subDir string) (*helm.Release, error) { + configPath := yaml.FixPathExt(filepath.Join(di.RenderedDir, subDir, "helm-chart.yaml")) + helmChartsDir := filepath.Join(di.Project.source.dir, ".helm-charts") + + hr, err := helm.NewRelease(di.Project.source.dir, filepath.Join(di.RelToSourceItemDir, subDir), configPath, helmChartsDir, di.ctx.HelmCredentials) + if err != nil { + return nil, err + } + return hr, nil +} + func (di *DeploymentItem) renderHelmCharts() error { if di.dir == nil { return nil @@ -188,7 +199,7 @@ func (di *DeploymentItem) renderHelmCharts() error { return err } - hr, err := helm.NewRelease(di.Project.source.dir, filepath.Join(di.RelToSourceItemDir, subDir), p, di.ctx.HelmChartsDir, di.ctx.HelmCredentials) + hr, err := di.newHelmRelease(subDir) if err != nil { return err } @@ -411,12 +422,12 @@ func (di *DeploymentItem) generateKustomizationYaml(subDir string) (*uo.Unstruct if di.isHelmValuesYaml(de.Name()) { continue } else if di.isHelmChartYaml(de.Name()) { - c, err := helm.NewRelease(di.Project.source.dir, filepath.Join(di.RelToSourceItemDir, subDir), filepath.Join(di.RenderedDir, subDir, de.Name()), di.ctx.HelmChartsDir, nil) + hr, err := di.newHelmRelease(subDir) if err != nil { return nil, err } - if !utils.IsFile(filepath.Join(di.RenderedDir, subDir, c.GetOutputPath())) { - resourcePath = c.GetOutputPath() + if !utils.IsFile(filepath.Join(di.RenderedDir, subDir, hr.GetOutputPath())) { + resourcePath = hr.GetOutputPath() } } else if strings.HasSuffix(lname, ".yml") || strings.HasSuffix(lname, ".yaml") { resourcePath = de.Name() diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index 49133fecb..dec1c4571 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -21,5 +21,4 @@ type SharedContext struct { RenderDir string SealedSecretsDir string DefaultSealedSecretsOutputPattern string - HelmChartsDir string } diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index d30936121..0df49e01c 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -20,7 +20,6 @@ type LoadedKluctlProject struct { ProjectDir string sealedSecretsDir string - helmChartsDir string Config types2.KluctlProject DynamicTargets []*types2.DynamicTarget diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index b809bdd68..4c075fd23 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -61,7 +61,6 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { } c.sealedSecretsDir = filepath.Join(c.ProjectDir, ".sealed-secrets") - c.helmChartsDir = filepath.Join(c.ProjectDir, ".helm-charts") sealedSecretsUsed := false if c.Config.SecretsConfig != nil { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 9ee3c53ec..f07436014 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -113,7 +113,6 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe RenderDir: params.RenderOutputDir, SealedSecretsDir: p.sealedSecretsDir, DefaultSealedSecretsOutputPattern: target.Name, - HelmChartsDir: p.helmChartsDir, } d, err := deployment.NewDeploymentProject(dctx, varsCtx, deployment.NewSource(deploymentDir), ".", nil) From 347059d4bb06c47f773764ab4f8d584c074d6494 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 22:06:53 +0000 Subject: [PATCH 0686/2268] chore(deps): Bump goreleaser/goreleaser-action from 3 to 4 Bumps [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) from 3 to 4. - [Release notes](https://github.com/goreleaser/goreleaser-action/releases) - [Commits](https://github.com/goreleaser/goreleaser-action/compare/v3...v4) --- updated-dependencies: - dependency-name: goreleaser/goreleaser-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dd4ea8163..4e2a47b9a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,7 +46,7 @@ jobs: restore-keys: | ${{ runner.os }}-goreleaser- - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v3 + uses: goreleaser/goreleaser-action@v4 with: distribution: goreleaser version: latest From 7f1b3c747100468040fa19ac29b8cf28210133f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 22:10:08 +0000 Subject: [PATCH 0687/2268] chore(deps): Bump github.com/ohler55/ojg from 1.14.5 to 1.15.0 Bumps [github.com/ohler55/ojg](https://github.com/ohler55/ojg) from 1.14.5 to 1.15.0. - [Release notes](https://github.com/ohler55/ojg/releases) - [Changelog](https://github.com/ohler55/ojg/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/ojg/compare/v1.14.5...v1.15.0) --- updated-dependencies: - dependency-name: github.com/ohler55/ojg dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4065cbaa8..c3fb32abb 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.14.5 + github.com/ohler55/ojg v1.15.0 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.9.0 diff --git a/go.sum b/go.sum index 54d211f55..3e97f3368 100644 --- a/go.sum +++ b/go.sum @@ -640,8 +640,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/ohler55/ojg v1.14.5 h1:xCX2oyh/ZaoesbLH6fwVHStSJpk4o4eJs8ttXutzdg0= -github.com/ohler55/ojg v1.14.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= +github.com/ohler55/ojg v1.15.0 h1:Z95FvBiMsMOOGP9Nzv5OVV4ND2KnEMxk0GOS8Kvcahg= +github.com/ohler55/ojg v1.15.0/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= From 55633c0df94f465b15f3915c2a97362cb6745219 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 22:10:29 +0000 Subject: [PATCH 0688/2268] chore(deps): Bump github.com/aws/aws-sdk-go from 1.44.161 to 1.44.163 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.161 to 1.44.163. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.44.161...v1.44.163) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4065cbaa8..3e4135281 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.161 + github.com/aws/aws-sdk-go v1.44.163 github.com/bitnami-labs/sealed-secrets v0.19.3 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index 54d211f55..e136f88e8 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.161 h1:uZdZJ30mlbaU2wsrd/wzibrX01cbgKE2t486TtRjeHs= -github.com/aws/aws-sdk-go v1.44.161/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.163 h1:XO1A/Laqf/l0IxVPghaQzdnVwxofVFv00IlX0BpmbhQ= +github.com/aws/aws-sdk-go v1.44.163/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= From c248ff003165b81151686122a2fec3f28468edf8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Dec 2022 10:27:43 +0100 Subject: [PATCH 0689/2268] fix: Don't try to treat final error string as formatted string --- cmd/kluctl/commands/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 8b7231230..f7520e0b5 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -292,7 +292,7 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif err = rootCmd.Execute() if err != nil { - status.Error(ctx, err.Error()) + status.Error(ctx, "%s", err.Error()) return err } return nil From 14a3d829d3e522c7f0c4284091ddfe386613d78b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Dec 2022 11:28:31 +0100 Subject: [PATCH 0690/2268] docs: Fix typo --- docs/reference/deployments/helm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index bc31615f6..665f3ef51 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -116,7 +116,7 @@ Specifies version constraints to be used when running [helm-update](../commands/ [Checking Version Constraints](https://github.com/Masterminds/semver#checking-version-constraints) for details on the supported syntax. -If omitted, Kluctl will filter out pre-releases by default. Use a `updateConstraings` like `~1.2.3-0` to enable +If omitted, Kluctl will filter out pre-releases by default. Use a `updateConstraints` like `~1.2.3-0` to enable pre-releases. ### skipUpdate From 3bec1e366ebb4c398a2a5de2c141131d3401bad7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Dec 2022 15:11:34 +0100 Subject: [PATCH 0691/2268] fix: Fix globals flags being ignored when specified via env variables --- cmd/kluctl/commands/root.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index f7520e0b5..fe314bba0 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -223,7 +223,7 @@ func Main() { initViper(ctx) - err := Execute(ctx, os.Args[1:], func(ctxIn context.Context, cmd *cobra.Command, flags GlobalFlags) (context.Context, error) { + err := Execute(ctx, os.Args[1:], func(ctxIn context.Context, cmd *cobra.Command, flags *GlobalFlags) (context.Context, error) { err := copyViperValuesToCobraCmd(cmd) if err != nil { return ctx, err @@ -257,7 +257,7 @@ func Main() { } } -func Execute(ctx context.Context, args []string, preRun func(ctx context.Context, rootCmd *cobra.Command, flags GlobalFlags) (context.Context, error)) error { +func Execute(ctx context.Context, args []string, preRun func(ctx context.Context, rootCmd *cobra.Command, flags *GlobalFlags) (context.Context, error)) error { root := cli{} rootCmd, err := buildRootCobraCmd(&root, "kluctl", "Deploy and manage complex deployments on Kubernetes", @@ -277,7 +277,7 @@ composed of multiple smaller parts (Helm/Kustomize/...) in a manageable and unif rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { if preRun != nil { - ctx, err = preRun(ctx, cmd, root.GlobalFlags) + ctx, err = preRun(ctx, cmd, &root.GlobalFlags) if ctx != nil { for c := cmd; c != nil; c = c.Parent() { c.SetContext(ctx) From 07407b8ab90693be05ddbb66cd3d76389ff60c63 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Dec 2022 16:46:13 +0100 Subject: [PATCH 0692/2268] fix: Also obfuscate secrets when data/stringData was empty before --- e2e/obfuscate_test.go | 38 ++++++++++++++++++++++++++++++++++++-- pkg/diff/obfuscate.go | 20 ++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/e2e/obfuscate_test.go b/e2e/obfuscate_test.go index 8f0d018e5..b2fbfc849 100644 --- a/e2e/obfuscate_test.go +++ b/e2e/obfuscate_test.go @@ -25,16 +25,25 @@ func TestObfuscateSecrets(t *testing.T) { name: "secret", namespace: p.TestSlug(), }, false) + addSecretDeployment(p, "secret2", nil, resourceOpts{ + name: "secret2", + namespace: p.TestSlug(), + }, false) + addSecretDeployment(p, "secret3", nil, resourceOpts{ + name: "secret3", + namespace: p.TestSlug(), + }, false) + stdout, _ := p.KluctlMust("deploy", "--yes", "-t", "test") assertSecretExists(t, k, p.TestSlug(), "secret") - assert.NotContains(t, stdout, "secret_value") + assert.NotContains(t, stdout, base64.StdEncoding.EncodeToString([]byte("secret_value"))) p.UpdateYaml("secret/secret-secret.yml", func(o *uo.UnstructuredObject) error { _ = o.SetNestedField("secret_value_2", "stringData", "secret") return nil }, "") stdout, _ = p.KluctlMust("deploy", "--yes", "-t", "test") - assert.NotContains(t, stdout, "secret_value") + assert.NotContains(t, stdout, base64.StdEncoding.EncodeToString([]byte("secret_value"))) assert.Contains(t, stdout, "***** (obfuscated)") p.UpdateYaml("secret/secret-secret.yml", func(o *uo.UnstructuredObject) error { @@ -45,4 +54,29 @@ func TestObfuscateSecrets(t *testing.T) { assert.Contains(t, stdout, "-"+base64.StdEncoding.EncodeToString([]byte("secret_value_2"))) assert.Contains(t, stdout, "+"+base64.StdEncoding.EncodeToString([]byte("secret_value_3"))) assert.NotContains(t, stdout, "***** (obfuscated)") + + // also test changing from empty data to filled data + p.UpdateYaml("secret2/secret-secret2.yml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(map[string]any{ + "secret2": "secret_value_2", + }, "stringData") + return nil + }, "") + + stdout, _ = p.KluctlMust("deploy", "--yes", "-t", "test") + assert.NotContains(t, stdout, base64.StdEncoding.EncodeToString([]byte("secret_value_2"))) + assert.Contains(t, stdout, "+secret2: '***** (obfuscated)'") + + p.UpdateYaml("secret3/secret-secret3.yml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(map[string]any{ + "secret3": "secret_value_3", + "secret4": "secret_value_4", + }, "stringData") + return nil + }, "") + stdout, _ = p.KluctlMust("deploy", "--yes", "-t", "test") + assert.NotContains(t, stdout, base64.StdEncoding.EncodeToString([]byte("secret_value_3"))) + assert.NotContains(t, stdout, base64.StdEncoding.EncodeToString([]byte("secret_value_4"))) + assert.Contains(t, stdout, "+secret3: '***** (obfuscated)'") + assert.Contains(t, stdout, "+secret4: '***** (obfuscated)'") } diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index 710ca60c0..44f5a6109 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -19,6 +19,18 @@ func (o *Obfuscator) Obfuscate(ref k8s.ObjectRef, changes []types.Change) { } func (o *Obfuscator) obfuscateSecret(ref k8s.ObjectRef, changes []types.Change) { + setMapValues := func(m any, v string) { + if m == nil { + return + } + m2, ok := m.(map[string]any) + if ok { + for k, _ := range m2 { + m2[k] = v + } + } + } + for i, _ := range changes { c := &changes[i] if strings.HasPrefix(c.JsonPath, "data.") || strings.HasPrefix(c.JsonPath, "stringData.") { @@ -37,6 +49,14 @@ func (o *Obfuscator) obfuscateSecret(ref k8s.ObjectRef, changes []types.Change) c.OldValue = "*****" c.UnifiedDiff = strings.ReplaceAll(c.UnifiedDiff, "*****b", "***** (obfuscated)") } + } else if c.JsonPath == "data" || c.JsonPath == "stringData" { + setMapValues(c.NewValue, "*****a") + setMapValues(c.OldValue, "*****b") + _ = updateUnifiedDiff(c) + c.UnifiedDiff = strings.ReplaceAll(c.UnifiedDiff, "*****a", "***** (obfuscated)") + c.UnifiedDiff = strings.ReplaceAll(c.UnifiedDiff, "*****b", "***** (obfuscated)") + setMapValues(c.NewValue, "*****") + setMapValues(c.OldValue, "*****") } } } From 02c2bfa5361c73b9d04f3d8b7e4dba1b62134dac Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Dec 2022 21:09:07 +0100 Subject: [PATCH 0693/2268] fix: Also obfuscate data["key.with.dot"] secrets --- cmd/kluctl/commands/command_result.go | 5 +- e2e/obfuscate_test.go | 13 +++++ pkg/diff/obfuscate.go | 71 +++++++++++++++------------ 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index dcf709fa1..bc6bb1ca9 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -204,7 +204,10 @@ func outputCommandResult(ctx context.Context, output []string, noObfuscate bool, if !noObfuscate { var obfuscator diff.Obfuscator for _, c := range cr.ChangedObjects { - obfuscator.Obfuscate(c.Ref, c.Changes) + err := obfuscator.Obfuscate(c.Ref, c.Changes) + if err != nil { + return err + } } } diff --git a/e2e/obfuscate_test.go b/e2e/obfuscate_test.go index b2fbfc849..7cff67ab1 100644 --- a/e2e/obfuscate_test.go +++ b/e2e/obfuscate_test.go @@ -79,4 +79,17 @@ func TestObfuscateSecrets(t *testing.T) { assert.NotContains(t, stdout, base64.StdEncoding.EncodeToString([]byte("secret_value_4"))) assert.Contains(t, stdout, "+secret3: '***** (obfuscated)'") assert.Contains(t, stdout, "+secret4: '***** (obfuscated)'") + + p.UpdateYaml("secret3/secret-secret3.yml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(map[string]any{ + "secret.dot1": "secret_value_5", + "secret.dot2": "secret_value_6", + }, "stringData") + return nil + }, "") + stdout, _ = p.KluctlMust("deploy", "--yes", "-t", "test") + assert.NotContains(t, stdout, base64.StdEncoding.EncodeToString([]byte("secret_value_5"))) + assert.NotContains(t, stdout, base64.StdEncoding.EncodeToString([]byte("secret_value_6"))) + assert.Contains(t, stdout, "data[\"secret.dot1\"] | +***** (obfuscated)") + assert.Contains(t, stdout, "data[\"secret.dot2\"] | +***** (obfuscated)") } diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index 44f5a6109..e576c29b9 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -1,8 +1,10 @@ package diff import ( + "fmt" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/ohler55/ojg/jp" "k8s.io/apimachinery/pkg/runtime/schema" "strings" ) @@ -12,51 +14,58 @@ var secretGvk = schema.GroupKind{Group: "", Kind: "Secret"} type Obfuscator struct { } -func (o *Obfuscator) Obfuscate(ref k8s.ObjectRef, changes []types.Change) { +func (o *Obfuscator) Obfuscate(ref k8s.ObjectRef, changes []types.Change) error { if ref.GVK.GroupKind() == secretGvk { - o.obfuscateSecret(ref, changes) + err := o.obfuscateSecret(ref, changes) + if err != nil { + return err + } } + return nil } -func (o *Obfuscator) obfuscateSecret(ref k8s.ObjectRef, changes []types.Change) { - setMapValues := func(m any, v string) { - if m == nil { - return +func (o *Obfuscator) obfuscateSecret(ref k8s.ObjectRef, changes []types.Change) error { + replaceValues := func(x any, v string) any { + if x == nil { + return nil } - m2, ok := m.(map[string]any) - if ok { - for k, _ := range m2 { - m2[k] = v + if m, ok := x.(map[string]any); ok { + for k, _ := range m { + m[k] = v + } + return m + } else if a, ok := x.([]any); ok { + for i, _ := range a { + a[i] = v } + return a } + return v } for i, _ := range changes { c := &changes[i] - if strings.HasPrefix(c.JsonPath, "data.") || strings.HasPrefix(c.JsonPath, "stringData.") { - if c.NewValue != nil { - c.NewValue = "*****a" - } - if c.OldValue != nil { - c.OldValue = "*****b" - } - _ = updateUnifiedDiff(c) - if c.NewValue != nil { - c.NewValue = "*****" - c.UnifiedDiff = strings.ReplaceAll(c.UnifiedDiff, "*****a", "***** (obfuscated)") - } - if c.OldValue != nil { - c.OldValue = "*****" - c.UnifiedDiff = strings.ReplaceAll(c.UnifiedDiff, "*****b", "***** (obfuscated)") - } - } else if c.JsonPath == "data" || c.JsonPath == "stringData" { - setMapValues(c.NewValue, "*****a") - setMapValues(c.OldValue, "*****b") + j, err := jp.ParseString(c.JsonPath) + if err != nil { + return err + } + if len(j) == 0 { + return fmt.Errorf("unexpected empty jsonPath") + } + child, ok := j[0].(jp.Child) + if !ok { + return fmt.Errorf("unexpected jsonPath fragment: %s", c.JsonPath) + } + + if child == "data" || child == "stringData" { + c.NewValue = replaceValues(c.NewValue, "*****a") + c.OldValue = replaceValues(c.OldValue, "*****b") _ = updateUnifiedDiff(c) + c.NewValue = replaceValues(c.NewValue, "*****") + c.OldValue = replaceValues(c.OldValue, "*****") c.UnifiedDiff = strings.ReplaceAll(c.UnifiedDiff, "*****a", "***** (obfuscated)") c.UnifiedDiff = strings.ReplaceAll(c.UnifiedDiff, "*****b", "***** (obfuscated)") - setMapValues(c.NewValue, "*****") - setMapValues(c.OldValue, "*****") } } + return nil } From 076aa90549dc23dd7e5c365844c052fc3887a9b5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 22 Dec 2022 15:42:52 +0100 Subject: [PATCH 0694/2268] fix: loadFile should NOT ignore all files The "utils.Exists(path)" check always returned false as it did not take searchDirs into account. --- pkg/vars/vars_loader.go | 8 ++++---- pkg/vars/vars_loader_test.go | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 40e0bea86..86b5dc0e3 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -115,12 +115,12 @@ func (v *VarsLoader) mergeVars(varsCtx *VarsCtx, newVars *uo.UnstructuredObject, } func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, ignoreMissing bool, searchDirs []string, rootKey string) error { - if ignoreMissing && !utils.Exists(path) { - return nil - } - rendered, err := varsCtx.RenderFile(path, searchDirs) if err != nil { + // TODO the Jinja2 renderer should be able to better report this error + if ignoreMissing && err.Error() == fmt.Sprintf("template %s not found", path) { + return nil + } return fmt.Errorf("failed to render vars file %s: %w", path, err) } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index a4a99b4e6..d89772654 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -80,6 +80,18 @@ func TestVarsLoader_File(t *testing.T) { assert.Equal(t, int64(42), v) }) + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + b := true + err := vl.LoadVars(vc, &types.VarsSource{ + IgnoreMissing: &b, + File: utils.StrPtr("test.yaml"), + }, []string{d}, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { b := true err := vl.LoadVars(vc, &types.VarsSource{ From 49eff01307db791789572386f26a727deed346fc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 23 Dec 2022 11:55:29 +0100 Subject: [PATCH 0695/2268] fix: Switch to aws-sdk-go-v2 for AWSSecretsManager vars source --- go.mod | 17 +++++++++++-- go.sum | 33 ++++++++++++++++++++++++-- pkg/vars/aws/clientfactory.go | 38 +++++++++++++----------------- pkg/vars/aws/fake_clientfactory.go | 21 ++++++++++------- pkg/vars/aws/secrets_manager.go | 7 +++--- pkg/vars/vars_loader.go | 9 ++++--- 6 files changed, 83 insertions(+), 42 deletions(-) diff --git a/go.mod b/go.mod index 32b762214..44a1dec60 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/aws/aws-sdk-go v1.44.163 github.com/bitnami-labs/sealed-secrets v0.19.3 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible @@ -53,12 +52,13 @@ require ( k8s.io/apimachinery v0.26.0 k8s.io/client-go v0.26.0 k8s.io/klog/v2 v2.80.1 - sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) require ( + github.com/aws/aws-sdk-go-v2/config v1.18.7 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 @@ -94,6 +94,18 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/aws/aws-sdk-go v1.43.43 // indirect + github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 // indirect + github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect @@ -247,5 +259,6 @@ require ( k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect oras.land/oras-go v1.2.1 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index bbdda4737..15b7d656e 100644 --- a/go.sum +++ b/go.sum @@ -130,8 +130,34 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.163 h1:XO1A/Laqf/l0IxVPghaQzdnVwxofVFv00IlX0BpmbhQ= -github.com/aws/aws-sdk-go v1.44.163/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.43.43 h1:1L06qzQvl4aC3Skfh5rV7xVhGHjIZoHcqy16NoyQ1o4= +github.com/aws/aws-sdk-go v1.43.43/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY= +github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.7 h1:V94lTcix6jouwmAsgQMAEBozVAGJMFhVj+6/++xfe3E= +github.com/aws/aws-sdk-go-v2/config v1.18.7/go.mod h1:OZYsyHFL5PB9UpyS78NElgKs11qI/B5KJau2XOJDXHA= +github.com/aws/aws-sdk-go-v2/credentials v1.13.7 h1:qUUcNS5Z1092XBFT66IJM7mYkMwgZ8fcC8YDIbEwXck= +github.com/aws/aws-sdk-go-v2/credentials v1.13.7/go.mod h1:AdCcbZXHQCjJh6NaH3pFaw8LUeBFn5+88BZGMVGuBT8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 h1:77V7vnw/NC4DORHVgA97+Ky2p1ri0+ZVYXh6ordUZU0= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 h1:gItLq3zBYyRDPmqAClgzTH8PBjDQGeyptYGHIwtYYNA= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.28/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 h1:KCacyVSs/wlcPGx37hcbT3IGYO8P8Jx+TgSDhAXtQMY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8= +github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 h1:9Mtq1KM6nD8/+HStvWcvYnixJ5N85DX+P+OY3kI3W2k= +github.com/aws/aws-sdk-go-v2/service/sts v1.17.7/go.mod h1:+lGbb3+1ugwKrNTWcf2RT05Xmp543B06zDFTwiTLp7I= +github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= 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= @@ -368,6 +394,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/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.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-containerregistry v0.12.1 h1:W1mzdNUTx4Zla4JaixCRLhORcR7G6KxE5hHl5fkPsp8= @@ -944,6 +971,7 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd 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.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= @@ -1036,6 +1064,7 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/vars/aws/clientfactory.go b/pkg/vars/aws/clientfactory.go index db7e43d0e..b0542058c 100644 --- a/pkg/vars/aws/clientfactory.go +++ b/pkg/vars/aws/clientfactory.go @@ -1,42 +1,38 @@ package aws import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/secretsmanager" - "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" - "os" + "context" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager" ) +type GetSecretValueInterface interface { + GetSecretValue(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) +} + type AwsClientFactory interface { - SecretsManagerClient(profile *string, region *string) (secretsmanageriface.SecretsManagerAPI, error) + SecretsManagerClient(profile *string, region *string) (GetSecretValueInterface, error) } type awsClientFactory struct { } -func (a *awsClientFactory) getSession(profile *string) (*session.Session, error) { - var opts session.Options - opts.SharedConfigState = session.SharedConfigEnable - // Environment variable always takes precedence - if _, ok := os.LookupEnv("AWS_PROFILE"); !ok && profile != nil { - opts.Profile = *profile - } - s, err := session.NewSessionWithOptions(opts) - if err != nil { - return nil, err +func (a *awsClientFactory) SecretsManagerClient(profile *string, region *string) (GetSecretValueInterface, error) { + var configOpts []func(*config.LoadOptions) error + if profile != nil { + configOpts = append(configOpts, config.WithSharedConfigProfile(*profile)) } - return s, nil -} + if region != nil { + configOpts = append(configOpts, config.WithRegion(*region)) + } -func (a *awsClientFactory) SecretsManagerClient(profile *string, region *string) (secretsmanageriface.SecretsManagerAPI, error) { - s, err := a.getSession(profile) + cfg, err := config.LoadDefaultConfig(context.Background(), configOpts...) if err != nil { return nil, err } - return secretsmanager.New(s, &aws.Config{Region: region}), nil + return secretsmanager.NewFromConfig(cfg), nil } func NewClientFactory() AwsClientFactory { diff --git a/pkg/vars/aws/fake_clientfactory.go b/pkg/vars/aws/fake_clientfactory.go index d06d68fd5..18a170338 100644 --- a/pkg/vars/aws/fake_clientfactory.go +++ b/pkg/vars/aws/fake_clientfactory.go @@ -1,21 +1,21 @@ package aws import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/secretsmanager" - "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" ) type FakeAwsClientFactory struct { - secretsmanageriface.SecretsManagerAPI + GetSecretValueInterface Secrets map[string]string } -func (f *FakeAwsClientFactory) GetSecretValue(in *secretsmanager.GetSecretValueInput) (*secretsmanager.GetSecretValueOutput, error) { - name := *in.SecretId - arn, err := ParseArn(*in.SecretId) +func (f *FakeAwsClientFactory) GetSecretValue(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) { + name := *params.SecretId + arn, err := ParseArn(*params.SecretId) if err == nil { name = arn.Resource } @@ -28,10 +28,13 @@ func (f *FakeAwsClientFactory) GetSecretValue(in *secretsmanager.GetSecretValueI }, nil } - return nil, awserr.New(secretsmanager.ErrCodeResourceNotFoundException, fmt.Sprintf("secret %s not found", *in.SecretId), nil) + errMsg := fmt.Sprintf("secret %s not found", *params.SecretId) + return nil, &types.ResourceNotFoundException{ + Message: &errMsg, + } } -func (f *FakeAwsClientFactory) SecretsManagerClient(profile *string, region *string) (secretsmanageriface.SecretsManagerAPI, error) { +func (f *FakeAwsClientFactory) SecretsManagerClient(profile *string, region *string) (GetSecretValueInterface, error) { return f, nil } diff --git a/pkg/vars/aws/secrets_manager.go b/pkg/vars/aws/secrets_manager.go index 1a57bd5ae..d3cced231 100644 --- a/pkg/vars/aws/secrets_manager.go +++ b/pkg/vars/aws/secrets_manager.go @@ -1,11 +1,12 @@ package aws import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/service/secretsmanager" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager" ) -func GetAwsSecretsManagerSecret(aws AwsClientFactory, profile *string, region *string, secretName string) (string, error) { +func GetAwsSecretsManagerSecret(ctx context.Context, aws AwsClientFactory, profile *string, region *string, secretName string) (string, error) { if region == nil { arn, err := ParseArn(secretName) if err != nil { @@ -19,7 +20,7 @@ func GetAwsSecretsManagerSecret(aws AwsClientFactory, profile *string, region *s return "", fmt.Errorf("getting secret %s from AWS secrets manager failed: %w", secretName, err) } - r, err := smClient.GetSecretValue(&secretsmanager.GetSecretValueInput{ + r, err := smClient.GetSecretValue(ctx, &secretsmanager.GetSecretValueInput{ SecretId: &secretName, }) if err != nil { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 86b5dc0e3..9f070c54d 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -5,8 +5,7 @@ import ( "encoding/base64" errors2 "errors" "fmt" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/secretsmanager" + types2 "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/repocache" @@ -207,11 +206,11 @@ func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsS return fmt.Errorf("no AWS client factory provided") } - secret, err := aws.GetAwsSecretsManagerSecret(v.aws, source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) + secret, err := aws.GetAwsSecretsManagerSecret(v.ctx, v.aws, source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) if err != nil { - var aerr awserr.Error + var aerr *types2.ResourceNotFoundException if errors2.As(err, &aerr) { - if ignoreMissing && aerr.Code() == secretsmanager.ErrCodeResourceNotFoundException { + if ignoreMissing { return nil } } From 972f67a7bef777331785c2091265a94cc0e49581 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 23 Dec 2022 14:47:52 +0100 Subject: [PATCH 0696/2268] fix: Use custom SOPS decrytor implementation, inspired by the Flux decryptor This ensures that the CLI and the controller use the same implemntation. It also allows us to upgrade SOPS to the develop branch without breaking the controller's dependencies. --- go.mod | 16 +- go.sum | 22 +- pkg/deployment/shared_context.go | 4 +- pkg/helm/helm_release.go | 5 +- pkg/kluctl_project/load.go | 5 +- pkg/kluctl_project/project.go | 4 +- pkg/kluctl_project/project_load.go | 4 +- pkg/sops/decryptor/decryptor.go | 636 +++++++++++++ pkg/sops/decryptor/decryptor_test.go | 1229 ++++++++++++++++++++++++++ pkg/sops/sops_decryptor.go | 21 - pkg/sops/utils.go | 9 +- pkg/vars/vars_loader.go | 5 +- pkg/vars/vars_loader_test.go | 7 +- 13 files changed, 1910 insertions(+), 57 deletions(-) create mode 100644 pkg/sops/decryptor/decryptor.go create mode 100644 pkg/sops/decryptor/decryptor_test.go delete mode 100644 pkg/sops/sops_decryptor.go diff --git a/go.mod b/go.mod index 44a1dec60..f4b2b9d17 100644 --- a/go.mod +++ b/go.mod @@ -57,27 +57,30 @@ require ( ) require ( + filippo.io/age v1.0.0 github.com/aws/aws-sdk-go-v2/config v1.18.7 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 + github.com/onsi/gomega v1.24.1 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.3 sigs.k8s.io/controller-runtime v0.14.0 + sigs.k8s.io/kustomize/api v0.12.1 + sigs.k8s.io/yaml v1.3.0 ) require ( cloud.google.com/go/compute v1.14.0 // indirect cloud.google.com/go/compute/metadata v0.2.2 // indirect - filippo.io/age v1.0.0 // indirect - github.com/Azure/azure-sdk-for-go v67.1.0+incompatible // indirect + github.com/Azure/azure-sdk-for-go v63.3.0+incompatible // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.28 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect + github.com/Azure/go-autorest/autorest v0.11.27 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect @@ -251,6 +254,7 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect + gotest.tools/v3 v3.2.0 // indirect k8s.io/apiserver v0.26.0 // indirect k8s.io/cli-runtime v0.26.0 // indirect k8s.io/component-base v0.26.0 // indirect @@ -259,6 +263,4 @@ require ( k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect oras.land/oras-go v1.2.1 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.12.1 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 15b7d656e..af3d44e15 100644 --- a/go.sum +++ b/go.sum @@ -48,23 +48,22 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc= filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8= -github.com/Azure/azure-sdk-for-go v67.1.0+incompatible h1:oziYcaopbnIKfM69DL05wXdypiqfrUKdxUKrKpynJTw= -github.com/Azure/azure-sdk-for-go v67.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v63.3.0+incompatible h1:INepVujzUrmArRZjDLHbtER+FkvCoEwyRCXGqOlmDII= +github.com/Azure/azure-sdk-for-go v63.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= +github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= +github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= -github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= +github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= +github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= @@ -674,6 +673,7 @@ github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DV github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= +github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= @@ -787,6 +787,7 @@ github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 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.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= @@ -886,7 +887,6 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= @@ -1315,8 +1315,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C 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 v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I= +gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= helm.sh/helm/v3 v3.10.3 h1:wL7IUZ7Zyukm5Kz0OUmIFZgKHuAgByCrUcJBtY0kDyw= helm.sh/helm/v3 v3.10.3/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index dec1c4571..859f194ae 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -5,7 +5,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/repocache" - "github.com/kluctl/kluctl/v2/pkg/sops" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "github.com/kluctl/kluctl/v2/pkg/vars" ) @@ -14,7 +14,7 @@ type SharedContext struct { K *k8s.K8sCluster K8sVersion string RP *repocache.GitRepoCache - SopsDecrypter sops.SopsDecrypter + SopsDecrypter *decryptor.Decryptor VarsLoader *vars.VarsLoader HelmCredentials helm.HelmCredentialsProvider diff --git a/pkg/helm/helm_release.go b/pkg/helm/helm_release.go index c4d8963e4..efdaba29a 100644 --- a/pkg/helm/helm_release.go +++ b/pkg/helm/helm_release.go @@ -7,6 +7,7 @@ import ( securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/sops" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -97,7 +98,7 @@ func (hr *Release) GetFullOutputPath() (string, error) { return securejoin.SecureJoin(dir, hr.GetOutputPath()) } -func (hr *Release) Render(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { +func (hr *Release) Render(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter *decryptor.Decryptor) error { err := hr.doRender(ctx, k, k8sVersion, sopsDecrypter) if err != nil { return fmt.Errorf("rendering helm chart %s for release %s has failed: %w", hr.Chart.GetChartName(), hr.Config.ReleaseName, err) @@ -153,7 +154,7 @@ func (hr *Release) getPulledChart(ctx context.Context) (*PulledChart, error) { return pc, nil } -func (hr *Release) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter sops.SopsDecrypter) error { +func (hr *Release) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion string, sopsDecrypter *decryptor.Decryptor) error { pc, err := hr.getPulledChart(ctx) if err != nil { return err diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index d477429b8..f320482ba 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -3,7 +3,7 @@ package kluctl_project import ( "context" "github.com/kluctl/go-jinja2" - "github.com/kluctl/kluctl/v2/pkg/sops" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "github.com/kluctl/kluctl/v2/pkg/status" ) @@ -21,7 +21,8 @@ func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir s } if p.SopsDecrypter == nil { - p.SopsDecrypter = &sops.LocalSopsDecrypter{} + p.SopsDecrypter = decryptor.NewDecryptor(args.ProjectDir, decryptor.MaxEncryptedFileSize) + p.SopsDecrypter.AddLocalKeyService() } err := p.loadKluctlProject() diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 0df49e01c..cc3eb603a 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/repocache" - "github.com/kluctl/kluctl/v2/pkg/sops" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" types2 "github.com/kluctl/kluctl/v2/pkg/types" ) @@ -26,7 +26,7 @@ type LoadedKluctlProject struct { J2 *jinja2.Jinja2 RP *repocache.GitRepoCache - SopsDecrypter sops.SopsDecrypter + SopsDecrypter *decryptor.Decryptor } func (c *LoadedKluctlProject) FindBaseTarget(name string) (*types2.Target, error) { diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 4c075fd23..c397e5190 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -2,7 +2,7 @@ package kluctl_project import ( "github.com/kluctl/kluctl/v2/pkg/repocache" - "github.com/kluctl/kluctl/v2/pkg/sops" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -16,7 +16,7 @@ type LoadKluctlProjectArgs struct { ProjectDir string ProjectConfig string - SopsDecrypter sops.SopsDecrypter + SopsDecrypter *decryptor.Decryptor RP *repocache.GitRepoCache ClientConfigGetter func(context *string) (*rest.Config, *api.Config, error) diff --git a/pkg/sops/decryptor/decryptor.go b/pkg/sops/decryptor/decryptor.go new file mode 100644 index 000000000..84d65567b --- /dev/null +++ b/pkg/sops/decryptor/decryptor.go @@ -0,0 +1,636 @@ +/* +Copyright 2020 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package decryptor + +import ( + "bytes" + "encoding/base64" + "errors" + "fmt" + "go.mozilla.org/sops/v3/age" + "go.mozilla.org/sops/v3/keys" + "go.mozilla.org/sops/v3/pgp" + "io/fs" + "os" + "path/filepath" + "sort" + "strings" + "time" + + securejoin "github.com/cyphar/filepath-securejoin" + "go.mozilla.org/sops/v3" + "go.mozilla.org/sops/v3/aes" + "go.mozilla.org/sops/v3/cmd/sops/common" + "go.mozilla.org/sops/v3/cmd/sops/formats" + "go.mozilla.org/sops/v3/keyservice" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/resource" + kustypes "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +const ( + // MaxEncryptedFileSize is the max allowed file size in bytes of an encrypted + // file. + MaxEncryptedFileSize int64 = 5 << 20 + // unsupportedFormat is used to signal no sopsFormatToMarkerBytes format was + // detected by detectFormatFromMarkerBytes. + unsupportedFormat = formats.Format(-1) +) + +var ( + // sopsFormatToString is the counterpart to + // https://github.com/mozilla/sops/blob/v3.7.2/cmd/sops/formats/formats.go#L16 + sopsFormatToString = map[formats.Format]string{ + formats.Binary: "binary", + formats.Dotenv: "dotenv", + formats.Ini: "INI", + formats.Json: "JSON", + formats.Yaml: "YAML", + } + // sopsFormatToMarkerBytes contains a list of formats and their byte + // order markers, used to detect if a Secret data field is SOPS' encrypted. + sopsFormatToMarkerBytes = map[formats.Format][]byte{ + // formats.Binary is a JSON envelop at encrypted rest + formats.Binary: []byte("\"mac\": \"ENC["), + formats.Dotenv: []byte("sops_mac=ENC["), + formats.Ini: []byte("[sops]"), + formats.Json: []byte("\"mac\": \"ENC["), + formats.Yaml: []byte("mac: ENC["), + } +) + +// Decryptor performs decryption operations for a +// kluctlv1.KluctlDeployment. +// The only supported decryption provider at present is +// DecryptionProviderSOPS. +type Decryptor struct { + // root is the root for file system operations. Any (relative) path or + // symlink is not allowed to traverse outside this path. + root string + // maxFileSize is the max size in bytes a file is allowed to have to be + // decrypted. Defaults to MaxEncryptedFileSize. + maxFileSize int64 + // checkSopsMac instructs the decryptor to perform the SOPS data integrity + // check using the MAC. Not enabled by default, as arbitrary data gets + // injected into most resources, causing the integrity check to fail. + // Mostly kept around for feature completeness and documentation purposes. + checkSopsMac bool + + // keyServices are the SOPS keyservice.KeyServiceClient's available to the + // decryptor. + keyServices []keyservice.KeyServiceClient +} + +// NewDecryptor creates a new Decryptor for the given kluctlDeployment. +// gnuPGHome can be empty, in which case the systems' keyring is used. +func NewDecryptor(root string, maxFileSize int64) *Decryptor { + return &Decryptor{ + root: root, + maxFileSize: maxFileSize, + } +} + +func (d *Decryptor) AddLocalKeyService() { + d.AddKeyServiceClient(keyservice.NewLocalClient()) +} + +func (d *Decryptor) AddKeyServiceClient(s keyservice.KeyServiceClient) { + d.keyServices = append(d.keyServices, s) +} + +// IsEncryptedSecret checks if the given object is a Kubernetes Secret encrypted +// with Mozilla SOPS. +func IsEncryptedSecret(object *unstructured.Unstructured) bool { + if object.GetKind() == "Secret" && object.GetAPIVersion() == "v1" { + if _, found, _ := unstructured.NestedFieldNoCopy(object.Object, "sops"); found { + return true + } + } + return false +} + +// IsOfflineMethod returns true for offline decrypt methods or false otherwise +func IsOfflineMethod(mk keys.MasterKey) bool { + switch mk.(type) { + case *pgp.MasterKey, *age.MasterKey: + return true + default: + return false + } +} + +// SopsDecryptWithFormat attempts to load a SOPS encrypted file using the store +// for the input format, gathers the data key for it from the key service, +// and then decrypts the file data with the retrieved data key. +// It returns the decrypted bytes in the provided output format, or an error. +func (d *Decryptor) SopsDecryptWithFormat(data []byte, inputFormat, outputFormat formats.Format) (_ []byte, err error) { + defer func() { + // It was discovered that malicious input and/or output instructions can + // make SOPS panic. Recover from this panic and return as an error. + if r := recover(); r != nil { + err = fmt.Errorf("failed to emit encrypted %s file as decrypted %s: %v", + sopsFormatToString[inputFormat], sopsFormatToString[outputFormat], r) + } + }() + + store := common.StoreForFormat(inputFormat) + + tree, err := store.LoadEncryptedFile(data) + if err != nil { + return nil, sopsUserErr(fmt.Sprintf("failed to load encrypted %s data", sopsFormatToString[inputFormat]), err) + } + + for _, group := range tree.Metadata.KeyGroups { + // Sort MasterKeys in the group so offline ones are tried first + sort.SliceStable(group, func(i, j int) bool { + return IsOfflineMethod(group[i]) && !IsOfflineMethod(group[j]) + }) + } + + metadataKey, err := tree.Metadata.GetDataKeyWithKeyServices(d.keyServices) + if err != nil { + return nil, sopsUserErr("cannot get sops data key", err) + } + + cipher := aes.NewCipher() + mac, err := tree.Decrypt(metadataKey, cipher) + if err != nil { + return nil, sopsUserErr("error decrypting sops tree", err) + } + + if d.checkSopsMac { + // Compute the hash of the cleartext tree and compare it with + // the one that was stored in the document. If they match, + // integrity was preserved + // Ref: go.mozilla.org/sops/v3/decrypt/decrypt.go + originalMac, err := cipher.Decrypt( + tree.Metadata.MessageAuthenticationCode, + metadataKey, + tree.Metadata.LastModified.Format(time.RFC3339), + ) + if err != nil { + return nil, sopsUserErr("failed to verify sops data integrity", err) + } + if originalMac != mac { + // If the file has an empty MAC, display "no MAC" + if originalMac == "" { + originalMac = "no MAC" + } + return nil, fmt.Errorf("failed to verify sops data integrity: expected mac '%s', got '%s'", originalMac, mac) + } + } + + outputStore := common.StoreForFormat(outputFormat) + out, err := outputStore.EmitPlainFile(tree.Branches) + if err != nil { + return nil, sopsUserErr(fmt.Sprintf("failed to emit encrypted %s file as decrypted %s", + sopsFormatToString[inputFormat], sopsFormatToString[outputFormat]), err) + } + return out, err +} + +// DecryptResource attempts to decrypt the provided resource overwriting the resource +// with the decrypted data. +// It has special support for Kubernetes Secrets with encrypted data entries +// while decrypting with DecryptionProviderSOPS, to allow individual data entries +// injected by e.g. a Kustomize secret generator to be decrypted +func (d *Decryptor) DecryptResource(res *resource.Resource) (*resource.Resource, error) { + if res == nil { + return nil, nil + } + + switch { + case isSOPSEncryptedResource(res): + // As we are expecting to decrypt right before applying, we do not + // care about keeping any other data (e.g. comments) around. + // We can therefore simply work with JSON, which saves us from e.g. + // JSON -> YAML -> JSON transformations. + out, err := res.MarshalJSON() + if err != nil { + return nil, err + } + + data, err := d.SopsDecryptWithFormat(out, formats.Json, formats.Json) + if err != nil { + return nil, fmt.Errorf("failed to decrypt and format '%s/%s' %s data: %w", + res.GetNamespace(), res.GetName(), res.GetKind(), err) + } + + err = res.UnmarshalJSON(data) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal decrypted '%s/%s' %s to JSON: %w", + res.GetNamespace(), res.GetName(), res.GetKind(), err) + } + return res, nil + case res.GetKind() == "Secret": + dataMap := res.GetDataMap() + for key, value := range dataMap { + data, err := base64.StdEncoding.DecodeString(value) + if err != nil { + // If we fail to base64 decode, it is (very) likely to be a + // user input error. Instead of failing here, let it bubble + // up during the actual build. + continue + } + + if inF := detectFormatFromMarkerBytes(data); inF != unsupportedFormat { + outF := formatForPath(key) + out, err := d.SopsDecryptWithFormat(data, inF, outF) + if err != nil { + return nil, fmt.Errorf("failed to decrypt and format '%s/%s' Secret field '%s': %w", + res.GetNamespace(), res.GetName(), key, err) + } + dataMap[key] = base64.StdEncoding.EncodeToString(out) + } + } + res.SetDataMap(dataMap) + return res, nil + } + return nil, nil +} + +// DecryptEnvSources attempts to decrypt all types.SecretArgs FileSources and +// EnvSources a Kustomization file in the directory at the provided path refers +// to, before walking recursively over all other resources it refers to. +// It ignores resource references which refer to absolute or relative paths +// outside the working directory of the decryptor, but returns any decryption +// error. +func (d *Decryptor) DecryptEnvSources(path string) error { + decrypted, visited := make(map[string]struct{}, 0), make(map[string]struct{}, 0) + visit := d.decryptKustomizationEnvSources(decrypted) + return recurseKustomizationFiles(d.root, path, visit, visited) +} + +// decryptKustomizationEnvSources returns a visitKustomization implementation +// which attempts to decrypt any EnvSources entry it finds in the Kustomization +// file with which it is called. +// After decrypting successfully, it adds the absolute path of the file to the +// given map. +func (d *Decryptor) decryptKustomizationEnvSources(visited map[string]struct{}) visitKustomization { + return func(root, path string, kus *kustypes.Kustomization) error { + visitRef := func(sourcePath string, format formats.Format) error { + if !filepath.IsAbs(sourcePath) { + sourcePath = filepath.Join(path, sourcePath) + } + absRef, _, err := securePaths(root, sourcePath) + if err != nil { + return err + } + if _, ok := visited[absRef]; ok { + return nil + } + + if err := d.sopsDecryptFile(absRef, format, format); err != nil { + return securePathErr(root, err) + } + + // Explicitly set _after_ the decryption operation, this makes + // visited work as a list of actually decrypted files + visited[absRef] = struct{}{} + return nil + } + + for _, gen := range kus.SecretGenerator { + for _, fileSrc := range gen.FileSources { + parts := strings.SplitN(fileSrc, "=", 2) + key := parts[0] + var filePath string + if len(parts) > 1 { + filePath = parts[1] + } else { + filePath = key + } + if err := visitRef(filePath, formatForPath(key)); err != nil { + return err + } + } + for _, envFile := range gen.EnvSources { + format := formatForPath(envFile) + if format == formats.Binary { + // Default to dotenv + format = formats.Dotenv + } + if err := visitRef(envFile, format); err != nil { + return err + } + } + } + return nil + } +} + +// sopsDecryptFile attempts to decrypt the file at the given path using SOPS' +// store for the provided input format, and writes it back to the path using +// the store for the output format. +// Path must be absolute and a regular file, the file is not allowed to exceed +// the maxFileSize. +// +// NB: The method only does the simple checks described above and does not +// verify whether the path provided is inside the working directory. Boundary +// enforcement is expected to have been done by the caller. +func (d *Decryptor) sopsDecryptFile(path string, inputFormat, outputFormat formats.Format) error { + fi, err := os.Lstat(path) + if err != nil { + return err + } + + if !fi.Mode().IsRegular() { + return fmt.Errorf("cannot decrypt irregular file as it has file mode type bits set") + } + if fileSize := fi.Size(); d.maxFileSize > 0 && fileSize > d.maxFileSize { + return fmt.Errorf("cannot decrypt file with size (%d bytes) exceeding limit (%d)", fileSize, d.maxFileSize) + } + + data, err := os.ReadFile(path) + if err != nil { + return err + } + + if !bytes.Contains(data, sopsFormatToMarkerBytes[inputFormat]) { + return nil + } + + out, err := d.SopsDecryptWithFormat(data, inputFormat, outputFormat) + if err != nil { + return err + } + err = os.WriteFile(path, out, 0o644) + if err != nil { + return fmt.Errorf("error writing sops decrypted %s data to %s file: %w", + sopsFormatToString[inputFormat], sopsFormatToString[outputFormat], err) + } + return nil +} + +// sopsEncryptWithFormat attempts to load a plain file using the store +// for the input format, gathers the data key for it from the key service, +// and then encrypt the file data with the retrieved data key. +// It returns the encrypted bytes in the provided output format, or an error. +func (d *Decryptor) sopsEncryptWithFormat(metadata sops.Metadata, data []byte, inputFormat, outputFormat formats.Format) ([]byte, error) { + store := common.StoreForFormat(inputFormat) + + branches, err := store.LoadPlainFile(data) + if err != nil { + return nil, err + } + + tree := sops.Tree{ + Branches: branches, + Metadata: metadata, + } + dataKey, errs := tree.GenerateDataKeyWithKeyServices(d.keyServices) + if len(errs) > 0 { + return nil, sopsUserErr("could not generate data key", fmt.Errorf("%s", errs)) + } + + cipher := aes.NewCipher() + unencryptedMac, err := tree.Encrypt(dataKey, cipher) + if err != nil { + return nil, sopsUserErr("error encrypting sops tree", err) + } + tree.Metadata.LastModified = time.Now().UTC() + tree.Metadata.MessageAuthenticationCode, err = cipher.Encrypt(unencryptedMac, dataKey, tree.Metadata.LastModified.Format(time.RFC3339)) + if err != nil { + return nil, sopsUserErr("cannot encrypt sops data tree", err) + } + + outStore := common.StoreForFormat(outputFormat) + out, err := outStore.EmitEncryptedFile(tree) + if err != nil { + return nil, sopsUserErr("failed to emit sops encrypted file", err) + } + return out, nil +} + +// secureLoadKustomizationFile tries to securely load a Kustomization file from +// the given directory path. +// If multiple Kustomization files are found, or the request is ambiguous, an +// error is returned. +func secureLoadKustomizationFile(root, path string) (*kustypes.Kustomization, error) { + if !filepath.IsAbs(root) { + return nil, fmt.Errorf("root '%s' must be absolute", root) + } + if filepath.IsAbs(path) { + return nil, fmt.Errorf("path '%s' must be relative", path) + } + + var loadPath string + for _, fName := range konfig.RecognizedKustomizationFileNames() { + fPath, err := securejoin.SecureJoin(root, filepath.Join(path, fName)) + if err != nil { + return nil, fmt.Errorf("failed to secure join %s: %w", fName, err) + } + fi, err := os.Lstat(fPath) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + continue + } + return nil, fmt.Errorf("failed to lstat %s: %w", fName, securePathErr(root, err)) + } + + if !fi.Mode().IsRegular() { + return nil, fmt.Errorf("expected %s to be a regular file", fName) + } + if loadPath != "" { + return nil, fmt.Errorf("found multiple kustomization files") + } + loadPath = fPath + } + if loadPath == "" { + return nil, fmt.Errorf("no kustomization file found") + } + + data, err := os.ReadFile(loadPath) + if err != nil { + return nil, fmt.Errorf("failed to read kustomization file: %w", securePathErr(root, err)) + } + + kus := kustypes.Kustomization{ + TypeMeta: kustypes.TypeMeta{ + APIVersion: kustypes.KustomizationVersion, + Kind: kustypes.KustomizationKind, + }, + } + if err := yaml.Unmarshal(data, &kus); err != nil { + return nil, fmt.Errorf("failed to unmarshal kustomization file from '%s': %w", loadPath, err) + } + return &kus, nil +} + +// visitKustomization is called by recurseKustomizationFiles after every +// successful Kustomization file load. +type visitKustomization func(root, path string, kus *kustypes.Kustomization) error + +// errRecurseIgnore is a wrapping error to signal to recurseKustomizationFiles +// the error can be ignored during recursion. For example, because the +// Kustomization file can not be loaded for a subsequent call. +type errRecurseIgnore struct { + Err error +} + +// Unwrap returns the actual underlying error. +func (e *errRecurseIgnore) Unwrap() error { + return e.Err +} + +// Error returns the error string of the underlying error. +func (e *errRecurseIgnore) Error() string { + if err := e.Err; err != nil { + return e.Err.Error() + } + return "recurse ignore" +} + +// recurseKustomizationFiles attempts to recursively load and visit +// Kustomization files. +// The provided path is allowed to be relative, in which case it is safely +// joined with root. When absolute, it must be inside root. +func recurseKustomizationFiles(root, path string, visit visitKustomization, visited map[string]struct{}) error { + // Resolve the secure paths + absPath, relPath, err := securePaths(root, path) + if err != nil { + return err + } + + if _, ok := visited[absPath]; ok { + // Short-circuit + return nil + } + visited[absPath] = struct{}{} + + // Confirm we are dealing with a directory + fi, err := os.Lstat(absPath) + if err != nil { + err = securePathErr(root, err) + if errors.Is(err, fs.ErrNotExist) { + err = &errRecurseIgnore{Err: err} + } + return err + } + if !fi.IsDir() { + return &errRecurseIgnore{Err: fmt.Errorf("not a directory")} + } + + // Attempt to load the Kustomization file from the directory + kus, err := secureLoadKustomizationFile(root, relPath) + if err != nil { + return err + } + + // Visit the Kustomization + if err = visit(root, path, kus); err != nil { + return err + } + + // Recurse over other resources in Kustomization, + // repeating the above logic per item + for _, res := range kus.Resources { + if !filepath.IsAbs(res) { + res = filepath.Join(path, res) + } + if err = recurseKustomizationFiles(root, res, visit, visited); err != nil { + // When the resource does not exist at the compiled path, it's + // either an invalid reference, or a URL. + // If the reference is valid but does not point to a directory, + // we have run into a dead end as well. + // In all other cases, the error is of (possible) importance to + // the user, and we should return it. + if _, ok := err.(*errRecurseIgnore); !ok { + return err + } + } + } + return nil +} + +// isSOPSEncryptedResource detects if the given resource is a SOPS' encrypted +// resource by looking for ".sops" and ".sops.mac" fields. +func isSOPSEncryptedResource(res *resource.Resource) bool { + if res == nil { + return false + } + sopsField := res.Field("sops") + if sopsField.IsNilOrEmpty() { + return false + } + macField := sopsField.Value.Field("mac") + return !macField.IsNilOrEmpty() +} + +// securePaths returns the absolute and relative paths for the provided path, +// guaranteed to be scoped inside the provided root. +// When the given path is absolute, the root is stripped before secure joining +// it on root. +func securePaths(root, path string) (string, string, error) { + if filepath.IsAbs(path) { + path = stripRoot(root, path) + } + secureAbsPath, err := securejoin.SecureJoin(root, path) + if err != nil { + return "", "", err + } + return secureAbsPath, stripRoot(root, secureAbsPath), nil +} + +func stripRoot(root, path string) string { + sepStr := string(filepath.Separator) + root, path = filepath.Clean(sepStr+root), filepath.Clean(sepStr+path) + switch { + case path == root: + path = sepStr + case root == sepStr: + // noop + case strings.HasPrefix(path, root+sepStr): + path = strings.TrimPrefix(path, root+sepStr) + } + return filepath.Clean(filepath.Join("."+sepStr, path)) +} + +func sopsUserErr(msg string, err error) error { + if userErr, ok := err.(sops.UserError); ok { + err = fmt.Errorf(userErr.UserError()) + } + return fmt.Errorf("%s: %w", msg, err) +} + +func securePathErr(root string, err error) error { + if pathErr := new(fs.PathError); errors.As(err, &pathErr) { + err = &fs.PathError{Op: pathErr.Op, Path: stripRoot(root, pathErr.Path), Err: pathErr.Err} + } + return err +} + +func formatForPath(path string) formats.Format { + switch { + case strings.HasSuffix(path, corev1.DockerConfigJsonKey): + return formats.Json + default: + return formats.FormatForPath(path) + } +} + +func detectFormatFromMarkerBytes(b []byte) formats.Format { + for k, v := range sopsFormatToMarkerBytes { + if bytes.Contains(b, v) { + return k + } + } + return unsupportedFormat +} diff --git a/pkg/sops/decryptor/decryptor_test.go b/pkg/sops/decryptor/decryptor_test.go new file mode 100644 index 000000000..9f3c642ec --- /dev/null +++ b/pkg/sops/decryptor/decryptor_test.go @@ -0,0 +1,1229 @@ +/* +Copyright 2021 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package decryptor + +import ( + "bytes" + "encoding/base64" + extage "filippo.io/age" + "fmt" + . "github.com/onsi/gomega" + gt "github.com/onsi/gomega/types" + "go.mozilla.org/sops/v3" + sopsage "go.mozilla.org/sops/v3/age" + "go.mozilla.org/sops/v3/cmd/sops/formats" + "io/fs" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "os" + "path/filepath" + "regexp" + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resource" + kustypes "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" + "strings" + "testing" +) + +func TestIsEncryptedSecret(t *testing.T) { + tests := []struct { + name string + object []byte + want gt.GomegaMatcher + }{ + {name: "encrypted secret", object: []byte("apiVersion: v1\nkind: Secret\nsops: true\n"), want: BeTrue()}, + {name: "decrypted secret", object: []byte("apiVersion: v1\nkind: Secret\n"), want: BeFalse()}, + {name: "other resource", object: []byte("apiVersion: v1\nkind: Deployment\n"), want: BeFalse()}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + u := &unstructured.Unstructured{} + g.Expect(yaml.Unmarshal(tt.object, u)).To(Succeed()) + g.Expect(IsEncryptedSecret(u)).To(tt.want) + }) + } +} + +func TestDecryptor_SopsDecryptWithFormat(t *testing.T) { + t.Run("decrypt INI to INI", func(t *testing.T) { + g := NewWithT(t) + + ageID, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + + t.Setenv(sopsage.SopsAgeKeyEnv, ageID.String()) + + kd := &Decryptor{ + checkSopsMac: true, + } + kd.AddLocalKeyService() + + format := formats.Ini + data := []byte("[config]\nkey = value\n") + encData, err := kd.sopsEncryptWithFormat(sops.Metadata{ + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: ageID.Recipient().String()}}, + }, + }, data, format, format) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(bytes.Contains(encData, sopsFormatToMarkerBytes[format])).To(BeTrue()) + g.Expect(encData).ToNot(Equal(data)) + + out, err := kd.SopsDecryptWithFormat(encData, format, format) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(out).To(Equal(data)) + }) + + t.Run("decrypt JSON to YAML", func(t *testing.T) { + g := NewWithT(t) + + ageID, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + + t.Setenv(sopsage.SopsAgeKeyEnv, ageID.String()) + + kd := &Decryptor{ + checkSopsMac: true, + } + kd.AddLocalKeyService() + + inputFormat, outputFormat := formats.Json, formats.Yaml + data := []byte("{\"key\": \"value\"}\n") + encData, err := kd.sopsEncryptWithFormat(sops.Metadata{ + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: ageID.Recipient().String()}}, + }, + }, data, inputFormat, inputFormat) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(bytes.Contains(encData, sopsFormatToMarkerBytes[inputFormat])).To(BeTrue()) + + out, err := kd.SopsDecryptWithFormat(encData, inputFormat, outputFormat) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(out).To(Equal([]byte("key: value\n"))) + }) + + t.Run("invalid JSON data", func(t *testing.T) { + g := NewWithT(t) + + format := formats.Json + data, err := (&Decryptor{}).SopsDecryptWithFormat([]byte("invalid json"), format, format) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to load encrypted JSON data")) + g.Expect(data).To(BeNil()) + }) + + t.Run("no data key", func(t *testing.T) { + g := NewWithT(t) + + ageID, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + + kd := &Decryptor{} + kd.AddLocalKeyService() + + format := formats.Binary + encData, err := kd.sopsEncryptWithFormat(sops.Metadata{ + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: ageID.Recipient().String()}}, + }, + }, []byte("foo bar"), format, format) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(bytes.Contains(encData, sopsFormatToMarkerBytes[format])).To(BeTrue()) + + data, err := kd.SopsDecryptWithFormat(encData, format, format) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("cannot get sops data key")) + g.Expect(data).To(BeNil()) + }) + + t.Run("with mac check", func(t *testing.T) { + g := NewWithT(t) + + ageID, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + + t.Setenv(sopsage.SopsAgeKeyEnv, ageID.String()) + + kd := &Decryptor{ + checkSopsMac: true, + } + kd.AddLocalKeyService() + + format := formats.Dotenv + data := []byte("key=value\n") + encData, err := kd.sopsEncryptWithFormat(sops.Metadata{ + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: ageID.Recipient().String()}}, + }, + }, data, format, format) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(bytes.Contains(encData, sopsFormatToMarkerBytes[format])).To(BeTrue()) + + out, err := kd.SopsDecryptWithFormat(encData, format, format) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(out).To(Equal(data)) + + badMAC := regexp.MustCompile("(?m)[\r\n]+^.*sops_mac=.*$") + badMACData := badMAC.ReplaceAll(encData, []byte("\nsops_mac=\n")) + out, err = kd.SopsDecryptWithFormat(badMACData, format, format) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to verify sops data integrity: expected mac 'no MAC'")) + g.Expect(out).To(BeNil()) + }) +} + +func TestDecryptor_DecryptResource(t *testing.T) { + var ( + resourceFactory = provider.NewDefaultDepProvider().GetResourceFactory() + emptyResource = resourceFactory.FromMap(map[string]interface{}{}) + ) + + newSecretResource := func(namespace, name string, data map[string]interface{}) *resource.Resource { + return resourceFactory.FromMap(map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": name, + "namespace": namespace, + }, + "data": data, + }) + } + + t.Run("SOPS-encrypted Secret resource", func(t *testing.T) { + g := NewWithT(t) + + d := NewDecryptor("", MaxEncryptedFileSize) + d.AddLocalKeyService() + + ageID, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + t.Setenv(sopsage.SopsAgeKeyEnv, ageID.String()) + + secret := newSecretResource("test", "secret", map[string]interface{}{ + "key": "value", + }) + g.Expect(isSOPSEncryptedResource(secret)).To(BeFalse()) + + secretData, err := secret.MarshalJSON() + g.Expect(err).ToNot(HaveOccurred()) + + encData, err := d.sopsEncryptWithFormat(sops.Metadata{ + EncryptedRegex: "^(data|stringData)$", + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: ageID.Recipient().String()}}, + }, + }, secretData, formats.Json, formats.Json) + g.Expect(err).ToNot(HaveOccurred()) + + g.Expect(secret.UnmarshalJSON(encData)).To(Succeed()) + g.Expect(isSOPSEncryptedResource(secret)).To(BeTrue()) + + got, err := d.DecryptResource(secret) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.MarshalJSON()).To(Equal(secretData)) + }) + + t.Run("SOPS-encrypted binary-format Secret data field", func(t *testing.T) { + g := NewWithT(t) + + d := NewDecryptor("", MaxEncryptedFileSize) + d.AddLocalKeyService() + + ageID, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + t.Setenv(sopsage.SopsAgeKeyEnv, ageID.String()) + + plainData := []byte("[config]\napp = secret\n") + encData, err := d.sopsEncryptWithFormat(sops.Metadata{ + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: ageID.Recipient().String()}}, + }, + }, plainData, formats.Ini, formats.Yaml) + g.Expect(err).ToNot(HaveOccurred()) + + secret := newSecretResource("test", "secret-data", map[string]interface{}{ + "file.ini": base64.StdEncoding.EncodeToString(encData), + }) + g.Expect(isSOPSEncryptedResource(secret)).To(BeFalse()) + + got, err := d.DecryptResource(secret) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.GetDataMap()).To(HaveKeyWithValue("file.ini", base64.StdEncoding.EncodeToString(plainData))) + }) + + t.Run("SOPS-encrypted YAML-format Secret data field", func(t *testing.T) { + g := NewWithT(t) + + d := NewDecryptor("", MaxEncryptedFileSize) + d.AddLocalKeyService() + + ageID, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + t.Setenv(sopsage.SopsAgeKeyEnv, ageID.String()) + + plainData := []byte("structured:\n data:\n key: value\n") + encData, err := d.sopsEncryptWithFormat(sops.Metadata{ + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: ageID.Recipient().String()}}, + }, + }, plainData, formats.Yaml, formats.Yaml) + g.Expect(err).ToNot(HaveOccurred()) + + secret := newSecretResource("test", "secret-data", map[string]interface{}{ + "key.yaml": base64.StdEncoding.EncodeToString(encData), + }) + g.Expect(isSOPSEncryptedResource(secret)).To(BeFalse()) + + got, err := d.DecryptResource(secret) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.GetDataMap()).To(HaveKeyWithValue("key.yaml", base64.StdEncoding.EncodeToString(plainData))) + }) + + t.Run("SOPS-encrypted Docker config Secret", func(t *testing.T) { + g := NewWithT(t) + + d := NewDecryptor("", MaxEncryptedFileSize) + d.AddLocalKeyService() + + ageID, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + t.Setenv(sopsage.SopsAgeKeyEnv, ageID.String()) + + plainData := []byte(`{ + "auths": { + "my-registry.example:5000": { + "username": "tiger", + "password": "pass1234", + "email": "tiger@acme.example", + "auth": "dGlnZXI6cGFzczEyMzQ=" + } + } +}`) + encData, err := d.sopsEncryptWithFormat(sops.Metadata{ + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: ageID.Recipient().String()}}, + }, + }, plainData, formats.Json, formats.Yaml) + g.Expect(err).ToNot(HaveOccurred()) + + secret := resourceFactory.FromMap(map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": "secret", + "namespace": "test", + }, + "type": corev1.SecretTypeDockerConfigJson, + "data": map[string]interface{}{ + corev1.DockerConfigJsonKey: base64.StdEncoding.EncodeToString(encData), + }, + }) + g.Expect(isSOPSEncryptedResource(secret)).To(BeFalse()) + + got, err := d.DecryptResource(secret) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.GetDataMap()).To(HaveKeyWithValue(corev1.DockerConfigJsonKey, base64.StdEncoding.EncodeToString(plainData))) + }) + + t.Run("nil resource", func(t *testing.T) { + g := NewWithT(t) + + d := NewDecryptor("", MaxEncryptedFileSize) + + got, err := d.DecryptResource(nil) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got).To(BeNil()) + }) + + t.Run("no decryption spec", func(t *testing.T) { + g := NewWithT(t) + + d := NewDecryptor("", MaxEncryptedFileSize) + + got, err := d.DecryptResource(emptyResource.DeepCopy()) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got).To(BeNil()) + }) + + t.Run("unimplemented decryption provider", func(t *testing.T) { + g := NewWithT(t) + + d := NewDecryptor("", MaxEncryptedFileSize) + + got, err := d.DecryptResource(emptyResource.DeepCopy()) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got).To(BeNil()) + }) +} + +func TestDecryptor_decryptKustomizationEnvSources(t *testing.T) { + type file struct { + name string + symlink string + data []byte + originalFormat *formats.Format + encrypt bool + expectData bool + } + binaryFormat := formats.Binary + tests := []struct { + name string + wordirSuffix string + path string + files []file + secretGenerator []kustypes.SecretArgs + expectVisited []string + wantErr error + }{ + { + name: "decrypt env sources", + path: "subdir", + files: []file{ + {name: "subdir/app.env", data: []byte("var1=value1\n"), encrypt: true, expectData: true}, + // NB: Despite the file extension representing the SOPS-encrypted JSON output + // format, the original data is plain text, or "binary." + {name: "subdir/combination.json", data: []byte("The safe combination is ..."), originalFormat: &binaryFormat, encrypt: true, expectData: true}, + {name: "subdir/file.txt", data: []byte("file"), encrypt: true, expectData: true}, + {name: "secret.env", data: []byte("var2=value2\n"), encrypt: true, expectData: true}, + }, + secretGenerator: []kustypes.SecretArgs{ + { + GeneratorArgs: kustypes.GeneratorArgs{ + Name: "envSecret", + KvPairSources: kustypes.KvPairSources{ + FileSources: []string{"file.txt", "combo=combination.json"}, + EnvSources: []string{"app.env", "../secret.env"}, + }, + }, + }, + }, + expectVisited: []string{"subdir/app.env", "subdir/combination.json", "subdir/file.txt", "secret.env"}, + }, + { + name: "decryption error", + files: []file{}, + secretGenerator: []kustypes.SecretArgs{ + { + GeneratorArgs: kustypes.GeneratorArgs{ + Name: "envSecret", + KvPairSources: kustypes.KvPairSources{ + EnvSources: []string{"file.txt"}, + }, + }, + }, + }, + expectVisited: []string{}, + wantErr: &fs.PathError{Op: "lstat", Path: "file.txt", Err: fmt.Errorf("")}, + }, + { + name: "follows relative symlink within root", + path: "subdir", + files: []file{ + {name: "subdir/symlink", symlink: "../otherdir/data.env"}, + {name: "otherdir/data.env", data: []byte("key=value\n"), encrypt: true, expectData: true}, + }, + secretGenerator: []kustypes.SecretArgs{ + { + GeneratorArgs: kustypes.GeneratorArgs{ + Name: "envSecret", + KvPairSources: kustypes.KvPairSources{ + EnvSources: []string{"symlink"}, + }, + }, + }, + }, + expectVisited: []string{"otherdir/data.env"}, + }, + { + name: "error on symlink outside root", + wordirSuffix: "subdir", + path: "./", + files: []file{ + {name: "subdir/symlink", symlink: "../otherdir/data.env"}, + {name: "otherdir/data.env", data: []byte("key=value\n"), encrypt: true, expectData: false}, + }, + secretGenerator: []kustypes.SecretArgs{ + { + GeneratorArgs: kustypes.GeneratorArgs{ + Name: "envSecret", + KvPairSources: kustypes.KvPairSources{ + EnvSources: []string{"symlink"}, + }, + }, + }, + }, + wantErr: &fs.PathError{Op: "lstat", Path: "otherdir/data.env", Err: fmt.Errorf("")}, + expectVisited: []string{}, + }, + { + name: "error on reference outside root", + wordirSuffix: "subdir", + path: "./", + files: []file{ + {name: "data.env", data: []byte("key=value\n"), encrypt: true, expectData: false}, + }, + secretGenerator: []kustypes.SecretArgs{ + { + GeneratorArgs: kustypes.GeneratorArgs{ + Name: "envSecret", + KvPairSources: kustypes.KvPairSources{ + EnvSources: []string{"../data.env"}, + }, + }, + }, + }, + wantErr: &fs.PathError{Op: "lstat", Path: "data.env", Err: fmt.Errorf("")}, + expectVisited: []string{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + tmpDir := t.TempDir() + root := filepath.Join(tmpDir, tt.wordirSuffix) + + id, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + t.Setenv(sopsage.SopsAgeKeyEnv, id.String()) + + d := &Decryptor{ + root: root, + } + d.AddLocalKeyService() + + for _, f := range tt.files { + fPath := filepath.Join(tmpDir, f.name) + g.Expect(os.MkdirAll(filepath.Dir(fPath), 0o700)).To(Succeed()) + if f.symlink != "" { + g.Expect(os.Symlink(f.symlink, fPath)).To(Succeed()) + continue + } + data := f.data + if f.encrypt { + var format formats.Format + if f.originalFormat != nil { + format = *f.originalFormat + } else { + format = formats.FormatForPath(f.name) + } + data, err = d.sopsEncryptWithFormat(sops.Metadata{ + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: id.Recipient().String()}}, + }, + }, f.data, format, format) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(data).ToNot(Equal(f.data)) + } + g.Expect(os.WriteFile(fPath, data, 0o644)).To(Succeed()) + } + + visited := make(map[string]struct{}, 0) + visit := d.decryptKustomizationEnvSources(visited) + kus := &kustypes.Kustomization{SecretGenerator: tt.secretGenerator} + + err = visit(root, tt.path, kus) + if tt.wantErr == nil { + g.Expect(err).ToNot(HaveOccurred()) + } else { + g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(BeAssignableToTypeOf(tt.wantErr)) + g.Expect(err.Error()).To(ContainSubstring(tt.wantErr.Error())) + } + + for _, f := range tt.files { + if f.symlink != "" { + continue + } + + b, err := os.ReadFile(filepath.Join(tmpDir, f.name)) + g.Expect(err).ToNot(HaveOccurred()) + if f.expectData { + g.Expect(b).To(Equal(f.data)) + } else { + g.Expect(b).ToNot(Equal(f.data)) + } + } + + absVisited := make(map[string]struct{}, 0) + for _, v := range tt.expectVisited { + absVisited[filepath.Join(tmpDir, v)] = struct{}{} + } + g.Expect(visited).To(Equal(absVisited)) + }) + } +} + +func TestDecryptor_decryptSopsFile(t *testing.T) { + g := NewWithT(t) + + id, err := extage.GenerateX25519Identity() + g.Expect(err).ToNot(HaveOccurred()) + t.Setenv(sopsage.SopsAgeKeyEnv, id.String()) + + type file struct { + name string + symlink string + data []byte + encrypt bool + format formats.Format + expectData bool + } + tests := []struct { + name string + maxFileSize int64 + files []file + path string + format formats.Format + wantErr error + }{ + { + name: "decrypt dotenv file", + files: []file{ + {name: "app.env", data: []byte("app=key\n"), encrypt: true, format: formats.Dotenv, expectData: true}, + }, + path: "app.env", + format: formats.Dotenv, + }, + { + name: "decrypt YAML file", + files: []file{ + {name: "app.yaml", data: []byte("app: key\n"), encrypt: true, format: formats.Yaml, expectData: true}, + }, + path: "app.yaml", + format: formats.Yaml, + }, + { + name: "irregular file", + files: []file{}, + wantErr: fmt.Errorf("cannot decrypt irregular file as it has file mode type bits set"), + }, + { + name: "file exceeds max size", + maxFileSize: 5, + files: []file{ + {name: "app.env", data: []byte("app=key\n"), encrypt: true, format: formats.Dotenv, expectData: false}, + }, + path: "app.env", + wantErr: fmt.Errorf("cannot decrypt file with size (972 bytes) exceeding limit (5)"), + }, + { + name: "wrong file format", + files: []file{ + {name: "app.ini", data: []byte("[app]\nkey = value"), encrypt: true, format: formats.Ini, expectData: false}, + }, + path: "app.ini", + }, + { + name: "does not follow symlink", + files: []file{ + {name: "link", symlink: "../"}, + }, + path: "link", + wantErr: fmt.Errorf("cannot decrypt irregular file as it has file mode type bits set"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + tmpDir := t.TempDir() + + d := &Decryptor{ + root: tmpDir, + maxFileSize: MaxEncryptedFileSize, + } + d.AddLocalKeyService() + if tt.maxFileSize != 0 { + d.maxFileSize = tt.maxFileSize + } + + for _, f := range tt.files { + fPath := filepath.Join(tmpDir, f.name) + if f.symlink != "" { + g.Expect(os.Symlink(f.symlink, fPath)).To(Succeed()) + continue + } + data := f.data + if f.encrypt { + b, err := d.sopsEncryptWithFormat(sops.Metadata{ + KeyGroups: []sops.KeyGroup{ + {&sopsage.MasterKey{Recipient: id.Recipient().String()}}, + }, + }, data, f.format, f.format) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(b).ToNot(Equal(f.data)) + data = b + } + g.Expect(os.MkdirAll(filepath.Dir(fPath), 0o700)).To(Succeed()) + g.Expect(os.WriteFile(fPath, data, 0o644)).To(Succeed()) + } + + path := filepath.Join(tmpDir, tt.path) + err := d.sopsDecryptFile(path, tt.format, tt.format) + if tt.wantErr != nil { + g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(BeAssignableToTypeOf(tt.wantErr)) + g.Expect(err.Error()).To(ContainSubstring(tt.wantErr.Error())) + } else { + g.Expect(err).ToNot(HaveOccurred()) + } + for _, f := range tt.files { + if f.symlink != "" { + continue + } + + b, err := os.ReadFile(filepath.Join(tmpDir, f.name)) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(bytes.Equal(f.data, b)).To(Equal(f.expectData)) + } + }) + } +} + +func TestDecryptor_secureLoadKustomizationFile(t *testing.T) { + kusType := kustypes.TypeMeta{ + APIVersion: kustypes.KustomizationVersion, + Kind: kustypes.KustomizationKind, + } + type file struct { + name string + symlink string + data []byte + } + tests := []struct { + name string + rootSuffix string + files []file + path string + want *kustypes.Kustomization + wantErr error + }{ + { + name: "loads default kustomization file", + files: []file{ + {name: konfig.DefaultKustomizationFileName(), data: []byte("resources:\n- resource.yaml")}, + }, + path: "./", + want: &kustypes.Kustomization{ + TypeMeta: kusType, + Resources: []string{"resource.yaml"}, + }, + }, + { + name: "loads recognized kustomization file", + files: []file{ + {name: konfig.RecognizedKustomizationFileNames()[1], data: []byte("resources:\n- resource.yaml")}, + }, + path: "./", + want: &kustypes.Kustomization{ + TypeMeta: kusType, + Resources: []string{"resource.yaml"}, + }, + }, + { + name: "error on ambitious file match", + files: []file{ + {name: konfig.RecognizedKustomizationFileNames()[0], data: []byte("resources:\n- resource.yaml")}, + {name: konfig.RecognizedKustomizationFileNames()[1], data: []byte("resources:\n- resource.yaml")}, + }, + path: "./", + wantErr: fmt.Errorf("found multiple kustomization files"), + }, + { + name: "error on no file found", + files: []file{}, + path: "./", + wantErr: fmt.Errorf("no kustomization file found"), + }, + { + name: "error on symlink outside root", + rootSuffix: "subdir", + files: []file{ + {name: konfig.DefaultKustomizationFileName(), data: []byte("resources:\n- resource.yaml")}, + {name: "subdir/" + konfig.DefaultKustomizationFileName(), symlink: "../kustomization.yaml"}, + }, + wantErr: fmt.Errorf("no kustomization file found"), + }, + { + name: "error on invalid file", + files: []file{ + {name: konfig.DefaultKustomizationFileName(), data: []byte("resources")}, + }, + wantErr: fmt.Errorf("failed to unmarshal kustomization file"), + }, + { + name: "error on absolute path", + path: "/absolute/", + wantErr: fmt.Errorf("path '/absolute/' must be relative"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + tmpDir := t.TempDir() + for _, f := range tt.files { + fPath := filepath.Join(tmpDir, f.name) + if f.symlink != "" { + g.Expect(os.Symlink(f.symlink, fPath)) + continue + } + g.Expect(os.MkdirAll(filepath.Dir(fPath), 0o700)).To(Succeed()) + g.Expect(os.WriteFile(fPath, f.data, 0o644)).To(Succeed()) + } + + root := filepath.Join(tmpDir, tt.rootSuffix) + got, err := secureLoadKustomizationFile(root, tt.path) + if wantErr := tt.wantErr; wantErr != nil { + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring(wantErr.Error())) + g.Expect(got).To(BeNil()) + return + } + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(got).To(Equal(tt.want)) + }) + } +} + +func TestDecryptor_recurseKustomizationFiles(t *testing.T) { + type kusNode struct { + path string + symlink string + resources []string + visitErr error + visited int + expectVisited int + expectCached bool + } + tests := []struct { + name string + wordirSuffix string + path string + nodes []*kusNode + wantErr error + wantErrStr string + }{ + { + name: "recurse on resources", + wordirSuffix: "foo", + path: "bar", + nodes: []*kusNode{ + { + path: "foo/bar/kustomization.yaml", + resources: []string{"../baz"}, + expectVisited: 1, + expectCached: true, + }, + { + path: "foo/baz/kustomization.yaml", + resources: []string{"/foo/bar/baz"}, + expectVisited: 1, + expectCached: true, + }, + { + path: "foo/bar/baz/kustomization.yaml", + resources: []string{}, + expectVisited: 1, + expectCached: true, + }, + }, + }, + { + name: "recursive loop", + wordirSuffix: "foo", + path: "bar", + nodes: []*kusNode{ + { + path: "foo/bar/kustomization.yaml", + resources: []string{"../baz"}, + expectVisited: 1, + expectCached: true, + }, + { + path: "foo/baz/kustomization.yaml", + resources: []string{"../foobar"}, + expectVisited: 1, + expectCached: true, + }, + { + path: "foo/foobar/kustomization.yaml", + resources: []string{"../bar"}, + expectVisited: 1, + expectCached: true, + }, + }, + }, + { + name: "absolute symlink", + path: "bar", + nodes: []*kusNode{ + { + path: "bar/baz/kustomization.yaml", + resources: []string{"../bar/absolute"}, + expectVisited: 1, + expectCached: true, + }, + { + path: "bar/absolute", + symlink: "/bar/foo/", + }, + { + path: "bar/foo/kustomization.yaml", + expectVisited: 1, + expectCached: true, + }, + }, + }, + { + name: "relative symlink", + path: "bar", + nodes: []*kusNode{ + { + path: "bar/baz/kustomization.yaml", + resources: []string{"../bar/relative"}, + expectVisited: 1, + expectCached: true, + }, + { + path: "bar/relative", + symlink: "../foo/", + }, + { + path: "bar/foo/kustomization.yaml", + expectVisited: 1, + expectCached: true, + }, + }, + }, + { + name: "recognized kustomization names", + path: "./", + nodes: []*kusNode{ + { + path: konfig.RecognizedKustomizationFileNames()[1], + resources: []string{"bar"}, + expectVisited: 1, + expectCached: true, + }, + { + path: filepath.Join("bar", konfig.RecognizedKustomizationFileNames()[0]), + resources: []string{"../baz"}, + expectVisited: 1, + expectCached: true, + }, + { + path: filepath.Join("baz", konfig.RecognizedKustomizationFileNames()[2]), + expectVisited: 1, + expectCached: true, + }, + }, + }, + { + name: "path does not exist", + path: "./invalid", + wantErr: &errRecurseIgnore{Err: fs.ErrNotExist}, + wantErrStr: "lstat invalid", + }, + { + name: "path is not a directory", + path: "./file.txt", + nodes: []*kusNode{ + { + path: "file.txt", + }, + }, + wantErr: &errRecurseIgnore{Err: fmt.Errorf("not a directory")}, + wantErrStr: "not a directory", + }, + { + name: "recurse error is returned", + path: "/foo", + nodes: []*kusNode{ + { + path: "foo/kustomization.yaml", + resources: []string{"../baz"}, + expectVisited: 1, + expectCached: true, + }, + { + path: "baz/wrongfile.yaml", + expectVisited: 0, + expectCached: false, + }, + }, + wantErr: fmt.Errorf("no kustomization file found"), + }, + { + name: "recurse ignores errRecurseIgnore", + path: "/foo", + nodes: []*kusNode{ + { + path: "foo/kustomization.yaml", + resources: []string{"../baz"}, + expectVisited: 1, + expectCached: true, + }, + { + path: "baz", + expectVisited: 0, + expectCached: false, + }, + }, + }, + { + name: "remote build references are ignored", + path: "/foo", + nodes: []*kusNode{ + { + path: "foo/kustomization.yaml", + resources: []string{ + "../baz", + "https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6", + }, + expectVisited: 1, + expectCached: true, + }, + { + path: "baz/kustomization.yaml", + resources: []string{ + "github.com/Liujingfang1/mysql?ref=test", + }, + expectVisited: 1, + expectCached: true, + }, + }, + }, + { + name: "visit error is returned", + path: "/", + nodes: []*kusNode{ + { + path: "kustomization.yaml", + resources: []string{ + "baz", + }, + expectVisited: 1, + expectCached: true, + }, + { + path: "baz/kustomization.yaml", + visitErr: fmt.Errorf("visit error"), + expectVisited: 1, + expectCached: true, + }, + }, + wantErr: fmt.Errorf("visit error"), + wantErrStr: "visit error", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + tmpDir := t.TempDir() + + for _, n := range tt.nodes { + path := filepath.Join(tmpDir, n.path) + if n.symlink != "" { + g.Expect(os.Symlink(strings.Replace(n.symlink, "", tmpDir, 1), path)).To(Succeed()) + return + } + kus := kustypes.Kustomization{ + TypeMeta: kustypes.TypeMeta{ + APIVersion: kustypes.KustomizationVersion, + Kind: kustypes.KustomizationKind, + }, + } + for _, res := range n.resources { + res = strings.Replace(res, "", tmpDir, 1) + kus.Resources = append(kus.Resources, res) + } + b, err := yaml.Marshal(kus) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(os.MkdirAll(filepath.Dir(path), 0o700)).To(Succeed()) + g.Expect(os.WriteFile(path, b, 0o644)) + } + + visit := func(root, path string, kus *kustypes.Kustomization) error { + if filepath.IsAbs(path) { + path = stripRoot(root, path) + } + for _, n := range tt.nodes { + if dir := filepath.Dir(n.path); filepath.Join(tt.wordirSuffix, path) != dir { + continue + } + n.visited++ + if n.visitErr != nil { + return n.visitErr + } + } + return nil + } + + visited := make(map[string]struct{}, 0) + err := recurseKustomizationFiles(filepath.Join(tmpDir, tt.wordirSuffix), tt.path, visit, visited) + if tt.wantErr != nil { + g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(BeAssignableToTypeOf(tt.wantErr)) + if tt.wantErrStr != "" { + g.Expect(err.Error()).To(ContainSubstring(tt.wantErrStr)) + } + return + } + + g.Expect(err).ToNot(HaveOccurred()) + for _, n := range tt.nodes { + g.Expect(n.visited).To(Equal(n.expectVisited), n.path) + + haveCache := HaveKey(filepath.Dir(filepath.Join(tmpDir, n.path))) + if n.expectCached { + g.Expect(visited).To(haveCache) + } else { + g.Expect(visited).ToNot(haveCache) + } + } + }) + } +} + +func TestDecryptor_isSOPSEncryptedResource(t *testing.T) { + g := NewWithT(t) + + resourceFactory := provider.NewDefaultDepProvider().GetResourceFactory() + encrypted := resourceFactory.FromMap(map[string]interface{}{ + "sops": map[string]string{ + "mac": "some mac value", + }, + }) + empty := resourceFactory.FromMap(map[string]interface{}{}) + + g.Expect(isSOPSEncryptedResource(encrypted)).To(BeTrue()) + g.Expect(isSOPSEncryptedResource(empty)).To(BeFalse()) +} + +func TestDecryptor_secureAbsPath(t *testing.T) { + tests := []struct { + name string + root string + path string + wantAbs string + wantRel string + wantErr bool + }{ + { + name: "absolute to root", + root: "/wordir/", + path: "/wordir/foo/", + wantAbs: "/wordir/foo", + wantRel: "foo", + }, + { + name: "relative to root", + root: "/wordir", + path: "./foo", + wantAbs: "/wordir/foo", + wantRel: "foo", + }, + { + name: "illegal traverse", + root: "/wordir/foo", + path: "../../bar", + wantAbs: "/wordir/foo/bar", + wantRel: "bar", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + gotAbs, gotRel, err := securePaths(tt.root, tt.path) + if tt.wantErr { + g.Expect(err).To(HaveOccurred()) + g.Expect(gotAbs).To(BeEmpty()) + g.Expect(gotRel).To(BeEmpty()) + return + } + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(gotAbs).To(Equal(tt.wantAbs)) + g.Expect(gotRel).To(Equal(tt.wantRel)) + }) + } +} + +func TestDecryptor_formatForPath(t *testing.T) { + tests := []struct { + name string + path string + want formats.Format + }{ + { + name: "docker config", + path: corev1.DockerConfigJsonKey, + want: formats.Json, + }, + { + name: "fallback", + path: "foo.yaml", + want: formats.Yaml, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + g.Expect(formatForPath(tt.path)).To(Equal(tt.want)) + }) + } +} + +func TestDecryptor_detectFormatFromMarkerBytes(t *testing.T) { + tests := []struct { + name string + b []byte + want formats.Format + }{ + { + name: "detects format", + b: bytes.Join([][]byte{[]byte("random other bytes"), sopsFormatToMarkerBytes[formats.Yaml], []byte("more random bytes")}, []byte(" ")), + want: formats.Yaml, + }, + { + name: "returns unsupported format", + b: []byte("no marker bytes present"), + want: unsupportedFormat, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := detectFormatFromMarkerBytes(tt.b); got != tt.want { + t.Errorf("detectFormatFromMarkerBytes() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/sops/sops_decryptor.go b/pkg/sops/sops_decryptor.go deleted file mode 100644 index 474d9872d..000000000 --- a/pkg/sops/sops_decryptor.go +++ /dev/null @@ -1,21 +0,0 @@ -package sops - -import ( - "fmt" - "go.mozilla.org/sops/v3/cmd/sops/formats" - "go.mozilla.org/sops/v3/decrypt" -) - -type SopsDecrypter interface { - SopsDecryptWithFormat(data []byte, inputFormat, outputFormat formats.Format) ([]byte, error) -} - -type LocalSopsDecrypter struct { -} - -func (_ LocalSopsDecrypter) SopsDecryptWithFormat(data []byte, inputFormat, outputFormat formats.Format) ([]byte, error) { - if inputFormat != outputFormat { - return nil, fmt.Errorf("inputFormat and outputFormat must be equal") - } - return decrypt.DataWithFormat(data, inputFormat) -} diff --git a/pkg/sops/utils.go b/pkg/sops/utils.go index 5b05e20ca..ab6f881ce 100644 --- a/pkg/sops/utils.go +++ b/pkg/sops/utils.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "github.com/kluctl/kluctl/v2/pkg/utils" "go.mozilla.org/sops/v3" "go.mozilla.org/sops/v3/cmd/sops/formats" @@ -15,7 +16,7 @@ func IsMaybeSopsFile(s []byte) bool { return bytes.Index(s, []byte("sops")) != -1 } -func MaybeDecrypt(decrypter SopsDecrypter, encrypted []byte, inputFormat, outputFormat formats.Format) ([]byte, bool, error) { +func MaybeDecrypt(decrypter *decryptor.Decryptor, encrypted []byte, inputFormat, outputFormat formats.Format) ([]byte, bool, error) { if decrypter == nil { return encrypted, false, nil } @@ -34,11 +35,11 @@ func MaybeDecrypt(decrypter SopsDecrypter, encrypted []byte, inputFormat, output return d, true, nil } -func MaybeDecryptFile(decrypter SopsDecrypter, path string) error { +func MaybeDecryptFile(decrypter *decryptor.Decryptor, path string) error { return MaybeDecryptFileTo(decrypter, path, path) } -func MaybeDecryptFileTo(decrypter SopsDecrypter, path string, to string) error { +func MaybeDecryptFileTo(decrypter *decryptor.Decryptor, path string, to string) error { format := formats.FormatForPath(path) file, err := os.ReadFile(path) @@ -61,7 +62,7 @@ func MaybeDecryptFileTo(decrypter SopsDecrypter, path string, to string) error { return nil } -func MaybeDecryptFileToTmp(ctx context.Context, decrypter SopsDecrypter, path string) (string, error) { +func MaybeDecryptFileToTmp(ctx context.Context, decrypter *decryptor.Decryptor, path string) (string, error) { tmp, err := os.CreateTemp(utils.GetTmpBaseDir(ctx), "sops-decrypt-") if err != nil { return "", err diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 9f070c54d..39161f372 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -10,6 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -32,14 +33,14 @@ type usernamePassword struct { type VarsLoader struct { ctx context.Context k *k8s.K8sCluster - sops sops.SopsDecrypter + sops *decryptor.Decryptor rp *repocache.GitRepoCache aws aws.AwsClientFactory credentialsCache map[string]usernamePassword } -func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, sops sops.SopsDecrypter, rp *repocache.GitRepoCache, aws aws.AwsClientFactory) *VarsLoader { +func NewVarsLoader(ctx context.Context, k *k8s.K8sCluster, sops *decryptor.Decryptor, rp *repocache.GitRepoCache, aws aws.AwsClientFactory) *VarsLoader { return &VarsLoader{ ctx: ctx, k: k, diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index d89772654..f8320e8fd 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -2,6 +2,7 @@ package vars import ( "context" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "io" "net/http" "net/http/httptest" @@ -19,7 +20,6 @@ import ( ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/repocache" - "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -48,7 +48,10 @@ func testVarsLoader(t *testing.T, test func(vl *VarsLoader, vc *VarsCtx, aws *aw grc := newRP(t) fakeAws := aws.NewFakeClientFactory() - vl := NewVarsLoader(context.TODO(), k, &sops.LocalSopsDecrypter{}, grc, fakeAws) + d := decryptor.NewDecryptor("", decryptor.MaxEncryptedFileSize) + d.AddLocalKeyService() + + vl := NewVarsLoader(context.TODO(), k, d, grc, fakeAws) vc := NewVarsCtx(newJinja2Must(t)) test(vl, vc, fakeAws) From 9627fbc354debd8febaa7565ce76619051f54df9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 23 Dec 2022 15:14:55 +0100 Subject: [PATCH 0697/2268] fix: Upgrade SOPS to the develop branch This also upgrades SOPS to use github.com/aws/aws-sdk-go-v2 for AWS KMS, which is needed to support SSO sessions. --- go.mod | 32 +++++++++++------------ go.sum | 82 ++++++++++++++++++++++++++++------------------------------ 2 files changed, 54 insertions(+), 60 deletions(-) diff --git a/go.mod b/go.mod index f4b2b9d17..0368e53c8 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/huandu/xstrings v1.4.0 github.com/onsi/gomega v1.24.1 github.com/otiai10/copy v1.9.0 - go.mozilla.org/sops/v3 v3.7.3 + go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.0 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/yaml v1.3.0 @@ -74,18 +74,15 @@ require ( require ( cloud.google.com/go/compute v1.14.0 // indirect cloud.google.com/go/compute/metadata v0.2.2 // indirect - github.com/Azure/azure-sdk-for-go v63.3.0+incompatible // indirect + cloud.google.com/go/iam v0.8.0 // indirect + cloud.google.com/go/kms v1.6.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.5.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.27 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect - github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2 // indirect github.com/BurntSushi/toml v1.2.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -97,7 +94,6 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go v1.43.43 // indirect github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.7 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect @@ -105,6 +101,7 @@ require ( github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 // indirect @@ -116,8 +113,8 @@ require ( github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.3.0 // indirect github.com/containerd/containerd v1.6.13 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect github.com/docker/cli v20.10.21+incompatible // indirect github.com/docker/docker v20.10.21+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect @@ -144,6 +141,7 @@ require ( github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -176,14 +174,13 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/vault/sdk v0.6.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect - github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.13 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.1 // indirect @@ -213,6 +210,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pjbgf/sha1cd v0.2.3 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -229,6 +227,7 @@ require ( github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.1 // indirect + github.com/urfave/cli v1.22.7 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -252,7 +251,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gotest.tools/v3 v3.2.0 // indirect k8s.io/apiserver v0.26.0 // indirect diff --git a/go.sum b/go.sum index af3d44e15..cb57720fb 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,10 @@ cloud.google.com/go/compute/metadata v0.2.2/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxB 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= 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= @@ -48,37 +52,22 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc= filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8= -github.com/Azure/azure-sdk-for-go v63.3.0+incompatible h1:INepVujzUrmArRZjDLHbtER+FkvCoEwyRCXGqOlmDII= -github.com/Azure/azure-sdk-for-go v63.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 h1:tz19qLF65vuu2ibfTqGVJxG/zZAI27NEIIbvAOQwYbw= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.5.1 h1:AXFNQ6kLaPODEpGSMWjmbkt6iP7fa1DIEzjx6JRFC9U= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.5.1/go.mod h1:yOYJv0tO0TTNcje8ahhBHQcdAiYqRIp5fsog5FPefr4= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 h1:9cn6ICCGiWFNA/slKnrkf+ENyvaCRKHtuoGtnLIAgao= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= -github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= -github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2 h1:BGX4OiGP9htYSd6M3pAZctcUUSruhIAUVkv2X0Cn9yE= +github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -129,8 +118,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.43.43 h1:1L06qzQvl4aC3Skfh5rV7xVhGHjIZoHcqy16NoyQ1o4= -github.com/aws/aws-sdk-go v1.43.43/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY= github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.18.7 h1:V94lTcix6jouwmAsgQMAEBozVAGJMFhVj+6/++xfe3E= @@ -139,14 +127,18 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.7 h1:qUUcNS5Z1092XBFT66IJM7mYkMwg github.com/aws/aws-sdk-go-v2/credentials v1.13.7/go.mod h1:AdCcbZXHQCjJh6NaH3pFaw8LUeBFn5+88BZGMVGuBT8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k= +github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= +github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 h1:77V7vnw/NC4DORHVgA97+Ky2p1ri0+ZVYXh6ordUZU0= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 h1:gItLq3zBYyRDPmqAClgzTH8PBjDQGeyptYGHIwtYYNA= @@ -155,6 +147,7 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 h1:KCacyVSs/wlcPGx37hcbT3I github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8= github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 h1:9Mtq1KM6nD8/+HStvWcvYnixJ5N85DX+P+OY3kI3W2k= github.com/aws/aws-sdk-go-v2/service/sts v1.17.7/go.mod h1:+lGbb3+1ugwKrNTWcf2RT05Xmp543B06zDFTwiTLp7I= +github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -204,7 +197,9 @@ github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvA github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= 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/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 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/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -214,9 +209,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= +github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= @@ -336,8 +330,9 @@ github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH 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-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= @@ -505,8 +500,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= -github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -528,9 +521,7 @@ github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= @@ -571,6 +562,8 @@ 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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -660,6 +653,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -702,6 +696,9 @@ github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9F github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= 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= @@ -814,6 +811,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/urfave/cli v1.22.7 h1:aXiFAgRugfJ27UFDsGJ9DB2FvTC73hlVXFSqq5bo9eU= +github.com/urfave/cli v1.22.7/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -849,8 +848,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3 go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k= go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a/go.mod h1:YDKUvO0b//78PaaEro6CAPH6NqohCmL2Cwju5XI2HoE= -go.mozilla.org/sops/v3 v3.7.3 h1:CYx02LnWTATWv6NqWJIt4JCKVKSnGV+MsRiDpvwWQhg= -go.mozilla.org/sops/v3 v3.7.3/go.mod h1:AutdccISG5Nt/faUigaKPU9aGmhyZuCyUiSx5YCa1O8= +go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 h1:4t4bmkosajncfGrQGfg6wXhyf96KdDzx+QxRd+rpQPM= +go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1/go.mod h1:eC/nw/CfC/v4aFitUY1JyiL+0dP6Drun/cpS/c9mypQ= 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= @@ -971,7 +970,6 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd 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.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= @@ -1058,13 +1056,13 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20210809222454-d867a43fc93e/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1296,8 +1294,6 @@ 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/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From def1dd4e3bc3144d2970d0566c8d2bdde9b26938 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 27 Dec 2022 13:59:34 +0100 Subject: [PATCH 0698/2268] fix: Disable decryptor tests on windows --- pkg/sops/decryptor/decryptor_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/sops/decryptor/decryptor_test.go b/pkg/sops/decryptor/decryptor_test.go index 9f3c642ec..283901abe 100644 --- a/pkg/sops/decryptor/decryptor_test.go +++ b/pkg/sops/decryptor/decryptor_test.go @@ -1,3 +1,5 @@ +//go:build !windows + /* Copyright 2021 The Flux authors From b528623135b4d383de71a2707cdeb529902a6f42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Dec 2022 14:16:43 +0000 Subject: [PATCH 0699/2268] chore(deps): Bump sigs.k8s.io/controller-runtime from 0.14.0 to 0.14.1 Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.14.0 to 0.14.1. - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/master/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.14.0...v0.14.1) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0368e53c8..48d1c83b1 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/onsi/gomega v1.24.1 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 - sigs.k8s.io/controller-runtime v0.14.0 + sigs.k8s.io/controller-runtime v0.14.1 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/yaml v1.3.0 ) diff --git a/go.sum b/go.sum index cb57720fb..6a9d1513e 100644 --- a/go.sum +++ b/go.sum @@ -1349,8 +1349,8 @@ oras.land/oras-go v1.2.1/go.mod h1:3N11Z5E3c4ZzOjroCl1RtAdB4yNAYl7A27j2SVf913A= 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/controller-runtime v0.14.0 h1:ju2xsov5Ara6FoQuddg+az+rAxsUsTYn2IYyEKCTyDc= -sigs.k8s.io/controller-runtime v0.14.0/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU= +sigs.k8s.io/controller-runtime v0.14.1 h1:vThDes9pzg0Y+UbCPY3Wj34CGIYPgdmspPm2GIpxpzM= +sigs.k8s.io/controller-runtime v0.14.1/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU= 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/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= From 0fc95ad364104ced0c5db0c313c6d2be5d18bdc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Dec 2022 22:07:22 +0000 Subject: [PATCH 0700/2268] chore(deps): Bump github.com/onsi/gomega from 1.24.1 to 1.24.2 Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.24.1 to 1.24.2. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.24.1...v1.24.2) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 0368e53c8..6e2520ea5 100644 --- a/go.mod +++ b/go.mod @@ -63,7 +63,7 @@ require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.24.1 + github.com/onsi/gomega v1.24.2 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.0 diff --git a/go.sum b/go.sum index cb57720fb..e1e13386c 100644 --- a/go.sum +++ b/go.sum @@ -665,9 +665,9 @@ github.com/ohler55/ojg v1.15.0/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfli github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= -github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= -github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q= +github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= +github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= From aa440a3043c985e3faea5f7003ffe9834332fb96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Dec 2022 22:07:42 +0000 Subject: [PATCH 0701/2268] chore(deps): Bump filippo.io/age from 1.0.0 to 1.1.1 Bumps [filippo.io/age](https://github.com/FiloSottile/age) from 1.0.0 to 1.1.1. - [Release notes](https://github.com/FiloSottile/age/releases) - [Commits](https://github.com/FiloSottile/age/compare/v1.0.0...v1.1.1) --- updated-dependencies: - dependency-name: filippo.io/age dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0368e53c8..82f4a3c6b 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,7 @@ require ( ) require ( - filippo.io/age v1.0.0 + filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2/config v1.18.7 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 github.com/go-logr/logr v1.2.3 diff --git a/go.sum b/go.sum index cb57720fb..44ad1ecdc 100644 --- a/go.sum +++ b/go.sum @@ -50,8 +50,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX 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= -filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc= -filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8= +filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= +filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 h1:tz19qLF65vuu2ibfTqGVJxG/zZAI27NEIIbvAOQwYbw= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= From b5536adc70f04f51a54bfdffaa0276fce1cc0287 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 28 Dec 2022 17:47:39 +0100 Subject: [PATCH 0702/2268] fix: Merge external args after target args External args are meant to override target args. --- pkg/kluctl_project/target_context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index f07436014..f1619b2b9 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -171,7 +171,6 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, externalArgs *uo.U allArgs := uo.New() - allArgs.Merge(externalArgs) if target != nil { if target.Args != nil { allArgs.Merge(target.Args) @@ -182,6 +181,7 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, externalArgs *uo.U } } } + allArgs.Merge(externalArgs) deprecatedArgs, err := deployment.LoadDeprecatedDeploymentArgs(p.ctx, p.ProjectDir, varsCtx, allArgs) if err != nil { From 9ae41582a28cce558ed89e5ed98e9ce39eec828a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Dec 2022 22:07:51 +0000 Subject: [PATCH 0703/2268] chore(deps): Bump github.com/mattn/go-isatty from 0.0.16 to 0.0.17 Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.16 to 0.0.17. - [Release notes](https://github.com/mattn/go-isatty/releases) - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.16...v0.0.17) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 8528b8513..59163b399 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/kluctl/go-embed-python v0.0.0-3.10.8-20221106-1 github.com/kluctl/go-jinja2 v0.0.0-20221215083015-c3f906953ba1 github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.16 + github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 github.com/ohler55/ojg v1.15.0 diff --git a/go.sum b/go.sum index c6c400acc..c5d5c3f9b 100644 --- a/go.sum +++ b/go.sum @@ -599,8 +599,9 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 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 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 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/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= From 5a9371796bd98fe702bca8155b1b7d1126efeef9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Dec 2022 22:08:52 +0000 Subject: [PATCH 0704/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/service/secretsmanager Bumps [github.com/aws/aws-sdk-go-v2/service/secretsmanager](https://github.com/aws/aws-sdk-go-v2) from 1.16.11 to 1.17.0. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.16.11...v1.17.0) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/secretsmanager dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8528b8513..bb8ec023b 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,7 @@ require ( require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2/config v1.18.7 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.17.0 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 diff --git a/go.sum b/go.sum index c6c400acc..522c00323 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViS github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11 h1:77V7vnw/NC4DORHVgA97+Ky2p1ri0+ZVYXh6ordUZU0= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.11/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.17.0 h1:6W6BLZcXytRJsVvc2gGwxKE4wbMSlWqdxZivBP/E+ys= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.17.0/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 h1:gItLq3zBYyRDPmqAClgzTH8PBjDQGeyptYGHIwtYYNA= github.com/aws/aws-sdk-go-v2/service/sso v1.11.28/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 h1:KCacyVSs/wlcPGx37hcbT3IGYO8P8Jx+TgSDhAXtQMY= From 3e946de42b625c3b39db12d8848a9d4e319cc0fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 22:07:07 +0000 Subject: [PATCH 0705/2268] chore(deps): Bump github.com/ohler55/ojg from 1.15.0 to 1.16.0 Bumps [github.com/ohler55/ojg](https://github.com/ohler55/ojg) from 1.15.0 to 1.16.0. - [Release notes](https://github.com/ohler55/ojg/releases) - [Changelog](https://github.com/ohler55/ojg/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/ojg/compare/v1.15.0...v1.16.0) --- updated-dependencies: - dependency-name: github.com/ohler55/ojg dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8528b8513..d91f47188 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.15.0 + github.com/ohler55/ojg v1.16.0 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.9.0 diff --git a/go.sum b/go.sum index c6c400acc..517a76973 100644 --- a/go.sum +++ b/go.sum @@ -660,8 +660,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/ohler55/ojg v1.15.0 h1:Z95FvBiMsMOOGP9Nzv5OVV4ND2KnEMxk0GOS8Kvcahg= -github.com/ohler55/ojg v1.15.0/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= +github.com/ohler55/ojg v1.16.0 h1:JQMJp/ygkak1YiGFB2ohbks2Y6BAE061i5cq5fECiVA= +github.com/ohler55/ojg v1.16.0/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= From 1b7af20ce6c17693eb0ba1d5242f3d3c8e2ed803 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 Jan 2023 12:12:39 +0100 Subject: [PATCH 0706/2268] refactor: Implement go routine helper and use it whereever possible --- cmd/kluctl/commands/cmd_delete.go | 2 +- cmd/kluctl/commands/cmd_helm_pull.go | 17 +--- cmd/kluctl/commands/cmd_helm_update.go | 26 ++---- cmd/kluctl/commands/cobra_utils.go | 13 +-- pkg/deployment/deployment_collection.go | 116 ++++++++---------------- pkg/deployment/utils/delete_utils.go | 28 ++---- pkg/k8s/k8s_cluster.go | 40 ++++---- pkg/utils/go_helper.go | 68 ++++++++++++++ pkg/utils/limited_routine.go | 42 --------- 9 files changed, 152 insertions(+), 200 deletions(-) create mode 100644 pkg/utils/go_helper.go delete mode 100644 pkg/utils/limited_routine.go diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index ed20376b7..bdac4154e 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -89,5 +89,5 @@ func confirmedDeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2. } } - return utils.DeleteObjects(k, refs, true) + return utils.DeleteObjects(ctx, k, refs, true) } diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 0931ea0b8..ae0bf7bfd 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -3,17 +3,14 @@ package commands import ( "context" "fmt" - "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" - "golang.org/x/sync/semaphore" "io/fs" "os" "path/filepath" - "sync" ) type helmPullCmd struct { @@ -58,10 +55,7 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { } } - var errs *multierror.Error - var wg sync.WaitGroup - var mutex sync.Mutex - sem := semaphore.NewWeighted(8) + g := utils.NewGoHelper(ctx, 8) for _, chart := range charts { chart := chart @@ -97,7 +91,7 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { for version, _ := range versionsToPull { version := version - utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { + g.RunE(func() error { s := status.Start(ctx, "%s: Pulling Chart with version %s", statusPrefix, version) defer s.Failed() @@ -112,12 +106,9 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { }) } } - wg.Wait() - if err != nil { - errs = multierror.Append(errs, err) - } + g.Wait() - if errs.ErrorOrNil() != nil { + if g.ErrorOrNil() != nil { return fmt.Errorf("command failed") } diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index fd4856c1b..a2df12433 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -5,18 +5,15 @@ import ( "fmt" "github.com/fluxcd/go-git/v5" "github.com/fluxcd/go-git/v5/plumbing/format/index" - "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" - "golang.org/x/sync/semaphore" "io/fs" "os" "path/filepath" - "sync" ) type helmUpdateCmd struct { @@ -70,10 +67,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { baseChartsDir := filepath.Join(projectDir, ".helm-charts") - var errs *multierror.Error - var wg sync.WaitGroup - var mutex sync.Mutex - sem := semaphore.NewWeighted(8) + g := utils.NewGoHelper(ctx, 8) releases, charts, err := loadHelmReleases(projectDir, baseChartsDir, &cmd.HelmCredentials) if err != nil { @@ -94,7 +88,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { for _, chart := range charts { chart := chart - utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { + g.RunE(func() error { s := status.Start(ctx, "%s: Querying versions", chart.GetChartName()) defer s.Failed() err := chart.QueryVersions(ctx) @@ -106,9 +100,9 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { return nil }) } - wg.Wait() - if errs.ErrorOrNil() != nil { - return errs + g.Wait() + if g.ErrorOrNil() != nil { + return g.ErrorOrNil() } for _, chart := range charts { @@ -124,7 +118,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { for version, _ := range versionsToPull { version := version - utils.GoLimitedMultiError(ctx, sem, &errs, &mutex, &wg, func() error { + g.RunE(func() error { s := status.Start(ctx, "%s: Downloading Chart with version %s into cache", chart.GetChartName(), version) defer s.Failed() _, err := chart.PullCached(ctx, version) @@ -137,9 +131,9 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { }) } } - wg.Wait() - if errs.ErrorOrNil() != nil { - return errs + g.Wait() + if g.ErrorOrNil() != nil { + return g.ErrorOrNil() } versionUseCounts := map[string]map[string]int{} @@ -212,7 +206,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { } } - return errs.ErrorOrNil() + return nil } func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]bool) error { diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index e8e68f528..173303bfe 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/term" "github.com/spf13/cobra" @@ -229,7 +230,7 @@ func copyViperValuesToCobraCmd(cmd *cobra.Command) error { } func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { - var retErr []error + var errs *multierror.Error flags.VisitAll(func(flag *pflag.Flag) { sliceValue, _ := flag.Value.(pflag.SliceValue) if flag.Changed && sliceValue == nil { @@ -245,13 +246,13 @@ func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { for _, y := range x { s, ok := y.(string) if !ok { - retErr = append(retErr, fmt.Errorf("viper flag %s has unexpected type", flag.Name)) + errs = multierror.Append(errs, fmt.Errorf("viper flag %s has unexpected type", flag.Name)) return } a = append(a, s) } } else { - retErr = append(retErr, fmt.Errorf("viper flag %s has unexpected type", flag.Name)) + errs = multierror.Append(errs, fmt.Errorf("viper flag %s has unexpected type", flag.Name)) return } } @@ -269,18 +270,18 @@ func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { a = append(a, sliceValue.GetSlice()...) err := sliceValue.Replace(a) if err != nil { - retErr = append(retErr, err) + errs = multierror.Append(errs, err) } } else { for _, x := range a { err := flag.Value.Set(x) if err != nil { - retErr = append(retErr, err) + errs = multierror.Append(errs, err) } } } }) - return utils.NewErrorListOrNil(retErr) + return errs.ErrorOrNil() } func (c *rootCommand) helpFunc(cg *commandAndGroups, cmd *cobra.Command) { diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index c37a9fc9d..bc442fdc4 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -1,13 +1,11 @@ package deployment import ( - "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" - "golang.org/x/sync/semaphore" "path/filepath" "sync" ) @@ -114,59 +112,38 @@ func (c *DeploymentCollection) RenderDeployments() error { s := status.Start(c.ctx.Ctx, "Rendering templates") defer s.Failed() - var wg sync.WaitGroup - var mutex sync.Mutex - var errors []error + g := utils.NewGoHelper(c.ctx.Ctx, 0) for _, d := range c.Deployments { d := d - wg.Add(1) - go func() { - defer wg.Done() - err := d.render(c.forSeal) - if err != nil { - mutex.Lock() - errors = append(errors, err) - mutex.Unlock() - } - }() + g.RunE(func() error { + return d.render(c.forSeal) + }) } - wg.Wait() - if len(errors) != 0 { - return utils.NewErrorListOrNil(errors) + g.Wait() + if g.ErrorOrNil() == nil { + s.Success() } - s.Success() - return nil + return g.ErrorOrNil() } func (c *DeploymentCollection) renderHelmCharts() error { s := status.Start(c.ctx.Ctx, "Rendering Helm Charts") defer s.Failed() - var wg sync.WaitGroup - var mutex sync.Mutex - var errors []error + g := utils.NewGoHelper(c.ctx.Ctx, 0) for _, d := range c.Deployments { d := d - wg.Add(1) - go func() { - defer wg.Done() - err := d.renderHelmCharts() - if err != nil { - mutex.Lock() - errors = append(errors, err) - mutex.Unlock() - } - }() + g.RunE(func() error { + return d.renderHelmCharts() + }) } - wg.Wait() - if len(errors) != 0 { - return utils.NewErrorListOrNil(errors) + g.Wait() + if g.ErrorOrNil() == nil { + s.Success() } - - s.Success() - return nil + return g.ErrorOrNil() } func (c *DeploymentCollection) resolveSealedSecrets() error { @@ -184,80 +161,61 @@ func (c *DeploymentCollection) resolveSealedSecrets() error { } func (c *DeploymentCollection) buildKustomizeObjects() error { - var wg sync.WaitGroup - var errs []error - var mutex sync.Mutex - sem := semaphore.NewWeighted(16) - - handleError := func(err error) { - mutex.Lock() - errs = append(errs, err) - mutex.Unlock() - } + g := utils.NewGoHelper(c.ctx.Ctx, 0) s := status.Start(c.ctx.Ctx, "Building kustomize objects") for _, d_ := range c.Deployments { d := d_ - - wg.Add(1) - go func() { + g.RunE(func() error { err := d.buildKustomize() if err != nil { - handleError(fmt.Errorf("building kustomize objects for %s failed. %w", *d.dir, err)) + return fmt.Errorf("building kustomize objects for %s failed. %w", *d.dir, err) } - wg.Done() - }() + return nil + }) } - wg.Wait() + g.Wait() - if len(errs) != 0 { + if g.ErrorOrNil() != nil { s.Failed() - return utils.NewErrorListOrNil(errs) + return g.ErrorOrNil() } s.Success() s = status.Start(c.ctx.Ctx, "Postprocessing objects") for _, d_ := range c.Deployments { d := d_ - - wg.Add(1) - go func() { + g.RunE(func() error { err := d.postprocessCRDs() if err != nil { - handleError(fmt.Errorf("postprocessing CRDs failed: %w", err)) + return fmt.Errorf("postprocessing CRDs failed: %w", err) } - wg.Done() - }() + return nil + }) } - wg.Wait() + g.Wait() + g = utils.NewGoHelper(c.ctx.Ctx, 16) for _, d_ := range c.Deployments { d := d_ - wg.Add(1) - go func() { - _ = sem.Acquire(context.Background(), 1) - defer sem.Release(1) - + g.RunE(func() error { err := d.postprocessObjects(c.Images) if err != nil { - mutex.Lock() - errs = append(errs, fmt.Errorf("postprocessing kustomize objects for %s failed: %w", *d.dir, err)) - mutex.Unlock() + return fmt.Errorf("postprocessing kustomize objects for %s failed: %w", *d.dir, err) } - - wg.Done() - }() + return nil + }) } - wg.Wait() + g.Wait() - if len(errs) == 0 { + if g.ErrorOrNil() == nil { s.Success() } else { s.Failed() } - return utils.NewErrorListOrNil(errs) + return g.ErrorOrNil() } func (c *DeploymentCollection) LocalObjectsByRef() map[k8s2.ObjectRef]bool { diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index d79f5d229..00f72ad39 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -7,7 +7,6 @@ import ( k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "golang.org/x/sync/semaphore" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "sync" @@ -161,9 +160,8 @@ func FindObjectsForDelete(k *k8s.K8sCluster, allClusterObjects []*uo.Unstructure return ret, nil } -func DeleteObjects(k *k8s.K8sCluster, refs []k8s2.ObjectRef, doWait bool) (*types.CommandResult, error) { - var wg sync.WaitGroup - sem := semaphore.NewWeighted(8) +func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, doWait bool) (*types.CommandResult, error) { + g := utils.NewGoHelper(ctx, 8) var ret types.CommandResult namespaceNames := make(map[string]bool) @@ -193,18 +191,13 @@ func DeleteObjects(k *k8s.K8sCluster, refs []k8s2.ObjectRef, doWait bool) (*type ref := ref_ if ref.GVK.GroupVersion().String() == "v1" && ref.GVK.Kind == "Namespace" { namespaceNames[ref.Name] = true - wg.Add(1) - go func() { - defer wg.Done() - _ = sem.Acquire(context.Background(), 1) - defer sem.Release(1) - + g.Run(func() { apiWarnings, err := k.DeleteSingleObject(ref, k8s.DeleteOptions{NoWait: !doWait, IgnoreNotFoundError: true}) handleResult(ref, apiWarnings, err) - }() + }) } } - wg.Wait() + g.Wait() for _, ref_ := range refs { ref := ref_ @@ -215,17 +208,12 @@ func DeleteObjects(k *k8s.K8sCluster, refs []k8s2.ObjectRef, doWait bool) (*type // already deleted via namespace continue } - wg.Add(1) - go func() { - defer wg.Done() - _ = sem.Acquire(context.Background(), 1) - defer sem.Release(1) - + g.Run(func() { apiWarnings, err := k.DeleteSingleObject(ref, k8s.DeleteOptions{NoWait: !doWait, IgnoreNotFoundError: true}) handleResult(ref, apiWarnings, err) - }() + }) } - wg.Wait() + g.Wait() return &ret, nil } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 751664902..36af06bd9 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -136,10 +136,8 @@ func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, map[schema.GroupVersionKind][]ApiWarning, error) { var ret []*uo.UnstructuredObject - var errs []error retApiWarnings := make(map[schema.GroupVersionKind][]ApiWarning) var mutex sync.Mutex - var wg sync.WaitGroup filter := func(ar *v1.APIResource) bool { foundVerb := false @@ -152,29 +150,27 @@ func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map return foundVerb } + g := utils.NewGoHelper(k.ctx, 0) for _, gvk := range k.Resources.GetFilteredPreferredGVKs(filter) { gvk := gvk - wg.Add(1) - go func() { - defer wg.Done() - + g.RunE(func() error { l, apiWarnings, err := k.ListObjects(gvk, namespace, labels) mutex.Lock() defer mutex.Unlock() if err != nil && !errors.IsNotFound(err) { - errs = append(errs, err) - return + return err } ret = append(ret, l...) if len(apiWarnings) != 0 { retApiWarnings[gvk] = apiWarnings } - }() + return nil + }) } - wg.Wait() + g.Wait() - if len(errs) != 0 { - return nil, retApiWarnings, utils.NewErrorListOrNil(errs) + if g.ErrorOrNil() != nil { + return nil, retApiWarnings, g.ErrorOrNil() } return ret, retApiWarnings, nil @@ -196,16 +192,13 @@ func (k *K8sCluster) GetSingleObject(ref k8s.ObjectRef) (*uo.UnstructuredObject, func (k *K8sCluster) GetObjectsByRefs(refs []k8s.ObjectRef) ([]*uo.UnstructuredObject, map[k8s.ObjectRef][]ApiWarning, error) { var ret []*uo.UnstructuredObject - var errs []error retApiWarnings := make(map[k8s.ObjectRef][]ApiWarning) var mutex sync.Mutex - var wg sync.WaitGroup + g := utils.NewGoHelper(k.ctx, 0) for _, ref_ := range refs { ref := ref_ - wg.Add(1) - go func() { - defer wg.Done() + g.RunE(func() error { o, apiWarnings, err := k.GetSingleObject(ref) mutex.Lock() defer mutex.Unlock() @@ -214,16 +207,17 @@ func (k *K8sCluster) GetObjectsByRefs(refs []k8s.ObjectRef) ([]*uo.UnstructuredO } if err != nil { if !errors.IsNotFound(err) && !meta.IsNoMatchError(err) { - errs = append(errs, err) + return err } - return + return nil } ret = append(ret, o) - }() + return nil + }) } - wg.Wait() - if len(errs) != 0 { - return nil, retApiWarnings, utils.NewErrorListOrNil(errs) + g.Wait() + if g.ErrorOrNil() != nil { + return nil, retApiWarnings, g.ErrorOrNil() } return ret, retApiWarnings, nil diff --git a/pkg/utils/go_helper.go b/pkg/utils/go_helper.go new file mode 100644 index 000000000..b37a0a4de --- /dev/null +++ b/pkg/utils/go_helper.go @@ -0,0 +1,68 @@ +package utils + +import ( + "context" + "github.com/hashicorp/go-multierror" + "golang.org/x/sync/semaphore" + "sync" +) + +type goHelper struct { + ctx context.Context + sem *semaphore.Weighted + wg sync.WaitGroup + mutex sync.Mutex + errs *multierror.Error +} + +func NewGoHelper(ctx context.Context, max int) *goHelper { + g := &goHelper{ + ctx: ctx, + } + if max > 0 { + g.sem = semaphore.NewWeighted(int64(max)) + } + return g +} + +func (g *goHelper) addError(err error) { + g.mutex.Lock() + defer g.mutex.Unlock() + g.errs = multierror.Append(g.errs, err) +} + +func (g *goHelper) RunE(fn func() error) { + g.wg.Add(1) + go func() { + defer g.wg.Done() + if g.sem != nil { + err := g.sem.Acquire(g.ctx, 1) + if err != nil { + g.addError(err) + return + } + } + if g.sem != nil { + defer g.sem.Release(1) + } + err := fn() + if err != nil { + g.addError(err) + } + }() +} + +func (g *goHelper) Run(fn func()) { + g.RunE(func() error { + fn() + return nil + }) +} + +func (g *goHelper) Wait() { + g.wg.Wait() +} + +func (g *goHelper) ErrorOrNil() error { + return g.errs.ErrorOrNil() +} diff --git a/pkg/utils/limited_routine.go b/pkg/utils/limited_routine.go deleted file mode 100644 index f38a1bdde..000000000 --- a/pkg/utils/limited_routine.go +++ /dev/null @@ -1,42 +0,0 @@ -package utils - -import ( - "context" - "github.com/hashicorp/go-multierror" - "golang.org/x/sync/semaphore" - "sync" -) - -func GoLimited(ctx context.Context, sem *semaphore.Weighted, wg *sync.WaitGroup, fn func(), errCb func(err error)) { - if wg != nil { - wg.Add(1) - } - go func() { - if wg != nil { - defer wg.Done() - } - err := sem.Acquire(ctx, 1) - if err != nil { - if errCb != nil { - errCb(err) - } - return - } - defer sem.Release(1) - fn() - }() -} - -func GoLimitedMultiError(ctx context.Context, sem *semaphore.Weighted, merr **multierror.Error, mutex *sync.Mutex, wg *sync.WaitGroup, fn func() error) { - errCb := func(err error) { - mutex.Lock() - defer mutex.Unlock() - *merr = multierror.Append(*merr, err) - } - GoLimited(ctx, sem, wg, func() { - err := fn() - if err != nil { - errCb(err) - } - }, errCb) -} From 4329dbe857bb817f143f2ea443edb9484cdf8cec Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 Jan 2023 14:19:55 +0100 Subject: [PATCH 0707/2268] refactor: Replace last uses of NewErrorListOrNil with multierror --- pkg/deployment/deployment_item.go | 9 +++++---- pkg/deployment/utils/errors_holder.go | 4 ++-- pkg/utils/errorlist.go | 23 ----------------------- 3 files changed, 7 insertions(+), 29 deletions(-) delete mode 100644 pkg/utils/errorlist.go diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index a9ac22883..af74a2f46 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -3,6 +3,7 @@ package deployment import ( "fmt" "github.com/fluxcd/pkg/kustomize" + "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/sops" @@ -567,7 +568,7 @@ func (di *DeploymentItem) postprocessObjects(images *Images) error { var objects []interface{} - var errList []error + var errs *multierror.Error for _, o := range di.Objects { commonLabels := di.getCommonLabels() commonAnnotations := di.getCommonAnnotations() @@ -588,7 +589,7 @@ func (di *DeploymentItem) postprocessObjects(images *Images) error { // Resolve image placeholders err := images.ResolvePlaceholders(di.ctx.K, o, di.RelRenderedDir, di.Tags.ListKeys()) if err != nil { - errList = append(errList, err) + errs = multierror.Append(errs, err) } return nil }) @@ -596,8 +597,8 @@ func (di *DeploymentItem) postprocessObjects(images *Images) error { objects = append(objects, o.Object) } - if len(errList) != 0 { - return utils.NewErrorListOrNil(errList) + if errs.ErrorOrNil() != nil { + return errs.ErrorOrNil() } // Need to write it back to disk in case it is needed externally diff --git a/pkg/deployment/utils/errors_holder.go b/pkg/deployment/utils/errors_holder.go index 9b702a378..bf8faef1d 100644 --- a/pkg/deployment/utils/errors_holder.go +++ b/pkg/deployment/utils/errors_holder.go @@ -3,10 +3,10 @@ package utils import ( "errors" "fmt" + "github.com/hashicorp/go-multierror" k8s2 "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/utils" "sync" ) @@ -106,5 +106,5 @@ func (dew *DeploymentErrorsAndWarnings) getPlainErrorsList() []error { func (dew *DeploymentErrorsAndWarnings) GetMultiError() error { l := dew.getPlainErrorsList() - return utils.NewErrorListOrNil(l) + return multierror.Append(nil, l...).ErrorOrNil() } diff --git a/pkg/utils/errorlist.go b/pkg/utils/errorlist.go deleted file mode 100644 index 0191aa7d1..000000000 --- a/pkg/utils/errorlist.go +++ /dev/null @@ -1,23 +0,0 @@ -package utils - -type errorList struct { - errors []error -} - -func NewErrorListOrNil(errors []error) error { - if len(errors) == 0 { - return nil - } - return &errorList{errors: errors} -} - -func (el *errorList) Error() string { - s := "" - for _, err := range el.errors { - if len(s) != 0 { - s += "\n" - } - s += err.Error() - } - return s -} From f9951a923dfda20ab9eb361cdf25cf9f3ab9708f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jan 2023 22:06:21 +0000 Subject: [PATCH 0708/2268] chore(deps): Bump golang.org/x/net from 0.4.0 to 0.5.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.4.0 to 0.5.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.4.0...v0.5.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index a812c641c..835759cb6 100644 --- a/go.mod +++ b/go.mod @@ -39,11 +39,11 @@ require ( github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.4.0 - golang.org/x/net v0.4.0 + golang.org/x/net v0.5.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.3.0 - golang.org/x/term v0.3.0 - golang.org/x/text v0.5.0 + golang.org/x/sys v0.4.0 + golang.org/x/term v0.4.0 + golang.org/x/text v0.6.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.3 diff --git a/go.sum b/go.sum index c5d9e9066..39517d07d 100644 --- a/go.sum +++ b/go.sum @@ -975,8 +975,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= 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= @@ -1074,16 +1074,16 @@ golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= 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= @@ -1094,8 +1094,8 @@ 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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= From 66dfb825d58f9605cf3d1e99b8266a2b9cdcbaa7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 Jan 2023 15:21:39 +0100 Subject: [PATCH 0709/2268] refactor: Remove generic ListAllObjects/GetObjectsByRefs from K8sCluster And implement the needed functionality where it is used. Allows for better error handling later. --- pkg/deployment/utils/remote_objects_utils.go | 118 ++++++++++++++----- pkg/k8s/k8s_cluster.go | 75 ------------ 2 files changed, 89 insertions(+), 104 deletions(-) diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index 520a786a0..e9fbd99c2 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -2,12 +2,16 @@ package utils import ( "context" + "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + errors2 "k8s.io/apimachinery/pkg/api/errors" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "sync" ) type RemoteObjectUtils struct { @@ -26,53 +30,109 @@ func NewRemoteObjectsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnings) } } -func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[string]string, refs []k8s2.ObjectRef) error { - if k == nil { +func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string]string) error { + var mutex sync.Mutex + if len(labels) == 0 { return nil } - s := status.Start(u.ctx, "Getting remote objects by commonLabels") + baseStatus := "Getting remote objects by commonLabels" + s := status.Start(u.ctx, baseStatus) defer s.Failed() - if len(labels) != 0 { - allObjects, apiWarnings, err := k.ListAllObjects([]string{"get"}, "", labels) - for gvk, aw := range apiWarnings { - u.dew.AddApiWarnings(k8s2.ObjectRef{GVK: gvk}, aw) - } - if err != nil { - return err - } - for _, o := range allObjects { - u.remoteObjects[o.GetK8sRef()] = o - } + gvks := k.Resources.GetFilteredPreferredGVKs(func(ar *v1.APIResource) bool { + return utils.FindStrInSlice(ar.Verbs, "list") != -1 + }) + + g := utils.NewGoHelper(u.ctx, 0) + for _, gvk := range gvks { + gvk := gvk + g.RunE(func() error { + l, apiWarnings, err := k.ListObjects(gvk, "", labels) + u.dew.AddApiWarnings(k8s2.ObjectRef{GVK: gvk}, apiWarnings) + if err != nil { + if errors2.IsNotFound(err) { + return nil + } + return err + } + mutex.Lock() + defer mutex.Unlock() + for _, o := range l { + u.remoteObjects[o.GetK8sRef()] = o + } + return nil + }) } + g.Wait() + if g.ErrorOrNil() == nil { + s.Success() + } + return g.ErrorOrNil() +} +func (u *RemoteObjectUtils) getMissingObjects(k *k8s.K8sCluster, refs []k8s2.ObjectRef) error { notFoundRefsMap := make(map[k8s2.ObjectRef]bool) - var notFoundRefsList []k8s2.ObjectRef for _, ref := range refs { if _, ok := u.remoteObjects[ref]; !ok { if _, ok = notFoundRefsMap[ref]; !ok { notFoundRefsMap[ref] = true - notFoundRefsList = append(notFoundRefsList, ref) } } } - if len(notFoundRefsList) != 0 { - s.UpdateAndInfoFallback("Getting %d additional remote objects", len(notFoundRefsList)) - r, apiWarnings, err := k.GetObjectsByRefs(notFoundRefsList) - for ref, aw := range apiWarnings { - u.dew.AddApiWarnings(ref, aw) - } - if err != nil { - return err - } - for _, o := range r { - u.remoteObjects[o.GetK8sRef()] = o - } + var mutex sync.Mutex + if len(notFoundRefsMap) == 0 { + return nil } - s.UpdateAndInfoFallback("Getting namespaces") + baseStatus := fmt.Sprintf("Getting %d additional remote objects", len(notFoundRefsMap)) + s := status.Start(u.ctx, baseStatus) + defer s.Failed() + + g := utils.NewGoHelper(u.ctx, 0) + for ref, _ := range notFoundRefsMap { + ref := ref + g.RunE(func() error { + r, apiWarnings, err := k.GetSingleObject(ref) + u.dew.AddApiWarnings(ref, apiWarnings) + if err != nil { + if errors2.IsNotFound(err) { + return nil + } + return err + } + mutex.Lock() + defer mutex.Unlock() + u.remoteObjects[r.GetK8sRef()] = r + return nil + }) + } + g.Wait() + if g.ErrorOrNil() == nil { + s.Success() + } + return g.ErrorOrNil() +} + +func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[string]string, refs []k8s2.ObjectRef) error { + if k == nil { + return nil + } + + err := u.getAllByLabels(k, labels) + if err != nil { + return err + } + + err = u.getMissingObjects(k, refs) + if err != nil { + return err + } + + s := status.Start(u.ctx, "Getting namespaces") + defer s.Failed() + r, _, err := k.ListObjects(schema.GroupVersionKind{ Group: "", Version: "v1", diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 36af06bd9..31b5a227c 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -134,48 +134,6 @@ func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, return result, apiWarnings, err } -func (k *K8sCluster) ListAllObjects(verbs []string, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, map[schema.GroupVersionKind][]ApiWarning, error) { - var ret []*uo.UnstructuredObject - retApiWarnings := make(map[schema.GroupVersionKind][]ApiWarning) - var mutex sync.Mutex - - filter := func(ar *v1.APIResource) bool { - foundVerb := false - for _, v := range verbs { - if utils.FindStrInSlice(ar.Verbs, v) != -1 { - foundVerb = true - break - } - } - return foundVerb - } - - g := utils.NewGoHelper(k.ctx, 0) - for _, gvk := range k.Resources.GetFilteredPreferredGVKs(filter) { - gvk := gvk - g.RunE(func() error { - l, apiWarnings, err := k.ListObjects(gvk, namespace, labels) - mutex.Lock() - defer mutex.Unlock() - if err != nil && !errors.IsNotFound(err) { - return err - } - ret = append(ret, l...) - if len(apiWarnings) != 0 { - retApiWarnings[gvk] = apiWarnings - } - return nil - }) - } - g.Wait() - - if g.ErrorOrNil() != nil { - return nil, retApiWarnings, g.ErrorOrNil() - } - - return ret, retApiWarnings, nil -} - func (k *K8sCluster) GetSingleObject(ref k8s.ObjectRef) (*uo.UnstructuredObject, []ApiWarning, error) { var result *uo.UnstructuredObject apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { @@ -190,39 +148,6 @@ func (k *K8sCluster) GetSingleObject(ref k8s.ObjectRef) (*uo.UnstructuredObject, return result, apiWarnings, err } -func (k *K8sCluster) GetObjectsByRefs(refs []k8s.ObjectRef) ([]*uo.UnstructuredObject, map[k8s.ObjectRef][]ApiWarning, error) { - var ret []*uo.UnstructuredObject - retApiWarnings := make(map[k8s.ObjectRef][]ApiWarning) - var mutex sync.Mutex - - g := utils.NewGoHelper(k.ctx, 0) - for _, ref_ := range refs { - ref := ref_ - g.RunE(func() error { - o, apiWarnings, err := k.GetSingleObject(ref) - mutex.Lock() - defer mutex.Unlock() - if len(apiWarnings) != 0 { - retApiWarnings[ref] = apiWarnings - } - if err != nil { - if !errors.IsNotFound(err) && !meta.IsNoMatchError(err) { - return err - } - return nil - } - ret = append(ret, o) - return nil - }) - } - g.Wait() - if g.ErrorOrNil() != nil { - return nil, retApiWarnings, g.ErrorOrNil() - } - - return ret, retApiWarnings, nil -} - type DeleteOptions struct { ForceDryRun bool NoWait bool From 94b68e1ee2bcf7f68e7371aa8963cb17d0d731c7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 Jan 2023 16:06:37 +0100 Subject: [PATCH 0710/2268] fix: Do not abort when errors occur in UpdateRemoteObjects Instead, record errors and show them later. --- pkg/deployment/utils/remote_objects_utils.go | 37 ++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index e9fbd99c2..f18854073 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -40,6 +40,8 @@ func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string] s := status.Start(u.ctx, baseStatus) defer s.Failed() + errCount := 0 + gvks := k.Resources.GetFilteredPreferredGVKs(func(ar *v1.APIResource) bool { return utils.FindStrInSlice(ar.Verbs, "list") != -1 }) @@ -47,26 +49,32 @@ func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string] g := utils.NewGoHelper(u.ctx, 0) for _, gvk := range gvks { gvk := gvk - g.RunE(func() error { + g.Run(func() { l, apiWarnings, err := k.ListObjects(gvk, "", labels) u.dew.AddApiWarnings(k8s2.ObjectRef{GVK: gvk}, apiWarnings) if err != nil { if errors2.IsNotFound(err) { - return nil + return } - return err + u.dew.AddError(k8s2.ObjectRef{GVK: gvk}, err) + errCount += 1 + return } mutex.Lock() defer mutex.Unlock() for _, o := range l { u.remoteObjects[o.GetK8sRef()] = o } - return nil }) } g.Wait() if g.ErrorOrNil() == nil { - s.Success() + if errCount != 0 { + s.UpdateAndInfoFallback("%s: Failed with %d errors", baseStatus, errCount) + s.Warning() + } else { + s.Success() + } } return g.ErrorOrNil() } @@ -86,6 +94,8 @@ func (u *RemoteObjectUtils) getMissingObjects(k *k8s.K8sCluster, refs []k8s2.Obj return nil } + errCount := 0 + baseStatus := fmt.Sprintf("Getting %d additional remote objects", len(notFoundRefsMap)) s := status.Start(u.ctx, baseStatus) defer s.Failed() @@ -93,24 +103,31 @@ func (u *RemoteObjectUtils) getMissingObjects(k *k8s.K8sCluster, refs []k8s2.Obj g := utils.NewGoHelper(u.ctx, 0) for ref, _ := range notFoundRefsMap { ref := ref - g.RunE(func() error { + g.Run(func() { r, apiWarnings, err := k.GetSingleObject(ref) u.dew.AddApiWarnings(ref, apiWarnings) if err != nil { if errors2.IsNotFound(err) { - return nil + return } - return err + u.dew.AddError(ref, err) + errCount += 1 + return } mutex.Lock() defer mutex.Unlock() u.remoteObjects[r.GetK8sRef()] = r - return nil + return }) } g.Wait() if g.ErrorOrNil() == nil { - s.Success() + if errCount != 0 { + s.UpdateAndInfoFallback("%s: Failed with %d errors", baseStatus, errCount) + s.Warning() + } else { + s.Success() + } } return g.ErrorOrNil() } From 46782658385335515e062833aef982901145bf52 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 Jan 2023 17:04:39 +0100 Subject: [PATCH 0711/2268] fix: Properly print errors/warnings without a ref --- cmd/kluctl/commands/command_result.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index bc6bb1ca9..1d43fda5c 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -82,7 +82,11 @@ func prettyObjectRefs(buf io.StringWriter, refs []k8s.ObjectRef) { func prettyErrors(buf io.StringWriter, errors []types.DeploymentError) { for _, e := range errors { - _, _ = buf.WriteString(fmt.Sprintf(" %s: %s\n", e.Ref.String(), e.Error)) + prefix := "" + if s := e.Ref.String(); s != "" { + prefix = fmt.Sprintf("%s: ", s) + } + _, _ = buf.WriteString(fmt.Sprintf(" %s%s\n", prefix, e.Error)) } } From 1a1e63c745f930b9f1c41d5633d7c1001aeca528 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 Jan 2023 17:04:54 +0100 Subject: [PATCH 0712/2268] fix: Don't be too verbose about permission errors --- pkg/deployment/utils/remote_objects_utils.go | 22 +++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index f18854073..cb378acd9 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -41,6 +41,7 @@ func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string] defer s.Failed() errCount := 0 + permissionErrCount := 0 gvks := k.Resources.GetFilteredPreferredGVKs(func(ar *v1.APIResource) bool { return utils.FindStrInSlice(ar.Verbs, "list") != -1 @@ -52,16 +53,20 @@ func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string] g.Run(func() { l, apiWarnings, err := k.ListObjects(gvk, "", labels) u.dew.AddApiWarnings(k8s2.ObjectRef{GVK: gvk}, apiWarnings) + mutex.Lock() + defer mutex.Unlock() if err != nil { if errors2.IsNotFound(err) { return } - u.dew.AddError(k8s2.ObjectRef{GVK: gvk}, err) errCount += 1 + if errors2.IsForbidden(err) || errors2.IsUnauthorized(err) { + permissionErrCount += 1 + return + } + u.dew.AddWarning(k8s2.ObjectRef{GVK: gvk}, err) return } - mutex.Lock() - defer mutex.Unlock() for _, o := range l { u.remoteObjects[o.GetK8sRef()] = o } @@ -72,6 +77,9 @@ func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string] if errCount != 0 { s.UpdateAndInfoFallback("%s: Failed with %d errors", baseStatus, errCount) s.Warning() + if permissionErrCount != 0 { + u.dew.AddWarning(k8s2.ObjectRef{}, fmt.Errorf("at least one permission error was encountered while gathering objects by labels. This might result in orphan object detection to not work properly")) + } } else { s.Success() } @@ -95,6 +103,7 @@ func (u *RemoteObjectUtils) getMissingObjects(k *k8s.K8sCluster, refs []k8s2.Obj } errCount := 0 + permissionErrCount := 0 baseStatus := fmt.Sprintf("Getting %d additional remote objects", len(notFoundRefsMap)) s := status.Start(u.ctx, baseStatus) @@ -110,6 +119,10 @@ func (u *RemoteObjectUtils) getMissingObjects(k *k8s.K8sCluster, refs []k8s2.Obj if errors2.IsNotFound(err) { return } + if errors2.IsForbidden(err) || errors2.IsUnauthorized(err) { + permissionErrCount += 1 + return + } u.dew.AddError(ref, err) errCount += 1 return @@ -125,6 +138,9 @@ func (u *RemoteObjectUtils) getMissingObjects(k *k8s.K8sCluster, refs []k8s2.Obj if errCount != 0 { s.UpdateAndInfoFallback("%s: Failed with %d errors", baseStatus, errCount) s.Warning() + if permissionErrCount != 0 { + u.dew.AddWarning(k8s2.ObjectRef{}, fmt.Errorf("at least one permission error was encountered while gathering known objects. This might result in orphan object detection and diffs to not work properly")) + } } else { s.Success() } From 8fd992bae1a18e06878ed56ab452b6b0a6b66c2a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 Jan 2023 22:41:22 +0100 Subject: [PATCH 0713/2268] tests: Add test for permission errors in RemoteObjectUtils --- .../utils/remote_objects_utils_test.go | 48 ++++++++++++ pkg/k8s/fake_client_factory.go | 74 +++++++++++++++---- pkg/k8s/resources.go | 7 ++ 3 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 pkg/deployment/utils/remote_objects_utils_test.go diff --git a/pkg/deployment/utils/remote_objects_utils_test.go b/pkg/deployment/utils/remote_objects_utils_test.go new file mode 100644 index 000000000..519c8ebf2 --- /dev/null +++ b/pkg/deployment/utils/remote_objects_utils_test.go @@ -0,0 +1,48 @@ +package utils + +import ( + "context" + "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "testing" +) + +func TestRemoteObjectUtils_PermissionErrors(t *testing.T) { + gvr := schema.GroupVersionResource{ + Group: "", + Version: "v1", + Resource: "secrets", + } + expectedErr := errors.NewForbidden(gvr.GroupResource(), "secret", nil) + + secret := &corev1.Secret{ + ObjectMeta: v1.ObjectMeta{Name: "cm1", Namespace: "ns", Labels: map[string]string{"label1": "value1"}}, + Data: map[string][]byte{ + "test": []byte(`test`), + }, + } + + f := k8s.NewFakeClientFactory(secret) + f.AddError(gvr, "", "", expectedErr) + k, err := k8s.NewK8sCluster(context.TODO(), f, false) + if err != nil { + t.Fatal(err) + } + + dew := NewDeploymentErrorsAndWarnings() + u := NewRemoteObjectsUtil(context.Background(), dew) + err = u.UpdateRemoteObjects(k, map[string]string{"l1": "v1"}, []k8s2.ObjectRef{ + k8s2.NewObjectRef("", "v1", "Secret", "secret", "default"), + }) + assert.NoError(t, err) + assert.Equal(t, []types.DeploymentError{{ + Error: "at least one permission error was encountered while gathering objects by labels. This might result in orphan object detection to not work properly"}, + }, dew.GetWarningsList()) + assert.Empty(t, dew.GetErrorsList()) +} diff --git a/pkg/k8s/fake_client_factory.go b/pkg/k8s/fake_client_factory.go index cee966194..6ee62d1e4 100644 --- a/pkg/k8s/fake_client_factory.go +++ b/pkg/k8s/fake_client_factory.go @@ -3,6 +3,7 @@ package k8s import ( v1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -12,15 +13,15 @@ import ( "k8s.io/client-go/kubernetes/fake" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" + "k8s.io/client-go/testing" "sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/yaml" "strings" ) type fakeClientFactory struct { - clientSet *fake.Clientset - objects []runtime.Object - scheme *runtime.Scheme + clientSet *fake.Clientset + dynamicClient *fake_dynamic.FakeDynamicClient } func (f *fakeClientFactory) RESTConfig() *rest.Config { @@ -43,10 +44,10 @@ func (f *fakeClientFactory) CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1I } func (f *fakeClientFactory) DynamicClient(wh rest.WarningHandler) (dynamic.Interface, error) { - return fake_dynamic.NewSimpleDynamicClient(f.scheme, f.objects...), nil + return f.dynamicClient, nil } -func NewFakeClientFactory(objects ...runtime.Object) ClientFactory { +func NewFakeClientFactory(objects ...runtime.Object) *fakeClientFactory { scheme := runtime.NewScheme() _ = v1.AddToScheme(scheme) _ = apiextensionsv1.AddToScheme(scheme) @@ -54,30 +55,77 @@ func NewFakeClientFactory(objects ...runtime.Object) ClientFactory { clientSet.Fake.Resources = ConvertSchemeToAPIResources(scheme) + dynamicClient := fake_dynamic.NewSimpleDynamicClient(scheme, objects...) + return &fakeClientFactory{ - clientSet: clientSet, - objects: objects, - scheme: scheme, + clientSet: clientSet, + dynamicClient: dynamicClient, } } +type HasName interface { + GetName() string +} + +func (f *fakeClientFactory) AddError(gvr schema.GroupVersionResource, name string, namespace string, retErr error) { + f.dynamicClient.PrependReactor("*", gvr.Resource, func(action testing.Action) (handled bool, ret runtime.Object, err error) { + if namespace != "" && namespace != action.GetNamespace() { + return false, nil, nil + } + switch a := action.(type) { + case HasName: + if name != "" && name != a.GetName() { + return false, nil, nil + } + return true, nil, retErr + default: + return true, nil, retErr + } + }) +} + func ConvertSchemeToAPIResources(s *runtime.Scheme) []*metav1.APIResourceList { m := map[schema.GroupVersion][]metav1.APIResource{} - + var allTypes []schema.GroupVersionKind + listTypes := map[schema.GroupVersionKind]bool{} for gvk, _ := range s.AllKnownTypes() { + if gvk.Version == "__internal" { + continue + } + if strings.HasSuffix(gvk.Kind, "List") { + listTypes[gvk] = true + } + allTypes = append(allTypes, gvk) + } + + for _, gvk := range allTypes { + if strings.HasSuffix(gvk.Kind, "List") { + continue + } // we misuse kyaml here n, _ := openapi.IsNamespaceScoped(yaml.TypeMeta{ APIVersion: gvk.GroupVersion().String(), Kind: gvk.Kind, }) + verbs := []string{"delete", "deletecollection", "get", "patch", "create", "update", "watch"} + if listTypes[schema.GroupVersionKind{ + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind + "List", + }] { + verbs = append(verbs, "list") + } + + singularGvr, _ := meta.UnsafeGuessKindToResource(gvk) + ar := metav1.APIResource{ - Name: buildPluralName(gvk.Kind), + Name: singularGvr.Resource, Namespaced: n, Group: gvk.Group, Version: gvk.Version, Kind: gvk.Kind, - Verbs: []string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"}, + Verbs: verbs, } l, _ := m[gvk.GroupVersion()] @@ -95,7 +143,3 @@ func ConvertSchemeToAPIResources(s *runtime.Scheme) []*metav1.APIResourceList { return ret } - -func buildPluralName(n string) string { - return strings.ToLower(n) + "s" -} diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index a6a5aac81..47ce75c2b 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -12,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" + "runtime" "sort" "strings" "sync" @@ -96,6 +97,9 @@ func (k *k8sResources) updateResources() error { } for _, ar := range arl.APIResources { + if ar.Version == "__internal" { + continue + } if strings.Index(ar.Name, "/") != -1 { // skip subresources continue @@ -125,6 +129,9 @@ func (k *k8sResources) updateResources() error { } } } + if len(k.preferredResources) == 0 { + runtime.Breakpoint() + } return nil } From 4a3ecce1c599fcbce0568111616221d852d17690 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 4 Jan 2023 23:01:48 +0100 Subject: [PATCH 0714/2268] tests: Print KUBECONFIG on start of tests --- e2e/default_clusters.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 0d5479d27..b50159416 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -2,6 +2,7 @@ package e2e import "C" import ( + "fmt" "github.com/imdario/mergo" "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/e2e/test_resources" @@ -59,6 +60,8 @@ func init() { mergedKubeconfig = tmpKubeconfig.Name() _ = os.Setenv("KUBECONFIG", mergedKubeconfig) + + _, _ = fmt.Fprintf(os.Stderr, "KUBECONFIG=%s\n", mergedKubeconfig) } func mergeKubeconfig(path string, kubeconfig []byte) { From 404f185a6921328a03b5ecbf61d3273bfac8d6a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Jan 2023 22:10:45 +0000 Subject: [PATCH 0715/2268] chore(deps): Bump golang.org/x/crypto from 0.4.0 to 0.5.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.4.0 to 0.5.0. - [Release notes](https://github.com/golang/crypto/releases) - [Commits](https://github.com/golang/crypto/compare/v0.4.0...v0.5.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 835759cb6..d6909f797 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/stretchr/testify v1.8.1 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3 - golang.org/x/crypto v0.4.0 + golang.org/x/crypto v0.5.0 golang.org/x/net v0.5.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.4.0 diff --git a/go.sum b/go.sum index 39517d07d..842181df8 100644 --- a/go.sum +++ b/go.sum @@ -890,8 +890,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= -golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= 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= From e7fcb90d1e91b03d2bc5abd64a04763d296c42de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aljoscha=20P=C3=B6rtner?= Date: Fri, 6 Jan 2023 17:48:03 +0100 Subject: [PATCH 0716/2268] tests: Add unit tests for the yaml pkg (#241) * test(yaml): add tests for the yaml pkg Signed-off-by: Aljoscha Poertner * test(yaml): switch to temp folder provided by the testing pkg Signed-off-by: Aljoscha Poertner * test(yaml): use assert pkg and fix typos Signed-off-by: Aljoscha Poertner * test(yaml): remove Fs from func name Signed-off-by: Aljoscha Poertner * test(yaml): remove CreateTempFile Signed-off-by: Aljoscha Poertner * test(yaml): remove redundant information in messages Signed-off-by: Aljoscha Poertner * test(yaml): setup test files without use of temp files Signed-off-by: Aljoscha Poertner * test(yaml): use assert.Empty() instead of assert.Nil() Signed-off-by: Aljoscha Poertner * test(yaml): test read with two docs in file Signed-off-by: Aljoscha Poertner * test(yaml): use assert.Equal() with equal object instead of extracting the value Signed-off-by: Aljoscha Poertner Signed-off-by: Aljoscha Poertner --- pkg/yaml/yaml_test.go | 375 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 pkg/yaml/yaml_test.go diff --git a/pkg/yaml/yaml_test.go b/pkg/yaml/yaml_test.go new file mode 100644 index 000000000..bd2d6b772 --- /dev/null +++ b/pkg/yaml/yaml_test.go @@ -0,0 +1,375 @@ +package yaml + +import ( + "bytes" + "errors" + "fmt" + "github.com/stretchr/testify/assert" + "os" + "path/filepath" + "strings" + "testing" + "testing/iotest" +) + +type EmptyYamlConfig struct { +} + +type SimpleYamlConfig struct { + Value string `yaml:"value"` +} + +func TestReadYamlFile(t *testing.T) { + // Setup variables + existingEmptyYamlFileName := "file_existing_empty.yaml" + var existingEmptyYamlConf EmptyYamlConfig + nonExistingEmptyYamlFileName := "non_existing_empty.yaml" + var nonExistingEmptyYamlConf EmptyYamlConfig + + // Setup temporary file + path := filepath.Join(t.TempDir(), existingEmptyYamlFileName) + os.WriteFile(path, nil, 0600) + + //Read existing empty yaml file + existingEmptyYamlErr := ReadYamlFile(path, &existingEmptyYamlConf) + assert.NoError(t, existingEmptyYamlErr, "Can't read empty yaml file: %s", path) + + //Read non-existing empty yaml file + nonExistingEmptyYamlErr := ReadYamlFile(nonExistingEmptyYamlFileName, &nonExistingEmptyYamlConf) + assert.Error(t, nonExistingEmptyYamlErr, "Should throw an error because %s doesn't exist", nonExistingEmptyYamlFileName) +} + +func TestReadYamlAllFile(t *testing.T) { + // Setup variables + twoDocsYamlContent := `value: anyValue1 +--- +value: anyValue2 +` + expectedTwoDocsYaml := []any{ + map[string]any{ + "value": "anyValue1", + }, + map[string]any{ + "value": "anyValue2", + }, + } + existingEmptyYamlFileName := "file_existing_empty.yaml" + existingTwoDocsYamlFileName := "file_existing_two_docs.yaml" + nonExistingEmptyYamlFileName := "non_existing_empty.yaml" + + // Setup temporary file + path := filepath.Join(t.TempDir(), existingEmptyYamlFileName) + os.WriteFile(path, nil, 0600) + + //Read existing empty yaml file + existingEmptyYamlAllFileResult, existingEmptyYamlAllFileErr := ReadYamlAllFile(path) + assert.NoError(t, existingEmptyYamlAllFileErr, "Can't read empty yaml file: %s", path) + assert.Empty(t, existingEmptyYamlAllFileResult, "Empty YAML stream read incorrectly. Value should be empty") + + //Read existing empty yaml file with two documents + path = filepath.Join(t.TempDir(), existingTwoDocsYamlFileName) + os.WriteFile(path, []byte(twoDocsYamlContent), 0600) + + twoDocsYamlAllFileResult, twoDocsYamlAllFileErr := ReadYamlAllFile(path) + assert.NoError(t, twoDocsYamlAllFileErr, "Can't read yaml file: %s", path) + assert.Equal(t, expectedTwoDocsYaml, twoDocsYamlAllFileResult) + + //Read non-existing empty yaml file + nonExistingEmptyYamlAllFileResult, nonExistingEmptyYamlAllFileErr := ReadYamlAllFile(nonExistingEmptyYamlFileName) + assert.Error(t, nonExistingEmptyYamlAllFileErr, "Should throw an error because %s doesn't exist", nonExistingEmptyYamlFileName) + assert.Nil(t, nonExistingEmptyYamlAllFileResult, "Empty YAML stream read incorrectly. Value should be nil because file doesn't exist.") +} + +func TestReadYamlStream(t *testing.T) { + // Setup variables + simpleYamlDataString := ` + value: anyValue + ` + simpleYamlDataReader := bytes.NewReader([]byte(simpleYamlDataString)) + var existingSimpleYamlConf SimpleYamlConfig + + existingReadYamlStreamErr := ReadYamlStream(simpleYamlDataReader, &existingSimpleYamlConf) + assert.NoError(t, existingReadYamlStreamErr, "Can't read simple yaml stream: %s", existingReadYamlStreamErr) + + //Check if it can handle errors + errorReadYamlStreamErr := ReadYamlStream(iotest.ErrReader(errors.New("timeout")), &EmptyYamlConfig{}) + assert.Error(t, errorReadYamlStreamErr, "It should throw an error because of a timeout") +} + +func TestReadYamlString(t *testing.T) { + // Setup variables + simpleYamlDataString := ` + value: anyValue + ` + var existingSimpleYamlConf SimpleYamlConfig + + existingReadYamlStringErr := ReadYamlString(simpleYamlDataString, &existingSimpleYamlConf) + assert.NoError(t, existingReadYamlStringErr, "Can't read simple yaml stream: %s", existingReadYamlStringErr) +} + +func TestReadYamlAllString(t *testing.T) { + // Setup variables + expectedSimpleYamlAllString := []any{ + map[string]any{ + "value": "anyValue", + }, + } + simpleYamlDataString := ` + value: anyValue + ` + simpleYamlAllStringResult, simpleYamlAllStringErr := ReadYamlAllString(simpleYamlDataString) + assert.NoError(t, simpleYamlAllStringErr, "Can't read simple yaml stream: %s", simpleYamlAllStringErr) + + assert.Equal(t, expectedSimpleYamlAllString, simpleYamlAllStringResult, "Simple YAML stream read incorrectly") +} + +func TestReadYamlBytes(t *testing.T) { + // Setup variables + simpleYamlDataString := ` + value: anyValue + ` + var existingSimpleYamlConf SimpleYamlConfig + simpleYamlDataBytes := []byte(simpleYamlDataString) + existingReadYamlBytesErr := ReadYamlBytes(simpleYamlDataBytes, &existingSimpleYamlConf) + assert.NoError(t, existingReadYamlBytesErr, "Can't read simple yaml stream: %s", existingReadYamlBytesErr) +} + +func TestReadYamlAllBytes(t *testing.T) { + // Setup variables + expectedSimpleYamlAllBytes := []any{ + map[string]any{ + "value": "anyValue", + }, + } + simpleYamlDataString := ` + value: anyValue + ` + simpleYamlDataBytes := []byte(simpleYamlDataString) + simpleYamlAllBytesResult, simpleYamlAllBytesErr := ReadYamlAllBytes(simpleYamlDataBytes) + + assert.NoError(t, simpleYamlAllBytesErr, "Can't read simple yaml stream: %s", simpleYamlAllBytesErr) + assert.Equal(t, expectedSimpleYamlAllBytes, simpleYamlAllBytesResult, "Simple YAML stream read incorrectly") +} + +func TestReadYamlAllStream(t *testing.T) { + expectedSimpleYamlAllStream := []any{ + map[string]any{ + "value": "anyValue", + }, + } + // Setup variables + simpleYamlDataReader := bytes.NewReader([]byte("value: 'anyValue'")) + emptyYamlDataReader := bytes.NewReader([]byte("")) + + //Read empty yaml from stream + emptyYamlAllStreamResult, emptyYamlAllStreamResultErr := ReadYamlAllStream(emptyYamlDataReader) + assert.NoError(t, emptyYamlAllStreamResultErr, "Can't read empty yaml stream: %s", emptyYamlAllStreamResultErr) + assert.Nil(t, emptyYamlAllStreamResult, "Empty YAML stream read incorrectly. Value should be nil") + + //Read simple yaml from stream + simpleYamlAllStreamResult, simpleYamlAllStreamResultErr := ReadYamlAllStream(simpleYamlDataReader) + assert.NoError(t, simpleYamlAllStreamResultErr, "Can't read simple yaml stream: %s", simpleYamlAllStreamResultErr) + + assert.Equal(t, expectedSimpleYamlAllStream, simpleYamlAllStreamResult, "Simple YAML stream read incorrectly") + + //Check if it can handle errors + _, errorReadYamlAllStreamErr := ReadYamlAllStream(iotest.ErrReader(errors.New("timeout"))) + assert.Error(t, errorReadYamlAllStreamErr, "It should throw an error because of a timeout") +} + +func TestWriteYamlAllFile(t *testing.T) { + // Setup variables + yamlFileName := "file.yaml" + var yaml []any + yaml = append(yaml, SimpleYamlConfig{ + Value: "anyValue1", + }, SimpleYamlConfig{ + Value: "anyValue2", + }) + expectedString := `value: anyValue1 +--- +value: anyValue2 +` + + // Setup temporary file + path := filepath.Join(t.TempDir(), yamlFileName) + os.WriteFile(path, nil, 0600) + + //Check if writing multiple YAML works + writeYamlAllFileErr := WriteYamlAllFile(path, yaml) + assert.NoError(t, writeYamlAllFileErr, "Error while trying to write YAML to file") + + b, err := os.ReadFile(path) + assert.NoError(t, err, "Error while reading file for evaluation") + assert.Equal(t, expectedString, string(b), "Yaml not written correctly") +} + +func TestWriteYamlFile(t *testing.T) { + // Setup variables + yamlFileName := "file.yaml" + yaml := SimpleYamlConfig{ + Value: "anyValue1", + } + expectedString := `value: anyValue1 +` + + // Setup temporary file + path := filepath.Join(t.TempDir(), yamlFileName) + + //Check if writing a single YAML works + writeYamlFileErr := WriteYamlFile(path, yaml) + assert.NoError(t, writeYamlFileErr, "Error while trying to write YAML to file.") + + b, err := os.ReadFile(path) + assert.NoError(t, err, "Error while reading file for evaluation") + assert.Equal(t, expectedString, string(b), "Yaml not written correctly.") +} + +func TestWriteYamlString(t *testing.T) { + // Setup variables + yaml := SimpleYamlConfig{ + Value: "anyValue1", + } + expectedString := `value: anyValue1 +` + // Write YAML to String + writeYamlStringResult, writeYamlStringErr := WriteYamlString(yaml) + assert.NoError(t, writeYamlStringErr, "Can't write simple yaml string: %s", writeYamlStringErr) + assert.Equal(t, expectedString, writeYamlStringResult, "Yaml not written correctly.") +} + +func TestWriteYamlAllString(t *testing.T) { + // Setup variables + var yaml []any + yaml = append(yaml, SimpleYamlConfig{ + Value: "anyValue1", + }, SimpleYamlConfig{ + Value: "anyValue2", + }) + expectedString := `value: anyValue1 +--- +value: anyValue2 +` + // Write multiple YAML to String + writeYamlAllStringResult, writeYamlAllStringErr := WriteYamlAllString(yaml) + assert.NoError(t, writeYamlAllStringErr, "Can't write simple yaml string: %s", writeYamlAllStringErr) + assert.Equal(t, expectedString, writeYamlAllStringResult, "Yaml not written correctly.") +} + +func TestWriteYamlBytes(t *testing.T) { + // Setup variables + yaml := SimpleYamlConfig{ + Value: "anyValue1", + } + expectedString := `value: anyValue1 +` + expectedBytes := []byte(expectedString) + // Write YAML to bytes + writeYamlBytesResult, writeYamlBytesErr := WriteYamlBytes(yaml) + assert.NoError(t, writeYamlBytesErr, "Can't write simple yaml string: %s", writeYamlBytesErr) + assert.Equal(t, expectedBytes, writeYamlBytesResult, "Yaml not written correctly.") +} + +func TestWriteYamlAllBytes(t *testing.T) { + // Setup variables + var yaml []any + yaml = append(yaml, SimpleYamlConfig{ + Value: "anyValue1", + }, SimpleYamlConfig{ + Value: "anyValue2", + }) + expectedString := `value: anyValue1 +--- +value: anyValue2 +` + expectedBytes := []byte(expectedString) + // Write multiple YAML to bytes + writeYamlAllBytesResult, writeYamlAllBytesErr := WriteYamlAllBytes(yaml) + assert.NoError(t, writeYamlAllBytesErr, "Can't write simple yaml string: %s", writeYamlAllBytesErr) + assert.Equal(t, expectedBytes, writeYamlAllBytesResult, "Yaml not written correctly.") +} + +func TestWriteYamlAllStream(t *testing.T) { + // Setup variables + var yaml []any + yaml = append(yaml, SimpleYamlConfig{ + Value: "anyValue1", + }, SimpleYamlConfig{ + Value: "anyValue2", + }) + expectedString := `value: anyValue1 +--- +value: anyValue2 +` + var buffer bytes.Buffer + // Write multiple YAML to stream + writeYamlAllStreamErr := WriteYamlAllStream(&buffer, yaml) + assert.NoError(t, writeYamlAllStreamErr, "Can't write simple yaml string: %s", writeYamlAllStreamErr) + assert.Equal(t, expectedString, buffer.String(), "Yaml not written correctly.") +} + +func TestConvertYamlToJson(t *testing.T) { + yaml := `value: anyValue1` + expectedJson := `{"value":"anyValue1"}` + yamlBytes := []byte(yaml) + expectedJsonBytes := []byte(expectedJson) + jsonBytes, convertYamlToJsonErr := ConvertYamlToJson(yamlBytes) + assert.NoError(t, convertYamlToJsonErr, "Can't convert yaml to json: %s", convertYamlToJsonErr) + assert.Equal(t, expectedJsonBytes, jsonBytes, "Yaml not converted correctly.") +} + +func TestWriteJsonString(t *testing.T) { + yaml := SimpleYamlConfig{ + Value: "anyValue1", + } + expectedJson := `{"value":"anyValue1"}` + writeJsonStringResult, writeJsonStringErr := WriteJsonString(yaml) + assert.NoError(t, writeJsonStringErr, "Can't write yaml to json string: %s", writeJsonStringErr) + assert.Equal(t, expectedJson, writeJsonStringResult, "Yaml not converted correctly.") +} + +func TestRemoveDuplicateFields(t *testing.T) { + // Check if duplicate field gets removed + duplicateYaml := `value: anyValue1 +value: anyValue2 +` + expectedDuplicateYaml := `value: anyValue2 +` + expectedDuplicateYamlBytes := []byte(expectedDuplicateYaml) + duplicateFieldYamlDataReader := bytes.NewReader([]byte(duplicateYaml)) + removeDuplicateFieldsResult, removeDuplicateFieldsErr := RemoveDuplicateFields(duplicateFieldYamlDataReader) + assert.NoError(t, removeDuplicateFieldsErr, "Can't remove duplicate fields: %s", removeDuplicateFieldsErr) + assert.Equal(t, expectedDuplicateYamlBytes, removeDuplicateFieldsResult, "Duplicate fields not removed correctly.") + + // Check if non-duplicate fields are untouched + nonDuplicateYaml := `value1: anyValue +value2: anyValue +` + expectedNonDuplicateYaml := `value1: anyValue +value2: anyValue +` + expectedNonDuplicateYamlBytes := []byte(expectedNonDuplicateYaml) + + nonDuplicateFieldYamlDataReader := bytes.NewReader([]byte(nonDuplicateYaml)) + removeNonDuplicateFieldsResult, removeNonDuplicateFieldsErr := RemoveDuplicateFields(nonDuplicateFieldYamlDataReader) + assert.NoError(t, removeNonDuplicateFieldsErr, "Can't remove duplicate fields: %s", removeNonDuplicateFieldsErr) + assert.Equal(t, expectedNonDuplicateYamlBytes, removeNonDuplicateFieldsResult, "Duplicate fields not removed correctly.") +} + +func TestFixPathExt(t *testing.T) { + // Check if *.yaml gets converted + path := filepath.Join(t.TempDir(), "fix_path_ext.yml") + os.WriteFile(path, nil, 0600) + + yamlFileName := fmt.Sprintf("%s.yaml", strings.TrimSuffix(path, filepath.Ext(path))) + fixedYamlFileName := FixPathExt(yamlFileName) + assert.Equal(t, path, fixedYamlFileName, "Fix of path extension failed!") + + // Check if *.yml gets converted + path = filepath.Join(t.TempDir(), "fix_path_ext.yaml") + os.WriteFile(path, nil, 0600) + + ymlFileName := fmt.Sprintf("%s.yml", strings.TrimSuffix(path, filepath.Ext(path))) + fixedYmlFileName := FixPathExt(ymlFileName) + assert.Equal(t, path, fixedYmlFileName, "Fix of path extension failed!") +} From 6160c4a1c604630af9c252234a0981b664ed6bf5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 Jan 2023 09:21:55 +0100 Subject: [PATCH 0717/2268] fix: Don't crash when encountering nil maps in SetNestedField --- pkg/utils/uo/nested_fields.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/uo/nested_fields.go b/pkg/utils/uo/nested_fields.go index b3927dc9d..7d3ce0afd 100644 --- a/pkg/utils/uo/nested_fields.go +++ b/pkg/utils/uo/nested_fields.go @@ -28,7 +28,7 @@ func (uo *UnstructuredObject) SetNestedField(value interface{}, keys ...interfac if err != nil { return err } - if ok { + if ok && val != nil { o = val } else { newVal := make(map[string]interface{}) From 74ccf49860080e01aa028c746c7b60a6009a2c10 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 12 Jan 2023 09:41:00 +0100 Subject: [PATCH 0718/2268] fix: Only query for known GroupKinds when calling validate This should remove some load to the apiserver when validate is used periodically (like in the Kluctl Controller). --- pkg/deployment/commands/delete.go | 2 +- pkg/deployment/commands/deploy.go | 2 +- pkg/deployment/commands/diff.go | 2 +- pkg/deployment/commands/poke_images.go | 2 +- pkg/deployment/commands/prune.go | 2 +- pkg/deployment/commands/validate.go | 2 +- pkg/deployment/utils/remote_objects_utils.go | 24 ++++++++++++++++--- .../utils/remote_objects_utils_test.go | 2 +- 8 files changed, 28 insertions(+), 10 deletions(-) diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index e32ae759f..6a3040cda 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -42,7 +42,7 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.Ob inclusion = cmd.c.Inclusion } - err := ru.UpdateRemoteObjects(k, labels, nil) + err := ru.UpdateRemoteObjects(k, labels, nil, false) if err != nil { return nil, err } diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index c82a6a303..bbf4284dc 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -30,7 +30,7 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult dew := utils2.NewDeploymentErrorsAndWarnings() ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs()) + err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs(), false) if err != nil { return nil, err } diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index dd811a84a..890908fc0 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -29,7 +29,7 @@ func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.Comm dew := utils.NewDeploymentErrorsAndWarnings() ru := utils.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs()) + err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs(), false) if err != nil { return nil, err } diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 3bcaaef36..3c99172d1 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -28,7 +28,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type dew := utils2.NewDeploymentErrorsAndWarnings() ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs()) + err := ru.UpdateRemoteObjects(k, nil, cmd.c.LocalObjectRefs(), false) if err != nil { return nil, err } diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index bc2336e74..e3b5f4689 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -22,7 +22,7 @@ func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.Obj dew := utils2.NewDeploymentErrorsAndWarnings() ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), nil) + err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), nil, false) if err != nil { return nil, err } diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index a5781f855..010fa9868 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -31,7 +31,7 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types. cmd.dew.Init() - err := cmd.ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs()) + err := cmd.ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs(), true) if err != nil { return nil, err } diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index cb378acd9..1ff37177f 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -30,7 +30,7 @@ func NewRemoteObjectsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnings) } } -func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string]string) error { +func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string]string, onlyUsedGKs map[schema.GroupKind]bool) error { var mutex sync.Mutex if len(labels) == 0 { return nil @@ -44,6 +44,15 @@ func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string] permissionErrCount := 0 gvks := k.Resources.GetFilteredPreferredGVKs(func(ar *v1.APIResource) bool { + if onlyUsedGKs != nil { + gk := schema.GroupKind{ + Group: ar.Group, + Kind: ar.Kind, + } + if !onlyUsedGKs[gk] { + return false + } + } return utils.FindStrInSlice(ar.Verbs, "list") != -1 }) @@ -148,12 +157,21 @@ func (u *RemoteObjectUtils) getMissingObjects(k *k8s.K8sCluster, refs []k8s2.Obj return g.ErrorOrNil() } -func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[string]string, refs []k8s2.ObjectRef) error { +func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[string]string, refs []k8s2.ObjectRef, onlyUsedGKs bool) error { if k == nil { return nil } - err := u.getAllByLabels(k, labels) + var usedGKs map[schema.GroupKind]bool + + if onlyUsedGKs { + usedGKs = map[schema.GroupKind]bool{} + for _, ref := range refs { + usedGKs[ref.GVK.GroupKind()] = true + } + } + + err := u.getAllByLabels(k, labels, usedGKs) if err != nil { return err } diff --git a/pkg/deployment/utils/remote_objects_utils_test.go b/pkg/deployment/utils/remote_objects_utils_test.go index 519c8ebf2..aaa1671a2 100644 --- a/pkg/deployment/utils/remote_objects_utils_test.go +++ b/pkg/deployment/utils/remote_objects_utils_test.go @@ -39,7 +39,7 @@ func TestRemoteObjectUtils_PermissionErrors(t *testing.T) { u := NewRemoteObjectsUtil(context.Background(), dew) err = u.UpdateRemoteObjects(k, map[string]string{"l1": "v1"}, []k8s2.ObjectRef{ k8s2.NewObjectRef("", "v1", "Secret", "secret", "default"), - }) + }, false) assert.NoError(t, err) assert.Equal(t, []types.DeploymentError{{ Error: "at least one permission error was encountered while gathering objects by labels. This might result in orphan object detection to not work properly"}, From 8a599de21ef6c3d45749855a302fba641124df22 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 13 Jan 2023 08:54:49 +0100 Subject: [PATCH 0719/2268] Revert "chore: Download kubebuilder-tools for windows from kubebuilder-tools-releases-windows repo" This reverts commit 44982e64a4a974aa27b3ff5f5250043c7f992521. --- Makefile | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index dcae8fd0d..69a9805ac 100644 --- a/Makefile +++ b/Makefile @@ -64,21 +64,11 @@ setup-envtest: ## Download envtest-setup locally if necessary. $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) # Download the envtest binaries to testbin -ENVTEST_KUBERNETES_VERSION?=1.25.0 ENVTEST_ASSETS_DIR=$(BUILD_DIR)/testbin - -ifeq ($(OS),Windows_NT) -ENVTEST_ASSETS_DIR_WINDOWS=$(ENVTEST_ASSETS_DIR)/k8s/$(ENVTEST_KUBERNETES_VERSION)-windows-amd64 -install-envtest: setup-envtest $(ENVTEST_ASSETS_DIR_WINDOWS)/etcd.exe -$(ENVTEST_ASSETS_DIR_WINDOWS)/etcd.exe: - mkdir -p $(ENVTEST_ASSETS_DIR_WINDOWS) - curl -o $(ENVTEST_ASSETS_DIR_WINDOWS)/kubebuilder-tools.tar.gz "https://raw.githubusercontent.com/kluctl/kubebuilder-tools-releases-windows/main/releases/kubebuilder-tools-$(ENVTEST_KUBERNETES_VERSION)_windows_amd64.tar.gz" - cd $(ENVTEST_ASSETS_DIR_WINDOWS) && tar xzf kubebuilder-tools.tar.gz && mv kubebuilder/bin/* . - rm $(ENVTEST_ASSETS_DIR_WINDOWS)/kubebuilder-tools.tar.gz -else +ENVTEST_KUBERNETES_VERSION?=latest install-envtest: setup-envtest + mkdir -p ${ENVTEST_ASSETS_DIR} $(ENVTEST) use $(ENVTEST_KUBERNETES_VERSION) --arch=$(ENVTEST_ARCH) --bin-dir=$(ENVTEST_ASSETS_DIR) -endif ## Test: test: test-unit test-e2e ## Runs the complete test suite From 1253253ab097cd2f1aa11b3b0a35e5bc44dcdefb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 13 Jan 2023 10:36:36 +0100 Subject: [PATCH 0720/2268] fix: findCommit did not account for tags --- pkg/repocache/cache.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/repocache/cache.go b/pkg/repocache/cache.go index 9d9e70b32..2d5dd519a 100644 --- a/pkg/repocache/cache.go +++ b/pkg/repocache/cache.go @@ -158,13 +158,14 @@ func (e *CacheEntry) GetRepoInfo() RepoInfo { } func (e *CacheEntry) findCommit(ref string) (string, string, error) { - if strings.HasPrefix(ref, "refs/heads") { + switch { + case strings.HasPrefix(ref, "refs/heads"), strings.HasPrefix(ref, "refs/tags"): c, ok := e.refs[ref] if !ok { return "", "", fmt.Errorf("ref %s not found", ref) } return ref, c, nil - } else { + default: ref2 := "refs/heads/" + ref c, ok := e.refs[ref2] if ok { From 712804cd02a6f3291bb43d0c3e3c5b1495f0a4d1 Mon Sep 17 00:00:00 2001 From: Aljoscha Poertner Date: Thu, 19 Jan 2023 17:09:25 +0100 Subject: [PATCH 0721/2268] refactor(vars): use aws arn parser when region is nil Signed-off-by: Aljoscha Poertner --- pkg/vars/aws/secrets_manager.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/vars/aws/secrets_manager.go b/pkg/vars/aws/secrets_manager.go index d3cced231..b258e2c5d 100644 --- a/pkg/vars/aws/secrets_manager.go +++ b/pkg/vars/aws/secrets_manager.go @@ -3,12 +3,13 @@ package aws import ( "context" "fmt" + arn2 "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" ) func GetAwsSecretsManagerSecret(ctx context.Context, aws AwsClientFactory, profile *string, region *string, secretName string) (string, error) { if region == nil { - arn, err := ParseArn(secretName) + arn, err := arn2.Parse(secretName) if err != nil { return "", fmt.Errorf("when omitting the AWS region, the secret name must be a valid ARN") } From ec62c3175ad8203b9373e6cb5e0faca0c1833b2c Mon Sep 17 00:00:00 2001 From: Aljoscha Poertner Date: Thu, 19 Jan 2023 17:11:01 +0100 Subject: [PATCH 0722/2268] refactor(vars): extract splitting of resource to a separate method Signed-off-by: Aljoscha Poertner --- pkg/vars/aws/arn.go | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/pkg/vars/aws/arn.go b/pkg/vars/aws/arn.go index 3b7141e99..8fafed9ec 100644 --- a/pkg/vars/aws/arn.go +++ b/pkg/vars/aws/arn.go @@ -1,42 +1,24 @@ package aws import ( - "fmt" "strings" ) -type Arn struct { - Arn string - Partition string - Service string - Region string - Account string - Resource string +type Resource struct { + ResourceId string ResourceType string } -func ParseArn(arn string) (Arn, error) { - // http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html - elements := strings.SplitN(arn, ":", 6) - if len(elements) < 6 { - return Arn{}, fmt.Errorf("%s is not a valid arn", arn) - } - var result Arn - result.Arn = elements[0] - result.Partition = elements[1] - result.Service = elements[2] - result.Region = elements[3] - result.Account = elements[4] - result.Resource = elements[5] - - if strings.Index(result.Resource, "/") != -1 { - s := strings.SplitN(result.Resource, "/", 2) +func SplitResource(resource string) (Resource, error) { + result := Resource{resource, ""} + if strings.Index(resource, "/") != -1 { + s := strings.SplitN(resource, "/", 2) result.ResourceType = s[0] - result.Resource = s[1] - } else if strings.Index(result.Resource, ":") != -1 { - s := strings.SplitN(result.Resource, ":", 2) + result.ResourceId = s[1] + } else if strings.Index(resource, ":") != -1 { + s := strings.SplitN(resource, ":", 2) result.ResourceType = s[0] - result.Resource = s[1] + result.ResourceId = s[1] } return result, nil } From 7ff58f8bd9a28b45b72f2394988c92ce0f782ef5 Mon Sep 17 00:00:00 2001 From: Aljoscha Poertner Date: Thu, 19 Jan 2023 17:12:10 +0100 Subject: [PATCH 0723/2268] refactor(vars): use aws arn parser in FakeAwsClientFactory Signed-off-by: Aljoscha Poertner --- pkg/vars/aws/fake_clientfactory.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/vars/aws/fake_clientfactory.go b/pkg/vars/aws/fake_clientfactory.go index 18a170338..45859a5e1 100644 --- a/pkg/vars/aws/fake_clientfactory.go +++ b/pkg/vars/aws/fake_clientfactory.go @@ -3,6 +3,7 @@ package aws import ( "context" "fmt" + arn2 "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" ) @@ -14,16 +15,18 @@ type FakeAwsClientFactory struct { } func (f *FakeAwsClientFactory) GetSecretValue(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) { - name := *params.SecretId - arn, err := ParseArn(*params.SecretId) + var arnResource Resource + arn, err := arn2.Parse(*params.SecretId) if err == nil { - name = arn.Resource + arnResource, _ = SplitResource(arn.Resource) + } else { + arnResource, _ = SplitResource(*params.SecretId) } - s, ok := f.Secrets[name] + s, ok := f.Secrets[arnResource.ResourceId] if ok { return &secretsmanager.GetSecretValueOutput{ - Name: &name, + Name: &arnResource.ResourceId, SecretString: &s, }, nil } From 4bba601909ba0a36dd34d5b27b3285039fbb14f7 Mon Sep 17 00:00:00 2001 From: Petr Michalec Date: Fri, 3 Feb 2023 14:21:44 +0100 Subject: [PATCH 0724/2268] feat: Implement --local-git-group-override with group match on repo path (#273) * improve local-git-override * mid-work-updates from review * mid-work-updates from review * refactor: Move clonded dir calculation to the top * feat: Implement logic to filter for git group overrides * update documentation * update docs, revert subDir validation * revert ValidateGitProject changes * update command description --------- Co-authored-by: Petr Michalec Co-authored-by: Alexander Block --- cmd/kluctl/args/project.go | 3 +- cmd/kluctl/commands/utils.go | 30 +++-- docs/reference/commands/common-arguments.md | 73 +++++----- pkg/helm/helm_release.go | 7 +- pkg/repocache/cache.go | 140 +++++++++++++------- 5 files changed, 159 insertions(+), 94 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index eff3ec1f0..c14455303 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -27,7 +27,8 @@ type ProjectFlags struct { Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` - LocalGitOverride []string `group:"project" help:"Specify a local git override in the form of 'github.com:my-org/my-repo=/local/path/to/override'. This will cause kluctl to not use git to clone for the specified repository but instead use the local directory. This is useful in case you need to test out changes in external git repositories without pushing them. To only override a single branch of the repo, use 'github.com:my-org/my-repo:my-branch=/local/path/to/override'"` + LocalGitOverride []string `group:"project" help:"Specify a single repository local git override in the form of 'github.com:my-org/my-repo=/local/path/to/override'. This will cause kluctl to not use git to clone for the specified repository but instead use the local directory. This is useful in case you need to test out changes in external git repositories without pushing them. To only override a single branch of the repo, use 'github.com:my-org/my-repo:my-branch=/local/path/to/override'"` + LocalGitGroupOverride []string `group:"project" help:"Same as --local-git-override, but for a whole group prefix instead of a single repository. All repositories that have the given prefix will be overridden with the given local path and the repository suffix appended. For example, 'gitlab.com:some-org/sub-org=/local/path/to/my-forks' will override all repositories below 'gitlab.com:some-org/sub-org/' with the repositories found in '/local/path/to/my-forks'. It will however only perform an override if the given repository actually exists locally and otherwise revert to the actual (non-overridden) repository."` } type ArgsFlags struct { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index a209d2829..63da52161 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -3,6 +3,9 @@ package commands import ( "context" "fmt" + "os" + "strings" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/git" @@ -20,8 +23,6 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" - "os" - "strings" ) func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { @@ -54,9 +55,16 @@ func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFla var repoOverrides []repocache.RepoOverride for _, x := range projectFlags.LocalGitOverride { - ro, err := parseRepoOverride(x) + ro, err := parseRepoOverride(x, false) if err != nil { - return err + return fmt.Errorf("invalid --local-git-override: %w", err) + } + repoOverrides = append(repoOverrides, ro) + } + for _, x := range projectFlags.LocalGitGroupOverride { + ro, err := parseRepoOverride(x, true) + if err != nil { + return fmt.Errorf("invalid --local-git-group-override: %w", err) } repoOverrides = append(repoOverrides, ro) } @@ -226,23 +234,25 @@ func clientConfigGetter(forCompletion bool) func(context *string) (*rest.Config, } } -func parseRepoOverride(s string) (ret repocache.RepoOverride, err error) { +func parseRepoOverride(s string, isGroup bool) (ret repocache.RepoOverride, err error) { + ret.IsGroup = isGroup + sp := strings.SplitN(s, "=", 2) if len(sp) != 2 { - return repocache.RepoOverride{}, fmt.Errorf("invalid --local-git-override %s", s) + return repocache.RepoOverride{}, fmt.Errorf("%s", s) } sp2 := strings.Split(sp[0], ":") if len(sp2) < 2 || len(sp2) > 3 { - return repocache.RepoOverride{}, fmt.Errorf("invalid --local-git-override %s", s) + return repocache.RepoOverride{}, fmt.Errorf("%s", s) } u, err := git_url.Parse(fmt.Sprintf("%s:%s", sp2[0], sp2[1])) if err != nil { - return repocache.RepoOverride{}, fmt.Errorf("invalid --local-git-override %s: %w", s, err) + return repocache.RepoOverride{}, fmt.Errorf("%s: %w", s, err) } - - ret.RepoKey = u.NormalizedRepoKey() + u = u.Normalize() + ret.RepoUrl = *u if len(sp2) == 3 { ret.Ref = sp2[2] } diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index 0f3150151..ac7f3d69a 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -39,37 +39,48 @@ They control where and how to load the kluctl project and deployment project. Project arguments: Define where and how to load the kluctl project and its components from. - -a, --arg stringArray Passes a template argument in the form of name=value. Nested args can - be set with the '-a my.nested.arg=value' syntax. Values are - interpreted as yaml values, meaning that 'true' and 'false' will lead - to boolean values and numbers will be treated as numbers. Use quotes - if you want these to be treated as strings. If the value starts with - @, it is treated as a file, meaning that the contents of the file - will be loaded and treated as yaml. - --args-from-file stringArray Loads a yaml file and makes it available as arguments, meaning that - they will be available thought the global 'args' variable. - --context string Overrides the context name specified in the target. If the selected - target does not specify a context or the no-name target is used, - --context will override the currently active context. - --git-cache-update-interval duration Specify the time to wait between git cache updates. Defaults to not - wait at all and always updating caches. - --local-git-override stringArray Specify a local git override in the form of - 'github.com:my-org/my-repo=/local/path/to/override'. This will cause - kluctl to not use git to clone for the specified repository but - instead use the local directory. This is useful in case you need to - test out changes in external git repositories without pushing them. - To only override a single branch of the repo, use - 'github.com:my-org/my-repo:my-branch=/local/path/to/override' - -c, --project-config existingfile Location of the .kluctl.yaml config file. Defaults to - $PROJECT/.kluctl.yaml - --project-dir existingdir Specify the project directory. Defaults to the current working directory. - -t, --target string Target name to run command for. Target must exist in .kluctl.yaml. - -T, --target-name-override string Overrides the target name. If -t is used at the same time, then the - target will be looked up based on -t and then renamed to the - value of -T. If no target is specified via -t, then the no-name - target is renamed to the value of -T. - --timeout duration Specify timeout for all operations, including loading of the project, - all external api calls and waiting for readiness. (default 10m0s) + -a, --arg stringArray Passes a template argument in the form of name=value. Nested args + can be set with the '-a my.nested.arg=value' syntax. Values are + interpreted as yaml values, meaning that 'true' and 'false' will + lead to boolean values and numbers will be treated as numbers. Use + quotes if you want these to be treated as strings. If the value + starts with @, it is treated as a file, meaning that the contents + of the file will be loaded and treated as yaml. + --args-from-file stringArray Loads a yaml file and makes it available as arguments, meaning that + they will be available thought the global 'args' variable. + --context string Overrides the context name specified in the target. If the selected + target does not specify a context or the no-name target is used, + --context will override the currently active context. + --git-cache-update-interval duration Specify the time to wait between git cache updates. Defaults to not + wait at all and always updating caches. + --local-git-group-override stringArray Same as --local-git-override, but for a whole group prefix instead + of a single repository. All repositories that have the given prefix + will be overridden with the given local path and the repository + suffix appended. For example, + 'gitlab.com:some-org/sub-org=/local/path/to/my-forks' will override + all repositories below 'gitlab.com:some-org/sub-org/' with the + repositories found in '/local/path/to/my-forks'. It will however + only perform an override if the given repository actually exists + locally and otherwise revert to the actual (non-overridden) repository. + --local-git-override stringArray Specify a single repository local git override in the form of + 'github.com:my-org/my-repo=/local/path/to/override'. This will + cause kluctl to not use git to clone for the specified repository + but instead use the local directory. This is useful in case you + need to test out changes in external git repositories without + pushing them. To only override a single branch of the repo, use + 'github.com:my-org/my-repo:my-branch=/local/path/to/override' + -c, --project-config existingfile Location of the .kluctl.yaml config file. Defaults to + $PROJECT/.kluctl.yaml + --project-dir existingdir Specify the project directory. Defaults to the current working + directory. + -t, --target string Target name to run command for. Target must exist in .kluctl.yaml. + -T, --target-name-override string Overrides the target name. If -t is used at the same time, then the + target will be looked up based on -t and then renamed to the + value of -T. If no target is specified via -t, then the no-name + target is renamed to the value of -T. + --timeout duration Specify timeout for all operations, including loading of the + project, all external api calls and waiting for readiness. (default + 10m0s) ``` diff --git a/pkg/helm/helm_release.go b/pkg/helm/helm_release.go index efdaba29a..7012e00f5 100644 --- a/pkg/helm/helm_release.go +++ b/pkg/helm/helm_release.go @@ -3,6 +3,10 @@ package helm import ( "context" "fmt" + "os" + "path/filepath" + "strings" + "github.com/Masterminds/semver/v3" securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -23,9 +27,6 @@ import ( "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/release" "k8s.io/apimachinery/pkg/runtime/schema" - "os" - "path/filepath" - "strings" ) type Release struct { diff --git a/pkg/repocache/cache.go b/pkg/repocache/cache.go index 2d5dd519a..131567488 100644 --- a/pkg/repocache/cache.go +++ b/pkg/repocache/cache.go @@ -3,6 +3,13 @@ package repocache import ( "context" "fmt" + "os" + "path" + "path/filepath" + "strings" + "sync" + "time" + "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" @@ -10,12 +17,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" cp "github.com/otiai10/copy" - "os" - "path" - "path/filepath" - "strings" - "sync" - "time" ) type GitRepoCache struct { @@ -35,12 +36,14 @@ type GitRepoCache struct { type CacheEntry struct { rp *GitRepoCache + url git_url.GitUrl mr *git.MirroredGitRepo defaultRef string refs map[string]string - clonedDirs map[string]clonedDir - updateMutex sync.Mutex + clonedDirs map[string]clonedDir + updateMutex sync.Mutex + overridePath string } type RepoInfo struct { @@ -50,9 +53,10 @@ type RepoInfo struct { } type RepoOverride struct { - RepoKey string + RepoUrl git_url.GitUrl Ref string Override string + IsGroup bool } type clonedDir struct { @@ -85,7 +89,52 @@ func (rp *GitRepoCache) GetEntry(url git_url.GitUrl) (*CacheEntry, error) { rp.reposMutex.Lock() defer rp.reposMutex.Unlock() - e, ok := rp.repos[url.NormalizedRepoKey()] + urlN := url.Normalize() + repoKey := url.NormalizedRepoKey() + + // evaluate overrides + for _, ro := range rp.repoOverrides { + if ro.RepoUrl.Host != urlN.Host { + continue + } + + var overridePath string + if ro.IsGroup { + if !strings.HasPrefix(urlN.Path, ro.RepoUrl.Path+"/") { + continue + } + relPath := strings.TrimPrefix(urlN.Path, ro.RepoUrl.Path+"/") + overridePath = path.Join(ro.Override, relPath) + } else { + if ro.Override != urlN.Path { + continue + } + overridePath = ro.Override + } + + if st, err := os.Stat(overridePath); err != nil { + if os.IsNotExist(err) { + continue + } + return nil, fmt.Errorf("can not override repo %s with %s: %w", url.String(), overridePath, err) + } else if !st.IsDir() { + return nil, fmt.Errorf("can not override repo %s. %s is not a directory", url.String(), overridePath) + } + + status.WarningOnce(rp.ctx, fmt.Sprintf("git-override-%s", repoKey), "Overriding git repo %s with local directory %s", url.String(), overridePath) + + e := &CacheEntry{ + rp: rp, + url: url, + mr: nil, // mark as overridden + clonedDirs: map[string]clonedDir{}, + overridePath: overridePath, + } + rp.repos[repoKey] = e + return e, nil + } + + e, ok := rp.repos[repoKey] if !ok { mr, err := git.NewMirroredGitRepo(rp.ctx, url, filepath.Join(utils.GetTmpBaseDir(rp.ctx), "git-cache"), rp.sshPool, rp.authProviders) if err != nil { @@ -93,10 +142,11 @@ func (rp *GitRepoCache) GetEntry(url git_url.GitUrl) (*CacheEntry, error) { } e = &CacheEntry{ rp: rp, + url: url, mr: mr, clonedDirs: map[string]clonedDir{}, } - rp.repos[url.NormalizedRepoKey()] = e + rp.repos[repoKey] = e } err := e.Update() if err != nil { @@ -109,6 +159,10 @@ func (e *CacheEntry) Update() error { e.updateMutex.Lock() defer e.updateMutex.Unlock() + if e.mr == nil { + return nil + } + err := e.mr.Lock() if err != nil { return err @@ -149,7 +203,7 @@ func (e *CacheEntry) GetRepoInfo() RepoInfo { defer e.updateMutex.Unlock() info := RepoInfo{ - Url: e.mr.Url(), + Url: e.url, RemoteRefs: e.refs, DefaultRef: e.defaultRef, } @@ -184,28 +238,13 @@ func (e *CacheEntry) GetClonedDir(ref string) (string, git.CheckoutInfo, error) e.updateMutex.Lock() defer e.updateMutex.Unlock() - err := e.mr.Lock() - if err != nil { - return "", git.CheckoutInfo{}, err - } - defer e.mr.Unlock() - - if ref == "" { - ref = e.defaultRef - } - - ref2, commit, err := e.findCommit(ref) - if err != nil { - return "", git.CheckoutInfo{}, err - } - tmpDir := filepath.Join(utils.GetTmpBaseDir(e.rp.ctx), "git-cloned") - err = os.MkdirAll(tmpDir, 0700) + err := os.MkdirAll(tmpDir, 0700) if err != nil { return "", git.CheckoutInfo{}, err } - url := e.mr.Url() + url := e.url repoName := path.Base(url.Normalize().Path) + "-" if ref == "" { repoName += "HEAD-" @@ -223,29 +262,32 @@ func (e *CacheEntry) GetClonedDir(ref string) (string, git.CheckoutInfo, error) e.rp.cleanupDirs = append(e.rp.cleanupDirs, p) e.rp.cleeanupDirsMutex.Unlock() - var foundRo *RepoOverride - for _, ro := range e.rp.repoOverrides { - u := e.mr.Url() - if ro.RepoKey == u.NormalizedRepoKey() { - if ro.Ref == "" || strings.HasSuffix(ref2, "/"+ro.Ref) { - foundRo = &ro - break - } - } - } - - if foundRo != nil { - u := e.mr.Url() - status.WarningOnce(e.rp.ctx, fmt.Sprintf("git-override-%s|%s", foundRo.RepoKey, foundRo.Ref), "Overriding git repo %s with local directory %s", u.String(), foundRo.Override) - err = cp.Copy(foundRo.Override, p) - if err != nil { - return "", git.CheckoutInfo{}, err - } - } else { - err = e.mr.CloneProjectByCommit(commit, p) + if e.mr == nil { // local override exist + err = cp.Copy(e.overridePath, p) if err != nil { return "", git.CheckoutInfo{}, err } + return p, git.CheckoutInfo{}, err + } + + err = e.mr.Lock() + if err != nil { + return "", git.CheckoutInfo{}, err + } + defer e.mr.Unlock() + + if ref == "" { + ref = e.defaultRef + } + + ref2, commit, err := e.findCommit(ref) + if err != nil { + return "", git.CheckoutInfo{}, err + } + + err = e.mr.CloneProjectByCommit(commit, p) + if err != nil { + return "", git.CheckoutInfo{}, err } repoInfo, err := git.GetCheckoutInfo(p) From e7ac7c102c6ee8482214c4daf3a8b13972485154 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Feb 2023 14:34:46 +0100 Subject: [PATCH 0725/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/service/secretsmanager (#271) Bumps [github.com/aws/aws-sdk-go-v2/service/secretsmanager](https://github.com/aws/aws-sdk-go-v2) from 1.17.0 to 1.18.2. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.17.0...config/v1.18.2) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/secretsmanager dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index d6909f797..e8c0f20ef 100644 --- a/go.mod +++ b/go.mod @@ -58,8 +58,9 @@ require ( require ( filippo.io/age v1.1.1 + github.com/aws/aws-sdk-go-v2 v1.17.3 github.com/aws/aws-sdk-go-v2/config v1.18.7 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.17.0 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 @@ -94,7 +95,6 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.7 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect diff --git a/go.sum b/go.sum index 842181df8..448ad7a51 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViS github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.17.0 h1:6W6BLZcXytRJsVvc2gGwxKE4wbMSlWqdxZivBP/E+ys= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.17.0/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2 h1:QDVKb2VpuwzIslzshumxksayV5GkpqT+rkVvdPVrA9E= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 h1:gItLq3zBYyRDPmqAClgzTH8PBjDQGeyptYGHIwtYYNA= github.com/aws/aws-sdk-go-v2/service/sso v1.11.28/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 h1:KCacyVSs/wlcPGx37hcbT3IGYO8P8Jx+TgSDhAXtQMY= From 4710e347f8d9881be982cf742f0371500d755492 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Feb 2023 14:35:29 +0100 Subject: [PATCH 0726/2268] chore(deps): Bump github.com/ohler55/ojg from 1.16.0 to 1.17.3 (#274) Bumps [github.com/ohler55/ojg](https://github.com/ohler55/ojg) from 1.16.0 to 1.17.3. - [Release notes](https://github.com/ohler55/ojg/releases) - [Changelog](https://github.com/ohler55/ojg/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/ojg/compare/v1.16.0...v1.17.3) --- updated-dependencies: - dependency-name: github.com/ohler55/ojg dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e8c0f20ef..20d85c018 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.16.0 + github.com/ohler55/ojg v1.17.3 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.9.0 diff --git a/go.sum b/go.sum index 448ad7a51..abaf6b2b5 100644 --- a/go.sum +++ b/go.sum @@ -661,8 +661,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/ohler55/ojg v1.16.0 h1:JQMJp/ygkak1YiGFB2ohbks2Y6BAE061i5cq5fECiVA= -github.com/ohler55/ojg v1.16.0/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= +github.com/ohler55/ojg v1.17.3 h1:NsyfSN+GScWFzXZCVuVimK0xOqUEqTSwuZ+J1WA50nw= +github.com/ohler55/ojg v1.17.3/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= From 94acf99a3118c088866869b564a10d44f7da6072 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Feb 2023 14:40:36 +0100 Subject: [PATCH 0727/2268] chore(deps): Bump github.com/bitnami-labs/sealed-secrets (#263) Bumps [github.com/bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) from 0.19.3 to 0.19.4. - [Release notes](https://github.com/bitnami-labs/sealed-secrets/releases) - [Changelog](https://github.com/bitnami-labs/sealed-secrets/blob/main/RELEASE-NOTES.md) - [Commits](https://github.com/bitnami-labs/sealed-secrets/compare/v0.19.3...v0.19.4) --- updated-dependencies: - dependency-name: github.com/bitnami-labs/sealed-secrets dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 20d85c018..5774ed81a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/bitnami-labs/sealed-secrets v0.19.3 + github.com/bitnami-labs/sealed-secrets v0.19.4 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible // See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility diff --git a/go.sum b/go.sum index abaf6b2b5..9bd56dc4a 100644 --- a/go.sum +++ b/go.sum @@ -155,8 +155,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.19.3 h1:D9v0fQt8JNDEoBU6HRKKJ20VhqvGUyaDrK1bl714i4I= -github.com/bitnami-labs/sealed-secrets v0.19.3/go.mod h1:G7Psbu6s3ed7GCO6ZjZ51zff0GGGSymLxFXb4QkJnRw= +github.com/bitnami-labs/sealed-secrets v0.19.4 h1:TUad0o/mNp2D53lHMGzjdsdrx+yrHy/fyuEIAbFqj+U= +github.com/bitnami-labs/sealed-secrets v0.19.4/go.mod h1:rXBdWOdAPbuowgFphH8bcCB3jSVlYXg9mMWRcYY49dc= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -666,7 +666,7 @@ github.com/ohler55/ojg v1.17.3/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfli github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q= +github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow= github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= From 31b3c95858eb3e93f0c1e4c98e1adac5fb89ee90 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 15:15:54 +0100 Subject: [PATCH 0728/2268] docs: Add missing documentation about onlyRender --- docs/reference/deployments/deployment-yml.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index 73e0fb3a5..77115380e 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -196,6 +196,17 @@ deployments: - path: kustomizeDeployment2 ``` +### onlyRender +Causes a path to be rendered only but not treated as a deployment item. This can be useful if you for example want to +use Kustomize components which you'd refer from other deployment items. + +```yaml +deployments: +- path: component + onlyRender: true +- path: kustomizeDeployment2 +``` + ## vars (deployment project) A list of variable sets to be loaded into the templating context, which is then available in all [deployment items](#deployments) and [sub-deployments](#includes). From 7b956a0ce14b82b44ad209d0417a5677f75fb881 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 15:37:25 +0100 Subject: [PATCH 0729/2268] tests: Add test for onlyRender --- e2e/deployment_items_test.go | 55 ++++++++++++++++++++++++++++++++++++ pkg/git/test_git_server.go | 4 +++ 2 files changed, 59 insertions(+) diff --git a/e2e/deployment_items_test.go b/e2e/deployment_items_test.go index 71e93a34a..8e07ffcbc 100644 --- a/e2e/deployment_items_test.go +++ b/e2e/deployment_items_test.go @@ -1,8 +1,10 @@ package e2e import ( + "fmt" "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/stretchr/testify/assert" "testing" ) @@ -80,3 +82,56 @@ func TestGeneratedKustomize(t *testing.T) { assertConfigMapExists(t, k, p.TestSlug(), "cm2") assertConfigMapNotExists(t, k, p.TestSlug(), "cm3") } + +func TestOnlyRender(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + p.AddDeploymentItem("", uo.FromMap(map[string]interface{}{ + "path": "only-render", + "onlyRender": true, + })) + p.UpdateFile("only-render/value.txt", func(f string) (string, error) { + return "{{ args.a }}\n", nil + }, "") + p.UpdateFile("only-render/kustomization.yaml", func(f string) (string, error) { + return fmt.Sprintf(` +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +generatorOptions: + disableNameSuffixHash: true + +configMapGenerator: +- name: %s-cm + files: + - value.txt +`, p.TestSlug()), nil + }, "") + + p.AddDeploymentItem("", uo.FromMap(map[string]interface{}{ + "path": "d", + })) + p.UpdateFile("d/kustomization.yaml", func(f string) (string, error) { + return fmt.Sprintf(` +components: +- ../only-render +namespace: %s +`, p.TestSlug()), nil + }, "") + + p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "a=v1") + // it should not appear in the default namespace as that would indicate that the component was treated as a deployment item + assertConfigMapNotExists(t, k, "default", p.TestSlug()+"-cm") + s := assertConfigMapExists(t, k, p.TestSlug(), p.TestSlug()+"-cm") + assert.Equal(t, s.Object["data"], map[string]any{ + "value.txt": "v1", + }) +} diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index 7dd0a1614..9ecaf8597 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -183,6 +183,10 @@ func (p *TestGitServer) UpdateFile(repo string, pth string, update func(f string if f == newF { return } + err = os.MkdirAll(filepath.Dir(fullPath), 0o700) + if err != nil { + p.t.Fatal(err) + } err = os.WriteFile(fullPath, []byte(newF), 0o600) if err != nil { p.t.Fatal(err) From b5c5a368ba6037e8c09eeeb46120ef5705a64634 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 15:48:38 +0100 Subject: [PATCH 0730/2268] fix: Fix onlyRender to actually not treat the path as deployment item --- pkg/deployment/deployment_item.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index af74a2f46..3e0f3ec56 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -494,6 +494,9 @@ func (di *DeploymentItem) buildKustomize() error { if di.dir == nil { return nil } + if di.Config.OnlyRender { + return nil + } ky, err := di.prepareKustomizationYaml() if err != nil { From 143c7260bb9b84d487fee766441b4947c6aa543b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 15:50:59 +0100 Subject: [PATCH 0731/2268] feat: Allow to load variables without overriding previously loaded variables (#230) --- docs/reference/templating/variable-sources.md | 5 + pkg/types/vars_source.go | 3 +- pkg/vars/vars_loader.go | 150 +++++++++--------- pkg/vars/vars_loader_http.go | 39 ++--- pkg/vars/vars_loader_test.go | 19 +++ 5 files changed, 115 insertions(+), 101 deletions(-) diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index 43c03e071..3f157f400 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -29,6 +29,8 @@ vars: - file: vars2.yaml - file: optional-vars.yaml ignoreMissing: true +- file: default-vars.yaml + noOverride: true ``` `vars2.yaml` can now use variables that are defined in `vars1.yaml`. At all times, variables defined by @@ -37,6 +39,9 @@ parents of the current sub-deployment project can be used in the current vars fi Each variable source can have the optional field `ignoreMissing` set to `true`, causing Kluctl to ignore if the source can not be found. +When specifying `noOverride: true`, Kluctl will not override variables from the previously loaded variables. This is +useful if you want to load default values for variables. + Different types of vars entries are possible: ### file diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index c32578469..e7db5f765 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -55,6 +55,7 @@ type VarsSourceVault struct { type VarsSource struct { IgnoreMissing *bool `yaml:"ignoreMissing,omitempty"` + NoOverride *bool `yaml:"noOverride,omitempty"` Values *uo.UnstructuredObject `yaml:"values,omitempty"` File *string `yaml:"file,omitempty"` @@ -73,7 +74,7 @@ func ValidateVarsSource(sl validator.StructLevel) { count := 0 v := reflect.ValueOf(s) for i := 0; i < v.NumField(); i++ { - if v.Type().Field(i).Name == "IgnoreMissing" { + if v.Type().Field(i).Name == "IgnoreMissing" || v.Type().Field(i).Name == "NoOverride" { continue } if !v.Field(i).IsNil() { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 39161f372..a0dc29267 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -83,27 +83,45 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear ignoreMissing = *source.IgnoreMissing } + var newVars *uo.UnstructuredObject if source.Values != nil { - v.mergeVars(varsCtx, source.Values, rootKey) - return nil + newVars = source.Values + if rootKey != "" { + newVars = uo.FromMap(map[string]interface{}{ + rootKey: newVars.Object, + }) + } } else if source.File != nil { - return v.loadFile(varsCtx, *source.File, ignoreMissing, searchDirs, rootKey) + newVars, err = v.loadFile(varsCtx, *source.File, ignoreMissing, searchDirs) } else if source.Git != nil { - return v.loadGit(varsCtx, source.Git, ignoreMissing, rootKey) + newVars, err = v.loadGit(varsCtx, source.Git, ignoreMissing) } else if source.ClusterConfigMap != nil { - return v.loadFromK8sObject(varsCtx, *source.ClusterConfigMap, "ConfigMap", source.ClusterConfigMap.Key, rootKey, ignoreMissing, false) + newVars, err = v.loadFromK8sObject(varsCtx, *source.ClusterConfigMap, "ConfigMap", source.ClusterConfigMap.Key, ignoreMissing, false) } else if source.ClusterSecret != nil { - return v.loadFromK8sObject(varsCtx, *source.ClusterSecret, "Secret", source.ClusterSecret.Key, rootKey, ignoreMissing, true) + newVars, err = v.loadFromK8sObject(varsCtx, *source.ClusterSecret, "Secret", source.ClusterSecret.Key, ignoreMissing, true) } else if source.SystemEnvVars != nil { - return v.loadSystemEnvs(varsCtx, &source, ignoreMissing, rootKey) + newVars, err = v.loadSystemEnvs(varsCtx, &source, ignoreMissing, rootKey) } else if source.Http != nil { - return v.loadHttp(varsCtx, &source, ignoreMissing, rootKey) + newVars, err = v.loadHttp(varsCtx, &source, ignoreMissing) } else if source.AwsSecretsManager != nil { - return v.loadAwsSecretsManager(varsCtx, &source, ignoreMissing, rootKey) + newVars, err = v.loadAwsSecretsManager(varsCtx, &source, ignoreMissing) } else if source.Vault != nil { - return v.loadVault(varsCtx, &source, ignoreMissing, rootKey) + newVars, err = v.loadVault(varsCtx, &source, ignoreMissing) + } else { + return fmt.Errorf("invalid vars source") + } + if err != nil { + return err } - return fmt.Errorf("invalid vars source") + + if source.NoOverride == nil || !*source.NoOverride { + varsCtx.Vars.Merge(newVars) + } else { + newVars.Merge(varsCtx.Vars) + varsCtx.Vars = newVars + } + + return nil } func (v *VarsLoader) mergeVars(varsCtx *VarsCtx, newVars *uo.UnstructuredObject, rootKey string) { @@ -114,45 +132,35 @@ func (v *VarsLoader) mergeVars(varsCtx *VarsCtx, newVars *uo.UnstructuredObject, } } -func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, ignoreMissing bool, searchDirs []string, rootKey string) error { +func (v *VarsLoader) loadFile(varsCtx *VarsCtx, path string, ignoreMissing bool, searchDirs []string) (*uo.UnstructuredObject, error) { rendered, err := varsCtx.RenderFile(path, searchDirs) if err != nil { // TODO the Jinja2 renderer should be able to better report this error if ignoreMissing && err.Error() == fmt.Sprintf("template %s not found", path) { - return nil + return uo.New(), nil } - return fmt.Errorf("failed to render vars file %s: %w", path, err) + return nil, fmt.Errorf("failed to render vars file %s: %w", path, err) } format := formats.FormatForPath(path) decrypted, _, err := sops.MaybeDecrypt(v.sops, []byte(rendered), format, format) if err != nil { - return fmt.Errorf("failed to decrypt vars file %s: %w", path, err) + return nil, fmt.Errorf("failed to decrypt vars file %s: %w", path, err) } rendered = string(decrypted) newVars := uo.New() err = yaml.ReadYamlString(rendered, newVars) if err != nil { - return err + return nil, err } if err != nil { - return fmt.Errorf("failed to load vars from %s: %w", path, err) + return nil, fmt.Errorf("failed to load vars from %s: %w", path, err) } - if rootKey != "" { - newVars, _, err = newVars.GetNestedObject(rootKey) - if err != nil { - return err - } - if newVars == nil { - return fmt.Errorf("vars from %s have no '%s' root", path, rootKey) - } - } - v.mergeVars(varsCtx, newVars, rootKey) - return nil + return newVars, nil } -func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool, rootKey string) error { +func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool, rootKey string) (*uo.UnstructuredObject, error) { newVars := uo.New() err := source.SystemEnvVars.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { envName, ok := it.Value().(string) @@ -196,15 +204,19 @@ func (v *VarsLoader) loadSystemEnvs(varsCtx *VarsCtx, source *types.VarsSource, return nil }) if err != nil { - return err + return nil, err } - v.mergeVars(varsCtx, newVars, rootKey) - return nil + if rootKey != "" { + newVars = uo.FromMap(map[string]interface{}{ + rootKey: newVars.Object, + }) + } + return newVars, nil } -func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool, rootKey string) error { +func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool) (*uo.UnstructuredObject, error) { if v.aws == nil { - return fmt.Errorf("no AWS client factory provided") + return uo.New(), fmt.Errorf("no AWS client factory provided") } secret, err := aws.GetAwsSecretsManagerSecret(v.ctx, v.aws, source.AwsSecretsManager.Profile, source.AwsSecretsManager.Region, source.AwsSecretsManager.SecretName) @@ -212,45 +224,45 @@ func (v *VarsLoader) loadAwsSecretsManager(varsCtx *VarsCtx, source *types.VarsS var aerr *types2.ResourceNotFoundException if errors2.As(err, &aerr) { if ignoreMissing { - return nil + return uo.New(), nil } } - return err + return nil, err } - return v.loadFromString(varsCtx, secret, "awsSecretsManager", rootKey) + return v.loadFromString(varsCtx, secret) } -func (v *VarsLoader) loadVault(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool, rootKey string) error { +func (v *VarsLoader) loadVault(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool) (*uo.UnstructuredObject, error) { secret, err := vault.GetSecret(source.Vault.Address, source.Vault.Path) if err != nil { - return err + return nil, err } if secret == nil { if ignoreMissing { - return nil + return uo.New(), nil } - return fmt.Errorf("the specified vault secret was not found") + return nil, fmt.Errorf("the specified vault secret was not found") } - return v.loadFromString(varsCtx, *secret, "vault", rootKey) + return v.loadFromString(varsCtx, *secret) } -func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, ignoreMissing bool, rootKey string) error { +func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, ignoreMissing bool) (*uo.UnstructuredObject, error) { ge, err := v.rp.GetEntry(gitFile.Url) if err != nil { - return err + return nil, err } clonedDir, _, err := ge.GetClonedDir(gitFile.Ref) if err != nil { - return fmt.Errorf("failed to load vars from git repository %s: %w", gitFile.Url.String(), err) + return nil, fmt.Errorf("failed to load vars from git repository %s: %w", gitFile.Url.String(), err) } - return v.loadFile(varsCtx, gitFile.Path, ignoreMissing, []string{clonedDir}, rootKey) + return v.loadFile(varsCtx, gitFile.Path, ignoreMissing, []string{clonedDir}) } -func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, key string, rootKey string, ignoreMissing bool, base64Decode bool) error { +func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, key string, ignoreMissing bool, base64Decode bool) (*uo.UnstructuredObject, error) { if v.k == nil { - return fmt.Errorf("loading vars from cluster is disabled") + return nil, fmt.Errorf("loading vars from cluster is disabled") } var err error @@ -260,9 +272,9 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo o, _, err = v.k.GetSingleObject(k8s2.NewObjectRef("", "v1", kind, varsSource.Name, varsSource.Namespace)) if err != nil { if ignoreMissing && errors.IsNotFound(err) { - return nil + return uo.New(), nil } - return err + return nil, err } } else { objs, _, err := v.k.ListObjects(schema.GroupVersionKind{ @@ -271,16 +283,16 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo Kind: kind, }, varsSource.Namespace, varsSource.Labels) if err != nil { - return err + return nil, err } if len(objs) == 0 { if ignoreMissing { - return nil + return uo.New(), nil } - return fmt.Errorf("no object found with labels %v", varsSource.Labels) + return nil, fmt.Errorf("no object found with labels %v", varsSource.Labels) } if len(objs) > 1 { - return fmt.Errorf("found more than one objects with labels %v", varsSource.Labels) + return nil, fmt.Errorf("found more than one objects with labels %v", varsSource.Labels) } o = objs[0] } @@ -289,10 +301,10 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo f, found, err := o.GetNestedField("data", key) if err != nil { - return err + return nil, err } if !found { - return fmt.Errorf("key %s not found in %s on cluster", key, ref.String()) + return nil, fmt.Errorf("key %s not found in %s on cluster", key, ref.String()) } var value string @@ -302,7 +314,7 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo if base64Decode { b, err := base64.StdEncoding.DecodeString(s) if err != nil { - return err + return nil, err } value = string(b) } else { @@ -310,32 +322,20 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo } } - err = v.loadFromString(varsCtx, value, "k8s", rootKey) + newVars, err := v.loadFromString(varsCtx, value) if err != nil { - return fmt.Errorf("failed to load vars from kubernetes object %s and key %s: %w", ref.String(), key, err) + return nil, fmt.Errorf("failed to load vars from kubernetes object %s and key %s: %w", ref.String(), key, err) } - return nil + return newVars, nil } -func (v *VarsLoader) loadFromString(varsCtx *VarsCtx, s string, secretType string, rootKey string) error { +func (v *VarsLoader) loadFromString(varsCtx *VarsCtx, s string) (*uo.UnstructuredObject, error) { newVars := uo.New() err := v.renderYamlString(varsCtx, s, newVars) if err != nil { - return err - } - - if rootKey != "" { - newVars, _, err = newVars.GetNestedObject(rootKey) - if err != nil { - return err - } - if newVars == nil { - return fmt.Errorf("%s secret has no '%s' root", secretType, rootKey) - } + return nil, err } - - v.mergeVars(varsCtx, newVars, rootKey) - return nil + return newVars, nil } func (v *VarsLoader) renderYamlString(varsCtx *VarsCtx, s string, out interface{}) error { diff --git a/pkg/vars/vars_loader_http.go b/pkg/vars/vars_loader_http.go index 7de4ca0a2..6e0a68f30 100644 --- a/pkg/vars/vars_loader_http.go +++ b/pkg/vars/vars_loader_http.go @@ -64,12 +64,12 @@ func (v *VarsLoader) doHttp(httpSource *types.VarsSourceHttp, ignoreMissing bool return resp, string(respBody), nil } -func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool, rootKey string) error { +func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, ignoreMissing bool) (*uo.UnstructuredObject, error) { resp, respBody, err := v.doHttp(source.Http, ignoreMissing, "", "") if err != nil && resp != nil && resp.StatusCode == http.StatusUnauthorized { chgs := challenge.ResponseChallenges(resp) if len(chgs) == 0 { - return err + return nil, err } var realms []string @@ -86,7 +86,7 @@ func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, ignore if !ok { username, password, err := status.AskForCredentials(v.ctx, fmt.Sprintf("Please enter credentials for host '%s'", source.Http.Url.Host)) if err != nil { - return err + return nil, err } creds = usernamePassword{ username: username, @@ -97,13 +97,13 @@ func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, ignore resp, respBody, err = v.doHttp(source.Http, ignoreMissing, creds.username, creds.password) if err != nil { - return err + return nil, err } } else if err != nil { if ignoreMissing && resp != nil && resp.StatusCode == http.StatusNotFound { - return nil + return uo.New(), nil } - return err + return nil, err } var respObj interface{} @@ -111,45 +111,34 @@ func (v *VarsLoader) loadHttp(varsCtx *VarsCtx, source *types.VarsSource, ignore err = yaml.ReadYamlString(respBody, &respObj) if err != nil { - return err + return nil, err } if err != nil { - return err + return nil, err } if source.Http.JsonPath != nil { p, err := uo.NewMyJsonPath(*source.Http.JsonPath) if err != nil { - return err + return nil, err } x, ok := p.GetFirstFromAny(respObj) if !ok { - return fmt.Errorf("%s not found in result from http request %s", *source.Http.JsonPath, source.Http.Url.String()) + return nil, fmt.Errorf("%s not found in result from http request %s", *source.Http.JsonPath, source.Http.Url.String()) } s, ok := x.(string) if !ok { - return fmt.Errorf("%s in result of http request %s is not a string", *source.Http.JsonPath, source.Http.Url.String()) + return nil, fmt.Errorf("%s in result of http request %s is not a string", *source.Http.JsonPath, source.Http.Url.String()) } newVars, err = uo.FromString(s) if err != nil { - return err + return nil, err } } else { x, ok := respObj.(map[string]interface{}) if !ok { - return fmt.Errorf("result of http request %s is not an object", source.Http.Url.String()) + return nil, fmt.Errorf("result of http request %s is not an object", source.Http.Url.String()) } newVars = uo.FromMap(x) } - - if rootKey != "" { - newVars, _, err = newVars.GetNestedObject(rootKey) - if err != nil { - return err - } - } - - if newVars != nil { - v.mergeVars(varsCtx, newVars, rootKey) - } - return nil + return newVars, nil } diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index f8320e8fd..40c038e99 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -69,6 +69,25 @@ func TestVarsLoader_Values(t *testing.T) { }) } +func TestVarsLoader_ValuesNoOverrides(t *testing.T) { + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + Values: uo.FromStringMust(`{"test1": {"test2": 42}}`), + }, nil, "") + assert.NoError(t, err) + + b := true + err = vl.LoadVars(vc, &types.VarsSource{ + Values: uo.FromStringMust(`{"test1": {"test2": 43}}`), + NoOverride: &b, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("test1", "test2") + assert.Equal(t, int64(42), v) + }) +} + func TestVarsLoader_File(t *testing.T) { d := t.TempDir() _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": 42}}`), 0o600) From 5bc357c88efa1ff9f7e544c4d00a1da094590fab Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 15:51:25 +0100 Subject: [PATCH 0732/2268] fix: Respect IdentityAgent from SSH config (#280) --- pkg/git/auth/ssh_auth_provider.go | 36 ++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index 5af2d2c0d..c6911e846 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -10,10 +10,13 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git/messages" sshagent "github.com/xanzy/ssh-agent" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" "io" + "net" "os" "os/user" "path/filepath" + "runtime" "sync" ) @@ -85,12 +88,43 @@ func (a *sshDefaultIdentityAndAgent) addConfigIdentities(gitUrl git_url.GitUrl) } } +func (a *sshDefaultIdentityAndAgent) createAgent(gitUrl git_url.GitUrl) (agent.Agent, error) { + if runtime.GOOS == "windows" { + a, _, err := sshagent.New() + return a, err + } + + // see `man ssh_config` + + sshAuthSock := ssh_config.Get(gitUrl.Hostname(), "IdentityAgent") + if sshAuthSock == "none" { + return nil, nil + } + if sshAuthSock == "" || sshAuthSock == "SSH_AUTH_SOCK" { + a, _, err := sshagent.New() + return a, err + } + + sshAuthSock = os.ExpandEnv(sshAuthSock) + sshAuthSock = expandHomeDir(sshAuthSock) + + conn, err := net.Dial("unix", sshAuthSock) + if err != nil { + return nil, fmt.Errorf("error connecting to unix socket: %v", err) + } + + return agent.NewClient(conn), nil +} + func (a *sshDefaultIdentityAndAgent) addAgentIdentities(gitUrl git_url.GitUrl) { a.authProvider.MessageCallbacks.Trace("trying to add agent keys") - agent, _, err := sshagent.New() + agent, err := a.createAgent(gitUrl) if err != nil { a.authProvider.MessageCallbacks.Warning("Failed to connect to ssh agent for url %s: %v", gitUrl.String(), err) } else { + if agent == nil { + return + } signers, err := agent.Signers() if err != nil { a.authProvider.MessageCallbacks.Warning("Failed to get signers from ssh agent for url %s: %v", gitUrl.String(), err) From b5ac47375adc4a0ab6ca9b7720694a797868bfa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Feb 2023 15:51:46 +0100 Subject: [PATCH 0733/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/config (#277) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.7 to 1.18.11. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.7...config/v1.18.11) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 5774ed81a..c83c9c98b 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,7 @@ require ( require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.17.3 - github.com/aws/aws-sdk-go-v2/config v1.18.7 + github.com/aws/aws-sdk-go-v2/config v1.18.11 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 @@ -95,16 +95,16 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.11 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.18.2 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index 9bd56dc4a..58e4aa64c 100644 --- a/go.sum +++ b/go.sum @@ -121,10 +121,10 @@ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:W github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY= github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.7 h1:V94lTcix6jouwmAsgQMAEBozVAGJMFhVj+6/++xfe3E= -github.com/aws/aws-sdk-go-v2/config v1.18.7/go.mod h1:OZYsyHFL5PB9UpyS78NElgKs11qI/B5KJau2XOJDXHA= -github.com/aws/aws-sdk-go-v2/credentials v1.13.7 h1:qUUcNS5Z1092XBFT66IJM7mYkMwgZ8fcC8YDIbEwXck= -github.com/aws/aws-sdk-go-v2/credentials v1.13.7/go.mod h1:AdCcbZXHQCjJh6NaH3pFaw8LUeBFn5+88BZGMVGuBT8= +github.com/aws/aws-sdk-go-v2/config v1.18.11 h1:7dJD4p90OyKYIihuwe/LbHfP7uw4yVm5P1hel+b8UZ8= +github.com/aws/aws-sdk-go-v2/config v1.18.11/go.mod h1:FTGKr2F7QL7IAg22dUmEB5NWpLPAOuhrONzXe7TVhAI= +github.com/aws/aws-sdk-go-v2/credentials v1.13.11 h1:QnvlTut1XXKkX4aaM1Ydo5X0CHriv0jmLu8PTVQQJJo= +github.com/aws/aws-sdk-go-v2/credentials v1.13.11/go.mod h1:tqAm4JmQaShel+Qi38hmd1QglSnnxaYt50k/9yGQzzc= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= @@ -141,12 +141,12 @@ github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3f github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2 h1:QDVKb2VpuwzIslzshumxksayV5GkpqT+rkVvdPVrA9E= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 h1:gItLq3zBYyRDPmqAClgzTH8PBjDQGeyptYGHIwtYYNA= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.28/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 h1:KCacyVSs/wlcPGx37hcbT3IGYO8P8Jx+TgSDhAXtQMY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 h1:9Mtq1KM6nD8/+HStvWcvYnixJ5N85DX+P+OY3kI3W2k= -github.com/aws/aws-sdk-go-v2/service/sts v1.17.7/go.mod h1:+lGbb3+1ugwKrNTWcf2RT05Xmp543B06zDFTwiTLp7I= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 h1:/2gzjhQowRLarkkBOGPXSRnb8sQ2RVsjdG1C/UliK/c= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.0/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 h1:Jfly6mRxk2ZOSlbCvZfKNS7TukSx1mIzhSsqZ/IGSZI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.2 h1:J/4wIaGInCEYCGhTSruxCxeoA5cy91a+JT7cHFKFSHQ= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.2/go.mod h1:+lGbb3+1ugwKrNTWcf2RT05Xmp543B06zDFTwiTLp7I= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From be1d16d6c37eb26a9b234029b53c6c72b77d53e3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 16:40:54 +0100 Subject: [PATCH 0734/2268] tests: Allow to set sub dir for test projects --- e2e/helm_test.go | 8 ++++---- e2e/no_target_test.go | 2 +- e2e/seal_test.go | 18 +++++++++--------- e2e/test-utils/project.go | 32 ++++++++++++++++++++++---------- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 9d8f519cb..6993804de 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -486,8 +486,8 @@ func TestHelmLocalChart(t *testing.T) { p.AddHelmDeployment("helm1", "../test-chart1", "", "", "test-helm-1", p.TestSlug(), nil) p.AddHelmDeployment("helm2", "test-chart2", "", "", "test-helm-2", p.TestSlug(), nil) - test_utils.CreateHelmDir(t, "test-chart1", "0.1.0", filepath.Join(p.LocalRepoDir(), "test-chart1")) - test_utils.CreateHelmDir(t, "test-chart2", "0.1.0", filepath.Join(p.LocalRepoDir(), "helm2/test-chart2")) + test_utils.CreateHelmDir(t, "test-chart1", "0.1.0", filepath.Join(p.LocalProjectDir(), "test-chart1")) + test_utils.CreateHelmDir(t, "test-chart2", "0.1.0", filepath.Join(p.LocalProjectDir(), "helm2/test-chart2")) p.KluctlMust("deploy", "--yes", "-t", "test") assertConfigMapExists(t, k, p.TestSlug(), "test-helm-1-test-chart1") @@ -505,9 +505,9 @@ func getChartDir(t *testing.T, p *test_utils.TestProject, url2 string, chartName } var dir string if u.Scheme == "oci" { - dir = filepath.Join(p.LocalRepoDir(), ".helm-charts", fmt.Sprintf("%s_%s", u.Scheme, u.Hostname()), chartName) + dir = filepath.Join(p.LocalProjectDir(), ".helm-charts", fmt.Sprintf("%s_%s", u.Scheme, u.Hostname()), chartName) } else { - dir = filepath.Join(p.LocalRepoDir(), ".helm-charts", fmt.Sprintf("%s_%s_%s", u.Scheme, u.Port(), u.Hostname()), chartName) + dir = filepath.Join(p.LocalProjectDir(), ".helm-charts", fmt.Sprintf("%s_%s_%s", u.Scheme, u.Port(), u.Hostname()), chartName) } if chartVersion != "" { dir = filepath.Join(dir, chartVersion) diff --git a/e2e/no_target_test.go b/e2e/no_target_test.go index 1451d10d8..90c8086c6 100644 --- a/e2e/no_target_test.go +++ b/e2e/no_target_test.go @@ -26,7 +26,7 @@ func prepareNoTargetTest(t *testing.T, withDeploymentYaml bool) *test_utils.Test p.AddKustomizeDeployment("cm", []test_utils.KustomizeResource{{Name: "cm.yaml", Content: cm}}, nil) } else { p.AddKustomizeResources("", []test_utils.KustomizeResource{{Name: "cm.yaml", Content: cm}}) - err := os.Remove(filepath.Join(p.LocalRepoDir(), "deployment.yml")) + err := os.Remove(filepath.Join(p.LocalProjectDir(), "deployment.yml")) assert.NoError(t, err) } diff --git a/e2e/seal_test.go b/e2e/seal_test.go index 532088948..e0cfab88d 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -178,7 +178,7 @@ func TestSeal_WithOperator(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.LocalRepoDir() + sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -213,7 +213,7 @@ func TestSeal_WithBootstrap(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.LocalRepoDir() + sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) test_resources.ApplyYaml("sealed-secrets.yaml", k) @@ -261,7 +261,7 @@ func TestSeal_MultipleVarSources(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.LocalRepoDir() + sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -301,7 +301,7 @@ func TestSeal_MultipleSecretSets(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.LocalRepoDir() + sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -352,7 +352,7 @@ func TestSeal_MultipleTargets(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") p.KluctlMust("seal", "-t", "test-target2") - sealedSecretsDir := p.LocalRepoDir() + sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target2/secret-secret.yml")) @@ -395,7 +395,7 @@ func TestSeal_MultipleSecrets(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.LocalRepoDir() + sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment2/test-target/secret-secret2.yml")) @@ -442,7 +442,7 @@ func TestSeal_MultipleSecretsInOneFile(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.LocalRepoDir() + sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -483,7 +483,7 @@ func TestSeal_File(t *testing.T) { p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.LocalRepoDir() + sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") @@ -538,7 +538,7 @@ func TestSeal_Vault(t *testing.T) { p.AddExtraEnv("VAULT_TOKEN=root") p.KluctlMust("seal", "-t", "test-target") - sealedSecretsDir := p.LocalRepoDir() + sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) p.KluctlMust("deploy", "--yes", "-t", "test-target") diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index b824a5223..9da4e2a84 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -17,6 +17,7 @@ import ( "net/url" "os" "os/exec" + "path" "path/filepath" "reflect" "strings" @@ -29,6 +30,7 @@ type TestProject struct { extraEnv []string useProcess bool + gitSubDir string gitServer *git2.TestGitServer } @@ -41,6 +43,12 @@ func WithUseProcess(useProcess bool) TestProjectOption { } } +func WithGitSubDir(subDir string) TestProjectOption { + return func(p *TestProject) { + p.gitSubDir = subDir + } +} + func NewTestProject(t *testing.T, opts ...TestProjectOption) *TestProject { p := &TestProject{ t: t, @@ -86,8 +94,8 @@ func (p *TestProject) UpdateDeploymentYaml(dir string, update func(o *uo.Unstruc }, "") } -func (p *TestProject) UpdateYaml(path string, update func(o *uo.UnstructuredObject) error, message string) { - p.gitServer.UpdateYaml("kluctl-project", path, func(o map[string]any) error { +func (p *TestProject) UpdateYaml(pth string, update func(o *uo.UnstructuredObject) error, message string) { + p.gitServer.UpdateYaml("kluctl-project", path.Join(p.gitSubDir, pth), func(o map[string]any) error { u := uo.FromMap(o) err := update(u) if err != nil { @@ -98,12 +106,12 @@ func (p *TestProject) UpdateYaml(path string, update func(o *uo.UnstructuredObje }, message) } -func (p *TestProject) UpdateFile(path string, update func(f string) (string, error), message string) { - p.gitServer.UpdateFile("kluctl-project", path, update, message) +func (p *TestProject) UpdateFile(pth string, update func(f string) (string, error), message string) { + p.gitServer.UpdateFile("kluctl-project", path.Join(p.gitSubDir, pth), update, message) } func (p *TestProject) GetYaml(path string) *uo.UnstructuredObject { - o, err := uo.FromFile(filepath.Join(p.LocalRepoDir(), path)) + o, err := uo.FromFile(filepath.Join(p.LocalProjectDir(), path)) if err != nil { p.t.Fatal(err) } @@ -231,7 +239,7 @@ func (p *TestProject) AddKustomizeDeployment(dir string, resources []KustomizeRe p.AddDeploymentIncludes(deploymentDir) } - absKustomizeDir := filepath.Join(p.LocalRepoDir(), dir) + absKustomizeDir := filepath.Join(p.LocalProjectDir(), dir) err := os.MkdirAll(absKustomizeDir, 0o700) if err != nil { @@ -334,11 +342,11 @@ func (p *TestProject) AddKustomizeResources(dir string, resources []KustomizeRes } if r.Content != nil { x := p.convertInterfaceToList(r.Content) - err := yaml.WriteYamlAllFile(filepath.Join(p.LocalRepoDir(), dir, fileName), x) + err := yaml.WriteYamlAllFile(filepath.Join(p.LocalProjectDir(), dir, fileName), x) if err != nil { return err } - _, err = wt.Add(filepath.Join(dir, fileName)) + _, err = wt.Add(filepath.Join(path.Join(p.gitSubDir, dir), fileName)) if err != nil { return err } @@ -372,6 +380,10 @@ func (p *TestProject) LocalRepoDir() string { return p.gitServer.LocalRepoDir("kluctl-project") } +func (p *TestProject) LocalProjectDir() string { + return path.Join(p.LocalRepoDir(), p.gitSubDir) +} + func (p *TestProject) GetGitRepo() *git.Repository { return p.gitServer.GetGitRepo("kluctl-project") } @@ -381,7 +393,7 @@ func (p *TestProject) KluctlProcess(argsIn ...string) (string, string, error) { args = append(args, argsIn...) args = append(args, "--no-update-check") - cwd := p.LocalRepoDir() + cwd := p.LocalProjectDir() args = append(args, "--debug") @@ -422,7 +434,7 @@ func (p *TestProject) KluctlExecute(argsIn ...string) (string, string, error) { } var args []string - args = append(args, "--project-dir", p.LocalRepoDir()) + args = append(args, "--project-dir", p.LocalProjectDir()) args = append(args, argsIn...) p.t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) From f110cb86e5e8ab1ce7db102738f4bf48e405b902 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 16:41:01 +0100 Subject: [PATCH 0735/2268] tests: Implement git include tests --- e2e/git_include_test.go | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 e2e/git_include_test.go diff --git a/e2e/git_include_test.go b/e2e/git_include_test.go new file mode 100644 index 000000000..8eb03fbfe --- /dev/null +++ b/e2e/git_include_test.go @@ -0,0 +1,55 @@ +package e2e + +import ( + "fmt" + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "testing" +) + +func prepareIncludeProject(t *testing.T, prefix string, subDir string) *test_utils.TestProject { + p := test_utils.NewTestProject(t, test_utils.WithGitSubDir(subDir)) + addConfigMapDeployment(p, "cm", map[string]string{"a": "v"}, resourceOpts{ + name: fmt.Sprintf("%s-cm", prefix), + namespace: p.TestSlug(), + }) + return p +} + +func prepareGitIncludeTest(t *testing.T) (*test_utils.TestProject, *test_utils.TestProject, *test_utils.TestProject) { + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + ip1 := prepareIncludeProject(t, "include1", "") + ip2 := prepareIncludeProject(t, "include2", "subDir") + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", func(target *uo.UnstructuredObject) {}) + + p.AddDeploymentItem("", uo.FromMap(map[string]interface{}{ + "git": map[string]any{ + "url": ip1.GitUrl(), + }, + })) + p.AddDeploymentItem("", uo.FromMap(map[string]interface{}{ + "git": map[string]any{ + "url": ip2.GitUrl(), + "subDir": "subDir", + }, + })) + + return p, ip1, ip2 +} + +func TestGitInclude(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p, _, _ := prepareGitIncludeTest(t) + + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.TestSlug(), "include1-cm") + assertConfigMapExists(t, k, p.TestSlug(), "include2-cm") +} From da34b2764dd01602a082816b4935365d8def2158 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 17:20:45 +0100 Subject: [PATCH 0736/2268] tests: Implement --local-git-override tests --- e2e/git_include_test.go | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/e2e/git_include_test.go b/e2e/git_include_test.go index 8eb03fbfe..a78268cb4 100644 --- a/e2e/git_include_test.go +++ b/e2e/git_include_test.go @@ -3,7 +3,12 @@ package e2e import ( "fmt" test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + cp "github.com/otiai10/copy" + "github.com/stretchr/testify/assert" + "path/filepath" "testing" ) @@ -53,3 +58,40 @@ func TestGitInclude(t *testing.T) { assertConfigMapExists(t, k, p.TestSlug(), "include1-cm") assertConfigMapExists(t, k, p.TestSlug(), "include2-cm") } + +func TestLocalGitOverride(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + p, ip1, ip2 := prepareGitIncludeTest(t) + + override1 := t.TempDir() + err := cp.Copy(ip1.LocalRepoDir(), override1) + assert.NoError(t, err) + + override2 := t.TempDir() + err = cp.Copy(ip2.LocalRepoDir(), override2) + assert.NoError(t, err) + + cm, err := uo.FromFile(filepath.Join(override1, "cm", "configmap-include1-cm.yml")) + assert.NoError(t, err) + _ = cm.SetNestedField("o1", "data", "a") + _ = yaml.WriteYamlFile(filepath.Join(override1, "cm", "configmap-include1-cm.yml"), cm) + + cm, err = uo.FromFile(filepath.Join(override2, "subDir", "cm", "configmap-include2-cm.yml")) + assert.NoError(t, err) + _ = cm.SetNestedField("o2", "data", "a") + _ = yaml.WriteYamlFile(filepath.Join(override2, "subDir", "cm", "configmap-include2-cm.yml"), cm) + + u1, _ := git_url.Parse(ip1.GitUrl()) + u2, _ := git_url.Parse(ip2.GitUrl()) + + p.KluctlMust("deploy", "--yes", "-t", "test", + "--local-git-override", fmt.Sprintf("%s=%s", u1.NormalizedRepoKey(), override1), + "--local-git-override", fmt.Sprintf("%s=%s", u2.NormalizedRepoKey(), override2), + ) + cm = assertConfigMapExists(t, k, p.TestSlug(), "include1-cm") + assertNestedFieldEquals(t, cm, "o1", "data", "a") + cm = assertConfigMapExists(t, k, p.TestSlug(), "include2-cm") + assertNestedFieldEquals(t, cm, "o2", "data", "a") +} From e60ad8feaa9a1c6c6a54dccb0de90ca3c8cc036a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 17:21:16 +0100 Subject: [PATCH 0737/2268] fix: Fix NormalizedRepoKey() with non-default ports --- pkg/git/git-url/url.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/git/git-url/url.go b/pkg/git/git-url/url.go index 452df12ee..727e8796f 100644 --- a/pkg/git/git-url/url.go +++ b/pkg/git/git-url/url.go @@ -101,5 +101,5 @@ func (u *GitUrl) NormalizedRepoKey() string { if u.User != nil && u.User.Username() != "" { username = u.User.Username() + "@" } - return fmt.Sprintf("%s%s:%s", username, u2.Host, u2.Path) + return fmt.Sprintf("%s%s:%s%s", username, u2.Hostname(), u2.Port(), u2.Path) } From 0b0f7271aebf7040dd69ad6119b9e2a8ffe938e7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 17:23:58 +0100 Subject: [PATCH 0738/2268] feat: Remove broken support for branch overriding --- cmd/kluctl/args/project.go | 2 +- cmd/kluctl/commands/utils.go | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index c14455303..fc896f813 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -27,7 +27,7 @@ type ProjectFlags struct { Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` - LocalGitOverride []string `group:"project" help:"Specify a single repository local git override in the form of 'github.com:my-org/my-repo=/local/path/to/override'. This will cause kluctl to not use git to clone for the specified repository but instead use the local directory. This is useful in case you need to test out changes in external git repositories without pushing them. To only override a single branch of the repo, use 'github.com:my-org/my-repo:my-branch=/local/path/to/override'"` + LocalGitOverride []string `group:"project" help:"Specify a single repository local git override in the form of 'github.com:my-org/my-repo=/local/path/to/override'. This will cause kluctl to not use git to clone for the specified repository but instead use the local directory. This is useful in case you need to test out changes in external git repositories without pushing them."` LocalGitGroupOverride []string `group:"project" help:"Same as --local-git-override, but for a whole group prefix instead of a single repository. All repositories that have the given prefix will be overridden with the given local path and the repository suffix appended. For example, 'gitlab.com:some-org/sub-org=/local/path/to/my-forks' will override all repositories below 'gitlab.com:some-org/sub-org/' with the repositories found in '/local/path/to/my-forks'. It will however only perform an override if the given repository actually exists locally and otherwise revert to the actual (non-overridden) repository."` } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 63da52161..c789882ac 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -242,20 +242,12 @@ func parseRepoOverride(s string, isGroup bool) (ret repocache.RepoOverride, err return repocache.RepoOverride{}, fmt.Errorf("%s", s) } - sp2 := strings.Split(sp[0], ":") - if len(sp2) < 2 || len(sp2) > 3 { - return repocache.RepoOverride{}, fmt.Errorf("%s", s) - } - - u, err := git_url.Parse(fmt.Sprintf("%s:%s", sp2[0], sp2[1])) + u, err := git_url.Parse(sp[0]) if err != nil { return repocache.RepoOverride{}, fmt.Errorf("%s: %w", s, err) } u = u.Normalize() ret.RepoUrl = *u - if len(sp2) == 3 { - ret.Ref = sp2[2] - } ret.Override = sp[1] return } From aa9f146cefa0fd77e49e76ed7a14bc27f5b659c0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 21:42:16 +0100 Subject: [PATCH 0739/2268] fix: Check corrent path when looking for overrides --- pkg/repocache/cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/repocache/cache.go b/pkg/repocache/cache.go index 131567488..3db2b00c5 100644 --- a/pkg/repocache/cache.go +++ b/pkg/repocache/cache.go @@ -106,7 +106,7 @@ func (rp *GitRepoCache) GetEntry(url git_url.GitUrl) (*CacheEntry, error) { relPath := strings.TrimPrefix(urlN.Path, ro.RepoUrl.Path+"/") overridePath = path.Join(ro.Override, relPath) } else { - if ro.Override != urlN.Path { + if ro.RepoUrl.Path != urlN.Path { continue } overridePath = ro.Override From f314e3eebfcbf7aec7b1789c7252cfef24ce005f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 21:48:20 +0100 Subject: [PATCH 0740/2268] fix: Fix parsing in parseRepoOverride --- cmd/kluctl/commands/utils.go | 5 ++++- e2e/git_include_test.go | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index c789882ac..2a610fee0 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -242,7 +242,10 @@ func parseRepoOverride(s string, isGroup bool) (ret repocache.RepoOverride, err return repocache.RepoOverride{}, fmt.Errorf("%s", s) } - u, err := git_url.Parse(sp[0]) + // we need to prepend a dummy scheme to the repo key so that it is properly parsed + dummyUrl := fmt.Sprintf("git://%s", sp[0]) + + u, err := git_url.Parse(dummyUrl) if err != nil { return repocache.RepoOverride{}, fmt.Errorf("%s: %w", s, err) } diff --git a/e2e/git_include_test.go b/e2e/git_include_test.go index a78268cb4..22afa21f8 100644 --- a/e2e/git_include_test.go +++ b/e2e/git_include_test.go @@ -85,10 +85,12 @@ func TestLocalGitOverride(t *testing.T) { u1, _ := git_url.Parse(ip1.GitUrl()) u2, _ := git_url.Parse(ip2.GitUrl()) + k1 := u1.NormalizedRepoKey() + k2 := u2.NormalizedRepoKey() p.KluctlMust("deploy", "--yes", "-t", "test", - "--local-git-override", fmt.Sprintf("%s=%s", u1.NormalizedRepoKey(), override1), - "--local-git-override", fmt.Sprintf("%s=%s", u2.NormalizedRepoKey(), override2), + "--local-git-override", fmt.Sprintf("%s=%s", k1, override1), + "--local-git-override", fmt.Sprintf("%s=%s", k2, override2), ) cm = assertConfigMapExists(t, k, p.TestSlug(), "include1-cm") assertNestedFieldEquals(t, cm, "o1", "data", "a") From 656e102ac357fd9a962a24389ee6dfbc06b8f9a3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 23:00:30 +0100 Subject: [PATCH 0741/2268] tests: Allow to reuse Git servers for multiple projects --- e2e/test-utils/project.go | 42 ++++++++++++++++++++++++++---------- pkg/git/test_git_server.go | 8 +++++-- pkg/vars/vars_loader_test.go | 6 +++--- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index 9da4e2a84..63f918dea 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -30,9 +30,10 @@ type TestProject struct { extraEnv []string useProcess bool - gitSubDir string - gitServer *git2.TestGitServer + gitServer *git2.TestGitServer + gitRepoName string + gitSubDir string } type TestProjectOption func(p *TestProject) @@ -43,6 +44,18 @@ func WithUseProcess(useProcess bool) TestProjectOption { } } +func WithGitServer(s *git2.TestGitServer) TestProjectOption { + return func(p *TestProject) { + p.gitServer = s + } +} + +func WithRepoName(n string) TestProjectOption { + return func(p *TestProject) { + p.gitRepoName = n + } +} + func WithGitSubDir(subDir string) TestProjectOption { return func(p *TestProject) { p.gitSubDir = subDir @@ -51,15 +64,18 @@ func WithGitSubDir(subDir string) TestProjectOption { func NewTestProject(t *testing.T, opts ...TestProjectOption) *TestProject { p := &TestProject{ - t: t, + t: t, + gitRepoName: "kluctl-project", } for _, o := range opts { o(p) } - p.gitServer = git2.NewTestGitServer(t) - p.gitServer.GitInit("kluctl-project") + if p.gitServer == nil { + p.gitServer = git2.NewTestGitServer(t) + } + p.gitServer.GitInit(p.gitRepoName) p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { return nil @@ -70,6 +86,10 @@ func NewTestProject(t *testing.T, opts ...TestProjectOption) *TestProject { return p } +func (p *TestProject) GitServer() *git2.TestGitServer { + return p.gitServer +} + func (p *TestProject) TestSlug() string { n := p.t.Name() n = xstrings.ToKebabCase(n) @@ -95,7 +115,7 @@ func (p *TestProject) UpdateDeploymentYaml(dir string, update func(o *uo.Unstruc } func (p *TestProject) UpdateYaml(pth string, update func(o *uo.UnstructuredObject) error, message string) { - p.gitServer.UpdateYaml("kluctl-project", path.Join(p.gitSubDir, pth), func(o map[string]any) error { + p.gitServer.UpdateYaml(p.gitRepoName, path.Join(p.gitSubDir, pth), func(o map[string]any) error { u := uo.FromMap(o) err := update(u) if err != nil { @@ -107,7 +127,7 @@ func (p *TestProject) UpdateYaml(pth string, update func(o *uo.UnstructuredObjec } func (p *TestProject) UpdateFile(pth string, update func(f string) (string, error), message string) { - p.gitServer.UpdateFile("kluctl-project", path.Join(p.gitSubDir, pth), update, message) + p.gitServer.UpdateFile(p.gitRepoName, path.Join(p.gitSubDir, pth), update, message) } func (p *TestProject) GetYaml(path string) *uo.UnstructuredObject { @@ -147,7 +167,7 @@ func (p *TestProject) ListDeploymentItemPathes(dir string, fullPath bool) []stri } func (p *TestProject) UpdateKustomizeDeployment(dir string, update func(o *uo.UnstructuredObject, wt *git.Worktree) error) { - wt := p.gitServer.GetWorktree("kluctl-project") + wt := p.gitServer.GetWorktree(p.gitRepoName) pth := filepath.Join(dir, "kustomization.yml") p.UpdateYaml(pth, func(o *uo.UnstructuredObject) error { @@ -373,11 +393,11 @@ func (p *TestProject) DeleteKustomizeDeployment(dir string) { } func (p *TestProject) GitUrl() string { - return p.gitServer.GitUrl("kluctl-project") + return p.gitServer.GitRepoUrl(p.gitRepoName) } func (p *TestProject) LocalRepoDir() string { - return p.gitServer.LocalRepoDir("kluctl-project") + return p.gitServer.LocalRepoDir(p.gitRepoName) } func (p *TestProject) LocalProjectDir() string { @@ -385,7 +405,7 @@ func (p *TestProject) LocalProjectDir() string { } func (p *TestProject) GetGitRepo() *git.Repository { - return p.gitServer.GetGitRepo("kluctl-project") + return p.gitServer.GetGitRepo(p.gitRepoName) } func (p *TestProject) KluctlProcess(argsIn ...string) (string, string, error) { diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index 7dd0a1614..fc58d02ab 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -223,8 +223,12 @@ func (p *TestGitServer) UpdateYaml(repo string, pth string, update func(o map[st p.CommitYaml(repo, pth, message, o) } -func (p *TestGitServer) GitUrl(repo string) string { - return fmt.Sprintf("http://localhost:%d/%s/.git", p.gitServerPort, repo) +func (p *TestGitServer) GitUrl() string { + return fmt.Sprintf("http://localhost:%d", p.gitServerPort) +} + +func (p *TestGitServer) GitRepoUrl(repo string) string { + return fmt.Sprintf("%s/%s/.git", p.GitUrl(), repo) } func (p *TestGitServer) LocalRepoDir(repo string) string { diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 40c038e99..a5b774f45 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -199,7 +199,7 @@ func TestVarsLoader_Git(t *testing.T) { }, "") testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { - url, _ := git_url.Parse(gs.GitUrl("repo")) + url, _ := git_url.Parse(gs.GitRepoUrl("repo")) err := vl.LoadVars(vc, &types.VarsSource{ Git: &types.VarsSourceGit{ Url: *url, @@ -213,7 +213,7 @@ func TestVarsLoader_Git(t *testing.T) { }) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { - url, _ := git_url.Parse(gs.GitUrl("repo")) + url, _ := git_url.Parse(gs.GitRepoUrl("repo")) b := true err := vl.LoadVars(vc, &types.VarsSource{ IgnoreMissing: &b, @@ -250,7 +250,7 @@ func TestVarsLoader_GitBranch(t *testing.T) { assert.NoError(t, err) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { - url, _ := git_url.Parse(gs.GitUrl("repo")) + url, _ := git_url.Parse(gs.GitRepoUrl("repo")) err = vl.LoadVars(vc, &types.VarsSource{ Git: &types.VarsSourceGit{ Url: *url, From 8f98b33eaf96a80f0807b9de80801f8eb62486d6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 23:00:45 +0100 Subject: [PATCH 0742/2268] tests: Implement --local-git-group-override tests --- e2e/git_include_test.go | 59 +++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/e2e/git_include_test.go b/e2e/git_include_test.go index 22afa21f8..fc24635e1 100644 --- a/e2e/git_include_test.go +++ b/e2e/git_include_test.go @@ -3,6 +3,7 @@ package e2e import ( "fmt" test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + git2 "github.com/kluctl/kluctl/v2/pkg/git" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -12,8 +13,12 @@ import ( "testing" ) -func prepareIncludeProject(t *testing.T, prefix string, subDir string) *test_utils.TestProject { - p := test_utils.NewTestProject(t, test_utils.WithGitSubDir(subDir)) +func prepareIncludeProject(t *testing.T, prefix string, subDir string, gitServer *git2.TestGitServer) *test_utils.TestProject { + p := test_utils.NewTestProject(t, + test_utils.WithGitSubDir(subDir), + test_utils.WithGitServer(gitServer), + test_utils.WithRepoName(fmt.Sprintf("repos/%s", prefix)), + ) addConfigMapDeployment(p, "cm", map[string]string{"a": "v"}, resourceOpts{ name: fmt.Sprintf("%s-cm", prefix), namespace: p.TestSlug(), @@ -21,12 +26,12 @@ func prepareIncludeProject(t *testing.T, prefix string, subDir string) *test_uti return p } -func prepareGitIncludeTest(t *testing.T) (*test_utils.TestProject, *test_utils.TestProject, *test_utils.TestProject) { +func prepareGitIncludeTest(t *testing.T, gitServer *git2.TestGitServer) (*test_utils.TestProject, *test_utils.TestProject, *test_utils.TestProject) { k := defaultCluster1 - p := test_utils.NewTestProject(t) - ip1 := prepareIncludeProject(t, "include1", "") - ip2 := prepareIncludeProject(t, "include2", "subDir") + p := test_utils.NewTestProject(t, test_utils.WithGitServer(gitServer)) + ip1 := prepareIncludeProject(t, "include1", "", gitServer) + ip2 := prepareIncludeProject(t, "include2", "subDir", gitServer) createNamespace(t, k, p.TestSlug()) @@ -52,7 +57,7 @@ func TestGitInclude(t *testing.T) { k := defaultCluster1 - p, _, _ := prepareGitIncludeTest(t) + p, _, _ := prepareGitIncludeTest(t, nil) p.KluctlMust("deploy", "--yes", "-t", "test") assertConfigMapExists(t, k, p.TestSlug(), "include1-cm") @@ -63,7 +68,7 @@ func TestLocalGitOverride(t *testing.T) { t.Parallel() k := defaultCluster1 - p, ip1, ip2 := prepareGitIncludeTest(t) + p, ip1, ip2 := prepareGitIncludeTest(t, nil) override1 := t.TempDir() err := cp.Copy(ip1.LocalRepoDir(), override1) @@ -97,3 +102,41 @@ func TestLocalGitOverride(t *testing.T) { cm = assertConfigMapExists(t, k, p.TestSlug(), "include2-cm") assertNestedFieldEquals(t, cm, "o2", "data", "a") } + +func TestLocalGitGroupOverride(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + p, ip1, ip2 := prepareGitIncludeTest(t, git2.NewTestGitServer(t)) + + overrideGroupDir := t.TempDir() + + override1 := filepath.Join(overrideGroupDir, "include1") + err := cp.Copy(ip1.LocalRepoDir(), override1) + assert.NoError(t, err) + + override2 := filepath.Join(overrideGroupDir, "include2") + err = cp.Copy(ip2.LocalRepoDir(), override2) + assert.NoError(t, err) + + cm, err := uo.FromFile(filepath.Join(override1, "cm", "configmap-include1-cm.yml")) + assert.NoError(t, err) + _ = cm.SetNestedField("o1", "data", "a") + _ = yaml.WriteYamlFile(filepath.Join(override1, "cm", "configmap-include1-cm.yml"), cm) + + cm, err = uo.FromFile(filepath.Join(override2, "subDir", "cm", "configmap-include2-cm.yml")) + assert.NoError(t, err) + _ = cm.SetNestedField("o2", "data", "a") + _ = yaml.WriteYamlFile(filepath.Join(override2, "subDir", "cm", "configmap-include2-cm.yml"), cm) + + u1, _ := git_url.Parse(p.GitServer().GitUrl() + "/repos") + k1 := u1.NormalizedRepoKey() + + p.KluctlMust("deploy", "--yes", "-t", "test", + "--local-git-group-override", fmt.Sprintf("%s=%s", k1, overrideGroupDir), + ) + cm = assertConfigMapExists(t, k, p.TestSlug(), "include1-cm") + assertNestedFieldEquals(t, cm, "o1", "data", "a") + cm = assertConfigMapExists(t, k, p.TestSlug(), "include2-cm") + assertNestedFieldEquals(t, cm, "o2", "data", "a") +} From 2444089ee2cd682c000f973fd4e93927d6568a59 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 3 Feb 2023 23:05:56 +0100 Subject: [PATCH 0743/2268] docs: Run make replace-commands-help --- docs/reference/commands/common-arguments.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index ac7f3d69a..c08d93b4b 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -67,8 +67,7 @@ Project arguments: cause kluctl to not use git to clone for the specified repository but instead use the local directory. This is useful in case you need to test out changes in external git repositories without - pushing them. To only override a single branch of the repo, use - 'github.com:my-org/my-repo:my-branch=/local/path/to/override' + pushing them. -c, --project-config existingfile Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml --project-dir existingdir Specify the project directory. Defaults to the current working From 521c62dd6e1d858e11d1a4a6c1d375d886ef36d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Feb 2023 22:21:11 +0100 Subject: [PATCH 0744/2268] chore(deps): Bump k8s.io/apiextensions-apiserver from 0.26.0 to 0.26.1 (#286) Bumps [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) from 0.26.0 to 0.26.1. - [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases) - [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.26.0...v0.26.1) --- updated-dependencies: - dependency-name: k8s.io/apiextensions-apiserver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index c83c9c98b..235ac37db 100644 --- a/go.mod +++ b/go.mod @@ -47,10 +47,10 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.3 - k8s.io/api v0.26.0 - k8s.io/apiextensions-apiserver v0.26.0 - k8s.io/apimachinery v0.26.0 - k8s.io/client-go v0.26.0 + k8s.io/api v0.26.1 + k8s.io/apiextensions-apiserver v0.26.1 + k8s.io/apimachinery v0.26.1 + k8s.io/client-go v0.26.1 k8s.io/klog/v2 v2.80.1 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 @@ -253,9 +253,9 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gotest.tools/v3 v3.2.0 // indirect - k8s.io/apiserver v0.26.0 // indirect + k8s.io/apiserver v0.26.1 // indirect k8s.io/cli-runtime v0.26.0 // indirect - k8s.io/component-base v0.26.0 // indirect + k8s.io/component-base v0.26.1 // indirect k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715 // indirect k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect diff --git a/go.sum b/go.sum index 58e4aa64c..5181e8dd0 100644 --- a/go.sum +++ b/go.sum @@ -1323,20 +1323,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= -k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= -k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo= -k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ= -k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= -k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= -k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o= -k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84= +k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= +k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= +k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= +k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= +k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc= +k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= -k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= -k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= -k8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs= -k8s.io/component-base v0.26.0/go.mod h1:lqHwlfV1/haa14F/Z5Zizk5QmzaVf23nQzCwVOQpfC8= +k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= +k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715 h1:tBEbstoM+K0FiBV5KGAKQ0kuvf54v/hwpldiJt69w1s= From fc14969e8dedba4f8d700218d05919c0b39ca253 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Feb 2023 22:21:32 +0100 Subject: [PATCH 0745/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/config (#283) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.11 to 1.18.12. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.11...config/v1.18.12) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 22 +++++++++++----------- go.sum | 41 ++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 235ac37db..f8f74f210 100644 --- a/go.mod +++ b/go.mod @@ -58,8 +58,8 @@ require ( require ( filippo.io/age v1.1.1 - github.com/aws/aws-sdk-go-v2 v1.17.3 - github.com/aws/aws-sdk-go-v2/config v1.18.11 + github.com/aws/aws-sdk-go-v2 v1.17.4 + github.com/aws/aws-sdk-go-v2/config v1.18.12 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 @@ -95,16 +95,16 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.11 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index 5181e8dd0..2c9a9ea0c 100644 --- a/go.sum +++ b/go.sum @@ -119,34 +119,37 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY= github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.11 h1:7dJD4p90OyKYIihuwe/LbHfP7uw4yVm5P1hel+b8UZ8= -github.com/aws/aws-sdk-go-v2/config v1.18.11/go.mod h1:FTGKr2F7QL7IAg22dUmEB5NWpLPAOuhrONzXe7TVhAI= -github.com/aws/aws-sdk-go-v2/credentials v1.13.11 h1:QnvlTut1XXKkX4aaM1Ydo5X0CHriv0jmLu8PTVQQJJo= -github.com/aws/aws-sdk-go-v2/credentials v1.13.11/go.mod h1:tqAm4JmQaShel+Qi38hmd1QglSnnxaYt50k/9yGQzzc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg= +github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= +github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.12 h1:fKs/I4wccmfrNRO9rdrbMO1NgLxct6H9rNMiPdBxHWw= +github.com/aws/aws-sdk-go-v2/config v1.18.12/go.mod h1:J36fOhj1LQBr+O4hJCiT8FwVvieeoSGOtPuvhKlsNu8= +github.com/aws/aws-sdk-go-v2/credentials v1.13.12 h1:Cb+HhuEnV19zHRaYYVglwvdHGMJWbdsyP4oHhw04xws= +github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtbM2J48ra2+Ltu+tmwr/jO0KA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaYh+BRMbvrrmZwIQ5jWqCZQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFuMO22HkV5VWGLBvmCLBCLPivUAmpdpnp4Vs= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2 h1:QDVKb2VpuwzIslzshumxksayV5GkpqT+rkVvdPVrA9E= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 h1:/2gzjhQowRLarkkBOGPXSRnb8sQ2RVsjdG1C/UliK/c= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.0/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 h1:Jfly6mRxk2ZOSlbCvZfKNS7TukSx1mIzhSsqZ/IGSZI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.2 h1:J/4wIaGInCEYCGhTSruxCxeoA5cy91a+JT7cHFKFSHQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.2/go.mod h1:+lGbb3+1ugwKrNTWcf2RT05Xmp543B06zDFTwiTLp7I= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 h1:lQKN/LNa3qqu2cDOQZybP7oL4nMGGiFqob0jZJaR8/4= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 h1:0bLhH6DRAqox+g0LatcjGKjjhU6Eudyys6HB6DJVPj8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 h1:s49mSnsBZEXjfGBkRfmK+nPqzT7Lt3+t2SmAKNyHblw= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.3/go.mod h1:b+psTJn33Q4qGoDaM7ZiOVVG8uVjGI6HaZ8WBHdgDgU= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From b50bac1e545e641ca38eb364d8efba1ab2e9d049 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Feb 2023 22:21:48 +0100 Subject: [PATCH 0746/2268] chore(deps): Bump github.com/go-playground/validator/v10 (#284) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.11.1 to 10.11.2. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.11.1...v10.11.2) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 19 +++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index f8f74f210..1f00bd653 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( // See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df github.com/fluxcd/pkg/kustomize v0.12.0 - github.com/go-playground/validator/v10 v10.11.1 + github.com/go-playground/validator/v10 v10.11.2 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 github.com/google/go-containerregistry v0.12.1 @@ -138,8 +138,8 @@ require ( github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/go.sum b/go.sum index 2c9a9ea0c..da078e7e6 100644 --- a/go.sum +++ b/go.sum @@ -306,14 +306,13 @@ github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXym github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= +github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= @@ -559,7 +558,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB 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/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 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= @@ -745,7 +743,6 @@ github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 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/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -887,7 +884,6 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -1063,7 +1059,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20210809222454-d867a43fc93e/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From c3cbb70d289b83504094166dd65868290de9c06d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 6 Feb 2023 17:38:21 +0100 Subject: [PATCH 0747/2268] fix: Upgrade go-jinja2 and go-embed-python to fix Alpine support --- go.mod | 10 +++++----- go.sum | 21 ++++++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 1f00bd653..7ba28228c 100644 --- a/go.mod +++ b/go.mod @@ -21,8 +21,8 @@ require ( github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 - github.com/kluctl/go-embed-python v0.0.0-3.10.8-20221106-1 - github.com/kluctl/go-jinja2 v0.0.0-20221215083015-c3f906953ba1 + github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 + github.com/kluctl/go-jinja2 v0.0.0-20230206202229-6e5a9f576647 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 @@ -132,8 +132,8 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect - github.com/go-git/go-billy/v5 v5.3.1 // indirect - github.com/go-git/go-git/v5 v5.5.1 // indirect + github.com/go-git/go-billy/v5 v5.4.1 // indirect + github.com/go-git/go-git/v5 v5.5.2 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect @@ -179,7 +179,7 @@ require ( github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.13 // indirect + github.com/klauspost/compress v1.15.15 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect diff --git a/go.sum b/go.sum index da078e7e6..a8900a476 100644 --- a/go.sum +++ b/go.sum @@ -277,12 +277,14 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.4.0/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= +github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.5.1 h1:5vtv2TB5PM/gPM+EvsHJ16hJh4uAkdGcKilcwY7FYwo= -github.com/go-git/go-git/v5 v5.5.1/go.mod h1:uz5PQ3d0gz7mSgzZhSJToM6ALPaKCdSnl58/Xb5hzr8= +github.com/go-git/go-git/v5 v5.5.2 h1:v8lgZa5k9ylUw+OR/roJHTxR4QItsNFI5nKtAXFuynw= +github.com/go-git/go-git/v5 v5.5.2/go.mod h1:BE5hUJ5yaV2YMxhmaP4l6RBQ08kMxKSPD4BlxtH7OjI= 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= @@ -545,12 +547,12 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4 github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0= -github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/kluctl/go-embed-python v0.0.0-3.10.8-20221106-1 h1:PXkdM8Z/Lh1+GIjXVG3dFs5enQoQ1eC7JXMz/Mo4R04= -github.com/kluctl/go-embed-python v0.0.0-3.10.8-20221106-1/go.mod h1:qVVSnYkq5NSgUuLzz4hTCXvVsRQyF/ic4U5v7DaiCGM= -github.com/kluctl/go-jinja2 v0.0.0-20221215083015-c3f906953ba1 h1:LwFIJE5cb6AAThM8FPbg589Y7zaToSrBCEQ1bxs8eiY= -github.com/kluctl/go-jinja2 v0.0.0-20221215083015-c3f906953ba1/go.mod h1:hkHz+qOoK67xFAM8M6OkvvXpEsYq8ZKQxkYkat6J058= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 h1:K96SwIr8MzBQ3kFFz2H/pA2y+EEk04vZ4fWj/YZghBU= +github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2/go.mod h1:vzngsPshNKUtq0gxkYQKNJafrcH7Qy7Qt6yGNt7JmQI= +github.com/kluctl/go-jinja2 v0.0.0-20230206202229-6e5a9f576647 h1:H7qN4RcRsFX6OuKn2La0ueu0nSFE9uPI/AMcgF5fXi8= +github.com/kluctl/go-jinja2 v0.0.0-20230206202229-6e5a9f576647/go.mod h1:oGEuC0taLwg20ORdSN7cdrN4rook79mJJ01QZIU858U= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -1072,6 +1074,7 @@ golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From e5ede940642c035c3e01428eef8e0bd6f4bdf1eb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 6 Feb 2023 17:38:33 +0100 Subject: [PATCH 0748/2268] feat: Switch to alpine for docker image --- Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index b22c03a9a..f3ee702f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,6 @@ -# We must use a glibc based distro due to embedded python not supporting musl libc for aarch64 -FROM debian:bullseye-slim +FROM alpine:3.17.1 -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* +RUN apk add ca-certificates COPY kluctl /usr/bin/ ENTRYPOINT ["/usr/bin/kluctl"] From 6f132f7f808a8d26722d9a6950049ebda4fc6a38 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 08:37:20 +0100 Subject: [PATCH 0749/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/service/secretsmanager (#292) Bumps [github.com/aws/aws-sdk-go-v2/service/secretsmanager](https://github.com/aws/aws-sdk-go-v2) from 1.18.2 to 1.18.3. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.2...config/v1.18.3) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/secretsmanager dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 1f00bd653..0e761063f 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.17.4 github.com/aws/aws-sdk-go-v2/config v1.18.12 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 diff --git a/go.sum b/go.sum index da078e7e6..d98304d6d 100644 --- a/go.sum +++ b/go.sum @@ -119,7 +119,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.18.12 h1:fKs/I4wccmfrNRO9rdrbMO1NgLxct6H9rNMiPdBxHWw= @@ -129,11 +128,9 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtb github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaYh+BRMbvrrmZwIQ5jWqCZQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A= @@ -142,8 +139,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFu github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2 h1:QDVKb2VpuwzIslzshumxksayV5GkpqT+rkVvdPVrA9E= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.2/go.mod h1:jAeo/PdIJZuDSwsvxJS94G4d6h8tStj7WXVuKwLHWU8= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3 h1:Zod/h9QcDvbrrG3jjTUp4lctRb6Qg2nj7ARC/xMsUc4= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3/go.mod h1:hqPcyOuLU6yWIbLy3qMnQnmidgKuIEwqIlW6+chYnog= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 h1:lQKN/LNa3qqu2cDOQZybP7oL4nMGGiFqob0jZJaR8/4= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 h1:0bLhH6DRAqox+g0LatcjGKjjhU6Eudyys6HB6DJVPj8= From 00a4aa3294696a16eda7fc9a8de6501485f5400a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 08:38:42 +0100 Subject: [PATCH 0750/2268] chore(deps): Bump github.com/spf13/viper from 1.14.0 to 1.15.0 (#293) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 13 ++++++------- go.sum | 26 ++++++++++++-------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 0e761063f..728bec1e9 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.14.0 + github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.1 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3 @@ -74,7 +74,7 @@ require ( require ( cloud.google.com/go/compute v1.14.0 // indirect - cloud.google.com/go/compute/metadata v0.2.2 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v0.8.0 // indirect cloud.google.com/go/kms v1.6.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 // indirect @@ -205,7 +205,6 @@ require ( github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect @@ -226,7 +225,7 @@ require ( 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/subosito/gotenv v1.4.1 // indirect + github.com/subosito/gotenv v1.4.2 // indirect github.com/urfave/cli v1.22.7 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect @@ -243,10 +242,10 @@ require ( golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.4.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/api v0.105.0 // indirect + google.golang.org/api v0.107.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 // indirect - google.golang.org/grpc v1.51.0 // indirect + google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect + google.golang.org/grpc v1.52.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index d98304d6d..4796239a1 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,8 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0= cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute/metadata v0.2.2 h1:aWKAjYaBaOSrpKl57+jnS/3fJRQnxL7TvR/u1VVbt6k= -cloud.google.com/go/compute/metadata v0.2.2/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -684,8 +684,6 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.3/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/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -786,8 +784,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn 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.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= -github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= +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/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= @@ -806,8 +804,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/urfave/cli v1.22.7 h1:aXiFAgRugfJ27UFDsGJ9DB2FvTC73hlVXFSqq5bo9eU= github.com/urfave/cli v1.22.7/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -1180,8 +1178,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.105.0 h1:t6P9Jj+6XTn4U9I2wycQai6Q/Kz7iOT+QzjJ3G2V4x8= -google.golang.org/api v0.105.0/go.mod h1:qh7eD5FJks5+BcE+cjBIm6Gz8vioK7EHvnlniqXBnqI= +google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= 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= @@ -1234,8 +1232,8 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70= -google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= 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= @@ -1258,8 +1256,8 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG 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.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= +google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= 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= From c07afbb42fd7daef7cb614a0a8a34521fe73a5b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 08:39:11 +0100 Subject: [PATCH 0751/2268] chore(deps): Bump github.com/fluxcd/pkg/kustomize from 0.12.0 to 0.13.0 (#294) Bumps [github.com/fluxcd/pkg/kustomize](https://github.com/fluxcd/pkg) from 0.12.0 to 0.13.0. - [Release notes](https://github.com/fluxcd/pkg/releases) - [Commits](https://github.com/fluxcd/pkg/compare/oci/v0.12.0...oci/v0.13.0) --- updated-dependencies: - dependency-name: github.com/fluxcd/pkg/kustomize dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 728bec1e9..2ed371303 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/docker/distribution v2.8.1+incompatible // See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df - github.com/fluxcd/pkg/kustomize v0.12.0 + github.com/fluxcd/pkg/kustomize v0.13.0 github.com/go-playground/validator/v10 v10.11.2 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 @@ -64,7 +64,7 @@ require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.24.2 + github.com/onsi/gomega v1.26.0 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.1 @@ -128,7 +128,7 @@ require ( github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect - github.com/fluxcd/pkg/apis/kustomize v0.7.0 // indirect + github.com/fluxcd/pkg/apis/kustomize v0.8.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect diff --git a/go.sum b/go.sum index 4796239a1..0b63075c3 100644 --- a/go.sum +++ b/go.sum @@ -259,10 +259,10 @@ github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBd github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df h1:2BHXJp1PwX7D47Q2oaKDekn+BZVZCmxeCWNi+FyownE= github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df/go.mod h1:raWgfUV7lDQVXp4QXUaeNNJkRVKz97UQuF+0kdY7Vmo= -github.com/fluxcd/pkg/apis/kustomize v0.7.0 h1:X2htBmJ91nGYv4d93gin665MFWKNGiNwUiZ08/Zz0hY= -github.com/fluxcd/pkg/apis/kustomize v0.7.0/go.mod h1:Mu+KdktsEKWA4l/33CZdY5lB4hz51mqfcLzBZSwAqVg= -github.com/fluxcd/pkg/kustomize v0.12.0 h1:4MQdbP3M8NTIcr8TgNMxRCN+2xZoMWtCDDS3RiOT+8M= -github.com/fluxcd/pkg/kustomize v0.12.0/go.mod h1:awHID4OKe2/WAfTFg4u0fURXZPUkrIslSZNSPX9MEFQ= +github.com/fluxcd/pkg/apis/kustomize v0.8.0 h1:A6aLolxPV2Sll44SOHiX96lbXXmRZmS5BoEerkRHrfM= +github.com/fluxcd/pkg/apis/kustomize v0.8.0/go.mod h1:9DPEVSfVIkiC2H3Dk6Ght4YJkswhYIaufXla4tB5Y84= +github.com/fluxcd/pkg/kustomize v0.13.0 h1:oC50lpGdz/04aH4dPS/kRBjo+7PUx7BgGsJtSS0CmmE= +github.com/fluxcd/pkg/kustomize v0.13.0/go.mod h1:6vAmxEe/41jBEspGq4OZA/4WlnszPyavm74TGSEVpXg= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -665,8 +665,8 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow= -github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= -github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= +github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= From a3471516ed1812cd357e41ca3d35ac8e2e171dcc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Feb 2023 08:41:17 +0100 Subject: [PATCH 0752/2268] fix: Don't fail known_hosts verification when the key-type doesn't match (#287) Instead, ask for confirmation again. --- pkg/git/auth/ssh_known_hosts.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/git/auth/ssh_known_hosts.go b/pkg/git/auth/ssh_known_hosts.go index 9e8017040..ec375b1f2 100644 --- a/pkg/git/auth/ssh_known_hosts.go +++ b/pkg/git/auth/ssh_known_hosts.go @@ -74,9 +74,6 @@ func verifyHost(messageCallbacks messages.MessageCallbacks, host string, remote for _, f := range files { hostFound, err := goph.CheckKnownHost(host, remote, key, f) - if hostFound && err != nil { - return err - } if hostFound && err == nil { return nil } From 01a03d5086bd485686d0f0ebc071695a63e777ff Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Feb 2023 16:30:12 +0100 Subject: [PATCH 0753/2268] feat: Deprecate fixed images from external targetConfig --- pkg/kluctl_project/targets.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index baa2aa52f..e32f627c8 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -252,6 +252,9 @@ func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) if targetConfig.Args != nil { target.Args.Merge(targetConfig.Args) } + if len(targetConfig.Images) != 0 { + status.Deprecation(c.ctx, "target-config-images", "specifying fixed images via external 'targetConfig' is deprecated and support for this will be removed in a future kluctl release.") + } // We prepend the dynamic images to ensure they get higher priority later target.Images = append(targetConfig.Images, target.Images...) From c257fb112e04e93e2b1c2fc8cbe6dbafcd1f2f2e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Feb 2023 16:37:32 +0100 Subject: [PATCH 0754/2268] refactor: Load vars in NewDeploymentItem instead of at render time --- pkg/deployment/deployment_item.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 3e0f3ec56..6d02374cd 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -10,6 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/fs" "k8s.io/apimachinery/pkg/runtime/schema" @@ -27,6 +28,7 @@ type DeploymentItem struct { Project *DeploymentProject Inclusion *utils.Inclusion Config *types.DeploymentItemConfig + VarsCtx *vars.VarsCtx dir *string index int @@ -51,6 +53,7 @@ func NewDeploymentItem(ctx SharedContext, project *DeploymentProject, collection Project: project, Inclusion: collection.Inclusion, Config: config, + VarsCtx: project.VarsCtx.Copy(), dir: dir, index: index, } @@ -81,6 +84,12 @@ func NewDeploymentItem(ctx SharedContext, project *DeploymentProject, collection di.RenderedDir = filepath.Join(di.RenderedSourceRootDir, di.RelRenderedDir) di.renderedYamlPath = filepath.Join(di.RenderedDir, ".rendered.yml") } + + err = di.Project.loadVarsList(di.VarsCtx, di.Config.Vars) + if err != nil { + return nil, err + } + return di, nil } @@ -115,13 +124,6 @@ func (di *DeploymentItem) render(forSeal bool) error { return err } - varsCtx := di.Project.VarsCtx.Copy() - - err = di.Project.loadVarsList(varsCtx, di.Config.Vars) - if err != nil { - return err - } - var excludePatterns []string excludePatterns = append(excludePatterns, "**/.git") @@ -154,7 +156,7 @@ func (di *DeploymentItem) render(forSeal bool) error { // also add deployment item dir to search dirs searchDirs = append([]string{*di.dir}, searchDirs...) - return varsCtx.RenderDirectory( + return di.VarsCtx.RenderDirectory( filepath.Join(di.Project.source.dir, di.RelToSourceItemDir), di.RenderedDir, excludePatterns, From 4829488670c5b7e40f1988e1221ca39221c66bbb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Feb 2023 17:25:11 +0100 Subject: [PATCH 0755/2268] fix: Load deprecated args from deployment.yaml after .kluctl.yaml --- pkg/kluctl_project/target_context.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index f1619b2b9..1ccf21812 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -183,6 +183,11 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, externalArgs *uo.U } allArgs.Merge(externalArgs) + err = deployment.LoadDefaultArgs(p.Config.Args, allArgs) + if err != nil { + return nil, err + } + deprecatedArgs, err := deployment.LoadDeprecatedDeploymentArgs(p.ctx, p.ProjectDir, varsCtx, allArgs) if err != nil { return nil, err @@ -192,11 +197,6 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, externalArgs *uo.U return nil, fmt.Errorf("mixing deprecated 'args' from deployment.yaml and .kluctl.yaml is not allowed") } - err = deployment.LoadDefaultArgs(p.Config.Args, allArgs) - if err != nil { - return nil, err - } - varsCtx.UpdateChild("args", allArgs) return varsCtx, nil From 405b0b7c39bf3c57c5e62bf4d68e7376c37d4f9b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Feb 2023 17:31:14 +0100 Subject: [PATCH 0756/2268] feat: Support fixed images configuration from vars --- pkg/deployment/deployment_item.go | 2 +- pkg/deployment/images.go | 77 +++++++++++++++++++++++-------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 6d02374cd..05678dd8c 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -592,7 +592,7 @@ func (di *DeploymentItem) postprocessObjects(images *Images) error { } // Resolve image placeholders - err := images.ResolvePlaceholders(di.ctx.K, o, di.RelRenderedDir, di.Tags.ListKeys()) + err := images.ResolvePlaceholders(di.ctx.K, o, di.RelRenderedDir, di.Tags.ListKeys(), di.VarsCtx.Vars) if err != nil { errs = multierror.Append(errs, err) } diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 8dc9c5f77..24964fe94 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -66,24 +66,60 @@ func (images *Images) SeenImages(simple bool) []types.FixedImage { return ret } -func (images *Images) GetFixedImage(image string, namespace string, deployment string, container string) *string { - for i := len(images.fixedImages) - 1; i >= 0; i-- { - fi := &images.fixedImages[i] - if fi.Image != image { - continue - } - if fi.Namespace != nil && namespace != *fi.Namespace { - continue - } - if fi.Deployment != nil && deployment != *fi.Deployment { - continue +func (images *Images) parseFixedImagesFromVars(vars *uo.UnstructuredObject) ([]types.FixedImage, error) { + fisU, _, err := vars.GetNestedObjectList("images") + if err != nil { + return nil, fmt.Errorf("failed to parse fixed images from vars: %w", err) + } + fis := make([]types.FixedImage, 0, len(fisU)) + for i, u := range fisU { + var fi types.FixedImage + err = u.ToStruct(&fi) + if err != nil { + return nil, fmt.Errorf("failed to parse fixed image from vars at index %d: %w", i, err) } - if fi.Container != nil && container != *fi.Container { - continue + fis = append(fis, fi) + } + return fis, nil +} + +func (images *Images) getFixedImage(image string, namespace string, deployment string, container string, vars *uo.UnstructuredObject) (*string, error) { + cmpList := func(fis []types.FixedImage) *string { + for i := len(fis) - 1; i >= 0; i-- { + fi := fis[i] + if fi.Image != image { + continue + } + if fi.Namespace != nil && namespace != *fi.Namespace { + continue + } + if fi.Deployment != nil && deployment != *fi.Deployment { + continue + } + if fi.Container != nil && container != *fi.Container { + continue + } + + return &fi.ResultImage } - return &fi.ResultImage + return nil } - return nil + + fisFromVars, err := images.parseFixedImagesFromVars(vars) + if err != nil { + return nil, err + } + + fi := cmpList(images.fixedImages) + if fi != nil { + return fi, nil + } + fi = cmpList(fisFromVars) + if fi != nil { + return fi, nil + } + + return nil, nil } func (images *Images) GetLatestImageFromRegistry(image string, latestVersion string) (*string, error) { @@ -204,7 +240,7 @@ func (images *Images) FindPlaceholders(o *uo.UnstructuredObject) ([]placeHolder, return ret, nil } -func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredObject, deploymentDir string, tags []string) error { +func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredObject, deploymentDir string, tags []string, vars *uo.UnstructuredObject) error { placeholders, err := images.FindPlaceholders(o) if err != nil { return err @@ -240,7 +276,7 @@ func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredO } } - resultImage, err := images.resolveImage(ph, ref, deployment, deployed, deploymentDir, tags) + resultImage, err := images.resolveImage(ph, ref, deployment, deployed, deploymentDir, tags, vars) if err != nil { return err } @@ -257,8 +293,11 @@ func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredO return nil } -func (images *Images) resolveImage(ph placeHolder, ref k8s2.ObjectRef, deployment string, deployed *string, deploymentDir string, tags []string) (*string, error) { - fixed := images.GetFixedImage(ph.Image, ref.Namespace, deployment, ph.Container) +func (images *Images) resolveImage(ph placeHolder, ref k8s2.ObjectRef, deployment string, deployed *string, deploymentDir string, tags []string, vars *uo.UnstructuredObject) (*string, error) { + fixed, err := images.getFixedImage(ph.Image, ref.Namespace, deployment, ph.Container, vars) + if err != nil { + return nil, err + } registry, err := images.GetLatestImageFromRegistry(ph.Image, ph.LatestVersion) if err != nil { From 9ac3b0fc01706159257c8c393803ca62107916d4 Mon Sep 17 00:00:00 2001 From: Petr Michalec Date: Tue, 7 Feb 2023 20:29:30 +0100 Subject: [PATCH 0757/2268] improve subDir path validation (#279) Closes: https://github.com/kluctl/kluctl/issues/269 --- pkg/types/git_project.go | 27 ++++++++++++++++++++++++--- pkg/types/validators_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 pkg/types/validators_test.go diff --git a/pkg/types/git_project.go b/pkg/types/git_project.go index dd8c7bae6..5183cf964 100644 --- a/pkg/types/git_project.go +++ b/pkg/types/git_project.go @@ -1,12 +1,18 @@ package types import ( + "fmt" + "regexp" + "strings" + "github.com/go-playground/validator/v10" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/yaml" - "strings" ) +// gitDirPatternNeg defines forbiden characters on git directory path/subDir +var gitDirPatternNeg = regexp.MustCompile(`[\\\/:\*?"<>|[:cntrl:]\0^]`) + type GitProject struct { Url git_url.GitUrl `yaml:"url" validate:"required"` Ref string `yaml:"ref,omitempty"` @@ -22,10 +28,25 @@ func (gp *GitProject) UnmarshalYAML(unmarshal func(interface{}) error) error { return unmarshal((*raw)(gp)) } +// invalidDirName evaluate directory name against forbidden characters +func invalidDirName(dirName string) bool { + return gitDirPatternNeg.MatchString(dirName) +} + +// validateGitSubDir evaluate syntax for subdirectory path +func validateGitSubDir(path string) bool { + for _, dirName := range strings.Split(path, "/") { + if invalidDirName(dirName) { + return false + } + } + return true +} + func ValidateGitProject(sl validator.StructLevel) { gp := sl.Current().Interface().(GitProject) - if strings.Index(gp.SubDir, "/") != -1 { - sl.ReportError(gp.SubDir, "subDir", "SubDir", "/ is not allowed in subDir", "") + if !validateGitSubDir(gp.SubDir) { + sl.ReportError(gp.SubDir, "subDir", "SubDir", fmt.Sprintf("'%s' is not valid git subdirectory path", gp.SubDir), "") } } diff --git a/pkg/types/validators_test.go b/pkg/types/validators_test.go new file mode 100644 index 000000000..e7c2067f3 --- /dev/null +++ b/pkg/types/validators_test.go @@ -0,0 +1,35 @@ +package types + +import ( + "testing" + + validator "github.com/go-playground/validator/v10" + "github.com/stretchr/testify/assert" +) + +func TestValidateGitProjectSubDir(t *testing.T) { + + var subDirItems = []struct { + have string + want bool + }{ + {"subDir", true}, + {"subDir/../subDir", true}, + {"subDir?", false}, // wrong characters + {"subDir*/Another", false}, // wrong characters + {"subDir/../sub?D|ir", false}, // wrong characters + } + + validate := validator.New() + validate.RegisterStructValidation(ValidateGitProject, GitProject{}) + + for _, item := range subDirItems { + gp := GitProject{SubDir: item.have} + err := validate.Struct(gp) + if item.want { + assert.Nil(t, err) + } else { + assert.Error(t, err) + } + } +} From dd4d92ea48b55ddc81e82c438fa2a036755ad850 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:00:47 +0100 Subject: [PATCH 0758/2268] chore(deps): Bump github.com/ohler55/ojg from 1.17.3 to 1.17.4 (#300) Bumps [github.com/ohler55/ojg](https://github.com/ohler55/ojg) from 1.17.3 to 1.17.4. - [Release notes](https://github.com/ohler55/ojg/releases) - [Changelog](https://github.com/ohler55/ojg/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/ojg/compare/v1.17.3...v1.17.4) --- updated-dependencies: - dependency-name: github.com/ohler55/ojg dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 36e68afe3..953b86b30 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.17.3 + github.com/ohler55/ojg v1.17.4 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.9.0 diff --git a/go.sum b/go.sum index 410b73528..c8a2b1211 100644 --- a/go.sum +++ b/go.sum @@ -661,8 +661,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/ohler55/ojg v1.17.3 h1:NsyfSN+GScWFzXZCVuVimK0xOqUEqTSwuZ+J1WA50nw= -github.com/ohler55/ojg v1.17.3/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= +github.com/ohler55/ojg v1.17.4 h1:6Ss87DyAZHU0ODZu6Cmuahj5UiVaRD1n8C4KNm0qMYg= +github.com/ohler55/ojg v1.17.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= From e99b006260cc60ce2b403e3530f4aa25a18086db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:01:04 +0100 Subject: [PATCH 0759/2268] chore(deps): Bump k8s.io/klog/v2 from 2.80.1 to 2.90.0 (#299) Bumps [k8s.io/klog/v2](https://github.com/kubernetes/klog) from 2.80.1 to 2.90.0. - [Release notes](https://github.com/kubernetes/klog/releases) - [Changelog](https://github.com/kubernetes/klog/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes/klog/compare/v2.80.1...v2.90.0) --- updated-dependencies: - dependency-name: k8s.io/klog/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 953b86b30..d84385a11 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( k8s.io/apiextensions-apiserver v0.26.1 k8s.io/apimachinery v0.26.1 k8s.io/client-go v0.26.1 - k8s.io/klog/v2 v2.80.1 + k8s.io/klog/v2 v2.90.0 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) diff --git a/go.sum b/go.sum index c8a2b1211..bc2ea3b3d 100644 --- a/go.sum +++ b/go.sum @@ -1333,8 +1333,8 @@ k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.90.0 h1:VkTxIV/FjRXn1fgNNcKGM8cfmL1Z33ZjXRTVxKCoF5M= +k8s.io/klog/v2 v2.90.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715 h1:tBEbstoM+K0FiBV5KGAKQ0kuvf54v/hwpldiJt69w1s= k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= From 8082e89fcc666ae25f4699293f03b114667b464b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:01:30 +0100 Subject: [PATCH 0760/2268] chore(deps): Bump golang.org/x/sys from 0.4.0 to 0.5.0 (#298) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.4.0 to 0.5.0. - [Release notes](https://github.com/golang/sys/releases) - [Commits](https://github.com/golang/sys/compare/v0.4.0...v0.5.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d84385a11..9f80ab451 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( golang.org/x/crypto v0.5.0 golang.org/x/net v0.5.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.4.0 + golang.org/x/sys v0.5.0 golang.org/x/term v0.4.0 golang.org/x/text v0.6.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index bc2ea3b3d..09f9b9014 100644 --- a/go.sum +++ b/go.sum @@ -1070,8 +1070,8 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 6ca2413a25b76826474427be77e654b3c6bf9789 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:09:57 +0100 Subject: [PATCH 0761/2268] chore(deps): Bump golang.org/x/term from 0.4.0 to 0.5.0 (#297) Bumps [golang.org/x/term](https://github.com/golang/term) from 0.4.0 to 0.5.0. - [Release notes](https://github.com/golang/term/releases) - [Commits](https://github.com/golang/term/compare/v0.4.0...v0.5.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9f80ab451..0a9e7de6f 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( golang.org/x/net v0.5.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.5.0 - golang.org/x/term v0.4.0 + golang.org/x/term v0.5.0 golang.org/x/text v0.6.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 09f9b9014..a8302d9d2 100644 --- a/go.sum +++ b/go.sum @@ -1078,8 +1078,8 @@ golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 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= From 9c19fd968633ab2372304c11207d0defe74bffe4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Feb 2023 17:33:08 +0100 Subject: [PATCH 0762/2268] feat: Deprecate querying latest image tag from registries --- cmd/kluctl/args/images.go | 2 +- cmd/kluctl/commands/utils.go | 6 ++++++ docs/reference/commands/common-arguments.md | 5 ++++- pkg/deployment/deployment_item.go | 2 +- pkg/deployment/images.go | 17 ++++++++++++----- pkg/kluctl_jinja2/ext/images_ext.py | 4 ++++ 6 files changed, 28 insertions(+), 8 deletions(-) diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index 13aaf244d..7d0364722 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -11,7 +11,7 @@ type ImageFlags struct { FixedImage []string `group:"images" short:"F" help:"Pin an image to a given version. Expects '--fixed-image=image<:namespace:deployment:container>=result'"` FixedImagesFile existingFileType `group:"images" help:"Use .yaml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format" exts:"yml,yaml"` UpdateImages bool `group:"images" short:"u" help:"This causes kluctl to prefer the latest image found in registries, based on the 'latest_image' filters provided to 'images.get_image(...)' calls. Use this flag if you want to update to the latest versions/tags of all images. '-u' takes precedence over '--fixed-image/--fixed-images-file', meaning that the latest images are used even if an older image is given via fixed images."` - OfflineImages bool `group:"images" help:"Omit contacting image registries and do not query for latest image tags."` + OfflineImages bool `group:"images" help:"DEPRECATED: Omit contacting image registries and do not query for latest image tags. This flag is by default set to true. At the same time, the whole requesting of image tags from registries functionality is deprecated and will be removed from kluctl in a future release." default:"true"` } func (args *ImageFlags) LoadFixedImagesFromArgs() ([]types.FixedImage, error) { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 2a610fee0..909a14c3b 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -130,6 +130,12 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm if err != nil { return fmt.Errorf("failed to parse registry auth from environment: %w", err) } + if args.imageFlags.UpdateImages { + status.Deprecation(ctx, "update-images", "--update-images is deprecated and will be removed in a future kluctl release.") + } + if !args.imageFlags.OfflineImages { + status.Deprecation(ctx, "online-images", "--offline-images=false is deprecated and will be removed in a future kluctl release.") + } images, err := deployment.NewImages(rh, args.imageFlags.UpdateImages, args.imageFlags.OfflineImages || args.forCompletion) if err != nil { return err diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index c08d93b4b..dbdf48621 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -98,7 +98,10 @@ Image arguments: '--fixed-image=image<:namespace:deployment:container>=result' --fixed-images-file existingfile Use .yaml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format - --offline-images Omit contacting image registries and do not query for latest image tags. + --offline-images DEPRECATED: Omit contacting image registries and do not query for latest + image tags. This flag is by default set to true. At the same time, the + whole requesting of image tags from registries functionality is + deprecated and will be removed from kluctl in a future release. (default true) -u, --update-images This causes kluctl to prefer the latest image found in registries, based on the 'latest_image' filters provided to 'images.get_image(...)' calls. Use this flag if you want to update to the latest versions/tags of all diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 05678dd8c..6d059e96c 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -592,7 +592,7 @@ func (di *DeploymentItem) postprocessObjects(images *Images) error { } // Resolve image placeholders - err := images.ResolvePlaceholders(di.ctx.K, o, di.RelRenderedDir, di.Tags.ListKeys(), di.VarsCtx.Vars) + err := images.ResolvePlaceholders(di.ctx.Ctx, di.ctx.K, o, di.RelRenderedDir, di.Tags.ListKeys(), di.VarsCtx.Vars) if err != nil { errs = multierror.Append(errs, err) } diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 24964fe94..076350faf 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -2,10 +2,12 @@ package deployment import ( "bytes" + "context" "encoding/base64" "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/registries" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -158,8 +160,9 @@ const beginPlaceholder = "XXXXXbegin_get_image_" const endPlaceholder = "_end_get_imageXXXXX" type placeHolder struct { - Image string `yaml:"image"` - LatestVersion string `yaml:"latestVersion"` + Image string `yaml:"image"` + LatestVersion string `yaml:"latestVersion"` + HasLatestVersion bool `yaml:"hasLatestVersion"` Container string @@ -240,7 +243,7 @@ func (images *Images) FindPlaceholders(o *uo.UnstructuredObject) ([]placeHolder, return ret, nil } -func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredObject, deploymentDir string, tags []string, vars *uo.UnstructuredObject) error { +func (images *Images) ResolvePlaceholders(ctx context.Context, k *k8s.K8sCluster, o *uo.UnstructuredObject, deploymentDir string, tags []string, vars *uo.UnstructuredObject) error { placeholders, err := images.FindPlaceholders(o) if err != nil { return err @@ -276,7 +279,7 @@ func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredO } } - resultImage, err := images.resolveImage(ph, ref, deployment, deployed, deploymentDir, tags, vars) + resultImage, err := images.resolveImage(ctx, ph, ref, deployment, deployed, deploymentDir, tags, vars) if err != nil { return err } @@ -293,12 +296,16 @@ func (images *Images) ResolvePlaceholders(k *k8s.K8sCluster, o *uo.UnstructuredO return nil } -func (images *Images) resolveImage(ph placeHolder, ref k8s2.ObjectRef, deployment string, deployed *string, deploymentDir string, tags []string, vars *uo.UnstructuredObject) (*string, error) { +func (images *Images) resolveImage(ctx context.Context, ph placeHolder, ref k8s2.ObjectRef, deployment string, deployed *string, deploymentDir string, tags []string, vars *uo.UnstructuredObject) (*string, error) { fixed, err := images.getFixedImage(ph.Image, ref.Namespace, deployment, ph.Container, vars) if err != nil { return nil, err } + if ph.HasLatestVersion { + status.Deprecation(ctx, "latest-version-filter", "latest_version is deprecated when using images.get_image()") + } + registry, err := images.GetLatestImageFromRegistry(ph.Image, ph.LatestVersion) if err != nil { return nil, err diff --git a/pkg/kluctl_jinja2/ext/images_ext.py b/pkg/kluctl_jinja2/ext/images_ext.py index 4c363b194..ba523653a 100644 --- a/pkg/kluctl_jinja2/ext/images_ext.py +++ b/pkg/kluctl_jinja2/ext/images_ext.py @@ -12,11 +12,15 @@ def __init__(self, environment): environment.globals.update(self.build_images_vars()) def get_image_wrapper(self, image, latest_version=None): + has_latest_version = False if latest_version is None: latest_version = "semver()" + else: + has_latest_version = True placeholder = { "image": image, "latestVersion": str(latest_version), + "hasLatestVersion": has_latest_version, } j = json.dumps(placeholder) j = base64.b64encode(j.encode("utf8")).decode("utf8") From 7417b33c01dde40f8255aa1b5ea30a2af189125e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 09:11:09 +0100 Subject: [PATCH 0763/2268] feat: Deprecate check-image-updates command --- cmd/kluctl/commands/cmd_check_image_updates.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index 64471dd3b..7e7966ed0 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -19,10 +19,11 @@ type checkImageUpdatesCmd struct { } func (cmd *checkImageUpdatesCmd) Help() string { - return `This is based on a best effort approach and might give many false-positives.` + return `DEPRECATED: This is based on a best effort approach and might give many false-positives.` } func (cmd *checkImageUpdatesCmd) Run(ctx context.Context) error { + status.Deprecation(ctx, "check-image-updates", "check-image-updates is deprecated and will be removed in a future kluctl release.") ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, From 8aa562d77d8abb2bb4e97bc94356e39592db2516 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 09:11:59 +0100 Subject: [PATCH 0764/2268] fix: Show deprecation message for sealedSecrets config --- pkg/deployment/deployment_project.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 9f3909ab7..7ee143f3d 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -3,6 +3,7 @@ package deployment import ( "fmt" securejoin "github.com/cyphar/filepath-securejoin" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -60,6 +61,10 @@ func NewDeploymentProject(ctx SharedContext, varsCtx *vars.VarsCtx, source Sourc return nil, fmt.Errorf("failed to load includes for %s: %w", dir, err) } + if dp.Config.SealedSecrets != nil { + status.Deprecation(ctx.Ctx, "sealed-secrets-config", "'sealedSecrets' is deprecated in deployment projects. Support for it will be removed in a future kluctl release.") + } + return dp, nil } From a48e3a3e877e7f503a1164a13243203ae60211c0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 09:56:29 +0100 Subject: [PATCH 0765/2268] docs: Update docs in regard to images.get_image changes --- docs/reference/deployments/images.md | 127 +++++++----------- .../kluctl-project/targets/README.md | 2 +- .../templating/predefined-variables.md | 3 - 3 files changed, 50 insertions(+), 82 deletions(-) diff --git a/docs/reference/deployments/images.md b/docs/reference/deployments/images.md index b6270cf7d..0328b5403 100644 --- a/docs/reference/deployments/images.md +++ b/docs/reference/deployments/images.md @@ -13,54 +13,26 @@ description: > There are usually 2 different scenarios where Container Images need to be specified: 1. When deploying third party applications like nginx, redis, ... (e.g. via the [Helm integration](./helm.md)).
- * In this case, image versions/tags rarely change, and if they do, this is an explicit change to the deployment. -1. When deploying your own applications.
- * In this case, image versions/tags might change very rapidly, sometimes multiple times per hour. It would be too much - effort and overhead when this would be managed explicitly via your deployment. Even with Jinja2 templating, this - would be hard to maintain. + * In this case, image versions/tags rarely change, and if they do, this is an explicit change to the deployment. This + means it's fine to have the image versions/tags directly in the deployment manifests. +2. When deploying your own applications.
+ * In this case, image versions/tags might change very rapidly, sometimes multiple times per hour. Having these + versions/tags directly in the deployment manifests can easily lead to commit spam and hard to manage + multi-environment deployments. kluctl offers a better solution for the second case. -## Dynamic versions/tags - -kluctl is able to ask the used container registry for a list of tags/versions available for an image. It then can -sort the list of images via a configurable order and then use the latest image for your deployment. - -It however only does this when the involved resource (e.g. a `Deployment` or `StatefulSet`) is not yet deployed. In case -it is already deployed, the already deployed image will be reused to avoid undesired re-deployment/re-starting of -otherwise unchanged resources. - ## images.get_image() This is solved via a templating function that is available in all templates/resources. The function is part of the global `images` object and expects the following arguments: -`images.get_image(image, latest_version)` +`images.get_image(image)` * image - * The image location, excluding the tag. Please see [supported image registries](#supported-image-registries-and-authentication) to - understand which registries are supported.` -* latest_version - * Configures how tags/versions are sorted and thus how the latest image is determined. Can be: - * `version.semver()`
- Filters and sorts by loose semantic versioning. Versions must start with a number. It allows unlimited - `.` inside the version. It treats versions with a suffix as less then versions without a suffix - (e.g. 1.0-rc1 < 1.0). Two versions which only differ by suffix are sorted semantically. - * `version.prefix(prefix)`
- Only allows tags with the given prefix and then applies the same logic as images.semver() to whatever - follows right after the prefix. You can override the handling of the right part by providing `suffix=xxx`, - while `xxx` is another version filter, e.g. `version.prefix("master-", suffix=version.number()) - * `version.number()`
- Only allows plain numbers as version numbers sorts them accordingly. - * `version.regex(regex)`
- Only allows versions/tags that match the given regex. Sorting is done the same way as in version.semver(), - except that versions do not necessarily need to start with a number. - -The mentioned version filters must be specified as strings. For example, - -`images.get_version("my-image", "prefix('master-', suffix=number())")`. - -If no version_filter is specified, then it defaults to `"semver()"`. + * The image name/repository. It is looked up the list of fixed images. + +The function will lookup the given image in the list of fixed images and return the last match. Example deployment: @@ -77,59 +49,58 @@ spec: image: "{{ images.get_image('registry.gitlab.com/my-group/my-project') }}" ``` -## Always using the latest images -If you want to use the latest image no matter if an older version is already deployed, use the `-u` flag to your -`deploy`, `diff` or `list-images` commands. +## Fixed images -You can restrict updating to individual images by using `-I/--include-tag`. This is useful when using CI/CD for example, -where you often want to perform a deployment that only updates a single application/service from your large deployment. +Fixed images can be configured multiple methods: +1. Command line argument `--fixed-image` +2. Command line argument `--fixed-images-file` +3. Target definition +4. Global 'images' variable -## Fixed images via CLI -The described `images.get_image` logic however leads to a loosely defined state on your target cluster/environment. This -might be fine in a CI/CD environment, but might be undesired when deploying to production. In that case, it might be -desirable to explicitly define which versions need to be deployed. +## Command line argument `--fixed-image` -To achieve this, you can use the `-F FIXED_IMAGE` [argument](../commands/common-arguments.md#image-arguments). -`FIXED_IMAGE` must be in the form of `-F image<:namespace:deployment:container>=result`. For example, to pin the image -`registry.gitlab.com/my-group/my-project` to the tag `1.1.2` you'd have to specify -`-F registry.gitlab.com/my-group/my-project=registry.gitlab.com/my-group/my-project:1.1.2`. +You can pass fixed images configuration via the `--fixed-image` [argument](../commands/common-arguments.md#image-arguments). +Due to [environment variables support](../commands/environment-variables.md) in the CLI, you can also use the +environment variable `KLUCTL_FIXED_IMAGE_XXX` to configure fixed images. -## Fixed images via a yaml file +The format of the `--fixed-image` argument is `--fixed-image image<:namespace:deployment:container>=result`. The simplest +example is `--fixed-image registry.gitlab.com/my-group/my-project=registry.gitlab.com/my-group/my-project:1.1.2`. -As an alternative to specifying each fixed image via CLI (`--fixed-images-file=`), you can also specify a single -yaml file via CLI which then contains a list of entries that define image/deployment -> imageResult mappings. +## Command line argument `--fixed-images-file` -An example fixed-images files looks like this: +You can also configure fixed images via a yaml file by using `--fixed-images-file /path/to/fixed-images.yaml`. +file: ```yaml images: - image: registry.gitlab.com/my-group/my-project - resultImage: registry.gitlab.com/my-group/my-project:1.1.0 - - image: registry.gitlab.com/my-group/my-project2 - resultImage: registry.gitlab.com/my-group/my-project2:2.0.0 - - deployment: StatefulSet/my-sts - resultImage: registry.gitlab.com/my-group/my-project3:1.0.0 + resultImage: registry.gitlab.com/my-group/my-project:1.1.2 +``` + +The file must contain a single root list named `images` with each entry having the following form: + +```yaml +images: + - image: + resultImage: + # optional fields + namespace: + deployment: / + container: ``` -You can also take an existing deployment and export the already deployed image versions into a fixed-images file by -using the `list-images` command. It will produce a compatible fixed-images file based on the calls to -`images.get_image` if a deployment would be performed with the given arguments. The result of that call is quite -expressive, as it contains all the information gathered while images were collected. Use `--simple` to only return -a list with image -> resultImage mappings. +`image` and `resultImage` are required. All the other fields are optional and allow to specify in detail for which +object the fixed is specified. + +## Target definition -## Supported image registries and authentication -All [v2 API](https://docs.docker.com/registry/spec/api/) based image registries are supported, including the Docker Hub, -Gitlab, and many more. Private registries will need credentials to be setup correctly. This can be done by locally -logging in via `docker login ` or by specifying the following environment variables: +The [target](../kluctl-project/targets/README.md#targets) definition can optionally specify an `images` field that can +contain the same fixed images configuration as found in the `--fixed-images-file` file. -Simply set the following environment variables to pass credentials to you private repository: -1. KLUCTL_REGISTRY_HOST=registry.example.com -2. KLUCTL_REGISTRY_USERNAME=username -3. KLUCTL_REGISTRY_PASSWORD=password +## Global 'images' variable -You can also pass credentials for more registries by adding an index to the environment variables, -e.g. "KLUCTL_REGISTRY_1_HOST=registry.gitlab.com" +You can also define a global variable named `images` via one of the [variable sources](../templating/variable-sources.md). +This variable must be a list of the same format as the images list in the `--fixed-images-file` file. -In case your registry uses self-signed TLS certificates, it is currently required to disable TLS verification for these. -You can do this via `KLUCTL_REGISTRY_TLSVERIFY=1`/`KLUCTL_REGISTRY__TLSVERIFY=1` for the corresponding -`KLUCTL_REGISTRY_HOST`/`KLUCTL_REGISTRY__HOST` or by globally disabling it via `KLUCTL_REGISTRY_DEFAULT_TLSVERIFY=1`. +This option allows to externalize fixed images configuration, meaning that you can maintain image versions outside +the deployment project, e.g. in another [Git repository](../templating/variable-sources.md#git). diff --git a/docs/reference/kluctl-project/targets/README.md b/docs/reference/kluctl-project/targets/README.md index 74673d86d..4dc644fc1 100644 --- a/docs/reference/kluctl-project/targets/README.md +++ b/docs/reference/kluctl-project/targets/README.md @@ -54,7 +54,7 @@ have higher priority. ## images This field specifies a list of fixed images to be used by [`images.get_image(...)`](../../deployments/images.md#imagesget_image). -The format is identical to the [fixed images file](../../deployments/images.md#fixed-images-via-a-yaml-file). +The format is identical to the [fixed images file](../../deployments/images.md#command-line-argument---fixed-images-file). The fixed images specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets.md#images) have higher priority. diff --git a/docs/reference/templating/predefined-variables.md b/docs/reference/templating/predefined-variables.md index a547e1d86..bb1fe4d53 100644 --- a/docs/reference/templating/predefined-variables.md +++ b/docs/reference/templating/predefined-variables.md @@ -23,6 +23,3 @@ This is the target definition of the currently processed target. It contains all ### images This global object provides the dynamic images features described in [images](../deployments/images.md). - -### version -This global object defines latest version filters for `images.get_image(...)`. See [images](../deployments/images.md) for details. From 777f6875432b322f8d3556ab3f78629fe5c7bea7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 17:25:05 +0100 Subject: [PATCH 0766/2268] docs: Add docs for sha256 filter --- docs/reference/templating/filters.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/reference/templating/filters.md b/docs/reference/templating/filters.md index 6495f57cd..5fed49201 100644 --- a/docs/reference/templating/filters.md +++ b/docs/reference/templating/filters.md @@ -84,5 +84,11 @@ Renders the input string with the current Jinja2 context. Example: {{ a | render }} ``` +### sha256 +Calculates the sha256 digest of the input string. Example: +``` +{{ "some-string" | sha256 }} +``` + ### slugify Slugify a string based on [python-slugify](https://github.com/un33k/python-slugify). From b34e771907f7f33c205afa3c2b2486e07f529b79 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 17:26:12 +0100 Subject: [PATCH 0767/2268] feat: Rename kluctl.io/kustomize_dir annotation to kluctl.io/deployment-item-dir --- pkg/deployment/deployment_item.go | 3 +-- pkg/deployment/utils/remote_objects_utils.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 6d059e96c..b32964eae 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -104,9 +104,8 @@ func (di *DeploymentItem) getCommonLabels() map[string]string { } func (di *DeploymentItem) getCommonAnnotations() map[string]string { - // TODO change it to kluctl.io/deployment_dir a := map[string]string{ - "kluctl.io/kustomize_dir": filepath.ToSlash(di.RelToSourceItemDir), + "kluctl.io/deployment-item-dir": filepath.ToSlash(di.RelToSourceItemDir), } if di.Config.SkipDeleteIfTags { a["kluctl.io/skip-delete-if-tags"] = "true" diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index 1ff37177f..ff505702e 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -237,7 +237,7 @@ func (u *RemoteObjectUtils) getInclusionEntries(o *uo.UnstructuredObject) []util }) } - if itemDir := o.GetK8sAnnotation("kluctl.io/kustomize_dir"); itemDir != nil { + if itemDir := o.GetK8sAnnotation("kluctl.io/deployment-item-dir"); itemDir != nil { iv = append(iv, utils.InclusionEntry{ Type: "deploymentItemDir", Value: *itemDir, From 0e4a304ada84217c3d38aa142c1d281151982bd9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 17:29:58 +0100 Subject: [PATCH 0768/2268] refactor: Pass external args via loadArgs instead of TargetContextParams --- cmd/kluctl/commands/cmd_list_targets.go | 2 +- cmd/kluctl/commands/cmd_seal.go | 2 +- cmd/kluctl/commands/completion.go | 19 +++++++----- cmd/kluctl/commands/utils.go | 41 +++++++++++++------------ pkg/kluctl_project/project_load.go | 2 ++ pkg/kluctl_project/target_context.go | 9 +++--- pkg/kluctl_project/targets.go | 7 ++--- 7 files changed, 46 insertions(+), 36 deletions(-) diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index 91eacf231..dca1b646d 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -17,7 +17,7 @@ func (cmd *listTargetsCmd) Help() string { } func (cmd *listTargetsCmd) Run(ctx context.Context) error { - return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, nil, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var result []*types.Target for _, t := range p.DynamicTargets { result = append(result, t.Target) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 18fd57647..ec00b4612 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -155,7 +155,7 @@ func (cmd *sealCmd) loadCert(cmdCtx *commandCtx) (*x509.Certificate, error) { } func (cmd *sealCmd) Run(ctx context.Context) error { - return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, nil, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { hadError := false baseTargets := make(map[string]bool) diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 32ffab84f..225ae0fba 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -16,6 +16,7 @@ import ( func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) error { v := reflect.ValueOf(cmdStruct).Elem() projectFlags := v.FieldByName("ProjectFlags") + argsFlags := v.FieldByName("ArgsFlags") targetFlags := v.FieldByName("TargetFlags") inclusionFlags := v.FieldByName("InclusionFlags") imageFlags := v.FieldByName("ImageFlags") @@ -23,7 +24,11 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err ctx := context.Background() if projectFlags.IsValid() && targetFlags.IsValid() { - _ = ccmd.RegisterFlagCompletionFunc("target", buildTargetCompletionFunc(ctx, projectFlags.Addr().Interface().(*args.ProjectFlags))) + var argsFlag2 *args.ArgsFlags + if argsFlags.IsValid() { + argsFlag2 = argsFlags.Addr().Interface().(*args.ArgsFlags) + } + _ = ccmd.RegisterFlagCompletionFunc("target", buildTargetCompletionFunc(ctx, projectFlags.Addr().Interface().(*args.ProjectFlags), argsFlag2)) } if projectFlags.IsValid() && inclusionFlags.IsValid() { @@ -42,18 +47,18 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err return nil } -func withProjectForCompletion(ctx context.Context, projectArgs *args.ProjectFlags, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { +func withProjectForCompletion(ctx context.Context, projectArgs *args.ProjectFlags, argsFlags *args.ArgsFlags, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { // let's not update git caches too often projectArgs.GitCacheUpdateInterval = time.Second * 60 - return withKluctlProjectFromArgs(ctx, *projectArgs, false, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(ctx, *projectArgs, argsFlags, false, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return cb(ctx, p) }) } -func buildTargetCompletionFunc(ctx context.Context, projectArgs *args.ProjectFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func buildTargetCompletionFunc(ctx context.Context, projectArgs *args.ProjectFlags, argsFlags *args.ArgsFlags) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var ret []string - err := withProjectForCompletion(ctx, projectArgs, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(ctx, projectArgs, argsFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { for _, t := range p.DynamicTargets { ret = append(ret, t.Target.Name) } @@ -99,7 +104,7 @@ func buildInclusionCompletionFunc(ctx context.Context, cmdStruct interface{}, fo var deploymentItemDirs utils.OrderedMap var mutex sync.Mutex - err := withProjectForCompletion(ctx, &ptArgs.projectFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(ctx, &ptArgs.projectFlags, &ptArgs.argsFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { for _, t := range p.DynamicTargets { @@ -153,7 +158,7 @@ func buildImagesCompletionFunc(ctx context.Context, cmdStruct interface{}) func( var images utils.OrderedMap var mutex sync.Mutex - err := withProjectForCompletion(ctx, &ptArgs.projectFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + err := withProjectForCompletion(ctx, &ptArgs.projectFlags, &ptArgs.argsFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { for _, t := range p.DynamicTargets { diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 909a14c3b..3259ab704 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -25,7 +25,7 @@ import ( "k8s.io/client-go/tools/clientcmd/api" ) -func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { +func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFlags, argsFlags *args.ArgsFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { tmpDir, err := os.MkdirTemp(utils.GetTmpBaseDir(ctx), "project-") if err != nil { return fmt.Errorf("creating temporary project directory failed: %w", err) @@ -80,10 +80,30 @@ func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFla rp := repocache.NewGitRepoCache(ctx, sshPool, gitAuth, repoOverrides, projectFlags.GitCacheUpdateInterval) defer rp.Clear() + var externalArgs *uo.UnstructuredObject + if argsFlags != nil { + optionArgs, err := deployment.ParseArgs(argsFlags.Arg) + if err != nil { + return err + } + externalArgs, err = deployment.ConvertArgsToVars(optionArgs, true) + if err != nil { + return err + } + for _, a := range argsFlags.ArgsFromFile { + optionArgs2, err := uo.FromFile(a) + if err != nil { + return err + } + externalArgs.Merge(optionArgs2) + } + } + loadArgs := kluctl_project.LoadKluctlProjectArgs{ RepoRoot: repoRoot, ProjectDir: projectDir, ProjectConfig: projectFlags.ProjectConfig.String(), + ExternalArgs: externalArgs, RP: rp, ClientConfigGetter: clientConfigGetter(forCompletion), } @@ -119,7 +139,7 @@ type commandCtx struct { } func withProjectCommandContext(ctx context.Context, args projectTargetCommandArgs, cb func(cmdCtx *commandCtx) error) error { - return withKluctlProjectFromArgs(ctx, args.projectFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(ctx, args.projectFlags, &args.argsFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return withProjectTargetCommandContext(ctx, args, p, cb) }) } @@ -151,22 +171,6 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm return err } - optionArgs, err := deployment.ParseArgs(args.argsFlags.Arg) - if err != nil { - return err - } - optionArgs2, err := deployment.ConvertArgsToVars(optionArgs, true) - if err != nil { - return err - } - for _, a := range args.argsFlags.ArgsFromFile { - optionArgs3, err := uo.FromFile(a) - if err != nil { - return err - } - optionArgs2.Merge(optionArgs3) - } - renderOutputDir := args.renderOutputDirFlags.RenderOutputDir if renderOutputDir == "" { tmpDir, err := os.MkdirTemp(p.TmpDir, "rendered") @@ -184,7 +188,6 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm OfflineK8s: args.offlineKubernetes, K8sVersion: args.kubernetesVersion, DryRun: args.dryRunArgs == nil || args.dryRunArgs.DryRun || args.forCompletion, - ExternalArgs: optionArgs2, ForSeal: args.forSeal, Images: images, Inclusion: inclusion, diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index c397e5190..2af17c0d0 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -5,6 +5,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd/api" @@ -15,6 +16,7 @@ type LoadKluctlProjectArgs struct { RepoRoot string ProjectDir string ProjectConfig string + ExternalArgs *uo.UnstructuredObject SopsDecrypter *decryptor.Decryptor RP *repocache.GitRepoCache diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 1ccf21812..0a5e46627 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -34,7 +34,6 @@ type TargetContextParams struct { OfflineK8s bool K8sVersion string DryRun bool - ExternalArgs *uo.UnstructuredObject ForSeal bool Images *deployment.Images Inclusion *utils.Inclusion @@ -73,7 +72,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe } target.Context = &clusterContext - varsCtx, err := p.buildVars(target, params.ExternalArgs, params.ForSeal) + varsCtx, err := p.buildVars(target, params.ForSeal) if err != nil { return nil, err } @@ -160,7 +159,7 @@ func (p *LoadedKluctlProject) loadK8sConfig(target *types.Target, offlineK8s boo return clientConfig, "", nil } -func (p *LoadedKluctlProject) buildVars(target *types.Target, externalArgs *uo.UnstructuredObject, forSeal bool) (*vars.VarsCtx, error) { +func (p *LoadedKluctlProject) buildVars(target *types.Target, forSeal bool) (*vars.VarsCtx, error) { varsCtx := vars.NewVarsCtx(p.J2) targetVars, err := uo.FromStruct(target) @@ -181,7 +180,9 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, externalArgs *uo.U } } } - allArgs.Merge(externalArgs) + if p.loadArgs.ExternalArgs != nil { + allArgs.Merge(p.loadArgs.ExternalArgs) + } err = deployment.LoadDefaultArgs(p.Config.Args, allArgs) if err != nil { diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index e32f627c8..eda785735 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -6,7 +6,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" "os" "regexp" @@ -52,7 +51,8 @@ func (c *LoadedKluctlProject) loadTargets() error { err = c.renderTarget(target) if err != nil { - return err + status.Warning(c.ctx, "Failed to load target %s: %v", target.Name, err) + continue } if _, ok := targetNames[target.Name]; ok { @@ -77,8 +77,7 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) error { var errors []error for i := 0; i < 10; i++ { - varsCtx := vars.NewVarsCtx(c.J2) - err := varsCtx.UpdateChildFromStruct("target", target) + varsCtx, err := c.buildVars(target, false) if err != nil { return err } From ce578df1ffc05554ca2844d56819508d609cc14d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 17:31:06 +0100 Subject: [PATCH 0769/2268] feat: Implement deployment/target discriminator --- cmd/kluctl/commands/cmd_delete.go | 14 +++----- cmd/kluctl/commands/cmd_deploy.go | 2 +- cmd/kluctl/commands/cmd_diff.go | 2 +- cmd/kluctl/commands/cmd_prune.go | 2 +- cmd/kluctl/commands/cmd_validate.go | 2 +- pkg/deployment/commands/delete.go | 36 ++++++------------- pkg/deployment/commands/deploy.go | 18 +++++++--- pkg/deployment/commands/diff.go | 18 +++++++--- pkg/deployment/commands/prune.go | 15 +++++--- pkg/deployment/commands/validate.go | 13 ++++--- pkg/deployment/deployment_item.go | 3 ++ pkg/deployment/shared_context.go | 1 + pkg/deployment/utils/remote_objects_utils.go | 20 +++++++---- .../utils/remote_objects_utils_test.go | 5 +-- pkg/kluctl_project/target_context.go | 1 + pkg/kluctl_project/targets.go | 3 ++ pkg/types/kluctl_project.go | 2 ++ 17 files changed, 94 insertions(+), 63 deletions(-) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index bdac4154e..751410b04 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -25,7 +24,7 @@ type deleteCmd struct { args.OutputFormatFlags args.RenderOutputDirFlags - DeleteByLabel []string `group:"misc" short:"l" help:"Override the labels used to find objects for deletion."` + Discriminator string `group:"misc" help:"Override the discriminator used to find objects for deletion."` } func (cmd *deleteCmd) Help() string { @@ -48,14 +47,11 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - cmd2 := commands.NewDeleteCommand(cmdCtx.targetCtx.DeploymentCollection) - - deleteByLabels, err := deployment.ParseArgs(cmd.DeleteByLabel) - if err != nil { - return err + discriminator := cmdCtx.targetCtx.Target.Discriminator + if cmd.Discriminator != "" { + discriminator = cmd.Discriminator } - - cmd2.OverrideDeleteByLabels = deleteByLabels + cmd2 := commands.NewDeleteCommand(discriminator, cmdCtx.targetCtx.DeploymentCollection.Inclusion) objects, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) if err != nil { diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 624d6b6e3..083a97e26 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -55,7 +55,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { status.Trace(cmdCtx.ctx, "enter runCmdDeploy") defer status.Trace(cmdCtx.ctx, "leave runCmdDeploy") - cmd2 := commands.NewDeployCommand(cmdCtx.targetCtx.DeploymentCollection) + cmd2 := commands.NewDeployCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) cmd2.ForceApply = cmd.ForceApply cmd2.ReplaceOnError = cmd.ReplaceOnError cmd2.ForceReplaceOnError = cmd.ForceReplaceOnError diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index a32991d41..b04ff519d 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -39,7 +39,7 @@ func (cmd *diffCmd) Run(ctx context.Context) error { renderOutputDirFlags: cmd.RenderOutputDirFlags, } return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - cmd2 := commands.NewDiffCommand(cmdCtx.targetCtx.DeploymentCollection) + cmd2 := commands.NewDiffCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) cmd2.ForceApply = cmd.ForceApply cmd2.ReplaceOnError = cmd.ReplaceOnError cmd2.ForceReplaceOnError = cmd.ForceReplaceOnError diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 9c887eb1c..5b542cd27 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -45,7 +45,7 @@ func (cmd *pruneCmd) Run(ctx context.Context) error { } func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { - cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.DeploymentCollection) + cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) objects, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index 26b43da2f..532f52e28 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -39,7 +39,7 @@ func (cmd *validateCmd) Run(ctx context.Context) error { } return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { startTime := time.Now() - cmd2 := commands.NewValidateCommand(cmdCtx.ctx, cmdCtx.targetCtx.DeploymentCollection) + cmd2 := commands.NewValidateCommand(cmdCtx.ctx, cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) for true { result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) if err != nil { diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index 6a3040cda..da6d7e5d9 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -3,7 +3,6 @@ package commands import ( "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" @@ -11,41 +10,28 @@ import ( ) type DeleteCommand struct { - c *deployment.DeploymentCollection - OverrideDeleteByLabels map[string]string + discriminator string + inclusion *utils.Inclusion } -func NewDeleteCommand(c *deployment.DeploymentCollection) *DeleteCommand { +func NewDeleteCommand(discriminator string, inclusion *utils.Inclusion) *DeleteCommand { return &DeleteCommand{ - c: c, + discriminator: discriminator, + inclusion: inclusion, } } func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.ObjectRef, error) { - dew := utils2.NewDeploymentErrorsAndWarnings() - - ru := utils2.NewRemoteObjectsUtil(ctx, dew) - - var labels map[string]string - if len(cmd.OverrideDeleteByLabels) != 0 { - labels = cmd.OverrideDeleteByLabels - } else { - labels = cmd.c.Project.GetCommonLabels() - } - - if len(labels) == 0 { - return nil, fmt.Errorf("deletion without using commonLabels in the root deployment.yaml is not allowed") - } - - var inclusion *utils.Inclusion - if cmd.c != nil { - inclusion = cmd.c.Inclusion + if cmd.discriminator == "" { + return nil, fmt.Errorf("deletion without a discriminator is not supported") } - err := ru.UpdateRemoteObjects(k, labels, nil, false) + dew := utils2.NewDeploymentErrorsAndWarnings() + ru := utils2.NewRemoteObjectsUtil(ctx, dew) + err := ru.UpdateRemoteObjects(k, &cmd.discriminator, nil, false) if err != nil { return nil, err } - return utils2.FindObjectsForDelete(k, ru.GetFilteredRemoteObjects(inclusion), inclusion.HasType("tags"), nil) + return utils2.FindObjectsForDelete(k, ru.GetFilteredRemoteObjects(cmd.inclusion), cmd.inclusion.HasType("tags"), nil) } diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index bbf4284dc..cb3721b41 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -2,15 +2,19 @@ package commands import ( "context" + "fmt" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "time" ) type DeployCommand struct { - c *deployment.DeploymentCollection + c *deployment.DeploymentCollection + discriminator string ForceApply bool ReplaceOnError bool @@ -20,17 +24,23 @@ type DeployCommand struct { NoWait bool } -func NewDeployCommand(c *deployment.DeploymentCollection) *DeployCommand { +func NewDeployCommand(discriminator string, c *deployment.DeploymentCollection) *DeployCommand { return &DeployCommand{ - c: c, + discriminator: discriminator, + c: c, } } func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResultCb func(diffResult *types.CommandResult) error) (*types.CommandResult, error) { dew := utils2.NewDeploymentErrorsAndWarnings() + if cmd.discriminator == "" { + status.Warning(ctx, "No discriminator configured. Orphan object detection will not work") + dew.AddWarning(k8s2.ObjectRef{}, fmt.Errorf("no discriminator configured. Orphan object detection will not work")) + } + ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs(), false) + err := ru.UpdateRemoteObjects(k, &cmd.discriminator, cmd.c.LocalObjectRefs(), false) if err != nil { return nil, err } diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index 890908fc0..ff8e8f0ca 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -2,14 +2,18 @@ package commands import ( "context" + "fmt" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" ) type DiffCommand struct { - c *deployment.DeploymentCollection + c *deployment.DeploymentCollection + discriminator string ForceApply bool ReplaceOnError bool @@ -19,17 +23,23 @@ type DiffCommand struct { IgnoreAnnotations bool } -func NewDiffCommand(c *deployment.DeploymentCollection) *DiffCommand { +func NewDiffCommand(discriminator string, c *deployment.DeploymentCollection) *DiffCommand { return &DiffCommand{ - c: c, + discriminator: discriminator, + c: c, } } func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.CommandResult, error) { dew := utils.NewDeploymentErrorsAndWarnings() + if cmd.discriminator == "" { + status.Warning(ctx, "No discriminator configured. Orphan object detection will not work") + dew.AddWarning(k8s2.ObjectRef{}, fmt.Errorf("no discriminator configured. Orphan object detection will not work")) + } + ru := utils.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs(), false) + err := ru.UpdateRemoteObjects(k, &cmd.discriminator, cmd.c.LocalObjectRefs(), false) if err != nil { return nil, err } diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index e3b5f4689..4b7a32cfe 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -2,6 +2,7 @@ package commands import ( "context" + "fmt" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -9,20 +10,26 @@ import ( ) type PruneCommand struct { - c *deployment.DeploymentCollection + discriminator string + c *deployment.DeploymentCollection } -func NewPruneCommand(c *deployment.DeploymentCollection) *PruneCommand { +func NewPruneCommand(discriminator string, c *deployment.DeploymentCollection) *PruneCommand { return &PruneCommand{ - c: c, + discriminator: discriminator, + c: c, } } func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.ObjectRef, error) { + if cmd.discriminator == "" { + return nil, fmt.Errorf("pruning without a discriminator is not supported") + } + dew := utils2.NewDeploymentErrorsAndWarnings() ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), nil, false) + err := ru.UpdateRemoteObjects(k, &cmd.discriminator, nil, false) if err != nil { return nil, err } diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 010fa9868..f279d5042 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -11,15 +11,18 @@ import ( ) type ValidateCommand struct { - c *deployment.DeploymentCollection + c *deployment.DeploymentCollection + discriminator string + dew *utils2.DeploymentErrorsAndWarnings ru *utils2.RemoteObjectUtils } -func NewValidateCommand(ctx context.Context, c *deployment.DeploymentCollection) *ValidateCommand { +func NewValidateCommand(ctx context.Context, discriminator string, c *deployment.DeploymentCollection) *ValidateCommand { cmd := &ValidateCommand{ - c: c, - dew: utils2.NewDeploymentErrorsAndWarnings(), + c: c, + discriminator: discriminator, + dew: utils2.NewDeploymentErrorsAndWarnings(), } cmd.ru = utils2.NewRemoteObjectsUtil(ctx, cmd.dew) return cmd @@ -31,7 +34,7 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types. cmd.dew.Init() - err := cmd.ru.UpdateRemoteObjects(k, cmd.c.Project.GetCommonLabels(), cmd.c.LocalObjectRefs(), true) + err := cmd.ru.UpdateRemoteObjects(k, &cmd.discriminator, cmd.c.LocalObjectRefs(), true) if err != nil { return nil, err } diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index b32964eae..607ded1dd 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -95,6 +95,9 @@ func NewDeploymentItem(ctx SharedContext, project *DeploymentProject, collection func (di *DeploymentItem) getCommonLabels() map[string]string { l := di.Project.GetCommonLabels() + if di.ctx.Discriminator != "" { + l["kluctl.io/discriminator"] = di.ctx.Discriminator + } i := 0 for _, t := range di.Tags.ListKeys() { l[fmt.Sprintf("kluctl.io/tag-%d", i)] = t diff --git a/pkg/deployment/shared_context.go b/pkg/deployment/shared_context.go index 859f194ae..9b20d151b 100644 --- a/pkg/deployment/shared_context.go +++ b/pkg/deployment/shared_context.go @@ -18,6 +18,7 @@ type SharedContext struct { VarsLoader *vars.VarsLoader HelmCredentials helm.HelmCredentialsProvider + Discriminator string RenderDir string SealedSecretsDir string DefaultSealedSecretsOutputPattern string diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index ff505702e..37e078595 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -30,13 +30,21 @@ func NewRemoteObjectsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnings) } } -func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string]string, onlyUsedGKs map[schema.GroupKind]bool) error { +func (u *RemoteObjectUtils) getAllByDiscriminator(k *k8s.K8sCluster, discriminator *string, onlyUsedGKs map[schema.GroupKind]bool) error { var mutex sync.Mutex - if len(labels) == 0 { + if discriminator == nil { return nil } + if *discriminator == "" { + status.Warning(u.ctx, "No discriminator configured for target, retrieval of remote objects will be slow.") + return nil + } + + labels := map[string]string{ + "kluctl.io/discriminator": *discriminator, + } - baseStatus := "Getting remote objects by commonLabels" + baseStatus := "Getting remote objects by discriminator" s := status.Start(u.ctx, baseStatus) defer s.Failed() @@ -87,7 +95,7 @@ func (u *RemoteObjectUtils) getAllByLabels(k *k8s.K8sCluster, labels map[string] s.UpdateAndInfoFallback("%s: Failed with %d errors", baseStatus, errCount) s.Warning() if permissionErrCount != 0 { - u.dew.AddWarning(k8s2.ObjectRef{}, fmt.Errorf("at least one permission error was encountered while gathering objects by labels. This might result in orphan object detection to not work properly")) + u.dew.AddWarning(k8s2.ObjectRef{}, fmt.Errorf("at least one permission error was encountered while gathering objects by discriminator labels. This might result in orphan object detection to not work properly")) } } else { s.Success() @@ -157,7 +165,7 @@ func (u *RemoteObjectUtils) getMissingObjects(k *k8s.K8sCluster, refs []k8s2.Obj return g.ErrorOrNil() } -func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[string]string, refs []k8s2.ObjectRef, onlyUsedGKs bool) error { +func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, discriminator *string, refs []k8s2.ObjectRef, onlyUsedGKs bool) error { if k == nil { return nil } @@ -171,7 +179,7 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, labels map[st } } - err := u.getAllByLabels(k, labels, usedGKs) + err := u.getAllByDiscriminator(k, discriminator, usedGKs) if err != nil { return err } diff --git a/pkg/deployment/utils/remote_objects_utils_test.go b/pkg/deployment/utils/remote_objects_utils_test.go index aaa1671a2..8ded2b28c 100644 --- a/pkg/deployment/utils/remote_objects_utils_test.go +++ b/pkg/deployment/utils/remote_objects_utils_test.go @@ -37,12 +37,13 @@ func TestRemoteObjectUtils_PermissionErrors(t *testing.T) { dew := NewDeploymentErrorsAndWarnings() u := NewRemoteObjectsUtil(context.Background(), dew) - err = u.UpdateRemoteObjects(k, map[string]string{"l1": "v1"}, []k8s2.ObjectRef{ + discriminator := "d" + err = u.UpdateRemoteObjects(k, &discriminator, []k8s2.ObjectRef{ k8s2.NewObjectRef("", "v1", "Secret", "secret", "default"), }, false) assert.NoError(t, err) assert.Equal(t, []types.DeploymentError{{ - Error: "at least one permission error was encountered while gathering objects by labels. This might result in orphan object detection to not work properly"}, + Error: "at least one permission error was encountered while gathering objects by discriminator labels. This might result in orphan object detection to not work properly"}, }, dew.GetWarningsList()) assert.Empty(t, dew.GetErrorsList()) } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 0a5e46627..6ebb6fa86 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -109,6 +109,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe SopsDecrypter: p.SopsDecrypter, VarsLoader: varsLoader, HelmCredentials: params.HelmCredentials, + Discriminator: target.Discriminator, RenderDir: params.RenderOutputDir, SealedSecretsDir: p.sealedSecretsDir, DefaultSealedSecretsOutputPattern: target.Name, diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index eda785735..1577e230e 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -228,6 +228,9 @@ func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) if err != nil { return nil, err } + if target.Discriminator == "" { + target.Discriminator = c.Config.Discriminator + } if targetInfo.baseTarget.TargetConfig == nil { return &target, nil } diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 0266fa1a0..33078a4ae 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -34,6 +34,7 @@ type Target struct { TargetConfig *ExternalTargetConfig `yaml:"targetConfig,omitempty"` SealingConfig *SealingConfig `yaml:"sealingConfig,omitempty"` Images []FixedImage `yaml:"images,omitempty"` + Discriminator string `yaml:"discriminator,omitempty"` } type DynamicTarget struct { @@ -66,4 +67,5 @@ type KluctlProject struct { Targets []*Target `yaml:"targets,omitempty"` Args []*DeploymentArg `yaml:"args,omitempty"` SecretsConfig *SecretsConfig `yaml:"secretsConfig,omitempty"` + Discriminator string `yaml:"discriminator,omitempty"` } From 109d3a15d71398200801ad12f28ee984d6bea5fc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 17:31:20 +0100 Subject: [PATCH 0770/2268] fix: Properly fail if target rendering fails 10 times --- pkg/kluctl_project/targets.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 1577e230e..66a5afb1f 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -75,7 +75,7 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) error { // Try rendering the target multiple times, until all values can be rendered successfully. This allows the target // to reference itself in complex ways. We'll also try loading the cluster vars in each iteration. - var errors []error + var retErr error for i := 0; i < 10; i++ { varsCtx, err := c.buildVars(target, false) if err != nil { @@ -86,11 +86,9 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) error { if err == nil && !changed { return nil } + retErr = err } - if len(errors) != 0 { - return errors[0] - } - return nil + return retErr } func (c *LoadedKluctlProject) prepareDynamicTargets(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { From 93d346680d05d3cdbc6276d5ab70f2de9d8aca1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 14:02:02 +0100 Subject: [PATCH 0771/2268] chore(deps): Bump golang.org/x/net from 0.5.0 to 0.6.0 (#305) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.6.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.5.0...v0.6.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0a9e7de6f..a768ee877 100644 --- a/go.mod +++ b/go.mod @@ -39,11 +39,11 @@ require ( github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.5.0 - golang.org/x/net v0.5.0 + golang.org/x/net v0.6.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.5.0 golang.org/x/term v0.5.0 - golang.org/x/text v0.6.0 + golang.org/x/text v0.7.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.3 diff --git a/go.sum b/go.sum index a8302d9d2..f4d6f2911 100644 --- a/go.sum +++ b/go.sum @@ -971,8 +971,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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= @@ -1090,8 +1090,8 @@ 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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= From da919e0417a9cc8e7110d9f3b1e8dac21e0fed5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 14:02:44 +0100 Subject: [PATCH 0772/2268] chore(deps): Bump github.com/google/go-containerregistry (#306) Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.12.1 to 0.13.0. - [Release notes](https://github.com/google/go-containerregistry/releases) - [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml) - [Commits](https://github.com/google/go-containerregistry/compare/v0.12.1...v0.13.0) --- updated-dependencies: - dependency-name: github.com/google/go-containerregistry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a768ee877..eda6b6dc8 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/go-playground/validator/v10 v10.11.2 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 - github.com/google/go-containerregistry v0.12.1 + github.com/google/go-containerregistry v0.13.0 github.com/hashicorp/vault/api v1.8.2 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 diff --git a/go.sum b/go.sum index f4d6f2911..c5834f9a6 100644 --- a/go.sum +++ b/go.sum @@ -392,8 +392,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-containerregistry v0.12.1 h1:W1mzdNUTx4Zla4JaixCRLhORcR7G6KxE5hHl5fkPsp8= -github.com/google/go-containerregistry v0.12.1/go.mod h1:sdIK+oHQO7B93xI8UweYdl887YhuIwg9vz8BSLH3+8k= +github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k= +github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo= github.com/google/gofuzz v1.0.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= From 086645a5032bc4290254da13a9842f556a91935d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 14:04:41 +0100 Subject: [PATCH 0773/2268] chore(deps): Bump sigs.k8s.io/controller-runtime from 0.14.1 to 0.14.4 (#307) Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.14.1 to 0.14.4. - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/master/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.14.1...v0.14.4) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index eda6b6dc8..9ca767700 100644 --- a/go.mod +++ b/go.mod @@ -67,7 +67,7 @@ require ( github.com/onsi/gomega v1.26.0 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 - sigs.k8s.io/controller-runtime v0.14.1 + sigs.k8s.io/controller-runtime v0.14.4 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/yaml v1.3.0 ) diff --git a/go.sum b/go.sum index c5834f9a6..56a342515 100644 --- a/go.sum +++ b/go.sum @@ -1346,8 +1346,8 @@ oras.land/oras-go v1.2.1/go.mod h1:3N11Z5E3c4ZzOjroCl1RtAdB4yNAYl7A27j2SVf913A= 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/controller-runtime v0.14.1 h1:vThDes9pzg0Y+UbCPY3Wj34CGIYPgdmspPm2GIpxpzM= -sigs.k8s.io/controller-runtime v0.14.1/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU= +sigs.k8s.io/controller-runtime v0.14.4 h1:Kd/Qgx5pd2XUL08eOV2vwIq3L9GhIbJ5Nxengbd4/0M= +sigs.k8s.io/controller-runtime v0.14.4/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= 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/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= From 5f8a882f6d5970277d297e66d92299ed5e3bae59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 14:41:24 +0100 Subject: [PATCH 0774/2268] chore(deps): Bump github.com/hashicorp/vault/api from 1.8.2 to 1.8.3 (#308) Bumps [github.com/hashicorp/vault/api](https://github.com/hashicorp/vault) from 1.8.2 to 1.8.3. - [Release notes](https://github.com/hashicorp/vault/releases) - [Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/vault/compare/v1.8.2...v1.8.3) --- updated-dependencies: - dependency-name: github.com/hashicorp/vault/api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 9ca767700..984ecb0dc 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 github.com/google/go-containerregistry v0.13.0 - github.com/hashicorp/vault/api v1.8.2 + github.com/hashicorp/vault/api v1.8.3 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 @@ -172,7 +172,7 @@ require ( github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/vault/sdk v0.6.1 // indirect + github.com/hashicorp/vault/sdk v0.7.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect diff --git a/go.sum b/go.sum index 56a342515..931147457 100644 --- a/go.sum +++ b/go.sum @@ -493,10 +493,10 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.8.2 h1:C7OL9YtOtwQbTKI9ogB0A1wffRbCN+rH/LLCHO3d8HM= -github.com/hashicorp/vault/api v1.8.2/go.mod h1:ML8aYzBIhY5m1MD1B2Q0JV89cC85YVH4t5kBaZiyVaE= -github.com/hashicorp/vault/sdk v0.6.1 h1:sjZC1z4j5Rh2GXYbkxn5BLK05S1p7+MhW4AgdUmgRUA= -github.com/hashicorp/vault/sdk v0.6.1/go.mod h1:Ck4JuAC6usTphfrrRJCRH+7/N7O2ozZzkm/fzQFt4uM= +github.com/hashicorp/vault/api v1.8.3 h1:cHQOLcMhBR+aVI0HzhPxO62w2+gJhIrKguQNONPzu6o= +github.com/hashicorp/vault/api v1.8.3/go.mod h1:4g/9lj9lmuJQMtT6CmVMHC5FW1yENaVv+Nv4ZfG8fAg= +github.com/hashicorp/vault/sdk v0.7.0 h1:2pQRO40R1etpKkia5fb4kjrdYMx3BHklPxl1pxpxDHg= +github.com/hashicorp/vault/sdk v0.7.0/go.mod h1:KyfArJkhooyba7gYCKSq8v66QdqJmnbAxtV/OX1+JTs= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= From 93853f1132872210c4d7e2e690f46a50ef15953f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 17:42:50 +0100 Subject: [PATCH 0775/2268] tests: Fix pruning tests --- e2e/inclusion_test.go | 3 ++- e2e/test-utils/project.go | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/e2e/inclusion_test.go b/e2e/inclusion_test.go index 84b427c97..fee93caec 100644 --- a/e2e/inclusion_test.go +++ b/e2e/inclusion_test.go @@ -2,6 +2,7 @@ package e2e import ( "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" corev1 "k8s.io/api/core/v1" "path/filepath" "reflect" @@ -14,7 +15,7 @@ func prepareInclusionTestProject(t *testing.T, withIncludes bool) (*test_utils.T createNamespace(t, k, p.TestSlug()) - p.UpdateTarget("test", nil) + p.UpdateTarget("test", func(target *uo.UnstructuredObject) {}) addConfigMapDeployment(p, "cm1", nil, resourceOpts{name: "cm1", namespace: p.TestSlug()}) addConfigMapDeployment(p, "cm2", nil, resourceOpts{name: "cm2", namespace: p.TestSlug()}) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index 63f918dea..74b64f2e2 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -14,6 +14,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" registry2 "helm.sh/helm/v3/pkg/registry" + "k8s.io/apimachinery/pkg/util/rand" "net/url" "os" "os/exec" @@ -78,6 +79,7 @@ func NewTestProject(t *testing.T, opts ...TestProjectOption) *TestProject { p.gitServer.GitInit(p.gitRepoName) p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(fmt.Sprintf("%s-{{ target.name }}", rand.String(16)), "discriminator") return nil }) p.UpdateDeploymentYaml(".", func(c *uo.UnstructuredObject) error { From a21906e502cab23323b39a1c9971316435e9e945 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 20:12:24 +0100 Subject: [PATCH 0776/2268] docs: Run make replace-commands-help --- docs/reference/commands/delete.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index f75f0f77c..e8bb56368 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -34,7 +34,7 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - -l, --delete-by-label stringArray Override the labels used to find objects for deletion. + --discriminator string Override the discriminator used to find objects for deletion. --dry-run Performs all kubernetes API calls in dry-run mode. --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form --helm-insecure-skip-tls-verify=, where From 653454c7ec90cbe800928b7fddfbecc05d67741a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 9 Feb 2023 14:55:34 +0100 Subject: [PATCH 0777/2268] docs: Add documentation about discriminators --- docs/reference/kluctl-project/README.md | 9 +++++++ .../kluctl-project/targets/README.md | 25 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index e48f33f47..c42783829 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -19,6 +19,8 @@ available to invoke [commands](../commands) on. An example .kluctl.yaml looks like this: ```yaml +discriminator: "my-project-{{ target.name }}" + targets: # test cluster, dev env - name: dev @@ -42,6 +44,13 @@ args: ## Allowed fields +### discriminator + +Specifies a default discriminator template to be used for targets that don't have +their own discriminator specified. + +See [target discriminator](./targets/README.md#discriminator) for details. + ### targets Please check the [targets](./targets) sub-section for details. diff --git a/docs/reference/kluctl-project/targets/README.md b/docs/reference/kluctl-project/targets/README.md index 4dc644fc1..d3fea1d5f 100644 --- a/docs/reference/kluctl-project/targets/README.md +++ b/docs/reference/kluctl-project/targets/README.md @@ -32,6 +32,7 @@ targets: images: - image: my-image resultImage: my-image:1.2.3 + discriminator: "my-project-{{ target.name }}" ... ``` @@ -58,3 +59,27 @@ The format is identical to the [fixed images file](../../deployments/images.md#c The fixed images specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets.md#images) have higher priority. + +## discriminator + +Specifies a discriminator which is used to uniquely identify all deployed objects on the cluster. It is added to all +objects as the value of the `kluctl.io/discriminator` label. This label is then later used to identify all objects +belonging to the deployment project and target, so that Kluctl can determine which objects got orphaned and need to +be pruned. The discriminator is also used to identify all objects that need to be deleted when +[kluctl delete](../../commands/delete.md) is called. + +If no discriminator is set for a target, [kluctl prune](../../commands/prune.md) and +[kluctl delete](../../commands/delete.md) are not supported. + +The discriminator can be a [template](../../templating/README.md) which is rendered at project loading time. While +rendering, only the `target` and `args` are available as global variables in the templating context. + +The rendered discriminator should be unique on the target cluster to avoid mis-identification of objects from other +deployments or targets. It's good practice to prefix the discriminator with a project name and at least use the target +name to make it unique. Example discriminator to achieve this: `my-project-name-{{ target.name }}`. + +If a target is meant to be deployed multiple times, e.g. by using external [arguments](../README.md#args), the external +arguments should be taken into account as well. Example: `my-project-name-{{ target.name }}-{{ args.environment_name }}`. + +A [default discriminator](../../kluctl-project/README.md#discriminator) can also be specified which is used whenever +a target has no discriminator configured. From 71c5825fa1530b483fb913ae89731d158c6ae468 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 08:08:57 +0100 Subject: [PATCH 0778/2268] chore(deps): Bump golang.org/x/crypto from 0.5.0 to 0.6.0 (#310) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.5.0 to 0.6.0. - [Release notes](https://github.com/golang/crypto/releases) - [Commits](https://github.com/golang/crypto/compare/v0.5.0...v0.6.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 984ecb0dc..73a6870d8 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/stretchr/testify v1.8.1 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3 - golang.org/x/crypto v0.5.0 + golang.org/x/crypto v0.6.0 golang.org/x/net v0.6.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.5.0 diff --git a/go.sum b/go.sum index 931147457..555d4eddd 100644 --- a/go.sum +++ b/go.sum @@ -886,8 +886,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 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= From 7b0191038d4ba723fe8081e49e8fa64343668548 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 08:11:26 +0100 Subject: [PATCH 0779/2268] chore(deps): Bump helm.sh/helm/v3 from 3.10.3 to 3.11.1 (#311) Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.10.3 to 3.11.1. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.10.3...v3.11.1) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 7 +++---- go.sum | 21 ++++++++------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 73a6870d8..7963c49ef 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( golang.org/x/text v0.7.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.10.3 + helm.sh/helm/v3 v3.11.1 k8s.io/api v0.26.1 k8s.io/apiextensions-apiserver v0.26.1 k8s.io/apimachinery v0.26.1 @@ -112,7 +112,7 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.3.0 // indirect - github.com/containerd/containerd v1.6.13 // indirect + github.com/containerd/containerd v1.6.15 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v20.10.21+incompatible // indirect @@ -232,7 +232,6 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect - go.etcd.io/etcd/api/v3 v3.5.6 // indirect go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a // indirect go.opencensus.io v0.24.0 // indirect go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect @@ -258,6 +257,6 @@ require ( k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715 // indirect k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect - oras.land/oras-go v1.2.1 // indirect + oras.land/oras-go v1.2.2 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect ) diff --git a/go.sum b/go.sum index 555d4eddd..a2bfbdd9b 100644 --- a/go.sum +++ b/go.sum @@ -189,10 +189,9 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX 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/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/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= -github.com/containerd/containerd v1.6.13 h1:7llWEzjLH/fao0f2lppn1L6NhjsvxqMdUQa2mgVCs+U= -github.com/containerd/containerd v1.6.13/go.mod h1:vDm+BbU+dD9uvuUlHr+KmcY0HKX8WDyI6dzJjNi4ee8= +github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= +github.com/containerd/containerd v1.6.15 h1:4wWexxzLNHNE46aIETc6ge4TofO550v+BlLoANrbses= +github.com/containerd/containerd v1.6.15/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -209,7 +208,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= +github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -242,7 +241,6 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y 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= @@ -840,8 +838,6 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -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.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k= @@ -1258,7 +1254,6 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/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.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1310,8 +1305,8 @@ 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.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I= gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= -helm.sh/helm/v3 v3.10.3 h1:wL7IUZ7Zyukm5Kz0OUmIFZgKHuAgByCrUcJBtY0kDyw= -helm.sh/helm/v3 v3.10.3/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= +helm.sh/helm/v3 v3.11.1 h1:cmL9fFohOoNQf+wnp2Wa0OhNFH0KFnSzEkVxi3fcc3I= +helm.sh/helm/v3 v3.11.1/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8= 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= @@ -1341,8 +1336,8 @@ k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -oras.land/oras-go v1.2.1 h1:/VcGS8FUy3eEXLl/1vC4QypLHwrfSmgW7ygsoklqKK8= -oras.land/oras-go v1.2.1/go.mod h1:3N11Z5E3c4ZzOjroCl1RtAdB4yNAYl7A27j2SVf913A= +oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= +oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= 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= From 01acac9e893ce05f9341afebf4b844e1b9083c5e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 10 Feb 2023 15:53:33 +0100 Subject: [PATCH 0780/2268] feat: Implement skipPrePull for helm-chart.yaml (#289) * refactor: Move pull code into doHelmPull and allow for dryRuns * refactor: Simplify helm-update implementation by re-using helm-pull internally * feat: Implement skipPrePull for helm-chart.yaml * fix: Limit number of parallel Helm renders * tests: Fix helm tests * docs: Update documentation about skipPrePull --- cmd/kluctl/commands/cmd_helm_pull.go | 65 ++++++--- cmd/kluctl/commands/cmd_helm_update.go | 180 ++++++++++++------------ cmd/kluctl/commands/root.go | 2 +- docs/reference/commands/helm-pull.md | 8 +- docs/reference/deployments/helm.md | 7 +- e2e/helm_test.go | 111 ++++++++++++++- e2e/sops_test.go | 4 + pkg/deployment/deployment_collection.go | 2 +- pkg/helm/helm_release.go | 5 + pkg/types/helm_chart.go | 1 + 10 files changed, 260 insertions(+), 125 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index ae0bf7bfd..7bfc1f42a 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -19,9 +19,9 @@ type helmPullCmd struct { } func (cmd *helmPullCmd) Help() string { - return `The Helm charts are stored under the sub-directory 'charts/' next to the -'helm-chart.yaml'. These Helm charts are meant to be added to version control so that -pulling is only needed when really required (e.g. when the chart version changes).` + return `Kluctl requires Helm Charts to be pre-pulled by default, which is handled by this command. It will collect +all required Charts and versions and pre-pull them into .helm-charts. To disable pre-pulling for individual charts, +set 'skipPrePull: true' in helm-chart.yaml.` } func (cmd *helmPullCmd) Run(ctx context.Context) error { @@ -33,24 +33,33 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { if !yaml.Exists(filepath.Join(projectDir, ".kluctl.yaml")) { return fmt.Errorf("helm-pull can only be used on the root of a Kluctl project that must have a .kluctl.yaml file") } + _, err = doHelmPull(ctx, projectDir, &cmd.HelmCredentials, false, true) + return err +} + +func doHelmPull(ctx context.Context, projectDir string, helmCredentials *args.HelmCredentials, dryRun bool, force bool) (int, error) { + actions := 0 baseChartsDir := filepath.Join(projectDir, ".helm-charts") - releases, charts, err := loadHelmReleases(projectDir, baseChartsDir, &cmd.HelmCredentials) + releases, charts, err := loadHelmReleases(projectDir, baseChartsDir, helmCredentials) if err != nil { - return err + return actions, err } for _, hr := range releases { if utils.Exists(hr.GetDeprecatedChartDir()) { + actions++ rel, err := filepath.Rel(projectDir, hr.GetDeprecatedChartDir()) if err != nil { - return err + return actions, err } - status.Info(ctx, "Removing deprecated charts dir %s", rel) - err = os.RemoveAll(hr.GetDeprecatedChartDir()) - if err != nil { - return err + if !dryRun { + status.Info(ctx, "Removing deprecated charts dir %s", rel) + err = os.RemoveAll(hr.GetDeprecatedChartDir()) + if err != nil { + return actions, err + } } } } @@ -63,34 +72,50 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { versionsToPull := map[string]bool{} for _, hr := range releases { + if hr.Config.SkipPrePull { + continue + } if hr.Chart == chart { versionsToPull[hr.Config.ChartVersion] = true } } - cleanupDir, err := chart.BuildPulledChartDir(baseChartsDir, "") + chartsDir, err := chart.BuildPulledChartDir(baseChartsDir, "") if err != nil { - return err + return actions, err } - des, err := os.ReadDir(cleanupDir) + des, err := os.ReadDir(chartsDir) if err != nil && !os.IsNotExist(err) { - return err + return actions, err } for _, de := range des { if !de.IsDir() { continue } if _, ok := versionsToPull[de.Name()]; !ok { - status.Info(ctx, "Removing unused Chart with version %s", de.Name()) - err = os.RemoveAll(filepath.Join(cleanupDir, de.Name())) - if err != nil { - return err + actions++ + if !dryRun { + status.Info(ctx, "Removing unused Chart with version %s", de.Name()) + err = os.RemoveAll(filepath.Join(chartsDir, de.Name())) + if err != nil { + return actions, err + } } } } for version, _ := range versionsToPull { version := version + + if yaml.Exists(filepath.Join(chartsDir, version, "Chart.yaml")) && !force { + continue + } + + actions++ + + if dryRun { + continue + } g.RunE(func() error { s := status.Start(ctx, "%s: Pulling Chart with version %s", statusPrefix, version) defer s.Failed() @@ -109,10 +134,10 @@ func (cmd *helmPullCmd) Run(ctx context.Context) error { g.Wait() if g.ErrorOrNil() != nil { - return fmt.Errorf("command failed") + return actions, fmt.Errorf("command failed") } - return nil + return actions, nil } func loadHelmReleases(projectDir string, baseChartsDir string, credentialsProvider helm.HelmCredentialsProvider) ([]*helm.Release, []*helm.Chart, error) { diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index a2df12433..d079ec1b8 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -3,8 +3,8 @@ package commands import ( "context" "fmt" - "github.com/fluxcd/go-git/v5" "github.com/fluxcd/go-git/v5/plumbing/format/index" + "github.com/go-git/go-git/v5" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/helm" @@ -14,6 +14,7 @@ import ( "io/fs" "os" "path/filepath" + "strings" ) type helmUpdateCmd struct { @@ -58,7 +59,10 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { if err != nil { return err } - for _, s := range gitStatus { + for pth, s := range gitStatus { + if strings.HasPrefix(pth, ".helm-charts/") { + return fmt.Errorf("--commit can only be used when .helm-chart directory is clean") + } if (s.Staging != git.Untracked && s.Staging != git.Unmodified) || (s.Worktree != git.Untracked && s.Worktree != git.Unmodified) { return fmt.Errorf("--commit can only be used when the git worktree is unmodified") } @@ -86,6 +90,16 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { } } + if cmd.Commit { + actions, err := doHelmPull(ctx, projectDir, &cmd.HelmCredentials, true, false) + if err != nil { + return err + } + if actions != 0 { + return fmt.Errorf(".helm-charts is not up-to-date. Please run helm-pull before") + } + } + for _, chart := range charts { chart := chart g.RunE(func() error { @@ -136,16 +150,14 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { return g.ErrorOrNil() } - versionUseCounts := map[string]map[string]int{} + upgrades := map[helmUpgradeKey][]*helm.Release{} + for _, hr := range releases { - key := fmt.Sprintf("%s / %s", hr.Chart.GetRepo(), hr.Chart.GetChartName()) - if _, ok := versionUseCounts[key]; !ok { - versionUseCounts[key] = map[string]int{} + cd, err := hr.Chart.BuildPulledChartDir(baseChartsDir, "") + if err != nil { + return err } - versionUseCounts[key][hr.Config.ChartVersion]++ - } - for _, hr := range releases { relDir, err := filepath.Rel(projectDir, filepath.Dir(hr.ConfigFile)) if err != nil { return err @@ -183,24 +195,18 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { if err != nil { return err } - status.Info(ctx, "%s: Updated Chart version to %s", relDir, latestVersion) - key := fmt.Sprintf("%s / %s", hr.Chart.GetRepo(), hr.Chart.GetChartName()) - uv := versionUseCounts[key] - uv[oldVersion]-- - uv[latestVersion]++ - - pullChart := false - deleteChart := false - if uv[latestVersion] == 1 { - pullChart = true - } - if uv[oldVersion] == 0 { - deleteChart = true + k := helmUpgradeKey{ + chartDir: cd, + oldVersion: oldVersion, + newVersion: latestVersion, } + upgrades[k] = append(upgrades[k], hr) + } - err = cmd.pullAndCommitCharts(ctx, projectDir, baseChartsDir, gitRootPath, hr, oldVersion, latestVersion, pullChart, deleteChart) + for k, hrs := range upgrades { + err = cmd.pullAndCommit(ctx, projectDir, baseChartsDir, gitRootPath, hrs, k.oldVersion) if err != nil { return err } @@ -209,7 +215,7 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { return nil } -func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]bool) error { +func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]os.FileInfo) error { err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, err error) error { if d == nil || d.IsDir() { return nil @@ -221,7 +227,7 @@ func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]boo if _, ok := m[relToGit]; ok { return nil } - m[relToGit] = true + m[relToGit], _ = d.Info() return nil }) if os.IsNotExist(err) { @@ -230,17 +236,15 @@ func (cmd *helmUpdateCmd) collectFiles(root string, dir string, m map[string]boo return err } -func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, projectDir string, baseChartsDir string, gitRootPath string, hr *helm.Release, oldVersion string, newVersion string, pullChart bool, deleteChart bool) error { - relDir, err := filepath.Rel(projectDir, filepath.Dir(hr.ConfigFile)) - if err != nil { - return err - } +func (cmd *helmUpdateCmd) pullAndCommit(ctx context.Context, projectDir string, baseChartsDir string, gitRootPath string, hrs []*helm.Release, oldVersion string) error { + chart := hrs[0].Chart + newVersion := hrs[0].Config.ChartVersion - s := status.Start(ctx, "%s: Upgrading Chart %s to version %s", relDir, hr.Chart.GetChartName(), newVersion) + s := status.Start(ctx, "Upgrading Chart %s from version %s to %s", chart.GetChartName(), oldVersion, newVersion) defer s.Failed() doError := func(err error) error { - s.FailedWithMessage("%s: %s", relDir, err.Error()) + s.FailedWithMessage("%s", err.Error()) return err } @@ -254,94 +258,86 @@ func (cmd *helmUpdateCmd) pullAndCommitCharts(ctx context.Context, projectDir st } if cmd.Commit { - // add helm-chart.yaml - relToGit, err := filepath.Rel(gitRootPath, hr.ConfigFile) - if err != nil { - return doError(err) - } - _, err = wt.Add(relToGit) - if err != nil { - return doError(err) - } - } - - if deleteChart { - chartDir, err := hr.Chart.BuildPulledChartDir(baseChartsDir, oldVersion) - if err != nil { - return doError(err) - } - relChartDir, err := filepath.Rel(gitRootPath, chartDir) - if err != nil { - return doError(err) - } - if cmd.Commit { - _, err = wt.Remove(relChartDir) - if err != nil && err != index.ErrEntryNotFound { + for _, hr := range hrs { + // add helm-chart.yaml + relToGit, err := filepath.Rel(gitRootPath, hr.ConfigFile) + if err != nil { + return doError(err) + } + _, err = wt.Add(relToGit) + if err != nil { return doError(err) } - } - err = os.RemoveAll(chartDir) - if err != nil { - return doError(err) } } - if pullChart { - chartDir, err := hr.Chart.BuildPulledChartDir(baseChartsDir, newVersion) - if err != nil { - return doError(err) - } - relChartDir, err := filepath.Rel(gitRootPath, chartDir) + // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later + // know what got deleted + files := map[string]os.FileInfo{} + if cmd.Commit { + err = cmd.collectFiles(gitRootPath, baseChartsDir, files) if err != nil { return doError(err) } + } - // we need to list all files contained inside the charts dir BEFORE doing the pull, so that we later - // know what got deleted - files := map[string]bool{} - err = cmd.collectFiles(gitRootPath, chartDir, files) - if err != nil { - return doError(err) - } + _, err = doHelmPull(ctx, projectDir, &cmd.HelmCredentials, false, false) + if err != nil { + return doError(err) + } - _, err = hr.Chart.PullInProject(ctx, baseChartsDir, newVersion) + if cmd.Commit { + files2 := map[string]os.FileInfo{} + err = cmd.collectFiles(gitRootPath, baseChartsDir, files2) if err != nil { return doError(err) } - if cmd.Commit { - _, err = wt.Add(relChartDir) - if err != nil { - return doError(err) + for pth, st1 := range files { + st2, ok := files2[pth] + if !ok || st1.Mode() != st2.Mode() || st1.ModTime() != st2.ModTime() || st1.Size() != st2.Size() { + // removed or modified + if ok { + if !st2.IsDir() { + _, err = wt.Add(pth) + } + } else { + if !st1.IsDir() { + _, err = wt.Remove(pth) + } + } + if err != nil && err != index.ErrEntryNotFound { + return doError(err) + } } - - // figure out what got deleted - for p := range files { - _, err := os.Lstat(filepath.Join(gitRootPath, p)) - if err != nil { - if os.IsNotExist(err) { - _, err = wt.Remove(p) - if err != nil { - return doError(err) - } - } else { + } + for pth, st1 := range files2 { + if _, ok := files[pth]; !ok { + if !st1.IsDir() { + // added + _, err = wt.Add(pth) + if err != nil && err != index.ErrEntryNotFound { return doError(err) } } } } - } - if cmd.Commit { - commitMsg := fmt.Sprintf("Updated helm chart %s in %s to version %s", hr.Chart.GetChartName(), relDir, newVersion) + commitMsg := fmt.Sprintf("Updated helm chart %s from version %s to version %s", chart.GetChartName(), oldVersion, newVersion) _, err = wt.Commit(commitMsg, &git.CommitOptions{}) if err != nil { return doError(fmt.Errorf("failed to commit: %w", err)) } - s.UpdateAndInfoFallback("%s: Committed helm chart %s with version %s", relDir, hr.Chart.GetChartName(), newVersion) + s.UpdateAndInfoFallback("Committed helm chart %s with version %s", chart.GetChartName(), newVersion) } s.Success() return nil } + +type helmUpgradeKey struct { + chartDir string + oldVersion string + newVersion string +} diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index fe314bba0..64e3e772f 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -58,7 +58,7 @@ type cli struct { Delete deleteCmd `cmd:"" help:"Delete a target (or parts of it) from the corresponding cluster"` Deploy deployCmd `cmd:"" help:"Deploys a target to the corresponding cluster"` Diff diffCmd `cmd:"" help:"Perform a diff between the locally rendered target and the already deployed target"` - HelmPull helmPullCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and pulls the specified Helm charts"` + HelmPull helmPullCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and pre-pulls the specified Helm charts"` HelmUpdate helmUpdateCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and checks for new available versions"` ListImages listImagesCmd `cmd:"" help:"Renders the target and outputs all images used via 'images.get_image(...)"` ListTargets listTargetsCmd `cmd:"" help:"Outputs a yaml list with all target, including dynamic targets"` diff --git a/docs/reference/commands/helm-pull.md b/docs/reference/commands/helm-pull.md index 8cc7ee552..c8c5c8ce9 100644 --- a/docs/reference/commands/helm-pull.md +++ b/docs/reference/commands/helm-pull.md @@ -13,10 +13,10 @@ description: > Usage: kluctl helm-pull [flags] -Recursively searches for 'helm-chart.yaml' files and pulls the specified Helm charts -The Helm charts are stored under the sub-directory 'charts/' next to the -'helm-chart.yaml'. These Helm charts are meant to be added to version control so that -pulling is only needed when really required (e.g. when the chart version changes). +Recursively searches for 'helm-chart.yaml' files and pre-pulls the specified Helm charts +Kluctl requires Helm Charts to be pre-pulled by default, which is handled by this command. It will collect +all required Charts and versions and pre-pull them into .helm-charts. To disable pre-pulling for individual charts, +set 'skipPrePull: true' in helm-chart.yaml. diff --git a/docs/reference/deployments/helm.md b/docs/reference/deployments/helm.md index 665f3ef51..64116a4fe 100644 --- a/docs/reference/deployments/helm.md +++ b/docs/reference/deployments/helm.md @@ -70,6 +70,7 @@ helmChart: chartVersion: 12.1.1 updateConstraints: ~12.1.0 skipUpdate: false + skipPrePull: false releaseName: redis-cache namespace: "{{ my.jinja2.var }}" output: helm-rendered.yaml # this is optional @@ -120,9 +121,13 @@ If omitted, Kluctl will filter out pre-releases by default. Use a `updateConstra pre-releases. ### skipUpdate -Skip this Helm Chart when the [helm-update](../commands/helm-update.md) command is called. +If set to `true`, skip this Helm Chart when the [helm-update](../commands/helm-update.md) command is called. If omitted, defaults to `false`. +### skipPrePull +If set to `true`, skip pre-pulling of this Helm Chart when running [helm-pull](../commands/helm-pull.md). This will +also enable pulling on-demand when the deployment project is rendered/deployed. + ### releaseName The name of the Helm Release. diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 6993804de..848645507 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -76,6 +76,11 @@ func testHelmPull(t *testing.T, tc testCase, prePull bool) { assert.NoError(t, err) assert.FileExists(t, getChartFile(t, p, repoUrl, "test-chart1", "0.1.0")) } + } else { + p.UpdateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipPrePull") + return nil + }, "") } args := []string{"deploy", "--yes", "-t", "test"} @@ -198,6 +203,12 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { assert.FileExists(t, getChartFile(t, p, repoUrl, "test-chart1", "0.1.0")) assert.FileExists(t, getChartFile(t, p, repoUrl, "test-chart2", "0.1.0")) + if commit { + wt, _ := p.GetGitRepo().Worktree() + _, _ = wt.Add(".helm-charts") + _, _ = wt.Commit(".helm-charts", &git.CommitOptions{}) + } + args := []string{"helm-update"} if upgrade { args = append(args, "--upgrade") @@ -212,12 +223,12 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { assert.Contains(t, stderr, "helm3: Skipped update to version 0.2.0") if upgrade { - assert.Contains(t, stderr, "helm1: Upgrading Chart test-chart1 to version 0.2.0") - assert.Contains(t, stderr, "helm2: Upgrading Chart test-chart2 to version 0.3.0") + assert.Contains(t, stderr, "Upgrading Chart test-chart1 from version 0.1.0 to 0.2.0") + assert.Contains(t, stderr, "Upgrading Chart test-chart2 from version 0.1.0 to 0.3.0") } if commit { - assert.Contains(t, stderr, "helm1: Committed helm chart test-chart1 with version 0.2.0") - assert.Contains(t, stderr, "helm2: Committed helm chart test-chart2 with version 0.3.0") + assert.Contains(t, stderr, "Committed helm chart test-chart1 with version 0.2.0") + assert.Contains(t, stderr, "Committed helm chart test-chart2 with version 0.3.0") } pulledVersions1 := listChartVersions(t, p, repoUrl, "test-chart1") @@ -248,8 +259,8 @@ func testHelmUpdate(t *testing.T, oci bool, upgrade bool, commit bool) { return commitList[i].Message < commitList[j].Message }) - assert.Equal(t, "Updated helm chart test-chart1 in helm1 to version 0.2.0", commitList[0].Message) - assert.Equal(t, "Updated helm chart test-chart2 in helm2 to version 0.3.0", commitList[1].Message) + assert.Equal(t, "Updated helm chart test-chart1 from version 0.1.0 to version 0.2.0", commitList[0].Message) + assert.Equal(t, "Updated helm chart test-chart2 from version 0.1.0 to version 0.3.0", commitList[1].Message) } } @@ -451,6 +462,10 @@ func TestHelmRenderOfflineKubernetes(t *testing.T) { p.UpdateTarget("test", nil) p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) + p.UpdateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipPrePull") + return nil + }, "") stdout, _ := p.KluctlMust("render", "--print-all", "--offline-kubernetes", "-t", "test") cm1 := uo.FromStringMust(stdout) @@ -498,6 +513,90 @@ func TestHelmLocalChart(t *testing.T) { assert.NotContains(t, stderr, "test-chart2") } +func TestHelmSkipPrePull(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + repoUrl := createHelmOrOciRepo(t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, + {ChartName: "test-chart1", Version: "0.1.1"}, + {ChartName: "test-chart1", Version: "0.2.0"}, + }, false, "", "") + u, _ := url.Parse(repoUrl) + + p.UpdateTarget("test", nil) + p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), nil) + p.AddHelmDeployment("helm2", repoUrl, "test-chart1", "0.1.1", "test-helm2", p.TestSlug(), nil) + + p.UpdateYaml("helm2/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipPrePull") + return nil + }, "") + + args := []string{"helm-pull"} + + _, stderr := p.KluctlMust(args...) + assert.Contains(t, stderr, "Pulling Chart with version 0.1.0") + assert.NotContains(t, stderr, "version 0.1.1") + assert.DirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.0", u.Port()))) + assert.NoDirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.1", u.Port()))) + + p.UpdateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipPrePull") + return nil + }, "") + _, stderr = p.KluctlMust(args...) + assert.Contains(t, stderr, "Removing unused Chart with version 0.1.0") + assert.NotContains(t, stderr, "version 0.1.1") + assert.NoDirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.0", u.Port()))) + assert.NoDirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.1", u.Port()))) + + p.UpdateYaml("helm2/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(false, "helmChart", "skipPrePull") + return nil + }, "") + _, stderr = p.KluctlMust(args...) + assert.Contains(t, stderr, "test-chart1: Pulling Chart with version 0.1.1") + assert.NotContains(t, stderr, "version 0.1.0") + assert.NoDirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.0", u.Port()))) + assert.DirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.1", u.Port()))) + + p.UpdateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(false, "helmChart", "skipPrePull") + return nil + }, "") + _, stderr = p.KluctlMust(args...) + assert.Contains(t, stderr, "Pulling Chart with version 0.1.0") + assert.Contains(t, stderr, "Pulling Chart with version 0.1.1") + assert.DirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.0", u.Port()))) + assert.DirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.1", u.Port()))) + + // not try to update+pull + p.UpdateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipPrePull") + return nil + }, "") + _, stderr = p.KluctlMust(args...) + p.GitServer().CommitFiles("kluctl-project", []string{".helm-charts"}, false, ".helm-charts") + args = []string{ + "helm-update", + "--upgrade", + "--commit", + } + _, stderr = p.KluctlMust(args...) + assert.NotContains(t, stderr, "Pulling Chart with version 0.1.0") + assert.NotContains(t, stderr, "Pulling Chart with version 0.1.1") + assert.Contains(t, stderr, "Pulling Chart with version 0.2.0") + assert.NoDirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.0", u.Port()))) + assert.NoDirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.1.1", u.Port()))) + assert.DirExists(t, filepath.Join(p.LocalProjectDir(), fmt.Sprintf(".helm-charts/http_%s_127.0.0.1/test-chart1/0.2.0", u.Port()))) +} + func getChartDir(t *testing.T, p *test_utils.TestProject, url2 string, chartName string, chartVersion string) string { u, err := url.Parse(url2) if err != nil { diff --git a/e2e/sops_test.go b/e2e/sops_test.go index 2ea1d9ac7..a7c7c3369 100644 --- a/e2e/sops_test.go +++ b/e2e/sops_test.go @@ -109,6 +109,10 @@ func TestSopsHelmValues(t *testing.T) { p.UpdateTarget("test", nil) p.AddHelmDeployment("helm1", repoUrl, "test-chart1", "0.1.0", "test-helm1", p.TestSlug(), values1.Object) + p.UpdateYaml("helm1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipPrePull") + return nil + }, "") p.KluctlMust("deploy", "--yes", "-t", "test") diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index bc442fdc4..ad20b3158 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -131,7 +131,7 @@ func (c *DeploymentCollection) renderHelmCharts() error { s := status.Start(c.ctx.Ctx, "Rendering Helm Charts") defer s.Failed() - g := utils.NewGoHelper(c.ctx.Ctx, 0) + g := utils.NewGoHelper(c.ctx.Ctx, 8) for _, d := range c.Deployments { d := d diff --git a/pkg/helm/helm_release.go b/pkg/helm/helm_release.go index 7012e00f5..5b21b2215 100644 --- a/pkg/helm/helm_release.go +++ b/pkg/helm/helm_release.go @@ -137,6 +137,11 @@ func (hr *Release) getPulledChart(ctx context.Context) (*PulledChart, error) { return nil, err } if needsPull { + if !hr.Config.SkipPrePull { + //goland:noinspection ALL + return nil, fmt.Errorf("Helm Chart %s has not been pre-pulled, which is not allowed when skipPrePull is not enabled. "+ + "Run 'kluctl helm-pull' to pre-pull all Helm Charts", hr.Chart.GetChartName()) + } if versionChanged { return nil, fmt.Errorf("pre-pulled Helm Chart %s need to be pulled (call 'kluctl helm-pull'). "+ "Desired version is %s while pre-pulled version is %s", hr.Chart.GetChartName(), hr.Config.ChartVersion, prePulledVersion) diff --git a/pkg/types/helm_chart.go b/pkg/types/helm_chart.go index e2c45601f..78011ad58 100644 --- a/pkg/types/helm_chart.go +++ b/pkg/types/helm_chart.go @@ -18,6 +18,7 @@ type HelmChartConfig2 struct { Output *string `yaml:"output,omitempty"` SkipCRDs bool `yaml:"skipCRDs,omitempty"` SkipUpdate bool `yaml:"skipUpdate,omitempty"` + SkipPrePull bool `yaml:"skipPrePull,omitempty"` } func ValidateHelmChartConfig2(sl validator.StructLevel) { From cf5930d78a757c66d82100f534be6bd8bb1ead56 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 11:06:46 +0100 Subject: [PATCH 0781/2268] feat: Implement when conditionals --- pkg/deployment/deployment_collection.go | 9 ++++ pkg/deployment/deployment_project.go | 48 ++++++++++++++++++++ pkg/kluctl_jinja2/conditionals.go | 58 +++++++++++++++++++++++++ pkg/types/deployment.go | 3 ++ 4 files changed, 118 insertions(+) create mode 100644 pkg/kluctl_jinja2/conditionals.go diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index bc442fdc4..9f62ff75a 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -2,6 +2,7 @@ package deployment import ( "fmt" + "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" @@ -81,7 +82,15 @@ func findDeploymentItemIndex(project *DeploymentProject, pth *string, indexes ma func (c *DeploymentCollection) collectAllDeployments(project *DeploymentProject, indexes map[string]int) ([]*DeploymentItem, error) { var ret []*DeploymentItem + if !kluctl_jinja2.IsConditionalTrue(project.Config.When) { + return nil, nil + } + for i, diConfig := range project.Config.Deployments { + if !kluctl_jinja2.IsConditionalTrue(diConfig.When) { + continue + } + if diConfig.Include != nil || diConfig.Git != nil { includedProject, ok := project.includes[i] if !ok { diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 7ee143f3d..3431c0a74 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -3,6 +3,7 @@ package deployment import ( "fmt" securejoin "github.com/cyphar/filepath-securejoin" + "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -56,6 +57,10 @@ func NewDeploymentProject(ctx SharedContext, varsCtx *vars.VarsCtx, source Sourc return nil, fmt.Errorf("failed to load deployment config for %s: %w", dir, err) } + if !kluctl_jinja2.IsConditionalTrue(dp.Config.When) { + return dp, nil + } + err = dp.loadIncludes() if err != nil { return nil, fmt.Errorf("failed to load includes for %s: %w", dir, err) @@ -126,6 +131,11 @@ func (p *DeploymentProject) processConfig() error { return err } + err = p.renderConditionals() + if err != nil { + return err + } + if len(p.Config.Args) != 0 && p.parentProject != nil { return fmt.Errorf("only the root deployment.yml can define args") } @@ -157,11 +167,49 @@ func (p *DeploymentProject) checkDeploymentDirs() error { return nil } +func (p *DeploymentProject) renderConditionals() error { + if p.parentProject == nil && p.Config.When != "" { + return fmt.Errorf("the root deployment project can not contain 'when'") + } + + vars, err := p.VarsCtx.Vars.ToMap() + if err != nil { + return err + } + r, err := kluctl_jinja2.RenderConditional(p.VarsCtx.J2, vars, p.Config.When) + if err != nil { + return err + } + p.Config.When = r + + if !kluctl_jinja2.IsConditionalTrue(p.Config.When) { + // No need to render individual deployment item conditionals + return nil + } + + conditionals := make([]string, 0, len(p.Config.Deployments)) + for _, di := range p.Config.Deployments { + conditionals = append(conditionals, di.When) + } + rendered, err := kluctl_jinja2.RenderConditionals(p.VarsCtx.J2, vars, conditionals) + if err != nil { + return err + } + for i, r := range rendered { + p.Config.Deployments[i].When = r + } + return nil +} + func (p *DeploymentProject) loadIncludes() error { for i, inc := range p.Config.Deployments { var err error var newProject *DeploymentProject + if !kluctl_jinja2.IsConditionalTrue(inc.When) { + continue + } + if inc.Include != nil { newProject, err = p.loadLocalInclude(p.source, filepath.Join(p.relDir, *inc.Include), inc.Vars) if err != nil { diff --git a/pkg/kluctl_jinja2/conditionals.go b/pkg/kluctl_jinja2/conditionals.go new file mode 100644 index 000000000..7cee13000 --- /dev/null +++ b/pkg/kluctl_jinja2/conditionals.go @@ -0,0 +1,58 @@ +package kluctl_jinja2 + +import ( + "fmt" + "github.com/hashicorp/go-multierror" + "github.com/kluctl/go-jinja2" + "strings" +) + +func RenderConditionals(j *jinja2.Jinja2, vars map[string]any, conditionals []string) ([]string, error) { + ret := make([]string, len(conditionals)) + jobs := make([]*jinja2.RenderJob, 0, len(conditionals)) + + for _, c := range conditionals { + job := &jinja2.RenderJob{ + Template: buildConditionalTemplate(c), + } + jobs = append(jobs, job) + } + err := j.RenderStrings(jobs, jinja2.WithGlobals(vars)) + if err != nil { + return nil, err + } + + for i, job := range jobs { + if job.Error != nil { + e := fmt.Errorf("unable to render conditional '%s': %w", conditionals[i], job.Error) + err = multierror.Append(err, e) + } else { + r := strings.TrimSpace(*job.Result) + if r != "" && r != "True" && r != "False" { + err = multierror.Append(err, fmt.Errorf("unable to evaluate conditional: %s", conditionals[i])) + } else { + ret[i] = r + } + } + } + return ret, err +} + +func RenderConditional(j *jinja2.Jinja2, vars map[string]any, conditional string) (string, error) { + rendered, err := RenderConditionals(j, vars, []string{conditional}) + if err != nil { + return "", err + } + return rendered[0], nil +} + +func buildConditionalTemplate(c string) string { + if c == "" { + return "" + } + return fmt.Sprintf("{%% if %s %%} True {%% else %%} False {%% endif %%}", c) +} + +func IsConditionalTrue(c string) bool { + return c == "" || c == "True" +} diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index cefcf4b59..f792971bd 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -17,6 +17,7 @@ type DeploymentItemConfig struct { OnlyRender bool `yaml:"onlyRender,omitempty"` AlwaysDeploy bool `yaml:"alwaysDeploy,omitempty"` DeleteObjects []DeleteObjectItemConfig `yaml:"deleteObjects,omitempty"` + When string `yaml:"when,omitempty"` } func ValidateDeploymentItemConfig(sl validator.StructLevel) { @@ -88,6 +89,8 @@ type DeploymentProjectConfig struct { Vars []*VarsSource `yaml:"vars,omitempty"` SealedSecrets *SealedSecretsConfig `yaml:"sealedSecrets,omitempty"` + When string `yaml:"when,omitempty"` + Deployments []*DeploymentItemConfig `yaml:"deployments,omitempty"` CommonLabels map[string]string `yaml:"commonLabels,omitempty"` From e88f60c2b73c2eb694eeef8802b03494a3dfa77c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 12:05:41 +0100 Subject: [PATCH 0782/2268] tests: Implement e2e tests for "when" conditionals --- e2e/utils_resources.go | 14 +++++++ e2e/when_test.go | 91 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 e2e/when_test.go diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 4131374b7..928627aa6 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "path/filepath" ) type resourceOpts struct { @@ -12,6 +13,7 @@ type resourceOpts struct { tags []string labels map[string]string annotations map[string]string + when string } func mergeMetadata(o *uo.UnstructuredObject, opts resourceOpts) { @@ -57,6 +59,12 @@ func addConfigMapDeployment(p *test_utils.TestProject, dir string, data map[stri p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ {fmt.Sprintf("configmap-%s.yml", opts.name), "", o}, }, opts.tags) + if opts.when != "" { + p.UpdateDeploymentItems(filepath.Dir(dir), func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { + _ = items[len(items)-1].SetNestedField(opts.when, "when") + return items + }) + } } func addSecretDeployment(p *test_utils.TestProject, dir string, data map[string]string, opts resourceOpts, sealme bool) { @@ -69,4 +77,10 @@ func addSecretDeployment(p *test_utils.TestProject, dir string, data map[string] p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ {fname, fname + sealmeExt, o}, }, opts.tags) + if opts.when != "" { + p.UpdateDeploymentItems(filepath.Dir(dir), func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { + _ = items[len(items)-1].SetNestedField(opts.when, "when") + return items + }) + } } diff --git a/e2e/when_test.go b/e2e/when_test.go new file mode 100644 index 000000000..1eca921b4 --- /dev/null +++ b/e2e/when_test.go @@ -0,0 +1,91 @@ +package e2e + +import ( + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "path/filepath" + "strings" + "testing" +) + +func TestWhen(t *testing.T) { + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + createNamespace(t, k, p.TestSlug()) + p.UpdateTarget("test", func(target *uo.UnstructuredObject) {}) + + type testCase struct { + dir string + when string + whenInc string + depInc string + want bool + name string + } + + tests := []*testCase{ + {dir: "cm_empty", when: "", want: true}, + {dir: "cm_true", when: "True", want: true}, + {dir: "cm_false", when: "False", want: false}, + {dir: "cm_eq", when: "args.a == 'test'", want: true}, + {dir: "cm_ne", when: "args.a != 'test'", want: false}, + {dir: "cm_eq2", when: "args.b == 'test'", want: false}, + {dir: "cm_ne2", when: "args.b != 'test'", want: true}, + {dir: "inc1/cm_empty", when: "", want: true}, + {dir: "inc2/cm_true", when: "True", want: true}, + {dir: "inc3/cm_false", when: "False", want: false}, + {dir: "inc4/cm_eq", when: "args.a == 'test'", want: true}, + {dir: "inc5/cm_ne", when: "args.a != 'test'", want: false}, + {dir: "inc_when1/cm_empty", whenInc: "", want: true}, + {dir: "inc_when2/cm_true", whenInc: "True", want: true}, + {dir: "inc_when3/cm_false", whenInc: "False", want: false}, + {dir: "inc_when4/cm_eq", whenInc: "args.a == 'test'", want: true}, + {dir: "inc_when5/cm_ne", whenInc: "args.a != 'test'", want: false}, + {dir: "dep_inc_when1/cm_empty", depInc: "", want: true}, + {dir: "dep_inc_when2/cm_true", depInc: "True", want: true}, + {dir: "dep_inc_when3/cm_false", depInc: "False", want: false}, + {dir: "dep_inc_when4/cm_eq", depInc: "args.a == 'test'", want: true}, + {dir: "dep_inc_when5/cm_ne", depInc: "args.a != 'test'", want: false}, + } + + for _, test := range tests { + test.name = strings.ReplaceAll(test.dir, "/", "_") + test.name = strings.ReplaceAll(test.name, "_", "-") + addConfigMapDeployment(p, test.dir, nil, resourceOpts{ + name: test.name, + namespace: p.TestSlug(), + when: test.when, + }) + if test.whenInc != "" { + dir := filepath.Dir(test.dir) + p.UpdateDeploymentItems("", func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { + for _, it := range items { + inc, _, _ := it.GetNestedString("include") + if inc == dir { + _ = it.SetNestedField(test.whenInc, "when") + break + } + } + return items + }) + } + if test.depInc != "" { + dir := filepath.Dir(test.dir) + p.UpdateDeploymentYaml(dir, func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(test.depInc, "when") + return nil + }) + } + } + + p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=test", "-ab=test2") + + for _, test := range tests { + if test.want { + assertConfigMapExists(t, k, p.TestSlug(), test.name) + } else { + assertConfigMapNotExists(t, k, p.TestSlug(), test.name) + } + } +} From ef4bf54438ab28fec21396de89c3b72343fac7b5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 14:23:30 +0100 Subject: [PATCH 0783/2268] feat: Implement "when" conditional for variable sources --- pkg/types/vars_source.go | 5 ++++- pkg/vars/vars_loader.go | 11 +++++++++++ pkg/vars/vars_loader_test.go | 24 ++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index e7db5f765..9e9c3db25 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -66,6 +66,8 @@ type VarsSource struct { Http *VarsSourceHttp `yaml:"http,omitempty"` AwsSecretsManager *VarsSourceAwsSecretsManager `yaml:"awsSecretsManager,omitempty"` Vault *VarsSourceVault `yaml:"vault,omitempty"` + + When string `yaml:"when,omitempty"` } func ValidateVarsSource(sl validator.StructLevel) { @@ -74,7 +76,8 @@ func ValidateVarsSource(sl validator.StructLevel) { count := 0 v := reflect.ValueOf(s) for i := 0; i < v.NumField(); i++ { - if v.Type().Field(i).Name == "IgnoreMissing" || v.Type().Field(i).Name == "NoOverride" { + switch v.Type().Field(i).Name { + case "IgnoreMissing", "NoOverride", "When": continue } if !v.Field(i).IsNil() { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index a0dc29267..9ce0ff997 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -8,6 +8,7 @@ import ( types2 "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" @@ -78,6 +79,16 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear return err } + if source.When != "" { + r, err := kluctl_jinja2.RenderConditional(varsCtx.J2, globals, source.When) + if err != nil { + return err + } + if !kluctl_jinja2.IsConditionalTrue(r) { + return nil + } + } + ignoreMissing := false if source.IgnoreMissing != nil { ignoreMissing = *source.IgnoreMissing diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index a5b774f45..1764788de 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -88,6 +88,30 @@ func TestVarsLoader_ValuesNoOverrides(t *testing.T) { }) } +func TestVarsLoader_When(t *testing.T) { + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + Values: uo.FromStringMust(`{"test1": "a"}`), + }, nil, "") + assert.NoError(t, err) + err = vl.LoadVars(vc, &types.VarsSource{ + Values: uo.FromStringMust(`{"test1": "b"}`), + When: `test1 == "b"`, + }, nil, "") + assert.NoError(t, err) + v, _, _ := vc.Vars.GetNestedString("test1") + assert.Equal(t, "a", v) + + err = vl.LoadVars(vc, &types.VarsSource{ + Values: uo.FromStringMust(`{"test1": "b"}`), + When: `test1 == "a"`, + }, nil, "") + assert.NoError(t, err) + v, _, _ = vc.Vars.GetNestedString("test1") + assert.Equal(t, "b", v) + }) +} + func TestVarsLoader_File(t *testing.T) { d := t.TempDir() _ = os.WriteFile(filepath.Join(d, "test.yaml"), []byte(`{"test1": {"test2": 42}}`), 0o600) From 4a9f246e85c701dee5441e4395b61183d0277f6f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 8 Feb 2023 14:56:34 +0100 Subject: [PATCH 0784/2268] docs: Add documentation for conditional deployments and variable sources --- docs/reference/deployments/deployment-yml.md | 16 ++++++++++++++++ docs/reference/templating/variable-sources.md | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index 77115380e..013f60ef3 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -34,6 +34,8 @@ The following sub-chapters describe the available fields in the `deployment.yaml Individual deployments are performed in parallel, unless a [barrier](#barriers) is encountered which causes kluctl to wait for all previous deployments to finish. +Deployments can also be conditional by using the [when](#when) field. + ### Simple deployments Simple deployments are specified via `path` and are expected to be directories with Kubernetes manifests inside. @@ -150,6 +152,20 @@ deployments: - file: vars2.yaml ``` +### when + +Each deployment item can be conditional with the help of the `when` field. It must be set to a +[Jinja2 based expression](https://jinja.palletsprojects.com/en/latest/templates/#expressions) +that evaluates to a boolean. + +Example: +```yaml +deployments: +- path: item1 +- path: item2 + when: my.var == "my-value" +``` + ### tags (deployment item) A list of tags the deployment should have. See [tags](./tags.md) for more details. For includes, this means that all sub-deployments will get these tags applied to. If not specified, the default tags logic as described in [tags](./tags.md) diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index 3f157f400..32d1654b3 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -31,6 +31,8 @@ vars: ignoreMissing: true - file: default-vars.yaml noOverride: true +- file: vars3.yaml + when: some.var == "value" ``` `vars2.yaml` can now use variables that are defined in `vars1.yaml`. At all times, variables defined by @@ -42,6 +44,9 @@ can not be found. When specifying `noOverride: true`, Kluctl will not override variables from the previously loaded variables. This is useful if you want to load default values for variables. +Variables can also be loaded conditionally by specifying a condition via `when: `. The condition must be in +the same format as described in [conditional deployment items](../deployments/deployment-yml.md#when) + Different types of vars entries are possible: ### file From e335213e5ec4196a86b57073e554e248637f8577 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Feb 2023 13:57:41 +0100 Subject: [PATCH 0785/2268] feat: Deprecate dynamic targets --- pkg/kluctl_project/targets.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 66a5afb1f..dd2eb4ff5 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -126,6 +126,10 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Ta return nil, fmt.Errorf("'refPattern' and 'ref' can't be specified together") } + if baseTarget.TargetConfig.RefPattern != nil { + status.Deprecation(c.ctx, "dynamic-targets-ref-pattern", "'refPattern' and corresponding dynamic targets are deprecated and will be removed in an upcoming release.") + } + targetConfigRef := baseTarget.TargetConfig.Ref refPattern := baseTarget.TargetConfig.RefPattern From 66766066ac3db31033093407cddec42d11efd3e8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 10 Feb 2023 16:18:24 +0100 Subject: [PATCH 0786/2268] feat: Update deprecation warnings for feature that will be removed in the next release --- cmd/kluctl/commands/cmd_check_image_updates.go | 2 +- cmd/kluctl/commands/utils.go | 4 ++-- pkg/deployment/images.go | 2 +- pkg/kluctl_project/targets.go | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go index 7e7966ed0..782ed9868 100644 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ b/cmd/kluctl/commands/cmd_check_image_updates.go @@ -23,7 +23,7 @@ func (cmd *checkImageUpdatesCmd) Help() string { } func (cmd *checkImageUpdatesCmd) Run(ctx context.Context) error { - status.Deprecation(ctx, "check-image-updates", "check-image-updates is deprecated and will be removed in a future kluctl release.") + status.Deprecation(ctx, "check-image-updates", "check-image-updates is deprecated and will be removed in the next kluctl release.") ptArgs := projectTargetCommandArgs{ projectFlags: cmd.ProjectFlags, targetFlags: cmd.TargetFlags, diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 3259ab704..b2807f72b 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -151,10 +151,10 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm return fmt.Errorf("failed to parse registry auth from environment: %w", err) } if args.imageFlags.UpdateImages { - status.Deprecation(ctx, "update-images", "--update-images is deprecated and will be removed in a future kluctl release.") + status.Deprecation(ctx, "update-images", "--update-images is deprecated and will be removed in the next kluctl release.") } if !args.imageFlags.OfflineImages { - status.Deprecation(ctx, "online-images", "--offline-images=false is deprecated and will be removed in a future kluctl release.") + status.Deprecation(ctx, "online-images", "--offline-images=false is deprecated and will be removed in the next kluctl release.") } images, err := deployment.NewImages(rh, args.imageFlags.UpdateImages, args.imageFlags.OfflineImages || args.forCompletion) if err != nil { diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 076350faf..11112761a 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -303,7 +303,7 @@ func (images *Images) resolveImage(ctx context.Context, ph placeHolder, ref k8s2 } if ph.HasLatestVersion { - status.Deprecation(ctx, "latest-version-filter", "latest_version is deprecated when using images.get_image()") + status.Deprecation(ctx, "latest-version-filter", "latest_version is deprecated when using images.get_image() and will be removed in the next kluctl release.") } registry, err := images.GetLatestImageFromRegistry(ph.Image, ph.LatestVersion) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index dd2eb4ff5..9f010b542 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -127,7 +127,7 @@ func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Ta } if baseTarget.TargetConfig.RefPattern != nil { - status.Deprecation(c.ctx, "dynamic-targets-ref-pattern", "'refPattern' and corresponding dynamic targets are deprecated and will be removed in an upcoming release.") + status.Deprecation(c.ctx, "dynamic-targets-ref-pattern", "'refPattern' and corresponding dynamic targets are deprecated and will be removed in the next kluctl release.") } targetConfigRef := baseTarget.TargetConfig.Ref @@ -249,7 +249,7 @@ func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) } if len(target.DynamicArgs) != 0 { - status.Deprecation(c.ctx, "dynamic-args", "dynamicArgs are deprecated and ignored. The field will be removed in a future kluctl release.") + status.Deprecation(c.ctx, "dynamic-args", "dynamicArgs are deprecated and ignored. The field will be removed in the next kluctl release.") } // check and merge args @@ -257,7 +257,7 @@ func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) target.Args.Merge(targetConfig.Args) } if len(targetConfig.Images) != 0 { - status.Deprecation(c.ctx, "target-config-images", "specifying fixed images via external 'targetConfig' is deprecated and support for this will be removed in a future kluctl release.") + status.Deprecation(c.ctx, "target-config-images", "specifying fixed images via external 'targetConfig' is deprecated and support for this will be removed in the next kluctl release.") } // We prepend the dynamic images to ensure they get higher priority later target.Images = append(targetConfig.Images, target.Images...) From 08baf43e4ddf5c6faa073dac7335a6539fe7a11a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 10 Feb 2023 16:19:02 +0100 Subject: [PATCH 0787/2268] feat: Deprecate targetConfig --- pkg/kluctl_project/targets.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 9f010b542..59c27ee08 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -237,6 +237,8 @@ func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) return &target, nil } + status.Deprecation(c.ctx, "target-config", "'targetConfig' in targets is deprecated and will be removed in the next kluctl release.") + configFile, err := c.loadTargetConfigFile(targetInfo) if err != nil { return nil, err From 7cf8bbafe4b329988b268296598c697983ad54a8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 10 Feb 2023 16:30:08 +0100 Subject: [PATCH 0788/2268] docs: Remove mentions of dynamic targets --- cmd/kluctl/commands/cmd_list_targets.go | 2 +- cmd/kluctl/commands/root.go | 2 +- docs/reference/commands/list-targets.md | 4 +- .../kluctl-project/targets/README.md | 10 +- .../kluctl-project/targets/dynamic-targets.md | 100 ------------------ 5 files changed, 6 insertions(+), 112 deletions(-) delete mode 100644 docs/reference/kluctl-project/targets/dynamic-targets.md diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index dca1b646d..7d2649716 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -13,7 +13,7 @@ type listTargetsCmd struct { } func (cmd *listTargetsCmd) Help() string { - return `Outputs a yaml list with all target, including dynamic targets` + return `Outputs a yaml list with all targets` } func (cmd *listTargetsCmd) Run(ctx context.Context) error { diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 64e3e772f..2b37ba7d3 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -61,7 +61,7 @@ type cli struct { HelmPull helmPullCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and pre-pulls the specified Helm charts"` HelmUpdate helmUpdateCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and checks for new available versions"` ListImages listImagesCmd `cmd:"" help:"Renders the target and outputs all images used via 'images.get_image(...)"` - ListTargets listTargetsCmd `cmd:"" help:"Outputs a yaml list with all target, including dynamic targets"` + ListTargets listTargetsCmd `cmd:"" help:"Outputs a yaml list with all targets"` PokeImages pokeImagesCmd `cmd:"" help:"Replace all images in target"` Prune pruneCmd `cmd:"" help:"Searches the target cluster for prunable objects and deletes them"` Render renderCmd `cmd:"" help:"Renders all resources and configuration files"` diff --git a/docs/reference/commands/list-targets.md b/docs/reference/commands/list-targets.md index f99d5b2e0..52d882321 100644 --- a/docs/reference/commands/list-targets.md +++ b/docs/reference/commands/list-targets.md @@ -13,8 +13,8 @@ description: > Usage: kluctl list-targets [flags] -Outputs a yaml list with all target, including dynamic targets -Outputs a yaml list with all target, including dynamic targets +Outputs a yaml list with all targets +Outputs a yaml list with all targets diff --git a/docs/reference/kluctl-project/targets/README.md b/docs/reference/kluctl-project/targets/README.md index d3fea1d5f..4cb00e161 100644 --- a/docs/reference/kluctl-project/targets/README.md +++ b/docs/reference/kluctl-project/targets/README.md @@ -15,9 +15,9 @@ Specifies a list of targets for which commands can be invoked. A target puts tog configuration and the target cluster. Multiple targets can exist which target the same cluster but with differing configuration (via `args`). -Each value found in the target definition is rendered with a simple Jinja2 context that only contains the target itself. +Each value found in the target definition is rendered with a simple Jinja2 context that only contains the target and args. The rendering process is retried 10 times until it finally succeeds, allowing you to reference -the target itself in complex ways. This is especially useful when using [dynamic targets](./dynamic-targets.md). +the target itself in complex ways. Target entries have the following form: ```yaml @@ -50,16 +50,10 @@ If this field is omitted, Kluctl will always use the currently active context. This fields specifies a map of arguments to be passed to the deployment project when it is rendered. Allowed argument names are configured via [deployment args](../../deployments/deployment-yml.md#args). -The arguments specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets.md#args) -have higher priority. - ## images This field specifies a list of fixed images to be used by [`images.get_image(...)`](../../deployments/images.md#imagesget_image). The format is identical to the [fixed images file](../../deployments/images.md#command-line-argument---fixed-images-file). -The fixed images specified in the [dynamic target config](../../kluctl-project/targets/dynamic-targets.md#images) -have higher priority. - ## discriminator Specifies a discriminator which is used to uniquely identify all deployed objects on the cluster. It is added to all diff --git a/docs/reference/kluctl-project/targets/dynamic-targets.md b/docs/reference/kluctl-project/targets/dynamic-targets.md deleted file mode 100644 index 6b18cace4..000000000 --- a/docs/reference/kluctl-project/targets/dynamic-targets.md +++ /dev/null @@ -1,100 +0,0 @@ - - -# Dynamic Targets - -Targets can also be "dynamic", meaning that additional configuration can be sourced from another git repository. -This can be based on a single target repository and branch, or on a target repository and branch/ref pattern, resulting -in multiple dynamic targets being created from one target definition. - -Please note that a single entry in `target` might end up with multiple dynamic targets, meaning that the name must be -made unique between these dynamic targets. This can be achieved by using templating in the `name` field. As an example, -`{{ target.targetConfig.ref }}` can be used to set the target name to the branch name of the dynamic target. - -Dynamic targets have the following form: -```yaml -targets: -... - - name: - context: - args: ... - arg1: - arg2: - ... - targetConfig: - project: - url: - ref: - refPattern: - file: -... -``` - -All fields known from normal targets are allowed. In addition, the targetConfig with following fields is available. - -## targetConfig - -The presence of this field causes the target to become a dynamic target. -It specifies where to look for dynamic targets and their addional configuration. It has the following form: - -```yaml -... -targets: -... -- name: - ... - targetConfig: - project: - url: - ref: - refPattern: -... -``` - -### project.url -This field specifies the git clone url of the target configuration project. - -### ref -This field specifies the branch or tag to use. If this field is specified, using `refPattern` is forbidden. -This will result in one single dynamic target. - -### refPattern -This field specifies a regex pattern to use when looking for candidate branches and tags. If this is specified, -using `ref` is forbidden. This will result in multiple dynamic targets. Each dynamic target will have `ref` set to -the actual branch name it belong to. This allows using of `{{ target.targetConfig.ref }}` in all other target fields. - -### file -This field specifies the config file name to read externalized target config from. - -## Format of the target config -The target config file referenced in `targetConfig` must be of the following format: - -```yaml -args: - arg1: value1 - arg2: value2 -images: - - image: registry.gitlab.com/my-group/my-project - resultImage: registry.gitlab.com/my-group/my-project:1.1.0 -``` - -### args -An optional map of arguments, in the same format as in the normal [target args](../../kluctl-project/targets#args). - -The arguments specified here have higher priority. - -### images -An optional list of fixed images, in the same format as in the normal [target images](../../kluctl-project/targets#images) - -## Simple dynamic targets - -A simplified form of dynamic targets is to store target config inside the same directory/project as the `.kluctl.yaml`. -This can be done by omitting `project`, `ref` and `refPattern` from `targetConfig` and only specify `file`. From 95bd6ddf45c3f327f4c79a8f6a1eba5332c0a848 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 13 Feb 2023 10:12:38 +0100 Subject: [PATCH 0789/2268] fix: Use default discriminator for deployments without targets (#315) --- e2e/test-utils/project.go | 2 +- pkg/kluctl_project/target_context.go | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index 74b64f2e2..dc1c5daee 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -79,7 +79,7 @@ func NewTestProject(t *testing.T, opts ...TestProjectOption) *TestProject { p.gitServer.GitInit(p.gitRepoName) p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { - _ = o.SetNestedField(fmt.Sprintf("%s-{{ target.name }}", rand.String(16)), "discriminator") + _ = o.SetNestedField(fmt.Sprintf("%s-{{ target.name or 'no-name' }}", rand.String(16)), "discriminator") return nil }) p.UpdateDeploymentYaml(".", func(c *uo.UnstructuredObject) error { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 6ebb6fa86..a16a9e027 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -48,6 +48,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe } var target *types.Target + needRender := false if params.TargetName != "" { t, err := p.FindDynamicTarget(params.TargetName) if err != nil { @@ -55,7 +56,10 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe } target = &*t.Target } else { - target = &types.Target{} + target = &types.Target{ + Discriminator: p.Config.Discriminator, + } + needRender = true } if params.TargetNameOverride != "" { target.Name = params.TargetNameOverride @@ -64,6 +68,14 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe target.Context = ¶ms.ContextOverride } + if needRender { + // we must render the target after handling overrides + err = p.renderTarget(target) + if err != nil { + return nil, err + } + } + params.Images.PrependFixedImages(target.Images) clientConfig, clusterContext, err := p.loadK8sConfig(target, params.OfflineK8s) From 9162ce95222d33045e6e1ac7e7efcb6e5cbd7546 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 10:13:38 +0100 Subject: [PATCH 0790/2268] chore(deps): Bump github.com/hashicorp/vault/api from 1.8.3 to 1.9.0 (#312) Bumps [github.com/hashicorp/vault/api](https://github.com/hashicorp/vault) from 1.8.3 to 1.9.0. - [Release notes](https://github.com/hashicorp/vault/releases) - [Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/vault/compare/v1.8.3...v1.9.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/vault/api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 18 ++---------------- go.sum | 53 ++--------------------------------------------------- 2 files changed, 4 insertions(+), 67 deletions(-) diff --git a/go.mod b/go.mod index 7963c49ef..eeb038245 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 github.com/google/go-containerregistry v0.13.0 - github.com/hashicorp/vault/api v1.8.3 + github.com/hashicorp/vault/api v1.9.0 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.5 @@ -61,6 +61,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.17.4 github.com/aws/aws-sdk-go-v2/config v1.18.12 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3 + github.com/go-git/go-git/v5 v5.5.2 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 @@ -92,8 +93,6 @@ require ( github.com/Microsoft/go-winio v0.6.0 // indirect github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect - github.com/armon/go-metrics v0.4.1 // indirect - github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.12 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 // indirect @@ -133,7 +132,6 @@ require ( github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect - github.com/go-git/go-git/v5 v5.5.2 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect @@ -144,7 +142,6 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.5.9 // indirect @@ -160,20 +157,12 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.4.0 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-plugin v1.4.8 // indirect github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/vault/sdk v0.7.0 // indirect - github.com/hashicorp/yamux v0.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect @@ -191,7 +180,6 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/locker v1.0.1 // indirect @@ -202,12 +190,10 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oklog/run v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pjbgf/sha1cd v0.2.3 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index a2bfbdd9b..a529767df 100644 --- a/go.sum +++ b/go.sum @@ -73,7 +73,6 @@ 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/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -101,19 +100,13 @@ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat6 github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= 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/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= -github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= @@ -171,7 +164,6 @@ github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTx github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= 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/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= @@ -179,8 +171,6 @@ github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHe 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/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.0 h1:Anq00jxDtoyX3+aCaYUZ0vXC5r4k4epberfWGDXV1zE= @@ -252,7 +242,6 @@ github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSY github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df h1:2BHXJp1PwX7D47Q2oaKDekn+BZVZCmxeCWNi+FyownE= @@ -287,7 +276,6 @@ github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmnc github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= 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-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= @@ -367,8 +355,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 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= @@ -441,7 +427,6 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= @@ -449,22 +434,15 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= -github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= @@ -476,27 +454,18 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.8.3 h1:cHQOLcMhBR+aVI0HzhPxO62w2+gJhIrKguQNONPzu6o= -github.com/hashicorp/vault/api v1.8.3/go.mod h1:4g/9lj9lmuJQMtT6CmVMHC5FW1yENaVv+Nv4ZfG8fAg= -github.com/hashicorp/vault/sdk v0.7.0 h1:2pQRO40R1etpKkia5fb4kjrdYMx3BHklPxl1pxpxDHg= -github.com/hashicorp/vault/sdk v0.7.0/go.mod h1:KyfArJkhooyba7gYCKSq8v66QdqJmnbAxtV/OX1+JTs= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/vault/api v1.9.0 h1:ab7dI6W8DuCY7yCU8blo0UCYl2oHre/dloCmzMWg9w8= +github.com/hashicorp/vault/api v1.9.0/go.mod h1:lloELQP4EyhjnCQhF8agKvWIVTmxbpEJj70b98959sM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -517,7 +486,6 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -528,7 +496,6 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/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= @@ -621,8 +588,6 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= @@ -661,8 +626,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/ohler55/ojg v1.17.4 h1:6Ss87DyAZHU0ODZu6Cmuahj5UiVaRD1n8C4KNm0qMYg= github.com/ohler55/ojg v1.17.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= -github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= -github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow= github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= @@ -681,16 +644,12 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9 github.com/otiai10/mint v1.4.0 h1:umwcf7gbpEwf7WFzqmWwSv0CzbeMsae2u9ZvpP8j2q4= github.com/otiai10/mint v1.4.0/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.3/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/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= @@ -711,24 +670,20 @@ github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= 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.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 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.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= 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.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 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/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= @@ -759,7 +714,6 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= @@ -806,7 +760,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/urfave/cli v1.22.7 h1:aXiFAgRugfJ27UFDsGJ9DB2FvTC73hlVXFSqq5bo9eU= github.com/urfave/cli v1.22.7/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= @@ -1005,7 +958,6 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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= @@ -1292,7 +1244,6 @@ 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= From 0a998d887e52ff157a53c8a62f9f7e7f93fb5fa2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 10 Feb 2023 18:58:29 +0100 Subject: [PATCH 0791/2268] feat: Remove targetConfig and dynamic targets related features --- cmd/kluctl/commands/cmd_list_targets.go | 4 +- cmd/kluctl/commands/cmd_seal.go | 27 +--- cmd/kluctl/commands/completion.go | 12 +- pkg/kluctl_project/git.go | 50 ------ pkg/kluctl_project/project.go | 17 +- pkg/kluctl_project/project_load.go | 5 - pkg/kluctl_project/target_context.go | 4 +- pkg/kluctl_project/targets.go | 206 +----------------------- pkg/kluctl_project/targets_test.go | 26 --- pkg/types/kluctl_project.go | 24 +-- 10 files changed, 30 insertions(+), 345 deletions(-) delete mode 100644 pkg/kluctl_project/git.go delete mode 100644 pkg/kluctl_project/targets_test.go diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index 7d2649716..ce39e8476 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -19,8 +19,8 @@ func (cmd *listTargetsCmd) Help() string { func (cmd *listTargetsCmd) Run(ctx context.Context) error { return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, nil, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var result []*types.Target - for _, t := range p.DynamicTargets { - result = append(result, t.Target) + for _, t := range p.Targets { + result = append(result, t) } return outputYamlResult(ctx, cmd.Output, result, false) }) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index ec00b4612..69ecad6ee 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -158,35 +158,18 @@ func (cmd *sealCmd) Run(ctx context.Context) error { return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, nil, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { hadError := false - baseTargets := make(map[string]bool) noTargetMatch := true - for _, target := range p.DynamicTargets { - if cmd.Target != "" && cmd.Target != target.Target.Name { + for _, target := range p.Targets { + if cmd.Target != "" && cmd.Target != target.Name { continue } - if target.Target.SealingConfig == nil { - status.Info(ctx, "Target %s has no sealingConfig", target.Target.Name) + if target.SealingConfig == nil { + status.Info(ctx, "Target %s has no sealingConfig", target.Name) continue } noTargetMatch = false - sealTarget := target.Target - dynamicSealing := target.Target.SealingConfig.DynamicSealing == nil || *target.Target.SealingConfig.DynamicSealing - isDynamicTarget := target.BaseTargetName != target.Target.Name - if !dynamicSealing && isDynamicTarget { - baseTarget, err := p.FindBaseTarget(target.BaseTargetName) - if err != nil { - return err - } - if baseTargets[target.BaseTargetName] { - // Skip this target as it was already sealed - continue - } - baseTargets[target.BaseTargetName] = true - sealTarget = baseTarget - } - - err := cmd.runCmdSealForTarget(ctx, p, sealTarget.Name) + err := cmd.runCmdSealForTarget(ctx, p, target.Name) if err != nil { hadError = true status.Error(ctx, err.Error()) diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 225ae0fba..77e7fe978 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -59,8 +59,8 @@ func buildTargetCompletionFunc(ctx context.Context, projectArgs *args.ProjectFla return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var ret []string err := withProjectForCompletion(ctx, projectArgs, argsFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { - for _, t := range p.DynamicTargets { - ret = append(ret, t.Target.Name) + for _, t := range p.Targets { + ret = append(ret, t.Name) } return nil }) @@ -107,8 +107,8 @@ func buildInclusionCompletionFunc(ctx context.Context, cmdStruct interface{}, fo err := withProjectForCompletion(ctx, &ptArgs.projectFlags, &ptArgs.argsFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { - for _, t := range p.DynamicTargets { - targets = append(targets, t.Target.Name) + for _, t := range p.Targets { + targets = append(targets, t.Name) } } else { targets = append(targets, ptArgs.targetFlags.Target) @@ -161,8 +161,8 @@ func buildImagesCompletionFunc(ctx context.Context, cmdStruct interface{}) func( err := withProjectForCompletion(ctx, &ptArgs.projectFlags, &ptArgs.argsFlags, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var targets []string if ptArgs.targetFlags.Target == "" { - for _, t := range p.DynamicTargets { - targets = append(targets, t.Target.Name) + for _, t := range p.Targets { + targets = append(targets, t.Name) } } else { targets = append(targets, ptArgs.targetFlags.Target) diff --git a/pkg/kluctl_project/git.go b/pkg/kluctl_project/git.go deleted file mode 100644 index 393836704..000000000 --- a/pkg/kluctl_project/git.go +++ /dev/null @@ -1,50 +0,0 @@ -package kluctl_project - -import ( - "fmt" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "sync" -) - -func (c *LoadedKluctlProject) updateGitCaches() error { - var waitGroup sync.WaitGroup - var firstError error - var firstErrorLock sync.Mutex - - doError := func(err error) { - firstErrorLock.Lock() - defer firstErrorLock.Unlock() - if firstError == nil { - firstError = err - } - } - - doUpdateGitProject := func(u git_url.GitUrl) error { - waitGroup.Add(1) - go func() { - defer waitGroup.Done() - - _, err := c.RP.GetEntry(u) - if err != nil { - doError(fmt.Errorf("failed to update git project %v: %v", u.String(), err)) - } - }() - - return nil - } - - for _, target := range c.Config.Targets { - if target.TargetConfig == nil || target.TargetConfig.Project == nil { - continue - } - - err := doUpdateGitProject(target.TargetConfig.Project.Url) - if err != nil { - waitGroup.Wait() - return err - } - } - - waitGroup.Wait() - return firstError -} diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index cc3eb603a..d959f5dc3 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -21,28 +21,19 @@ type LoadedKluctlProject struct { sealedSecretsDir string - Config types2.KluctlProject - DynamicTargets []*types2.DynamicTarget + Config types2.KluctlProject + Targets []*types2.Target J2 *jinja2.Jinja2 RP *repocache.GitRepoCache SopsDecrypter *decryptor.Decryptor } -func (c *LoadedKluctlProject) FindBaseTarget(name string) (*types2.Target, error) { - for _, target := range c.Config.Targets { +func (c *LoadedKluctlProject) FindTarget(name string) (*types2.Target, error) { + for _, target := range c.Targets { if target.Name == name { return target, nil } } return nil, fmt.Errorf("target %s not existent in kluctl project config", name) } - -func (c *LoadedKluctlProject) FindDynamicTarget(name string) (*types2.DynamicTarget, error) { - for _, target := range c.DynamicTargets { - if target.Target.Name == name { - return target, nil - } - } - return nil, fmt.Errorf("target %s not existent in kluctl project config", name) -} diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 2af17c0d0..0d9648db1 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -57,11 +57,6 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { s := status.Start(c.ctx, "Loading kluctl project") defer s.Failed() - err = c.updateGitCaches() - if err != nil { - return err - } - c.sealedSecretsDir = filepath.Join(c.ProjectDir, ".sealed-secrets") sealedSecretsUsed := false diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index a16a9e027..4985784be 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -50,11 +50,11 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe var target *types.Target needRender := false if params.TargetName != "" { - t, err := p.FindDynamicTarget(params.TargetName) + t, err := p.FindTarget(params.TargetName) if err != nil { return nil, err } - target = &*t.Target + target = &*t } else { target = &types.Target{ Discriminator: p.Config.Discriminator, diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 59c27ee08..5a7ceb06d 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -1,16 +1,10 @@ package kluctl_project import ( - "fmt" - securejoin "github.com/cyphar/filepath-securejoin" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/yaml" - "os" - "regexp" "sort" - "strings" ) type dynamicTargetInfo struct { @@ -27,24 +21,11 @@ func (c *LoadedKluctlProject) loadTargets() error { defer status.Trace(c.ctx, "Done loading targets") targetNames := make(map[string]bool) - c.DynamicTargets = nil + c.Targets = nil - var targetInfos []*dynamicTargetInfo - for _, baseTarget := range c.Config.Targets { - l, err := c.prepareDynamicTargets(baseTarget) + for _, configTarget := range c.Config.Targets { + target, err := c.buildTarget(configTarget) if err != nil { - return err - } - targetInfos = append(targetInfos, l...) - } - - for _, targetInfo := range targetInfos { - target, err := c.buildDynamicTarget(targetInfo) - if err != nil { - // Only fail if non-dynamic targets fail to load - if targetInfo.refPattern == nil { - return err - } status.Warning(c.ctx, "Failed to load dynamic target config for project: %v", err) continue } @@ -59,14 +40,11 @@ func (c *LoadedKluctlProject) loadTargets() error { status.Warning(c.ctx, "Duplicate target %s", target.Name) } else { targetNames[target.Name] = true - c.DynamicTargets = append(c.DynamicTargets, &types.DynamicTarget{ - Target: target, - BaseTargetName: targetInfo.baseTarget.Name, - }) + c.Targets = append(c.Targets, target) } } - sort.SliceStable(c.DynamicTargets, func(i, j int) bool { - return c.DynamicTargets[i].Target.Name < c.DynamicTargets[j].Target.Name + sort.SliceStable(c.Targets, func(i, j int) bool { + return c.Targets[i].Name < c.Targets[j].Name }) return nil } @@ -91,182 +69,14 @@ func (c *LoadedKluctlProject) renderTarget(target *types.Target) error { return retErr } -func (c *LoadedKluctlProject) prepareDynamicTargets(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { - if baseTarget.TargetConfig != nil && baseTarget.TargetConfig.Project != nil { - return c.prepareDynamicTargetsExternal(baseTarget) - } else { - return c.prepareDynamicTargetsSimple(baseTarget) - } -} - -func (c *LoadedKluctlProject) prepareDynamicTargetsSimple(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { - if baseTarget.TargetConfig != nil { - if baseTarget.TargetConfig.Ref != nil || baseTarget.TargetConfig.RefPattern != nil { - return nil, fmt.Errorf("'ref' and/or 'refPattern' are not allowed for non-external dynamic targets") - } - } - dynamicTargets := []*dynamicTargetInfo{ - { - baseTarget: baseTarget, - dir: c.ProjectDir, - }, - } - return dynamicTargets, nil -} - -func (c *LoadedKluctlProject) prepareDynamicTargetsExternal(baseTarget *types.Target) ([]*dynamicTargetInfo, error) { - ge, err := c.RP.GetEntry(baseTarget.TargetConfig.Project.Url) - if err != nil { - return nil, err - } - - repoInfo := ge.GetRepoInfo() - - if baseTarget.TargetConfig.Ref != nil && baseTarget.TargetConfig.RefPattern != nil { - return nil, fmt.Errorf("'refPattern' and 'ref' can't be specified together") - } - - if baseTarget.TargetConfig.RefPattern != nil { - status.Deprecation(c.ctx, "dynamic-targets-ref-pattern", "'refPattern' and corresponding dynamic targets are deprecated and will be removed in the next kluctl release.") - } - - targetConfigRef := baseTarget.TargetConfig.Ref - refPattern := baseTarget.TargetConfig.RefPattern - - defaultBranch := repoInfo.DefaultRef - if defaultBranch == "" { - return nil, fmt.Errorf("git project %v seems to have no default branch", baseTarget.TargetConfig.Project.Url.String()) - } - - if baseTarget.TargetConfig.Ref == nil && baseTarget.TargetConfig.RefPattern == nil { - // use default branch of repo - targetConfigRef = &defaultBranch - } - - refs := repoInfo.RemoteRefs - if targetConfigRef != nil { - if _, ok := refs[fmt.Sprintf("refs/heads/%s", *targetConfigRef)]; !ok { - return nil, fmt.Errorf("git project %s has no ref %s", baseTarget.TargetConfig.Project.Url.String(), *targetConfigRef) - } - refPattern = targetConfigRef - } - - var dynamicTargets []*dynamicTargetInfo - for ref := range refs { - m, refShortName, err := c.matchRef(ref, *refPattern) - if err != nil { - return nil, err - } - if !m { - continue - } - - ge, err := c.RP.GetEntry(baseTarget.TargetConfig.Project.Url) - if err != nil { - return nil, err - } - - dir, _, err := ge.GetClonedDir(refShortName) - if err != nil { - return nil, err - } - - dynamicTargets = append(dynamicTargets, &dynamicTargetInfo{ - baseTarget: baseTarget, - dir: dir, - gitProject: baseTarget.TargetConfig.Project, - ref: &refShortName, - refPattern: refPattern, - defaultBranch: defaultBranch, - }) - } - return dynamicTargets, nil -} - -func (c *LoadedKluctlProject) matchRef(s string, pattern string) (bool, string, error) { - if strings.HasPrefix(pattern, "refs/") { - p, err := regexp.Compile(fmt.Sprintf("^%s$", pattern)) - if err != nil { - return false, "", err - } - return p.MatchString(s), s, nil - } - p1, err := regexp.Compile(fmt.Sprintf("^refs/heads/%s$", pattern)) - if err != nil { - return false, "", err - } - p2, err := regexp.Compile(fmt.Sprintf("^refs/tags/%s$", pattern)) - if err != nil { - return false, "", err - } - if p1.MatchString(s) { - return true, s[len("refs/heads/"):], nil - } else if p2.MatchString(s) { - return true, s[len("refs/tags/"):], nil - } else { - return false, "", nil - } -} - -func (c *LoadedKluctlProject) loadTargetConfigFile(targetInfo *dynamicTargetInfo) ([]byte, error) { - configFile := yaml.FixNameExt(targetInfo.dir, "target-config.yml") - if targetInfo.baseTarget.TargetConfig.File != nil { - configFile = *targetInfo.baseTarget.TargetConfig.File - } - configPath, err := securejoin.SecureJoin(targetInfo.dir, configFile) - if err != nil { - return nil, err - } - if !utils.IsFile(configPath) { - return nil, fmt.Errorf("no target config file with name %s found in target", configFile) - } - - return os.ReadFile(configPath) -} - -func (c *LoadedKluctlProject) buildDynamicTarget(targetInfo *dynamicTargetInfo) (*types.Target, error) { +func (c *LoadedKluctlProject) buildTarget(configTarget *types.Target) (*types.Target, error) { var target types.Target - err := utils.DeepCopy(&target, targetInfo.baseTarget) + err := utils.DeepCopy(&target, configTarget) if err != nil { return nil, err } if target.Discriminator == "" { target.Discriminator = c.Config.Discriminator } - if targetInfo.baseTarget.TargetConfig == nil { - return &target, nil - } - - status.Deprecation(c.ctx, "target-config", "'targetConfig' in targets is deprecated and will be removed in the next kluctl release.") - - configFile, err := c.loadTargetConfigFile(targetInfo) - if err != nil { - return nil, err - } - - var targetConfig types.TargetConfig - err = yaml.ReadYamlBytes(configFile, &targetConfig) - if err != nil { - return nil, err - } - - if len(target.DynamicArgs) != 0 { - status.Deprecation(c.ctx, "dynamic-args", "dynamicArgs are deprecated and ignored. The field will be removed in the next kluctl release.") - } - - // check and merge args - if targetConfig.Args != nil { - target.Args.Merge(targetConfig.Args) - } - if len(targetConfig.Images) != 0 { - status.Deprecation(c.ctx, "target-config-images", "specifying fixed images via external 'targetConfig' is deprecated and support for this will be removed in the next kluctl release.") - } - // We prepend the dynamic images to ensure they get higher priority later - target.Images = append(targetConfig.Images, target.Images...) - - if targetInfo.ref != nil { - target.TargetConfig.Ref = targetInfo.ref - } - return &target, nil } diff --git a/pkg/kluctl_project/targets_test.go b/pkg/kluctl_project/targets_test.go deleted file mode 100644 index 8f1cf30b1..000000000 --- a/pkg/kluctl_project/targets_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package kluctl_project - -import ( - "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/stretchr/testify/assert" - "os" - "path/filepath" - "testing" -) - -func TestLoadTargetConfigFile(t *testing.T) { - c := LoadedKluctlProject{} - ti := &dynamicTargetInfo{ - baseTarget: &types.Target{ - TargetConfig: &types.ExternalTargetConfig{}, - }, - dir: t.TempDir(), - } - err := os.WriteFile(filepath.Join(ti.dir, "target-config.yml"), []byte("test"), 0600) - assert.NoError(t, err) - - data, err := c.loadTargetConfigFile(ti) - assert.NoError(t, err) - - assert.Equal(t, []byte("test"), data) -} diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 33078a4ae..840542dd6 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -8,22 +8,10 @@ type DynamicArg struct { Name string `yaml:"name" validate:"required"` } -type ExternalTargetConfig struct { - Project *GitProject `yaml:"project,omitempty"` - // Ref Branch/Tag to be used. Can't be combined with 'refPattern'. If 'branch' and 'branchPattern' are not used, 'branch' defaults to the default branch of targetConfig.project - Ref *string `yaml:"ref,omitempty"` - // RefPattern If set, multiple dynamic targets are created, each with 'ref' being set to the ref that matched the given pattern. - RefPattern *string `yaml:"refPattern,omitempty"` - // File defaults to 'target-config.yml' - File *string `yaml:"file,omitempty"` -} - type SealingConfig struct { - // DynamicSealing Set this to false if you want to disable sealing for every dynamic target - DynamicSealing *bool `yaml:"dynamicSealing,omitempty"` - Args *uo.UnstructuredObject `yaml:"args,omitempty"` - SecretSets []string `yaml:"secretSets,omitempty"` - CertFile *string `yaml:"certFile,omitempty"` + Args *uo.UnstructuredObject `yaml:"args,omitempty"` + SecretSets []string `yaml:"secretSets,omitempty"` + CertFile *string `yaml:"certFile,omitempty"` } type Target struct { @@ -31,17 +19,11 @@ type Target struct { Context *string `yaml:"context,omitempty"` Args *uo.UnstructuredObject `yaml:"args,omitempty"` DynamicArgs []DynamicArg `yaml:"dynamicArgs,omitempty"` - TargetConfig *ExternalTargetConfig `yaml:"targetConfig,omitempty"` SealingConfig *SealingConfig `yaml:"sealingConfig,omitempty"` Images []FixedImage `yaml:"images,omitempty"` Discriminator string `yaml:"discriminator,omitempty"` } -type DynamicTarget struct { - Target *Target `yaml:"target" validate:"required"` - BaseTargetName string `yaml:"baseTargetName"` -} - type DeploymentArg struct { Name string `yaml:"name" validate:"required"` Default interface{} `yaml:"default,omitempty"` From 65ce9dfe09adb391e0c9a0f49c285bd58164f6fd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 13 Feb 2023 10:26:38 +0100 Subject: [PATCH 0792/2268] refactor: Use semver lib for checkNewVersion --- cmd/kluctl/commands/root.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 2b37ba7d3..3736d00ad 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -18,6 +18,7 @@ package commands import ( "context" "fmt" + "github.com/Masterminds/semver/v3" flag "github.com/spf13/pflag" "io" "log" @@ -28,10 +29,10 @@ import ( "strings" "time" + "github.com/Masterminds/semver/v3" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/version" "github.com/kluctl/kluctl/v2/pkg/yaml" "github.com/mattn/go-colorable" @@ -184,10 +185,18 @@ func checkNewVersion(ctx context.Context) { if strings.HasPrefix(latestVersionStr, "v") { latestVersionStr = latestVersionStr[1:] } - latestVersion := versions.LooseVersion(latestVersionStr) - localVersion := versions.LooseVersion(version.GetVersion()) - if localVersion.Less(latestVersion, true) { - s.Update(fmt.Sprintf("You are using an outdated version (%v) of kluctl. You should update soon to version %v", localVersion, latestVersion)) + latestVersion, err := semver.NewVersion(latestVersionStr) + if err != nil { + s.FailedWithMessage("Failed to parse latest version: %v", err) + return + } + localVersion, err := semver.NewVersion(version.GetVersion()) + if err != nil { + s.FailedWithMessage("Failed to parse local version: %v", err) + return + } + if localVersion.LessThan(latestVersion) { + s.Update(fmt.Sprintf("You are using an outdated version (%v) of kluctl. You should update soon to version %v", localVersion.String(), latestVersion.String())) } else { s.Update("Your kluctl version is up-to-date") } From 1114983aa66007752907810b43201c529b469aa5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 11 Feb 2023 13:50:24 +0100 Subject: [PATCH 0793/2268] feat: Remove deprecated code in regard to querying latest image versions --- cmd/kluctl/args/images.go | 2 - .../commands/cmd_check_image_updates.go | 135 -------- cmd/kluctl/commands/root.go | 28 +- cmd/kluctl/commands/utils.go | 14 +- docs/reference/commands/common-arguments.md | 10 - pkg/deployment/images.go | 76 +---- pkg/kluctl_jinja2/ext/images_ext.py | 5 +- pkg/types/target_config.go | 2 - pkg/utils/versions/latest_version.go | 160 ---------- pkg/utils/versions/latest_version_parse.go | 299 ------------------ .../versions/latest_version_parse_test.go | 47 --- pkg/utils/versions/looseversion.go | 171 ---------- pkg/utils/versions/looseversion_test.go | 92 ------ 13 files changed, 23 insertions(+), 1018 deletions(-) delete mode 100644 cmd/kluctl/commands/cmd_check_image_updates.go delete mode 100644 pkg/utils/versions/latest_version.go delete mode 100644 pkg/utils/versions/latest_version_parse.go delete mode 100644 pkg/utils/versions/latest_version_parse_test.go delete mode 100644 pkg/utils/versions/looseversion.go delete mode 100644 pkg/utils/versions/looseversion_test.go diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index 7d0364722..1c6fe4b79 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -10,8 +10,6 @@ import ( type ImageFlags struct { FixedImage []string `group:"images" short:"F" help:"Pin an image to a given version. Expects '--fixed-image=image<:namespace:deployment:container>=result'"` FixedImagesFile existingFileType `group:"images" help:"Use .yaml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format" exts:"yml,yaml"` - UpdateImages bool `group:"images" short:"u" help:"This causes kluctl to prefer the latest image found in registries, based on the 'latest_image' filters provided to 'images.get_image(...)' calls. Use this flag if you want to update to the latest versions/tags of all images. '-u' takes precedence over '--fixed-image/--fixed-images-file', meaning that the latest images are used even if an older image is given via fixed images."` - OfflineImages bool `group:"images" help:"DEPRECATED: Omit contacting image registries and do not query for latest image tags. This flag is by default set to true. At the same time, the whole requesting of image tags from registries functionality is deprecated and will be removed from kluctl in a future release." default:"true"` } func (args *ImageFlags) LoadFixedImagesFromArgs() ([]types.FixedImage, error) { diff --git a/cmd/kluctl/commands/cmd_check_image_updates.go b/cmd/kluctl/commands/cmd_check_image_updates.go deleted file mode 100644 index 782ed9868..000000000 --- a/cmd/kluctl/commands/cmd_check_image_updates.go +++ /dev/null @@ -1,135 +0,0 @@ -package commands - -import ( - "context" - "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/registries" - "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/versions" - "regexp" - "sort" - "strings" - "sync" -) - -type checkImageUpdatesCmd struct { - args.ProjectFlags - args.TargetFlags -} - -func (cmd *checkImageUpdatesCmd) Help() string { - return `DEPRECATED: This is based on a best effort approach and might give many false-positives.` -} - -func (cmd *checkImageUpdatesCmd) Run(ctx context.Context) error { - status.Deprecation(ctx, "check-image-updates", "check-image-updates is deprecated and will be removed in the next kluctl release.") - ptArgs := projectTargetCommandArgs{ - projectFlags: cmd.ProjectFlags, - targetFlags: cmd.TargetFlags, - } - return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - return runCheckImageUpdates(cmdCtx) - }) -} - -func runCheckImageUpdates(cmdCtx *commandCtx) error { - renderedImages := cmdCtx.targetCtx.DeploymentCollection.FindRenderedImages() - - rh := registries.NewRegistryHelper(cmdCtx.ctx) - - imageTags := make(map[string]interface{}) - var mutex sync.Mutex - var wg sync.WaitGroup - - for _, images := range renderedImages { - for _, image := range images { - s := strings.SplitN(image, ":", 2) - if len(s) == 1 { - continue - } - repo := s[0] - if _, ok := imageTags[repo]; !ok { - wg.Add(1) - go func() { - defer wg.Done() - tags, err := rh.ListImageTags(repo) - mutex.Lock() - defer mutex.Unlock() - if err != nil { - imageTags[repo] = err - } else { - imageTags[repo] = tags - } - }() - } - } - } - wg.Wait() - - prefixPattern := regexp.MustCompile("^([a-zA-Z]+[a-zA-Z-_.]*)") - suffixPattern := regexp.MustCompile("([-][a-zA-Z]+[a-zA-Z-_.]*)$") - - var table utils.PrettyTable - table.AddRow("Object", "Image", "Old", "New") - - for ref, images := range renderedImages { - for _, image := range images { - s := strings.SplitN(image, ":", 2) - if len(s) == 1 { - status.Warning(cmdCtx.ctx, "%s: Ignoring image %s as it doesn't specify a tag", ref.String(), image) - continue - } - repo := s[0] - curTag := s[1] - repoTags, _ := imageTags[repo].([]string) - err, _ := imageTags[repo].(error) - if err != nil { - status.Warning(cmdCtx.ctx, "%s: Failed to list tags for %s. %v", ref.String(), repo, err) - continue - } - - prefix := prefixPattern.FindString(curTag) - suffix := suffixPattern.FindString(curTag) - hasDot := strings.Index(curTag, ".") != -1 - - var filteredTags []string - for _, tag := range repoTags { - hasDot2 := strings.Index(tag, ".") != -1 - if hasDot != hasDot2 { - continue - } - if prefix != "" && !strings.HasPrefix(tag, prefix) { - continue - } - if suffix != "" && !strings.HasSuffix(tag, suffix) { - continue - } - filteredTags = append(filteredTags, tag) - } - doKey := func(tag string) versions.LooseVersion { - if prefix != "" { - tag = tag[len(prefix):] - } - if suffix != "" { - tag = tag[:len(tag)-len(suffix)] - } - return versions.LooseVersion(tag) - } - sort.SliceStable(filteredTags, func(i, j int) bool { - a := doKey(filteredTags[i]) - b := doKey(filteredTags[j]) - return a.Less(b, true) - }) - latestTag := filteredTags[len(filteredTags)-1] - - if latestTag != curTag { - table.AddRow(ref.String(), repo, curTag, latestTag) - } - } - } - - table.SortRows(1) - _, _ = getStdout(cmdCtx.ctx).WriteString(table.Render([]int{60})) - return nil -} diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 3736d00ad..4b949d272 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -18,7 +18,6 @@ package commands import ( "context" "fmt" - "github.com/Masterminds/semver/v3" flag "github.com/spf13/pflag" "io" "log" @@ -55,20 +54,19 @@ type GlobalFlags struct { type cli struct { GlobalFlags - CheckImageUpdates checkImageUpdatesCmd `cmd:"" help:"Render deployment and check if any images have new tags available"` - Delete deleteCmd `cmd:"" help:"Delete a target (or parts of it) from the corresponding cluster"` - Deploy deployCmd `cmd:"" help:"Deploys a target to the corresponding cluster"` - Diff diffCmd `cmd:"" help:"Perform a diff between the locally rendered target and the already deployed target"` - HelmPull helmPullCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and pre-pulls the specified Helm charts"` - HelmUpdate helmUpdateCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and checks for new available versions"` - ListImages listImagesCmd `cmd:"" help:"Renders the target and outputs all images used via 'images.get_image(...)"` - ListTargets listTargetsCmd `cmd:"" help:"Outputs a yaml list with all targets"` - PokeImages pokeImagesCmd `cmd:"" help:"Replace all images in target"` - Prune pruneCmd `cmd:"" help:"Searches the target cluster for prunable objects and deletes them"` - Render renderCmd `cmd:"" help:"Renders all resources and configuration files"` - Seal sealCmd `cmd:"" help:"Seal secrets based on target's sealingConfig"` - Validate validateCmd `cmd:"" help:"Validates the already deployed deployment"` - Flux fluxCmd `cmd:"" help:"Flux sub-commands"` + Delete deleteCmd `cmd:"" help:"Delete a target (or parts of it) from the corresponding cluster"` + Deploy deployCmd `cmd:"" help:"Deploys a target to the corresponding cluster"` + Diff diffCmd `cmd:"" help:"Perform a diff between the locally rendered target and the already deployed target"` + HelmPull helmPullCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and pre-pulls the specified Helm charts"` + HelmUpdate helmUpdateCmd `cmd:"" help:"Recursively searches for 'helm-chart.yaml' files and checks for new available versions"` + ListImages listImagesCmd `cmd:"" help:"Renders the target and outputs all images used via 'images.get_image(...)"` + ListTargets listTargetsCmd `cmd:"" help:"Outputs a yaml list with all targets"` + PokeImages pokeImagesCmd `cmd:"" help:"Replace all images in target"` + Prune pruneCmd `cmd:"" help:"Searches the target cluster for prunable objects and deletes them"` + Render renderCmd `cmd:"" help:"Renders all resources and configuration files"` + Seal sealCmd `cmd:"" help:"Seal secrets based on target's sealingConfig"` + Validate validateCmd `cmd:"" help:"Validates the already deployed deployment"` + Flux fluxCmd `cmd:"" help:"Flux sub-commands"` Version versionCmd `cmd:"" help:"Print kluctl version"` } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index b2807f72b..35fa12bab 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -15,7 +15,6 @@ import ( ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" - "github.com/kluctl/kluctl/v2/pkg/registries" "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -145,18 +144,7 @@ func withProjectCommandContext(ctx context.Context, args projectTargetCommandArg } func withProjectTargetCommandContext(ctx context.Context, args projectTargetCommandArgs, p *kluctl_project.LoadedKluctlProject, cb func(cmdCtx *commandCtx) error) error { - rh := registries.NewRegistryHelper(ctx) - err := rh.ParseAuthEntriesFromEnv() - if err != nil { - return fmt.Errorf("failed to parse registry auth from environment: %w", err) - } - if args.imageFlags.UpdateImages { - status.Deprecation(ctx, "update-images", "--update-images is deprecated and will be removed in the next kluctl release.") - } - if !args.imageFlags.OfflineImages { - status.Deprecation(ctx, "online-images", "--offline-images=false is deprecated and will be removed in the next kluctl release.") - } - images, err := deployment.NewImages(rh, args.imageFlags.UpdateImages, args.imageFlags.OfflineImages || args.forCompletion) + images, err := deployment.NewImages() if err != nil { return err } diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index dbdf48621..2f2e85433 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -98,16 +98,6 @@ Image arguments: '--fixed-image=image<:namespace:deployment:container>=result' --fixed-images-file existingfile Use .yaml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format - --offline-images DEPRECATED: Omit contacting image registries and do not query for latest - image tags. This flag is by default set to true. At the same time, the - whole requesting of image tags from registries functionality is - deprecated and will be removed from kluctl in a future release. (default true) - -u, --update-images This causes kluctl to prefer the latest image found in registries, based - on the 'latest_image' filters provided to 'images.get_image(...)' calls. - Use this flag if you want to update to the latest versions/tags of all - images. '-u' takes precedence over '--fixed-image/--fixed-images-file', - meaning that the latest images are used even if an older image is given - via fixed images. ``` diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 11112761a..b1af402d6 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -6,13 +6,10 @@ import ( "encoding/base64" "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/registries" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/utils/versions" "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/apimachinery/pkg/api/errors" "sort" @@ -21,22 +18,13 @@ import ( ) type Images struct { - rh *registries.RegistryHelper - updateImages bool - offline bool - fixedImages []types.FixedImage - seenImages []types.FixedImage - mutex sync.Mutex - - registryCache utils.ThreadSafeMultiCache + fixedImages []types.FixedImage + seenImages []types.FixedImage + mutex sync.Mutex } -func NewImages(rh *registries.RegistryHelper, updateImages bool, offline bool) (*Images, error) { - return &Images{ - rh: rh, - updateImages: updateImages, - offline: offline, - }, nil +func NewImages() (*Images, error) { + return &Images{}, nil } func (images *Images) AddFixedImage(fi types.FixedImage) { @@ -124,44 +112,11 @@ func (images *Images) getFixedImage(image string, namespace string, deployment s return nil, nil } -func (images *Images) GetLatestImageFromRegistry(image string, latestVersion string) (*string, error) { - if images.offline { - return nil, nil - } - - ret, err := images.registryCache.Get(image, "tag", func() (interface{}, error) { - return images.rh.ListImageTags(image) - }) - if err != nil { - return nil, err - } - tags, _ := ret.([]string) - - if len(tags) == 0 { - return nil, nil - } - - lv, err := versions.ParseLatestVersion(latestVersion) - if err != nil { - return nil, err - } - - tags = versions.Filter(lv, tags) - if len(tags) == 0 { - return nil, fmt.Errorf("no tag matched latest_version: %s", latestVersion) - } - - latest := lv.Latest(tags) - result := fmt.Sprintf("%s:%s", image, latest) - return &result, nil -} - const beginPlaceholder = "XXXXXbegin_get_image_" const endPlaceholder = "_end_get_imageXXXXX" type placeHolder struct { Image string `yaml:"image"` - LatestVersion string `yaml:"latestVersion"` HasLatestVersion bool `yaml:"hasLatestVersion"` Container string @@ -284,7 +239,7 @@ func (images *Images) ResolvePlaceholders(ctx context.Context, k *k8s.K8sCluster return err } if resultImage == nil { - return fmt.Errorf("failed to find image for %s and latest version %s", ph.Image, ph.LatestVersion) + return fmt.Errorf("failed to find fixed image for %s", ph.Image) } ph.FieldValue = ph.FieldValue[:ph.StartOffset] + *resultImage + ph.FieldValue[ph.EndOffset:] @@ -297,37 +252,22 @@ func (images *Images) ResolvePlaceholders(ctx context.Context, k *k8s.K8sCluster } func (images *Images) resolveImage(ctx context.Context, ph placeHolder, ref k8s2.ObjectRef, deployment string, deployed *string, deploymentDir string, tags []string, vars *uo.UnstructuredObject) (*string, error) { - fixed, err := images.getFixedImage(ph.Image, ref.Namespace, deployment, ph.Container, vars) - if err != nil { - return nil, err - } - if ph.HasLatestVersion { - status.Deprecation(ctx, "latest-version-filter", "latest_version is deprecated when using images.get_image() and will be removed in the next kluctl release.") + status.Deprecation(ctx, "latest-version-filter", "latest_version is deprecated when using images.get_image() and is completely ignored. Please remove usages of latest_version as it will fail to render in a future kluctl release.") } - registry, err := images.GetLatestImageFromRegistry(ph.Image, ph.LatestVersion) + result, err := images.getFixedImage(ph.Image, ref.Namespace, deployment, ph.Container, vars) if err != nil { return nil, err } - result := deployed - if result == nil || images.updateImages { - result = registry - } - if !images.updateImages && fixed != nil { - result = fixed - } - si := types.FixedImage{ Image: ph.Image, DeployedImage: deployed, - RegistryImage: registry, Namespace: &ref.Namespace, Object: &ref, Deployment: &deployment, Container: &ph.Container, - VersionFilter: &ph.LatestVersion, DeployTags: tags, DeploymentDir: &deploymentDir, } diff --git a/pkg/kluctl_jinja2/ext/images_ext.py b/pkg/kluctl_jinja2/ext/images_ext.py index ba523653a..0d0fd7ebd 100644 --- a/pkg/kluctl_jinja2/ext/images_ext.py +++ b/pkg/kluctl_jinja2/ext/images_ext.py @@ -13,13 +13,10 @@ def __init__(self, environment): def get_image_wrapper(self, image, latest_version=None): has_latest_version = False - if latest_version is None: - latest_version = "semver()" - else: + if latest_version is not None: has_latest_version = True placeholder = { "image": image, - "latestVersion": str(latest_version), "hasLatestVersion": has_latest_version, } j = json.dumps(placeholder) diff --git a/pkg/types/target_config.go b/pkg/types/target_config.go index 4f27a0cf6..7540757f6 100644 --- a/pkg/types/target_config.go +++ b/pkg/types/target_config.go @@ -9,12 +9,10 @@ type FixedImage struct { Image string `yaml:"image" validate:"required"` ResultImage string `yaml:"resultImage" validate:"required"` DeployedImage *string `yaml:"deployedImage,omitempty"` - RegistryImage *string `yaml:"registryImage,omitempty"` Namespace *string `yaml:"namespace,omitempty"` Object *k8s.ObjectRef `yaml:"object,omitempty"` Deployment *string `yaml:"deployment,omitempty"` Container *string `yaml:"container,omitempty"` - VersionFilter *string `yaml:"versionFilter,omitempty"` DeployTags []string `yaml:"deployTags,omitempty"` DeploymentDir *string `yaml:"deploymentDir,omitempty"` } diff --git a/pkg/utils/versions/latest_version.go b/pkg/utils/versions/latest_version.go deleted file mode 100644 index 24edbc5b2..000000000 --- a/pkg/utils/versions/latest_version.go +++ /dev/null @@ -1,160 +0,0 @@ -package versions - -import ( - "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils" - "regexp" - "strconv" -) - -type LatestVersionFilter interface { - Match(version string) bool - Latest(versions []string) string - String() string -} - -func Filter(lv LatestVersionFilter, versions []string) []string { - var filtered []string - for _, v := range versions { - if lv.Match(v) { - filtered = append(filtered, v) - } - } - return filtered -} - -type regexVersionFilter struct { - patternStr string - pattern *regexp.Regexp -} - -func NewRegexVersionFilter(pattern string) (LatestVersionFilter, error) { - p, err := regexp.Compile(fmt.Sprintf("^%s$", pattern)) - if err != nil { - return nil, err - } - return ®exVersionFilter{ - patternStr: pattern, - pattern: p, - }, nil -} - -func NewRegexVersionFilterMust(pattern string) LatestVersionFilter { - ret, err := NewRegexVersionFilter(pattern) - if err != nil { - panic(err) - } - return ret -} - -func (f *regexVersionFilter) Match(version string) bool { - return f.pattern.MatchString(version) -} - -func (f *regexVersionFilter) Latest(versions []string) string { - c := SortLooseVersionStrings(versions) - return string(c[len(c)-1]) -} - -func (f *regexVersionFilter) String() string { - return fmt.Sprintf(`regex(pattern="%s")`, f.patternStr) -} - -type looseSemVerVersionFilter struct { - allowNoNums bool -} - -func NewLooseSemVerVersionFilter(allowNoNums bool) LatestVersionFilter { - return &looseSemVerVersionFilter{allowNoNums: allowNoNums} -} - -func (f *looseSemVerVersionFilter) Match(version string) bool { - groups := looseSemverRegex.FindStringSubmatch(version) - if groups == nil { - return false - } - if !f.allowNoNums && groups[1] == "" { - return false - } - return true -} - -func (f *looseSemVerVersionFilter) Latest(versions []string) string { - c := SortLooseVersionStrings(versions) - return string(c[len(c)-1]) -} - -func (f *looseSemVerVersionFilter) String() string { - return fmt.Sprintf(`semver(allow_no_nums=%s)`, strconv.FormatBool(f.allowNoNums)) -} - -type prefixVersionFilter struct { - prefix string - suffix LatestVersionFilter - pattern *regexp.Regexp -} - -func NewPrefixVersionFilter(prefix string, suffix LatestVersionFilter) (LatestVersionFilter, error) { - if suffix == nil { - suffix = NewLooseSemVerVersionFilter(false) - } - - p, err := regexp.Compile(fmt.Sprintf(`^%s(.*)$`, prefix)) - if err != nil { - return nil, err - } - return &prefixVersionFilter{ - prefix: prefix, - suffix: suffix, - pattern: p, - }, nil -} - -func NewPrefixVersionFilterMust(prefix string, suffix LatestVersionFilter) LatestVersionFilter { - ret, err := NewPrefixVersionFilter(prefix, suffix) - if err != nil { - panic(err) - } - return ret -} - -func (f *prefixVersionFilter) Match(version string) bool { - groups := f.pattern.FindStringSubmatch(version) - if groups == nil { - return false - } - return f.suffix.Match(groups[1]) -} - -func (f *prefixVersionFilter) Latest(versions []string) string { - var filteredVersions []string - var suffixVersions []string - for _, v := range versions { - groups := f.pattern.FindStringSubmatch(v) - if groups == nil { - continue - } - filteredVersions = append(filteredVersions, v) - suffixVersions = append(suffixVersions, groups[1]) - } - latest := f.suffix.Latest(suffixVersions) - i := utils.FindStrInSlice(suffixVersions, latest) - return filteredVersions[i] -} - -func (f *prefixVersionFilter) String() string { - return fmt.Sprintf(`prefix(prefix="%s", suffix=%s)`, f.prefix, f.suffix.String()) -} - -type numberVersionFilter struct { - LatestVersionFilter -} - -func NewNumberVersionFilter() LatestVersionFilter { - f, _ := NewRegexVersionFilter("[0-9]+") - return &numberVersionFilter{f} -} - -func (f *numberVersionFilter) String() string { - return "number()" -} diff --git a/pkg/utils/versions/latest_version_parse.go b/pkg/utils/versions/latest_version_parse.go deleted file mode 100644 index 8317e46e6..000000000 --- a/pkg/utils/versions/latest_version_parse.go +++ /dev/null @@ -1,299 +0,0 @@ -package versions - -import ( - "fmt" - scanner "github.com/kluctl/kluctl/v2/pkg/utils/python_scanner" - "strconv" - "strings" -) - -type tokenAndText struct { - tok rune - text string -} - -type preparsed struct { - tokens []tokenAndText - nextPos int -} - -func (p *preparsed) CurToken() rune { - if p.nextPos > len(p.tokens) { - return scanner.EOF - } - return p.tokens[p.nextPos-1].tok -} - -func (p *preparsed) CurTokenText() string { - if p.nextPos > len(p.tokens) { - panic("CurTokenText at EOF") - } - return p.tokens[p.nextPos-1].text -} - -func (p *preparsed) Next() rune { - if p.nextPos > len(p.tokens) { - return scanner.EOF - } - p.nextPos += 1 - return p.CurToken() -} - -func (p *preparsed) Peek() rune { - return p.PeekN(0) -} - -func (p *preparsed) PeekN(n int) rune { - if p.nextPos+n >= len(p.tokens) { - return scanner.EOF - } - return p.tokens[p.nextPos+n].tok -} - -func ParseLatestVersion(str string) (LatestVersionFilter, error) { - var p preparsed - var s scanner.Scanner - s.Init(strings.NewReader(str)) - s.Mode |= scanner.ScanIdents | scanner.ScanStrings - - for true { - tok := s.Scan() - if tok == scanner.EOF { - break - } - p.tokens = append(p.tokens, tokenAndText{ - tok: tok, - text: s.TokenText(), - }) - } - - return parseFilter(&p) -} - -func parseFilter(p *preparsed) (LatestVersionFilter, error) { - tok := p.Next() - - if tok != scanner.Ident { - return nil, fmt.Errorf("unexpected token %v, expected ident", p.CurToken()) - } - name := p.CurTokenText() - - tok = p.Next() - if tok != '(' { - return nil, fmt.Errorf("unexpected token %v, expected (", tok) - } - - var f LatestVersionFilter - - var err error - switch name { - case "regex": - f, err = parseRegexFilter(p) - case "semver": - f, err = parseSemVerFilter(p) - case "prefix": - f, err = parsePrefixFilter(p) - case "number": - f, err = parseNumberFilter(p) - } - if err != nil { - return nil, err - } - - tok = p.Next() - if tok != ')' { - return nil, fmt.Errorf("unexpected token %v, expected (", tok) - } - - return f, nil -} - -func parseRegexFilter(p *preparsed) (LatestVersionFilter, error) { - args := []*arg{ - {name: "pattern", tok: scanner.String, required: true}, - } - err := parseArgs(p, args) - if err != nil { - return nil, err - } - - if args[0].value == "" { - return nil, fmt.Errorf("pattern can't be empty") - } - - return NewRegexVersionFilter(args[0].value.(string)) -} - -func parseSemVerFilter(p *preparsed) (LatestVersionFilter, error) { - args := []*arg{ - {name: "allow_no_nums", tok: scanner.Ident, value: false, isBool: true}, - } - err := parseArgs(p, args) - if err != nil { - return nil, err - } - - return NewLooseSemVerVersionFilter(args[0].value.(bool)), nil -} - -func parsePrefixFilter(p *preparsed) (LatestVersionFilter, error) { - args := []*arg{ - {name: "prefix", tok: scanner.String, required: true}, - {name: "suffix", tok: scanner.Ident, value: ""}, - } - err := parseArgs(p, args) - if err != nil { - return nil, err - } - - var suffix LatestVersionFilter - if args[1].found { - x, ok := args[1].value.(LatestVersionFilter) - if !ok { - return nil, fmt.Errorf("invalid suffix, must be a filter") - } - suffix = x - } - - return NewPrefixVersionFilter(args[0].value.(string), suffix) -} - -func parseNumberFilter(p *preparsed) (LatestVersionFilter, error) { - return NewNumberVersionFilter(), nil -} - -type arg struct { - name string - tok rune - value interface{} - required bool - isBool bool - - found bool -} - -func parseArgs(p *preparsed, args []*arg) error { - var parsedArgs []arg - parsedKvArgs := make(map[string]arg) - - for true { - var name string - - if p.Peek() == ')' { - break - } - if len(parsedArgs)+len(parsedKvArgs) != 0 { - if tok := p.Next(); tok != ',' { - return fmt.Errorf("unexpected token %v, expected ')' or ','", tok) - } - } - - a, err := parseArg(p) - if err != nil { - return err - } - - if a.name == "" { - if len(parsedKvArgs) != 0 { - return fmt.Errorf("can't have unnamed args after named args") - } - parsedArgs = append(parsedArgs, *a) - } else { - if _, ok := parsedKvArgs[name]; ok { - return fmt.Errorf("duplicate named arg %s", name) - } - parsedKvArgs[a.name] = *a - } - } - - if len(parsedArgs) > len(args) { - return fmt.Errorf("too many arguments") - } - for i, a := range parsedArgs { - if a.tok != args[i].tok { - return fmt.Errorf("unexpected argument type for %s, expected %v, got %v", a.name, args[i].tok, a.tok) - } - args[i].value = a.value - args[i].found = true - } - - for k, v := range parsedKvArgs { - foundArg := false - for _, a := range args { - if k == a.name { - foundArg = true - if a.found { - return fmt.Errorf("named arg %s has already been provided via unnamed args", k) - } - a.value = v.value - a.found = true - } - } - if !foundArg { - return fmt.Errorf("unkown arg %s", k) - } - } - - for _, a := range args { - if a.required && !a.found { - return fmt.Errorf("required arg %s not found", a.name) - } - if a.found && a.isBool { - if a.tok != scanner.Ident { - return fmt.Errorf("invalid value for arg %s, must be a bool", a.name) - } - b, err := strconv.ParseBool(a.value.(string)) - if err != nil { - return fmt.Errorf("invalid value for arg %s, must be a bool", a.name) - } - a.value = b - } - } - - return nil -} - -func parseArg(p *preparsed) (*arg, error) { - parseValue := func() (rune, interface{}, error) { - if p.Peek() == scanner.Ident && p.PeekN(1) == '(' { - f, err := parseFilter(p) - return scanner.Ident, f, err - } - tok := p.Next() - if tok != scanner.String && tok != scanner.Ident && tok != scanner.Int { - return 'e', nil, fmt.Errorf("unexpected token %v, expected string, ident or int", tok) - } - value := p.CurTokenText() - if tok == scanner.String { - value = value[1 : len(value)-1] - } - return tok, value, nil - } - - if p.Peek() == scanner.Ident && p.PeekN(1) == '=' { - p.Next() // consume ident - name := p.CurTokenText() - p.Next() // consume '=' - - valueTok, value, err := parseValue() - if err != nil { - return nil, err - } - a := arg{ - name: name, - tok: valueTok, - value: value, - } - return &a, nil - } - - valueTok, value, err := parseValue() - if err != nil { - return nil, err - } - a := arg{ - tok: valueTok, - value: value, - } - return &a, nil -} diff --git a/pkg/utils/versions/latest_version_parse_test.go b/pkg/utils/versions/latest_version_parse_test.go deleted file mode 100644 index 31ba9c483..000000000 --- a/pkg/utils/versions/latest_version_parse_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package versions - -import ( - "fmt" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestParse(t *testing.T) { - - type testCase struct { - s string - expectedFilter LatestVersionFilter - expectedErr error - } - - testCases := []testCase{ - {s: "regex('a*')", expectedFilter: NewRegexVersionFilterMust("a*")}, - {s: "regex(\"a*\")", expectedFilter: NewRegexVersionFilterMust("a*")}, - {s: "regex(a*\")", expectedErr: fmt.Errorf("unexpected token 42, expected ')' or ','")}, - {s: "semver()", expectedFilter: NewLooseSemVerVersionFilter(false)}, - {s: "semver(allow_no_nums)", expectedErr: fmt.Errorf("invalid value for arg allow_no_nums, must be a bool")}, - {s: "semver(allow_no_nums=false)", expectedFilter: NewLooseSemVerVersionFilter(false)}, - {s: "semver(allow_no_nums=true)", expectedFilter: NewLooseSemVerVersionFilter(true)}, - {s: "prefix('')", expectedFilter: NewPrefixVersionFilterMust("", NewLooseSemVerVersionFilter(false))}, - {s: "prefix('a')", expectedFilter: NewPrefixVersionFilterMust("a", NewLooseSemVerVersionFilter(false))}, - {s: "prefix('a', 'b')", expectedErr: fmt.Errorf("unexpected argument type for , expected -2, got -6")}, - {s: "prefix('a', regex('a*'))", expectedFilter: NewPrefixVersionFilterMust("a", NewRegexVersionFilterMust("a*"))}, - {s: "prefix('a', suffi=regex('a*'))", expectedErr: fmt.Errorf("unkown arg suffi")}, - {s: "prefix('a', suffix=regex('a*'))", expectedFilter: NewPrefixVersionFilterMust("a", NewRegexVersionFilterMust("a*"))}, - {s: "number()", expectedFilter: NewNumberVersionFilter()}, - {s: "number(a=1)", expectedErr: fmt.Errorf("unexpected token -2, expected (")}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.s, func(t *testing.T) { - f, err := ParseLatestVersion(tc.s) - assert.Equal(t, tc.expectedFilter, f) - if tc.expectedErr != nil { - assert.Error(t, err, tc.expectedErr) - } else if err != nil { - assert.Fail(t, "unexpected error", err) - } - }) - } -} diff --git a/pkg/utils/versions/looseversion.go b/pkg/utils/versions/looseversion.go deleted file mode 100644 index 0c6bbf529..000000000 --- a/pkg/utils/versions/looseversion.go +++ /dev/null @@ -1,171 +0,0 @@ -package versions - -import ( - "github.com/kluctl/kluctl/v2/pkg/utils" - "regexp" - "sort" - "strconv" - "strings" -) - -var looseSemverRegex = regexp.MustCompile(`^v?(([0-9]+)(\.[0-9]+)*)?(.*)$`) -var suffixComponentRegex = regexp.MustCompile(`\d+|[a-zA-Z]+|\.`) - -// Allows to compare ints and strings. Strings are always considered less than ints. -type LooseVersionSuffixElement struct { - v interface{} -} - -func (x LooseVersionSuffixElement) Less(b LooseVersionSuffixElement) bool { - ia, iaOk := x.v.(int64) - ib, ibOk := b.v.(int64) - sa, _ := x.v.(string) - sb, _ := b.v.(string) - if ibOk == iaOk { - if iaOk { - return ia < ib - } else { - return sa < sb - } - } - if iaOk { - return false - } - return true -} - -type LooseVersion string - -func (lv LooseVersion) SplitVersion() ([]int, string) { - m := looseSemverRegex.FindStringSubmatch(string(lv)) - numsStr := m[1] - suffix := m[4] - - var nums []int - for _, x := range strings.Split(numsStr, ".") { - i, _ := strconv.ParseInt(x, 10, 32) - nums = append(nums, int(i)) - } - return nums, suffix -} - -func regexSplitLikePython(s string, re *regexp.Regexp) []string { - ret := []string{s} - indexes := re.FindAllStringIndex(s, -1) - start := 0 - for _, se := range indexes { - m := s[start:se[0]] - if len(m) != 0 { - ret = append(ret, m) - } - m = s[se[0]:se[1]] - if len(m) != 0 { - ret = append(ret, m) - } - start = se[1] - } - if start < len(s) { - ret = append(ret, s[start:]) - } - return ret -} - -func splitSuffix(suffix string) []LooseVersionSuffixElement { - var components []LooseVersionSuffixElement - for i, x := range regexSplitLikePython(suffix, suffixComponentRegex) { - if i == 0 { - continue - } - if x != "." { - y, err := strconv.ParseInt(x, 10, 32) - if err == nil { - components = append(components, LooseVersionSuffixElement{v: y}) - } else { - components = append(components, LooseVersionSuffixElement{v: x}) - } - } - } - return components -} - -func (lv LooseVersion) Less(b LooseVersion, preferLongSuffix bool) bool { - aNums, aSuffixStr := lv.SplitVersion() - bNums, bSuffixStr := b.SplitVersion() - - cmp := func(a []int, b []int) bool { - l := utils.IntMin(len(a), len(b)) - for i := 0; i < l; i++ { - if a[i] < b[i] { - return true - } - if b[i] < a[i] { - return false - } - } - if len(a) < len(b) { - return true - } - return false - } - - if cmp(aNums, bNums) { - return true - } - if cmp(bNums, aNums) { - return false - } - if len(aSuffixStr) == 0 && len(bSuffixStr) != 0 { - return false - } else if len(aSuffixStr) != 0 && len(bSuffixStr) == 0 { - return true - } - - aSuffix := splitSuffix(aSuffixStr) - bSuffix := splitSuffix(bSuffixStr) - l := utils.IntMin(len(aSuffix), len(bSuffix)) - - for i := 0; i < l; i++ { - if aSuffix[i].Less(bSuffix[i]) { - return true - } else if bSuffix[i].Less(aSuffix[i]) { - return false - } - } - - if preferLongSuffix { - if len(aSuffix) < len(bSuffix) { - return true - } - } else { - if len(bSuffix) < len(aSuffix) { - return true - } - } - return false -} - -func (lv LooseVersion) Compare(b LooseVersion) int { - if lv.Less(b, true) { - return -1 - } else if b.Less(lv, true) { - return 1 - } - return 0 -} - -type LooseVersionSlice []LooseVersion - -func (x LooseVersionSlice) Less(i, j int) bool { - return x[i].Less(x[j], true) -} -func (x LooseVersionSlice) Len() int { return len(x) } -func (x LooseVersionSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] } - -func SortLooseVersionStrings(versions []string) LooseVersionSlice { - var c LooseVersionSlice - for _, v := range versions { - c = append(c, LooseVersion(v)) - } - sort.Stable(c) - return c -} diff --git a/pkg/utils/versions/looseversion_test.go b/pkg/utils/versions/looseversion_test.go deleted file mode 100644 index b25520f8d..000000000 --- a/pkg/utils/versions/looseversion_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package versions - -import ( - "testing" -) - -func checkEqual(t *testing.T, a_ string, b_ string) { - a := LooseVersion(a_) - b := LooseVersion(b_) - if a.Less(b, true) { - t.Errorf("%s < %s should be false", a, b) - } - if b.Less(a, true) { - t.Errorf("%s > %s should be false", b, a) - } - if a.Compare(b) != 0 { - t.Errorf("%s == %s should be true", a, b) - } -} - -func checkLess(t *testing.T, a_ string, b_ string) { - a := LooseVersion(a_) - b := LooseVersion(b_) - if !a.Less(b, true) { - t.Errorf("%s < %s should be true", a, b) - } - if b.Less(a, true) { - t.Errorf("%s < %s should be false", b, a) - } - if a.Compare(b) != -1 { - t.Errorf("%s.Compare(%s) should be -1", a, b) - } - if b.Compare(a) != 1 { - t.Errorf("%s.Compare(%s) should be 1", b, a) - } -} - -func TestEquality(t *testing.T) { - checkEqual(t, "1", "1") - checkEqual(t, "1.1", "1.1") - checkEqual(t, "1.1a", "1.1a") - checkEqual(t, "1.1-a", "1.1-a") - checkEqual(t, "1.a-1", "1.a-1") -} - -func TestLess(t *testing.T) { - checkLess(t, "1", "2") - checkLess(t, "1", "1.1") - checkLess(t, "1.1a", "1.1") - checkLess(t, "1.1-a", "1.1") - checkLess(t, "1.1a", "1.1b") - checkLess(t, "1.1-a", "1.1-b") - checkLess(t, "1.a-1", "1.a-2") -} - -func TestMavenVersions(t *testing.T) { - checkLess(t, "1.1-SNAPSHOT", "1.1") - checkLess(t, "1", "1.1") - checkLess(t, "1-SNAPSHOT", "1.1") - checkLess(t, "1.1", "1.1.1-SNAPSHOT") - checkLess(t, "1.1-SNAPSHOT", "1.1.1-SNAPSHOT") -} - -func TestSuffixes(t *testing.T) { - checkLess(t, "1.1-1", "1.1-2") - checkLess(t, "1.1-suffix-1", "1.1-suffix-2") - checkLess(t, "1.1-suffix-2", "1.1-suffix-10") - checkLess(t, "1.1-suffix-2", "1.1-suffiy-1") - checkLess(t, "1.1-1-1", "1.1-2-1") - checkLess(t, "1.1-2-1", "1.1-2-2") - checkLess(t, "1.1-2-1", "1.1-100-2") - checkLess(t, "1.1-2-1", "1.1") - checkLess(t, "1.1-2", "1.1-2-1") - checkLess(t, "1.1-a-1", "1.1-1-1") -} - -func TestLooseVersionNoNums(t *testing.T) { - checkLess(t, "-snapshot1", "-snapshot2") - checkLess(t, "-1", "-2") - checkLess(t, "-1.1", "-1.2") -} - -func TestLooseVersionVPrefix(t *testing.T) { - checkLess(t, "v1.0", "v1.1") - checkLess(t, "v2.0", "v2.1") - checkLess(t, "v1.0", "v2.0") - checkLess(t, "1.0", "v2.0") - checkLess(t, "v1.0suffix", "v1.0") - checkLess(t, "v1.0-suffix", "v1.0") - checkLess(t, "v1.0suffix1", "v1.0suffix2") - checkLess(t, "v1.0-suffix1", "v1.0-suffix2") -} From 6f015cc99b5083ae38899b2762759e4d7d5ccb6a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 13 Feb 2023 10:37:14 +0100 Subject: [PATCH 0794/2268] feat: Remove deprecated support of args in deployment.yaml --- e2e/args_test.go | 23 +++++------------ pkg/deployment/deployment_project.go | 3 --- pkg/deployment/external_args.go | 37 ---------------------------- pkg/kluctl_project/target_context.go | 9 ------- pkg/types/deployment.go | 1 - 5 files changed, 6 insertions(+), 67 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index d754bf3f6..5f689f624 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -func testArgs(t *testing.T, deprecated bool) { +func testArgs(t *testing.T) { t.Parallel() k := defaultCluster1 @@ -36,17 +36,10 @@ func testArgs(t *testing.T, deprecated bool) { }, } - if deprecated { - p.UpdateDeploymentYaml(".", func(o *uo.UnstructuredObject) error { - _ = o.SetNestedField(args, "args") - return nil - }) - } else { - p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { - _ = o.SetNestedField(args, "args") - return nil - }) - } + p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(args, "args") + return nil + }) addConfigMapDeployment(p, "cm", map[string]string{ "a": `{{ args.a | default("na") }}`, @@ -119,12 +112,8 @@ d: assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "d4"}}`, "data", "d") } -func TestDeprecatedArgs(t *testing.T) { - testArgs(t, true) -} - func TestArgs(t *testing.T) { - testArgs(t, false) + testArgs(t) } func TestArgsFromEnv(t *testing.T) { diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 3431c0a74..8264170d2 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -136,9 +136,6 @@ func (p *DeploymentProject) processConfig() error { return err } - if len(p.Config.Args) != 0 && p.parentProject != nil { - return fmt.Errorf("only the root deployment.yml can define args") - } return nil } diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index c7f0aefcc..e041ebdfb 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -1,15 +1,11 @@ package deployment import ( - "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" "os" - "path/filepath" "regexp" "strings" ) @@ -58,39 +54,6 @@ func ConvertArgsToVars(args map[string]string, allowLoadFromFiles bool) (*uo.Uns return vars, nil } -func LoadDeprecatedDeploymentArgs(ctx context.Context, dir string, varsCtx *vars.VarsCtx, deployArgs *uo.UnstructuredObject) (bool, error) { - // First try to load the config without templating to avoid getting errors while rendering because required - // args were not set. Otherwise we won't be able to iterator through the 'args' array in the deployment.yml - // when the rendering error is actually args related. - - if !yaml.Exists(filepath.Join(dir, "deployment.yml")) { - return false, nil - } - - var conf types.DeploymentProjectConfig - - err := yaml.ReadYamlFile(yaml.FixPathExt(filepath.Join(dir, "deployment.yml")), &conf) - if err != nil { - // If that failed, it might be that conditional jinja blocks are present in the config, so lets try loading - // the config in rendered form. If it fails due to missing args now, we can't help much with better error - // messages anymore. - varsCtx2 := varsCtx.Copy() - varsCtx2.UpdateChild("args", deployArgs) - err = varsCtx2.RenderYamlFile(yaml.FixNameExt(dir, "deployment.yml"), []string{dir}, &conf) - if err != nil { - return false, err - } - } - - if len(conf.Args) == 0 { - return false, nil - } - - status.Deprecation(ctx, "deployment-args", "'args' in deployment.yaml is deprecated, please use 'args' from .kluctl.yaml instead.") - - return true, LoadDefaultArgs(conf.Args, deployArgs) -} - func LoadDefaultArgs(args []*types.DeploymentArg, deployArgs *uo.UnstructuredObject) error { // load defaults defaults := uo.New() diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 4985784be..15df07449 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -202,15 +202,6 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, forSeal bool) (*va return nil, err } - deprecatedArgs, err := deployment.LoadDeprecatedDeploymentArgs(p.ctx, p.ProjectDir, varsCtx, allArgs) - if err != nil { - return nil, err - } - - if deprecatedArgs && len(p.Config.Args) != 0 { - return nil, fmt.Errorf("mixing deprecated 'args' from deployment.yaml and .kluctl.yaml is not allowed") - } - varsCtx.UpdateChild("args", allArgs) return varsCtx, nil diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index f792971bd..df656745c 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -85,7 +85,6 @@ type IgnoreForDiffItemConfig struct { } type DeploymentProjectConfig struct { - Args []*DeploymentArg `yaml:"args,omitempty"` Vars []*VarsSource `yaml:"vars,omitempty"` SealedSecrets *SealedSecretsConfig `yaml:"sealedSecrets,omitempty"` From 947c4e60e9478454e20c02662746871678b68f42 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 13 Feb 2023 10:47:06 +0100 Subject: [PATCH 0795/2268] feat: Remove deprecated helm chart location support --- cmd/kluctl/commands/cmd_helm_pull.go | 17 ----------------- cmd/kluctl/commands/cmd_helm_update.go | 12 ------------ pkg/helm/helm_release.go | 11 ----------- 3 files changed, 40 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_pull.go b/cmd/kluctl/commands/cmd_helm_pull.go index 7bfc1f42a..f13160c75 100644 --- a/cmd/kluctl/commands/cmd_helm_pull.go +++ b/cmd/kluctl/commands/cmd_helm_pull.go @@ -47,23 +47,6 @@ func doHelmPull(ctx context.Context, projectDir string, helmCredentials *args.He return actions, err } - for _, hr := range releases { - if utils.Exists(hr.GetDeprecatedChartDir()) { - actions++ - rel, err := filepath.Rel(projectDir, hr.GetDeprecatedChartDir()) - if err != nil { - return actions, err - } - if !dryRun { - status.Info(ctx, "Removing deprecated charts dir %s", rel) - err = os.RemoveAll(hr.GetDeprecatedChartDir()) - if err != nil { - return actions, err - } - } - } - } - g := utils.NewGoHelper(ctx, 8) for _, chart := range charts { diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index d079ec1b8..82ce4c2f8 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -78,18 +78,6 @@ func (cmd *helmUpdateCmd) Run(ctx context.Context) error { return err } - for _, hr := range releases { - if utils.Exists(hr.GetDeprecatedChartDir()) { - relDir, err := filepath.Rel(projectDir, filepath.Dir(hr.ConfigFile)) - if err != nil { - return err - } - status.Error(ctx, "%s: Project is using a pre-pulled Helm Chart that is next to the helm-chart.yaml, which is deprecated. "+ - "Updating is only possible after removing these. Use 'kluctl helm-pull' to remove all deprecated chart folders.", relDir) - return fmt.Errorf("detected deprecated chart folder") - } - } - if cmd.Commit { actions, err := doHelmPull(ctx, projectDir, &cmd.HelmCredentials, true, false) if err != nil { diff --git a/pkg/helm/helm_release.go b/pkg/helm/helm_release.go index 5b21b2215..26fc8ee08 100644 --- a/pkg/helm/helm_release.go +++ b/pkg/helm/helm_release.go @@ -107,11 +107,6 @@ func (hr *Release) Render(ctx context.Context, k *k8s.K8sCluster, k8sVersion str return nil } -func (hr *Release) GetDeprecatedChartDir() string { - dir := filepath.Dir(hr.ConfigFile) - return filepath.Join(dir, "charts", hr.Chart.GetChartName()) -} - func (hr *Release) getPulledChart(ctx context.Context) (*PulledChart, error) { if hr.Chart.IsLocalChart() { version, err := hr.Chart.GetLocalChartVersion() @@ -121,12 +116,6 @@ func (hr *Release) getPulledChart(ctx context.Context) (*PulledChart, error) { return NewPulledChart(hr.Chart, version, hr.Chart.GetLocalPath(), false), nil } - deprecatedPC := NewPulledChart(hr.Chart, hr.Config.ChartVersion, hr.GetDeprecatedChartDir(), false) - if deprecatedPC.CheckExists() { - status.Deprecation(ctx, "helm-charts-dir", "Your project has pre-pulled charts located next to the helm-chart.yaml, which is deprecated. "+ - "Please run 'kluctl helm-pull' on your project and ensure that the deprecated charts are removed! Future versions of kluctl will ignore these locations.") - return deprecatedPC, nil - } pc, err := hr.Chart.GetPulledChart(hr.baseChartsDir, hr.Config.ChartVersion) if err != nil { return nil, err From 7eb4028b724df7de78aa1003dbb28d66cd3a6da2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 13 Feb 2023 11:51:55 +0100 Subject: [PATCH 0796/2268] refactor: Remove unused dynamicTargetInfo struct --- pkg/kluctl_project/targets.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index 5a7ceb06d..cdc3b6d84 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -7,15 +7,6 @@ import ( "sort" ) -type dynamicTargetInfo struct { - baseTarget *types.Target - dir string - gitProject *types.GitProject - ref *string - refPattern *string - defaultBranch string -} - func (c *LoadedKluctlProject) loadTargets() error { status.Trace(c.ctx, "Loading targets") defer status.Trace(c.ctx, "Done loading targets") From 2523d39bc97045752e78d8b2924bd28ec5e30388 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 14:09:21 +0100 Subject: [PATCH 0797/2268] chore(deps): Bump golang.org/x/net from 0.6.0 to 0.7.0 (#317) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.6.0...v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index eeb038245..af0c71f63 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.6.0 - golang.org/x/net v0.6.0 + golang.org/x/net v0.7.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.5.0 golang.org/x/term v0.5.0 diff --git a/go.sum b/go.sum index a529767df..10888f833 100644 --- a/go.sum +++ b/go.sum @@ -920,8 +920,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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= From 31d87eb8dc364af9b10328b907973888526f2fc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 08:35:01 +0100 Subject: [PATCH 0798/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/service/secretsmanager (#318) Bumps [github.com/aws/aws-sdk-go-v2/service/secretsmanager](https://github.com/aws/aws-sdk-go-v2) from 1.18.3 to 1.18.4. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.3...config/v1.18.4) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/secretsmanager dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index af0c71f63..fa7f3bed3 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.17.4 github.com/aws/aws-sdk-go-v2/config v1.18.12 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4 github.com/go-git/go-git/v5 v5.5.2 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index 10888f833..5a8ed71b4 100644 --- a/go.sum +++ b/go.sum @@ -132,8 +132,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFu github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3 h1:Zod/h9QcDvbrrG3jjTUp4lctRb6Qg2nj7ARC/xMsUc4= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3/go.mod h1:hqPcyOuLU6yWIbLy3qMnQnmidgKuIEwqIlW6+chYnog= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4 h1:0P9VF9miVGT40WSZSuMzHwkwTVIltpDrTrvswMLjbx0= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4/go.mod h1:hqPcyOuLU6yWIbLy3qMnQnmidgKuIEwqIlW6+chYnog= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 h1:lQKN/LNa3qqu2cDOQZybP7oL4nMGGiFqob0jZJaR8/4= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 h1:0bLhH6DRAqox+g0LatcjGKjjhU6Eudyys6HB6DJVPj8= From aeb437a46f89a8d268cb299c0dd15231801b64ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 08:35:27 +0100 Subject: [PATCH 0799/2268] chore(deps): Bump github.com/bitnami-labs/sealed-secrets (#319) Bumps [github.com/bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) from 0.19.4 to 0.19.5. - [Release notes](https://github.com/bitnami-labs/sealed-secrets/releases) - [Changelog](https://github.com/bitnami-labs/sealed-secrets/blob/main/RELEASE-NOTES.md) - [Commits](https://github.com/bitnami-labs/sealed-secrets/compare/v0.19.4...v0.19.5) --- updated-dependencies: - dependency-name: github.com/bitnami-labs/sealed-secrets dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index fa7f3bed3..860ee3b02 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/Masterminds/semver/v3 v3.2.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/bitnami-labs/sealed-secrets v0.19.4 + github.com/bitnami-labs/sealed-secrets v0.19.5 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible // See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility @@ -225,7 +225,7 @@ require ( golang.org/x/mod v0.7.0 // indirect golang.org/x/oauth2 v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.4.0 // indirect + golang.org/x/tools v0.5.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/api v0.107.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 5a8ed71b4..833bc336c 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.19.4 h1:TUad0o/mNp2D53lHMGzjdsdrx+yrHy/fyuEIAbFqj+U= -github.com/bitnami-labs/sealed-secrets v0.19.4/go.mod h1:rXBdWOdAPbuowgFphH8bcCB3jSVlYXg9mMWRcYY49dc= +github.com/bitnami-labs/sealed-secrets v0.19.5 h1:Llrs8bm5MdJEoPIQo0xZOHu/2i+Ry8N5bQFpc48UZYc= +github.com/bitnami-labs/sealed-secrets v0.19.5/go.mod h1:IC5f2r0c8mxjx8nHs+du+gBso2Wsbdb2lcTwVmOOu2Y= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -627,7 +627,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/ohler55/ojg v1.17.4 h1:6Ss87DyAZHU0ODZu6Cmuahj5UiVaRD1n8C4KNm0qMYg= github.com/ohler55/ojg v1.17.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow= +github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI= github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -1099,8 +1099,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= +golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= 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= From a96783669ed32362491757ebbb247bbce0ae4faa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 08:35:50 +0100 Subject: [PATCH 0800/2268] chore(deps): Bump github.com/fluxcd/pkg/kustomize from 0.13.0 to 0.13.1 (#320) Bumps [github.com/fluxcd/pkg/kustomize](https://github.com/fluxcd/pkg) from 0.13.0 to 0.13.1. - [Release notes](https://github.com/fluxcd/pkg/releases) - [Commits](https://github.com/fluxcd/pkg/compare/oci/v0.13.0...runtime/v0.13.1) --- updated-dependencies: - dependency-name: github.com/fluxcd/pkg/kustomize dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 20 ++++++++------------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 860ee3b02..45d3955b7 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/docker/distribution v2.8.1+incompatible // See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df - github.com/fluxcd/pkg/kustomize v0.13.0 + github.com/fluxcd/pkg/kustomize v0.13.1 github.com/go-playground/validator/v10 v10.11.2 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 @@ -133,8 +133,8 @@ require ( github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect @@ -240,7 +240,7 @@ require ( k8s.io/apiserver v0.26.1 // indirect k8s.io/cli-runtime v0.26.0 // indirect k8s.io/component-base v0.26.1 // indirect - k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715 // indirect + k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 // indirect k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect oras.land/oras-go v1.2.2 // indirect diff --git a/go.sum b/go.sum index 833bc336c..e49b58b96 100644 --- a/go.sum +++ b/go.sum @@ -248,8 +248,8 @@ github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df h1:2BHXJp1PwX7D47 github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df/go.mod h1:raWgfUV7lDQVXp4QXUaeNNJkRVKz97UQuF+0kdY7Vmo= github.com/fluxcd/pkg/apis/kustomize v0.8.0 h1:A6aLolxPV2Sll44SOHiX96lbXXmRZmS5BoEerkRHrfM= github.com/fluxcd/pkg/apis/kustomize v0.8.0/go.mod h1:9DPEVSfVIkiC2H3Dk6Ght4YJkswhYIaufXla4tB5Y84= -github.com/fluxcd/pkg/kustomize v0.13.0 h1:oC50lpGdz/04aH4dPS/kRBjo+7PUx7BgGsJtSS0CmmE= -github.com/fluxcd/pkg/kustomize v0.13.0/go.mod h1:6vAmxEe/41jBEspGq4OZA/4WlnszPyavm74TGSEVpXg= +github.com/fluxcd/pkg/kustomize v0.13.1 h1:xfDghn/kRaa5vYN64dLTAL1b1B1tDwcXlnOAqmz5W28= +github.com/fluxcd/pkg/kustomize v0.13.1/go.mod h1:W+Nm9P8yUhTb8n3hpvceUnCAjl6DFsU0k5yI+HT2NE8= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -283,12 +283,10 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV 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/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -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/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -544,8 +542,6 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9 github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 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.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= @@ -1281,8 +1277,8 @@ k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= k8s.io/klog/v2 v2.90.0 h1:VkTxIV/FjRXn1fgNNcKGM8cfmL1Z33ZjXRTVxKCoF5M= k8s.io/klog/v2 v2.90.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715 h1:tBEbstoM+K0FiBV5KGAKQ0kuvf54v/hwpldiJt69w1s= -k8s.io/kube-openapi v0.0.0-20221207184640-f3cff1453715/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 h1:8cNCQs+WqqnSpZ7y0LMQPKD+RZUHU17VqLPMW3qxnxc= +k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596/go.mod h1:/BYxry62FuDzmI+i9B+X2pqfySRmSOW2ARmj5Zbqhj0= k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= From 1d1dc496bace5fda7d51e86310d9ff9cfaca333a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 16 Feb 2023 08:36:44 +0100 Subject: [PATCH 0801/2268] feat: Remove dynamicArgs from target spec --- pkg/types/kluctl_project.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 840542dd6..e00908085 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -4,10 +4,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) -type DynamicArg struct { - Name string `yaml:"name" validate:"required"` -} - type SealingConfig struct { Args *uo.UnstructuredObject `yaml:"args,omitempty"` SecretSets []string `yaml:"secretSets,omitempty"` @@ -18,7 +14,6 @@ type Target struct { Name string `yaml:"name" validate:"required"` Context *string `yaml:"context,omitempty"` Args *uo.UnstructuredObject `yaml:"args,omitempty"` - DynamicArgs []DynamicArg `yaml:"dynamicArgs,omitempty"` SealingConfig *SealingConfig `yaml:"sealingConfig,omitempty"` Images []FixedImage `yaml:"images,omitempty"` Discriminator string `yaml:"discriminator,omitempty"` From af17374463caf2eafcc91e43bef8230ccb0481ad Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 16 Feb 2023 08:37:02 +0100 Subject: [PATCH 0802/2268] fix: Fix warning message to not talk about dynamic targets --- pkg/kluctl_project/targets.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index cdc3b6d84..a9a8b714d 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -17,7 +17,7 @@ func (c *LoadedKluctlProject) loadTargets() error { for _, configTarget := range c.Config.Targets { target, err := c.buildTarget(configTarget) if err != nil { - status.Warning(c.ctx, "Failed to load dynamic target config for project: %v", err) + status.Warning(c.ctx, "Failed to load target config for project: %v", err) continue } From 3f60190c98392ff96b1fdf4fd841fc207ddd0c31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 09:07:27 +0100 Subject: [PATCH 0803/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/config (#321) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.12 to 1.18.13. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.12...config/v1.18.13) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 45d3955b7..e53d1ad16 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,7 @@ require ( require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.17.4 - github.com/aws/aws-sdk-go-v2/config v1.18.12 + github.com/aws/aws-sdk-go-v2/config v1.18.13 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4 github.com/go-git/go-git/v5 v5.5.2 github.com/go-logr/logr v1.2.3 @@ -94,15 +94,15 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.13 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.2 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/go.sum b/go.sum index e49b58b96..f411a6e42 100644 --- a/go.sum +++ b/go.sum @@ -114,10 +114,10 @@ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:W github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.12 h1:fKs/I4wccmfrNRO9rdrbMO1NgLxct6H9rNMiPdBxHWw= -github.com/aws/aws-sdk-go-v2/config v1.18.12/go.mod h1:J36fOhj1LQBr+O4hJCiT8FwVvieeoSGOtPuvhKlsNu8= -github.com/aws/aws-sdk-go-v2/credentials v1.13.12 h1:Cb+HhuEnV19zHRaYYVglwvdHGMJWbdsyP4oHhw04xws= -github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtbM2J48ra2+Ltu+tmwr/jO0KA= +github.com/aws/aws-sdk-go-v2/config v1.18.13 h1:v0xlYqbO6/EVlM8tUn2QEOA7btQxcgidEq2JRDBPTho= +github.com/aws/aws-sdk-go-v2/config v1.18.13/go.mod h1:r39wGSZB7wPDW1i54JyQXUpc5KsWjh5z/3S5D9eCqDg= +github.com/aws/aws-sdk-go-v2/credentials v1.13.13 h1:zw1KAc1kl00NYd3ofVmFrb09qnYlSQMeh+fmlQRAihI= +github.com/aws/aws-sdk-go-v2/credentials v1.13.13/go.mod h1:DW9nbIIF9MrIja0cBQrUpeWYQMSlNmP8fevLUyF9W38= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaYh+BRMbvrrmZwIQ5jWqCZQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= @@ -134,10 +134,10 @@ github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3f github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4 h1:0P9VF9miVGT40WSZSuMzHwkwTVIltpDrTrvswMLjbx0= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4/go.mod h1:hqPcyOuLU6yWIbLy3qMnQnmidgKuIEwqIlW6+chYnog= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 h1:lQKN/LNa3qqu2cDOQZybP7oL4nMGGiFqob0jZJaR8/4= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 h1:0bLhH6DRAqox+g0LatcjGKjjhU6Eudyys6HB6DJVPj8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.2 h1:EN102fWY7hI5u/2FPheTrwwMHkSXfl49RYkeEnJsrCU= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.2/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.2 h1:f1lmlce7r13CX1BPyPqt9oh/H+uqOWc9367lDoGGwNQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.2/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k= github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 h1:s49mSnsBZEXjfGBkRfmK+nPqzT7Lt3+t2SmAKNyHblw= github.com/aws/aws-sdk-go-v2/service/sts v1.18.3/go.mod h1:b+psTJn33Q4qGoDaM7ZiOVVG8uVjGI6HaZ8WBHdgDgU= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From b1a63589e639211c8b15b02a91c72ba61fd41ed7 Mon Sep 17 00:00:00 2001 From: Jochen Saalfeld Date: Thu, 16 Feb 2023 09:08:49 +0100 Subject: [PATCH 0804/2268] fix typo in .kluctl.yaml example reference for dev env (#322) --- docs/reference/kluctl-project/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index c42783829..3eae53d7f 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -24,7 +24,7 @@ discriminator: "my-project-{{ target.name }}" targets: # test cluster, dev env - name: dev - context: test.example.com + context: dev.example.com args: environment_name: dev # test cluster, test env From 5b41ff888cff84fc1821b03af2d7d2d4fc4f9bef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Feb 2023 09:03:24 +0100 Subject: [PATCH 0805/2268] chore(deps): Bump github.com/onsi/gomega from 1.26.0 to 1.27.0 (#323) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.26.0 to 1.27.0. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.26.0...v1.27.0) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index e53d1ad16..a47d9e838 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.26.0 + github.com/onsi/gomega v1.27.0 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.4 diff --git a/go.sum b/go.sum index f411a6e42..70544ce08 100644 --- a/go.sum +++ b/go.sum @@ -623,9 +623,9 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/ohler55/ojg v1.17.4 h1:6Ss87DyAZHU0ODZu6Cmuahj5UiVaRD1n8C4KNm0qMYg= github.com/ohler55/ojg v1.17.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI= -github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= -github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU= +github.com/onsi/gomega v1.27.0 h1:QLidEla4bXUuZVFa4KX6JHCsuGgbi85LC/pCHrt/O08= +github.com/onsi/gomega v1.27.0/go.mod h1:i189pavgK95OSIipFBa74gC2V4qrQuvjuyGEr3GmbXA= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= From 899bb32cece2cdcff8b7434fe973a8c3d9a3b617 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 08:42:36 +0100 Subject: [PATCH 0806/2268] chore(deps): Bump github.com/onsi/gomega from 1.27.0 to 1.27.1 (#327) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.0 to 1.27.1. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.27.0...v1.27.1) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a47d9e838..3c7f6235c 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.27.0 + github.com/onsi/gomega v1.27.1 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.4 diff --git a/go.sum b/go.sum index 70544ce08..4ed1c2dd8 100644 --- a/go.sum +++ b/go.sum @@ -624,8 +624,8 @@ github.com/ohler55/ojg v1.17.4 h1:6Ss87DyAZHU0ODZu6Cmuahj5UiVaRD1n8C4KNm0qMYg= github.com/ohler55/ojg v1.17.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU= -github.com/onsi/gomega v1.27.0 h1:QLidEla4bXUuZVFa4KX6JHCsuGgbi85LC/pCHrt/O08= -github.com/onsi/gomega v1.27.0/go.mod h1:i189pavgK95OSIipFBa74gC2V4qrQuvjuyGEr3GmbXA= +github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= +github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= From 8be6ec2c0913f3702ff090ea6dde9726dc635a98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 08:42:52 +0100 Subject: [PATCH 0807/2268] chore(deps): Bump github.com/ohler55/ojg from 1.17.4 to 1.17.5 (#329) Bumps [github.com/ohler55/ojg](https://github.com/ohler55/ojg) from 1.17.4 to 1.17.5. - [Release notes](https://github.com/ohler55/ojg/releases) - [Changelog](https://github.com/ohler55/ojg/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/ojg/compare/v1.17.4...v1.17.5) --- updated-dependencies: - dependency-name: github.com/ohler55/ojg dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3c7f6235c..db964bf66 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.17.4 + github.com/ohler55/ojg v1.17.5 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.9.0 diff --git a/go.sum b/go.sum index 4ed1c2dd8..f3ee51966 100644 --- a/go.sum +++ b/go.sum @@ -620,8 +620,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/ohler55/ojg v1.17.4 h1:6Ss87DyAZHU0ODZu6Cmuahj5UiVaRD1n8C4KNm0qMYg= -github.com/ohler55/ojg v1.17.4/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= +github.com/ohler55/ojg v1.17.5 h1:SY6/cdhVzsLinNFIBRNSWhJgihvEWco5Y0TJe46XJ1Y= +github.com/ohler55/ojg v1.17.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU= github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= From bebde018f5c0b2c3d525dfadde0d64472d1b5882 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 08:43:29 +0100 Subject: [PATCH 0808/2268] chore(deps): Bump github.com/golang-jwt/jwt/v4 from 4.4.3 to 4.5.0 (#331) Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.3 to 4.5.0. - [Release notes](https://github.com/golang-jwt/jwt/releases) - [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md) - [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.3...v4.5.0) --- updated-dependencies: - dependency-name: github.com/golang-jwt/jwt/v4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index db964bf66..5cf83491a 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/fluxcd/pkg/kustomize v0.13.1 github.com/go-playground/validator/v10 v10.11.2 github.com/gobwas/glob v0.2.3 // indirect - github.com/golang-jwt/jwt/v4 v4.4.3 + github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-containerregistry v0.13.0 github.com/hashicorp/vault/api v1.9.0 github.com/hexops/gotextdiff v1.0.3 diff --git a/go.sum b/go.sum index f3ee51966..c58e584e6 100644 --- a/go.sum +++ b/go.sum @@ -318,8 +318,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= -github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= From e2ac702205798fa90f5e35cfd2bc42fbd41e50b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 08:43:45 +0100 Subject: [PATCH 0809/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/config (#332) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.13 to 1.18.15. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.13...config/v1.18.15) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 22 +++++++++++----------- go.sum | 41 ++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 5cf83491a..d64f89087 100644 --- a/go.mod +++ b/go.mod @@ -58,8 +58,8 @@ require ( require ( filippo.io/age v1.1.1 - github.com/aws/aws-sdk-go-v2 v1.17.4 - github.com/aws/aws-sdk-go-v2/config v1.18.13 + github.com/aws/aws-sdk-go-v2 v1.17.5 + github.com/aws/aws-sdk-go-v2/config v1.18.15 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4 github.com/go-git/go-git/v5 v5.5.2 github.com/go-logr/logr v1.2.3 @@ -94,16 +94,16 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.13 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.15 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.2 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.18.5 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index c58e584e6..bef14ad07 100644 --- a/go.sum +++ b/go.sum @@ -112,34 +112,37 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.13 h1:v0xlYqbO6/EVlM8tUn2QEOA7btQxcgidEq2JRDBPTho= -github.com/aws/aws-sdk-go-v2/config v1.18.13/go.mod h1:r39wGSZB7wPDW1i54JyQXUpc5KsWjh5z/3S5D9eCqDg= -github.com/aws/aws-sdk-go-v2/credentials v1.13.13 h1:zw1KAc1kl00NYd3ofVmFrb09qnYlSQMeh+fmlQRAihI= -github.com/aws/aws-sdk-go-v2/credentials v1.13.13/go.mod h1:DW9nbIIF9MrIja0cBQrUpeWYQMSlNmP8fevLUyF9W38= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaYh+BRMbvrrmZwIQ5jWqCZQ= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU= +github.com/aws/aws-sdk-go-v2 v1.17.5 h1:TzCUW1Nq4H8Xscph5M/skINUitxM5UBAyvm2s7XBzL4= +github.com/aws/aws-sdk-go-v2 v1.17.5/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.15 h1:509yMO0pJUGUugBP2H9FOFyV+7Mz7sRR+snfDN5W4NY= +github.com/aws/aws-sdk-go-v2/config v1.18.15/go.mod h1:vS0tddZqpE8cD9CyW0/kITHF5Bq2QasW9Y1DFHD//O0= +github.com/aws/aws-sdk-go-v2/credentials v1.13.15 h1:0rZQIi6deJFjOEgHI9HI2eZcLPPEGQPictX66oRFLL8= +github.com/aws/aws-sdk-go-v2/credentials v1.13.15/go.mod h1:vRMLMD3/rXU+o6j2MW5YefrGMBmdTvkLLGqFwMLBHQc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 h1:Kbiv9PGnQfG/imNI4L/heyUXvzKmcWSBeDvkrQz5pFc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23/go.mod h1:mOtmAg65GT1HIL/HT/PynwPbS+UG0BgCZ6vhkPqnxWo= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 h1:9/aKwwus0TQxppPXFmf010DFrE+ssSbzroLVYINA+xE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29/go.mod h1:Dip3sIGv485+xerzVv24emnjX5Sg88utCL8fwGmCeWg= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFuMO22HkV5VWGLBvmCLBCLPivUAmpdpnp4Vs= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 h1:b/Vn141DBuLVgXbhRWIrl9g+ww7G+ScV5SzniWR13jQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23/go.mod h1:mr6c4cHC+S/MMkrjtSlG4QA36kOznDep+0fga5L/fGQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 h1:IVx9L7YFhpPq0tTnGo8u8TpluFu7nAn9X3sUDMb11c0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30/go.mod h1:vsbq62AOBwQ1LJ/GWKFxX8beUEYeRp/Agitrxee2/qM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 h1:QoOybhwRfciWUBbZ0gp9S7XaDnCuSTeK/fySB99V1ls= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23/go.mod h1:9uPh+Hrz2Vn6oMnQYiUi/zbh3ovbnQk19YKINkQny44= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4 h1:0P9VF9miVGT40WSZSuMzHwkwTVIltpDrTrvswMLjbx0= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4/go.mod h1:hqPcyOuLU6yWIbLy3qMnQnmidgKuIEwqIlW6+chYnog= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.2 h1:EN102fWY7hI5u/2FPheTrwwMHkSXfl49RYkeEnJsrCU= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.2/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.2 h1:f1lmlce7r13CX1BPyPqt9oh/H+uqOWc9367lDoGGwNQ= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.2/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 h1:s49mSnsBZEXjfGBkRfmK+nPqzT7Lt3+t2SmAKNyHblw= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.3/go.mod h1:b+psTJn33Q4qGoDaM7ZiOVVG8uVjGI6HaZ8WBHdgDgU= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 h1:qJdM48OOLl1FBSzI7ZrA1ZfLwOyCYqkXV5lko1hYDBw= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.4/go.mod h1:jtLIhd+V+lft6ktxpItycqHqiVXrPIRjWIsFIlzMriw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 h1:YRkWXQveFb0tFC0TLktmmhGsOcCgLwvq88MC2al47AA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4/go.mod h1:zVwRrfdSmbRZWkUkWjOItY7SOalnFnq/Yg2LVPqDjwc= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.5 h1:L1600eLr0YvTT7gNh3Ni24yGI7NSHkq9Gp62vijPRCs= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.5/go.mod h1:1mKZHLLpDMHTNSYPJ7qrcnCQdHCWsNQaT0xRvq2u80s= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From 328f5f3421b12b8c085a39658e2ad6bafc61a1a5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 27 Feb 2023 10:29:51 +0100 Subject: [PATCH 0810/2268] fix: Also add full resource strings to Helm's APIVersions (#334) Helm internally adds GroupVersion strings AND GroupVersionKind strings to APIVersions. We must replicate this behavior as otherwise things like `.Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress"` will not work. --- pkg/helm/helm_release.go | 35 ++++++++++++++++++++++++----------- pkg/k8s/resources.go | 2 +- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/pkg/helm/helm_release.go b/pkg/helm/helm_release.go index 26fc8ee08..7295a7f29 100644 --- a/pkg/helm/helm_release.go +++ b/pkg/helm/helm_release.go @@ -26,7 +26,6 @@ import ( "helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/release" - "k8s.io/apimachinery/pkg/runtime/schema" ) type Release struct { @@ -161,13 +160,6 @@ func (hr *Release) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion s } valuesPath := yaml.FixPathExt(filepath.Join(filepath.Dir(hr.ConfigFile), "helm-values.yml")) - var gvs []schema.GroupVersion - if k != nil { - gvs, err = k.Resources.GetAllGroupVersions() - if err != nil { - return err - } - } cfg, err := buildHelmConfig(k) if err != nil { return err @@ -211,15 +203,13 @@ func (hr *Release) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion s client.Replace = true client.ClientOnly = true client.KubeVersion = kubeVersion + client.APIVersions = hr.getApiVersions(k) if hr.Config.SkipCRDs { client.SkipCRDs = true } else { client.IncludeCRDs = true } - for _, gv := range gvs { - client.APIVersions = append(client.APIVersions, gv.String()) - } p := getter.All(settings) vals, err := valueOpts.MergeValues(p) @@ -291,6 +281,29 @@ func (hr *Release) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion s return nil } +func (hr *Release) getApiVersions(k *k8s.K8sCluster) chartutil.VersionSet { + if k == nil { + return nil + } + + m := map[string]bool{} + + gvks := k.Resources.GetFilteredGVKs(nil) + for _, gvk := range gvks { + gvStr := gvk.GroupVersion().String() + m[gvStr] = true + gvkStr := fmt.Sprintf("%s/%s", gvStr, gvk.Kind) + m[gvkStr] = true + } + + ret := make([]string, 0, len(m)) + for id, _ := range m { + ret = append(ret, id) + } + + return ret +} + func (hr *Release) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, error) { var parsed []*uo.UnstructuredObject diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 47ce75c2b..9a49b9093 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -310,7 +310,7 @@ func (k *k8sResources) GetFilteredGVKs(filter func(ar *v1.APIResource) bool) []s var ret []schema.GroupVersionKind for _, ar := range k.allResources { - if !filter(&ar) { + if filter != nil && !filter(&ar) { continue } gvk := schema.GroupVersionKind{ From 3ac06cea9e7f864a880f53b037dcf5e8b1f58710 Mon Sep 17 00:00:00 2001 From: Jochen Saalfeld Date: Mon, 27 Feb 2023 14:47:14 +0100 Subject: [PATCH 0811/2268] docs: change environment args to a working default case (#335) --- docs/reference/kluctl-project/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index 3eae53d7f..7c786c498 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -26,17 +26,17 @@ targets: - name: dev context: dev.example.com args: - environment_name: dev + environment: dev # test cluster, test env - name: test context: test.example.com args: - environment_name: test + environment: test # prod cluster, prod env - name: prod context: prod.example.com args: - environment_name: prod + environment: prod args: - name: environment From d16793f80fcf668d56467783d45153e82c2d82cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Feb 2023 08:46:26 +0100 Subject: [PATCH 0812/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/service/secretsmanager (#336) Bumps [github.com/aws/aws-sdk-go-v2/service/secretsmanager](https://github.com/aws/aws-sdk-go-v2) from 1.18.4 to 1.18.6. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.4...config/v1.18.6) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/secretsmanager dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index d64f89087..2e58406b4 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.17.5 github.com/aws/aws-sdk-go-v2/config v1.18.15 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6 github.com/go-git/go-git/v5 v5.5.2 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index bef14ad07..dff4afac9 100644 --- a/go.sum +++ b/go.sum @@ -112,7 +112,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.17.5 h1:TzCUW1Nq4H8Xscph5M/skINUitxM5UBAyvm2s7XBzL4= github.com/aws/aws-sdk-go-v2 v1.17.5/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.18.15 h1:509yMO0pJUGUugBP2H9FOFyV+7Mz7sRR+snfDN5W4NY= @@ -122,11 +121,9 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.15/go.mod h1:vRMLMD3/rXU+o6j2MW5Y github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 h1:Kbiv9PGnQfG/imNI4L/heyUXvzKmcWSBeDvkrQz5pFc= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23/go.mod h1:mOtmAg65GT1HIL/HT/PynwPbS+UG0BgCZ6vhkPqnxWo= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 h1:9/aKwwus0TQxppPXFmf010DFrE+ssSbzroLVYINA+xE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29/go.mod h1:Dip3sIGv485+xerzVv24emnjX5Sg88utCL8fwGmCeWg= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 h1:b/Vn141DBuLVgXbhRWIrl9g+ww7G+ScV5SzniWR13jQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23/go.mod h1:mr6c4cHC+S/MMkrjtSlG4QA36kOznDep+0fga5L/fGQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 h1:IVx9L7YFhpPq0tTnGo8u8TpluFu7nAn9X3sUDMb11c0= @@ -135,8 +132,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 h1:QoOybhwRf github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23/go.mod h1:9uPh+Hrz2Vn6oMnQYiUi/zbh3ovbnQk19YKINkQny44= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4 h1:0P9VF9miVGT40WSZSuMzHwkwTVIltpDrTrvswMLjbx0= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.4/go.mod h1:hqPcyOuLU6yWIbLy3qMnQnmidgKuIEwqIlW6+chYnog= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6 h1:VjvQw/1Qf/rhDSl+NNOeybSpdPRjBfH60//5vzveVsY= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6/go.mod h1:CJcdJtrO6ulXfI8l2DotKWmJShhXHCEcd9Wibyx3kC0= github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 h1:qJdM48OOLl1FBSzI7ZrA1ZfLwOyCYqkXV5lko1hYDBw= github.com/aws/aws-sdk-go-v2/service/sso v1.12.4/go.mod h1:jtLIhd+V+lft6ktxpItycqHqiVXrPIRjWIsFIlzMriw= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 h1:YRkWXQveFb0tFC0TLktmmhGsOcCgLwvq88MC2al47AA= From 572c96a12763ba367f722f76812300c19c631096 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Feb 2023 08:46:51 +0100 Subject: [PATCH 0813/2268] chore(deps): Bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#337) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 2e58406b4..683361d28 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.6.0 diff --git a/go.sum b/go.sum index dff4afac9..ca78af9e0 100644 --- a/go.sum +++ b/go.sum @@ -751,8 +751,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 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.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= From 37a61b1e0d90eddab494579ce009bc6ed3eaabd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Feb 2023 08:47:07 +0100 Subject: [PATCH 0814/2268] chore(deps): Bump actions/checkout from 2 to 3 (#338) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cross-compile.yaml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cross-compile.yaml b/.github/workflows/cross-compile.yaml index 556c864d7..6b96e9f56 100644 --- a/.github/workflows/cross-compile.yaml +++ b/.github/workflows/cross-compile.yaml @@ -11,7 +11,7 @@ jobs: if: github.event_name != 'pull_request' steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - uses: actions/setup-go@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e2a47b9a..b18a49db0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Fetch all tags diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 92f166bb7..5e3e4cc83 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - uses: actions/setup-go@v3 @@ -55,7 +55,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: go-version: '1.19' From fc02acdc89e041622fbd3f5d7d23a468ed9079f7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 28 Feb 2023 10:30:44 +0100 Subject: [PATCH 0815/2268] fix: Properly track new objects instead of guessing when diffing --- pkg/deployment/commands/deploy.go | 4 +-- pkg/deployment/commands/diff.go | 2 +- pkg/deployment/commands/poke_images.go | 2 +- pkg/deployment/utils/apply_utils.go | 38 ++++++++++++++++---------- pkg/deployment/utils/diff_utils.go | 12 ++------ 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index cb3721b41..a349fd103 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -65,7 +65,7 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) diffResult := &types.CommandResult{ - NewObjects: du.NewObjects, + NewObjects: au.GetNewObjects(), ChangedObjects: du.ChangedObjects, DeletedObjects: au.GetDeletedObjects(), HookObjects: au.GetAppliedHookObjects(), @@ -99,7 +99,7 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult return nil, err } return &types.CommandResult{ - NewObjects: du.NewObjects, + NewObjects: au.GetNewObjects(), ChangedObjects: du.ChangedObjects, DeletedObjects: au.GetDeletedObjects(), HookObjects: au.GetAppliedHookObjects(), diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index ff8e8f0ca..6a23ef6fb 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -66,7 +66,7 @@ func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.Comm return nil, err } return &types.CommandResult{ - NewObjects: du.NewObjects, + NewObjects: au.GetNewObjects(), ChangedObjects: du.ChangedObjects, DeletedObjects: au.GetDeletedObjects(), HookObjects: au.GetAppliedHookObjects(), diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 3c99172d1..97baf5772 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -95,7 +95,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type du.Diff() return &types.CommandResult{ - NewObjects: du.NewObjects, + NewObjects: nil, ChangedObjects: du.ChangedObjects, Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 69c6d7fd4..11717629e 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -40,6 +40,7 @@ type ApplyUtil struct { dew *DeploymentErrorsAndWarnings errorCount int warningCount int + newObjects map[k8s2.ObjectRef]*uo.UnstructuredObject appliedObjects map[k8s2.ObjectRef]*uo.UnstructuredObject appliedHookObjects map[k8s2.ObjectRef]*uo.UnstructuredObject deletedObjects map[k8s2.ObjectRef]bool @@ -95,6 +96,7 @@ func (ad *ApplyDeploymentsUtil) NewApplyUtil(ctx context.Context, statusCtx *sta ret := &ApplyUtil{ ctx: ctx, dew: ad.dew, + newObjects: map[k8s2.ObjectRef]*uo.UnstructuredObject{}, appliedObjects: map[k8s2.ObjectRef]*uo.UnstructuredObject{}, appliedHookObjects: map[k8s2.ObjectRef]*uo.UnstructuredObject{}, deletedObjects: map[k8s2.ObjectRef]bool{}, @@ -119,6 +121,10 @@ func (a *ApplyUtil) handleResult(appliedObject *uo.UnstructuredObject, hook bool a.appliedHookObjects[ref] = appliedObject } a.appliedObjects[ref] = appliedObject + + if !hook && a.ru.GetRemoteObject(ref) == nil { + a.newObjects[ref] = appliedObject + } } func (a *ApplyUtil) handleApiWarnings(ref k8s2.ObjectRef, warnings []k8s.ApiWarning) { @@ -684,13 +690,13 @@ func (a *ApplyUtil) ReplaceObject(ref k8s2.ObjectRef, firstVersion *uo.Unstructu a.HandleError(ref, fmt.Errorf("unexpected end of loop")) } -func (ad *ApplyDeploymentsUtil) GetAppliedObjects() []*types.RefAndObject { +func (ad *ApplyDeploymentsUtil) collectObjects(f func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject) []*types.RefAndObject { ad.resultsMutex.Lock() defer ad.resultsMutex.Unlock() var ret []*types.RefAndObject for _, a := range ad.results { - for _, o := range a.appliedObjects { + for _, o := range f(a) { ret = append(ret, &types.RefAndObject{ Ref: o.GetK8sRef(), Object: o, @@ -700,6 +706,18 @@ func (ad *ApplyDeploymentsUtil) GetAppliedObjects() []*types.RefAndObject { return ret } +func (ad *ApplyDeploymentsUtil) GetNewObjects() []*types.RefAndObject { + return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { + return au.newObjects + }) +} + +func (ad *ApplyDeploymentsUtil) GetAppliedObjects() []*types.RefAndObject { + return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { + return au.appliedObjects + }) +} + func (ad *ApplyDeploymentsUtil) GetAppliedObjectsMap() map[k8s2.ObjectRef]*uo.UnstructuredObject { ret := make(map[k8s2.ObjectRef]*uo.UnstructuredObject) for _, ro := range ad.GetAppliedObjects() { @@ -709,19 +727,9 @@ func (ad *ApplyDeploymentsUtil) GetAppliedObjectsMap() map[k8s2.ObjectRef]*uo.Un } func (ad *ApplyDeploymentsUtil) GetAppliedHookObjects() []*types.RefAndObject { - ad.resultsMutex.Lock() - defer ad.resultsMutex.Unlock() - - var ret []*types.RefAndObject - for _, a := range ad.results { - for _, o := range a.appliedHookObjects { - ret = append(ret, &types.RefAndObject{ - Ref: o.GetK8sRef(), - Object: o, - }) - } - } - return ret + return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { + return au.appliedHookObjects + }) } func (ad *ApplyDeploymentsUtil) GetDeletedObjects() []k8s2.ObjectRef { diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index 77b1e31ad..05f740a8f 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -22,7 +22,6 @@ type diffUtil struct { IgnoreAnnotations bool remoteDiffObjects map[k8s2.ObjectRef]*uo.UnstructuredObject - NewObjects []*types.RefAndObject ChangedObjects []*types.ChangedObject mutex sync.Mutex } @@ -62,9 +61,6 @@ func (u *diffUtil) Diff() { } wg.Wait() - sort.Slice(u.NewObjects, func(i, j int) bool { - return u.NewObjects[i].Ref.String() < u.NewObjects[j].Ref.String() - }) sort.Slice(u.ChangedObjects, func(i, j int) bool { return u.ChangedObjects[i].Ref.String() < u.ChangedObjects[j].Ref.String() }) @@ -72,12 +68,8 @@ func (u *diffUtil) Diff() { func (u *diffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, ao *uo.UnstructuredObject, ro *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreForDiffItemConfig) { if ao != nil && ro == nil { - u.mutex.Lock() - defer u.mutex.Unlock() - u.NewObjects = append(u.NewObjects, &types.RefAndObject{ - Ref: ao.GetK8sRef(), - Object: ao, - }) + // new? + return } else if ao == nil && ro != nil { // deleted? return From f74692f9daa04621008f16f4fb90ec25e4575a95 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 28 Feb 2023 10:48:28 +0100 Subject: [PATCH 0816/2268] fix: Properly simulate deletion of hooks in dryRun mode --- pkg/deployment/utils/apply_utils.go | 38 ++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 11717629e..67c17f045 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -169,13 +169,45 @@ func (a *ApplyUtil) DeleteObject(ref k8s2.ObjectRef, hook bool) bool { } apiWarnings, err := a.k.DeleteSingleObject(ref, o) a.handleApiWarnings(ref, apiWarnings) - if err != nil { - if !errors.IsNotFound(err) { - a.HandleError(ref, err) + + if err == nil { + a.mutex.Lock() + defer a.mutex.Unlock() + if hook { + a.deletedHookObjects[ref] = true + } else { + a.deletedObjects[ref] = true } + return true + } + if !errors.IsNotFound(err) { + a.HandleError(ref, err) + return false + } + if !a.o.DryRun { + // just ignore 404 errors return false } + // now simulate deletion of objects that got applied in the same run + + wasApplied := false + if hook { + if _, ok := a.appliedObjects[ref]; ok { + wasApplied = true + } + } else { + if _, ok := a.appliedHookObjects[ref]; ok { + wasApplied = true + } + } + if !wasApplied { + // did not get applied, so just ignore the 404 + return false + } + + // it got applied, so we need to pretend it actually got deleted + a.mutex.Lock() defer a.mutex.Unlock() if hook { From 9c2a5e85a80edff91867519c11494b0681f879c7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 28 Feb 2023 11:24:38 +0100 Subject: [PATCH 0817/2268] tests: Remove obsolete tests in regard to du.NewObjects --- pkg/deployment/utils/diff_utils_test.go | 47 ------------------------- 1 file changed, 47 deletions(-) diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index 0e1aa9d81..c97d60e62 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -57,34 +57,12 @@ func newTestConfigMap(name string, data map[string]interface{}) *uo.Unstructured func TestDiff(t *testing.T) { tests := []*diffTestConfig{ - { - name: "One new object", - ro: nil, - lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{})}, - ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{})}, - a: func(t *testing.T, dtc *diffTestConfig) { - assert.Len(t, dtc.du.NewObjects, 1) - assert.Len(t, dtc.du.ChangedObjects, 0) - assert.Equal(t, dtc.lo[0], dtc.du.NewObjects[0].Object) - }, - }, - { - name: "One deleted object", - ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{})}, - lo: nil, - ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{})}, - a: func(t *testing.T, dtc *diffTestConfig) { - assert.Len(t, dtc.du.NewObjects, 0) - assert.Len(t, dtc.du.ChangedObjects, 0) - }, - }, { name: "One changed object (changed field)", ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"})}, lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v2"})}, ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v2"})}, a: func(t *testing.T, dtc *diffTestConfig) { - assert.Len(t, dtc.du.NewObjects, 0) assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []types.Change{ @@ -98,7 +76,6 @@ func TestDiff(t *testing.T) { lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"})}, ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"})}, a: func(t *testing.T, dtc *diffTestConfig) { - assert.Len(t, dtc.du.NewObjects, 0) assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []types.Change{ @@ -112,7 +89,6 @@ func TestDiff(t *testing.T) { lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"})}, ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"})}, a: func(t *testing.T, dtc *diffTestConfig) { - assert.Len(t, dtc.du.NewObjects, 0) assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []types.Change{ @@ -126,7 +102,6 @@ func TestDiff(t *testing.T) { lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d2": "v2"})}, ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d2": "v2"})}, a: func(t *testing.T, dtc *diffTestConfig) { - assert.Len(t, dtc.du.NewObjects, 0) assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []types.Change{ @@ -141,7 +116,6 @@ func TestDiff(t *testing.T) { lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v12", "d3": "v3"})}, ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v12", "d3": "v3"})}, a: func(t *testing.T, dtc *diffTestConfig) { - assert.Len(t, dtc.du.NewObjects, 0) assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []types.Change{ @@ -151,27 +125,6 @@ func TestDiff(t *testing.T) { }, dtc.du.ChangedObjects[0].Changes) }, }, - { - name: "Two changed objects + One new object", - ro: []*uo.UnstructuredObject{newTestConfigMap("test1", map[string]interface{}{"d1": "v1", "d2": "v2"}), newTestConfigMap("test2", map[string]interface{}{"xd1": "xv1", "xd2": "xv2"})}, - lo: []*uo.UnstructuredObject{newTestConfigMap("test1", map[string]interface{}{"d1": "v1", "d2": "v3"}), newTestConfigMap("test2", map[string]interface{}{"xd3": "xv2"}), newTestConfigMap("test3", map[string]interface{}{"yd3": "yv2"})}, - ao: []*uo.UnstructuredObject{newTestConfigMap("test1", map[string]interface{}{"d1": "v1", "d2": "v3"}), newTestConfigMap("test2", map[string]interface{}{"xd3": "xv2"}), newTestConfigMap("test3", map[string]interface{}{"yd3": "yv2"})}, - a: func(t *testing.T, dtc *diffTestConfig) { - assert.Len(t, dtc.du.NewObjects, 1) - assert.Len(t, dtc.du.ChangedObjects, 2) - assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) - assert.Equal(t, dtc.du.ChangedObjects[1].NewObject, dtc.ao[1]) - assert.Equal(t, dtc.du.NewObjects[0].Object, dtc.ao[2]) - assert.Equal(t, []types.Change{ - types.Change{Type: "update", JsonPath: "data.d2", OldValue: "v2", NewValue: "v3", UnifiedDiff: "-v2\n+v3"}, - }, dtc.du.ChangedObjects[0].Changes) - assert.Equal(t, []types.Change{ - types.Change{Type: "delete", JsonPath: "data.xd1", OldValue: "xv1", NewValue: interface{}(nil), UnifiedDiff: "-xv1"}, - types.Change{Type: "delete", JsonPath: "data.xd2", OldValue: "xv2", NewValue: interface{}(nil), UnifiedDiff: "-xv2"}, - types.Change{Type: "insert", JsonPath: "data.xd3", OldValue: interface{}(nil), NewValue: "xv2", UnifiedDiff: "+xv2"}, - }, dtc.du.ChangedObjects[1].Changes) - }, - }, } for _, test := range tests { From ceffd6e9e59788e400dd263df91f920a77369c4e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 28 Feb 2023 11:25:47 +0100 Subject: [PATCH 0818/2268] chore: Remove unused code --- pkg/utils/python_scanner/README.md | 2 - pkg/utils/python_scanner/scanner.go | 793 ---------------------------- 2 files changed, 795 deletions(-) delete mode 100644 pkg/utils/python_scanner/README.md delete mode 100644 pkg/utils/python_scanner/scanner.go diff --git a/pkg/utils/python_scanner/README.md b/pkg/utils/python_scanner/README.md deleted file mode 100644 index 645cb1299..000000000 --- a/pkg/utils/python_scanner/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This package is a copy of golangs text/scanner package, with the difference that it interprets ' and ` the same way as ", so -that it's a little bit more like python. \ No newline at end of file diff --git a/pkg/utils/python_scanner/scanner.go b/pkg/utils/python_scanner/scanner.go deleted file mode 100644 index 006f9107b..000000000 --- a/pkg/utils/python_scanner/scanner.go +++ /dev/null @@ -1,793 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package scanner provides a scanner and tokenizer for UTF-8-encoded text. -// It takes an io.Reader providing the source, which then can be tokenized -// through repeated calls to the Scan function. For compatibility with -// existing tools, the NUL character is not allowed. If the first character -// in the source is a UTF-8 encoded byte order mark (BOM), it is discarded. -// -// By default, a Scanner skips white space and Go comments and recognizes all -// literals as defined by the Go language specification. It may be -// customized to recognize only a subset of those literals and to recognize -// different identifier and white space characters. -package python_scanner - -import ( - "bytes" - "fmt" - "io" - "os" - "unicode" - "unicode/utf8" -) - -// Position is a value that represents a source position. -// A position is valid if Line > 0. -type Position struct { - Filename string // filename, if any - Offset int // byte offset, starting at 0 - Line int // line number, starting at 1 - Column int // column number, starting at 1 (character count per line) -} - -// IsValid reports whether the position is valid. -func (pos *Position) IsValid() bool { return pos.Line > 0 } - -func (pos Position) String() string { - s := pos.Filename - if s == "" { - s = "" - } - if pos.IsValid() { - s += fmt.Sprintf(":%d:%d", pos.Line, pos.Column) - } - return s -} - -// Predefined mode bits to control recognition of tokens. For instance, -// to configure a Scanner such that it only recognizes (Go) identifiers, -// integers, and skips comments, set the Scanner's Mode field to: -// -// ScanIdents | ScanInts | SkipComments -// -// With the exceptions of comments, which are skipped if SkipComments is -// set, unrecognized tokens are not ignored. Instead, the scanner simply -// returns the respective individual characters (or possibly sub-tokens). -// For instance, if the mode is ScanIdents (not ScanStrings), the string -// "foo" is scanned as the token sequence '"' Ident '"'. -// -// Use GoTokens to configure the Scanner such that it accepts all Go -// literal tokens including Go identifiers. Comments will be skipped. -const ( - ScanIdents = 1 << -Ident - ScanInts = 1 << -Int - ScanFloats = 1 << -Float // includes Ints and hexadecimal floats - ScanChars = 1 << -Char - ScanStrings = 1 << -String - ScanRawStrings = 1 << -RawString - ScanComments = 1 << -Comment - SkipComments = 1 << -skipComment // if set with ScanComments, comments become white space - GoTokens = ScanIdents | ScanFloats | ScanChars | ScanStrings | ScanRawStrings | ScanComments | SkipComments -) - -// The result of Scan is one of these tokens or a Unicode character. -const ( - EOF = -(iota + 1) - Ident - Int - Float - Char - String - RawString - Comment - - // internal use only - skipComment -) - -var tokenString = map[rune]string{ - EOF: "EOF", - Ident: "Ident", - Int: "Int", - Float: "Float", - Char: "Char", - String: "String", - RawString: "RawString", - Comment: "Comment", -} - -// TokenString returns a printable string for a token or Unicode character. -func TokenString(tok rune) string { - if s, found := tokenString[tok]; found { - return s - } - return fmt.Sprintf("%q", string(tok)) -} - -// GoWhitespace is the default value for the Scanner's Whitespace field. -// Its value selects Go's white space characters. -const GoWhitespace = 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' ' - -const bufLen = 1024 // at least utf8.UTFMax - -// A Scanner implements reading of Unicode characters and tokens from an io.Reader. -type Scanner struct { - // Input - src io.Reader - - // Source buffer - srcBuf [bufLen + 1]byte // +1 for sentinel for common case of s.next() - srcPos int // reading position (srcBuf index) - srcEnd int // source end (srcBuf index) - - // Source position - srcBufOffset int // byte offset of srcBuf[0] in source - line int // line count - column int // character count - lastLineLen int // length of last line in characters (for correct column reporting) - lastCharLen int // length of last character in bytes - - // Token text buffer - // Typically, token text is stored completely in srcBuf, but in general - // the token text's head may be buffered in tokBuf while the token text's - // tail is stored in srcBuf. - tokBuf bytes.Buffer // token text head that is not in srcBuf anymore - tokPos int // token text tail position (srcBuf index); valid if >= 0 - tokEnd int // token text tail end (srcBuf index) - - // One character look-ahead - ch rune // character before current srcPos - - // Error is called for each error encountered. If no Error - // function is set, the error is reported to os.Stderr. - Error func(s *Scanner, msg string) - - // ErrorCount is incremented by one for each error encountered. - ErrorCount int - - // The Mode field controls which tokens are recognized. For instance, - // to recognize Ints, set the ScanInts bit in Mode. The field may be - // changed at any time. - Mode uint - - // The Whitespace field controls which characters are recognized - // as white space. To recognize a character ch <= ' ' as white space, - // set the ch'th bit in Whitespace (the Scanner's behavior is undefined - // for values ch > ' '). The field may be changed at any time. - Whitespace uint64 - - // IsIdentRune is a predicate controlling the characters accepted - // as the ith rune in an identifier. The set of valid characters - // must not intersect with the set of white space characters. - // If no IsIdentRune function is set, regular Go identifiers are - // accepted instead. The field may be changed at any time. - IsIdentRune func(ch rune, i int) bool - - // Start position of most recently scanned token; set by Scan. - // Calling Init or Next invalidates the position (Line == 0). - // The Filename field is always left untouched by the Scanner. - // If an error is reported (via Error) and Position is invalid, - // the scanner is not inside a token. Call Pos to obtain an error - // position in that case, or to obtain the position immediately - // after the most recently scanned token. - Position -} - -// Init initializes a Scanner with a new source and returns s. -// Error is set to nil, ErrorCount is set to 0, Mode is set to GoTokens, -// and Whitespace is set to GoWhitespace. -func (s *Scanner) Init(src io.Reader) *Scanner { - s.src = src - - // initialize source buffer - // (the first call to next() will fill it by calling src.Read) - s.srcBuf[0] = utf8.RuneSelf // sentinel - s.srcPos = 0 - s.srcEnd = 0 - - // initialize source position - s.srcBufOffset = 0 - s.line = 1 - s.column = 0 - s.lastLineLen = 0 - s.lastCharLen = 0 - - // initialize token text buffer - // (required for first call to next()). - s.tokPos = -1 - - // initialize one character look-ahead - s.ch = -2 // no char read yet, not EOF - - // initialize public fields - s.Error = nil - s.ErrorCount = 0 - s.Mode = GoTokens - s.Whitespace = GoWhitespace - s.Line = 0 // invalidate token position - - return s -} - -// next reads and returns the next Unicode character. It is designed such -// that only a minimal amount of work needs to be done in the common ASCII -// case (one test to check for both ASCII and end-of-buffer, and one test -// to check for newlines). -func (s *Scanner) next() rune { - ch, width := rune(s.srcBuf[s.srcPos]), 1 - - if ch >= utf8.RuneSelf { - // uncommon case: not ASCII or not enough bytes - for s.srcPos+utf8.UTFMax > s.srcEnd && !utf8.FullRune(s.srcBuf[s.srcPos:s.srcEnd]) { - // not enough bytes: read some more, but first - // save away token text if any - if s.tokPos >= 0 { - s.tokBuf.Write(s.srcBuf[s.tokPos:s.srcPos]) - s.tokPos = 0 - // s.tokEnd is set by Scan() - } - // move unread bytes to beginning of buffer - copy(s.srcBuf[0:], s.srcBuf[s.srcPos:s.srcEnd]) - s.srcBufOffset += s.srcPos - // read more bytes - // (an io.Reader must return io.EOF when it reaches - // the end of what it is reading - simply returning - // n == 0 will make this loop retry forever; but the - // error is in the reader implementation in that case) - i := s.srcEnd - s.srcPos - n, err := s.src.Read(s.srcBuf[i:bufLen]) - s.srcPos = 0 - s.srcEnd = i + n - s.srcBuf[s.srcEnd] = utf8.RuneSelf // sentinel - if err != nil { - if err != io.EOF { - s.error(err.Error()) - } - if s.srcEnd == 0 { - if s.lastCharLen > 0 { - // previous character was not EOF - s.column++ - } - s.lastCharLen = 0 - return EOF - } - // If err == EOF, we won't be getting more - // bytes; break to avoid infinite loop. If - // err is something else, we don't know if - // we can get more bytes; thus also break. - break - } - } - // at least one byte - ch = rune(s.srcBuf[s.srcPos]) - if ch >= utf8.RuneSelf { - // uncommon case: not ASCII - ch, width = utf8.DecodeRune(s.srcBuf[s.srcPos:s.srcEnd]) - if ch == utf8.RuneError && width == 1 { - // advance for correct error position - s.srcPos += width - s.lastCharLen = width - s.column++ - s.error("invalid UTF-8 encoding") - return ch - } - } - } - - // advance - s.srcPos += width - s.lastCharLen = width - s.column++ - - // special situations - switch ch { - case 0: - // for compatibility with other tools - s.error("invalid character NUL") - case '\n': - s.line++ - s.lastLineLen = s.column - s.column = 0 - } - - return ch -} - -// Next reads and returns the next Unicode character. -// It returns EOF at the end of the source. It reports -// a read error by calling s.Error, if not nil; otherwise -// it prints an error message to os.Stderr. Next does not -// update the Scanner's Position field; use Pos() to -// get the current position. -func (s *Scanner) Next() rune { - s.tokPos = -1 // don't collect token text - s.Line = 0 // invalidate token position - ch := s.Peek() - if ch != EOF { - s.ch = s.next() - } - return ch -} - -// Peek returns the next Unicode character in the source without advancing -// the scanner. It returns EOF if the scanner's position is at the last -// character of the source. -func (s *Scanner) Peek() rune { - if s.ch == -2 { - // this code is only run for the very first character - s.ch = s.next() - if s.ch == '\uFEFF' { - s.ch = s.next() // ignore BOM - } - } - return s.ch -} - -func (s *Scanner) error(msg string) { - s.tokEnd = s.srcPos - s.lastCharLen // make sure token text is terminated - s.ErrorCount++ - if s.Error != nil { - s.Error(s, msg) - return - } - pos := s.Position - if !pos.IsValid() { - pos = s.Pos() - } - fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) -} - -func (s *Scanner) errorf(format string, args ...interface{}) { - s.error(fmt.Sprintf(format, args...)) -} - -func (s *Scanner) isIdentRune(ch rune, i int) bool { - if s.IsIdentRune != nil { - return s.IsIdentRune(ch, i) - } - return ch == '_' || unicode.IsLetter(ch) || unicode.IsDigit(ch) && i > 0 -} - -func (s *Scanner) scanIdentifier() rune { - // we know the zero'th rune is OK; start scanning at the next one - ch := s.next() - for i := 1; s.isIdentRune(ch, i); i++ { - ch = s.next() - } - return ch -} - -func lower(ch rune) rune { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter -func isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' } -func isHex(ch rune) bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f' } - -// digits accepts the sequence { digit | '_' } starting with ch0. -// If base <= 10, digits accepts any decimal digit but records -// the first invalid digit >= base in *invalid if *invalid == 0. -// digits returns the first rune that is not part of the sequence -// anymore, and a bitset describing whether the sequence contained -// digits (bit 0 is set), or separators '_' (bit 1 is set). -func (s *Scanner) digits(ch0 rune, base int, invalid *rune) (ch rune, digsep int) { - ch = ch0 - if base <= 10 { - max := rune('0' + base) - for isDecimal(ch) || ch == '_' { - ds := 1 - if ch == '_' { - ds = 2 - } else if ch >= max && *invalid == 0 { - *invalid = ch - } - digsep |= ds - ch = s.next() - } - } else { - for isHex(ch) || ch == '_' { - ds := 1 - if ch == '_' { - ds = 2 - } - digsep |= ds - ch = s.next() - } - } - return -} - -func (s *Scanner) scanNumber(ch rune, seenDot bool) (rune, rune) { - base := 10 // number base - prefix := rune(0) // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b' - digsep := 0 // bit 0: digit present, bit 1: '_' present - invalid := rune(0) // invalid digit in literal, or 0 - - // integer part - var tok rune - var ds int - if !seenDot { - tok = Int - if ch == '0' { - ch = s.next() - switch lower(ch) { - case 'x': - ch = s.next() - base, prefix = 16, 'x' - case 'o': - ch = s.next() - base, prefix = 8, 'o' - case 'b': - ch = s.next() - base, prefix = 2, 'b' - default: - base, prefix = 8, '0' - digsep = 1 // leading 0 - } - } - ch, ds = s.digits(ch, base, &invalid) - digsep |= ds - if ch == '.' && s.Mode&ScanFloats != 0 { - ch = s.next() - seenDot = true - } - } - - // fractional part - if seenDot { - tok = Float - if prefix == 'o' || prefix == 'b' { - s.error("invalid radix point in " + litname(prefix)) - } - ch, ds = s.digits(ch, base, &invalid) - digsep |= ds - } - - if digsep&1 == 0 { - s.error(litname(prefix) + " has no digits") - } - - // exponent - if e := lower(ch); (e == 'e' || e == 'p') && s.Mode&ScanFloats != 0 { - switch { - case e == 'e' && prefix != 0 && prefix != '0': - s.errorf("%q exponent requires decimal mantissa", ch) - case e == 'p' && prefix != 'x': - s.errorf("%q exponent requires hexadecimal mantissa", ch) - } - ch = s.next() - tok = Float - if ch == '+' || ch == '-' { - ch = s.next() - } - ch, ds = s.digits(ch, 10, nil) - digsep |= ds - if ds&1 == 0 { - s.error("exponent has no digits") - } - } else if prefix == 'x' && tok == Float { - s.error("hexadecimal mantissa requires a 'p' exponent") - } - - if tok == Int && invalid != 0 { - s.errorf("invalid digit %q in %s", invalid, litname(prefix)) - } - - if digsep&2 != 0 { - s.tokEnd = s.srcPos - s.lastCharLen // make sure token text is terminated - if i := invalidSep(s.TokenText()); i >= 0 { - s.error("'_' must separate successive digits") - } - } - - return tok, ch -} - -func litname(prefix rune) string { - switch prefix { - default: - return "decimal literal" - case 'x': - return "hexadecimal literal" - case 'o', '0': - return "octal literal" - case 'b': - return "binary literal" - } -} - -// invalidSep returns the index of the first invalid separator in x, or -1. -func invalidSep(x string) int { - x1 := ' ' // prefix char, we only care if it's 'x' - d := '.' // digit, one of '_', '0' (a digit), or '.' (anything else) - i := 0 - - // a prefix counts as a digit - if len(x) >= 2 && x[0] == '0' { - x1 = lower(rune(x[1])) - if x1 == 'x' || x1 == 'o' || x1 == 'b' { - d = '0' - i = 2 - } - } - - // mantissa and exponent - for ; i < len(x); i++ { - p := d // previous digit - d = rune(x[i]) - switch { - case d == '_': - if p != '0' { - return i - } - case isDecimal(d) || x1 == 'x' && isHex(d): - d = '0' - default: - if p == '_' { - return i - 1 - } - d = '.' - } - } - if d == '_' { - return len(x) - 1 - } - - return -1 -} - -func digitVal(ch rune) int { - switch { - case '0' <= ch && ch <= '9': - return int(ch - '0') - case 'a' <= lower(ch) && lower(ch) <= 'f': - return int(lower(ch) - 'a' + 10) - } - return 16 // larger than any legal digit val -} - -func (s *Scanner) scanDigits(ch rune, base, n int) rune { - for n > 0 && digitVal(ch) < base { - ch = s.next() - n-- - } - if n > 0 { - s.error("invalid char escape") - } - return ch -} - -func (s *Scanner) scanEscape(quote rune) rune { - ch := s.next() // read character after '/' - switch ch { - case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote: - // nothing to do - ch = s.next() - case '0', '1', '2', '3', '4', '5', '6', '7': - ch = s.scanDigits(ch, 8, 3) - case 'x': - ch = s.scanDigits(s.next(), 16, 2) - case 'u': - ch = s.scanDigits(s.next(), 16, 4) - case 'U': - ch = s.scanDigits(s.next(), 16, 8) - default: - s.error("invalid char escape") - } - return ch -} - -func (s *Scanner) scanString(quote rune) (n int) { - ch := s.next() // read character after quote - for ch != quote { - if ch == '\n' || ch < 0 { - s.error("literal not terminated") - return - } - if ch == '\\' { - ch = s.scanEscape(quote) - } else { - ch = s.next() - } - n++ - } - return -} - -func (s *Scanner) scanRawString(quote rune) { - ch := s.next() // read character after '`' - for ch != quote { - if ch < 0 { - s.error("literal not terminated") - return - } - ch = s.next() - } -} - -func (s *Scanner) scanChar() { - if s.scanString('\'') != 1 { - s.error("invalid char literal") - } -} - -func (s *Scanner) scanComment(ch rune) rune { - // ch == '/' || ch == '*' - if ch == '/' { - // line comment - ch = s.next() // read character after "//" - for ch != '\n' && ch >= 0 { - ch = s.next() - } - return ch - } - - // general comment - ch = s.next() // read character after "/*" - for { - if ch < 0 { - s.error("comment not terminated") - break - } - ch0 := ch - ch = s.next() - if ch0 == '*' && ch == '/' { - ch = s.next() - break - } - } - return ch -} - -// Scan reads the next token or Unicode character from source and returns it. -// It only recognizes tokens t for which the respective Mode bit (1<<-t) is set. -// It returns EOF at the end of the source. It reports scanner errors (read and -// token errors) by calling s.Error, if not nil; otherwise it prints an error -// message to os.Stderr. -func (s *Scanner) Scan() rune { - ch := s.Peek() - - // reset token text position - s.tokPos = -1 - s.Line = 0 - -redo: - // skip white space - for s.Whitespace&(1< 0 { - // common case: last character was not a '\n' - s.Line = s.line - s.Column = s.column - } else { - // last character was a '\n' - // (we cannot be at the beginning of the source - // since we have called next() at least once) - s.Line = s.line - 1 - s.Column = s.lastLineLen - } - - // determine token value - tok := ch - switch { - case s.isIdentRune(ch, 0): - if s.Mode&ScanIdents != 0 { - tok = Ident - ch = s.scanIdentifier() - } else { - ch = s.next() - } - case isDecimal(ch): - if s.Mode&(ScanInts|ScanFloats) != 0 { - tok, ch = s.scanNumber(ch, false) - } else { - ch = s.next() - } - default: - switch ch { - case EOF: - break - case '"': - if s.Mode&ScanStrings != 0 { - s.scanRawString('"') - tok = String - } - ch = s.next() - case '\'': - // This is the difference to golang's text/scanner package, we handle ' as a string and not as a char - if s.Mode&ScanStrings != 0 { - s.scanRawString('\'') - tok = String - } - ch = s.next() - case '.': - ch = s.next() - if isDecimal(ch) && s.Mode&ScanFloats != 0 { - tok, ch = s.scanNumber(ch, true) - } - case '/': - ch = s.next() - if (ch == '/' || ch == '*') && s.Mode&ScanComments != 0 { - if s.Mode&SkipComments != 0 { - s.tokPos = -1 // don't collect token text - ch = s.scanComment(ch) - goto redo - } - ch = s.scanComment(ch) - tok = Comment - } - case '`': - if s.Mode&ScanRawStrings != 0 { - s.scanRawString('`') - tok = RawString - } - ch = s.next() - default: - ch = s.next() - } - } - - // end of token text - s.tokEnd = s.srcPos - s.lastCharLen - - s.ch = ch - return tok -} - -// Pos returns the position of the character immediately after -// the character or token returned by the last call to Next or Scan. -// Use the Scanner's Position field for the start position of the most -// recently scanned token. -func (s *Scanner) Pos() (pos Position) { - pos.Filename = s.Filename - pos.Offset = s.srcBufOffset + s.srcPos - s.lastCharLen - switch { - case s.column > 0: - // common case: last character was not a '\n' - pos.Line = s.line - pos.Column = s.column - case s.lastLineLen > 0: - // last character was a '\n' - pos.Line = s.line - 1 - pos.Column = s.lastLineLen - default: - // at the beginning of the source - pos.Line = 1 - pos.Column = 1 - } - return -} - -// TokenText returns the string corresponding to the most recently scanned token. -// Valid after calling Scan and in calls of Scanner.Error. -func (s *Scanner) TokenText() string { - if s.tokPos < 0 { - // no token text - return "" - } - - if s.tokEnd < s.tokPos { - // if EOF was reached, s.tokEnd is set to -1 (s.srcPos == 0) - s.tokEnd = s.tokPos - } - // s.tokEnd >= s.tokPos - - if s.tokBuf.Len() == 0 { - // common case: the entire token text is still in srcBuf - return string(s.srcBuf[s.tokPos:s.tokEnd]) - } - - // part of the token text was saved in tokBuf: save the rest in - // tokBuf as well and return its content - s.tokBuf.Write(s.srcBuf[s.tokPos:s.tokEnd]) - s.tokPos = s.tokEnd // ensure idempotency of TokenText() call - return s.tokBuf.String() -} From c49ee633d28995a88d514b87dd3809d84fdd9585 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 28 Feb 2023 11:32:26 +0100 Subject: [PATCH 0819/2268] fix: Don't consider objects as ready when the observedGeneration != generation (#340) This fixes issues with Deployment objects being marked as ready right after applying changes that would make in non-ready. --- pkg/utils/uo/k8s_fields.go | 8 ++++++++ pkg/validation/validation.go | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/pkg/utils/uo/k8s_fields.go b/pkg/utils/uo/k8s_fields.go index 2758945b1..f97296dee 100644 --- a/pkg/utils/uo/k8s_fields.go +++ b/pkg/utils/uo/k8s_fields.go @@ -186,6 +186,14 @@ func (uo *UnstructuredObject) GetK8sAnnotationsWithRegex(r interface{}) map[stri return ret } +func (uo *UnstructuredObject) GetK8sGeneration() int64 { + ret, ok, _ := uo.GetNestedInt("metadata", "generation") + if !ok { + return -1 + } + return ret +} + func (uo *UnstructuredObject) GetK8sResourceVersion() string { ret, _, _ := uo.GetNestedString("metadata", "resourceVersion") return ret diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 36cf3445e..36a034637 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -258,6 +258,12 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError return i, nil } + observedGeneration := getStatusFieldInt("observedGeneration", reactIgnore, false, -1) + if observedGeneration != -1 && observedGeneration != o.GetK8sGeneration() { + addNotReady("Waiting for reconciliation") + return + } + switch o.GetK8sGVK().GroupKind() { case schema.GroupKind{Group: "", Kind: "Pod"}: containerStatuses, _, err := status.GetNestedObjectList("containerStatuses") From 6af108299f3ae3fbfc11310382fd95b8720c30de Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 6 Mar 2023 15:19:50 +0100 Subject: [PATCH 0820/2268] fix: Also try to parse repo key without dummy scheme --- cmd/kluctl/commands/utils.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 35fa12bab..32f447dd3 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -239,13 +239,16 @@ func parseRepoOverride(s string, isGroup bool) (ret repocache.RepoOverride, err return repocache.RepoOverride{}, fmt.Errorf("%s", s) } - // we need to prepend a dummy scheme to the repo key so that it is properly parsed - dummyUrl := fmt.Sprintf("git://%s", sp[0]) - - u, err := git_url.Parse(dummyUrl) + u, err := git_url.Parse(sp[0]) if err != nil { - return repocache.RepoOverride{}, fmt.Errorf("%s: %w", s, err) + // we need to prepend a dummy scheme to the repo key so that it is properly parsed + dummyUrl := fmt.Sprintf("git://%s", sp[0]) + u, err = git_url.Parse(dummyUrl) + if err != nil { + return repocache.RepoOverride{}, fmt.Errorf("%s: %w", s, err) + } } + u = u.Normalize() ret.RepoUrl = *u ret.Override = sp[1] From c712cf0b8516dad783ce88f49e67c4da06512f0a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 6 Mar 2023 17:11:43 +0100 Subject: [PATCH 0821/2268] fix: Fix RemoveNestedField to properly delete slice elements (#353) --- pkg/utils/uo/nested_fields.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/uo/nested_fields.go b/pkg/utils/uo/nested_fields.go index 7d3ce0afd..13560b767 100644 --- a/pkg/utils/uo/nested_fields.go +++ b/pkg/utils/uo/nested_fields.go @@ -62,7 +62,7 @@ func (uo *UnstructuredObject) RemoveNestedField(keys ...interface{}) error { return nil } l = append(l[:i], l[i+1:]...) - return SetChild(o, i, l) + return uo.SetNestedField(l, keys[0:len(keys)-1]...) } else { return fmt.Errorf("key is not an index") } From 7dd2b430f74f6515f04e55fda376dc91c4c774f2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 6 Mar 2023 17:31:52 +0100 Subject: [PATCH 0822/2268] refactor: Use private copy of giturls --- go.mod | 1 - go.sum | 2 - pkg/git/auth/git_credentials_file.go | 2 +- pkg/git/git-url/giturls/LICENSE | 21 +++ pkg/git/git-url/giturls/urls.go | 150 +++++++++++++++++++ pkg/git/git-url/giturls/urls_test.go | 215 +++++++++++++++++++++++++++ pkg/git/git-url/url.go | 2 +- 7 files changed, 388 insertions(+), 5 deletions(-) create mode 100644 pkg/git/git-url/giturls/LICENSE create mode 100644 pkg/git/git-url/giturls/urls.go create mode 100644 pkg/git/git-url/giturls/urls_test.go diff --git a/go.mod b/go.mod index 683361d28..199e0bb85 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,6 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.2 - github.com/whilp/git-urls v1.0.0 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.6.0 golang.org/x/net v0.7.0 diff --git a/go.sum b/go.sum index ca78af9e0..46b38de43 100644 --- a/go.sum +++ b/go.sum @@ -762,8 +762,6 @@ github.com/urfave/cli v1.22.7/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= -github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index eb1696075..3f66d4b45 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -5,8 +5,8 @@ import ( "context" "github.com/fluxcd/go-git/v5/plumbing/transport/http" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/git/git-url/giturls" "github.com/kluctl/kluctl/v2/pkg/git/messages" - giturls "github.com/whilp/git-urls" "os" "path/filepath" ) diff --git a/pkg/git/git-url/giturls/LICENSE b/pkg/git/git-url/giturls/LICENSE new file mode 100644 index 000000000..2aa848db5 --- /dev/null +++ b/pkg/git/git-url/giturls/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Will Maier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pkg/git/git-url/giturls/urls.go b/pkg/git/git-url/giturls/urls.go new file mode 100644 index 000000000..02341030a --- /dev/null +++ b/pkg/git/git-url/giturls/urls.go @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014 Will Maier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Package giturls parses Git URLs. + +These URLs include standard RFC 3986 URLs as well as special formats that +are specific to Git. Examples are provided in the Git documentation at +https://www.kernel.org/pub/software/scm/git/docs/git-clone.html. +*/ +package giturls + +import ( + "fmt" + "net/url" + "regexp" + "strings" +) + +var ( + // scpSyntax was modified from https://golang.org/src/cmd/go/vcs.go. + scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):([a-zA-Z0-9./._-]+)(?:\?||$)(.*)$`) + + // Transports is a set of known Git URL schemes. + Transports = NewTransportSet( + "ssh", + "git", + "git+ssh", + "http", + "https", + "ftp", + "ftps", + "rsync", + "file", + ) +) + +// Parser converts a string into a URL. +type Parser func(string) (*url.URL, error) + +// Parse parses rawurl into a URL structure. Parse first attempts to +// find a standard URL with a valid Git transport as its scheme. If +// that cannot be found, it then attempts to find a SCP-like URL. And +// if that cannot be found, it assumes rawurl is a local path. If none +// of these rules apply, Parse returns an error. +func Parse(rawurl string) (u *url.URL, err error) { + parsers := []Parser{ + ParseTransport, + ParseScp, + ParseLocal, + } + + // Apply each parser in turn; if the parser succeeds, accept its + // result and return. + for _, p := range parsers { + u, err = p(rawurl) + if err == nil { + return u, err + } + } + + // It's unlikely that none of the parsers will succeed, since + // ParseLocal is very forgiving. + return new(url.URL), fmt.Errorf("failed to parse %q", rawurl) +} + +// ParseTransport parses rawurl into a URL object. Unless the URL's +// scheme is a known Git transport, ParseTransport returns an error. +func ParseTransport(rawurl string) (*url.URL, error) { + u, err := url.Parse(rawurl) + if err == nil && !Transports.Valid(u.Scheme) { + err = fmt.Errorf("scheme %q is not a valid transport", u.Scheme) + } + return u, err +} + +// ParseScp parses rawurl into a URL object. The rawurl must be +// an SCP-like URL, otherwise ParseScp returns an error. +func ParseScp(rawurl string) (*url.URL, error) { + match := scpSyntax.FindAllStringSubmatch(rawurl, -1) + if len(match) == 0 { + return nil, fmt.Errorf("no scp URL found in %q", rawurl) + } + m := match[0] + user := strings.TrimRight(m[1], "@") + var userinfo *url.Userinfo + if user != "" { + userinfo = url.User(user) + } + rawquery := "" + if len(m) > 3 { + rawquery = m[4] + } + return &url.URL{ + Scheme: "ssh", + User: userinfo, + Host: m[2], + Path: m[3], + RawQuery: rawquery, + }, nil +} + +// ParseLocal parses rawurl into a URL object with a "file" +// scheme. This will effectively never return an error. +func ParseLocal(rawurl string) (*url.URL, error) { + return &url.URL{ + Scheme: "file", + Host: "", + Path: rawurl, + }, nil +} + +// TransportSet represents a set of valid Git transport schemes. It +// maps these schemes to empty structs, providing a set-like +// interface. +type TransportSet struct { + Transports map[string]struct{} +} + +// NewTransportSet returns a TransportSet with the items keys mapped +// to empty struct values. +func NewTransportSet(items ...string) *TransportSet { + t := &TransportSet{ + Transports: map[string]struct{}{}, + } + for _, i := range items { + t.Transports[i] = struct{}{} + } + return t +} + +// Valid returns true if transport is a known Git URL scheme and false +// if not. +func (t *TransportSet) Valid(transport string) bool { + _, ok := t.Transports[transport] + return ok +} diff --git a/pkg/git/git-url/giturls/urls_test.go b/pkg/git/git-url/giturls/urls_test.go new file mode 100644 index 000000000..4e3f6cb33 --- /dev/null +++ b/pkg/git/git-url/giturls/urls_test.go @@ -0,0 +1,215 @@ +package giturls + +import ( + "net/url" + "reflect" + "strings" + "testing" +) + +var tests []*Test + +type Test struct { + in string + wantURL *url.URL + wantStr string // expected result of reserializing the URL; empty means same as "in". +} + +func NewTest(in, transport, user, host, path, str, rawquery string) *Test { + var userinfo *url.Userinfo + + if user != "" { + if strings.Contains(user, ":") { + username := strings.Split(user, ":")[0] + password := strings.Split(user, ":")[1] + userinfo = url.UserPassword(username, password) + } else { + userinfo = url.User(user) + } + } + if str == "" { + str = in + } + + return &Test{ + in: in, + wantURL: &url.URL{ + Scheme: transport, + Host: host, + Path: path, + User: userinfo, + RawQuery: rawquery, + }, + wantStr: str, + } +} + +func init() { + // https://www.kernel.org/pub/software/scm/git/docs/git-clone.html + tests = []*Test{ + NewTest( + "user@host.xz:path/to/repo.git/", + "ssh", "user", "host.xz", "path/to/repo.git/", + "ssh://user@host.xz/path/to/repo.git/", "", + ), + NewTest( + "host.xz:path/to/repo.git/", + "ssh", "", "host.xz", "path/to/repo.git/", + "ssh://host.xz/path/to/repo.git/", "", + ), + NewTest( + "host.xz:/path/to/repo.git/", + "ssh", "", "host.xz", "/path/to/repo.git/", + "ssh://host.xz/path/to/repo.git/", "", + ), + NewTest( + "host.xz:path/to/repo-with_specials.git/", + "ssh", "", "host.xz", "path/to/repo-with_specials.git/", + "ssh://host.xz/path/to/repo-with_specials.git/", "", + ), + NewTest( + "git://host.xz/path/to/repo.git/", + "git", "", "host.xz", "/path/to/repo.git/", + "", "", + ), + NewTest( + "git://host.xz:1234/path/to/repo.git/", + "git", "", "host.xz:1234", "/path/to/repo.git/", + "", "", + ), + NewTest( + "http://host.xz/path/to/repo.git/", + "http", "", "host.xz", "/path/to/repo.git/", + "", "", + ), + NewTest( + "http://host.xz:1234/path/to/repo.git/", + "http", "", "host.xz:1234", "/path/to/repo.git/", + "", "", + ), + NewTest( + "https://host.xz/path/to/repo.git/", + "https", "", "host.xz", "/path/to/repo.git/", + "", "", + ), + NewTest( + "https://host.xz:1234/path/to/repo.git/", + "https", "", "host.xz:1234", "/path/to/repo.git/", + "", "", + ), + NewTest( + "ftp://host.xz/path/to/repo.git/", + "ftp", "", "host.xz", "/path/to/repo.git/", + "", "", + ), + NewTest( + "ftp://host.xz:1234/path/to/repo.git/", + "ftp", "", "host.xz:1234", "/path/to/repo.git/", + "", "", + ), + NewTest( + "ftps://host.xz/path/to/repo.git/", + "ftps", "", "host.xz", "/path/to/repo.git/", + "", "", + ), + NewTest( + "ftps://host.xz:1234/path/to/repo.git/", + "ftps", "", "host.xz:1234", "/path/to/repo.git/", + "", "", + ), + NewTest( + "rsync://host.xz/path/to/repo.git/", + "rsync", "", "host.xz", "/path/to/repo.git/", + "", "", + ), + NewTest( + "ssh://user@host.xz:1234/path/to/repo.git/", + "ssh", "user", "host.xz:1234", "/path/to/repo.git/", + "", "", + ), + NewTest( + "ssh://host.xz:1234/path/to/repo.git/", + "ssh", "", "host.xz:1234", "/path/to/repo.git/", + "", "", + ), + NewTest( + "ssh://host.xz/path/to/repo.git/", + "ssh", "", "host.xz", "/path/to/repo.git/", + "", "", + ), + NewTest( + "git+ssh://host.xz/path/to/repo.git/", + "git+ssh", "", "host.xz", "/path/to/repo.git/", + "", "", + ), + NewTest( + "/path/to/repo.git/", + "file", "", "", "/path/to/repo.git/", + "file:///path/to/repo.git/", "", + ), + NewTest( + "file:///path/to/repo.git/", + "file", "", "", "/path/to/repo.git/", + "", "", + ), + // Tests with query strings + NewTest( + "https://host.xz/organization/repo.git?ref=", + "https", "", "host.xz", "/organization/repo.git", + "", "ref=", + ), + NewTest( + "https://host.xz/organization/repo.git?ref=test", + "https", "", "host.xz", "/organization/repo.git", + "", "ref=test", + ), + NewTest( + "https://host.xz/organization/repo.git?ref=feature/test", + "https", "", "host.xz", "/organization/repo.git", + "", "ref=feature/test", + ), + NewTest( + "git@host.xz:organization/repo.git?ref=test", + "ssh", "git", "host.xz", "organization/repo.git", + "ssh://git@host.xz/organization/repo.git?ref=test", "ref=test", + ), + NewTest( + "git@host.xz:organization/repo.git?ref=feature/test", + "ssh", "git", "host.xz", "organization/repo.git", + "ssh://git@host.xz/organization/repo.git?ref=feature/test", "ref=feature/test", + ), + // Tests with user+password and some with query strings + NewTest( + "https://user:password@host.xz/organization/repo.git/", + "https", "user:password", "host.xz", "/organization/repo.git/", + "", "", + ), + NewTest( + "https://user:password@host.xz/organization/repo.git?ref=test", + "https", "user:password", "host.xz", "/organization/repo.git", + "", "ref=test", + ), + NewTest( + "https://user:password@host.xz/organization/repo.git?ref=feature/test", + "https", "user:password", "host.xz", "/organization/repo.git", + "", "ref=feature/test", + ), + } +} + +func TestParse(t *testing.T) { + for _, tt := range tests { + got, err := Parse(tt.in) + if err != nil { + t.Errorf("Parse(%q) = unexpected err %q, want %q", tt.in, err, tt.wantURL) + continue + } + if !reflect.DeepEqual(got, tt.wantURL) { + t.Errorf("Parse(%q) = %q, want %q", tt.in, got, tt.wantURL) + } + str := got.String() + if str != tt.wantStr { + t.Errorf("Parse(%q).String() = %q, want %q", tt.in, str, tt.wantStr) + } + } +} diff --git a/pkg/git/git-url/url.go b/pkg/git/git-url/url.go index 727e8796f..5bf30d9ee 100644 --- a/pkg/git/git-url/url.go +++ b/pkg/git/git-url/url.go @@ -2,7 +2,7 @@ package git_url import ( "fmt" - giturls "github.com/whilp/git-urls" + "github.com/kluctl/kluctl/v2/pkg/git/git-url/giturls" "net/url" "strings" ) From fa04e67501c32b5325b291fa1ee4fcb4f0a8f1e1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 6 Mar 2023 17:50:34 +0100 Subject: [PATCH 0823/2268] fix: Porperly parse SCP urls with ports --- pkg/git/git-url/giturls/urls.go | 18 +++++++++++++----- pkg/git/git-url/giturls/urls_test.go | 5 +++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/pkg/git/git-url/giturls/urls.go b/pkg/git/git-url/giturls/urls.go index 02341030a..3fa2ebe80 100644 --- a/pkg/git/git-url/giturls/urls.go +++ b/pkg/git/git-url/giturls/urls.go @@ -32,7 +32,7 @@ import ( var ( // scpSyntax was modified from https://golang.org/src/cmd/go/vcs.go. - scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):([a-zA-Z0-9./._-]+)(?:\?||$)(.*)$`) + scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):([0-9]+/)?([a-zA-Z0-9./._-]+)(?:\?||$)(.*)$`) // Transports is a set of known Git URL schemes. Transports = NewTransportSet( @@ -101,14 +101,22 @@ func ParseScp(rawurl string) (*url.URL, error) { userinfo = url.User(user) } rawquery := "" - if len(m) > 3 { - rawquery = m[4] + if len(m) > 4 { + rawquery = m[5] + } + host := m[2] + path := m[4] + if m[3] != "" { + port := strings.TrimRight(m[3], "/") + host = fmt.Sprintf("%s:%s", host, port) + // host.xyz:1234/path/ only supports absolute paths + path = "/" + path } return &url.URL{ Scheme: "ssh", User: userinfo, - Host: m[2], - Path: m[3], + Host: host, + Path: path, RawQuery: rawquery, }, nil } diff --git a/pkg/git/git-url/giturls/urls_test.go b/pkg/git/git-url/giturls/urls_test.go index 4e3f6cb33..0a8f2cfc7 100644 --- a/pkg/git/git-url/giturls/urls_test.go +++ b/pkg/git/git-url/giturls/urls_test.go @@ -67,6 +67,11 @@ func init() { "ssh", "", "host.xz", "path/to/repo-with_specials.git/", "ssh://host.xz/path/to/repo-with_specials.git/", "", ), + NewTest( + "host.xz:1234/path/to/repo-with_specials.git/", + "ssh", "", "host.xz:1234", "/path/to/repo-with_specials.git/", + "ssh://host.xz:1234/path/to/repo-with_specials.git/", "", + ), NewTest( "git://host.xz/path/to/repo.git/", "git", "", "host.xz", "/path/to/repo.git/", From 61ad1ccaa5532249e6204fa6f1114bb31585a65f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Mar 2023 08:55:11 +0100 Subject: [PATCH 0824/2268] chore(deps): Bump github.com/onsi/gomega from 1.27.1 to 1.27.2 (#341) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.1 to 1.27.2. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.27.1...v1.27.2) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 16 +++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 199e0bb85..ee6420223 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.27.1 + github.com/onsi/gomega v1.27.2 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.4 @@ -221,10 +221,10 @@ require ( go.opencensus.io v0.24.0 // indirect go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/mod v0.7.0 // indirect + golang.org/x/mod v0.8.0 // indirect golang.org/x/oauth2 v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.5.0 // indirect + golang.org/x/tools v0.6.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/api v0.107.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 46b38de43..238912823 100644 --- a/go.sum +++ b/go.sum @@ -301,6 +301,7 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 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/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= @@ -394,6 +395,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -623,9 +625,9 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/ohler55/ojg v1.17.5 h1:SY6/cdhVzsLinNFIBRNSWhJgihvEWco5Y0TJe46XJ1Y= github.com/ohler55/ojg v1.17.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU= -github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= -github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= +github.com/onsi/ginkgo/v2 v2.8.4 h1:gf5mIQ8cLFieruNLAdgijHF1PYfLphKm2dxxcUtcqK0= +github.com/onsi/gomega v1.27.2 h1:SKU0CXeKE/WVgIV1T61kSa3+IRE8Ekrv9rdXDwwTqnY= +github.com/onsi/gomega v1.27.2/go.mod h1:5mR3phAHpkAVIDkHEUBY6HGVsU+cpcEscrGPB4oPlZI= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= @@ -868,8 +870,8 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1094,8 +1096,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= -golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 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= From fbfdd91ea2f4a00f4849ea588993458986533ce2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Mar 2023 08:55:18 +0100 Subject: [PATCH 0825/2268] chore(deps): Bump k8s.io/api from 0.26.1 to 0.26.2 (#342) Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.26.1 to 0.26.2. - [Release notes](https://github.com/kubernetes/api/releases) - [Commits](https://github.com/kubernetes/api/compare/v0.26.1...v0.26.2) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ee6420223..3dad10915 100644 --- a/go.mod +++ b/go.mod @@ -46,9 +46,9 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.11.1 - k8s.io/api v0.26.1 + k8s.io/api v0.26.2 k8s.io/apiextensions-apiserver v0.26.1 - k8s.io/apimachinery v0.26.1 + k8s.io/apimachinery v0.26.2 k8s.io/client-go v0.26.1 k8s.io/klog/v2 v2.90.0 sigs.k8s.io/kustomize/kyaml v0.13.9 diff --git a/go.sum b/go.sum index 238912823..67a41d297 100644 --- a/go.sum +++ b/go.sum @@ -1262,12 +1262,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= -k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= +k8s.io/api v0.26.2/go.mod h1:1kjMQsFE+QHPfskEcVNgL3+Hp88B80uj0QtSOlj8itU= k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= -k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= -k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= +k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc= k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= From 731f36fa22fbc80bf565b3c6b32d35bbd1b12f86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Mar 2023 08:55:28 +0100 Subject: [PATCH 0826/2268] chore(deps): Bump k8s.io/apimachinery from 0.26.1 to 0.26.2 (#344) Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.26.1 to 0.26.2. - [Release notes](https://github.com/kubernetes/apimachinery/releases) - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.26.1...v0.26.2) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From cf467677c7ee999ca9f91190e42953480cf86077 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Mar 2023 09:10:08 +0100 Subject: [PATCH 0827/2268] chore(deps): Bump k8s.io/client-go from 0.26.1 to 0.26.2 (#343) Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.26.1 to 0.26.2. - [Release notes](https://github.com/kubernetes/client-go/releases) - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.26.1...v0.26.2) --- updated-dependencies: - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3dad10915..bf1c2e2b5 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( k8s.io/api v0.26.2 k8s.io/apiextensions-apiserver v0.26.1 k8s.io/apimachinery v0.26.2 - k8s.io/client-go v0.26.1 + k8s.io/client-go v0.26.2 k8s.io/klog/v2 v2.90.0 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 diff --git a/go.sum b/go.sum index 67a41d297..e06dde8c9 100644 --- a/go.sum +++ b/go.sum @@ -1272,8 +1272,8 @@ k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc= k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= -k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= -k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= +k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU= k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= k8s.io/klog/v2 v2.90.0 h1:VkTxIV/FjRXn1fgNNcKGM8cfmL1Z33ZjXRTVxKCoF5M= From c0119ecf2a8a2e289c0ec7d7ace94b925af08fea Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Tue, 7 Mar 2023 10:00:19 +0100 Subject: [PATCH 0828/2268] doc: link to target discriminator gives a 404 (#354) update link to `target discriminator` target page --- docs/reference/kluctl-project/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/kluctl-project/README.md b/docs/reference/kluctl-project/README.md index 7c786c498..e6c3186d2 100644 --- a/docs/reference/kluctl-project/README.md +++ b/docs/reference/kluctl-project/README.md @@ -49,7 +49,7 @@ args: Specifies a default discriminator template to be used for targets that don't have their own discriminator specified. -See [target discriminator](./targets/README.md#discriminator) for details. +See [target discriminator](./targets/#discriminator) for details. ### targets From bb32a90767ddb52bb686be83a372669cb32c9fc9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 10 Mar 2023 09:14:02 +0100 Subject: [PATCH 0829/2268] fix: Trim spaces when extracting hook annotations (#359) --- pkg/deployment/utils/hooks_util.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/deployment/utils/hooks_util.go b/pkg/deployment/utils/hooks_util.go index 6ebcd928f..6dba39f65 100644 --- a/pkg/deployment/utils/hooks_util.go +++ b/pkg/deployment/utils/hooks_util.go @@ -149,6 +149,7 @@ func (u *HooksUtil) GetHook(o *uo.UnstructuredObject) *hook { return ret } for _, x := range strings.Split(*a, ",") { + x = strings.TrimSpace(x) if x != "" { ret[x] = true } From e46ca90da9c29a591bf2415c389e15a713bcc108 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 10 Mar 2023 12:07:06 +0100 Subject: [PATCH 0830/2268] fix: Upgrade go-jinja2 to get in fixes for to_yaml and get_var (#360) --- go.mod | 6 +++--- go.sum | 16 +++++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index bf1c2e2b5..65e8f5caf 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 - github.com/kluctl/go-jinja2 v0.0.0-20230206202229-6e5a9f576647 + github.com/kluctl/go-jinja2 v0.0.0-20230310104535-8136715a1e5a github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 @@ -60,7 +60,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.17.5 github.com/aws/aws-sdk-go-v2/config v1.18.15 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6 - github.com/go-git/go-git/v5 v5.5.2 + github.com/go-git/go-git/v5 v5.6.0 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 @@ -193,7 +193,7 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pjbgf/sha1cd v0.2.3 // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect diff --git a/go.sum b/go.sum index e06dde8c9..b82cee1bb 100644 --- a/go.sum +++ b/go.sum @@ -267,8 +267,8 @@ github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8 github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.5.2 h1:v8lgZa5k9ylUw+OR/roJHTxR4QItsNFI5nKtAXFuynw= -github.com/go-git/go-git/v5 v5.5.2/go.mod h1:BE5hUJ5yaV2YMxhmaP4l6RBQ08kMxKSPD4BlxtH7OjI= +github.com/go-git/go-git/v5 v5.6.0 h1:JvBdYfcttd+0kdpuWO7KTu0FYgCf5W0t5VwkWGobaa4= +github.com/go-git/go-git/v5 v5.6.0/go.mod h1:6nmJ0tJ3N4noMV1Omv7rC5FG3/o8Cm51TB4CJp7mRmE= 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= @@ -513,8 +513,8 @@ github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7y github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 h1:K96SwIr8MzBQ3kFFz2H/pA2y+EEk04vZ4fWj/YZghBU= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2/go.mod h1:vzngsPshNKUtq0gxkYQKNJafrcH7Qy7Qt6yGNt7JmQI= -github.com/kluctl/go-jinja2 v0.0.0-20230206202229-6e5a9f576647 h1:H7qN4RcRsFX6OuKn2La0ueu0nSFE9uPI/AMcgF5fXi8= -github.com/kluctl/go-jinja2 v0.0.0-20230206202229-6e5a9f576647/go.mod h1:oGEuC0taLwg20ORdSN7cdrN4rook79mJJ01QZIU858U= +github.com/kluctl/go-jinja2 v0.0.0-20230310104535-8136715a1e5a h1:48Mz5ZZmv64Pg/cKR4nIo3Ry49Hqs5YggLPGju0e8PU= +github.com/kluctl/go-jinja2 v0.0.0-20230310104535-8136715a1e5a/go.mod h1:qMY3lmIUPcCfjj5fZm39j+h7FilaqaQFYAze19xG0Dw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -599,6 +599,7 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -648,8 +649,9 @@ github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= @@ -813,6 +815,7 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -870,6 +873,7 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1096,6 +1100,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1287,6 +1292,7 @@ k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.14.4 h1:Kd/Qgx5pd2XUL08eOV2vwIq3L9GhIbJ5Nxengbd4/0M= From 55858276cc952d6d28ff3a8535e2d3e852275780 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 22:53:21 +0100 Subject: [PATCH 0831/2268] chore(deps): Bump github.com/fluxcd/pkg/kustomize from 0.13.1 to 0.13.2 (#355) Bumps [github.com/fluxcd/pkg/kustomize](https://github.com/fluxcd/pkg) from 0.13.1 to 0.13.2. - [Release notes](https://github.com/fluxcd/pkg/releases) - [Commits](https://github.com/fluxcd/pkg/compare/runtime/v0.13.1...runtime/v0.13.2) --- updated-dependencies: - dependency-name: github.com/fluxcd/pkg/kustomize dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 65e8f5caf..de2232db8 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/docker/distribution v2.8.1+incompatible // See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df - github.com/fluxcd/pkg/kustomize v0.13.1 + github.com/fluxcd/pkg/kustomize v0.13.2 github.com/go-playground/validator/v10 v10.11.2 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 @@ -47,7 +47,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.11.1 k8s.io/api v0.26.2 - k8s.io/apiextensions-apiserver v0.26.1 + k8s.io/apiextensions-apiserver v0.26.2 k8s.io/apimachinery v0.26.2 k8s.io/client-go v0.26.2 k8s.io/klog/v2 v2.90.0 @@ -67,7 +67,7 @@ require ( github.com/onsi/gomega v1.27.2 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 - sigs.k8s.io/controller-runtime v0.14.4 + sigs.k8s.io/controller-runtime v0.14.5 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/yaml v1.3.0 ) @@ -236,9 +236,9 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gotest.tools/v3 v3.2.0 // indirect - k8s.io/apiserver v0.26.1 // indirect + k8s.io/apiserver v0.26.2 // indirect k8s.io/cli-runtime v0.26.0 // indirect - k8s.io/component-base v0.26.1 // indirect + k8s.io/component-base v0.26.2 // indirect k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 // indirect k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect diff --git a/go.sum b/go.sum index b82cee1bb..cebd71946 100644 --- a/go.sum +++ b/go.sum @@ -248,8 +248,8 @@ github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df h1:2BHXJp1PwX7D47 github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df/go.mod h1:raWgfUV7lDQVXp4QXUaeNNJkRVKz97UQuF+0kdY7Vmo= github.com/fluxcd/pkg/apis/kustomize v0.8.0 h1:A6aLolxPV2Sll44SOHiX96lbXXmRZmS5BoEerkRHrfM= github.com/fluxcd/pkg/apis/kustomize v0.8.0/go.mod h1:9DPEVSfVIkiC2H3Dk6Ght4YJkswhYIaufXla4tB5Y84= -github.com/fluxcd/pkg/kustomize v0.13.1 h1:xfDghn/kRaa5vYN64dLTAL1b1B1tDwcXlnOAqmz5W28= -github.com/fluxcd/pkg/kustomize v0.13.1/go.mod h1:W+Nm9P8yUhTb8n3hpvceUnCAjl6DFsU0k5yI+HT2NE8= +github.com/fluxcd/pkg/kustomize v0.13.2 h1:isA9yi+m7sSIxdTrFR1U7+LyS2BraG07ZkKLHw3bnGo= +github.com/fluxcd/pkg/kustomize v0.13.2/go.mod h1:1H9qednPxL/JvZE5at/f6wVHTH4WmxJYqfgVOZJ3uAk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -1269,18 +1269,18 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= k8s.io/api v0.26.2/go.mod h1:1kjMQsFE+QHPfskEcVNgL3+Hp88B80uj0QtSOlj8itU= -k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= -k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= +k8s.io/apiextensions-apiserver v0.26.2 h1:/yTG2B9jGY2Q70iGskMf41qTLhL9XeNN2KhI0uDgwko= +k8s.io/apiextensions-apiserver v0.26.2/go.mod h1:Y7UPgch8nph8mGCuVk0SK83LnS8Esf3n6fUBgew8SH8= k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= -k8s.io/apiserver v0.26.1 h1:6vmnAqCDO194SVCPU3MU8NcDgSqsUA62tBUSWrFXhsc= -k8s.io/apiserver v0.26.1/go.mod h1:wr75z634Cv+sifswE9HlAo5FQ7UoUauIICRlOE+5dCg= +k8s.io/apiserver v0.26.2 h1:Pk8lmX4G14hYqJd1poHGC08G03nIHVqdJMR0SD3IH3o= +k8s.io/apiserver v0.26.2/go.mod h1:GHcozwXgXsPuOJ28EnQ/jXEM9QeG6HT22YxSNmpYNh8= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU= -k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= -k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= +k8s.io/component-base v0.26.2 h1:IfWgCGUDzrD6wLLgXEstJKYZKAFS2kO+rBRi0p3LqcI= +k8s.io/component-base v0.26.2/go.mod h1:DxbuIe9M3IZPRxPIzhch2m1eT7uFrSBJUBuVCQEBivs= k8s.io/klog/v2 v2.90.0 h1:VkTxIV/FjRXn1fgNNcKGM8cfmL1Z33ZjXRTVxKCoF5M= k8s.io/klog/v2 v2.90.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 h1:8cNCQs+WqqnSpZ7y0LMQPKD+RZUHU17VqLPMW3qxnxc= @@ -1295,8 +1295,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.14.4 h1:Kd/Qgx5pd2XUL08eOV2vwIq3L9GhIbJ5Nxengbd4/0M= -sigs.k8s.io/controller-runtime v0.14.4/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +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/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= From ee89808f5be927d2be573031fac8a1645504893d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 22:53:38 +0100 Subject: [PATCH 0832/2268] chore(deps): Bump k8s.io/apiextensions-apiserver from 0.26.1 to 0.26.2 (#356) Bumps [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) from 0.26.1 to 0.26.2. - [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases) - [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.26.1...v0.26.2) --- updated-dependencies: - dependency-name: k8s.io/apiextensions-apiserver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 67fdb4bd514bbab0e67a1262134a89ee96997394 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 22:53:54 +0100 Subject: [PATCH 0833/2268] chore(deps): Bump sigs.k8s.io/controller-runtime from 0.14.4 to 0.14.5 (#357) Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.14.4 to 0.14.5. - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.14.4...v0.14.5) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From b47e54319701d7571970a17308a6784cb3b49d07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 22:54:32 +0100 Subject: [PATCH 0834/2268] chore(deps): Bump golang.org/x/sys from 0.5.0 to 0.6.0 (#358) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.5.0 to 0.6.0. - [Release notes](https://github.com/golang/sys/releases) - [Commits](https://github.com/golang/sys/compare/v0.5.0...v0.6.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index de2232db8..098fbc2bc 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( golang.org/x/crypto v0.6.0 golang.org/x/net v0.7.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.5.0 + golang.org/x/sys v0.6.0 golang.org/x/term v0.5.0 golang.org/x/text v0.7.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index cebd71946..0d0c2b615 100644 --- a/go.sum +++ b/go.sum @@ -1019,8 +1019,8 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 9aeb4b82bbcfa60c1ea580bcd1eedfc6ddc28a3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 09:41:57 +0100 Subject: [PATCH 0835/2268] chore(deps): Bump github.com/onsi/gomega from 1.27.2 to 1.27.3 (#361) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.2 to 1.27.3. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.27.2...v1.27.3) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 098fbc2bc..9951c665a 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.27.2 + github.com/onsi/gomega v1.27.3 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.5 @@ -140,7 +140,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.5.9 // indirect diff --git a/go.sum b/go.sum index 0d0c2b615..df06a4abd 100644 --- a/go.sum +++ b/go.sum @@ -352,8 +352,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw 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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 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/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 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= @@ -626,9 +627,9 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/ohler55/ojg v1.17.5 h1:SY6/cdhVzsLinNFIBRNSWhJgihvEWco5Y0TJe46XJ1Y= github.com/ohler55/ojg v1.17.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.8.4 h1:gf5mIQ8cLFieruNLAdgijHF1PYfLphKm2dxxcUtcqK0= -github.com/onsi/gomega v1.27.2 h1:SKU0CXeKE/WVgIV1T61kSa3+IRE8Ekrv9rdXDwwTqnY= -github.com/onsi/gomega v1.27.2/go.mod h1:5mR3phAHpkAVIDkHEUBY6HGVsU+cpcEscrGPB4oPlZI= +github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8= +github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk= +github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= From c3d59bcea4b4dbae1292dc6957e65d1906c00167 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 09:42:58 +0100 Subject: [PATCH 0836/2268] chore(deps): Bump golang.org/x/crypto from 0.6.0 to 0.7.0 (#362) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/golang/crypto/releases) - [Commits](https://github.com/golang/crypto/compare/v0.6.0...v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 9951c665a..a91a277e2 100644 --- a/go.mod +++ b/go.mod @@ -37,12 +37,12 @@ require ( github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.2 github.com/xanzy/ssh-agent v0.3.3 - golang.org/x/crypto v0.6.0 - golang.org/x/net v0.7.0 + golang.org/x/crypto v0.7.0 + golang.org/x/net v0.8.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.6.0 - golang.org/x/term v0.5.0 - golang.org/x/text v0.7.0 + golang.org/x/term v0.6.0 + golang.org/x/text v0.8.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.11.1 diff --git a/go.sum b/go.sum index df06a4abd..4c9aef3c8 100644 --- a/go.sum +++ b/go.sum @@ -836,8 +836,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 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= @@ -922,8 +922,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 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= @@ -1028,8 +1028,8 @@ golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 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= @@ -1040,8 +1040,8 @@ 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.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= From faeff0802b242285e34d20e922fcaafc847dbcf2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 11:15:32 +0100 Subject: [PATCH 0837/2268] chore(deps): Bump helm.sh/helm/v3 from 3.11.1 to 3.11.2 (#364) Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.11.1 to 3.11.2. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.11.1...v3.11.2) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 +-- go.sum | 94 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index a91a277e2..db9644015 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( golang.org/x/text v0.8.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.11.1 + helm.sh/helm/v3 v3.11.2 k8s.io/api v0.26.2 k8s.io/apiextensions-apiserver v0.26.2 k8s.io/apimachinery v0.26.2 @@ -201,7 +201,7 @@ require ( github.com/prometheus/common v0.39.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/rivo/uniseg v0.4.3 // indirect - github.com/rubenv/sql-migrate v1.2.0 // indirect + github.com/rubenv/sql-migrate v1.3.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect diff --git a/go.sum b/go.sum index 4c9aef3c8..6df32aa4c 100644 --- a/go.sum +++ b/go.sum @@ -75,13 +75,12 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= +github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= @@ -95,6 +94,7 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= +github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= @@ -105,8 +105,10 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= @@ -184,8 +186,13 @@ github.com/containerd/containerd v1.6.15 h1:4wWexxzLNHNE46aIETc6ge4TofO550v+BlLo github.com/containerd/containerd v1.6.15/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= @@ -198,6 +205,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= @@ -240,6 +249,7 @@ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 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= @@ -251,6 +261,8 @@ github.com/fluxcd/pkg/apis/kustomize v0.8.0/go.mod h1:9DPEVSfVIkiC2H3Dk6Ght4YJks github.com/fluxcd/pkg/kustomize v0.13.2 h1:isA9yi+m7sSIxdTrFR1U7+LyS2BraG07ZkKLHw3bnGo= github.com/fluxcd/pkg/kustomize v0.13.2/go.mod h1:1H9qednPxL/JvZE5at/f6wVHTH4WmxJYqfgVOZJ3uAk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= @@ -272,7 +284,7 @@ github.com/go-git/go-git/v5 v5.6.0/go.mod h1:6nmJ0tJ3N4noMV1Omv7rC5FG3/o8Cm51TB4 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-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= +github.com/go-gorp/gorp/v3 v3.0.5/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -296,8 +308,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -314,6 +324,7 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= @@ -323,6 +334,7 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -373,6 +385,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -415,6 +428,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= @@ -422,6 +436,9 @@ github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce github.com/goware/prefixer v0.0.0-20160118172347-395022866408/go.mod h1:PE1ycukgRPJ7bJ9a1fdfQ9j8i/cEcRAoLZzbxYpNB/s= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -493,6 +510,7 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -508,6 +526,7 @@ github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= @@ -523,7 +542,9 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB 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/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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= @@ -537,11 +558,11 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6Fm github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -556,11 +577,14 @@ github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kN github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 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= @@ -570,16 +594,15 @@ github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mN github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= +github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -623,9 +646,12 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P 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/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= +github.com/nelsam/hel/v2 v2.3.3/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/ohler55/ojg v1.17.5 h1:SY6/cdhVzsLinNFIBRNSWhJgihvEWco5Y0TJe46XJ1Y= github.com/ohler55/ojg v1.17.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8= github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk= @@ -644,6 +670,7 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9 github.com/otiai10/mint v1.4.0 h1:umwcf7gbpEwf7WFzqmWwSv0CzbeMsae2u9ZvpP8j2q4= github.com/otiai10/mint v1.4.0/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/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= @@ -666,9 +693,12 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/poy/onpar v0.0.0-20200406201722-06f95a1c68e8/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= +github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= @@ -678,27 +708,33 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/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.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 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/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU= -github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y= +github.com/rubenv/sql-migrate v1.3.1 h1:Vx+n4Du8X8VTYuXbhNxdEUoh6wiJERA0GlWocR5FrbA= +github.com/rubenv/sql-migrate v1.3.1/go.mod h1:YzG/Vh82CwyhTFXy+Mf5ahAiiEOpAlHurg+23VEzcsk= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -723,21 +759,27 @@ github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ys github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.6.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.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 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 v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag 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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= @@ -762,6 +804,8 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 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-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.22.7 h1:aXiFAgRugfJ27UFDsGJ9DB2FvTC73hlVXFSqq5bo9eU= github.com/urfave/cli v1.22.7/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= @@ -777,8 +821,10 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -789,7 +835,7 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= -github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= @@ -809,11 +855,14 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20221205180719-3fd0dac74452 h1:JZtNuL6LPB+scU5yaQ6hqRlJFRiddZm2FwRt2AQqtHA= go.starlark.net v0.0.0-20221205180719-3fd0dac74452/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 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/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -836,6 +885,7 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -882,12 +932,14 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -922,6 +974,7 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -956,8 +1009,10 @@ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5h 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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1017,9 +1072,11 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1028,6 +1085,7 @@ golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1040,6 +1098,7 @@ 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1047,6 +1106,7 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1081,6 +1141,7 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK 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-20200313205530-4303120df7d8/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 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-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -1190,6 +1251,7 @@ google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqd google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -1239,10 +1301,12 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= @@ -1259,8 +1323,8 @@ 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.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I= gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= -helm.sh/helm/v3 v3.11.1 h1:cmL9fFohOoNQf+wnp2Wa0OhNFH0KFnSzEkVxi3fcc3I= -helm.sh/helm/v3 v3.11.1/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8= +helm.sh/helm/v3 v3.11.2 h1:P3cLaFxfoxaGLGJVnoPrhf1j86LC5EDINSpYSpMUkkA= +helm.sh/helm/v3 v3.11.2/go.mod h1:Hw+09mfpDiRRKAgAIZlFkPSeOkvv7Acl5McBvQyNPVw= 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= From f8d707b3de53ac284eb7d29104743a6dcce6d739 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 08:43:32 +0100 Subject: [PATCH 0838/2268] chore(deps): Bump github.com/onsi/gomega from 1.27.3 to 1.27.4 (#370) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.3 to 1.27.4. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.27.3...v1.27.4) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index db9644015..1d983cdc1 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.27.3 + github.com/onsi/gomega v1.27.4 github.com/otiai10/copy v1.9.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.5 @@ -221,10 +221,10 @@ require ( go.opencensus.io v0.24.0 // indirect go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/mod v0.8.0 // indirect + golang.org/x/mod v0.9.0 // indirect golang.org/x/oauth2 v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.6.0 // indirect + golang.org/x/tools v0.7.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/api v0.107.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 6df32aa4c..5013129ba 100644 --- a/go.sum +++ b/go.sum @@ -653,9 +653,9 @@ github.com/ohler55/ojg v1.17.5 h1:SY6/cdhVzsLinNFIBRNSWhJgihvEWco5Y0TJe46XJ1Y= github.com/ohler55/ojg v1.17.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8= -github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk= -github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= +github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= @@ -925,8 +925,8 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1163,8 +1163,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 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= From d80d25daeedd87a0993f5afc42dc5b96c4dd5066 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 08:43:46 +0100 Subject: [PATCH 0839/2268] chore(deps): Bump k8s.io/klog/v2 from 2.90.0 to 2.90.1 (#371) Bumps [k8s.io/klog/v2](https://github.com/kubernetes/klog) from 2.90.0 to 2.90.1. - [Release notes](https://github.com/kubernetes/klog/releases) - [Changelog](https://github.com/kubernetes/klog/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes/klog/compare/v2.90.0...v2.90.1) --- updated-dependencies: - dependency-name: k8s.io/klog/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1d983cdc1..55cc0c6d8 100644 --- a/go.mod +++ b/go.mod @@ -50,7 +50,7 @@ require ( k8s.io/apiextensions-apiserver v0.26.2 k8s.io/apimachinery v0.26.2 k8s.io/client-go v0.26.2 - k8s.io/klog/v2 v2.90.0 + k8s.io/klog/v2 v2.90.1 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) diff --git a/go.sum b/go.sum index 5013129ba..c5ee9e088 100644 --- a/go.sum +++ b/go.sum @@ -1346,8 +1346,8 @@ k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU= k8s.io/component-base v0.26.2 h1:IfWgCGUDzrD6wLLgXEstJKYZKAFS2kO+rBRi0p3LqcI= k8s.io/component-base v0.26.2/go.mod h1:DxbuIe9M3IZPRxPIzhch2m1eT7uFrSBJUBuVCQEBivs= -k8s.io/klog/v2 v2.90.0 h1:VkTxIV/FjRXn1fgNNcKGM8cfmL1Z33ZjXRTVxKCoF5M= -k8s.io/klog/v2 v2.90.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 h1:8cNCQs+WqqnSpZ7y0LMQPKD+RZUHU17VqLPMW3qxnxc= k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596/go.mod h1:/BYxry62FuDzmI+i9B+X2pqfySRmSOW2ARmj5Zbqhj0= k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= From 6cc003b102a3067226aae523608e5725c18844d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 08:44:07 +0100 Subject: [PATCH 0840/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/config (#376) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.15 to 1.18.18. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.15...config/v1.18.18) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 22 +++++++++++----------- go.sum | 41 ++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 55cc0c6d8..0496161c6 100644 --- a/go.mod +++ b/go.mod @@ -57,8 +57,8 @@ require ( require ( filippo.io/age v1.1.1 - github.com/aws/aws-sdk-go-v2 v1.17.5 - github.com/aws/aws-sdk-go-v2/config v1.18.15 + github.com/aws/aws-sdk-go-v2 v1.17.6 + github.com/aws/aws-sdk-go-v2/config v1.18.18 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6 github.com/go-git/go-git/v5 v5.6.0 github.com/go-logr/logr v1.2.3 @@ -93,16 +93,16 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.15 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.17 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index c5ee9e088..d11adf871 100644 --- a/go.sum +++ b/go.sum @@ -114,34 +114,37 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.17.5 h1:TzCUW1Nq4H8Xscph5M/skINUitxM5UBAyvm2s7XBzL4= github.com/aws/aws-sdk-go-v2 v1.17.5/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.15 h1:509yMO0pJUGUugBP2H9FOFyV+7Mz7sRR+snfDN5W4NY= -github.com/aws/aws-sdk-go-v2/config v1.18.15/go.mod h1:vS0tddZqpE8cD9CyW0/kITHF5Bq2QasW9Y1DFHD//O0= -github.com/aws/aws-sdk-go-v2/credentials v1.13.15 h1:0rZQIi6deJFjOEgHI9HI2eZcLPPEGQPictX66oRFLL8= -github.com/aws/aws-sdk-go-v2/credentials v1.13.15/go.mod h1:vRMLMD3/rXU+o6j2MW5YefrGMBmdTvkLLGqFwMLBHQc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 h1:Kbiv9PGnQfG/imNI4L/heyUXvzKmcWSBeDvkrQz5pFc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23/go.mod h1:mOtmAg65GT1HIL/HT/PynwPbS+UG0BgCZ6vhkPqnxWo= +github.com/aws/aws-sdk-go-v2 v1.17.6 h1:Y773UK7OBqhzi5VDXMi1zVGsoj+CVHs2eaC2bDsLwi0= +github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.18 h1:/ePABXvXl3ESlzUGnkkvvNnRFw3Gh13dyqaq0Qo3JcU= +github.com/aws/aws-sdk-go-v2/config v1.18.18/go.mod h1:Lj3E7XcxJnxMa+AYo89YiL68s1cFJRGduChynYU67VA= +github.com/aws/aws-sdk-go-v2/credentials v1.13.17 h1:IubQO/RNeIVKF5Jy77w/LfUvmmCxTnk2TP1UZZIMiF4= +github.com/aws/aws-sdk-go-v2/credentials v1.13.17/go.mod h1:K9xeFo1g/YPMguMUD69YpwB4Nyi6W/5wn706xIInJFg= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 h1:/2Cb3SK3xVOQA7Xfr5nCWCo5H3UiNINtsVvVdk8sQqA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0/go.mod h1:neYVaeKr5eT7BzwULuG2YbLhzWZ22lpjKdCybR7AXrQ= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 h1:9/aKwwus0TQxppPXFmf010DFrE+ssSbzroLVYINA+xE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29/go.mod h1:Dip3sIGv485+xerzVv24emnjX5Sg88utCL8fwGmCeWg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 h1:y+8n9AGDjikyXoMBTRaHHHSaFEB8267ykmvyPodJfys= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30/go.mod h1:LUBAO3zNXQjoONBKn/kR1y0Q4cj/D02Ts0uHYjcCQLM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 h1:b/Vn141DBuLVgXbhRWIrl9g+ww7G+ScV5SzniWR13jQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23/go.mod h1:mr6c4cHC+S/MMkrjtSlG4QA36kOznDep+0fga5L/fGQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 h1:IVx9L7YFhpPq0tTnGo8u8TpluFu7nAn9X3sUDMb11c0= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30/go.mod h1:vsbq62AOBwQ1LJ/GWKFxX8beUEYeRp/Agitrxee2/qM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 h1:QoOybhwRfciWUBbZ0gp9S7XaDnCuSTeK/fySB99V1ls= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23/go.mod h1:9uPh+Hrz2Vn6oMnQYiUi/zbh3ovbnQk19YKINkQny44= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 h1:r+Kv+SEJquhAZXaJ7G4u44cIwXV3f8K+N482NNAzJZA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24/go.mod h1:gAuCezX/gob6BSMbItsSlMb6WZGV7K2+fWOvk8xBSto= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 h1:hf+Vhp5WtTdcSdE+yEcUz8L73sAzN0R+0jQv+Z51/mI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31/go.mod h1:5zUjguZfG5qjhG9/wqmuyHRyUftl2B5Cp6NNxNC6kRA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 h1:c5qGfdbCHav6viBwiyDns3OXqhqAbGjfIB4uVu2ayhk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24/go.mod h1:HMA4FZG6fyib+NDo5bpIxX1EhYjrAOveZJY2YR0xrNE= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6 h1:VjvQw/1Qf/rhDSl+NNOeybSpdPRjBfH60//5vzveVsY= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6/go.mod h1:CJcdJtrO6ulXfI8l2DotKWmJShhXHCEcd9Wibyx3kC0= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 h1:qJdM48OOLl1FBSzI7ZrA1ZfLwOyCYqkXV5lko1hYDBw= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.4/go.mod h1:jtLIhd+V+lft6ktxpItycqHqiVXrPIRjWIsFIlzMriw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 h1:YRkWXQveFb0tFC0TLktmmhGsOcCgLwvq88MC2al47AA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4/go.mod h1:zVwRrfdSmbRZWkUkWjOItY7SOalnFnq/Yg2LVPqDjwc= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.5 h1:L1600eLr0YvTT7gNh3Ni24yGI7NSHkq9Gp62vijPRCs= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.5/go.mod h1:1mKZHLLpDMHTNSYPJ7qrcnCQdHCWsNQaT0xRvq2u80s= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 h1:bdKIX6SVF3nc3xJFw6Nf0igzS6Ff/louGq8Z6VP/3Hs= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.5/go.mod h1:vuWiaDB30M/QTC+lI3Wj6S/zb7tpUK2MSYgy3Guh2L0= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 h1:xLPZMyuZ4GuqRCIec/zWuIhRFPXh2UOJdLXBSi64ZWQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5/go.mod h1:QjxpHmCwAg0ESGtPQnLIVp7SedTOBMYy+Slr3IfMKeI= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 h1:rIFn5J3yDoeuKCE9sESXqM5POTAhOP1du3bv/qTL+tE= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.6/go.mod h1:48WJ9l3dwP0GSHWGc5sFGGlCkuA82Mc2xnw+T6Q8aDw= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From 794a68238db95966ce9273398aed81a2e5775926 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Mar 2023 08:54:23 +0100 Subject: [PATCH 0841/2268] chore(deps): Bump actions/setup-go from 3 to 4 (#377) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cross-compile.yaml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cross-compile.yaml b/.github/workflows/cross-compile.yaml index 6b96e9f56..e9651b820 100644 --- a/.github/workflows/cross-compile.yaml +++ b/.github/workflows/cross-compile.yaml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: go-version: '1.19' - uses: actions/cache@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b18a49db0..5289b22a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: - name: Fetch all tags run: git fetch --force --tags - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: 1.19 - name: Setup QEMU diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5e3e4cc83..e23e52c2b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: go-version: '1.19' - uses: actions/cache@v3 @@ -56,7 +56,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: go-version: '1.19' - uses: actions/cache@v3 From fcbe7896975882d1d36ad17b0291e40416dba664 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Mar 2023 08:55:06 +0100 Subject: [PATCH 0842/2268] chore(deps): Bump github.com/imdario/mergo from 0.3.13 to 0.3.14 (#379) Bumps [github.com/imdario/mergo](https://github.com/imdario/mergo) from 0.3.13 to 0.3.14. - [Release notes](https://github.com/imdario/mergo/releases) - [Commits](https://github.com/imdario/mergo/compare/v0.3.13...v0.3.14) --- updated-dependencies: - dependency-name: github.com/imdario/mergo dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 0496161c6..52010cc19 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/google/go-containerregistry v0.13.0 github.com/hashicorp/vault/api v1.9.0 github.com/hexops/gotextdiff v1.0.3 - github.com/imdario/mergo v0.3.13 + github.com/imdario/mergo v0.3.14 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 diff --git a/go.sum b/go.sum index d11adf871..f29c11df0 100644 --- a/go.sum +++ b/go.sum @@ -497,8 +497,9 @@ github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq 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.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.14 h1:fOqeC1+nCuuk6PKQdg9YmosXX7Y7mHX6R/0ZldI9iHo= +github.com/imdario/mergo v0.3.14/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= From eff52e78069417dba7ddf70813da7c9cd08faa53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Mar 2023 08:59:11 +0100 Subject: [PATCH 0843/2268] chore(deps): Bump github.com/go-git/go-git/v5 from 5.6.0 to 5.6.1 (#380) Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.6.0 to 5.6.1. - [Release notes](https://github.com/go-git/go-git/releases) - [Commits](https://github.com/go-git/go-git/compare/v5.6.0...v5.6.1) --- updated-dependencies: - dependency-name: github.com/go-git/go-git/v5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 52010cc19..73986b291 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.17.6 github.com/aws/aws-sdk-go-v2/config v1.18.18 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6 - github.com/go-git/go-git/v5 v5.6.0 + github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 @@ -90,8 +90,8 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect - github.com/acomagu/bufpipe v1.0.3 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect + github.com/acomagu/bufpipe v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.17 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 // indirect diff --git a/go.sum b/go.sum index f29c11df0..de7843866 100644 --- a/go.sum +++ b/go.sum @@ -91,14 +91,16 @@ github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2B github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= +github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= @@ -277,13 +279,12 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.4.0/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.6.0 h1:JvBdYfcttd+0kdpuWO7KTu0FYgCf5W0t5VwkWGobaa4= -github.com/go-git/go-git/v5 v5.6.0/go.mod h1:6nmJ0tJ3N4noMV1Omv7rC5FG3/o8Cm51TB4CJp7mRmE= +github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= +github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8= 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= @@ -890,6 +891,7 @@ golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -979,6 +981,8 @@ golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1081,6 +1085,7 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1090,6 +1095,7 @@ golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1103,6 +1109,7 @@ 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From b246b80498009865eaebf21d8811689d4737a274 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 21 Mar 2023 10:05:27 +0100 Subject: [PATCH 0844/2268] fix: Switch back to official go-git version --- cmd/kluctl/commands/cmd_helm_update.go | 2 +- e2e/helm_test.go | 4 ++-- e2e/test-utils/project.go | 2 +- go.mod | 2 -- pkg/git/auth/auth_provider.go | 4 ++-- pkg/git/auth/git_credentials_file.go | 2 +- pkg/git/auth/list_auth_provider.go | 4 ++-- pkg/git/list_refs.go | 10 +++++----- pkg/git/mirrored_repo.go | 8 ++++---- pkg/git/poor_mans_clone.go | 4 ++-- pkg/git/ssh-pool/hostport.go | 2 +- pkg/git/test_git_server.go | 2 +- pkg/git/utils.go | 2 +- pkg/vars/vars_loader_test.go | 4 ++-- 14 files changed, 25 insertions(+), 27 deletions(-) diff --git a/cmd/kluctl/commands/cmd_helm_update.go b/cmd/kluctl/commands/cmd_helm_update.go index 82ce4c2f8..34b75ccf8 100644 --- a/cmd/kluctl/commands/cmd_helm_update.go +++ b/cmd/kluctl/commands/cmd_helm_update.go @@ -3,8 +3,8 @@ package commands import ( "context" "fmt" - "github.com/fluxcd/go-git/v5/plumbing/format/index" "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/format/index" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" git2 "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/helm" diff --git a/e2e/helm_test.go b/e2e/helm_test.go index 848645507..a3c8ae976 100644 --- a/e2e/helm_test.go +++ b/e2e/helm_test.go @@ -2,8 +2,8 @@ package e2e import ( "fmt" - "github.com/fluxcd/go-git/v5" - "github.com/fluxcd/go-git/v5/plumbing/object" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index dc1c5daee..ae402a1b9 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "fmt" - "github.com/fluxcd/go-git/v5" + "github.com/go-git/go-git/v5" "github.com/huandu/xstrings" "github.com/jinzhu/copier" "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" diff --git a/go.mod b/go.mod index 73986b291..d51f94ac4 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,6 @@ require ( github.com/bitnami-labs/sealed-secrets v0.19.5 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible - // See https://github.com/fluxcd/pkg/issues/397, especially in regard to skeema/knownhosts breaking compatibility - github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df github.com/fluxcd/pkg/kustomize v0.13.2 github.com/go-playground/validator/v10 v10.11.2 github.com/gobwas/glob v0.2.3 // indirect diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index 18809899a..ab2607dba 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -3,8 +3,8 @@ package auth import ( "context" "fmt" - "github.com/fluxcd/go-git/v5/plumbing/transport" - ssh2 "github.com/fluxcd/go-git/v5/plumbing/transport/ssh" + "github.com/go-git/go-git/v5/plumbing/transport" + ssh2 "github.com/go-git/go-git/v5/plumbing/transport/ssh" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" "golang.org/x/crypto/ssh" diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index 3f66d4b45..bf754be36 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -3,7 +3,7 @@ package auth import ( "bufio" "context" - "github.com/fluxcd/go-git/v5/plumbing/transport/http" + "github.com/go-git/go-git/v5/plumbing/transport/http" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/git-url/giturls" "github.com/kluctl/kluctl/v2/pkg/git/messages" diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index c6876027e..0e2d2b453 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -2,8 +2,8 @@ package auth import ( "context" - "github.com/fluxcd/go-git/v5/plumbing/transport/http" - "github.com/fluxcd/go-git/v5/plumbing/transport/ssh" + "github.com/go-git/go-git/v5/plumbing/transport/http" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" ssh2 "golang.org/x/crypto/ssh" diff --git a/pkg/git/list_refs.go b/pkg/git/list_refs.go index a4a8bb7d5..735234d6b 100644 --- a/pkg/git/list_refs.go +++ b/pkg/git/list_refs.go @@ -4,11 +4,11 @@ import ( "bytes" "context" "fmt" - "github.com/fluxcd/go-git/v5" - "github.com/fluxcd/go-git/v5/config" - "github.com/fluxcd/go-git/v5/plumbing" - "github.com/fluxcd/go-git/v5/plumbing/protocol/packp" - "github.com/fluxcd/go-git/v5/storage/memory" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/protocol/packp" + "github.com/go-git/go-git/v5/storage/memory" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index e1642cdb9..10e2e805c 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -5,10 +5,10 @@ import ( "crypto/sha256" "encoding/hex" "fmt" - "github.com/fluxcd/go-git/v5" - "github.com/fluxcd/go-git/v5/config" - "github.com/fluxcd/go-git/v5/plumbing" - "github.com/fluxcd/go-git/v5/plumbing/object" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" _ "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" diff --git a/pkg/git/poor_mans_clone.go b/pkg/git/poor_mans_clone.go index 9804cab66..975112564 100644 --- a/pkg/git/poor_mans_clone.go +++ b/pkg/git/poor_mans_clone.go @@ -1,8 +1,8 @@ package git import ( - "github.com/fluxcd/go-git/v5" - "github.com/fluxcd/go-git/v5/config" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" cp "github.com/otiai10/copy" "os" "path/filepath" diff --git a/pkg/git/ssh-pool/hostport.go b/pkg/git/ssh-pool/hostport.go index d4e314ae0..4ca5c5cc7 100644 --- a/pkg/git/ssh-pool/hostport.go +++ b/pkg/git/ssh-pool/hostport.go @@ -2,7 +2,7 @@ package ssh_pool import ( "fmt" - "github.com/fluxcd/go-git/v5/plumbing/transport/ssh" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" "strconv" ) diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index ed737c2ff..3e87f6531 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -11,7 +11,7 @@ import ( "reflect" "testing" - "github.com/fluxcd/go-git/v5" + "github.com/go-git/go-git/v5" "github.com/jinzhu/copier" http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" "gopkg.in/yaml.v3" diff --git a/pkg/git/utils.go b/pkg/git/utils.go index 121bca567..f007fa3ee 100644 --- a/pkg/git/utils.go +++ b/pkg/git/utils.go @@ -2,7 +2,7 @@ package git import ( "fmt" - "github.com/fluxcd/go-git/v5" + "github.com/go-git/go-git/v5" "os" "path/filepath" ) diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 1764788de..64b8ba404 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -12,8 +12,8 @@ import ( "strings" "testing" - git2 "github.com/fluxcd/go-git/v5" - "github.com/fluxcd/go-git/v5/plumbing" + git2 "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" From 86335fd767854e3f94ec1c3f98a48d177a6bdb10 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 21 Mar 2023 10:05:49 +0100 Subject: [PATCH 0845/2268] fix: Properly handle HostKeyAlgorithms calls --- pkg/git/auth/ssh_known_hosts.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/git/auth/ssh_known_hosts.go b/pkg/git/auth/ssh_known_hosts.go index ec375b1f2..4543bad6c 100644 --- a/pkg/git/auth/ssh_known_hosts.go +++ b/pkg/git/auth/ssh_known_hosts.go @@ -1,10 +1,12 @@ package auth import ( + "errors" "fmt" "github.com/kluctl/kluctl/v2/pkg/git/auth/goph" "github.com/kluctl/kluctl/v2/pkg/git/messages" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/knownhosts" "net" "os" "path/filepath" @@ -72,6 +74,24 @@ func verifyHost(messageCallbacks messages.MessageCallbacks, host string, remote files = append(files, tmpFile.Name()) } + if key.Type() == "fake-public-key" { + // this makes us compatible to knownhosts.HostKeyAlgorithms which calls us with a fake public key and expects us + // to return all known keys + var keyErr knownhosts.KeyError + for _, f := range files { + hostFound, err := goph.CheckKnownHost(host, remote, key, f) + if hostFound && err == nil { + return fmt.Errorf("fake-public-key was unexpectadly found") + } + var tmpKeyErr *knownhosts.KeyError + if !errors.As(err, &tmpKeyErr) { + return fmt.Errorf("CheckKnownHost did not return expected KeyError: %v", err) + } + keyErr.Want = append(keyErr.Want, tmpKeyErr.Want...) + } + return &keyErr + } + for _, f := range files { hostFound, err := goph.CheckKnownHost(host, remote, key, f) if hostFound && err == nil { From 8b6df3beeee0c983f641ba23b28958e1404fab48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Mar 2023 10:12:29 +0100 Subject: [PATCH 0846/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/service/secretsmanager (#381) Bumps [github.com/aws/aws-sdk-go-v2/service/secretsmanager](https://github.com/aws/aws-sdk-go-v2) from 1.18.6 to 1.19.0. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.6...service/s3/v1.19.0) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/secretsmanager dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 73986b291..30f18acf5 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,7 @@ require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.17.6 github.com/aws/aws-sdk-go-v2/config v1.18.18 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0 github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.3 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index de7843866..286d2b2e4 100644 --- a/go.sum +++ b/go.sum @@ -116,7 +116,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.17.5/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.17.6 h1:Y773UK7OBqhzi5VDXMi1zVGsoj+CVHs2eaC2bDsLwi0= github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.18.18 h1:/ePABXvXl3ESlzUGnkkvvNnRFw3Gh13dyqaq0Qo3JcU= @@ -126,11 +125,9 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.17/go.mod h1:K9xeFo1g/YPMguMUD69Y github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 h1:/2Cb3SK3xVOQA7Xfr5nCWCo5H3UiNINtsVvVdk8sQqA= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0/go.mod h1:neYVaeKr5eT7BzwULuG2YbLhzWZ22lpjKdCybR7AXrQ= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29/go.mod h1:Dip3sIGv485+xerzVv24emnjX5Sg88utCL8fwGmCeWg= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 h1:y+8n9AGDjikyXoMBTRaHHHSaFEB8267ykmvyPodJfys= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30/go.mod h1:LUBAO3zNXQjoONBKn/kR1y0Q4cj/D02Ts0uHYjcCQLM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23/go.mod h1:mr6c4cHC+S/MMkrjtSlG4QA36kOznDep+0fga5L/fGQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 h1:r+Kv+SEJquhAZXaJ7G4u44cIwXV3f8K+N482NNAzJZA= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24/go.mod h1:gAuCezX/gob6BSMbItsSlMb6WZGV7K2+fWOvk8xBSto= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 h1:hf+Vhp5WtTdcSdE+yEcUz8L73sAzN0R+0jQv+Z51/mI= @@ -139,8 +136,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 h1:c5qGfdbCH github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24/go.mod h1:HMA4FZG6fyib+NDo5bpIxX1EhYjrAOveZJY2YR0xrNE= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6 h1:VjvQw/1Qf/rhDSl+NNOeybSpdPRjBfH60//5vzveVsY= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6/go.mod h1:CJcdJtrO6ulXfI8l2DotKWmJShhXHCEcd9Wibyx3kC0= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0 h1:B4LvuBxrxh2WXakqwJL22EPAWgqGGK9/E4YQV/IIkYo= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0/go.mod h1:XF4Gbmcn6V9xIIm6lhwtyX1NXConNJ8x6yizt2Ejx/0= github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 h1:bdKIX6SVF3nc3xJFw6Nf0igzS6Ff/louGq8Z6VP/3Hs= github.com/aws/aws-sdk-go-v2/service/sso v1.12.5/go.mod h1:vuWiaDB30M/QTC+lI3Wj6S/zb7tpUK2MSYgy3Guh2L0= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 h1:xLPZMyuZ4GuqRCIec/zWuIhRFPXh2UOJdLXBSi64ZWQ= From cdde647e47e1578288c0634f5e9a116988da77b4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 24 Mar 2023 14:52:12 +0100 Subject: [PATCH 0847/2268] fix: Fix .git-credentials checking/loading/matching (#386) * fix: Fix checking of .git-credentials existence * fix: Fix matching of credentials in .git-credentials --- pkg/git/auth/git_credentials_file.go | 44 +++++++++++++++++++++------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index bf754be36..cd9231154 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -7,6 +7,7 @@ import ( git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/git-url/giturls" "github.com/kluctl/kluctl/v2/pkg/git/messages" + "net/url" "os" "path/filepath" ) @@ -19,6 +20,7 @@ func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl g if gitUrl.Scheme != "http" && gitUrl.Scheme != "https" { return AuthMethodAndCA{} } + a.MessageCallbacks.Trace("GitCredentialsFileAuthProvider: BuildAuth for %s", gitUrl.String()) home, err := os.UserHomeDir() if err != nil { @@ -46,10 +48,12 @@ func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl g func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl git_url.GitUrl, gitCredentialsPath string) *AuthMethodAndCA { st, err := os.Stat(gitCredentialsPath) - if err != nil || !st.Mode().IsDir() { + if err != nil || st.Mode().IsDir() { return nil } + a.MessageCallbacks.Trace("GitCredentialsFileAuthProvider: trying file %s", gitCredentialsPath) + f, err := os.Open(gitCredentialsPath) if err != nil { a.MessageCallbacks.Warning("Failed to open %s: %v", gitCredentialsPath, err) @@ -59,25 +63,43 @@ func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl git_url.GitUrl, git s := bufio.NewScanner(f) for s.Scan() { + if s.Text() == "" || s.Text()[0] == '#' { + continue + } u, err := giturls.Parse(s.Text()) if err != nil { continue } + // create temporary url without password, which can be printed + tmpU := *u + tmpU.User = url.User(u.User.Username()) + + if u.User == nil || u.User.Username() == "" { + a.MessageCallbacks.Trace("GitCredentialsFileAuthProvider: ignoring %s", tmpU.String()) + continue + } + a.MessageCallbacks.Trace("GitCredentialsFileAuthProvider: trying %s", tmpU.String()) + if u.Host != gitUrl.Host { + a.MessageCallbacks.Trace("GitCredentialsFileAuthProvider: host does not match") continue } - if u.User == nil { + if gitUrl.User != nil && gitUrl.User.Username() != u.User.Username() { + a.MessageCallbacks.Trace("GitCredentialsFileAuthProvider: user does not match") continue } - if password, ok := u.User.Password(); ok { - if u.User.Username() != "" { - return &AuthMethodAndCA{ - AuthMethod: &http.BasicAuth{ - Username: u.User.Username(), - Password: password, - }, - } - } + password, ok := u.User.Password() + if !ok { + a.MessageCallbacks.Trace("GitCredentialsFileAuthProvider: no password provided") + continue + } + + a.MessageCallbacks.Trace("GitCredentialsFileAuthProvider: matched") + return &AuthMethodAndCA{ + AuthMethod: &http.BasicAuth{ + Username: u.User.Username(), + Password: password, + }, } } return nil From 01c1318075a29a600c9d972398a351f1afc5c6d5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 29 Mar 2023 21:09:42 +0200 Subject: [PATCH 0848/2268] fix: Honor ignore-conflicts even if force-apply is active for a field (#387) --- pkg/diff/managed_fields.go | 37 ++++++++++++++++-------------- pkg/diff/managed_fields_test.go | 40 +++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/pkg/diff/managed_fields.go b/pkg/diff/managed_fields.go index e2b4e6797..9c7f11cc8 100644 --- a/pkg/diff/managed_fields.go +++ b/pkg/diff/managed_fields.go @@ -229,9 +229,24 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr panic(fmt.Sprintf("field '%s' not found in remote object...which can't be!", cause.Field)) } - overwrite := true - ignoreConflict := ignoreConflictsAll - if !forceApplyAll { + ignoreConflict := false + if ignoreConflictsAll { + ignoreConflict = true + } else if _, ok := ignoreConflictFields[localKeyPath.ToJsonPath()]; ok { + ignoreConflict = true + } else if _, ok := ignoreConflictFields[remoteKeyPath.ToJsonPath()]; ok { + ignoreConflict = true + } + + overwrite := false + if !ignoreConflict { + if forceApplyAll { + overwrite = true + } else if _, ok := forceApplyFields[localKeyPath.ToJsonPath()]; ok { + overwrite = true + } else if _, ok := forceApplyFields[remoteKeyPath.ToJsonPath()]; ok { + overwrite = true + } for _, mfn := range mf.managers { found := false for _, oa := range overwriteAllowedManagers { @@ -240,23 +255,11 @@ func ResolveFieldManagerConflicts(local *uo.UnstructuredObject, remote *uo.Unstr break } } - if !found { - overwrite = false + if found { + overwrite = true break } } - if _, ok := forceApplyFields[localKeyPath.ToJsonPath()]; ok { - overwrite = true - } - if _, ok := forceApplyFields[remoteKeyPath.ToJsonPath()]; ok { - overwrite = true - } - } - if _, ok := ignoreConflictFields[localKeyPath.ToJsonPath()]; ok { - ignoreConflict = true - } - if _, ok := ignoreConflictFields[remoteKeyPath.ToJsonPath()]; ok { - ignoreConflict = true } if !overwrite { diff --git a/pkg/diff/managed_fields_test.go b/pkg/diff/managed_fields_test.go index 3c6fcd96e..1cae73f91 100644 --- a/pkg/diff/managed_fields_test.go +++ b/pkg/diff/managed_fields_test.go @@ -164,6 +164,42 @@ func TestResolveFieldManagerConflicts(t *testing.T) { lost: buildLost("d1"), anns: buildAnnotations("kluctl.io/ignore-conflicts-field-123", "data.d3"), }, + { + name: "force-apply-object-ignore-conflicts", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d2", "x", "m1"}), + lost: buildLost(), + anns: buildAnnotations("kluctl.io/force-apply", "true", "kluctl.io/ignore-conflicts", "true"), + }, + { + name: "force-apply-object-ignore-conflicts-field", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + lost: buildLost(), + anns: buildAnnotations("kluctl.io/force-apply", "true", "kluctl.io/ignore-conflicts-field", "data.d1"), + }, + { + name: "force-apply-field-ignore-conflicts", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d2", "x", "m1"}), + lost: buildLost(), + anns: buildAnnotations("kluctl.io/force-apply-field", "data.d1", "kluctl.io/ignore-conflicts", "true"), + }, + { + name: "force-apply-field-ignore-conflicts-field", + remote: buildConfigMap(fieldInfo{"d1", "v1", "c1"}, fieldInfo{"d2", "v2", "m1"}, fieldInfo{"d3", "v3", "c1"}), + local: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}, fieldInfo{"d3", "x", "m1"}), + status: buildConflicts("d1", "d3"), + result: buildConfigMap(fieldInfo{"d1", "x", "m1"}, fieldInfo{"d2", "x", "m1"}), + lost: buildLost(), + anns: buildAnnotations("kluctl.io/force-apply-field", "data.d1", "kluctl.io/ignore-conflicts-field", "data.d3"), + }, } for _, tc := range tests { @@ -185,8 +221,8 @@ func TestResolveFieldManagerConflicts(t *testing.T) { r, l, err := ResolveFieldManagerConflicts(tc.local, tc.remote, tc.status) assert.NoError(t, err) - assert.Equal(t, r, tc.result) - assert.Equal(t, l, tc.lost) + assert.Equal(t, tc.result, r) + assert.Equal(t, tc.lost, l) }) } } From 4e2dff288202e81f8195313327f9927d80e79ba9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 10:00:12 +0200 Subject: [PATCH 0849/2268] chore(deps): Bump k8s.io/client-go from 0.26.2 to 0.26.3 (#383) Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.26.2 to 0.26.3. - [Release notes](https://github.com/kubernetes/client-go/releases) - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.26.2...v0.26.3) --- updated-dependencies: - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 18 ++++++------------ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index aef2530c4..176c8ebf3 100644 --- a/go.mod +++ b/go.mod @@ -44,10 +44,10 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.11.2 - k8s.io/api v0.26.2 + k8s.io/api v0.26.3 k8s.io/apiextensions-apiserver v0.26.2 - k8s.io/apimachinery v0.26.2 - k8s.io/client-go v0.26.2 + k8s.io/apimachinery v0.26.3 + k8s.io/client-go v0.26.3 k8s.io/klog/v2 v2.90.1 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 diff --git a/go.sum b/go.sum index 286d2b2e4..2acf695ec 100644 --- a/go.sum +++ b/go.sum @@ -91,14 +91,12 @@ github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2B github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -256,8 +254,6 @@ 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/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df h1:2BHXJp1PwX7D47Q2oaKDekn+BZVZCmxeCWNi+FyownE= -github.com/fluxcd/go-git/v5 v5.0.0-20221206140629-ec778c2c37df/go.mod h1:raWgfUV7lDQVXp4QXUaeNNJkRVKz97UQuF+0kdY7Vmo= github.com/fluxcd/pkg/apis/kustomize v0.8.0 h1:A6aLolxPV2Sll44SOHiX96lbXXmRZmS5BoEerkRHrfM= github.com/fluxcd/pkg/apis/kustomize v0.8.0/go.mod h1:9DPEVSfVIkiC2H3Dk6Ght4YJkswhYIaufXla4tB5Y84= github.com/fluxcd/pkg/kustomize v0.13.2 h1:isA9yi+m7sSIxdTrFR1U7+LyS2BraG07ZkKLHw3bnGo= @@ -679,7 +675,6 @@ github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= @@ -813,7 +808,6 @@ github.com/urfave/cli v1.22.7/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1340,18 +1334,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= -k8s.io/api v0.26.2/go.mod h1:1kjMQsFE+QHPfskEcVNgL3+Hp88B80uj0QtSOlj8itU= +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.2 h1:/yTG2B9jGY2Q70iGskMf41qTLhL9XeNN2KhI0uDgwko= k8s.io/apiextensions-apiserver v0.26.2/go.mod h1:Y7UPgch8nph8mGCuVk0SK83LnS8Esf3n6fUBgew8SH8= -k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= -k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= +k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= +k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= k8s.io/apiserver v0.26.2 h1:Pk8lmX4G14hYqJd1poHGC08G03nIHVqdJMR0SD3IH3o= k8s.io/apiserver v0.26.2/go.mod h1:GHcozwXgXsPuOJ28EnQ/jXEM9QeG6HT22YxSNmpYNh8= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= -k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= -k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU= +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/component-base v0.26.2 h1:IfWgCGUDzrD6wLLgXEstJKYZKAFS2kO+rBRi0p3LqcI= k8s.io/component-base v0.26.2/go.mod h1:DxbuIe9M3IZPRxPIzhch2m1eT7uFrSBJUBuVCQEBivs= k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= From 7eb0d5123e7622b8c14d7cf0b59f882864af76a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 10:00:47 +0200 Subject: [PATCH 0850/2268] chore(deps): Bump github.com/ohler55/ojg from 1.17.5 to 1.18.4 (#392) Bumps [github.com/ohler55/ojg](https://github.com/ohler55/ojg) from 1.17.5 to 1.18.4. - [Release notes](https://github.com/ohler55/ojg/releases) - [Changelog](https://github.com/ohler55/ojg/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/ojg/compare/v1.17.5...v1.18.4) --- updated-dependencies: - dependency-name: github.com/ohler55/ojg dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 176c8ebf3..5b4218bf7 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.17.5 + github.com/ohler55/ojg v1.18.4 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.9.0 diff --git a/go.sum b/go.sum index 2acf695ec..cc65b2183 100644 --- a/go.sum +++ b/go.sum @@ -647,8 +647,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/nelsam/hel/v2 v2.3.3/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/ohler55/ojg v1.17.5 h1:SY6/cdhVzsLinNFIBRNSWhJgihvEWco5Y0TJe46XJ1Y= -github.com/ohler55/ojg v1.17.5/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= +github.com/ohler55/ojg v1.18.4 h1:FXHUddnBgrx5RwlkTDJnXBL5s3DNOMlZfv4K27nNNtM= +github.com/ohler55/ojg v1.18.4/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= From 6effa40de3a86db9141539f365777928aada95ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:17:04 +0200 Subject: [PATCH 0851/2268] chore(deps): Bump k8s.io/apiextensions-apiserver from 0.26.2 to 0.26.3 (#385) Bumps [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) from 0.26.2 to 0.26.3. - [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases) - [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.26.2...v0.26.3) --- updated-dependencies: - dependency-name: k8s.io/apiextensions-apiserver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 5b4218bf7..66e385b36 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.11.2 k8s.io/api v0.26.3 - k8s.io/apiextensions-apiserver v0.26.2 + k8s.io/apiextensions-apiserver v0.26.3 k8s.io/apimachinery v0.26.3 k8s.io/client-go v0.26.3 k8s.io/klog/v2 v2.90.1 @@ -234,9 +234,9 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gotest.tools/v3 v3.2.0 // indirect - k8s.io/apiserver v0.26.2 // indirect + k8s.io/apiserver v0.26.3 // indirect k8s.io/cli-runtime v0.26.0 // indirect - k8s.io/component-base v0.26.2 // indirect + k8s.io/component-base v0.26.3 // indirect k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 // indirect k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect diff --git a/go.sum b/go.sum index cc65b2183..d11a72886 100644 --- a/go.sum +++ b/go.sum @@ -1336,18 +1336,18 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 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.2 h1:/yTG2B9jGY2Q70iGskMf41qTLhL9XeNN2KhI0uDgwko= -k8s.io/apiextensions-apiserver v0.26.2/go.mod h1:Y7UPgch8nph8mGCuVk0SK83LnS8Esf3n6fUBgew8SH8= +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.2 h1:Pk8lmX4G14hYqJd1poHGC08G03nIHVqdJMR0SD3IH3o= -k8s.io/apiserver v0.26.2/go.mod h1:GHcozwXgXsPuOJ28EnQ/jXEM9QeG6HT22YxSNmpYNh8= +k8s.io/apiserver v0.26.3 h1:blBpv+yOiozkPH2aqClhJmJY+rp53Tgfac4SKPDJnU4= +k8s.io/apiserver v0.26.3/go.mod h1:CJe/VoQNcXdhm67EvaVjYXxR3QyfwpceKPuPaeLibTA= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= 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/component-base v0.26.2 h1:IfWgCGUDzrD6wLLgXEstJKYZKAFS2kO+rBRi0p3LqcI= -k8s.io/component-base v0.26.2/go.mod h1:DxbuIe9M3IZPRxPIzhch2m1eT7uFrSBJUBuVCQEBivs= +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/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 h1:8cNCQs+WqqnSpZ7y0LMQPKD+RZUHU17VqLPMW3qxnxc= From 6f4352afd5a1823255443859420ab404e5f067e6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 12 Apr 2023 08:59:16 +0200 Subject: [PATCH 0852/2268] fix: Pin markdown-link-check to 3.10.3 until the latest version gets fixed (#400) --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 69a9805ac..d8f9ce4aa 100644 --- a/Makefile +++ b/Makefile @@ -115,8 +115,9 @@ endif replace-commands-help: ## Replace commands help in docs $(GOCMD) run ./internal/replace-commands-help --docs-dir ./docs/reference/commands +MARKDOWN_LINK_CHECK_VERSION=3.10.3 # warning, 3.11.x is broken markdown-link-check: ## Check markdown files for dead links - find . -name '*.md' | xargs docker run -v ${PWD}:/tmp:ro --rm -i -w /tmp ghcr.io/tcort/markdown-link-check:stable + find . -name '*.md' | xargs docker run -v ${PWD}:/tmp:ro --rm -i -w /tmp ghcr.io/tcort/markdown-link-check:$(MARKDOWN_LINK_CHECK_VERSION) ## Release: version: ## Write next version into version file From a8452df46957c1d691ae6e703eccd1d3ef710e7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 23:53:57 +0200 Subject: [PATCH 0853/2268] chore(deps): Bump golang.org/x/text from 0.8.0 to 0.9.0 (#397) Bumps [golang.org/x/text](https://github.com/golang/text) from 0.8.0 to 0.9.0. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 66e385b36..5bd149ad5 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( golang.org/x/sync v0.1.0 golang.org/x/sys v0.6.0 golang.org/x/term v0.6.0 - golang.org/x/text v0.8.0 + golang.org/x/text v0.9.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.11.2 diff --git a/go.sum b/go.sum index d11a72886..1802c6399 100644 --- a/go.sum +++ b/go.sum @@ -1101,8 +1101,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +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= From 780d1322338f7906eab9741101f61b6214f855e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 23:54:19 +0200 Subject: [PATCH 0854/2268] chore(deps): Bump github.com/otiai10/copy from 1.9.0 to 1.10.0 (#398) Bumps [github.com/otiai10/copy](https://github.com/otiai10/copy) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/otiai10/copy/releases) - [Commits](https://github.com/otiai10/copy/compare/v1.9.0...v1.10.0) --- updated-dependencies: - dependency-name: github.com/otiai10/copy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 5bd149ad5..900c9155a 100644 --- a/go.mod +++ b/go.mod @@ -63,7 +63,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 github.com/onsi/gomega v1.27.4 - github.com/otiai10/copy v1.9.0 + github.com/otiai10/copy v1.10.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.5 sigs.k8s.io/kustomize/api v0.12.1 diff --git a/go.sum b/go.sum index 1802c6399..466f95ec2 100644 --- a/go.sum +++ b/go.sum @@ -660,13 +660,9 @@ github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7X github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= -github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4= -github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.4.0 h1:umwcf7gbpEwf7WFzqmWwSv0CzbeMsae2u9ZvpP8j2q4= -github.com/otiai10/mint v1.4.0/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI= +github.com/otiai10/copy v1.10.0 h1:znyI7l134wNg/wDktoVQPxPkgvhDfGCYUasey+h0rDQ= +github.com/otiai10/copy v1.10.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= +github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= From 5148c8fdc8928f3bd9ee81e2a5ec8ad5a8b6b6a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 23:54:47 +0200 Subject: [PATCH 0855/2268] chore(deps): Bump k8s.io/apimachinery from 0.26.3 to 0.27.0 (#399) Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.26.3 to 0.27.0. - [Release notes](https://github.com/kubernetes/apimachinery/releases) - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.26.3...v0.27.0) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 900c9155a..e8e104afa 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( helm.sh/helm/v3 v3.11.2 k8s.io/api v0.26.3 k8s.io/apiextensions-apiserver v0.26.3 - k8s.io/apimachinery v0.26.3 + k8s.io/apimachinery v0.27.0 k8s.io/client-go v0.26.3 k8s.io/klog/v2 v2.90.1 sigs.k8s.io/kustomize/kyaml v0.13.9 @@ -237,9 +237,9 @@ require ( k8s.io/apiserver v0.26.3 // indirect k8s.io/cli-runtime v0.26.0 // indirect k8s.io/component-base v0.26.3 // indirect - k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 // indirect + k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // indirect k8s.io/kubectl v0.26.0 // indirect - k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect oras.land/oras-go v1.2.2 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect ) diff --git a/go.sum b/go.sum index 466f95ec2..1e9193c3d 100644 --- a/go.sum +++ b/go.sum @@ -228,7 +228,6 @@ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arX github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -406,7 +405,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -1334,8 +1333,8 @@ 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/apimachinery v0.27.0 h1:vEyy/PVMbPMCPutrssCVHCf0JNZ0Px+YqPi82K2ALlk= +k8s.io/apimachinery v0.27.0/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= k8s.io/apiserver v0.26.3 h1:blBpv+yOiozkPH2aqClhJmJY+rp53Tgfac4SKPDJnU4= k8s.io/apiserver v0.26.3/go.mod h1:CJe/VoQNcXdhm67EvaVjYXxR3QyfwpceKPuPaeLibTA= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= @@ -1346,12 +1345,12 @@ 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/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 h1:8cNCQs+WqqnSpZ7y0LMQPKD+RZUHU17VqLPMW3qxnxc= -k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596/go.mod h1:/BYxry62FuDzmI+i9B+X2pqfySRmSOW2ARmj5Zbqhj0= +k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a h1:gmovKNur38vgoWfGtP5QOGNOA7ki4n6qNYoFAgMlNvg= +k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From 1b833d259bba71d34bf3fee92ca18e7ba88ddc6c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 13 Apr 2023 14:11:44 +0200 Subject: [PATCH 0856/2268] feat: Allow to specify regex based path in ignoreForDiff --- .../deployments/annotations/all-resources.md | 6 +++ docs/reference/deployments/deployment-yml.md | 15 ++++++- pkg/deployment/utils/diff_utils.go | 12 +++++- pkg/diff/normalize.go | 41 ++++++++++++------- pkg/types/deployment.go | 19 ++++++--- pkg/utils/uo/jsonpath.go | 2 +- pkg/utils/uo/nested_fields.go | 29 +++++++++++++ 7 files changed, 99 insertions(+), 25 deletions(-) diff --git a/docs/reference/deployments/annotations/all-resources.md b/docs/reference/deployments/annotations/all-resources.md index 9340ebdbf..dacdc5b04 100644 --- a/docs/reference/deployments/annotations/all-resources.md +++ b/docs/reference/deployments/annotations/all-resources.md @@ -104,3 +104,9 @@ Specifies a [JSON Path](https://goessner.net/articles/JsonPath/) for fields that diffs. If more than one field needs to be specified, add `-xxx` to the annotation key, where `xxx` is an arbitrary number. + +### kluctl.io/ignore-diff-field-regex +Same as [kluctl.io/ignore-diff-field](#kluctlioignore-diff-field) but specifying a regular expressions instead of a +JSON Path. + +If more than one field needs to be specified, add `-xxx` to the annotation key, where `xxx` is an arbitrary number. diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index 013f60ef3..9d40ee88e 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -294,8 +294,19 @@ This will remove the `spec.replicas` field from every resource that matches the `group`, `kind`, `namespace` and `name` can be omitted, which results in all objects matching. `fieldPath` must be a valid [JSON Path](https://goessner.net/articles/JsonPath/). `fieldPath` may also be a list of JSON paths. -The JSON Path implementation used in kluctl has extended support for wildcards in field -names, allowing you to also specify paths like `metadata.labels.my-prefix-*`. +Using regex expressions instead of JSON Pathes is also supported: + +```yaml +deployments: + - ... + +ignoreForDiff: + - group: apps + kind: Deployment + namespace: my-namespace + name: my-deployment + fieldPathRegex: metadata.labels.my-label-.* +``` As an alternative, [annotations](./annotations/all-resources.md#control-diff-behavior) can be used to control diff behavior of individual resources. diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index 05f740a8f..e62f33e16 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -77,8 +77,16 @@ func (u *diffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, // did not apply? (e.g. in downscale command) return } else { - nao := diff.NormalizeObject(ao, ignoreForDiffs, lo) - nro := diff.NormalizeObject(ro, ignoreForDiffs, lo) + nao, err := diff.NormalizeObject(ao, ignoreForDiffs, lo) + if err != nil { + u.dew.AddError(lo.GetK8sRef(), err) + return + } + nro, err := diff.NormalizeObject(ro, ignoreForDiffs, lo) + if err != nil { + u.dew.AddError(lo.GetK8sRef(), err) + return + } changes, err := diff.Diff(nro, nao) if err != nil { u.dew.AddError(lo.GetK8sRef(), err) diff --git a/pkg/diff/normalize.go b/pkg/diff/normalize.go index 5123a347f..81e94fb4a 100644 --- a/pkg/diff/normalize.go +++ b/pkg/diff/normalize.go @@ -118,9 +118,10 @@ func normalizeMisc(o *uo.UnstructuredObject) { } var ignoreDiffFieldAnnotationRegex = regexp.MustCompile(`^kluctl.io/ignore-diff-field(-\d*)?$`) +var ignoreDiffFieldRegexAnnotationRegex = regexp.MustCompile(`^kluctl.io/ignore-diff-field-regex(-\d*)?$`) // NormalizeObject Performs some deterministic sorting and other normalizations to avoid ugly diffs due to order changes -func NormalizeObject(o_ *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreForDiffItemConfig, localObject *uo.UnstructuredObject) *uo.UnstructuredObject { +func NormalizeObject(o_ *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreForDiffItemConfig, localObject *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { gvk := o_.GetK8sGVK() name := o_.GetK8sName() ns := o_.GetK8sNamespace() @@ -138,6 +139,11 @@ func NormalizeObject(o_ *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreFo normalizeServiceAccount(o) } + if utils.ParseBoolOrFalse(localObject.GetK8sAnnotation("kluctl.io/ignore-diff")) { + // Return empty object so that diffs will always be empty + return &uo.UnstructuredObject{Object: map[string]interface{}{}}, nil + } + checkMatch := func(v string, m *string) bool { if v == "" || m == nil { return true @@ -145,6 +151,18 @@ func NormalizeObject(o_ *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreFo return v == *m } + ignoreForDiffs = append([]*types.IgnoreForDiffItemConfig{}, ignoreForDiffs...) + for _, v := range localObject.GetK8sAnnotationsWithRegex(ignoreDiffFieldAnnotationRegex) { + ignoreForDiffs = append(ignoreForDiffs, &types.IgnoreForDiffItemConfig{ + FieldPath: []string{v}, + }) + } + for _, v := range localObject.GetK8sAnnotationsWithRegex(ignoreDiffFieldRegexAnnotationRegex) { + ignoreForDiffs = append(ignoreForDiffs, &types.IgnoreForDiffItemConfig{ + FieldPathRegex: []string{v}, + }) + } + for _, ifd := range ignoreForDiffs { if !checkMatch(gvk.Group, ifd.Group) { continue @@ -162,24 +180,17 @@ func NormalizeObject(o_ *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreFo for _, fp := range ifd.FieldPath { jp, err := uo.NewMyJsonPath(fp) if err != nil { - continue + return nil, err } _ = jp.Del(o) } - } - - if utils.ParseBoolOrFalse(localObject.GetK8sAnnotation("kluctl.io/ignore-diff")) { - // Return empty object so that diffs will always be empty - return &uo.UnstructuredObject{Object: map[string]interface{}{}} - } - - for _, v := range localObject.GetK8sAnnotationsWithRegex(ignoreDiffFieldAnnotationRegex) { - j, err := uo.NewMyJsonPath(v) - if err != nil { - continue + for _, fp := range ifd.FieldPathRegex { + err := o.RemoveFieldsByPathRegex(fp) + if err != nil { + return nil, err + } } - _ = j.Del(o) } - return o + return o, nil } diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index df656745c..22e2a1914 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -77,11 +77,19 @@ func (s *SingleStringOrList) UnmarshalYAML(unmarshal func(interface{}) error) er } type IgnoreForDiffItemConfig struct { - FieldPath SingleStringOrList `yaml:"fieldPath" validate:"required"` - Group *string `yaml:"group,omitempty"` - Kind *string `yaml:"kind,omitempty"` - Name *string `yaml:"name,omitempty"` - Namespace *string `yaml:"namespace,omitempty"` + FieldPath SingleStringOrList `yaml:"fieldPath" validate:"required"` + FieldPathRegex SingleStringOrList `yaml:"fieldPathRegex,omitempty"` + Group *string `yaml:"group,omitempty"` + Kind *string `yaml:"kind,omitempty"` + Name *string `yaml:"name,omitempty"` + Namespace *string `yaml:"namespace,omitempty"` +} + +func ValidateIgnoreForDiffItemConfig(sl validator.StructLevel) { + s := sl.Current().Interface().(IgnoreForDiffItemConfig) + if len(s.FieldPath)+len(s.FieldPathRegex) == 0 { + sl.ReportError(s, "self", "self", "at least one of fieldPath or fieldPathRegex must be set", "") + } } type DeploymentProjectConfig struct { @@ -102,4 +110,5 @@ type DeploymentProjectConfig struct { func init() { yaml.Validator.RegisterStructValidation(ValidateDeploymentItemConfig, DeploymentItemConfig{}) yaml.Validator.RegisterStructValidation(ValidateDeleteObjectItemConfig, DeleteObjectItemConfig{}) + yaml.Validator.RegisterStructValidation(ValidateIgnoreForDiffItemConfig, IgnoreForDiffItemConfig{}) } diff --git a/pkg/utils/uo/jsonpath.go b/pkg/utils/uo/jsonpath.go index 5263f8639..44e375534 100644 --- a/pkg/utils/uo/jsonpath.go +++ b/pkg/utils/uo/jsonpath.go @@ -7,7 +7,7 @@ import ( "strings" ) -var isSimpleIdentifier = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]+$`) +var isSimpleIdentifier = regexp.MustCompile(`^[A-Za-z_-][A-Za-z0-9_-]+$`) type KeyPath []interface{} diff --git a/pkg/utils/uo/nested_fields.go b/pkg/utils/uo/nested_fields.go index 13560b767..6da20472a 100644 --- a/pkg/utils/uo/nested_fields.go +++ b/pkg/utils/uo/nested_fields.go @@ -3,6 +3,7 @@ package uo import ( "fmt" "reflect" + "regexp" ) func (uo *UnstructuredObject) GetNestedField(keys ...interface{}) (interface{}, bool, error) { @@ -77,6 +78,34 @@ func (uo *UnstructuredObject) RemoveNestedField(keys ...interface{}) error { return nil } +func (uo *UnstructuredObject) RemoveFieldsByPathRegex(path string) error { + r, err := regexp.Compile(path) + if err != nil { + return err + } + + var toDelete []KeyPath + err = uo.NewIterator().IterateLeafs(func(it *ObjectIterator) error { + jp := it.KeyPath().ToJsonPath() + if r.MatchString(jp) { + toDelete = append(toDelete, it.KeyPathCopy()) + } + return nil + }) + if err != nil { + return err + } + + for _, p := range toDelete { + err = uo.RemoveNestedField(p...) + if err != nil { + return err + } + } + + return nil +} + func (uo *UnstructuredObject) GetNestedString(keys ...interface{}) (string, bool, error) { v, found, err := uo.GetNestedField(keys...) if err != nil { From 6aa0469b830757f8c080864425192ffb297ff486 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 13 Apr 2023 14:12:15 +0200 Subject: [PATCH 0857/2268] fix: Fix --ignore-tags to actually work The JSON Path library used in Kluctl does not support extended wildcards. --- pkg/deployment/deployment_project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 8264170d2..49e3263ba 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -344,7 +344,7 @@ func (p *DeploymentProject) GetIgnoreForDiffs(ignoreTags, ignoreLabels, ignoreAn ret = append(ret, e.p.Config.IgnoreForDiff...) } if ignoreTags { - ret = append(ret, &types.IgnoreForDiffItemConfig{FieldPath: []string{`metadata.labels."kluctl.io/tag-*"`}}) + ret = append(ret, &types.IgnoreForDiffItemConfig{FieldPathRegex: []string{`metadata\.labels\["kluctl\.io/tag-.*"\]`}}) } if ignoreLabels { ret = append(ret, &types.IgnoreForDiffItemConfig{FieldPath: []string{`metadata.labels.*`}}) From 1a4a00722604680806cc05c0ab86f295e265c527 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 13 Apr 2023 15:19:17 +0200 Subject: [PATCH 0858/2268] tests: Add tests for object normalization --- pkg/diff/normalize_test.go | 203 +++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 pkg/diff/normalize_test.go diff --git a/pkg/diff/normalize_test.go b/pkg/diff/normalize_test.go new file mode 100644 index 000000000..d380ea9b0 --- /dev/null +++ b/pkg/diff/normalize_test.go @@ -0,0 +1,203 @@ +package diff + +import ( + "fmt" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "github.com/stretchr/testify/assert" + "testing" +) + +func buildObject(j ...string) *uo.UnstructuredObject { + o := uo.FromMap(map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]any{ + "name": "test", + "namespace": "ns", + }, + }) + + for _, x := range j { + o2 := uo.FromStringMust(x) + o.Merge(o2) + } + return o +} + +func buildResultObject(j ...string) *uo.UnstructuredObject { + o := buildObject(`{"metadata": {"labels": {}, "annotations": {}}}`) + for _, x := range j { + o2 := uo.FromStringMust(x) + o.Merge(o2) + } + return o +} + +type testCase struct { + remote *uo.UnstructuredObject + local *uo.UnstructuredObject + result *uo.UnstructuredObject + ignoreForDiffs []*types.IgnoreForDiffItemConfig +} + +func runTests(t *testing.T, tests []testCase) { + for i, tc := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + r, err := NormalizeObject(tc.remote, tc.ignoreForDiffs, tc.local) + if err != nil { + t.Error(err) + } else { + rj, _ := yaml.WriteJsonString(r) + ej, _ := yaml.WriteJsonString(tc.result) + assert.Equal(t, ej, rj) + } + }) + } +} + +func TestNormalizeNoop(t *testing.T) { + testCases := []testCase{ + {remote: buildObject(), local: buildObject(), result: buildResultObject()}, + } + runTests(t, testCases) +} + +func TestNormalizeMetadata(t *testing.T) { + testCases := []testCase{ + {remote: buildObject(`{"metadata": {"labels": null, "annotations": null}}`), local: buildObject(), result: buildResultObject()}, + {remote: buildObject(`{"metadata": {"managedFields": {}, "creationTimestamp": "test", "generation": "test", "resourceVersion": 123, "selfLink": "test", "uid": "test", "good": "keep"}}`), local: buildObject(), result: buildResultObject(`{"metadata": {"good": "keep"}}`)}, + {remote: buildObject(`{"metadata": {"annotations": {"kubectl.kubernetes.io/last-applied-configuration": "test", "good": "keep"}}}`), local: buildObject(), result: buildResultObject(`{"metadata": {"annotations": {"good": "keep"}}}`)}, + } + runTests(t, testCases) +} + +func TestNormalizeMisc(t *testing.T) { + testCases := []testCase{ + {remote: buildObject(`{"spec": {"template": {"metadata": {"labels": {"controller-uid": "test", "good": "keep"}}}}, "selector": {"controller-uid": "test", "good": "keep"}}`), local: buildObject(), result: buildResultObject(`{"metadata":{"annotations":{},"labels":{}},"selector":{"controller-uid":"test","good":"keep"},"spec":{"template":{"metadata":{"labels":{"good":"keep"}}}}}`)}, + {remote: buildObject(`{"status": {"test": "test"}}`), local: buildObject(), result: buildResultObject()}, + } + runTests(t, testCases) +} + +func TestNormalizeContainers(t *testing.T) { + testCases := []testCase{ + {remote: buildObject(`{"spec": {"template": {"spec": {"containers": ["env": [{"name": "a", "value": "x"}]]}}}}`), local: buildObject(), result: buildResultObject(`{"spec": {"template": {"spec": {"containers": ["env": {"a":{"name":"a","value":"x"}}]}}}}`)}, + } + runTests(t, testCases) +} + +func TestNormalizeData(t *testing.T) { + testCases := []testCase{ + {remote: buildObject(`{"apiVersion": "v1", "kind": "ConfigMap", "data": {"good": "keep"}}`), local: buildObject(), result: buildResultObject(`{"apiVersion": "v1", "kind": "ConfigMap", "data": {"good": "keep"}}`)}, + {remote: buildObject(`{"apiVersion": "v1", "kind": "ConfigMap", "data": {}}`), local: buildObject(), result: buildResultObject(`{"apiVersion": "v1", "kind": "ConfigMap"}`)}, + {remote: buildObject(`{"apiVersion": "v1", "kind": "ConfigMap", "data": null}`), local: buildObject(), result: buildResultObject(`{"apiVersion": "v1", "kind": "ConfigMap"}`)}, + {remote: buildObject(`{"apiVersion": "v1", "kind": "ConfigMap"}`), local: buildObject(), result: buildResultObject(`{"apiVersion": "v1", "kind": "ConfigMap"}`)}, + } + runTests(t, testCases) +} + +func TestNormalizeServiceAccounts(t *testing.T) { + testCases := []testCase{ + {remote: buildObject(`{"apiVersion": "v1", "kind": "ServiceAccount", "secrets": [{"name": "test-remove"},{"name": "good"}]}`), local: buildObject(), result: buildResultObject(`{"apiVersion": "v1", "kind": "ServiceAccount", "secrets": [{"name": "good"}]}`)}, + } + runTests(t, testCases) +} + +func TestNormalizeIgnoreForDiffs(t *testing.T) { + testCases := []testCase{ + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + local: buildObject(), + result: buildResultObject(`{"metadata": {"labels": {"good": "keep"}}}`), + ignoreForDiffs: []*types.IgnoreForDiffItemConfig{ + {FieldPath: []string{"metadata.labels.l1", "metadata.labels.l2"}}, + }, + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2"}}}`), + local: buildObject(), + result: buildResultObject(`{"metadata": {"labels": {}}}`), + ignoreForDiffs: []*types.IgnoreForDiffItemConfig{ + {FieldPath: []string{"metadata.labels.*"}}, + }, + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + local: buildObject(), + result: buildResultObject(`{"metadata": {"labels": {"good": "keep"}}}`), + ignoreForDiffs: []*types.IgnoreForDiffItemConfig{ + {FieldPathRegex: []string{`metadata\.labels\.l.*`}}, + }, + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + local: buildObject(), + result: buildResultObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + ignoreForDiffs: []*types.IgnoreForDiffItemConfig{ + {FieldPath: []string{"metadata.labels.*"}, Group: utils.StrPtr("Nope")}, + }, + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + local: buildObject(), + result: buildResultObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + ignoreForDiffs: []*types.IgnoreForDiffItemConfig{ + {FieldPath: []string{"metadata.labels.*"}, Kind: utils.StrPtr("Nope")}, + }, + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + local: buildObject(), + result: buildResultObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + ignoreForDiffs: []*types.IgnoreForDiffItemConfig{ + {FieldPath: []string{"metadata.labels.*"}, Name: utils.StrPtr("Nope")}, + }, + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + local: buildObject(), + result: buildResultObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + ignoreForDiffs: []*types.IgnoreForDiffItemConfig{ + {FieldPath: []string{"metadata.labels.*"}, Namespace: utils.StrPtr("Nope")}, + }, + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2"}}}`), + local: buildObject(), + result: buildResultObject(`{"metadata": {"labels": {}}}`), + ignoreForDiffs: []*types.IgnoreForDiffItemConfig{ + {FieldPath: []string{"metadata.labels.*"}, Group: utils.StrPtr("apps"), Kind: utils.StrPtr("Deployment"), Name: utils.StrPtr("test"), Namespace: utils.StrPtr("ns")}, + }, + }, + } + runTests(t, testCases) +} + +func TestNormalizeIgnoreForDiffsByAnnotations(t *testing.T) { + testCases := []testCase{ + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "good": "keep"}}}`), + local: buildObject(`{"metadata": {"annotations": {"kluctl.io/ignore-diff": "true"}}}`), + result: uo.FromStringMust(`{}`), + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2"}}}`), + local: buildObject(`{"metadata": {"annotations": {"kluctl.io/ignore-diff-field": "metadata.labels.l1"}}}`), + result: buildResultObject(`{"metadata": {"labels": {"l2": "l2"}}}`), + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "l3": "l3", "good": "keep"}}}`), + local: buildObject(`{"metadata": {"annotations": {"kluctl.io/ignore-diff-field": "metadata.labels.l1", "kluctl.io/ignore-diff-field-1": "metadata.labels.l2", "kluctl.io/ignore-diff-field-3": "metadata.labels.l3"}}}`), + result: buildResultObject(`{"metadata": {"labels": {"good": "keep"}}}`), + }, + { + remote: buildObject(`{"metadata": {"labels": {"l1": "v1", "l2": "l2", "l3": "l3", "good": "keep"}}}`), + local: buildObject(`{"metadata": {"annotations": {"kluctl.io/ignore-diff-field-regex": "metadata\\.labels\\.l[12]", "kluctl.io/ignore-diff-field-regex-1": "metadata\\.labels\\.l3"}}}`), + result: buildResultObject(`{"metadata": {"labels": {"good": "keep"}}}`), + }, + } + runTests(t, testCases) +} From 3d9386cf8bce5ec75192ea0ee135e97ef0d80f43 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 13 Apr 2023 15:19:51 +0200 Subject: [PATCH 0859/2268] fix: GetNestedObject should not treat nil fields as not found --- pkg/utils/uo/nested_fields.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/utils/uo/nested_fields.go b/pkg/utils/uo/nested_fields.go index 6da20472a..35e3be0c0 100644 --- a/pkg/utils/uo/nested_fields.go +++ b/pkg/utils/uo/nested_fields.go @@ -182,6 +182,10 @@ func (uo *UnstructuredObject) GetNestedObject(keys ...interface{}) (*Unstructure return nil, false, nil } + if a == nil { + return nil, true, nil + } + m, ok := a.(map[string]interface{}) if !ok { return nil, false, fmt.Errorf("nested value is not a map") From 6064653bf95009cc4fc14949c9b4a2e582fc6225 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 13 Apr 2023 15:20:20 +0200 Subject: [PATCH 0860/2268] fix: Fix multiple issues with normalization found while implementing tests --- pkg/diff/normalize.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/diff/normalize.go b/pkg/diff/normalize.go index 81e94fb4a..f32f7a2d7 100644 --- a/pkg/diff/normalize.go +++ b/pkg/diff/normalize.go @@ -64,8 +64,8 @@ func normalizeContainers(containers []*uo.UnstructuredObject) { func normalizeSecretAndConfigMaps(o *uo.UnstructuredObject) { data, found, _ := o.GetNestedObject("data") - if found && len(data.Object) == 0 { - _ = data.RemoveNestedField("data") + if found && (data == nil || len(data.Object) == 0) { + _ = o.RemoveNestedField("data") } } @@ -95,7 +95,7 @@ func normalizeServiceAccount(o *uo.UnstructuredObject) { func normalizeMetadata(o *uo.UnstructuredObject) { // We don't care about managedFields when diffing (they just produce noise) _ = o.RemoveNestedField("metadata", "managedFields") - _ = o.RemoveNestedField("metadata", "annotations", "managedFields", "kubectl.kubernetes.io/last-applied-configuration") + _ = o.RemoveNestedField("metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration") // We don't want to see this in diffs _ = o.RemoveNestedField("metadata", "creationTimestamp") @@ -105,8 +105,8 @@ func normalizeMetadata(o *uo.UnstructuredObject) { _ = o.RemoveNestedField("metadata", "uid") // Ensure empty labels/metadata exist - _ = o.SetNestedFieldDefault(map[string]string{}, "metadata", "labels") - _ = o.SetNestedFieldDefault(map[string]string{}, "metadata", "annotations") + _ = o.SetNestedFieldDefault(map[string]any{}, "metadata", "labels") + _ = o.SetNestedFieldDefault(map[string]any{}, "metadata", "annotations") } func normalizeMisc(o *uo.UnstructuredObject) { From 7daec3454bae322734282127a066a6e8b9f20147 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:35:40 +0200 Subject: [PATCH 0861/2268] chore(deps): Bump github.com/mattn/go-isatty from 0.0.17 to 0.0.18 (#401) Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.17 to 0.0.18. - [Release notes](https://github.com/mattn/go-isatty/releases) - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.17...v0.0.18) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index e8e104afa..f002c7f3b 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 github.com/kluctl/go-jinja2 v0.0.0-20230310104535-8136715a1e5a github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.17 + github.com/mattn/go-isatty v0.0.18 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 github.com/ohler55/ojg v1.18.4 diff --git a/go.sum b/go.sum index 1e9193c3d..5a0c373a8 100644 --- a/go.sum +++ b/go.sum @@ -585,8 +585,9 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA 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/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= From 2c5b0f9c274b5293a566e20acd2344a4c98dd829 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:35:52 +0200 Subject: [PATCH 0862/2268] chore(deps): Bump github.com/Masterminds/semver/v3 from 3.2.0 to 3.2.1 (#402) Bumps [github.com/Masterminds/semver/v3](https://github.com/Masterminds/semver) from 3.2.0 to 3.2.1. - [Release notes](https://github.com/Masterminds/semver/releases) - [Changelog](https://github.com/Masterminds/semver/blob/master/CHANGELOG.md) - [Commits](https://github.com/Masterminds/semver/compare/v3.2.0...v3.2.1) --- updated-dependencies: - dependency-name: github.com/Masterminds/semver/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index f002c7f3b..429cb1ebb 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 - github.com/Masterminds/semver/v3 v3.2.0 + github.com/Masterminds/semver/v3 v3.2.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/bitnami-labs/sealed-secrets v0.19.5 github.com/cyphar/filepath-securejoin v0.2.3 diff --git a/go.sum b/go.sum index 5a0c373a8..548fa93e1 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,9 @@ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= From fb20232ffe962b9dba8ece05fdfe4fe298fcda79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:37:36 +0200 Subject: [PATCH 0863/2268] chore(deps): Bump github.com/imdario/mergo from 0.3.14 to 0.3.15 (#403) Bumps [github.com/imdario/mergo](https://github.com/imdario/mergo) from 0.3.14 to 0.3.15. - [Release notes](https://github.com/imdario/mergo/releases) - [Commits](https://github.com/imdario/mergo/compare/v0.3.14...v0.3.15) --- updated-dependencies: - dependency-name: github.com/imdario/mergo dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 429cb1ebb..8656e3d5d 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/google/go-containerregistry v0.13.0 github.com/hashicorp/vault/api v1.9.0 github.com/hexops/gotextdiff v1.0.3 - github.com/imdario/mergo v0.3.14 + github.com/imdario/mergo v0.3.15 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 diff --git a/go.sum b/go.sum index 548fa93e1..942dca7f5 100644 --- a/go.sum +++ b/go.sum @@ -492,8 +492,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/imdario/mergo v0.3.14 h1:fOqeC1+nCuuk6PKQdg9YmosXX7Y7mHX6R/0ZldI9iHo= -github.com/imdario/mergo v0.3.14/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= From 1379ed7e79a04f773df9e3f0635690cab03dd567 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Apr 2023 09:16:28 +0200 Subject: [PATCH 0864/2268] chore(deps): Bump github.com/go-playground/validator/v10 (#408) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.11.2 to 10.12.0. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.11.2...v10.12.0) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8656e3d5d..3f5d22cc3 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/fluxcd/pkg/kustomize v0.13.2 - github.com/go-playground/validator/v10 v10.11.2 + github.com/go-playground/validator/v10 v10.12.0 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-containerregistry v0.13.0 @@ -169,7 +169,7 @@ require ( github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/leodido/go-urn v1.2.1 // indirect + github.com/leodido/go-urn v1.2.2 // indirect github.com/lib/pq v1.10.7 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/go.sum b/go.sum index 942dca7f5..b6c2847f4 100644 --- a/go.sum +++ b/go.sum @@ -303,8 +303,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= -github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= +github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= +github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -553,8 +553,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4= +github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -732,6 +732,7 @@ github.com/rubenv/sql-migrate v1.3.1/go.mod h1:YzG/Vh82CwyhTFXy+Mf5ahAiiEOpAlHur github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= From 0a4395515a076459f78fa49a52f005638ce4c7ad Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 14 Apr 2023 09:27:36 +0200 Subject: [PATCH 0865/2268] fix: Revert handling - as "simple identifier" when building a json path (#409) --- pkg/utils/uo/jsonpath.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/uo/jsonpath.go b/pkg/utils/uo/jsonpath.go index 44e375534..5263f8639 100644 --- a/pkg/utils/uo/jsonpath.go +++ b/pkg/utils/uo/jsonpath.go @@ -7,7 +7,7 @@ import ( "strings" ) -var isSimpleIdentifier = regexp.MustCompile(`^[A-Za-z_-][A-Za-z0-9_-]+$`) +var isSimpleIdentifier = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]+$`) type KeyPath []interface{} From a4987270fca40079873e626ccc0ac0115cbb104d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 09:14:18 +0200 Subject: [PATCH 0866/2268] chore(deps): Bump k8s.io/apimachinery from 0.27.0 to 0.27.1 (#410) Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.27.0 to 0.27.1. - [Release notes](https://github.com/kubernetes/apimachinery/releases) - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.27.0...v0.27.1) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3f5d22cc3..d1822c4f6 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( helm.sh/helm/v3 v3.11.2 k8s.io/api v0.26.3 k8s.io/apiextensions-apiserver v0.26.3 - k8s.io/apimachinery v0.27.0 + k8s.io/apimachinery v0.27.1 k8s.io/client-go v0.26.3 k8s.io/klog/v2 v2.90.1 sigs.k8s.io/kustomize/kyaml v0.13.9 diff --git a/go.sum b/go.sum index b6c2847f4..239984801 100644 --- a/go.sum +++ b/go.sum @@ -1336,8 +1336,8 @@ 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.27.0 h1:vEyy/PVMbPMCPutrssCVHCf0JNZ0Px+YqPi82K2ALlk= -k8s.io/apimachinery v0.27.0/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= +k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc= +k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= k8s.io/apiserver v0.26.3 h1:blBpv+yOiozkPH2aqClhJmJY+rp53Tgfac4SKPDJnU4= k8s.io/apiserver v0.26.3/go.mod h1:CJe/VoQNcXdhm67EvaVjYXxR3QyfwpceKPuPaeLibTA= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= From 8bb0011d686e85e91bec6b93258a370f013f8edc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 19 Apr 2023 10:08:04 +0200 Subject: [PATCH 0867/2268] feat: Switch to using json tags instead of yaml tags (#416) * refactor: Remove unused TargetConfig * refactor: Remove unused WriteYamlToTar * feat: Use k8s yaml parsing based on json for all structs --- cmd/kluctl/commands/root.go | 2 +- pkg/deployment/images.go | 4 +- pkg/deployment/utils/diff_utils_test.go | 2 +- pkg/git/git-url/url.go | 9 +- pkg/git/test_git_server.go | 2 +- pkg/git/utils.go | 4 +- pkg/repocache/cache.go | 6 +- pkg/types/command_result.go | 56 ++++----- pkg/types/deployment.go | 69 +++++------ pkg/types/git_project.go | 17 +-- pkg/types/helm_chart.go | 26 ++-- pkg/types/k8s/ref.go | 2 +- pkg/types/kluctl_project.go | 44 +++---- pkg/types/target_config.go | 26 ++-- pkg/types/url.go | 9 +- pkg/types/vars_source.go | 58 ++++----- pkg/utils/uo/nested_fields.go | 2 + pkg/utils/uo/uo.go | 11 +- pkg/vars/vars_loader_test.go | 6 +- pkg/vars/vars_test.go | 8 +- pkg/yaml/yaml.go | 150 +++++++++--------------- pkg/yaml/yaml_test.go | 12 +- 22 files changed, 239 insertions(+), 286 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 4b949d272..291655957 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -141,7 +141,7 @@ func setupProfiling(cpuProfile string) error { } type VersionCheckState struct { - LastVersionCheck time.Time `yaml:"lastVersionCheck"` + LastVersionCheck time.Time `json:"lastVersionCheck"` } func checkNewVersion(ctx context.Context) { diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index b1af402d6..715f0c327 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -116,8 +116,8 @@ const beginPlaceholder = "XXXXXbegin_get_image_" const endPlaceholder = "_end_get_imageXXXXX" type placeHolder struct { - Image string `yaml:"image"` - HasLatestVersion bool `yaml:"hasLatestVersion"` + Image string `json:"image"` + HasLatestVersion bool `json:"hasLatestVersion"` Container string diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index c97d60e62..042fd32ef 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -119,9 +119,9 @@ func TestDiff(t *testing.T) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []types.Change{ + types.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v12", UnifiedDiff: "-v1\n+v12"}, types.Change{Type: "delete", JsonPath: "data.d2", OldValue: "v2", NewValue: interface{}(nil), UnifiedDiff: "-v2"}, types.Change{Type: "insert", JsonPath: "data.d3", OldValue: interface{}(nil), NewValue: "v3", UnifiedDiff: "+v3"}, - types.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v12", UnifiedDiff: "-v1\n+v12"}, }, dtc.du.ChangedObjects[0].Changes) }, }, diff --git a/pkg/git/git-url/url.go b/pkg/git/git-url/url.go index 5bf30d9ee..57f5cfa0f 100644 --- a/pkg/git/git-url/url.go +++ b/pkg/git/git-url/url.go @@ -1,6 +1,7 @@ package git_url import ( + "encoding/json" "fmt" "github.com/kluctl/kluctl/v2/pkg/git/git-url/giturls" "net/url" @@ -19,9 +20,9 @@ func Parse(u string) (*GitUrl, error) { return &GitUrl{*u2}, nil } -func (u *GitUrl) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (u *GitUrl) UnmarshalJSON(b []byte) error { var s string - err := unmarshal(&s) + err := json.Unmarshal(b, &s) if err != nil { return err } @@ -33,8 +34,8 @@ func (u *GitUrl) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } -func (u GitUrl) MarshalYAML() (interface{}, error) { - return u.String(), nil +func (u GitUrl) MarshalJSON() ([]byte, error) { + return json.Marshal(u.String()) } func (u *GitUrl) IsSsh() bool { diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index 3e87f6531..26b656b5c 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -9,12 +9,12 @@ import ( "os" "path/filepath" "reflect" + "sigs.k8s.io/yaml" "testing" "github.com/go-git/go-git/v5" "github.com/jinzhu/copier" http_server "github.com/kluctl/kluctl/v2/pkg/git/http-server" - "gopkg.in/yaml.v3" ) type TestGitServer struct { diff --git a/pkg/git/utils.go b/pkg/git/utils.go index f007fa3ee..2fae8fd60 100644 --- a/pkg/git/utils.go +++ b/pkg/git/utils.go @@ -8,8 +8,8 @@ import ( ) type CheckoutInfo struct { - CheckedOutRef string `yaml:"checkedOutRef"` - CheckedOutCommit string `yaml:"checkedOutCommit"` + CheckedOutRef string `json:"checkedOutRef"` + CheckedOutCommit string `json:"checkedOutCommit"` } func GetCheckoutInfo(path string) (ri CheckoutInfo, err error) { diff --git a/pkg/repocache/cache.go b/pkg/repocache/cache.go index 3db2b00c5..b3ca0907f 100644 --- a/pkg/repocache/cache.go +++ b/pkg/repocache/cache.go @@ -47,9 +47,9 @@ type CacheEntry struct { } type RepoInfo struct { - Url git_url.GitUrl `yaml:"url"` - RemoteRefs map[string]string `yaml:"remoteRefs"` - DefaultRef string `yaml:"defaultRef"` + Url git_url.GitUrl `json:"url"` + RemoteRefs map[string]string `json:"remoteRefs"` + DefaultRef string `json:"defaultRef"` } type RepoOverride struct { diff --git a/pkg/types/command_result.go b/pkg/types/command_result.go index 5142d73c0..d6a235c9a 100644 --- a/pkg/types/command_result.go +++ b/pkg/types/command_result.go @@ -6,50 +6,50 @@ import ( ) type Change struct { - Type string `yaml:"type" validate:"required"` - JsonPath string `yaml:"jsonPath" validate:"required"` - OldValue interface{} `yaml:"oldValue,omitempty"` - NewValue interface{} `yaml:"newValue,omitempty"` - UnifiedDiff string `yaml:"unifiedDiff,omitempty"` + Type string `json:"type" validate:"required"` + JsonPath string `json:"jsonPath" validate:"required"` + OldValue interface{} `json:"oldValue,omitempty"` + NewValue interface{} `json:"newValue,omitempty"` + UnifiedDiff string `json:"unifiedDiff,omitempty"` } type ChangedObject struct { - Ref k8s.ObjectRef `yaml:"ref"` - NewObject *uo.UnstructuredObject `yaml:"newObject,omitempty"` - OldObject *uo.UnstructuredObject `yaml:"oldObject,omitempty"` - Changes []Change `yaml:"changes,omitempty"` + Ref k8s.ObjectRef `json:"ref"` + NewObject *uo.UnstructuredObject `json:"newObject,omitempty"` + OldObject *uo.UnstructuredObject `json:"oldObject,omitempty"` + Changes []Change `json:"changes,omitempty"` } type RefAndObject struct { - Ref k8s.ObjectRef `yaml:"ref"` - Object *uo.UnstructuredObject `yaml:"object,omitempty"` + Ref k8s.ObjectRef `json:"ref"` + Object *uo.UnstructuredObject `json:"object,omitempty"` } type DeploymentError struct { - Ref k8s.ObjectRef `yaml:"ref"` - Error string `yaml:"error"` + Ref k8s.ObjectRef `json:"ref"` + Error string `json:"error"` } type CommandResult struct { - NewObjects []*RefAndObject `yaml:"newObjects,omitempty"` - ChangedObjects []*ChangedObject `yaml:"changedObjects,omitempty"` - HookObjects []*RefAndObject `yaml:"hookObjects,omitempty"` - OrphanObjects []k8s.ObjectRef `yaml:"orphanObjects,omitempty"` - DeletedObjects []k8s.ObjectRef `yaml:"deletedObjects,omitempty"` - Errors []DeploymentError `yaml:"errors,omitempty"` - Warnings []DeploymentError `yaml:"warnings,omitempty"` - SeenImages []FixedImage `yaml:"seenImages,omitempty"` + NewObjects []*RefAndObject `json:"newObjects,omitempty"` + ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` + HookObjects []*RefAndObject `json:"hookObjects,omitempty"` + OrphanObjects []k8s.ObjectRef `json:"orphanObjects,omitempty"` + DeletedObjects []k8s.ObjectRef `json:"deletedObjects,omitempty"` + Errors []DeploymentError `json:"errors,omitempty"` + Warnings []DeploymentError `json:"warnings,omitempty"` + SeenImages []FixedImage `json:"seenImages,omitempty"` } type ValidateResultEntry struct { - Ref k8s.ObjectRef `yaml:"ref"` - Annotation string `yaml:"annotation"` - Message string `yaml:"message"` + Ref k8s.ObjectRef `json:"ref"` + Annotation string `json:"annotation"` + Message string `json:"message"` } type ValidateResult struct { - Ready bool `yaml:"ready"` - Warnings []DeploymentError `yaml:"warnings,omitempty"` - Errors []DeploymentError `yaml:"errors,omitempty"` - Results []ValidateResultEntry `yaml:"results,omitempty"` + Ready bool `json:"ready"` + Warnings []DeploymentError `json:"warnings,omitempty"` + Errors []DeploymentError `json:"errors,omitempty"` + Results []ValidateResultEntry `json:"results,omitempty"` } diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index 22e2a1914..dfa43ce2c 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -1,23 +1,24 @@ package types import ( + "encoding/json" "github.com/go-playground/validator/v10" "github.com/kluctl/kluctl/v2/pkg/yaml" ) type DeploymentItemConfig struct { - Path *string `yaml:"path,omitempty"` - Include *string `yaml:"include,omitempty"` - Git *GitProject `yaml:"git,omitempty"` - Tags []string `yaml:"tags,omitempty"` - Barrier bool `yaml:"barrier,omitempty"` - WaitReadiness bool `yaml:"waitReadiness,omitempty"` - Vars []*VarsSource `yaml:"vars,omitempty"` - SkipDeleteIfTags bool `yaml:"skipDeleteIfTags,omitempty"` - OnlyRender bool `yaml:"onlyRender,omitempty"` - AlwaysDeploy bool `yaml:"alwaysDeploy,omitempty"` - DeleteObjects []DeleteObjectItemConfig `yaml:"deleteObjects,omitempty"` - When string `yaml:"when,omitempty"` + Path *string `json:"path,omitempty"` + Include *string `json:"include,omitempty"` + Git *GitProject `json:"git,omitempty"` + Tags []string `json:"tags,omitempty"` + Barrier bool `json:"barrier,omitempty"` + WaitReadiness bool `json:"waitReadiness,omitempty"` + Vars []*VarsSource `json:"vars,omitempty"` + SkipDeleteIfTags bool `json:"skipDeleteIfTags,omitempty"` + OnlyRender bool `json:"onlyRender,omitempty"` + AlwaysDeploy bool `json:"alwaysDeploy,omitempty"` + DeleteObjects []DeleteObjectItemConfig `json:"deleteObjects,omitempty"` + When string `json:"when,omitempty"` } func ValidateDeploymentItemConfig(sl validator.StructLevel) { @@ -41,10 +42,10 @@ func ValidateDeploymentItemConfig(sl validator.StructLevel) { } type DeleteObjectItemConfig struct { - Group *string `yaml:"group,omitempty"` - Kind *string `yaml:"kind,omitempty"` - Name string `yaml:"name" validate:"required"` - Namespace string `yaml:"namespace,omitempty"` + Group *string `json:"group,omitempty"` + Kind *string `json:"kind,omitempty"` + Name string `json:"name" validate:"required"` + Namespace string `json:"namespace,omitempty"` } func ValidateDeleteObjectItemConfig(sl validator.StructLevel) { @@ -55,21 +56,21 @@ func ValidateDeleteObjectItemConfig(sl validator.StructLevel) { } type SealedSecretsConfig struct { - OutputPattern *string `yaml:"outputPattern,omitempty"` + OutputPattern *string `json:"outputPattern,omitempty"` } type SingleStringOrList []string -func (s *SingleStringOrList) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (s *SingleStringOrList) UnmarshalJSON(b []byte) error { var single string - if err := unmarshal(&single); err == nil { + if err := json.Unmarshal(b, &single); err == nil { // it's a single project *s = []string{single} return nil } // try as array var arr []string - if err := unmarshal(&arr); err != nil { + if err := json.Unmarshal(b, &arr); err != nil { return err } *s = arr @@ -77,12 +78,12 @@ func (s *SingleStringOrList) UnmarshalYAML(unmarshal func(interface{}) error) er } type IgnoreForDiffItemConfig struct { - FieldPath SingleStringOrList `yaml:"fieldPath" validate:"required"` - FieldPathRegex SingleStringOrList `yaml:"fieldPathRegex,omitempty"` - Group *string `yaml:"group,omitempty"` - Kind *string `yaml:"kind,omitempty"` - Name *string `yaml:"name,omitempty"` - Namespace *string `yaml:"namespace,omitempty"` + FieldPath SingleStringOrList `json:"fieldPath,omitempty"` + FieldPathRegex SingleStringOrList `json:"fieldPathRegex,omitempty"` + Group *string `json:"group,omitempty"` + Kind *string `json:"kind,omitempty"` + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` } func ValidateIgnoreForDiffItemConfig(sl validator.StructLevel) { @@ -93,18 +94,18 @@ func ValidateIgnoreForDiffItemConfig(sl validator.StructLevel) { } type DeploymentProjectConfig struct { - Vars []*VarsSource `yaml:"vars,omitempty"` - SealedSecrets *SealedSecretsConfig `yaml:"sealedSecrets,omitempty"` + Vars []*VarsSource `json:"vars,omitempty"` + SealedSecrets *SealedSecretsConfig `json:"sealedSecrets,omitempty"` - When string `yaml:"when,omitempty"` + When string `json:"when,omitempty"` - Deployments []*DeploymentItemConfig `yaml:"deployments,omitempty"` + Deployments []*DeploymentItemConfig `json:"deployments,omitempty"` - CommonLabels map[string]string `yaml:"commonLabels,omitempty"` - OverrideNamespace *string `yaml:"overrideNamespace,omitempty"` - Tags []string `yaml:"tags,omitempty"` + CommonLabels map[string]string `json:"commonLabels,omitempty"` + OverrideNamespace *string `json:"overrideNamespace,omitempty"` + Tags []string `json:"tags,omitempty"` - IgnoreForDiff []*IgnoreForDiffItemConfig `yaml:"ignoreForDiff,omitempty"` + IgnoreForDiff []*IgnoreForDiffItemConfig `json:"ignoreForDiff,omitempty"` } func init() { diff --git a/pkg/types/git_project.go b/pkg/types/git_project.go index 5183cf964..e43997353 100644 --- a/pkg/types/git_project.go +++ b/pkg/types/git_project.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "fmt" "regexp" "strings" @@ -14,18 +15,18 @@ import ( var gitDirPatternNeg = regexp.MustCompile(`[\\\/:\*?"<>|[:cntrl:]\0^]`) type GitProject struct { - Url git_url.GitUrl `yaml:"url" validate:"required"` - Ref string `yaml:"ref,omitempty"` - SubDir string `yaml:"subDir,omitempty"` + Url git_url.GitUrl `json:"url" validate:"required"` + Ref string `json:"ref,omitempty"` + SubDir string `json:"subDir,omitempty"` } -func (gp *GitProject) UnmarshalYAML(unmarshal func(interface{}) error) error { - if err := unmarshal(&gp.Url); err == nil { +func (gp *GitProject) UnmarshalJSON(b []byte) error { + if err := json.Unmarshal(b, &gp.Url); err == nil { // it's a simple string return nil } type raw GitProject - return unmarshal((*raw)(gp)) + return json.Unmarshal(b, (*raw)(gp)) } // invalidDirName evaluate directory name against forbidden characters @@ -51,8 +52,8 @@ func ValidateGitProject(sl validator.StructLevel) { } type ExternalProject struct { - Project *GitProject `yaml:"project,omitempty"` - Path *string `yaml:"path,omitempty"` + Project *GitProject `json:"project,omitempty"` + Path *string `json:"path,omitempty"` } func ValidateExternalProject(sl validator.StructLevel) { diff --git a/pkg/types/helm_chart.go b/pkg/types/helm_chart.go index 78011ad58..fe1260358 100644 --- a/pkg/types/helm_chart.go +++ b/pkg/types/helm_chart.go @@ -7,18 +7,18 @@ import ( ) type HelmChartConfig2 struct { - Repo string `yaml:"repo,omitempty"` - Path string `yaml:"path,omitempty"` - CredentialsId *string `yaml:"credentialsId,omitempty"` - ChartName string `yaml:"chartName,omitempty"` - ChartVersion string `yaml:"chartVersion,omitempty"` - UpdateConstraints *string `yaml:"updateConstraints,omitempty"` - ReleaseName string `yaml:"releaseName" validate:"required"` - Namespace *string `yaml:"namespace,omitempty"` - Output *string `yaml:"output,omitempty"` - SkipCRDs bool `yaml:"skipCRDs,omitempty"` - SkipUpdate bool `yaml:"skipUpdate,omitempty"` - SkipPrePull bool `yaml:"skipPrePull,omitempty"` + Repo string `json:"repo,omitempty"` + Path string `json:"path,omitempty"` + CredentialsId *string `json:"credentialsId,omitempty"` + ChartName string `json:"chartName,omitempty"` + ChartVersion string `json:"chartVersion,omitempty"` + UpdateConstraints *string `json:"updateConstraints,omitempty"` + ReleaseName string `json:"releaseName" validate:"required"` + Namespace *string `json:"namespace,omitempty"` + Output *string `json:"output,omitempty"` + SkipCRDs bool `json:"skipCRDs,omitempty"` + SkipUpdate bool `json:"skipUpdate,omitempty"` + SkipPrePull bool `json:"skipPrePull,omitempty"` } func ValidateHelmChartConfig2(sl validator.StructLevel) { @@ -54,7 +54,7 @@ func ValidateHelmChartConfig2(sl validator.StructLevel) { } type HelmChartConfig struct { - HelmChartConfig2 `yaml:"helmChart" validate:"required"` + HelmChartConfig2 `json:"helmChart" validate:"required"` } func init() { diff --git a/pkg/types/k8s/ref.go b/pkg/types/k8s/ref.go index d166f88bd..c3e3da2bc 100644 --- a/pkg/types/k8s/ref.go +++ b/pkg/types/k8s/ref.go @@ -6,7 +6,7 @@ import ( ) type ObjectRef struct { - GVK schema.GroupVersionKind `yaml:"gvk,inline"` + GVK schema.GroupVersionKind `json:"gvk,inline"` Name string Namespace string } diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index e00908085..063a41398 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -5,44 +5,44 @@ import ( ) type SealingConfig struct { - Args *uo.UnstructuredObject `yaml:"args,omitempty"` - SecretSets []string `yaml:"secretSets,omitempty"` - CertFile *string `yaml:"certFile,omitempty"` + Args *uo.UnstructuredObject `json:"args,omitempty"` + SecretSets []string `json:"secretSets,omitempty"` + CertFile *string `json:"certFile,omitempty"` } type Target struct { - Name string `yaml:"name" validate:"required"` - Context *string `yaml:"context,omitempty"` - Args *uo.UnstructuredObject `yaml:"args,omitempty"` - SealingConfig *SealingConfig `yaml:"sealingConfig,omitempty"` - Images []FixedImage `yaml:"images,omitempty"` - Discriminator string `yaml:"discriminator,omitempty"` + Name string `json:"name" validate:"required"` + Context *string `json:"context,omitempty"` + Args *uo.UnstructuredObject `json:"args,omitempty"` + SealingConfig *SealingConfig `json:"sealingConfig,omitempty"` + Images []FixedImage `json:"images,omitempty"` + Discriminator string `json:"discriminator,omitempty"` } type DeploymentArg struct { - Name string `yaml:"name" validate:"required"` - Default interface{} `yaml:"default,omitempty"` + Name string `json:"name" validate:"required"` + Default interface{} `json:"default,omitempty"` } type SecretSet struct { - Name string `yaml:"name" validate:"required"` - Vars []*VarsSource `yaml:"vars,omitempty"` + Name string `json:"name" validate:"required"` + Vars []*VarsSource `json:"vars,omitempty"` } type GlobalSealedSecretsConfig struct { - Bootstrap *bool `yaml:"bootstrap,omitempty"` - Namespace *string `yaml:"namespace,omitempty"` - ControllerName *string `yaml:"controllerName,omitempty"` + Bootstrap *bool `json:"bootstrap,omitempty"` + Namespace *string `json:"namespace,omitempty"` + ControllerName *string `json:"controllerName,omitempty"` } type SecretsConfig struct { - SealedSecrets *GlobalSealedSecretsConfig `yaml:"sealedSecrets,omitempty"` - SecretSets []SecretSet `yaml:"secretSets,omitempty"` + SealedSecrets *GlobalSealedSecretsConfig `json:"sealedSecrets,omitempty"` + SecretSets []SecretSet `json:"secretSets,omitempty"` } type KluctlProject struct { - Targets []*Target `yaml:"targets,omitempty"` - Args []*DeploymentArg `yaml:"args,omitempty"` - SecretsConfig *SecretsConfig `yaml:"secretsConfig,omitempty"` - Discriminator string `yaml:"discriminator,omitempty"` + Targets []*Target `json:"targets,omitempty"` + Args []*DeploymentArg `json:"args,omitempty"` + SecretsConfig *SecretsConfig `json:"secretsConfig,omitempty"` + Discriminator string `json:"discriminator,omitempty"` } diff --git a/pkg/types/target_config.go b/pkg/types/target_config.go index 7540757f6..262bf793a 100644 --- a/pkg/types/target_config.go +++ b/pkg/types/target_config.go @@ -2,26 +2,20 @@ package types import ( "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) type FixedImage struct { - Image string `yaml:"image" validate:"required"` - ResultImage string `yaml:"resultImage" validate:"required"` - DeployedImage *string `yaml:"deployedImage,omitempty"` - Namespace *string `yaml:"namespace,omitempty"` - Object *k8s.ObjectRef `yaml:"object,omitempty"` - Deployment *string `yaml:"deployment,omitempty"` - Container *string `yaml:"container,omitempty"` - DeployTags []string `yaml:"deployTags,omitempty"` - DeploymentDir *string `yaml:"deploymentDir,omitempty"` + Image string `json:"image" validate:"required"` + ResultImage string `json:"resultImage" validate:"required"` + DeployedImage *string `json:"deployedImage,omitempty"` + Namespace *string `json:"namespace,omitempty"` + Object *k8s.ObjectRef `json:"object,omitempty"` + Deployment *string `json:"deployment,omitempty"` + Container *string `json:"container,omitempty"` + DeployTags []string `json:"deployTags,omitempty"` + DeploymentDir *string `json:"deploymentDir,omitempty"` } type FixedImagesConfig struct { - Images []FixedImage `yaml:"images,omitempty"` -} - -type TargetConfig struct { - FixedImagesConfig `yaml:"fixed_images_config,inline"` - Args *uo.UnstructuredObject `yaml:"args,omitempty"` + Images []FixedImage `json:"images,omitempty"` } diff --git a/pkg/types/url.go b/pkg/types/url.go index 703f8dfba..59560e2df 100644 --- a/pkg/types/url.go +++ b/pkg/types/url.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "net/url" ) @@ -8,9 +9,9 @@ type YamlUrl struct { url.URL } -func (u *YamlUrl) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (u *YamlUrl) UnmarshalJSON(b []byte) error { var s string - err := unmarshal(&s) + err := json.Unmarshal(b, &s) if err != nil { return err } @@ -22,6 +23,6 @@ func (u *YamlUrl) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } -func (u YamlUrl) MarshalYAML() (interface{}, error) { - return u.String(), nil +func (u YamlUrl) MarshalJSON() ([]byte, error) { + return json.Marshal(u.String()) } diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index 9e9c3db25..942cd6654 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -9,16 +9,16 @@ import ( ) type VarsSourceGit struct { - Url git_url.GitUrl `yaml:"url" validate:"required"` - Ref string `yaml:"ref,omitempty"` - Path string `yaml:"path" validate:"required"` + Url git_url.GitUrl `json:"url" validate:"required"` + Ref string `json:"ref,omitempty"` + Path string `json:"path" validate:"required"` } type VarsSourceClusterConfigMapOrSecret struct { - Name string `yaml:"name,omitempty"` - Labels map[string]string `yaml:"labels,omitempty"` - Namespace string `yaml:"namespace" validate:"required"` - Key string `yaml:"key" validate:"required"` + Name string `json:"name,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Namespace string `json:"namespace" validate:"required"` + Key string `json:"key" validate:"required"` } func ValidateVarsSourceClusterConfigMapOrSecret(sl validator.StructLevel) { @@ -32,42 +32,42 @@ func ValidateVarsSourceClusterConfigMapOrSecret(sl validator.StructLevel) { } type VarsSourceHttp struct { - Url YamlUrl `yaml:"url,omitempty" validate:"required"` - Method *string `yaml:"method,omitempty"` - Body *string `yaml:"body,omitempty"` - Headers map[string]string `yaml:"headers,omitempty"` - JsonPath *string `yaml:"jsonPath,omitempty"` + Url YamlUrl `json:"url,omitempty" validate:"required"` + Method *string `json:"method,omitempty"` + Body *string `json:"body,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + JsonPath *string `json:"jsonPath,omitempty"` } type VarsSourceAwsSecretsManager struct { // Name or ARN of the secret. In case a name is given, the region must be specified as well - SecretName string `yaml:"secretName" validate:"required"` + SecretName string `json:"secretName" validate:"required"` // The aws region - Region *string `yaml:"region,omitempty"` + Region *string `json:"region,omitempty"` // AWS credentials profile to use. The AWS_PROFILE environemnt variables will take precedence in case it is also set - Profile *string `yaml:"profile,omitempty"` + Profile *string `json:"profile,omitempty"` } type VarsSourceVault struct { - Address string `yaml:"address" validate:"required"` - Path string `yaml:"path" validate:"required"` + Address string `json:"address" validate:"required"` + Path string `json:"path" validate:"required"` } type VarsSource struct { - IgnoreMissing *bool `yaml:"ignoreMissing,omitempty"` - NoOverride *bool `yaml:"noOverride,omitempty"` + IgnoreMissing *bool `json:"ignoreMissing,omitempty"` + NoOverride *bool `json:"noOverride,omitempty"` - Values *uo.UnstructuredObject `yaml:"values,omitempty"` - File *string `yaml:"file,omitempty"` - Git *VarsSourceGit `yaml:"git,omitempty"` - ClusterConfigMap *VarsSourceClusterConfigMapOrSecret `yaml:"clusterConfigMap,omitempty"` - ClusterSecret *VarsSourceClusterConfigMapOrSecret `yaml:"clusterSecret,omitempty"` - SystemEnvVars *uo.UnstructuredObject `yaml:"systemEnvVars,omitempty"` - Http *VarsSourceHttp `yaml:"http,omitempty"` - AwsSecretsManager *VarsSourceAwsSecretsManager `yaml:"awsSecretsManager,omitempty"` - Vault *VarsSourceVault `yaml:"vault,omitempty"` + Values *uo.UnstructuredObject `json:"values,omitempty"` + File *string `json:"file,omitempty"` + Git *VarsSourceGit `json:"git,omitempty"` + ClusterConfigMap *VarsSourceClusterConfigMapOrSecret `json:"clusterConfigMap,omitempty"` + ClusterSecret *VarsSourceClusterConfigMapOrSecret `json:"clusterSecret,omitempty"` + SystemEnvVars *uo.UnstructuredObject `json:"systemEnvVars,omitempty"` + Http *VarsSourceHttp `json:"http,omitempty"` + AwsSecretsManager *VarsSourceAwsSecretsManager `json:"awsSecretsManager,omitempty"` + Vault *VarsSourceVault `json:"vault,omitempty"` - When string `yaml:"when,omitempty"` + When string `json:"when,omitempty"` } func ValidateVarsSource(sl validator.StructLevel) { diff --git a/pkg/utils/uo/nested_fields.go b/pkg/utils/uo/nested_fields.go index 35e3be0c0..a30a992a5 100644 --- a/pkg/utils/uo/nested_fields.go +++ b/pkg/utils/uo/nested_fields.go @@ -134,6 +134,8 @@ func (uo *UnstructuredObject) GetNestedInt(keys ...interface{}) (int64, bool, er return vv.Int(), true, nil } else if vv.CanUint() { return int64(vv.Uint()), true, nil + } else if vv.CanFloat() { + return int64(vv.Float()), true, nil } else { return 0, false, fmt.Errorf("value at %s is not an int", KeyPath(keys).ToJsonPath()) } diff --git a/pkg/utils/uo/uo.go b/pkg/utils/uo/uo.go index d5190d1ae..7983e3f23 100644 --- a/pkg/utils/uo/uo.go +++ b/pkg/utils/uo/uo.go @@ -1,6 +1,7 @@ package uo import ( + "encoding/json" "fmt" "github.com/jinzhu/copier" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -9,15 +10,15 @@ import ( ) type UnstructuredObject struct { - Object map[string]interface{} `yaml:"object,omitempty,inline"` + Object map[string]interface{} `json:"object,omitempty,inline"` } -func (uo *UnstructuredObject) MarshalYAML() (interface{}, error) { - return &uo.Object, nil +func (uo *UnstructuredObject) MarshalJSON() ([]byte, error) { + return json.Marshal(uo.Object) } -func (uo *UnstructuredObject) UnmarshalYAML(unmarshal func(interface{}) error) error { - return unmarshal(&uo.Object) +func (uo *UnstructuredObject) UnmarshalJSON(b []byte) error { + return json.Unmarshal(b, &uo.Object) } func (uo *UnstructuredObject) IsZero() bool { diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index 64b8ba404..d4387a66d 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -479,19 +479,19 @@ func TestVarsLoader_SystemEnv(t *testing.T) { assert.NoError(t, err) v, _, _ := vc.Vars.GetNestedField("test1") - assert.Equal(t, 42, v) + assert.Equal(t, 42., v) v, _, _ = vc.Vars.GetNestedField("test2") assert.Equal(t, "43", v) v, _, _ = vc.Vars.GetNestedField("test3", "test4") - assert.Equal(t, 44, v) + assert.Equal(t, 44., v) v, _, _ = vc.Vars.GetNestedField("test5") assert.Equal(t, "def", v) v, _, _ = vc.Vars.GetNestedField("test6") - assert.Equal(t, 42, v) + assert.Equal(t, 42., v) v, _, _ = vc.Vars.GetNestedField("test7") assert.Equal(t, "", v) diff --git a/pkg/vars/vars_test.go b/pkg/vars/vars_test.go index 46c822e8b..a69c7077c 100644 --- a/pkg/vars/vars_test.go +++ b/pkg/vars/vars_test.go @@ -52,10 +52,12 @@ func TestVarsCtxStruct(t *testing.T) { s := struct { Test1 struct { - Test2 int - } + Test2 int `json:"test2"` + } `json:"test1"` }{ - Test1: struct{ Test2 int }{Test2: 42}, + Test1: struct { + Test2 int `json:"test2"` + }{Test2: 42}, } err := varsCtx.UpdateChildFromStruct("child", s) diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 89610c94a..95bfb5a33 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -1,7 +1,6 @@ package yaml import ( - "archive/tar" "bytes" "encoding/json" "errors" @@ -10,41 +9,14 @@ import ( "golang.org/x/text/encoding/unicode" "golang.org/x/text/transform" yaml2 "gopkg.in/yaml.v2" - yaml3 "gopkg.in/yaml.v3" "io" "os" "path/filepath" + "regexp" + "sigs.k8s.io/yaml" "strings" ) -type Decoder interface { - Decode(v interface{}) error -} - -type decoderWrapper struct { - d *yaml3.Decoder -} - -func (w *decoderWrapper) Decode(v interface{}) error { - err := w.d.Decode(v) - if err != nil { - return err - } - - err = ValidateStructs(v) - if err != nil { - return err - } - - return nil -} - -func newDecoder(r io.Reader, out any) Decoder { - d := yaml3.NewDecoder(r) - d.KnownFields(true) - return &decoderWrapper{d: d} -} - func newUnicodeReader(r io.Reader) io.Reader { utf16bom := unicode.BOMOverride(unicode.UTF8.NewDecoder()) return transform.NewReader(r, utf16bom) @@ -75,13 +47,17 @@ func ReadYamlBytes(b []byte, o interface{}) error { func ReadYamlStream(r io.Reader, o interface{}) error { r = newUnicodeReader(r) - d := newDecoder(r, o) + b, err := io.ReadAll(r) + if err != nil { + return err + } - err := d.Decode(o) - if err != nil && errors.Is(err, io.EOF) { - return nil + err = yaml.UnmarshalStrict(b, o) + if err != nil { + return err } - return err + + return ValidateStructs(o) } func ReadYamlAllFile(p string) ([]interface{}, error) { @@ -102,27 +78,45 @@ func ReadYamlAllBytes(b []byte) ([]interface{}, error) { return ReadYamlAllStream(bytes.NewReader(b)) } +var docsSep = regexp.MustCompile("(?:^|\\s*\n)---\\s*") + func ReadYamlAllStream(r io.Reader) ([]interface{}, error) { r = newUnicodeReader(r) - d := newDecoder(r, nil) + b, err := io.ReadAll(r) + if err != nil { + return nil, err + } + s := string(b) + s = strings.TrimSpace(s) - var l []interface{} - for true { - var o interface{} - err := d.Decode(&o) + if s == "" { + return nil, nil + } + + docs := docsSep.Split(s, -1) + + ret := make([]any, 0, len(docs)) + for _, doc := range docs { + if doc == "" { + continue + } + + var x any + err = yaml.UnmarshalStrict([]byte(doc), &x) if err != nil { - if errors.Is(err, io.EOF) { - break - } return nil, err } - if o != nil { - l = append(l, o) + if x == nil { + continue } + err = ValidateStructs(x) + if err != nil { + return nil, err + } + ret = append(ret, x) } - - return l, nil + return ret, nil } func WriteYamlString(o interface{}) (string, error) { @@ -166,65 +160,31 @@ func WriteYamlAllString(l []interface{}) (string, error) { } func WriteYamlAllStream(w io.Writer, l []interface{}) error { - enc := yaml3.NewEncoder(w) - defer enc.Close() - - enc.SetIndent(2) - - for _, o := range l { - err := enc.Encode(o) + for i, o := range l { + if i != 0 { + _, err := w.Write([]byte("---\n")) + if err != nil { + return err + } + } + b, err := yaml.Marshal(o) + if err != nil { + return err + } + _, err = w.Write(b) if err != nil { return err } - } - return nil -} - -func WriteYamlToTar(tw *tar.Writer, o interface{}, name string) error { - str, err := WriteYamlBytes(o) - if err != nil { - return err - } - - err = tw.WriteHeader(&tar.Header{ - Name: name, - Size: int64(len(str)), - Mode: 0o666 | tar.TypeReg, - }) - if err != nil { - return err - } - _, err = tw.Write(str) - if err != nil { - return err } return nil } func WriteJsonString(o interface{}) (string, error) { - x, err := WriteYamlBytes(o) - if err != nil { - return "", err - } - - x, err = ConvertYamlToJson(x) + b, err := json.Marshal(o) if err != nil { return "", err } - return string(x), nil -} - -func ConvertYamlToJson(b []byte) ([]byte, error) { - var x interface{} - err := ReadYamlBytes(b, &x) - if err != nil { - return nil, err - } - b, err = json.Marshal(x) - if err != nil { - return nil, err - } - return b, nil + return string(b), nil } // RemoveDuplicateFields is a helper/hack to remove duplicate fields from yaml maps/structs. The yaml spec explicitly diff --git a/pkg/yaml/yaml_test.go b/pkg/yaml/yaml_test.go index bd2d6b772..65144ea1e 100644 --- a/pkg/yaml/yaml_test.go +++ b/pkg/yaml/yaml_test.go @@ -16,7 +16,7 @@ type EmptyYamlConfig struct { } type SimpleYamlConfig struct { - Value string `yaml:"value"` + Value string `json:"value"` } func TestReadYamlFile(t *testing.T) { @@ -308,16 +308,6 @@ value: anyValue2 assert.Equal(t, expectedString, buffer.String(), "Yaml not written correctly.") } -func TestConvertYamlToJson(t *testing.T) { - yaml := `value: anyValue1` - expectedJson := `{"value":"anyValue1"}` - yamlBytes := []byte(yaml) - expectedJsonBytes := []byte(expectedJson) - jsonBytes, convertYamlToJsonErr := ConvertYamlToJson(yamlBytes) - assert.NoError(t, convertYamlToJsonErr, "Can't convert yaml to json: %s", convertYamlToJsonErr) - assert.Equal(t, expectedJsonBytes, jsonBytes, "Yaml not converted correctly.") -} - func TestWriteJsonString(t *testing.T) { yaml := SimpleYamlConfig{ Value: "anyValue1", From 545ed5261e981a8c3605d80b9692d23abf46b251 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 20 Apr 2023 08:42:16 +0200 Subject: [PATCH 0868/2268] docs: Update commonLabels documentation to not mention the use for deletion/pruning (#419) --- docs/reference/deployments/deployment-yml.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index 9d40ee88e..c9ffb0808 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -231,10 +231,7 @@ See [templating](../templating/variable-sources.md) for more details. ## commonLabels A dictionary of [labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) and values to be -added to all resources deployed by any of the kustomize deployments in this deployment project. - -This feature is mainly meant to make it possible to identify all objects in a kubernetes cluster that were once deployed -through a specific deployment project. +added to all resources deployed by any of the deployment items in this deployment project. Consider the following example `deployment.yaml`: ```yaml @@ -249,16 +246,13 @@ commonLabels: my.prefix/label-2: value-2 ``` -Every resource deployed by the kustomize deployment `nginx` will now get the two provided labels attached. All included -sub-deployment projects (e.g. `sub-deployment1`) will also recursively inherit these labels and pass them to further +Every resource deployed by the kustomize deployment `nginx` will now get the four provided labels attached. All included +sub-deployment projects (e.g. `sub-deployment1`) will also recursively inherit these labels and pass them further down. -In case an included sub-deployment project also contains `commonLabels`, both dictionaries of common labels are merged +In case an included sub-deployment project also contains `commonLabels`, both dictionaries of commonLabels are merged inside the included sub-deployment project. In case of conflicts, the included common labels override the inherited. -The root deployment's `commonLabels` is also used to identify objects to be deleted when performing `kluctl delete` -or `kluctl prune` operations - Please note that these `commonLabels` are not related to `commonLabels` supported in `kustomization.yaml` files. It was decided to not rely on this feature but instead attach labels manually to resources right before sending them to kubernetes. This is due to an [implementation detail](https://github.com/kubernetes-sigs/kustomize/issues/1009) in From 08ad2e31474f8722669669aacda86d419684e00a Mon Sep 17 00:00:00 2001 From: Pat Riehecky <3534830+jcpunk@users.noreply.github.com> Date: Mon, 24 Apr 2023 02:24:14 -0500 Subject: [PATCH 0869/2268] docs: Add example for Kustomize integration (#421) Signed-off-by: Pat Riehecky --- docs/reference/deployments/kustomize.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/reference/deployments/kustomize.md b/docs/reference/deployments/kustomize.md index bcf694096..d772b5208 100644 --- a/docs/reference/deployments/kustomize.md +++ b/docs/reference/deployments/kustomize.md @@ -20,3 +20,24 @@ Generally, everything is possible via `kustomization.yaml`, is thus possible in We advise to read the kustomize [reference](https://kubectl.docs.kubernetes.io/references/kustomize/). You can also look into the official kustomize [example](https://github.com/kubernetes-sigs/kustomize/tree/master/examples). + +One way you might use this is to Kustomize a set of manifests from an external project. + +For example: +```yaml +# deployment.yml +deployments: +- git: git@github.com/example/example.git + onlyRender: true +- path: kustomize_example +``` +```yaml +# kustomize_example/kustomization.yml +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +bases: + - ../example +patches: + - # your patches here +``` From 5533d3b4efb3d9a02cf132cafedad233ea5f19f9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Apr 2023 09:13:03 +0200 Subject: [PATCH 0870/2268] chore: Upgrade many k8s api/client related libs to latest version --- e2e/test-utils/envtest_cluster_callback.go | 4 +- go.mod | 33 ++++++------ go.sum | 62 +++++++++++----------- 3 files changed, 53 insertions(+), 46 deletions(-) diff --git a/e2e/test-utils/envtest_cluster_callback.go b/e2e/test-utils/envtest_cluster_callback.go index 54b95fc02..d28a532a9 100644 --- a/e2e/test-utils/envtest_cluster_callback.go +++ b/e2e/test-utils/envtest_cluster_callback.go @@ -26,7 +26,9 @@ func (k *EnvTestCluster) buildServeCallback(gvr schema.GroupVersionResource, cb return admission.Allowed("") }), } - _ = wh.InjectLogger(logr.New(log.NullLogSink{})) + wh.LogConstructor = func(base logr.Logger, req *admission.Request) logr.Logger { + return logr.New(log.NullLogSink{}) + } return wh } diff --git a/go.mod b/go.mod index d1822c4f6..4cebb73b0 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/ohler55/ojg v1.18.4 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 - github.com/rogpeppe/go-internal v1.9.0 + github.com/rogpeppe/go-internal v1.10.0 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 @@ -38,16 +38,16 @@ require ( golang.org/x/crypto v0.7.0 golang.org/x/net v0.8.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.6.0 + golang.org/x/sys v0.7.0 golang.org/x/term v0.6.0 golang.org/x/text v0.9.0 gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.1 + gopkg.in/yaml.v3 v3.0.1 // indirect helm.sh/helm/v3 v3.11.2 - k8s.io/api v0.26.3 - k8s.io/apiextensions-apiserver v0.26.3 + k8s.io/api v0.27.1 + k8s.io/apiextensions-apiserver v0.27.1 k8s.io/apimachinery v0.27.1 - k8s.io/client-go v0.26.3 + k8s.io/client-go v0.27.1 k8s.io/klog/v2 v2.90.1 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 @@ -59,10 +59,10 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.18.18 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0 github.com/go-git/go-git/v5 v5.6.1 - github.com/go-logr/logr v1.2.3 + github.com/go-logr/logr v1.2.4 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.27.4 + github.com/onsi/gomega v1.27.6 github.com/otiai10/copy v1.10.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.5 @@ -70,6 +70,9 @@ require ( sigs.k8s.io/yaml v1.3.0 ) +// TODO: when controller-runtime past v0.14.6 is released, remove this line +replace sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22 + require ( cloud.google.com/go/compute v1.14.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect @@ -194,10 +197,10 @@ require ( github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_golang v1.15.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.39.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect github.com/rivo/uniseg v0.4.3 // indirect github.com/rubenv/sql-migrate v1.3.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -220,7 +223,7 @@ require ( go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.9.0 // indirect - golang.org/x/oauth2 v0.3.0 // indirect + golang.org/x/oauth2 v0.5.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.7.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect @@ -228,15 +231,15 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect google.golang.org/grpc v1.52.0 // indirect - google.golang.org/protobuf v1.28.1 // 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/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gotest.tools/v3 v3.2.0 // indirect - k8s.io/apiserver v0.26.3 // indirect + k8s.io/apiserver v0.27.1 // indirect k8s.io/cli-runtime v0.26.0 // indirect - k8s.io/component-base v0.26.3 // indirect + k8s.io/component-base v0.27.1 // indirect k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // indirect k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect diff --git a/go.sum b/go.sum index 239984801..4ae3c05a0 100644 --- a/go.sum +++ b/go.sum @@ -289,8 +289,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 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 v1.2.0/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/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -308,7 +308,7 @@ github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6A github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 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/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= @@ -652,9 +652,9 @@ github.com/ohler55/ojg v1.18.4 h1:FXHUddnBgrx5RwlkTDJnXBL5s3DNOMlZfv4K27nNNtM= github.com/ohler55/ojg v1.18.4/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= +github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= @@ -695,8 +695,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -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_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= 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= @@ -706,14 +706,14 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= -github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -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/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= @@ -725,8 +725,9 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L 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/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rubenv/sql-migrate v1.3.1 h1:Vx+n4Du8X8VTYuXbhNxdEUoh6wiJERA0GlWocR5FrbA= github.com/rubenv/sql-migrate v1.3.1/go.mod h1:YzG/Vh82CwyhTFXy+Mf5ahAiiEOpAlHurg+23VEzcsk= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -986,8 +987,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8= -golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= +golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= 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= @@ -1075,8 +1076,9 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1286,8 +1288,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +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= @@ -1332,20 +1334,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.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/api v0.27.1 h1:Z6zUGQ1Vd10tJ+gHcNNNgkV5emCyW+v2XTmn+CLjSd0= +k8s.io/api v0.27.1/go.mod h1:z5g/BpAiD+f6AArpqNjkY+cji8ueZDU/WV1jcj5Jk4E= +k8s.io/apiextensions-apiserver v0.27.1 h1:Hp7B3KxKHBZ/FxmVFVpaDiXI6CCSr49P1OJjxKO6o4g= +k8s.io/apiextensions-apiserver v0.27.1/go.mod h1:8jEvRDtKjVtWmdkhOqE84EcNWJt/uwF8PC4627UZghY= k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc= k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= -k8s.io/apiserver v0.26.3 h1:blBpv+yOiozkPH2aqClhJmJY+rp53Tgfac4SKPDJnU4= -k8s.io/apiserver v0.26.3/go.mod h1:CJe/VoQNcXdhm67EvaVjYXxR3QyfwpceKPuPaeLibTA= +k8s.io/apiserver v0.27.1 h1:phY+BtXjjzd+ta3a4kYbomC81azQSLa1K8jo9RBw7Lg= +k8s.io/apiserver v0.27.1/go.mod h1:UGrOjLY2KsieA9Fw6lLiTObxTb8Z1xEba4uqSuMY0WU= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= -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/component-base v0.26.3 h1:oC0WMK/ggcbGDTkdcqefI4wIZRYdK3JySx9/HADpV0g= -k8s.io/component-base v0.26.3/go.mod h1:5kj1kZYwSC6ZstHJN7oHBqcJC6yyn41eR+Sqa/mQc8E= +k8s.io/client-go v0.27.1 h1:oXsfhW/qncM1wDmWBIuDzRHNS2tLhK3BZv512Nc59W8= +k8s.io/client-go v0.27.1/go.mod h1:f8LHMUkVb3b9N8bWturc+EDtVVVwZ7ueTVquFAJb2vA= +k8s.io/component-base v0.27.1 h1:kEB8p8lzi4gCs5f2SPU242vOumHJ6EOsOnDM3tTuDTM= +k8s.io/component-base v0.27.1/go.mod h1:UGEd8+gxE4YWoigz5/lb3af3Q24w98pDseXcXZjw+E0= k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a h1:gmovKNur38vgoWfGtP5QOGNOA7ki4n6qNYoFAgMlNvg= @@ -1360,8 +1362,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/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-runtime v0.13.1-0.20230426175615-dca0be70fd22 h1:KnAZ+ITT57UpjlUM9AsuuPzzF0OjZAhtSgEPeQUHRzg= +sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22/go.mod h1:ujEX5tSkpg5cCOhcwDWLsXwNuMCO+j4rpmmkIn6BGGc= 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/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= From a63ddcbbc908dcfe2f78dcfaa9ab142054b27ae6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Apr 2023 09:44:12 +0200 Subject: [PATCH 0871/2268] chore: Upgrade kustomize --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 4cebb73b0..1c82bd6db 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( k8s.io/apimachinery v0.27.1 k8s.io/client-go v0.27.1 k8s.io/klog/v2 v2.90.1 - sigs.k8s.io/kustomize/kyaml v0.13.9 + sigs.k8s.io/kustomize/kyaml v0.14.1 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) @@ -66,7 +66,7 @@ require ( github.com/otiai10/copy v1.10.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.5 - sigs.k8s.io/kustomize/api v0.12.1 + sigs.k8s.io/kustomize/api v0.13.2 sigs.k8s.io/yaml v1.3.0 ) @@ -238,7 +238,7 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gotest.tools/v3 v3.2.0 // indirect k8s.io/apiserver v0.27.1 // indirect - k8s.io/cli-runtime v0.26.0 // indirect + k8s.io/cli-runtime v0.27.1 // indirect k8s.io/component-base v0.27.1 // indirect k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // indirect k8s.io/kubectl v0.26.0 // indirect diff --git a/go.sum b/go.sum index 4ae3c05a0..3f52a2aab 100644 --- a/go.sum +++ b/go.sum @@ -1342,8 +1342,8 @@ k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc= k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= k8s.io/apiserver v0.27.1 h1:phY+BtXjjzd+ta3a4kYbomC81azQSLa1K8jo9RBw7Lg= k8s.io/apiserver v0.27.1/go.mod h1:UGrOjLY2KsieA9Fw6lLiTObxTb8Z1xEba4uqSuMY0WU= -k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= -k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= +k8s.io/cli-runtime v0.27.1 h1:MMzp5Q/Xmr5L1Lrowuc+Y/r95XINC6c6/fE3aN7JDRM= +k8s.io/cli-runtime v0.27.1/go.mod h1:tEbTB1XP/nTH3wujsi52bw91gWpErtWiS15R6CwYsAI= k8s.io/client-go v0.27.1 h1:oXsfhW/qncM1wDmWBIuDzRHNS2tLhK3BZv512Nc59W8= k8s.io/client-go v0.27.1/go.mod h1:f8LHMUkVb3b9N8bWturc+EDtVVVwZ7ueTVquFAJb2vA= k8s.io/component-base v0.27.1 h1:kEB8p8lzi4gCs5f2SPU242vOumHJ6EOsOnDM3tTuDTM= @@ -1366,10 +1366,10 @@ sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22 h1:KnAZ+ITT sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22/go.mod h1:ujEX5tSkpg5cCOhcwDWLsXwNuMCO+j4rpmmkIn6BGGc= 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/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= -sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= -sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= -sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= +sigs.k8s.io/kustomize/api v0.13.2 h1:kejWfLeJhUsTGioDoFNJET5LQe/ajzXhJGYoU+pJsiA= +sigs.k8s.io/kustomize/api v0.13.2/go.mod h1:DUp325VVMFVcQSq+ZxyDisA8wtldwHxLZbr1g94UHsw= +sigs.k8s.io/kustomize/kyaml v0.14.1 h1:c8iibius7l24G2wVAGZn/Va2wNys03GXLjYVIcFVxKA= +sigs.k8s.io/kustomize/kyaml v0.14.1/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4= 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.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= From c946a2f2ba7a632a52ce5392806a6b0cea50e95f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Apr 2023 10:03:32 +0200 Subject: [PATCH 0872/2268] chore: Upgrade helm --- go.mod | 32 +++++++++++++----------- go.sum | 78 ++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/go.mod b/go.mod index 1c82bd6db..4d68198ed 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( golang.org/x/text v0.9.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 // indirect - helm.sh/helm/v3 v3.11.2 + helm.sh/helm/v3 v3.11.3 k8s.io/api v0.27.1 k8s.io/apiextensions-apiserver v0.27.1 k8s.io/apimachinery v0.27.1 @@ -74,10 +74,11 @@ require ( replace sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22 require ( - cloud.google.com/go/compute v1.14.0 // indirect + cloud.google.com/go/compute v1.18.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.8.0 // indirect - cloud.google.com/go/kms v1.6.0 // indirect + cloud.google.com/go/iam v0.12.0 // indirect + cloud.google.com/go/kms v1.9.0 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect @@ -111,11 +112,11 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.3.0 // indirect - github.com/containerd/containerd v1.6.15 // indirect + github.com/containerd/containerd v1.7.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v20.10.21+incompatible // indirect - github.com/docker/docker v20.10.21+incompatible // indirect + github.com/docker/docker v20.10.24+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -133,6 +134,7 @@ require ( github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.22.3 // indirect @@ -148,7 +150,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect @@ -168,7 +170,7 @@ require ( github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.15 // indirect + github.com/klauspost/compress v1.16.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect @@ -191,7 +193,7 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect @@ -212,7 +214,7 @@ require ( github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect - github.com/urfave/cli v1.22.7 // indirect + github.com/urfave/cli v1.22.12 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -220,6 +222,8 @@ require ( github.com/xlab/treeprint v1.1.0 // indirect go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.9.0 // indirect @@ -227,10 +231,10 @@ require ( golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.7.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/api v0.107.0 // indirect + google.golang.org/api v0.110.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect - google.golang.org/grpc v1.52.0 // indirect + google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect + google.golang.org/grpc v1.53.0 // 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 @@ -242,7 +246,7 @@ require ( k8s.io/component-base v0.27.1 // indirect k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // indirect k8s.io/kubectl v0.26.0 // indirect - k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect oras.land/oras-go v1.2.2 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect ) diff --git a/go.sum b/go.sum index 3f52a2aab..b7133dd0c 100644 --- a/go.sum +++ b/go.sum @@ -20,25 +20,25 @@ cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPT cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= +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.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= +cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/kms v1.9.0 h1:b0votJQa/9DSsxgHwN33/tTLA7ZHVzfWhDCrfiXijSo= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= 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= @@ -52,6 +52,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 h1:tz19qLF65vuu2ibfTqGVJxG/zZAI27NEIIbvAOQwYbw= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= @@ -89,7 +91,7 @@ github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= +github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= @@ -182,9 +184,9 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX 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/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/containerd v1.6.15 h1:4wWexxzLNHNE46aIETc6ge4TofO550v+BlLoANrbses= -github.com/containerd/containerd v1.6.15/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg= +github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -194,7 +196,6 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -214,8 +215,8 @@ github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SH github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= -github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= +github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -289,8 +290,11 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 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 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.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/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-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -414,8 +418,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= 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/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= @@ -527,8 +531,8 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 h1:K96SwIr8MzBQ3kFFz2H/pA2y+EEk04vZ4fWj/YZghBU= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2/go.mod h1:vzngsPshNKUtq0gxkYQKNJafrcH7Qy7Qt6yGNt7JmQI= github.com/kluctl/go-jinja2 v0.0.0-20230310104535-8136715a1e5a h1:48Mz5ZZmv64Pg/cKR4nIo3Ry49Hqs5YggLPGju0e8PU= @@ -627,7 +631,7 @@ github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -657,9 +661,9 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= -github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= +github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= +github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/otiai10/copy v1.10.0 h1:znyI7l134wNg/wDktoVQPxPkgvhDfGCYUasey+h0rDQ= github.com/otiai10/copy v1.10.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= @@ -802,8 +806,8 @@ 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-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v1.22.7 h1:aXiFAgRugfJ27UFDsGJ9DB2FvTC73hlVXFSqq5bo9eU= -github.com/urfave/cli v1.22.7/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= +github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -847,6 +851,10 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20221205180719-3fd0dac74452 h1:JZtNuL6LPB+scU5yaQ6hqRlJFRiddZm2FwRt2AQqtHA= go.starlark.net v0.0.0-20221205180719-3fd0dac74452/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= @@ -1195,8 +1203,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= 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= @@ -1249,8 +1257,8 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1273,8 +1281,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/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.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= 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= @@ -1325,8 +1333,8 @@ 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.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I= gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= -helm.sh/helm/v3 v3.11.2 h1:P3cLaFxfoxaGLGJVnoPrhf1j86LC5EDINSpYSpMUkkA= -helm.sh/helm/v3 v3.11.2/go.mod h1:Hw+09mfpDiRRKAgAIZlFkPSeOkvv7Acl5McBvQyNPVw= +helm.sh/helm/v3 v3.11.3 h1:n1X5yaQTP5DYywlBOZMl2gX398Gp6YwFp/IAVj6+5D4= +helm.sh/helm/v3 v3.11.3/go.mod h1:S+sOdQc3BLvt09a9rSlKKVs9x0N/yx+No0y3qFw+FQ8= 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= @@ -1354,8 +1362,8 @@ k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a h1:gmovKNur38vgoWfGtP5QOG k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= +k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From 5adbddfc4caa641d03308464bc92ede38c5fda65 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Apr 2023 10:04:55 +0200 Subject: [PATCH 0873/2268] refactor: Simplify RemoveDuplicateFields --- go.mod | 4 ++-- pkg/yaml/yaml.go | 41 ++++++++++++++--------------------------- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 4d68198ed..37d242d34 100644 --- a/go.mod +++ b/go.mod @@ -41,8 +41,6 @@ require ( golang.org/x/sys v0.7.0 golang.org/x/term v0.6.0 golang.org/x/text v0.9.0 - gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.1 // indirect helm.sh/helm/v3 v3.11.3 k8s.io/api v0.27.1 k8s.io/apiextensions-apiserver v0.27.1 @@ -240,6 +238,8 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.2.0 // indirect k8s.io/apiserver v0.27.1 // indirect k8s.io/cli-runtime v0.27.1 // indirect diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 95bfb5a33..b28c789eb 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -3,12 +3,10 @@ package yaml import ( "bytes" "encoding/json" - "errors" "fmt" "github.com/kluctl/kluctl/v2/pkg/utils" "golang.org/x/text/encoding/unicode" "golang.org/x/text/transform" - yaml2 "gopkg.in/yaml.v2" "io" "os" "path/filepath" @@ -81,6 +79,10 @@ func ReadYamlAllBytes(b []byte) ([]interface{}, error) { var docsSep = regexp.MustCompile("(?:^|\\s*\n)---\\s*") func ReadYamlAllStream(r io.Reader) ([]interface{}, error) { + return readYamlAllStream(r, true) +} + +func readYamlAllStream(r io.Reader, strict bool) ([]interface{}, error) { r = newUnicodeReader(r) b, err := io.ReadAll(r) @@ -103,7 +105,11 @@ func ReadYamlAllStream(r io.Reader) ([]interface{}, error) { } var x any - err = yaml.UnmarshalStrict([]byte(doc), &x) + if strict { + err = yaml.UnmarshalStrict([]byte(doc), &x) + } else { + err = yaml.Unmarshal([]byte(doc), &x) + } if err != nil { return nil, err } @@ -188,32 +194,13 @@ func WriteJsonString(o interface{}) (string, error) { } // RemoveDuplicateFields is a helper/hack to remove duplicate fields from yaml maps/structs. The yaml spec explicitly -// forbids duplicate keys, but yaml.v2 ignored those by default, leading to some tools (e.g. Helm) ignoring these. This -// forces us to also ignore/remove them in some cases. We do this by loading the yaml via yaml.v2 and then writing them -// back to a string which can then be parsed by yaml.v3 -// TODO Remove this helper method when https://github.com/go-yaml/yaml/issues/751 is implemented +// forbids duplicate keys, but yaml.v2 ignored those by default, leading to some tools (e.g. Helm) ignoring these func RemoveDuplicateFields(r io.Reader) ([]byte, error) { - buf := bytes.NewBuffer(nil) - d := yaml2.NewDecoder(r) - e := yaml2.NewEncoder(buf) - - for { - var o interface{} - err := d.Decode(&o) - if err != nil { - if errors.Is(err, io.EOF) { - break - } - return nil, err - } - if o != nil { - err = e.Encode(o) - if err != nil { - return nil, err - } - } + docs, err := readYamlAllStream(r, false) + if err != nil { + return nil, err } - return buf.Bytes(), nil + return WriteYamlAllBytes(docs) } func FixNameExt(dir string, name string) string { From 7eded7c74e93c0d380eaf9e1238154f6ec3122a6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 27 Apr 2023 10:17:35 +0200 Subject: [PATCH 0874/2268] chore: Upgrade go-containerregistry --- go.mod | 11 +++++------ go.sum | 26 +++++++++++++------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 37d242d34..fa099ed8a 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/go-playground/validator/v10 v10.12.0 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/google/go-containerregistry v0.13.0 + github.com/google/go-containerregistry v0.14.0 github.com/hashicorp/vault/api v1.9.0 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.15 @@ -113,8 +113,8 @@ require ( github.com/containerd/containerd v1.7.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.21+incompatible // indirect - github.com/docker/docker v20.10.24+incompatible // indirect + github.com/docker/cli v23.0.1+incompatible // indirect + github.com/docker/docker v23.0.1+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -225,7 +225,7 @@ require ( go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.9.0 // indirect - golang.org/x/oauth2 v0.5.0 // indirect + golang.org/x/oauth2 v0.6.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.7.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect @@ -240,13 +240,12 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.2.0 // indirect k8s.io/apiserver v0.27.1 // indirect k8s.io/cli-runtime v0.27.1 // indirect k8s.io/component-base v0.27.1 // indirect k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // indirect k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect - oras.land/oras-go v1.2.2 // indirect + oras.land/oras-go v1.2.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect ) diff --git a/go.sum b/go.sum index b7133dd0c..df3bf349c 100644 --- a/go.sum +++ b/go.sum @@ -188,7 +188,7 @@ github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaD github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg= github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= -github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= +github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -211,12 +211,12 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= -github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= -github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM= +github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= -github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY= +github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -390,8 +390,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k= -github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo= +github.com/google/go-containerregistry v0.14.0 h1:z58vMqHxuwvAsVwvKEkmVBz2TlgBgH5k6koEXBtlYkw= +github.com/google/go-containerregistry v0.14.0/go.mod h1:aiJ2fp/SXvkWgmYHioXnbMdlgB8eXiiYOY55gfN91Wk= github.com/google/gofuzz v1.0.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= @@ -995,8 +995,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= 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= @@ -1331,8 +1331,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C 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.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I= -gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= helm.sh/helm/v3 v3.11.3 h1:n1X5yaQTP5DYywlBOZMl2gX398Gp6YwFp/IAVj6+5D4= helm.sh/helm/v3 v3.11.3/go.mod h1:S+sOdQc3BLvt09a9rSlKKVs9x0N/yx+No0y3qFw+FQ8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1364,8 +1364,8 @@ k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= -oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= +oras.land/oras-go v1.2.3 h1:v8PJl+gEAntI1pJ/LCrDgsuk+1PKVavVEPsYIHFE5uY= +oras.land/oras-go v1.2.3/go.mod h1:M/uaPdYklze0Vf3AakfarnpoEckvw0ESbRdN8Z1vdJg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= From 1012a959c32c9e5d12f4c2f75f8c5fbb6dd56aab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 09:16:15 +0200 Subject: [PATCH 0875/2268] chore(deps): Bump golang.org/x/term from 0.6.0 to 0.7.0 (#431) Bumps [golang.org/x/term](https://github.com/golang/term) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/golang/term/releases) - [Commits](https://github.com/golang/term/compare/v0.6.0...v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fa099ed8a..3cdeda2d3 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( golang.org/x/net v0.8.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.7.0 - golang.org/x/term v0.6.0 + golang.org/x/term v0.7.0 golang.org/x/text v0.9.0 helm.sh/helm/v3 v3.11.3 k8s.io/api v0.27.1 diff --git a/go.sum b/go.sum index df3bf349c..c7a8f30d2 100644 --- a/go.sum +++ b/go.sum @@ -1095,8 +1095,8 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 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= From caf94eaefa0d43109b195f453ce65edfd4081776 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 09:16:29 +0200 Subject: [PATCH 0876/2268] chore(deps): Bump golang.org/x/net from 0.8.0 to 0.9.0 (#432) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.8.0 to 0.9.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3cdeda2d3..801ac99ae 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/stretchr/testify v1.8.2 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.7.0 - golang.org/x/net v0.8.0 + golang.org/x/net v0.9.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.7.0 golang.org/x/term v0.7.0 diff --git a/go.sum b/go.sum index c7a8f30d2..a436a6384 100644 --- a/go.sum +++ b/go.sum @@ -981,8 +981,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 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= From bb95b8269fcd5b191e5561fdc831d3dfa9daa316 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 09:24:24 +0200 Subject: [PATCH 0877/2268] chore(deps): Bump github.com/otiai10/copy from 1.10.0 to 1.11.0 (#433) Bumps [github.com/otiai10/copy](https://github.com/otiai10/copy) from 1.10.0 to 1.11.0. - [Release notes](https://github.com/otiai10/copy/releases) - [Commits](https://github.com/otiai10/copy/compare/v1.10.0...v1.11.0) --- updated-dependencies: - dependency-name: github.com/otiai10/copy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 801ac99ae..43dd48a8f 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 github.com/onsi/gomega v1.27.6 - github.com/otiai10/copy v1.10.0 + github.com/otiai10/copy v1.11.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.5 sigs.k8s.io/kustomize/api v0.13.2 diff --git a/go.sum b/go.sum index a436a6384..130804335 100644 --- a/go.sum +++ b/go.sum @@ -665,8 +665,8 @@ github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1 github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= -github.com/otiai10/copy v1.10.0 h1:znyI7l134wNg/wDktoVQPxPkgvhDfGCYUasey+h0rDQ= -github.com/otiai10/copy v1.10.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= +github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= +github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= From 080ac65dd5cddd54effcc3ed40f621038ac76ba4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 09:24:37 +0200 Subject: [PATCH 0878/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/config (#434) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.18 to 1.18.22. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.18...config/v1.18.22) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 22 +++++++++++----------- go.sum | 41 ++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 43dd48a8f..a88d7e4f2 100644 --- a/go.mod +++ b/go.mod @@ -53,8 +53,8 @@ require ( require ( filippo.io/age v1.1.1 - github.com/aws/aws-sdk-go-v2 v1.17.6 - github.com/aws/aws-sdk-go-v2/config v1.18.18 + github.com/aws/aws-sdk-go-v2 v1.18.0 + github.com/aws/aws-sdk-go-v2/config v1.18.22 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0 github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.4 @@ -93,16 +93,16 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.17 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.21 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index 130804335..8a19761c3 100644 --- a/go.sum +++ b/go.sum @@ -117,34 +117,37 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.17.6 h1:Y773UK7OBqhzi5VDXMi1zVGsoj+CVHs2eaC2bDsLwi0= github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.18 h1:/ePABXvXl3ESlzUGnkkvvNnRFw3Gh13dyqaq0Qo3JcU= -github.com/aws/aws-sdk-go-v2/config v1.18.18/go.mod h1:Lj3E7XcxJnxMa+AYo89YiL68s1cFJRGduChynYU67VA= -github.com/aws/aws-sdk-go-v2/credentials v1.13.17 h1:IubQO/RNeIVKF5Jy77w/LfUvmmCxTnk2TP1UZZIMiF4= -github.com/aws/aws-sdk-go-v2/credentials v1.13.17/go.mod h1:K9xeFo1g/YPMguMUD69YpwB4Nyi6W/5wn706xIInJFg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 h1:/2Cb3SK3xVOQA7Xfr5nCWCo5H3UiNINtsVvVdk8sQqA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0/go.mod h1:neYVaeKr5eT7BzwULuG2YbLhzWZ22lpjKdCybR7AXrQ= +github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= +github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.22 h1:7vkUEmjjv+giht4wIROqLs+49VWmiQMMHSduxmoNKLU= +github.com/aws/aws-sdk-go-v2/config v1.18.22/go.mod h1:mN7Li1wxaPxSSy4Xkr6stFuinJGf3VZW3ZSNvO0q6sI= +github.com/aws/aws-sdk-go-v2/credentials v1.13.21 h1:VRiXnPEaaPeGeoFcXvMZOB5K/yfIXOYE3q97Kgb0zbU= +github.com/aws/aws-sdk-go-v2/credentials v1.13.21/go.mod h1:90Dk1lJoMyspa/EDUrldTxsPns0wn6+KpRKpdAWc0uA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 h1:y+8n9AGDjikyXoMBTRaHHHSaFEB8267ykmvyPodJfys= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30/go.mod h1:LUBAO3zNXQjoONBKn/kR1y0Q4cj/D02Ts0uHYjcCQLM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 h1:r+Kv+SEJquhAZXaJ7G4u44cIwXV3f8K+N482NNAzJZA= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24/go.mod h1:gAuCezX/gob6BSMbItsSlMb6WZGV7K2+fWOvk8xBSto= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 h1:hf+Vhp5WtTdcSdE+yEcUz8L73sAzN0R+0jQv+Z51/mI= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31/go.mod h1:5zUjguZfG5qjhG9/wqmuyHRyUftl2B5Cp6NNxNC6kRA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 h1:c5qGfdbCHav6viBwiyDns3OXqhqAbGjfIB4uVu2ayhk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24/go.mod h1:HMA4FZG6fyib+NDo5bpIxX1EhYjrAOveZJY2YR0xrNE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0 h1:B4LvuBxrxh2WXakqwJL22EPAWgqGGK9/E4YQV/IIkYo= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0/go.mod h1:XF4Gbmcn6V9xIIm6lhwtyX1NXConNJ8x6yizt2Ejx/0= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 h1:bdKIX6SVF3nc3xJFw6Nf0igzS6Ff/louGq8Z6VP/3Hs= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.5/go.mod h1:vuWiaDB30M/QTC+lI3Wj6S/zb7tpUK2MSYgy3Guh2L0= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 h1:xLPZMyuZ4GuqRCIec/zWuIhRFPXh2UOJdLXBSi64ZWQ= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5/go.mod h1:QjxpHmCwAg0ESGtPQnLIVp7SedTOBMYy+Slr3IfMKeI= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 h1:rIFn5J3yDoeuKCE9sESXqM5POTAhOP1du3bv/qTL+tE= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.6/go.mod h1:48WJ9l3dwP0GSHWGc5sFGGlCkuA82Mc2xnw+T6Q8aDw= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 h1:GAiaQWuQhQQui76KjuXeShmyXqECwQ0mGRMc/rwsL+c= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.9/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 h1:TraLwncRJkWqtIBVKI/UqBymq4+hL+3MzUOtUATuzkA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 h1:6UbNM/KJhMBfOI5+lpVcJ/8OA7cBSz0O6OX37SRKlSw= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.10/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From 126666d54b700c660eb451fd17172218c81d0483 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Apr 2023 09:25:08 +0200 Subject: [PATCH 0879/2268] fix: Add support for all known default SSH keys (#428) This includes: - ~/.ssh/id_rsa - ~/.ssh/id_ecdsa - ~/.ssh/id_ed25519 - ~/.ssh/id_xmss - ~/.ssh/id_dsa --- pkg/git/auth/ssh_auth_provider.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index c6911e846..313e342ef 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -56,13 +56,16 @@ func (a *sshDefaultIdentityAndAgent) Signers() ([]ssh.Signer, error) { return a.signers, nil } -func (a *sshDefaultIdentityAndAgent) addDefaultIdentity(gitUrl git_url.GitUrl) { +func (a *sshDefaultIdentityAndAgent) addDefaultIdentities(gitUrl git_url.GitUrl) { a.authProvider.MessageCallbacks.Trace("trying to add default identity") u, err := user.Current() if err != nil { a.authProvider.MessageCallbacks.Trace("No current user: %v", err) - } else { - path := filepath.Join(u.HomeDir, ".ssh", "id_rsa") + return + } + + doAdd := func(name string) { + path := filepath.Join(u.HomeDir, ".ssh", name) signer, err := a.authProvider.readKey(a.ctx, path) if err != nil && !os.IsNotExist(err) { a.authProvider.MessageCallbacks.Warning("Failed to read default identity file for url %s: %v", gitUrl.String(), err) @@ -71,6 +74,12 @@ func (a *sshDefaultIdentityAndAgent) addDefaultIdentity(gitUrl git_url.GitUrl) { a.signers = append(a.signers, signer) } } + + doAdd("id_rsa") + doAdd("id_ecdsa") + doAdd("id_ed25519") + doAdd("id_xmss") + doAdd("id_dsa") } func (a *sshDefaultIdentityAndAgent) addConfigIdentities(gitUrl git_url.GitUrl) { @@ -152,7 +161,7 @@ func (a *GitSshAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUr // Try agent identities first. They might be unencrypted already, making passphrase prompts unnecessary auth.addAgentIdentities(gitUrl) - auth.addDefaultIdentity(gitUrl) + auth.addDefaultIdentities(gitUrl) auth.addConfigIdentities(gitUrl) return AuthMethodAndCA{ From 544a99e384eb9ddfe0dc695c3c48175ef7f9b001 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Apr 2023 09:25:23 +0200 Subject: [PATCH 0880/2268] feat: Upgrade go-jinja2 to latest main branch to implement sha256 prefix_length (#429) --- docs/reference/templating/filters.md | 7 ++++++- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/reference/templating/filters.md b/docs/reference/templating/filters.md index 5fed49201..b0050eeab 100644 --- a/docs/reference/templating/filters.md +++ b/docs/reference/templating/filters.md @@ -84,11 +84,16 @@ Renders the input string with the current Jinja2 context. Example: {{ a | render }} ``` -### sha256 +### sha256(digest_len) Calculates the sha256 digest of the input string. Example: ``` {{ "some-string" | sha256 }} ``` +`digest_len` is an optional parameter that allows to limit the length of the returned hex digest. Example: +``` +{{ "some-string" | sha256(6) }} +``` + ### slugify Slugify a string based on [python-slugify](https://github.com/un33k/python-slugify). diff --git a/go.mod b/go.mod index a88d7e4f2..c556d3c57 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 - github.com/kluctl/go-jinja2 v0.0.0-20230310104535-8136715a1e5a + github.com/kluctl/go-jinja2 v0.0.0-20230427204639-8226cd4a231e github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.18 github.com/mattn/go-runewidth v0.0.14 diff --git a/go.sum b/go.sum index 8a19761c3..7648c9561 100644 --- a/go.sum +++ b/go.sum @@ -538,8 +538,8 @@ github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 h1:K96SwIr8MzBQ3kFFz2H/pA2y+EEk04vZ4fWj/YZghBU= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2/go.mod h1:vzngsPshNKUtq0gxkYQKNJafrcH7Qy7Qt6yGNt7JmQI= -github.com/kluctl/go-jinja2 v0.0.0-20230310104535-8136715a1e5a h1:48Mz5ZZmv64Pg/cKR4nIo3Ry49Hqs5YggLPGju0e8PU= -github.com/kluctl/go-jinja2 v0.0.0-20230310104535-8136715a1e5a/go.mod h1:qMY3lmIUPcCfjj5fZm39j+h7FilaqaQFYAze19xG0Dw= +github.com/kluctl/go-jinja2 v0.0.0-20230427204639-8226cd4a231e h1:x5QMWvWLiuwJWojKXSH/3SAVkT2/y35Q5WlMSfnsDGk= +github.com/kluctl/go-jinja2 v0.0.0-20230427204639-8226cd4a231e/go.mod h1:16hIQ1ibrf02WYqeCPggfIBQx23lTUQ+jlk5kJGF0xI= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= From 03a5f2526b3ccc00c448c213f818eaae7421a8c4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Apr 2023 09:25:39 +0200 Subject: [PATCH 0881/2268] feat: Implement commonAnnotations (#430) * docs: Remove reference to deleteByLabels from delete command * feat: Implement commonAnnotations --- cmd/kluctl/commands/cmd_delete.go | 4 ++-- docs/reference/commands/delete.md | 4 ++-- docs/reference/deployments/deployment-yml.md | 6 ++++++ pkg/deployment/deployment_item.go | 5 ++--- pkg/deployment/deployment_project.go | 10 ++++++++++ pkg/types/deployment.go | 1 + 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 751410b04..e9dbe2091 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -28,10 +28,10 @@ type deleteCmd struct { } func (cmd *deleteCmd) Help() string { - return `Objects are located based on 'commonLabels', configured in 'deployment.yaml' + return `Objects are located based on the target discriminator. WARNING: This command will also delete objects which are not part of your deployment -project (anymore). It really only decides based on the 'deleteByLabel' labels and does NOT +project (anymore). It really only decides based on the discriminator and does NOT take the local target/state into account!` } diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index e8bb56368..4b1f73c93 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -14,10 +14,10 @@ description: > Usage: kluctl delete [flags] Delete a target (or parts of it) from the corresponding cluster -Objects are located based on 'commonLabels', configured in 'deployment.yaml' +Objects are located based on the target discriminator. WARNING: This command will also delete objects which are not part of your deployment -project (anymore). It really only decides based on the 'deleteByLabel' labels and does NOT +project (anymore). It really only decides based on the discriminator and does NOT take the local target/state into account! diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index c9ffb0808..aead29af5 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -259,6 +259,12 @@ kubernetes. This is due to an [implementation detail](https://github.com/kuberne kustomize which causes `commonLabels` to also be applied to label selectors, which makes otherwise editable resources read-only when it comes to `commonLabels`. +## commonAnnotations +A dictionary of [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) and values to be +added to all resources deployed by any of the deployment items in this deployment project. + +`commonAnnotations` are handled the same as [commonLabels](#commonlabels) in regard to inheriting, merging and overriding. + ## overrideNamespace A string that is used as the default namespace for all kustomize deployments which don't have a `namespace` set in their `kustomization.yaml`. diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 607ded1dd..11d769a6e 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -107,9 +107,8 @@ func (di *DeploymentItem) getCommonLabels() map[string]string { } func (di *DeploymentItem) getCommonAnnotations() map[string]string { - a := map[string]string{ - "kluctl.io/deployment-item-dir": filepath.ToSlash(di.RelToSourceItemDir), - } + a := di.Project.GetCommonAnnotations() + a["kluctl.io/deployment-item-dir"] = filepath.ToSlash(di.RelToSourceItemDir) if di.Config.SkipDeleteIfTags { a["kluctl.io/skip-delete-if-tags"] = "true" } diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 49e3263ba..88c496924 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -318,6 +318,16 @@ func (p *DeploymentProject) GetCommonLabels() map[string]string { return ret } +func (p *DeploymentProject) GetCommonAnnotations() map[string]string { + ret := make(map[string]string) + parents := p.getParents() + for i, _ := range parents { + d := parents[len(parents)-i-1] + uo.MergeStrMap(ret, d.p.Config.CommonAnnotations) + } + return ret +} + func (p *DeploymentProject) getOverrideNamespace() *string { for _, e := range p.getParents() { if e.p.Config.OverrideNamespace != nil { diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index dfa43ce2c..e2a5a4a8d 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -102,6 +102,7 @@ type DeploymentProjectConfig struct { Deployments []*DeploymentItemConfig `json:"deployments,omitempty"` CommonLabels map[string]string `json:"commonLabels,omitempty"` + CommonAnnotations map[string]string `json:"commonAnnotations,omitempty"` OverrideNamespace *string `json:"overrideNamespace,omitempty"` Tags []string `json:"tags,omitempty"` From 4ee6d5e47989afeaf11874f8577a8d86e81189ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 09:26:01 +0200 Subject: [PATCH 0882/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/service/secretsmanager (#435) Bumps [github.com/aws/aws-sdk-go-v2/service/secretsmanager](https://github.com/aws/aws-sdk-go-v2) from 1.19.0 to 1.19.6. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.19.0...service/efs/v1.19.6) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/secretsmanager dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c556d3c57..7e1109e3b 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2/config v1.18.22 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.6 github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.4 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index 7648c9561..bc154f69f 100644 --- a/go.sum +++ b/go.sum @@ -117,7 +117,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.18.22 h1:7vkUEmjjv+giht4wIROqLs+49VWmiQMMHSduxmoNKLU= @@ -127,11 +126,9 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.21/go.mod h1:90Dk1lJoMyspa/EDUrld github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30/go.mod h1:LUBAO3zNXQjoONBKn/kR1y0Q4cj/D02Ts0uHYjcCQLM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24/go.mod h1:gAuCezX/gob6BSMbItsSlMb6WZGV7K2+fWOvk8xBSto= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= @@ -140,8 +137,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAc github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0 h1:B4LvuBxrxh2WXakqwJL22EPAWgqGGK9/E4YQV/IIkYo= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0/go.mod h1:XF4Gbmcn6V9xIIm6lhwtyX1NXConNJ8x6yizt2Ejx/0= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.6 h1:xC25kY/HSssnA1lC0GFT8mfhmrpMql/24bkyWYDRgzU= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.6/go.mod h1:3ARttS6G6U3auEdKfaN4GlnfS9UxYE9nqub1+0YGycA= github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 h1:GAiaQWuQhQQui76KjuXeShmyXqECwQ0mGRMc/rwsL+c= github.com/aws/aws-sdk-go-v2/service/sso v1.12.9/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 h1:TraLwncRJkWqtIBVKI/UqBymq4+hL+3MzUOtUATuzkA= From cfaa3428870b9a134e216703edb6caee7fac5d0b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Apr 2023 14:12:45 +0200 Subject: [PATCH 0883/2268] feat: Allow .templateignore files in parent folders (#437) --- e2e/deployment_items_test.go | 55 +++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 +-- pkg/deployment/deployment_item.go | 1 + pkg/vars/vars.go | 4 +-- 5 files changed, 61 insertions(+), 5 deletions(-) diff --git a/e2e/deployment_items_test.go b/e2e/deployment_items_test.go index 8e07ffcbc..855e20120 100644 --- a/e2e/deployment_items_test.go +++ b/e2e/deployment_items_test.go @@ -135,3 +135,58 @@ namespace: %s "value.txt": "v1", }) } + +func TestTemplateIgnore(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + addConfigMapDeployment(p, "cm1", map[string]string{ + "k1": `{{ "a" }}`, + }, resourceOpts{ + name: "cm1", + namespace: p.TestSlug(), + }) + addConfigMapDeployment(p, "cm2", map[string]string{ + "k1": `{{ "a" }}`, + }, resourceOpts{ + name: "cm2", + namespace: p.TestSlug(), + }) + addConfigMapDeployment(p, "cm3", map[string]string{ + "k1": `{{ "a" }}`, + }, resourceOpts{ + name: "cm3", + namespace: p.TestSlug(), + }) + + // .templateignore outside of deployment item + p.UpdateFile(".templateignore", func(f string) (string, error) { + return `cm2/configmap-cm2.yml`, nil + }, "") + // .templateignore inside of deployment item + p.UpdateFile("cm3/.templateignore", func(f string) (string, error) { + return `/configmap-cm3.yml`, nil + }, "") + + p.KluctlMust("deploy", "--yes", "-t", "test") + cm1 := assertConfigMapExists(t, k, p.TestSlug(), "cm1") + cm2 := assertConfigMapExists(t, k, p.TestSlug(), "cm2") + cm3 := assertConfigMapExists(t, k, p.TestSlug(), "cm3") + + assert.Equal(t, map[string]any{ + "k1": "a", + }, cm1.Object["data"]) + assert.Equal(t, map[string]any{ + "k1": `{{ "a" }}`, + }, cm2.Object["data"]) + assert.Equal(t, map[string]any{ + "k1": `{{ "a" }}`, + }, cm3.Object["data"]) +} diff --git a/go.mod b/go.mod index 7e1109e3b..8ae0028fe 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 - github.com/kluctl/go-jinja2 v0.0.0-20230427204639-8226cd4a231e + github.com/kluctl/go-jinja2 v0.0.0-20230428103343-a832225dc94c github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.18 github.com/mattn/go-runewidth v0.0.14 diff --git a/go.sum b/go.sum index bc154f69f..db7836ad8 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 h1:K96SwIr8MzBQ3kFFz2H/pA2y+EEk04vZ4fWj/YZghBU= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2/go.mod h1:vzngsPshNKUtq0gxkYQKNJafrcH7Qy7Qt6yGNt7JmQI= -github.com/kluctl/go-jinja2 v0.0.0-20230427204639-8226cd4a231e h1:x5QMWvWLiuwJWojKXSH/3SAVkT2/y35Q5WlMSfnsDGk= -github.com/kluctl/go-jinja2 v0.0.0-20230427204639-8226cd4a231e/go.mod h1:16hIQ1ibrf02WYqeCPggfIBQx23lTUQ+jlk5kJGF0xI= +github.com/kluctl/go-jinja2 v0.0.0-20230428103343-a832225dc94c h1:qAIvhYamCEU/tY6NaENEIQCynGV5sdON7zgZKnbrhhw= +github.com/kluctl/go-jinja2 v0.0.0-20230428103343-a832225dc94c/go.mod h1:16hIQ1ibrf02WYqeCPggfIBQx23lTUQ+jlk5kJGF0xI= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 11d769a6e..4ac1d153e 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -162,6 +162,7 @@ func (di *DeploymentItem) render(forSeal bool) error { di.RenderedDir, excludePatterns, searchDirs, + di.Project.source.dir, ) } diff --git a/pkg/vars/vars.go b/pkg/vars/vars.go index cc3599471..11151f24d 100644 --- a/pkg/vars/vars.go +++ b/pkg/vars/vars.go @@ -91,10 +91,10 @@ func (vc *VarsCtx) RenderYamlFile(p string, searchDirs []string, out interface{} return nil } -func (vc *VarsCtx) RenderDirectory(sourceDir string, targetDir string, excludePatterns []string, searchDirs []string) error { +func (vc *VarsCtx) RenderDirectory(sourceDir string, targetDir string, excludePatterns []string, searchDirs []string, templateIgnoreRoot string) error { globals, err := vc.Vars.ToMap() if err != nil { return err } - return vc.J2.RenderDirectory(sourceDir, targetDir, excludePatterns, jinja2.WithGlobals(globals), jinja2.WithSearchDirs(searchDirs)) + return vc.J2.RenderDirectory(sourceDir, targetDir, excludePatterns, jinja2.WithGlobals(globals), jinja2.WithSearchDirs(searchDirs), jinja2.WithTemplateIgnoreRootDir(templateIgnoreRoot)) } From ee46c662e6a313940465df662965603d62c41de2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Apr 2023 16:35:36 +0200 Subject: [PATCH 0884/2268] feat: Simulate applying of CRs where the CRD is not applied yet (#438) --- pkg/deployment/utils/apply_utils.go | 36 +++++++++++++++++++++++++++-- pkg/yaml/validator.go | 7 ++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 67c17f045..e77c78c36 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -15,8 +15,10 @@ import ( "github.com/kluctl/kluctl/v2/pkg/validation" "github.com/kluctl/kluctl/v2/pkg/yaml" "golang.org/x/sync/semaphore" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" "reflect" "strings" "sync" @@ -49,6 +51,7 @@ type ApplyUtil struct { abortSignal *atomic.Value allNamespaces *sync.Map + allCRDs *sync.Map ru *RemoteObjectUtils k *k8s.K8sCluster @@ -67,10 +70,11 @@ type ApplyDeploymentsUtil struct { abortSignal atomic.Value - // Used to track all created namespaces + // Used to track all created namespaces and CRDs // All ApplyUtil instances write to this in parallel and we ignore that order might be unstable - // This is only used to simulate dryRun apply into new namespaces + // This is only used to simulate dryRun apply allNamespaces sync.Map + allCRDs sync.Map resultsMutex sync.Mutex results []*ApplyUtil @@ -103,6 +107,7 @@ func (ad *ApplyDeploymentsUtil) NewApplyUtil(ctx context.Context, statusCtx *sta deletedHookObjects: map[k8s2.ObjectRef]bool{}, abortSignal: &ad.abortSignal, allNamespaces: &ad.allNamespaces, + allCRDs: &ad.allCRDs, ru: ad.ru, k: ad.k, o: ad.o, @@ -371,10 +376,19 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo _ = r.ReplaceKeys(tmpName, ref.Name) _ = r.ReplaceValues(tmpName, ref.Name) r.SetK8sNamespace(ref.Namespace) + } else if a.o.DryRun && errors.IsNotFound(err) { + if _, ok := a.allCRDs.Load(x.GetK8sGVK()); ok { + a.handleResult(x, hook) + a.HandleWarning(x.GetK8sRef(), fmt.Errorf("the underyling custom resource definition for %s has not been applied yet as Kluctl is running in dry-run mode. It is not guaranteed that the object will actually sucessfully apply", x.GetK8sRef().String())) + return + } } if r != nil && ref.GVK.GroupKind().String() == "Namespace" { a.allNamespaces.Store(ref.Name, r) } + if r != nil && ref.GVK.GroupKind().String() == "CustomResourceDefinition.apiextensions.k8s.io" { + a.handleObservedCRD(r) + } a.handleApiWarnings(ref, apiWarnings) if err == nil { a.handleResult(r, hook) @@ -387,6 +401,24 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo } } +func (a *ApplyUtil) handleObservedCRD(r *uo.UnstructuredObject) { + y, err := yaml.WriteYamlBytes(r) + if err == nil { + var crd *apiextensionsv1.CustomResourceDefinition + err = yaml.ReadYamlBytes(y, &crd) + if err == nil { + for _, v := range crd.Spec.Versions { + gvk := schema.GroupVersionKind{ + Group: crd.Spec.Group, + Version: v.Name, + Kind: crd.Spec.Names.Kind, + } + a.allCRDs.Store(gvk, &crd) + } + } + } +} + func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) bool { if a.o.DryRun { return true diff --git a/pkg/yaml/validator.go b/pkg/yaml/validator.go index 4c3265da2..45f120744 100644 --- a/pkg/yaml/validator.go +++ b/pkg/yaml/validator.go @@ -4,16 +4,23 @@ import ( "github.com/go-playground/validator/v10" "github.com/mitchellh/reflectwalk" "reflect" + "time" ) var ( Validator = validator.New() + timeType = reflect.TypeOf(time.Time{}) ) type structValidationWalker struct { } func (w *structValidationWalker) Struct(v reflect.Value) error { + if v.Type().ConvertibleTo(timeType) { + // this is for some reason causing validation to fail + return reflectwalk.SkipEntry + } + v2 := v.Interface() err := Validator.Struct(v2) if err != nil { From 6f355698164f76d30e4c570642cc0a1e28edd4fd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Apr 2023 16:36:03 +0200 Subject: [PATCH 0885/2268] fix: Ignore ESC in readLine (#439) --- pkg/utils/term/read_line.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/utils/term/read_line.go b/pkg/utils/term/read_line.go index 910b039e7..8f5c9f6fd 100644 --- a/pkg/utils/term/read_line.go +++ b/pkg/utils/term/read_line.go @@ -17,6 +17,9 @@ func readLine(reader io.Reader, cb func(ret []byte)) ([]byte, error) { n, err := reader.Read(buf[:]) if n > 0 { switch buf[0] { + case '': + // ignore ESC sequences + continue case '', '\b': if len(ret) > 0 { ret = ret[:len(ret)-1] From 51758273cf141d8395e18f16f92d00b199995e7f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 28 Apr 2023 17:15:38 +0200 Subject: [PATCH 0886/2268] docs: Add docs for deleteObjects (#440) --- docs/reference/deployments/deployment-yml.md | 20 ++++++++++++++++++++ pkg/types/deployment.go | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index aead29af5..45d144ad3 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -128,6 +128,26 @@ deployments: - path: kustomizeDeployment3 ``` +### deleteObjects +Causes kluctl to delete matching objects, specified by a list of group/kind/name/namespace dictionaries. +The order/parallelization of deletion is identical to the order and parallelization of normal deployment items, +meaning that it happens in parallel by default until a barrier is encountered. + +Example: +```yaml +deployments: + - deleteObjects: + - group: apps + kind: DaemonSet + namespace: kube-system + name: kube-proxy + - barrier: true + - path: my-cni +``` + +The above example shows how to delete the kube-proxy DaemonSet before installing a CNI (e.g. Cilium in +proxy-replacement mode). + ## deployments common properties All entries in `deployments` can have the following common properties: diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index e2a5a4a8d..f5695951f 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -51,7 +51,7 @@ type DeleteObjectItemConfig struct { func ValidateDeleteObjectItemConfig(sl validator.StructLevel) { s := sl.Current().Interface().(DeleteObjectItemConfig) if s.Group == nil && s.Kind == nil { - sl.ReportError(s, "self", "self", "at least one of group/version/kind must be set", "") + sl.ReportError(s, "self", "self", "at least one of group or kind must be set", "") } } From 7701039161c20f8d30679fdae5335710fc59319e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 19:24:43 +0200 Subject: [PATCH 0887/2268] chore(deps): Bump github.com/bitnami-labs/sealed-secrets (#441) Bumps [github.com/bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) from 0.19.5 to 0.20.5. - [Release notes](https://github.com/bitnami-labs/sealed-secrets/releases) - [Changelog](https://github.com/bitnami-labs/sealed-secrets/blob/main/RELEASE-NOTES.md) - [Commits](https://github.com/bitnami-labs/sealed-secrets/compare/v0.19.5...v0.20.5) --- updated-dependencies: - dependency-name: github.com/bitnami-labs/sealed-secrets dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8ae0028fe..cd55454c1 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/Masterminds/semver/v3 v3.2.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/bitnami-labs/sealed-secrets v0.19.5 + github.com/bitnami-labs/sealed-secrets v0.20.5 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/fluxcd/pkg/kustomize v0.13.2 @@ -35,7 +35,7 @@ require ( github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.2 github.com/xanzy/ssh-agent v0.3.3 - golang.org/x/crypto v0.7.0 + golang.org/x/crypto v0.8.0 golang.org/x/net v0.9.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.7.0 diff --git a/go.sum b/go.sum index db7836ad8..7c61ef871 100644 --- a/go.sum +++ b/go.sum @@ -153,8 +153,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.19.5 h1:Llrs8bm5MdJEoPIQo0xZOHu/2i+Ry8N5bQFpc48UZYc= -github.com/bitnami-labs/sealed-secrets v0.19.5/go.mod h1:IC5f2r0c8mxjx8nHs+du+gBso2Wsbdb2lcTwVmOOu2Y= +github.com/bitnami-labs/sealed-secrets v0.20.5 h1:ejHSbd7GcJOWgAjBnkn+QFPTFSsNOZAJOomS4bM/LWk= +github.com/bitnami-labs/sealed-secrets v0.20.5/go.mod h1:EAN3XxSWbJOi2AwF0wAEo0bCAReAD0ToTP9euRw5nVQ= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -890,8 +890,8 @@ golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= 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= From a5700bbc6cfc0d633085f2afcc7141580ec68181 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 19:28:10 +0200 Subject: [PATCH 0888/2268] chore(deps): Bump github.com/spf13/cobra from 1.6.1 to 1.7.0 (#442) Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.6.1 to 1.7.0. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.6.1...v1.7.0) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index cd55454c1..3e681d428 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.10.0 github.com/sirupsen/logrus v1.9.0 - github.com/spf13/cobra v1.6.1 + github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.2 diff --git a/go.sum b/go.sum index 7c61ef871..c4051945a 100644 --- a/go.sum +++ b/go.sum @@ -499,7 +499,6 @@ github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -771,8 +770,8 @@ 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 v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +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.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= From a583a31659a182b661b11a91997418e235057bda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 19:29:21 +0200 Subject: [PATCH 0889/2268] chore(deps): Bump github.com/ohler55/ojg from 1.18.4 to 1.18.5 (#443) Bumps [github.com/ohler55/ojg](https://github.com/ohler55/ojg) from 1.18.4 to 1.18.5. - [Release notes](https://github.com/ohler55/ojg/releases) - [Changelog](https://github.com/ohler55/ojg/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/ojg/compare/v1.18.4...v1.18.5) --- updated-dependencies: - dependency-name: github.com/ohler55/ojg dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3e681d428..b0e2715cd 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/mattn/go-isatty v0.0.18 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.18.4 + github.com/ohler55/ojg v1.18.5 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.10.0 diff --git a/go.sum b/go.sum index c4051945a..c5b2aa562 100644 --- a/go.sum +++ b/go.sum @@ -651,8 +651,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/nelsam/hel/v2 v2.3.3/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/ohler55/ojg v1.18.4 h1:FXHUddnBgrx5RwlkTDJnXBL5s3DNOMlZfv4K27nNNtM= -github.com/ohler55/ojg v1.18.4/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= +github.com/ohler55/ojg v1.18.5 h1:tzn5LJtkSyXowCo8SlGieU0zEc7WF4143Ri9MYlQy7Q= +github.com/ohler55/ojg v1.18.5/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= From 97075430041c404ab0d1dda5d85e5100ac4bc697 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 19:30:13 +0200 Subject: [PATCH 0890/2268] chore(deps): Bump github.com/hashicorp/vault/api from 1.9.0 to 1.9.1 (#445) Bumps [github.com/hashicorp/vault/api](https://github.com/hashicorp/vault) from 1.9.0 to 1.9.1. - [Release notes](https://github.com/hashicorp/vault/releases) - [Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/vault/compare/v1.9.0...v1.9.1) --- updated-dependencies: - dependency-name: github.com/hashicorp/vault/api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b0e2715cd..0aad818e1 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-containerregistry v0.14.0 - github.com/hashicorp/vault/api v1.9.0 + github.com/hashicorp/vault/api v1.9.1 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.15 github.com/jinzhu/copier v0.3.5 diff --git a/go.sum b/go.sum index c5b2aa562..35c16e215 100644 --- a/go.sum +++ b/go.sum @@ -483,8 +483,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.9.0 h1:ab7dI6W8DuCY7yCU8blo0UCYl2oHre/dloCmzMWg9w8= -github.com/hashicorp/vault/api v1.9.0/go.mod h1:lloELQP4EyhjnCQhF8agKvWIVTmxbpEJj70b98959sM= +github.com/hashicorp/vault/api v1.9.1 h1:LtY/I16+5jVGU8rufyyAkwopgq/HpUnxFBg+QLOAV38= +github.com/hashicorp/vault/api v1.9.1/go.mod h1:78kktNcQYbBGSrOjQfHjXN32OhhxXnbYl3zxpd2uPUs= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= From b87cc3ee6e03ffdc74f3831be8d9ac746288513f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 23:11:57 +0200 Subject: [PATCH 0891/2268] chore(deps): Bump github.com/fluxcd/pkg/kustomize from 0.13.2 to 1.1.1 (#444) Bumps [github.com/fluxcd/pkg/kustomize](https://github.com/fluxcd/pkg) from 0.13.2 to 1.1.1. - [Release notes](https://github.com/fluxcd/pkg/releases) - [Commits](https://github.com/fluxcd/pkg/compare/runtime/v0.13.2...kustomize/v1.1.1) --- updated-dependencies: - dependency-name: github.com/fluxcd/pkg/kustomize dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 ++++-- go.sum | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0aad818e1..5fc4bdaf8 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/bitnami-labs/sealed-secrets v0.20.5 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible - github.com/fluxcd/pkg/kustomize v0.13.2 + github.com/fluxcd/pkg/kustomize v1.1.1 github.com/go-playground/validator/v10 v10.12.0 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 @@ -126,7 +126,9 @@ require ( github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect - github.com/fluxcd/pkg/apis/kustomize v0.8.0 // indirect + github.com/fluxcd/go-git/v5 v5.0.0-20221219190809-2e5c9d01cfc4 // indirect + github.com/fluxcd/pkg/apis/kustomize v1.0.0 // indirect + github.com/fluxcd/pkg/sourceignore v0.3.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect diff --git a/go.sum b/go.sum index 35c16e215..9ad1ee69d 100644 --- a/go.sum +++ b/go.sum @@ -94,12 +94,14 @@ github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2B github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -255,10 +257,14 @@ 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/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fluxcd/pkg/apis/kustomize v0.8.0 h1:A6aLolxPV2Sll44SOHiX96lbXXmRZmS5BoEerkRHrfM= -github.com/fluxcd/pkg/apis/kustomize v0.8.0/go.mod h1:9DPEVSfVIkiC2H3Dk6Ght4YJkswhYIaufXla4tB5Y84= -github.com/fluxcd/pkg/kustomize v0.13.2 h1:isA9yi+m7sSIxdTrFR1U7+LyS2BraG07ZkKLHw3bnGo= -github.com/fluxcd/pkg/kustomize v0.13.2/go.mod h1:1H9qednPxL/JvZE5at/f6wVHTH4WmxJYqfgVOZJ3uAk= +github.com/fluxcd/go-git/v5 v5.0.0-20221219190809-2e5c9d01cfc4 h1:Gm5sGGk+/Wq6RhX4xpCZ2IqjDp5XkjlhENaAuAlpdKc= +github.com/fluxcd/go-git/v5 v5.0.0-20221219190809-2e5c9d01cfc4/go.mod h1:raWgfUV7lDQVXp4QXUaeNNJkRVKz97UQuF+0kdY7Vmo= +github.com/fluxcd/pkg/apis/kustomize v1.0.0 h1:5T2b/mRZiGWtP7fvSU8gZOApIc06H6SdLX3MlsE6LRo= +github.com/fluxcd/pkg/apis/kustomize v1.0.0/go.mod h1:XaDYlKxrf9D2zZWcZ0BnSIqGtcm8mdNtJGzZWYjCnQo= +github.com/fluxcd/pkg/kustomize v1.1.1 h1:hYFJGi+fiaecY4gXvx52fumlvDEq/1RdFbaev67oBhE= +github.com/fluxcd/pkg/kustomize v1.1.1/go.mod h1:i+Z9iPAoSz28oH0FmDI73iqZ3oXZxQR2O3HfhdsWhfo= +github.com/fluxcd/pkg/sourceignore v0.3.3 h1:Ue29JAuPECEYdvIqdpXpQaDxpeySn7amarLArp7XoIs= +github.com/fluxcd/pkg/sourceignore v0.3.3/go.mod h1:yuJzKggph0Bdbk9LgXjJQhvJZSTJV/1vS7mJuB7mPa0= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -675,6 +681,7 @@ github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= +github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= @@ -810,6 +817,7 @@ github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= From a323315b881463e590544ce4c28b792805531583 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 29 Apr 2023 23:48:14 +0200 Subject: [PATCH 0892/2268] fix: Don't perform version check in completion command (#448) --- cmd/kluctl/commands/root.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 291655957..5a4b2589d 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -245,7 +245,9 @@ func Main() { } ctx = initStatusHandler(ctxIn, flags.Debug, flags.NoColor) if !flags.NoUpdateCheck { - checkNewVersion(ctx) + if len(os.Args) < 2 || (os.Args[1] != "completion" && os.Args[1] != "__complete") { + checkNewVersion(ctx) + } } return ctx, nil }) From 6443ac0a70fcb04c06cb9be51e5d13261459ac69 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 1 May 2023 21:30:24 +0200 Subject: [PATCH 0893/2268] feat: Implement --short-output flag (#451) * refactor: Pass OutputFormatFlags to outputCommandResult * feat: Implement --short-output flag --- cmd/kluctl/args/misc.go | 1 + cmd/kluctl/commands/cmd_delete.go | 2 +- cmd/kluctl/commands/cmd_deploy.go | 11 ++++++---- cmd/kluctl/commands/cmd_diff.go | 2 +- cmd/kluctl/commands/cmd_poke_images.go | 2 +- cmd/kluctl/commands/cmd_prune.go | 2 +- cmd/kluctl/commands/command_result.go | 28 +++++++++++++++----------- docs/reference/commands/delete.md | 3 +++ docs/reference/commands/deploy.md | 3 +++ docs/reference/commands/diff.md | 3 +++ docs/reference/commands/poke-images.md | 3 +++ docs/reference/commands/prune.md | 3 +++ 12 files changed, 43 insertions(+), 20 deletions(-) diff --git a/cmd/kluctl/args/misc.go b/cmd/kluctl/args/misc.go index 513e71165..45ec1050d 100644 --- a/cmd/kluctl/args/misc.go +++ b/cmd/kluctl/args/misc.go @@ -43,6 +43,7 @@ type AbortOnErrorFlags struct { type OutputFormatFlags struct { OutputFormat []string `group:"misc" short:"o" help:"Specify output format and target file, in the format 'format=path'. Format can either be 'text' or 'yaml'. Can be specified multiple times. The actual format for yaml is currently not documented and subject to change."` NoObfuscate bool `group:"misc" help:"Disable obfuscation of sensitive/secret data"` + ShortOutput bool `group:"misc" help:"When using the 'text' output format (which is the default), only names of changes objects are shown instead of showing all changes."` } type OutputFlags struct { diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index e9dbe2091..91d099332 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -61,7 +61,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(ctx, cmd.OutputFormat, cmd.NoObfuscate, result) + err = outputCommandResult(ctx, cmd.OutputFormatFlags, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 083a97e26..d6b6a23a3 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -64,7 +64,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { cmd2.NoWait = cmd.NoWait cb := func(diffResult *types.CommandResult) error { - return cmd.diffResultCb(cmdCtx.ctx, cmd.NoObfuscate, diffResult) + return cmd.diffResultCb(cmdCtx.ctx, diffResult) } if cmd.Yes || cmd.DryRun { cb = nil @@ -74,7 +74,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { if err != nil { return err } - err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormat, cmd.NoObfuscate, result) + err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormatFlags, result) if err != nil { return err } @@ -84,8 +84,11 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { return nil } -func (cmd *deployCmd) diffResultCb(ctx context.Context, noObfuscate bool, diffResult *types.CommandResult) error { - err := outputCommandResult(ctx, nil, noObfuscate, diffResult) +func (cmd *deployCmd) diffResultCb(ctx context.Context, diffResult *types.CommandResult) error { + flags := cmd.OutputFormatFlags + flags.OutputFormat = nil // use default output format + + err := outputCommandResult(ctx, flags, diffResult) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index b04ff519d..21edf81f1 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -50,7 +50,7 @@ func (cmd *diffCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(ctx, cmd.OutputFormat, cmd.NoObfuscate, result) + err = outputCommandResult(ctx, cmd.OutputFormatFlags, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index c1593e58e..76b69a538 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -51,7 +51,7 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(ctx, cmd.OutputFormat, cmd.NoObfuscate, result) + err = outputCommandResult(ctx, cmd.OutputFormatFlags, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 5b542cd27..c975dc8bd 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -54,7 +54,7 @@ func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { if err != nil { return err } - err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormat, cmd.NoObfuscate, result) + err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormatFlags, result) if err != nil { return err } diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index 1d43fda5c..27f648079 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/diff" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" @@ -15,7 +16,7 @@ import ( "strings" ) -func formatCommandResultText(cr *types.CommandResult) string { +func formatCommandResultText(cr *types.CommandResult, short bool) string { buf := bytes.NewBuffer(nil) if len(cr.Warnings) != 0 { @@ -39,12 +40,15 @@ func formatCommandResultText(cr *types.CommandResult) string { } prettyObjectRefs(buf, refs) - buf.WriteString("\n") - for i, co := range cr.ChangedObjects { - if i != 0 { - buf.WriteString("\n") + if !short { + buf.WriteString("\n") + + for i, co := range cr.ChangedObjects { + if i != 0 { + buf.WriteString("\n") + } + prettyChanges(buf, co.Ref, co.Changes) } - prettyChanges(buf, co.Ref, co.Changes) } } @@ -111,10 +115,10 @@ func formatCommandResultYaml(cr *types.CommandResult) (string, error) { return b, nil } -func formatCommandResult(cr *types.CommandResult, format string) (string, error) { +func formatCommandResult(cr *types.CommandResult, format string, short bool) (string, error) { switch format { case "text": - return formatCommandResultText(cr), nil + return formatCommandResultText(cr, short), nil case "yaml": return formatCommandResultYaml(cr) default: @@ -202,10 +206,10 @@ func outputHelper(ctx context.Context, output []string, cb func(format string) ( return nil } -func outputCommandResult(ctx context.Context, output []string, noObfuscate bool, cr *types.CommandResult) error { +func outputCommandResult(ctx context.Context, flags args.OutputFormatFlags, cr *types.CommandResult) error { status.Flush(ctx) - if !noObfuscate { + if !flags.NoObfuscate { var obfuscator diff.Obfuscator for _, c := range cr.ChangedObjects { err := obfuscator.Obfuscate(c.Ref, c.Changes) @@ -215,8 +219,8 @@ func outputCommandResult(ctx context.Context, output []string, noObfuscate bool, } } - return outputHelper(ctx, output, func(format string) (string, error) { - return formatCommandResult(cr, format) + return outputHelper(ctx, flags.OutputFormat, func(format string) (string, error) { + return formatCommandResult(cr, format, flags.ShortOutput) }) } diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index 4b1f73c93..da4ad7739 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -58,6 +58,9 @@ Misc arguments: currently not documented and subject to change. --render-output-dir string Specifies the target directory to render the project into. If omitted, a temporary directory is used. + --short-output When using the 'text' output format (which is the default), + only names of changes objects are shown instead of showing all + changes. -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. diff --git a/docs/reference/commands/deploy.md b/docs/reference/commands/deploy.md index edc7d6544..ea4d88e11 100644 --- a/docs/reference/commands/deploy.md +++ b/docs/reference/commands/deploy.md @@ -68,6 +68,9 @@ Misc arguments: omitted, a temporary directory is used. --replace-on-error When patching an object fails, try to replace it. See documentation for more details. + --short-output When using the 'text' output format (which is the default), + only names of changes objects are shown instead of showing all + changes. -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. diff --git a/docs/reference/commands/diff.md b/docs/reference/commands/diff.md index 4441498af..19909b274 100644 --- a/docs/reference/commands/diff.md +++ b/docs/reference/commands/diff.md @@ -64,6 +64,9 @@ Misc arguments: omitted, a temporary directory is used. --replace-on-error When patching an object fails, try to replace it. See documentation for more details. + --short-output When using the 'text' output format (which is the default), + only names of changes objects are shown instead of showing all + changes. ``` diff --git a/docs/reference/commands/poke-images.md b/docs/reference/commands/poke-images.md index 77a33c404..55ce70bbc 100644 --- a/docs/reference/commands/poke-images.md +++ b/docs/reference/commands/poke-images.md @@ -55,6 +55,9 @@ Misc arguments: currently not documented and subject to change. --render-output-dir string Specifies the target directory to render the project into. If omitted, a temporary directory is used. + --short-output When using the 'text' output format (which is the default), + only names of changes objects are shown instead of showing all + changes. -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. diff --git a/docs/reference/commands/prune.md b/docs/reference/commands/prune.md index 5bb327f3c..6d012920e 100644 --- a/docs/reference/commands/prune.md +++ b/docs/reference/commands/prune.md @@ -51,6 +51,9 @@ Misc arguments: currently not documented and subject to change. --render-output-dir string Specifies the target directory to render the project into. If omitted, a temporary directory is used. + --short-output When using the 'text' output format (which is the default), + only names of changes objects are shown instead of showing all + changes. -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. From 1468e535713cd4696f88d8413ebf74efe6225fa8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 1 May 2023 21:30:36 +0200 Subject: [PATCH 0894/2268] feat: Support SOPS decryption for all files involved in Kustomize deployments (#450) This is solved with the help of the decryptingFs that automatically decrypts all files that are loaded by Kustomize. --- pkg/deployment/deployment_item.go | 16 +++--- pkg/sops/decrypting_fs.go | 86 +++++++++++++++++++++++++++++++ pkg/sops/utils.go | 2 +- 3 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 pkg/sops/decrypting_fs.go diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 4ac1d153e..7c87d24b6 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -3,6 +3,7 @@ package deployment import ( "fmt" "github.com/fluxcd/pkg/kustomize" + securefs "github.com/fluxcd/pkg/kustomize/filesys" "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -513,18 +514,13 @@ func (di *DeploymentItem) buildKustomize() error { return err } - resources, _, _ := ky.GetNestedStringList("resources") - for _, r := range resources { - p := filepath.Join(di.RenderedDir, r) - if utils.IsFile(p) { - err = sops.MaybeDecryptFile(di.ctx.SopsDecrypter, p) - if err != nil { - return err - } - } + fs, err := securefs.MakeFsOnDiskSecureBuild(di.RenderedSourceRootDir) + if err != nil { + return err } - rm, err := kustomize.SecureBuild(di.RenderedSourceRootDir, di.RenderedDir, true) + fs = sops.NewDecryptingFs(fs, di.ctx.SopsDecrypter) + rm, err := kustomize.Build(fs, di.RenderedDir) if err != nil { return err } diff --git a/pkg/sops/decrypting_fs.go b/pkg/sops/decrypting_fs.go new file mode 100644 index 000000000..9bfc6f0b5 --- /dev/null +++ b/pkg/sops/decrypting_fs.go @@ -0,0 +1,86 @@ +package sops + +import ( + "bytes" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" + "go.mozilla.org/sops/v3/cmd/sops/formats" + "io" + "os" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +type decryptingFs struct { + filesys.FileSystem + decryptor *decryptor.Decryptor +} + +type file struct { + os.FileInfo + data *bytes.Buffer + size int64 +} + +func NewDecryptingFs(fs filesys.FileSystem, decryptor *decryptor.Decryptor) filesys.FileSystem { + return &decryptingFs{ + FileSystem: fs, + decryptor: decryptor, + } +} + +// Open opens the named file for reading. +func (fs *decryptingFs) Open(path string) (filesys.File, error) { + f, err := fs.FileSystem.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + st, err := f.Stat() + if err != nil { + return nil, err + } + b, err := io.ReadAll(f) + if err != nil { + return nil, err + } + + format := formats.FormatForPath(path) + b2, _, err := MaybeDecrypt(fs.decryptor, b, format, format) + if err != nil { + return nil, fmt.Errorf("failed to decrypt file %s: %w", path, err) + } + ret := &file{ + FileInfo: st, + data: bytes.NewBuffer(b2), + } + return ret, nil +} + +// ReadFile returns the contents of the file at the given path. +func (fs *decryptingFs) ReadFile(path string) ([]byte, error) { + f, err := fs.Open(path) + if err != nil { + return nil, err + } + return io.ReadAll(f) +} + +func (f file) Read(p []byte) (n int, err error) { + return f.data.Read(p) +} + +func (f file) Write(p []byte) (n int, err error) { + panic("write is not implemented") +} + +func (f file) Close() error { + return nil +} + +func (f file) Stat() (os.FileInfo, error) { + return f, nil +} + +func (f file) Size() int64 { + return int64(f.data.Len()) +} diff --git a/pkg/sops/utils.go b/pkg/sops/utils.go index ab6f881ce..66a733e72 100644 --- a/pkg/sops/utils.go +++ b/pkg/sops/utils.go @@ -17,7 +17,7 @@ func IsMaybeSopsFile(s []byte) bool { } func MaybeDecrypt(decrypter *decryptor.Decryptor, encrypted []byte, inputFormat, outputFormat formats.Format) ([]byte, bool, error) { - if decrypter == nil { + if decrypter == nil || inputFormat == formats.Binary { return encrypted, false, nil } From ae8bb1e78aa395577ceba475086cf4351a17edf0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 1 May 2023 22:47:42 +0200 Subject: [PATCH 0895/2268] feat: Implement targetPath in configMap/secret vars sources (#452) * refactor: Remove unneded key parameter from loadFromK8sObject * feat: Implement targetPath in configMap/secret vars sources --- docs/reference/templating/variable-sources.md | 36 ++++++++++++++--- pkg/types/vars_source.go | 9 +++-- pkg/utils/uo/jsonpath.go | 4 ++ pkg/vars/vars_loader.go | 39 +++++++++++++++---- pkg/vars/vars_loader_test.go | 29 +++++++++++++- 5 files changed, 98 insertions(+), 19 deletions(-) diff --git a/docs/reference/templating/variable-sources.md b/docs/reference/templating/variable-sources.md index 32d1654b3..4ed5dac62 100644 --- a/docs/reference/templating/variable-sources.md +++ b/docs/reference/templating/variable-sources.md @@ -99,10 +99,14 @@ Kluctl also supports variable files encrypted with [SOPS](https://github.com/moz [sops integration](../deployments/sops.md) integration for more details. ### clusterConfigMap -Loads a configmap from the target's cluster and loads the specified key's value as a yaml file into the jinja2 variables -context. +Loads a configmap from the target's cluster and loads the specified key's value into the templating context. The value +is treated and loaded as YAML and thus can either be a simple value or a complex nested structure. In case of a simple +value (e.g. a number), you must also specify `targetPath`. -Assume the following configmap to be deployed to the target cluster: +The referred ConfigMap must already exist while the Kluctl project is loaded, meaning that it is not possible to use +a ConfigMap that is deployed as part of the Kluctl project itself. + +Assume the following ConfigMap to be already deployed to the target cluster: ```yaml apiVersion: v1 kind: ConfigMap @@ -118,7 +122,7 @@ data: - l2 ``` -This configmap can be loaded via: +This ConfigMap can be loaded via: ```yaml vars: @@ -128,8 +132,28 @@ vars: key: vars ``` -It assumes that the configmap is already deployed before the kluctl deployment happens. This might for example be -useful to store meta information about the cluster itself and then make it available to kluctl deployments. +The following example uses a simple value: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-vars + namespace: my-namespace +data: + value: 123 +``` + +This ConfigMap can be loaded via: + +```yaml +vars: + - clusterConfigMap: + name: my-vars + namespace: my-namespace + key: value + targetPath: deep.nested.path +``` ### clusterSecret Same as clusterConfigMap, but for secrets. diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index 942cd6654..c195cdace 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -15,10 +15,11 @@ type VarsSourceGit struct { } type VarsSourceClusterConfigMapOrSecret struct { - Name string `json:"name,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Namespace string `json:"namespace" validate:"required"` - Key string `json:"key" validate:"required"` + Name string `json:"name,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Namespace string `json:"namespace" validate:"required"` + Key string `json:"key" validate:"required"` + TargetPath string `json:"targetPath,omitempty"` } func ValidateVarsSourceClusterConfigMapOrSecret(sl validator.StructLevel) { diff --git a/pkg/utils/uo/jsonpath.go b/pkg/utils/uo/jsonpath.go index 5263f8639..e21876c7f 100644 --- a/pkg/utils/uo/jsonpath.go +++ b/pkg/utils/uo/jsonpath.go @@ -146,3 +146,7 @@ func (j *MyJsonPath) GetFirstListOfObjects(o *UnstructuredObject) ([]*Unstructur func (j *MyJsonPath) Del(o *UnstructuredObject) error { return j.exp.Del(o.Object) } + +func (j *MyJsonPath) Set(o *UnstructuredObject, v any) error { + return j.exp.Set(o.Object, v) +} diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 9ce0ff997..49170f161 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -107,9 +107,9 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear } else if source.Git != nil { newVars, err = v.loadGit(varsCtx, source.Git, ignoreMissing) } else if source.ClusterConfigMap != nil { - newVars, err = v.loadFromK8sObject(varsCtx, *source.ClusterConfigMap, "ConfigMap", source.ClusterConfigMap.Key, ignoreMissing, false) + newVars, err = v.loadFromK8sObject(varsCtx, *source.ClusterConfigMap, "ConfigMap", ignoreMissing, false) } else if source.ClusterSecret != nil { - newVars, err = v.loadFromK8sObject(varsCtx, *source.ClusterSecret, "Secret", source.ClusterSecret.Key, ignoreMissing, true) + newVars, err = v.loadFromK8sObject(varsCtx, *source.ClusterSecret, "Secret", ignoreMissing, true) } else if source.SystemEnvVars != nil { newVars, err = v.loadSystemEnvs(varsCtx, &source, ignoreMissing, rootKey) } else if source.Http != nil { @@ -271,7 +271,7 @@ func (v *VarsLoader) loadGit(varsCtx *VarsCtx, gitFile *types.VarsSourceGit, ign return v.loadFile(varsCtx, gitFile.Path, ignoreMissing, []string{clonedDir}) } -func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, key string, ignoreMissing bool, base64Decode bool) (*uo.UnstructuredObject, error) { +func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSourceClusterConfigMapOrSecret, kind string, ignoreMissing bool, base64Decode bool) (*uo.UnstructuredObject, error) { if v.k == nil { return nil, fmt.Errorf("loading vars from cluster is disabled") } @@ -310,12 +310,12 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo ref := o.GetK8sRef() - f, found, err := o.GetNestedField("data", key) + f, found, err := o.GetNestedField("data", varsSource.Key) if err != nil { return nil, err } if !found { - return nil, fmt.Errorf("key %s not found in %s on cluster", key, ref.String()) + return nil, fmt.Errorf("key %s not found in %s on cluster", varsSource.Key, ref.String()) } var value string @@ -333,11 +333,34 @@ func (v *VarsLoader) loadFromK8sObject(varsCtx *VarsCtx, varsSource types.VarsSo } } - newVars, err := v.loadFromString(varsCtx, value) + doError := func(err error) (*uo.UnstructuredObject, error) { + return nil, fmt.Errorf("failed to load vars from kubernetes object %s and key %s: %w", ref.String(), varsSource.Key, err) + } + + var parsed any + err = v.renderYamlString(varsCtx, value, &parsed) if err != nil { - return nil, fmt.Errorf("failed to load vars from kubernetes object %s and key %s: %w", ref.String(), key, err) + return doError(err) + } + + if varsSource.TargetPath == "" { + m, ok := parsed.(map[string]any) + if !ok { + return doError(fmt.Errorf("value is not a YAML dictionary")) + } + return uo.FromMap(m), nil + } else { + p, err := uo.NewMyJsonPath(varsSource.TargetPath) + if err != nil { + return doError(err) + } + newVars := uo.New() + err = p.Set(newVars, parsed) + if err != nil { + return doError(err) + } + return newVars, nil } - return newVars, nil } func (v *VarsLoader) loadFromString(varsCtx *VarsCtx, s string) (*uo.UnstructuredObject, error) { diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index d4387a66d..b119e5278 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -293,7 +293,8 @@ func TestVarsLoader_ClusterConfigMap(t *testing.T) { cm := corev1.ConfigMap{ ObjectMeta: v1.ObjectMeta{Name: "cm", Namespace: "ns"}, Data: map[string]string{ - "vars": `{"test1": {"test2": 42}}`, + "vars": `{"test1": {"test2": 42}}`, + "value": "42", }, } @@ -343,6 +344,32 @@ func TestVarsLoader_ClusterConfigMap(t *testing.T) { }, nil, "") assert.NoError(t, err) }, &cm) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + ClusterConfigMap: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "cm", + Namespace: "ns", + Key: "value", + }, + }, nil, "") + assert.Errorf(t, err, "failed to load vars from kubernetes object ns/ConfigMap/cm and key value: value is not a YAML dictionary") + }, &cm) + + testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { + err := vl.LoadVars(vc, &types.VarsSource{ + ClusterConfigMap: &types.VarsSourceClusterConfigMapOrSecret{ + Name: "cm", + Namespace: "ns", + Key: "value", + TargetPath: "deep.nested.path", + }, + }, nil, "") + assert.NoError(t, err) + + v, _, _ := vc.Vars.GetNestedInt("deep", "nested", "path") + assert.Equal(t, int64(42), v) + }, &cm) } func TestVarsLoader_ClusterSecret(t *testing.T) { From 809c15465025b312d56b2335627d87f2b8c16649 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 May 2023 09:49:49 +0200 Subject: [PATCH 0896/2268] chore(deps): Bump github.com/go-playground/validator/v10 (#453) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.12.0 to 10.13.0. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.12.0...v10.13.0) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 5fc4bdaf8..b64d72345 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.1+incompatible github.com/fluxcd/pkg/kustomize v1.1.1 - github.com/go-playground/validator/v10 v10.12.0 + github.com/go-playground/validator/v10 v10.13.0 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-containerregistry v0.14.0 @@ -174,7 +174,7 @@ require ( github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/leodido/go-urn v1.2.2 // indirect + github.com/leodido/go-urn v1.2.3 // indirect github.com/lib/pq v1.10.7 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/go.sum b/go.sum index 9ad1ee69d..5603be931 100644 --- a/go.sum +++ b/go.sum @@ -313,8 +313,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ= +github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -562,8 +562,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4= -github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ= +github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= +github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -743,7 +743,6 @@ github.com/rubenv/sql-migrate v1.3.1/go.mod h1:YzG/Vh82CwyhTFXy+Mf5ahAiiEOpAlHur github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= From 60886ec9f02e37a05fbfceb5574814b40be666c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 May 2023 09:50:02 +0200 Subject: [PATCH 0897/2268] chore(deps): Bump k8s.io/klog/v2 from 2.90.1 to 2.100.1 (#454) Bumps [k8s.io/klog/v2](https://github.com/kubernetes/klog) from 2.90.1 to 2.100.1. - [Release notes](https://github.com/kubernetes/klog/releases) - [Changelog](https://github.com/kubernetes/klog/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes/klog/compare/v2.90.1...v2.100.1) --- updated-dependencies: - dependency-name: k8s.io/klog/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b64d72345..99855b111 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( k8s.io/apiextensions-apiserver v0.27.1 k8s.io/apimachinery v0.27.1 k8s.io/client-go v0.27.1 - k8s.io/klog/v2 v2.90.1 + k8s.io/klog/v2 v2.100.1 sigs.k8s.io/kustomize/kyaml v0.14.1 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) diff --git a/go.sum b/go.sum index 5603be931..c955af503 100644 --- a/go.sum +++ b/go.sum @@ -1362,8 +1362,8 @@ k8s.io/client-go v0.27.1 h1:oXsfhW/qncM1wDmWBIuDzRHNS2tLhK3BZv512Nc59W8= k8s.io/client-go v0.27.1/go.mod h1:f8LHMUkVb3b9N8bWturc+EDtVVVwZ7ueTVquFAJb2vA= k8s.io/component-base v0.27.1 h1:kEB8p8lzi4gCs5f2SPU242vOumHJ6EOsOnDM3tTuDTM= k8s.io/component-base v0.27.1/go.mod h1:UGEd8+gxE4YWoigz5/lb3af3Q24w98pDseXcXZjw+E0= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a h1:gmovKNur38vgoWfGtP5QOGNOA7ki4n6qNYoFAgMlNvg= k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= From 7ce6b68202b3f08e1df240480f180ce6b7ff7f03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 09:08:04 +0200 Subject: [PATCH 0898/2268] chore(deps): Bump golang.org/x/sync from 0.1.0 to 0.2.0 (#455) Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.1.0 to 0.2.0. - [Commits](https://github.com/golang/sync/compare/v0.1.0...v0.2.0) --- updated-dependencies: - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 99855b111..ec5a2fb01 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.8.0 golang.org/x/net v0.9.0 - golang.org/x/sync v0.1.0 + golang.org/x/sync v0.2.0 golang.org/x/sys v0.7.0 golang.org/x/term v0.7.0 golang.org/x/text v0.9.0 diff --git a/go.sum b/go.sum index c955af503..a90cde171 100644 --- a/go.sum +++ b/go.sum @@ -1015,8 +1015,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/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-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= From 4c1edffdd859345e5ae4d82a75909cd3a93fd420 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 09:08:32 +0200 Subject: [PATCH 0899/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/service/secretsmanager (#456) Bumps [github.com/aws/aws-sdk-go-v2/service/secretsmanager](https://github.com/aws/aws-sdk-go-v2) from 1.19.6 to 1.19.7. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/efs/v1.19.6...service/efs/v1.19.7) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/secretsmanager dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ec5a2fb01..b1fb9bc5b 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2/config v1.18.22 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.6 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7 github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.4 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index a90cde171..029663850 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAc github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.6 h1:xC25kY/HSssnA1lC0GFT8mfhmrpMql/24bkyWYDRgzU= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.6/go.mod h1:3ARttS6G6U3auEdKfaN4GlnfS9UxYE9nqub1+0YGycA= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7 h1:W88E2kZGo+NHOsyvQbsOZYqxXJdLIqRzKadeVlv5J7k= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7/go.mod h1:3ARttS6G6U3auEdKfaN4GlnfS9UxYE9nqub1+0YGycA= github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 h1:GAiaQWuQhQQui76KjuXeShmyXqECwQ0mGRMc/rwsL+c= github.com/aws/aws-sdk-go-v2/service/sso v1.12.9/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 h1:TraLwncRJkWqtIBVKI/UqBymq4+hL+3MzUOtUATuzkA= From 7d39bb7e66cb59d52ee23e1a1dce5059157e4b7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 09:24:22 +0200 Subject: [PATCH 0900/2268] chore(deps): Bump golang.org/x/term from 0.7.0 to 0.8.0 (#457) Bumps [golang.org/x/term](https://github.com/golang/term) from 0.7.0 to 0.8.0. - [Commits](https://github.com/golang/term/compare/v0.7.0...v0.8.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index b1fb9bc5b..169af2d54 100644 --- a/go.mod +++ b/go.mod @@ -38,8 +38,8 @@ require ( golang.org/x/crypto v0.8.0 golang.org/x/net v0.9.0 golang.org/x/sync v0.2.0 - golang.org/x/sys v0.7.0 - golang.org/x/term v0.7.0 + golang.org/x/sys v0.8.0 + golang.org/x/term v0.8.0 golang.org/x/text v0.9.0 helm.sh/helm/v3 v3.11.3 k8s.io/api v0.27.1 diff --git a/go.sum b/go.sum index 029663850..926923bc1 100644 --- a/go.sum +++ b/go.sum @@ -1091,8 +1091,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1101,8 +1101,8 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +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= From 6b9590e4773ca8ca5b4ba5a7aea079082af3c9cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 09:24:31 +0200 Subject: [PATCH 0901/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/config (#459) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.22 to 1.18.23. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.22...config/v1.18.23) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 169af2d54..baacdc9e8 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.18.0 - github.com/aws/aws-sdk-go-v2/config v1.18.22 + github.com/aws/aws-sdk-go-v2/config v1.18.23 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7 github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.4 @@ -93,16 +93,16 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.21 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.22 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.18.11 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index 926923bc1..10c068ca4 100644 --- a/go.sum +++ b/go.sum @@ -121,10 +121,10 @@ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:W github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.22 h1:7vkUEmjjv+giht4wIROqLs+49VWmiQMMHSduxmoNKLU= -github.com/aws/aws-sdk-go-v2/config v1.18.22/go.mod h1:mN7Li1wxaPxSSy4Xkr6stFuinJGf3VZW3ZSNvO0q6sI= -github.com/aws/aws-sdk-go-v2/credentials v1.13.21 h1:VRiXnPEaaPeGeoFcXvMZOB5K/yfIXOYE3q97Kgb0zbU= -github.com/aws/aws-sdk-go-v2/credentials v1.13.21/go.mod h1:90Dk1lJoMyspa/EDUrldTxsPns0wn6+KpRKpdAWc0uA= +github.com/aws/aws-sdk-go-v2/config v1.18.23 h1:gc3lPsAnZpwfi2exupmgHfva0JiAY2BWDg5JWYlmA28= +github.com/aws/aws-sdk-go-v2/config v1.18.23/go.mod h1:rx0ruaQ+gk3OrLFHRRx56lA//XxP8K8uPzeNiKNuWVY= +github.com/aws/aws-sdk-go-v2/credentials v1.13.22 h1:Hp9rwJS4giQ48xqonRV/s7QcDf/wxF6UY7osRmBabvI= +github.com/aws/aws-sdk-go-v2/credentials v1.13.22/go.mod h1:BfNcm6A9nSd+bzejDcMJ5RE+k6WbkCwWkQil7q4heRk= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= @@ -141,12 +141,12 @@ github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3f github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7 h1:W88E2kZGo+NHOsyvQbsOZYqxXJdLIqRzKadeVlv5J7k= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7/go.mod h1:3ARttS6G6U3auEdKfaN4GlnfS9UxYE9nqub1+0YGycA= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 h1:GAiaQWuQhQQui76KjuXeShmyXqECwQ0mGRMc/rwsL+c= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.9/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 h1:TraLwncRJkWqtIBVKI/UqBymq4+hL+3MzUOtUATuzkA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 h1:6UbNM/KJhMBfOI5+lpVcJ/8OA7cBSz0O6OX37SRKlSw= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.10/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.11 h1:uBE+Zj478pfxV98L6SEpvxYiADNjTlMNY714PJLE7uo= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.11/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From ca55014c355afc4347099ba10c897cb5c6d62f7a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 20 Mar 2023 20:00:17 +0100 Subject: [PATCH 0902/2268] refactor: Move Command result into its own package --- cmd/kluctl/commands/cmd_delete.go | 4 +-- cmd/kluctl/commands/cmd_deploy.go | 6 ++-- cmd/kluctl/commands/command_result.go | 24 ++++++++-------- pkg/deployment/commands/deploy.go | 8 +++--- pkg/deployment/commands/diff.go | 6 ++-- pkg/deployment/commands/poke_images.go | 5 ++-- pkg/deployment/commands/validate.go | 24 ++++++++-------- pkg/deployment/utils/apply_utils.go | 14 +++++----- pkg/deployment/utils/delete_utils.go | 10 +++---- pkg/deployment/utils/diff_utils.go | 5 ++-- pkg/deployment/utils/diff_utils_test.go | 28 +++++++++---------- pkg/deployment/utils/errors_holder.go | 26 ++++++++--------- .../utils/remote_objects_utils_test.go | 4 +-- pkg/diff/diff.go | 20 ++++++------- pkg/diff/obfuscate.go | 6 ++-- pkg/types/{ => result}/command_result.go | 19 +++++++------ pkg/validation/validation.go | 14 +++++----- 17 files changed, 113 insertions(+), 110 deletions(-) rename pkg/types/{ => result}/command_result.go (70%) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 91d099332..3dd6f51a1 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -8,8 +8,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" ) type deleteCmd struct { @@ -72,7 +72,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { }) } -func confirmedDeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, dryRun bool, forceYes bool) (*types.CommandResult, error) { +func confirmedDeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, dryRun bool, forceYes bool) (*result.CommandResult, error) { if len(refs) != 0 { _, _ = getStderr(ctx).WriteString("The following objects will be deleted:\n") for _, ref := range refs { diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index d6b6a23a3..fba082d2e 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -6,7 +6,7 @@ import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/result" ) type deployCmd struct { @@ -63,7 +63,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { cmd2.ReadinessTimeout = cmd.ReadinessTimeout cmd2.NoWait = cmd.NoWait - cb := func(diffResult *types.CommandResult) error { + cb := func(diffResult *result.CommandResult) error { return cmd.diffResultCb(cmdCtx.ctx, diffResult) } if cmd.Yes || cmd.DryRun { @@ -84,7 +84,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { return nil } -func (cmd *deployCmd) diffResultCb(ctx context.Context, diffResult *types.CommandResult) error { +func (cmd *deployCmd) diffResultCb(ctx context.Context, diffResult *result.CommandResult) error { flags := cmd.OutputFormatFlags flags.OutputFormat = nil // use default output format diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index 27f648079..fe960eae7 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -7,8 +7,8 @@ import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/diff" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" "io" @@ -16,7 +16,7 @@ import ( "strings" ) -func formatCommandResultText(cr *types.CommandResult, short bool) string { +func formatCommandResultText(cr *result.CommandResult, short bool) string { buf := bytes.NewBuffer(nil) if len(cr.Warnings) != 0 { @@ -84,7 +84,7 @@ func prettyObjectRefs(buf io.StringWriter, refs []k8s.ObjectRef) { } } -func prettyErrors(buf io.StringWriter, errors []types.DeploymentError) { +func prettyErrors(buf io.StringWriter, errors []result.DeploymentError) { for _, e := range errors { prefix := "" if s := e.Ref.String(); s != "" { @@ -94,7 +94,7 @@ func prettyErrors(buf io.StringWriter, errors []types.DeploymentError) { } } -func prettyChanges(buf io.StringWriter, ref k8s.ObjectRef, changes []types.Change) { +func prettyChanges(buf io.StringWriter, ref k8s.ObjectRef, changes []result.Change) { _, _ = buf.WriteString(fmt.Sprintf("Diff for object %s\n", ref.String())) var t utils.PrettyTable @@ -107,7 +107,7 @@ func prettyChanges(buf io.StringWriter, ref k8s.ObjectRef, changes []types.Chang _, _ = buf.WriteString(s) } -func formatCommandResultYaml(cr *types.CommandResult) (string, error) { +func formatCommandResultYaml(cr *result.CommandResult) (string, error) { b, err := yaml.WriteYamlString(cr) if err != nil { return "", err @@ -115,7 +115,7 @@ func formatCommandResultYaml(cr *types.CommandResult) (string, error) { return b, nil } -func formatCommandResult(cr *types.CommandResult, format string, short bool) (string, error) { +func formatCommandResult(cr *result.CommandResult, format string, short bool) (string, error) { switch format { case "text": return formatCommandResultText(cr, short), nil @@ -126,7 +126,7 @@ func formatCommandResult(cr *types.CommandResult, format string, short bool) (st } } -func prettyValidationResults(buf io.StringWriter, results []types.ValidateResultEntry) { +func prettyValidationResults(buf io.StringWriter, results []result.ValidateResultEntry) { var t utils.PrettyTable t.AddRow("Object", "Message") @@ -137,7 +137,7 @@ func prettyValidationResults(buf io.StringWriter, results []types.ValidateResult _, _ = buf.WriteString(s) } -func formatValidateResultText(vr *types.ValidateResult) string { +func formatValidateResultText(vr *result.ValidateResult) string { buf := bytes.NewBuffer(nil) if len(vr.Warnings) != 0 { @@ -163,7 +163,7 @@ func formatValidateResultText(vr *types.ValidateResult) string { return buf.String() } -func formatValidateResultYaml(vr *types.ValidateResult) (string, error) { +func formatValidateResultYaml(vr *result.ValidateResult) (string, error) { b, err := yaml.WriteYamlString(vr) if err != nil { return "", err @@ -171,7 +171,7 @@ func formatValidateResultYaml(vr *types.ValidateResult) (string, error) { return string(b), nil } -func formatValidateResult(vr *types.ValidateResult, format string) (string, error) { +func formatValidateResult(vr *result.ValidateResult, format string) (string, error) { switch format { case "text": return formatValidateResultText(vr), nil @@ -206,7 +206,7 @@ func outputHelper(ctx context.Context, output []string, cb func(format string) ( return nil } -func outputCommandResult(ctx context.Context, flags args.OutputFormatFlags, cr *types.CommandResult) error { +func outputCommandResult(ctx context.Context, flags args.OutputFormatFlags, cr *result.CommandResult) error { status.Flush(ctx) if !flags.NoObfuscate { @@ -224,7 +224,7 @@ func outputCommandResult(ctx context.Context, flags args.OutputFormatFlags, cr * }) } -func outputValidateResult(ctx context.Context, output []string, vr *types.ValidateResult) error { +func outputValidateResult(ctx context.Context, output []string, vr *result.ValidateResult) error { status.Flush(ctx) return outputHelper(ctx, output, func(format string) (string, error) { diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index a349fd103..a1f98531f 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -7,8 +7,8 @@ import ( utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "time" ) @@ -31,7 +31,7 @@ func NewDeployCommand(discriminator string, c *deployment.DeploymentCollection) } } -func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResultCb func(diffResult *types.CommandResult) error) (*types.CommandResult, error) { +func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResultCb func(diffResult *result.CommandResult) error) (*result.CommandResult, error) { dew := utils2.NewDeploymentErrorsAndWarnings() if cmd.discriminator == "" { @@ -64,7 +64,7 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult du.Diff() orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) - diffResult := &types.CommandResult{ + diffResult := &result.CommandResult{ NewObjects: au.GetNewObjects(), ChangedObjects: du.ChangedObjects, DeletedObjects: au.GetDeletedObjects(), @@ -98,7 +98,7 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult if err != nil { return nil, err } - return &types.CommandResult{ + return &result.CommandResult{ NewObjects: au.GetNewObjects(), ChangedObjects: du.ChangedObjects, DeletedObjects: au.GetDeletedObjects(), diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index 6a23ef6fb..335fecc91 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -7,8 +7,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" ) type DiffCommand struct { @@ -30,7 +30,7 @@ func NewDiffCommand(discriminator string, c *deployment.DeploymentCollection) *D } } -func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.CommandResult, error) { +func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.CommandResult, error) { dew := utils.NewDeploymentErrorsAndWarnings() if cmd.discriminator == "" { @@ -65,7 +65,7 @@ func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.Comm if err != nil { return nil, err } - return &types.CommandResult{ + return &result.CommandResult{ NewObjects: au.GetNewObjects(), ChangedObjects: du.ChangedObjects, DeletedObjects: au.GetDeletedObjects(), diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 97baf5772..8af5d519d 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -8,6 +8,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "sync" ) @@ -22,7 +23,7 @@ func NewPokeImagesCommand(c *deployment.DeploymentCollection) *PokeImagesCommand } } -func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.CommandResult, error) { +func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.CommandResult, error) { var wg sync.WaitGroup dew := utils2.NewDeploymentErrorsAndWarnings() @@ -94,7 +95,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*type du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, ad.GetAppliedObjectsMap()) du.Diff() - return &types.CommandResult{ + return &result.CommandResult{ NewObjects: nil, ChangedObjects: du.ChangedObjects, Errors: dew.GetErrorsList(), diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index f279d5042..b2dd91991 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -5,8 +5,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/validation" ) @@ -28,9 +28,9 @@ func NewValidateCommand(ctx context.Context, discriminator string, c *deployment return cmd } -func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types.ValidateResult, error) { - var result types.ValidateResult - result.Ready = true +func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.ValidateResult, error) { + var r result.ValidateResult + r.Ready = true cmd.dew.Init() @@ -53,23 +53,23 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*types. remoteObject := cmd.ru.GetRemoteObject(ref) if remoteObject == nil { - result.Errors = append(result.Errors, types.DeploymentError{Ref: ref, Error: "object not found"}) + r.Errors = append(r.Errors, result.DeploymentError{Ref: ref, Error: "object not found"}) continue } r := validation.ValidateObject(k, remoteObject, true, false) if !r.Ready { - result.Ready = false + r.Ready = false } - result.Errors = append(result.Errors, r.Errors...) - result.Warnings = append(result.Warnings, r.Warnings...) - result.Results = append(result.Results, r.Results...) + r.Errors = append(r.Errors, r.Errors...) + r.Warnings = append(r.Warnings, r.Warnings...) + r.Results = append(r.Results, r.Results...) } } - result.Warnings = append(result.Warnings, cmd.dew.GetWarningsList()...) - result.Errors = append(result.Errors, cmd.dew.GetErrorsList()...) + r.Warnings = append(r.Warnings, cmd.dew.GetWarningsList()...) + r.Errors = append(r.Errors, cmd.dew.GetErrorsList()...) - return &result, nil + return &r, nil } func (cmd *ValidateCommand) ForgetRemoteObject(ref k8s2.ObjectRef) { diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index e77c78c36..d467c10c3 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -8,8 +8,8 @@ import ( "github.com/kluctl/kluctl/v2/pkg/diff" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" @@ -754,14 +754,14 @@ func (a *ApplyUtil) ReplaceObject(ref k8s2.ObjectRef, firstVersion *uo.Unstructu a.HandleError(ref, fmt.Errorf("unexpected end of loop")) } -func (ad *ApplyDeploymentsUtil) collectObjects(f func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject) []*types.RefAndObject { +func (ad *ApplyDeploymentsUtil) collectObjects(f func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject) []*result.RefAndObject { ad.resultsMutex.Lock() defer ad.resultsMutex.Unlock() - var ret []*types.RefAndObject + var ret []*result.RefAndObject for _, a := range ad.results { for _, o := range f(a) { - ret = append(ret, &types.RefAndObject{ + ret = append(ret, &result.RefAndObject{ Ref: o.GetK8sRef(), Object: o, }) @@ -770,13 +770,13 @@ func (ad *ApplyDeploymentsUtil) collectObjects(f func(au *ApplyUtil) map[k8s2.Ob return ret } -func (ad *ApplyDeploymentsUtil) GetNewObjects() []*types.RefAndObject { +func (ad *ApplyDeploymentsUtil) GetNewObjects() []*result.RefAndObject { return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { return au.newObjects }) } -func (ad *ApplyDeploymentsUtil) GetAppliedObjects() []*types.RefAndObject { +func (ad *ApplyDeploymentsUtil) GetAppliedObjects() []*result.RefAndObject { return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { return au.appliedObjects }) @@ -790,7 +790,7 @@ func (ad *ApplyDeploymentsUtil) GetAppliedObjectsMap() map[k8s2.ObjectRef]*uo.Un return ret } -func (ad *ApplyDeploymentsUtil) GetAppliedHookObjects() []*types.RefAndObject { +func (ad *ApplyDeploymentsUtil) GetAppliedHookObjects() []*result.RefAndObject { return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { return au.appliedHookObjects }) diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index 00f72ad39..82a459eac 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -3,8 +3,8 @@ package utils import ( "context" "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -160,10 +160,10 @@ func FindObjectsForDelete(k *k8s.K8sCluster, allClusterObjects []*uo.Unstructure return ret, nil } -func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, doWait bool) (*types.CommandResult, error) { +func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, doWait bool) (*result.CommandResult, error) { g := utils.NewGoHelper(ctx, 8) - var ret types.CommandResult + var ret result.CommandResult namespaceNames := make(map[string]bool) var mutex sync.Mutex @@ -174,13 +174,13 @@ func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef if err == nil { ret.DeletedObjects = append(ret.DeletedObjects, ref) } else { - ret.Errors = append(ret.Errors, types.DeploymentError{ + ret.Errors = append(ret.Errors, result.DeploymentError{ Ref: ref, Error: err.Error(), }) } for _, w := range apiWarnings { - ret.Warnings = append(ret.Warnings, types.DeploymentError{ + ret.Warnings = append(ret.Warnings, result.DeploymentError{ Ref: ref, Error: w.Text, }) diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index e62f33e16..dbfd952c3 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -5,6 +5,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/diff" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "sort" "sync" @@ -22,7 +23,7 @@ type diffUtil struct { IgnoreAnnotations bool remoteDiffObjects map[k8s2.ObjectRef]*uo.UnstructuredObject - ChangedObjects []*types.ChangedObject + ChangedObjects []*result.ChangedObject mutex sync.Mutex } @@ -98,7 +99,7 @@ func (u *diffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, u.mutex.Lock() defer u.mutex.Unlock() - u.ChangedObjects = append(u.ChangedObjects, &types.ChangedObject{ + u.ChangedObjects = append(u.ChangedObjects, &result.ChangedObject{ Ref: diffRef, NewObject: ao, OldObject: ro, diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index 042fd32ef..0f7296e1f 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -3,8 +3,8 @@ package utils import ( "context" "github.com/kluctl/kluctl/v2/pkg/deployment" - "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" "testing" @@ -65,8 +65,8 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) - assert.Equal(t, []types.Change{ - types.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v2", UnifiedDiff: "-v1\n+v2"}, + assert.Equal(t, []result.Change{ + result.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v2", UnifiedDiff: "-v1\n+v2"}, }, dtc.du.ChangedObjects[0].Changes) }, }, @@ -78,8 +78,8 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) - assert.Equal(t, []types.Change{ - types.Change{Type: "insert", JsonPath: "data.d2", OldValue: interface{}(nil), NewValue: "v2", UnifiedDiff: "+v2"}, + assert.Equal(t, []result.Change{ + result.Change{Type: "insert", JsonPath: "data.d2", OldValue: interface{}(nil), NewValue: "v2", UnifiedDiff: "+v2"}, }, dtc.du.ChangedObjects[0].Changes) }, }, @@ -91,8 +91,8 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) - assert.Equal(t, []types.Change{ - types.Change{Type: "delete", JsonPath: "data.d2", OldValue: "v2", NewValue: interface{}(nil), UnifiedDiff: "-v2"}, + assert.Equal(t, []result.Change{ + result.Change{Type: "delete", JsonPath: "data.d2", OldValue: "v2", NewValue: interface{}(nil), UnifiedDiff: "-v2"}, }, dtc.du.ChangedObjects[0].Changes) }, }, @@ -104,9 +104,9 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) - assert.Equal(t, []types.Change{ - types.Change{Type: "delete", JsonPath: "data.d1", OldValue: "v1", NewValue: interface{}(nil), UnifiedDiff: "-v1"}, - types.Change{Type: "insert", JsonPath: "data.d2", OldValue: interface{}(nil), NewValue: "v2", UnifiedDiff: "+v2"}, + assert.Equal(t, []result.Change{ + result.Change{Type: "delete", JsonPath: "data.d1", OldValue: "v1", NewValue: interface{}(nil), UnifiedDiff: "-v1"}, + result.Change{Type: "insert", JsonPath: "data.d2", OldValue: interface{}(nil), NewValue: "v2", UnifiedDiff: "+v2"}, }, dtc.du.ChangedObjects[0].Changes) }, }, @@ -118,10 +118,10 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) - assert.Equal(t, []types.Change{ - types.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v12", UnifiedDiff: "-v1\n+v12"}, - types.Change{Type: "delete", JsonPath: "data.d2", OldValue: "v2", NewValue: interface{}(nil), UnifiedDiff: "-v2"}, - types.Change{Type: "insert", JsonPath: "data.d3", OldValue: interface{}(nil), NewValue: "v3", UnifiedDiff: "+v3"}, + assert.Equal(t, []result.Change{ + result.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v12", UnifiedDiff: "-v1\n+v12"}, + result.Change{Type: "delete", JsonPath: "data.d2", OldValue: "v2", NewValue: interface{}(nil), UnifiedDiff: "-v2"}, + result.Change{Type: "insert", JsonPath: "data.d3", OldValue: interface{}(nil), NewValue: "v3", UnifiedDiff: "+v3"}, }, dtc.du.ChangedObjects[0].Changes) }, }, diff --git a/pkg/deployment/utils/errors_holder.go b/pkg/deployment/utils/errors_holder.go index bf8faef1d..dd3c18a2d 100644 --- a/pkg/deployment/utils/errors_holder.go +++ b/pkg/deployment/utils/errors_holder.go @@ -5,14 +5,14 @@ import ( "fmt" "github.com/hashicorp/go-multierror" k8s2 "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "sync" ) type DeploymentErrorsAndWarnings struct { - errors map[k8s.ObjectRef]map[types.DeploymentError]bool - warnings map[k8s.ObjectRef]map[types.DeploymentError]bool + errors map[k8s.ObjectRef]map[result.DeploymentError]bool + warnings map[k8s.ObjectRef]map[result.DeploymentError]bool mutex sync.Mutex } @@ -25,12 +25,12 @@ func NewDeploymentErrorsAndWarnings() *DeploymentErrorsAndWarnings { func (dew *DeploymentErrorsAndWarnings) Init() { dew.mutex.Lock() defer dew.mutex.Unlock() - dew.warnings = map[k8s.ObjectRef]map[types.DeploymentError]bool{} - dew.errors = map[k8s.ObjectRef]map[types.DeploymentError]bool{} + dew.warnings = map[k8s.ObjectRef]map[result.DeploymentError]bool{} + dew.errors = map[k8s.ObjectRef]map[result.DeploymentError]bool{} } func (dew *DeploymentErrorsAndWarnings) AddWarning(ref k8s.ObjectRef, warning error) { - de := types.DeploymentError{ + de := result.DeploymentError{ Ref: ref, Error: warning.Error(), } @@ -38,14 +38,14 @@ func (dew *DeploymentErrorsAndWarnings) AddWarning(ref k8s.ObjectRef, warning er defer dew.mutex.Unlock() m, ok := dew.warnings[ref] if !ok { - m = make(map[types.DeploymentError]bool) + m = make(map[result.DeploymentError]bool) dew.warnings[ref] = m } m[de] = true } func (dew *DeploymentErrorsAndWarnings) AddError(ref k8s.ObjectRef, err error) { - de := types.DeploymentError{ + de := result.DeploymentError{ Ref: ref, Error: err.Error(), } @@ -53,7 +53,7 @@ func (dew *DeploymentErrorsAndWarnings) AddError(ref k8s.ObjectRef, err error) { defer dew.mutex.Unlock() m, ok := dew.errors[ref] if !ok { - m = make(map[types.DeploymentError]bool) + m = make(map[result.DeploymentError]bool) dew.errors[ref] = m } m[de] = true @@ -72,10 +72,10 @@ func (dew *DeploymentErrorsAndWarnings) HadError(ref k8s.ObjectRef) bool { return ok } -func (dew *DeploymentErrorsAndWarnings) GetErrorsList() []types.DeploymentError { +func (dew *DeploymentErrorsAndWarnings) GetErrorsList() []result.DeploymentError { dew.mutex.Lock() defer dew.mutex.Unlock() - var ret []types.DeploymentError + var ret []result.DeploymentError for _, m := range dew.errors { for e := range m { ret = append(ret, e) @@ -84,10 +84,10 @@ func (dew *DeploymentErrorsAndWarnings) GetErrorsList() []types.DeploymentError return ret } -func (dew *DeploymentErrorsAndWarnings) GetWarningsList() []types.DeploymentError { +func (dew *DeploymentErrorsAndWarnings) GetWarningsList() []result.DeploymentError { dew.mutex.Lock() defer dew.mutex.Unlock() - var ret []types.DeploymentError + var ret []result.DeploymentError for _, m := range dew.warnings { for e := range m { ret = append(ret, e) diff --git a/pkg/deployment/utils/remote_objects_utils_test.go b/pkg/deployment/utils/remote_objects_utils_test.go index 8ded2b28c..d1ba3a0d8 100644 --- a/pkg/deployment/utils/remote_objects_utils_test.go +++ b/pkg/deployment/utils/remote_objects_utils_test.go @@ -3,8 +3,8 @@ package utils import ( "context" "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -42,7 +42,7 @@ func TestRemoteObjectUtils_PermissionErrors(t *testing.T) { k8s2.NewObjectRef("", "v1", "Secret", "secret", "default"), }, false) assert.NoError(t, err) - assert.Equal(t, []types.DeploymentError{{ + assert.Equal(t, []result.DeploymentError{{ Error: "at least one permission error was encountered while gathering objects by discriminator labels. This might result in orphan object detection to not work properly"}, }, dew.GetWarningsList()) assert.Empty(t, dew.GetErrorsList()) diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index f9f1a1c55..f654b0507 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -5,7 +5,7 @@ import ( "github.com/hexops/gotextdiff" "github.com/hexops/gotextdiff/myers" "github.com/hexops/gotextdiff/span" - "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" diff2 "github.com/r3labs/diff/v2" @@ -38,7 +38,7 @@ func convertPath(path []string, o interface{}) (string, error) { return ret.ToJsonPath(), nil } -func Diff(oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) ([]types.Change, error) { +func Diff(oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) ([]result.Change, error) { differ, err := diff2.NewDiffer(diff2.AllowTypeMismatch(true)) if err != nil { return nil, err @@ -48,7 +48,7 @@ func Diff(oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) ([ return nil, err } - var changes []types.Change + var changes []result.Change for _, c := range cl { c2, err := convertChange(c, oldObject, newObject) if err != nil { @@ -66,14 +66,14 @@ func Diff(oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) ([ return changes, nil } -func convertChange(c diff2.Change, oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) (*types.Change, error) { +func convertChange(c diff2.Change, oldObject *uo.UnstructuredObject, newObject *uo.UnstructuredObject) (*result.Change, error) { switch c.Type { case "create": p, err := convertPath(c.Path, newObject.Object) if err != nil { return nil, err } - return &types.Change{ + return &result.Change{ Type: "insert", JsonPath: p, NewValue: c.To, @@ -83,7 +83,7 @@ func convertChange(c diff2.Change, oldObject *uo.UnstructuredObject, newObject * if err != nil { return nil, err } - return &types.Change{ + return &result.Change{ Type: "delete", JsonPath: p, OldValue: c.From, @@ -93,7 +93,7 @@ func convertChange(c diff2.Change, oldObject *uo.UnstructuredObject, newObject * if err != nil { return nil, err } - return &types.Change{ + return &result.Change{ Type: "update", JsonPath: p, NewValue: c.To, @@ -103,7 +103,7 @@ func convertChange(c diff2.Change, oldObject *uo.UnstructuredObject, newObject * return nil, fmt.Errorf("unknown change type %s", c.Type) } -func updateUnifiedDiff(change *types.Change) error { +func updateUnifiedDiff(change *result.Change) error { switch change.Type { case "insert": ud, err := buildUnifiedDiff(notPresent, change.NewValue, false) @@ -133,7 +133,7 @@ func updateUnifiedDiff(change *types.Change) error { return nil } -func stableSortChanges(changes []types.Change) { +func stableSortChanges(changes []result.Change) { changesStrs := make([]string, len(changes)) changesIndexes := make([]int, len(changes)) for i, _ := range changes { @@ -149,7 +149,7 @@ func stableSortChanges(changes []types.Change) { return changesStrs[changesIndexes[i]] < changesStrs[changesIndexes[j]] }) - changesSorted := make([]types.Change, len(changes)) + changesSorted := make([]result.Change, len(changes)) for i, _ := range changes { changesSorted[i] = changes[changesIndexes[i]] } diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index e576c29b9..89036c142 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -2,8 +2,8 @@ package diff import ( "fmt" - "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/ohler55/ojg/jp" "k8s.io/apimachinery/pkg/runtime/schema" "strings" @@ -14,7 +14,7 @@ var secretGvk = schema.GroupKind{Group: "", Kind: "Secret"} type Obfuscator struct { } -func (o *Obfuscator) Obfuscate(ref k8s.ObjectRef, changes []types.Change) error { +func (o *Obfuscator) Obfuscate(ref k8s.ObjectRef, changes []result.Change) error { if ref.GVK.GroupKind() == secretGvk { err := o.obfuscateSecret(ref, changes) if err != nil { @@ -24,7 +24,7 @@ func (o *Obfuscator) Obfuscate(ref k8s.ObjectRef, changes []types.Change) error return nil } -func (o *Obfuscator) obfuscateSecret(ref k8s.ObjectRef, changes []types.Change) error { +func (o *Obfuscator) obfuscateSecret(ref k8s.ObjectRef, changes []result.Change) error { replaceValues := func(x any, v string) any { if x == nil { return nil diff --git a/pkg/types/command_result.go b/pkg/types/result/command_result.go similarity index 70% rename from pkg/types/command_result.go rename to pkg/types/result/command_result.go index d6a235c9a..fce09787e 100644 --- a/pkg/types/command_result.go +++ b/pkg/types/result/command_result.go @@ -1,6 +1,7 @@ -package types +package result import ( + "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" ) @@ -31,14 +32,14 @@ type DeploymentError struct { } type CommandResult struct { - NewObjects []*RefAndObject `json:"newObjects,omitempty"` - ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` - HookObjects []*RefAndObject `json:"hookObjects,omitempty"` - OrphanObjects []k8s.ObjectRef `json:"orphanObjects,omitempty"` - DeletedObjects []k8s.ObjectRef `json:"deletedObjects,omitempty"` - Errors []DeploymentError `json:"errors,omitempty"` - Warnings []DeploymentError `json:"warnings,omitempty"` - SeenImages []FixedImage `json:"seenImages,omitempty"` + NewObjects []*RefAndObject `json:"newObjects,omitempty"` + ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` + HookObjects []*RefAndObject `json:"hookObjects,omitempty"` + OrphanObjects []k8s.ObjectRef `json:"orphanObjects,omitempty"` + DeletedObjects []k8s.ObjectRef `json:"deletedObjects,omitempty"` + Errors []DeploymentError `json:"errors,omitempty"` + Warnings []DeploymentError `json:"warnings,omitempty"` + SeenImages []types.FixedImage `json:"seenImages,omitempty"` } type ValidateResultEntry struct { diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 36a034637..94adf5079 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -3,7 +3,7 @@ package validation import ( "fmt" "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/apimachinery/pkg/api/errors" @@ -45,7 +45,7 @@ const ( reactNotReady ) -func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError bool, forceStatusRequired bool) (ret types.ValidateResult) { +func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError bool, forceStatusRequired bool) (ret result.ValidateResult) { ref := o.GetK8sRef() // We assume all is good in case no validation is performed @@ -61,17 +61,17 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError // all good } else if e, ok := r.(error); ok { err := fmt.Errorf("panic in ValidateObject: %w", e) - ret.Errors = append(ret.Errors, types.DeploymentError{Ref: ref, Error: err.Error()}) + ret.Errors = append(ret.Errors, result.DeploymentError{Ref: ref, Error: err.Error()}) } else { err := fmt.Errorf("panic in ValidateObject: %v", e) - ret.Errors = append(ret.Errors, types.DeploymentError{Ref: ref, Error: err.Error()}) + ret.Errors = append(ret.Errors, result.DeploymentError{Ref: ref, Error: err.Error()}) } ret.Ready = false } }() for k, v := range o.GetK8sAnnotationsWithRegex(resultAnnotation) { - ret.Results = append(ret.Results, types.ValidateResultEntry{ + ret.Results = append(ret.Results, result.ValidateResultEntry{ Ref: ref, Annotation: k, Message: v, @@ -79,14 +79,14 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError } addError := func(message string) { - ret.Errors = append(ret.Errors, types.DeploymentError{ + ret.Errors = append(ret.Errors, result.DeploymentError{ Ref: ref, Error: message, }) ret.Ready = false } addWarning := func(message string) { - ret.Warnings = append(ret.Warnings, types.DeploymentError{ + ret.Warnings = append(ret.Warnings, result.DeploymentError{ Ref: ref, Error: message, }) From 2f9019be3a8b9f9921d5dedb8077d34773146b9c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 20 Mar 2023 20:18:54 +0100 Subject: [PATCH 0903/2268] feat: Obfuscate new and hook objects --- cmd/kluctl/commands/command_result.go | 14 +++++++- pkg/diff/obfuscate.go | 47 ++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index fe960eae7..d8203d5c0 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -212,7 +212,19 @@ func outputCommandResult(ctx context.Context, flags args.OutputFormatFlags, cr * if !flags.NoObfuscate { var obfuscator diff.Obfuscator for _, c := range cr.ChangedObjects { - err := obfuscator.Obfuscate(c.Ref, c.Changes) + err := obfuscator.ObfuscateChanges(c.Ref, c.Changes) + if err != nil { + return err + } + } + for _, n := range cr.NewObjects { + err := obfuscator.ObfuscateObject(n.Object) + if err != nil { + return err + } + } + for _, h := range cr.HookObjects { + err := obfuscator.ObfuscateObject(h.Object) if err != nil { return err } diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index 89036c142..00e9c0deb 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -1,22 +1,24 @@ package diff import ( + "encoding/base64" "fmt" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/ohler55/ojg/jp" "k8s.io/apimachinery/pkg/runtime/schema" "strings" ) -var secretGvk = schema.GroupKind{Group: "", Kind: "Secret"} +var secretGk = schema.GroupKind{Group: "", Kind: "Secret"} type Obfuscator struct { } -func (o *Obfuscator) Obfuscate(ref k8s.ObjectRef, changes []result.Change) error { - if ref.GVK.GroupKind() == secretGvk { - err := o.obfuscateSecret(ref, changes) +func (o *Obfuscator) ObfuscateChanges(ref k8s.ObjectRef, changes []result.Change) error { + if ref.GVK.GroupKind() == secretGk { + err := o.obfuscateSecretChanges(ref, changes) if err != nil { return err } @@ -24,7 +26,18 @@ func (o *Obfuscator) Obfuscate(ref k8s.ObjectRef, changes []result.Change) error return nil } -func (o *Obfuscator) obfuscateSecret(ref k8s.ObjectRef, changes []result.Change) error { +func (o *Obfuscator) ObfuscateObject(x *uo.UnstructuredObject) error { + ref := x.GetK8sRef() + if ref.GVK.GroupKind() == secretGk { + err := o.obfuscateSecret(x) + if err != nil { + return err + } + } + return nil +} + +func (o *Obfuscator) obfuscateSecretChanges(ref k8s.ObjectRef, changes []result.Change) error { replaceValues := func(x any, v string) any { if x == nil { return nil @@ -69,3 +82,27 @@ func (o *Obfuscator) obfuscateSecret(ref k8s.ObjectRef, changes []result.Change) } return nil } + +func (o *Obfuscator) obfuscateSecret(x *uo.UnstructuredObject) error { + data, ok, _ := x.GetNestedField("data") + if ok { + if m, ok := data.(map[string]any); ok { + for k, _ := range m { + m[k] = base64.StdEncoding.EncodeToString([]byte("*****")) + } + } else { + return fmt.Errorf("'data' is not a map of strings") + } + } + data, ok, _ = x.GetNestedField("stringData") + if ok { + if m, ok := data.(map[string]any); ok { + for k, _ := range m { + m[k] = "*****" + } + } else { + return fmt.Errorf("'data' is not a map of strings") + } + } + return nil +} From e62348f04163529ce6e8eae796ebbb3c6d48babc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 20 Mar 2023 20:24:43 +0100 Subject: [PATCH 0904/2268] refactor: Add ObfuscateResult to Obfuscator --- cmd/kluctl/commands/command_result.go | 20 +++----------------- pkg/diff/obfuscate.go | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index d8203d5c0..bbdfffc7e 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -211,23 +211,9 @@ func outputCommandResult(ctx context.Context, flags args.OutputFormatFlags, cr * if !flags.NoObfuscate { var obfuscator diff.Obfuscator - for _, c := range cr.ChangedObjects { - err := obfuscator.ObfuscateChanges(c.Ref, c.Changes) - if err != nil { - return err - } - } - for _, n := range cr.NewObjects { - err := obfuscator.ObfuscateObject(n.Object) - if err != nil { - return err - } - } - for _, h := range cr.HookObjects { - err := obfuscator.ObfuscateObject(h.Object) - if err != nil { - return err - } + err := obfuscator.ObfuscateResult(cr) + if err != nil { + return err } } diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index 00e9c0deb..1bcb7d96a 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -16,6 +16,28 @@ var secretGk = schema.GroupKind{Group: "", Kind: "Secret"} type Obfuscator struct { } +func (o *Obfuscator) ObfuscateResult(r *result.CommandResult) error { + for _, c := range r.ChangedObjects { + err := o.ObfuscateChanges(c.Ref, c.Changes) + if err != nil { + return err + } + } + for _, n := range r.NewObjects { + err := o.ObfuscateObject(n.Object) + if err != nil { + return err + } + } + for _, h := range r.HookObjects { + err := o.ObfuscateObject(h.Object) + if err != nil { + return err + } + } + return nil +} + func (o *Obfuscator) ObfuscateChanges(ref k8s.ObjectRef, changes []result.Change) error { if ref.GVK.GroupKind() == secretGk { err := o.obfuscateSecretChanges(ref, changes) From 1ad8778d14247e847144c818566dc2195edcc95f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 21 Mar 2023 17:47:23 +0100 Subject: [PATCH 0905/2268] feat: Add rendered objects/inlucdes/vars to types --- pkg/deployment/deployment_item.go | 3 +++ pkg/deployment/deployment_project.go | 13 +++++++++++++ pkg/types/deployment.go | 6 ++++++ pkg/types/vars_source.go | 3 +++ pkg/vars/vars_loader.go | 6 ++++++ 5 files changed, 31 insertions(+) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 7c87d24b6..23f1192fa 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -225,6 +225,8 @@ func (di *DeploymentItem) renderHelmCharts() error { } } + di.Config.RenderedHelmChartConfig = hr.Config + return hr.Render(di.ctx.Ctx, di.ctx.K, di.ctx.K8sVersion, di.ctx.SopsDecrypter) }) if err != nil { @@ -533,6 +535,7 @@ func (di *DeploymentItem) buildKustomize() error { } o := uo.FromMap(y) di.Objects = append(di.Objects, o) + di.Config.RenderedObjects = append(di.Config.RenderedObjects, o) } return nil diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 88c496924..929073405 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -108,6 +108,18 @@ func (p *DeploymentProject) generateSingleKustomizeProject() error { } func (p *DeploymentProject) processConfig() error { + for _, item := range p.Config.Deployments { + if len(item.RenderedObjects) != 0 { + return fmt.Errorf("renderedObjects is not allowed here") + } + if item.RenderedHelmChartConfig != nil { + return fmt.Errorf("renderedHelmChartConfig is not allowed here") + } + if item.RenderedInclude != nil { + return fmt.Errorf("renderedInclude is not allowed here") + } + } + err := p.loadVarsList(p.VarsCtx, p.Config.Vars) if err != nil { return fmt.Errorf("failed to load deployment.yml vars: %w", err) @@ -228,6 +240,7 @@ func (p *DeploymentProject) loadIncludes() error { } else { continue } + inc.RenderedInclude = &newProject.Config newProject.parentProjectInclude = inc p.includes[i] = newProject } diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index f5695951f..6b39b7d0f 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -3,6 +3,7 @@ package types import ( "encoding/json" "github.com/go-playground/validator/v10" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" ) @@ -19,6 +20,11 @@ type DeploymentItemConfig struct { AlwaysDeploy bool `json:"alwaysDeploy,omitempty"` DeleteObjects []DeleteObjectItemConfig `json:"deleteObjects,omitempty"` When string `json:"when,omitempty"` + + // these are only allowed when writing the command result + RenderedHelmChartConfig *HelmChartConfig `json:"renderedHelmChartConfig,omitempty"` + RenderedObjects []*uo.UnstructuredObject `json:"renderedObjects,omitempty"` + RenderedInclude *DeploymentProjectConfig `json:"renderedInclude,omitempty"` } func ValidateDeploymentItemConfig(sl validator.StructLevel) { diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index c195cdace..f2c520f4e 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -69,6 +69,9 @@ type VarsSource struct { Vault *VarsSourceVault `json:"vault,omitempty"` When string `json:"when,omitempty"` + + // these are only allowed when writing the command result + RenderedVars *uo.UnstructuredObject `json:"renderedVars,omitempty"` } func ValidateVarsSource(sl validator.StructLevel) { diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 49170f161..9c90b4000 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -63,6 +63,10 @@ func (v *VarsLoader) LoadVarsList(varsCtx *VarsCtx, varsList []*types.VarsSource } func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, searchDirs []string, rootKey string) error { + if sourceIn.RenderedVars != nil && len(sourceIn.RenderedVars.Object) != 0 { + return fmt.Errorf("renderedVars is not allowed here") + } + var source types.VarsSource err := utils.DeepCopy(&source, sourceIn) if err != nil { @@ -125,6 +129,8 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear return err } + sourceIn.RenderedVars = newVars.Clone() + if source.NoOverride == nil || !*source.NoOverride { varsCtx.Vars.Merge(newVars) } else { From 480fbd09737026a8b411d4e875db36fa52d449a3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 21 Mar 2023 17:49:11 +0100 Subject: [PATCH 0906/2268] feat: Add info about the command to CommandResult --- cmd/kluctl/commands/cmd_delete.go | 4 +++ cmd/kluctl/commands/cmd_deploy.go | 4 +++ cmd/kluctl/commands/cmd_diff.go | 4 +++ cmd/kluctl/commands/cmd_poke_images.go | 4 +++ cmd/kluctl/commands/cmd_prune.go | 4 +++ cmd/kluctl/commands/utils.go | 45 ++++++++++++++++++++++++++ pkg/kluctl_project/load.go | 2 +- pkg/kluctl_project/project.go | 2 +- pkg/kluctl_project/project_load.go | 6 ++-- pkg/kluctl_project/target_context.go | 6 ++-- pkg/types/result/command_result.go | 38 ++++++++++++++++++++++ 11 files changed, 111 insertions(+), 8 deletions(-) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 3dd6f51a1..bb238bff3 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -61,6 +61,10 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { if err != nil { return err } + err = addCommandInfo(result, "delete", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) + if err != nil { + return err + } err = outputCommandResult(ctx, cmd.OutputFormatFlags, result) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index fba082d2e..469d93ae1 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -74,6 +74,10 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { if err != nil { return err } + err = addCommandInfo(result, "deploy", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, &cmd.ForceApplyFlags, &cmd.ReplaceOnErrorFlags, &cmd.AbortOnErrorFlags, cmd.NoWait) + if err != nil { + return err + } err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormatFlags, result) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 21edf81f1..46838fcd3 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -50,6 +50,10 @@ func (cmd *diffCmd) Run(ctx context.Context) error { if err != nil { return err } + err = addCommandInfo(result, "diff", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, nil, &cmd.ForceApplyFlags, &cmd.ReplaceOnErrorFlags, nil, false) + if err != nil { + return err + } err = outputCommandResult(ctx, cmd.OutputFormatFlags, result) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 76b69a538..ac72efe70 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -51,6 +51,10 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { if err != nil { return err } + err = addCommandInfo(result, "poke-images", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) + if err != nil { + return err + } err = outputCommandResult(ctx, cmd.OutputFormatFlags, result) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index c975dc8bd..ba60b59a8 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -54,6 +54,10 @@ func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { if err != nil { return err } + err = addCommandInfo(result, "prune", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) + if err != nil { + return err + } err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormatFlags, result) if err != nil { return err diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 32f447dd3..e41e9acd1 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/types/result" "os" "strings" @@ -204,6 +205,50 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm return cb(cmdCtx) } +func addCommandInfo(r *result.CommandResult, command string, ctx *commandCtx, targetFlags *args.TargetFlags, + imageFlags *args.ImageFlags, inclusionFlags *args.InclusionFlags, + dryRunFlags *args.DryRunFlags, forceApplyFlags *args.ForceApplyFlags, replaceOnErrorFlags *args.ReplaceOnErrorFlags, abortOnErrorFlags *args.AbortOnErrorFlags, noWait bool) error { + r.Command = &result.CommandInfo{ + Initiator: result.CommandInititiator_CommandLine, + Command: command, + Target: ctx.targetCtx.Target, + Args: ctx.targetCtx.KluctlProject.LoadArgs.ExternalArgs, + NoWait: noWait, + } + if targetFlags != nil { + r.Command.TargetNameOverride = targetFlags.TargetNameOverride + r.Command.ContextOverride = targetFlags.Context + } + if imageFlags != nil { + var err error + r.Command.Images, err = imageFlags.LoadFixedImagesFromArgs() + if err != nil { + return err + } + } + if inclusionFlags != nil { + r.Command.IncludeTags = inclusionFlags.IncludeTag + r.Command.ExcludeTags = inclusionFlags.ExcludeTag + r.Command.IncludeDeploymentDirs = inclusionFlags.IncludeDeploymentDir + r.Command.ExcludeDeploymentDirs = inclusionFlags.ExcludeDeploymentDir + } + if dryRunFlags != nil { + r.Command.DryRun = dryRunFlags.DryRun + } + if forceApplyFlags != nil { + r.Command.ForceApply = forceApplyFlags.ForceApply + } + if replaceOnErrorFlags != nil { + r.Command.ReplaceOnError = replaceOnErrorFlags.ReplaceOnError + r.Command.ForceReplaceOnError = replaceOnErrorFlags.ForceReplaceOnError + } + if abortOnErrorFlags != nil { + r.Command.AbortOnError = abortOnErrorFlags.AbortOnError + } + r.Deployment = &ctx.targetCtx.DeploymentProject.Config + return nil +} + func clientConfigGetter(forCompletion bool) func(context *string) (*rest.Config, *api.Config, error) { return func(context *string) (*rest.Config, *api.Config, error) { if forCompletion { diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index f320482ba..0535cbc61 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -13,7 +13,7 @@ func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir s p := &LoadedKluctlProject{ ctx: ctx, - loadArgs: args, + LoadArgs: args, TmpDir: tmpDir, J2: j2, RP: args.RP, diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index d959f5dc3..4fe0161ad 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -12,7 +12,7 @@ import ( type LoadedKluctlProject struct { ctx context.Context - loadArgs LoadKluctlProjectArgs + LoadArgs LoadKluctlProjectArgs TmpDir string diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 0d9648db1..2982be334 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -25,7 +25,7 @@ type LoadKluctlProjectArgs struct { } func (c *LoadedKluctlProject) getConfigPath() string { - configPath := c.loadArgs.ProjectConfig + configPath := c.LoadArgs.ProjectConfig if configPath == "" { p := yaml.FixPathExt(filepath.Join(c.ProjectDir, ".kluctl.yml")) if utils.IsFile(p) { @@ -38,8 +38,8 @@ func (c *LoadedKluctlProject) getConfigPath() string { func (c *LoadedKluctlProject) loadKluctlProject() error { var err error - c.projectRootDir = c.loadArgs.RepoRoot - c.ProjectDir = c.loadArgs.ProjectDir + c.projectRootDir = c.LoadArgs.RepoRoot + c.ProjectDir = c.LoadArgs.ProjectDir err = utils.CheckInDir(c.projectRootDir, c.ProjectDir) if err != nil { return err diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 15df07449..c431fc0e0 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -159,7 +159,7 @@ func (p *LoadedKluctlProject) loadK8sConfig(target *types.Target, offlineK8s boo var err error var clientConfig *rest.Config var restConfig *api.Config - clientConfig, restConfig, err = p.loadArgs.ClientConfigGetter(contextName) + clientConfig, restConfig, err = p.LoadArgs.ClientConfigGetter(contextName) if err != nil { return nil, "", err } @@ -193,8 +193,8 @@ func (p *LoadedKluctlProject) buildVars(target *types.Target, forSeal bool) (*va } } } - if p.loadArgs.ExternalArgs != nil { - allArgs.Merge(p.loadArgs.ExternalArgs) + if p.LoadArgs.ExternalArgs != nil { + allArgs.Merge(p.LoadArgs.ExternalArgs) } err = deployment.LoadDefaultArgs(p.Config.Args, allArgs) diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index fce09787e..414edeec6 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -31,7 +31,45 @@ type DeploymentError struct { Error string `json:"error"` } +type KluctlDeploymentInfo struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + GitUrl string `json:"gitUrl"` + GitRef string `json:"gitRef"` +} + +type CommandInitiator string + +const ( + CommandInititiator_CommandLine CommandInitiator = "CommandLine" + CommandInititiator_KluctlDeployment = "KluctlDeployment" +) + +type CommandInfo struct { + Initiator CommandInitiator `json:"initiator" validate:"oneof=CommandLine KluctlDeployment"` + KluctlDeployment *KluctlDeploymentInfo `json:"kluctlDeployment,omitempty"` + Command string `json:"command,omitempty"` + Target *types.Target `json:"target,omitempty"` + TargetNameOverride string `json:"targetNameOverride,omitempty"` + ContextOverride string `json:"contextOverride,omitempty"` + Args *uo.UnstructuredObject `json:"args,omitempty"` + Images []types.FixedImage `json:"images,omitempty"` + DryRun bool `json:"dryRun,omitempty"` + NoWait bool `json:"noWait,omitempty"` + ForceApply bool `json:"forceApply,omitempty"` + ReplaceOnError bool `json:"replaceOnError,omitempty"` + ForceReplaceOnError bool `json:"forceReplaceOnError,omitempty"` + AbortOnError bool `json:"abortOnError,omitempty"` + IncludeTags []string `json:"includeTags,omitempty"` + ExcludeTags []string `json:"excludeTags,omitempty"` + IncludeDeploymentDirs []string `json:"includeDeploymentDirs,omitempty"` + ExcludeDeploymentDirs []string `json:"excludeDeploymentDirs,omitempty"` +} + type CommandResult struct { + Command *CommandInfo `json:"command,omitempty"` + Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` + NewObjects []*RefAndObject `json:"newObjects,omitempty"` ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` HookObjects []*RefAndObject `json:"hookObjects,omitempty"` From ea216f912f6682d79fbf84207fef9521d50d915b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 21 Mar 2023 20:29:12 +0100 Subject: [PATCH 0907/2268] feat: Remove old/new objects from ChangedObject --- pkg/deployment/utils/diff_utils.go | 6 ++---- pkg/deployment/utils/diff_utils_test.go | 5 ----- pkg/types/result/command_result.go | 6 ++---- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index dbfd952c3..edffa3ef5 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -100,10 +100,8 @@ func (u *diffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, u.mutex.Lock() defer u.mutex.Unlock() u.ChangedObjects = append(u.ChangedObjects, &result.ChangedObject{ - Ref: diffRef, - NewObject: ao, - OldObject: ro, - Changes: changes, + Ref: diffRef, + Changes: changes, }) } } diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index 0f7296e1f..cb319d09a 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -64,7 +64,6 @@ func TestDiff(t *testing.T) { ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v2"})}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) - assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []result.Change{ result.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v2", UnifiedDiff: "-v1\n+v2"}, }, dtc.du.ChangedObjects[0].Changes) @@ -77,7 +76,6 @@ func TestDiff(t *testing.T) { ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"})}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) - assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []result.Change{ result.Change{Type: "insert", JsonPath: "data.d2", OldValue: interface{}(nil), NewValue: "v2", UnifiedDiff: "+v2"}, }, dtc.du.ChangedObjects[0].Changes) @@ -90,7 +88,6 @@ func TestDiff(t *testing.T) { ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"})}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) - assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []result.Change{ result.Change{Type: "delete", JsonPath: "data.d2", OldValue: "v2", NewValue: interface{}(nil), UnifiedDiff: "-v2"}, }, dtc.du.ChangedObjects[0].Changes) @@ -103,7 +100,6 @@ func TestDiff(t *testing.T) { ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d2": "v2"})}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) - assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []result.Change{ result.Change{Type: "delete", JsonPath: "data.d1", OldValue: "v1", NewValue: interface{}(nil), UnifiedDiff: "-v1"}, result.Change{Type: "insert", JsonPath: "data.d2", OldValue: interface{}(nil), NewValue: "v2", UnifiedDiff: "+v2"}, @@ -117,7 +113,6 @@ func TestDiff(t *testing.T) { ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v12", "d3": "v3"})}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) - assert.Equal(t, dtc.du.ChangedObjects[0].NewObject, dtc.ao[0]) assert.Equal(t, []result.Change{ result.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v12", UnifiedDiff: "-v1\n+v12"}, result.Change{Type: "delete", JsonPath: "data.d2", OldValue: "v2", NewValue: interface{}(nil), UnifiedDiff: "-v2"}, diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 414edeec6..c04523a59 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -15,10 +15,8 @@ type Change struct { } type ChangedObject struct { - Ref k8s.ObjectRef `json:"ref"` - NewObject *uo.UnstructuredObject `json:"newObject,omitempty"` - OldObject *uo.UnstructuredObject `json:"oldObject,omitempty"` - Changes []Change `json:"changes,omitempty"` + Ref k8s.ObjectRef `json:"ref"` + Changes []Change `json:"changes,omitempty"` } type RefAndObject struct { From 962c0ed66681fb53f76bf674bdcd1074ce3c5c51 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 21 Mar 2023 20:36:36 +0100 Subject: [PATCH 0908/2268] feat: Remove RefAndObject from CommandResult --- cmd/kluctl/commands/command_result.go | 4 ++-- pkg/deployment/utils/apply_utils.go | 20 ++++++++------------ pkg/diff/obfuscate.go | 4 ++-- pkg/types/result/command_result.go | 21 ++++++++------------- 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index bbdfffc7e..384f2739d 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -28,7 +28,7 @@ func formatCommandResultText(cr *result.CommandResult, short bool) string { buf.WriteString("\nNew objects:\n") var refs []k8s.ObjectRef for _, o := range cr.NewObjects { - refs = append(refs, o.Ref) + refs = append(refs, o.GetK8sRef()) } prettyObjectRefs(buf, refs) } @@ -61,7 +61,7 @@ func formatCommandResultText(cr *result.CommandResult, short bool) string { buf.WriteString("\nApplied hooks:\n") var refs []k8s.ObjectRef for _, o := range cr.HookObjects { - refs = append(refs, o.Ref) + refs = append(refs, o.GetK8sRef()) } prettyObjectRefs(buf, refs) } diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index d467c10c3..6e1b67b75 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -9,7 +9,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" @@ -754,29 +753,26 @@ func (a *ApplyUtil) ReplaceObject(ref k8s2.ObjectRef, firstVersion *uo.Unstructu a.HandleError(ref, fmt.Errorf("unexpected end of loop")) } -func (ad *ApplyDeploymentsUtil) collectObjects(f func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject) []*result.RefAndObject { +func (ad *ApplyDeploymentsUtil) collectObjects(f func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject) []*uo.UnstructuredObject { ad.resultsMutex.Lock() defer ad.resultsMutex.Unlock() - var ret []*result.RefAndObject + var ret []*uo.UnstructuredObject for _, a := range ad.results { for _, o := range f(a) { - ret = append(ret, &result.RefAndObject{ - Ref: o.GetK8sRef(), - Object: o, - }) + ret = append(ret, o) } } return ret } -func (ad *ApplyDeploymentsUtil) GetNewObjects() []*result.RefAndObject { +func (ad *ApplyDeploymentsUtil) GetNewObjects() []*uo.UnstructuredObject { return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { return au.newObjects }) } -func (ad *ApplyDeploymentsUtil) GetAppliedObjects() []*result.RefAndObject { +func (ad *ApplyDeploymentsUtil) GetAppliedObjects() []*uo.UnstructuredObject { return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { return au.appliedObjects }) @@ -784,13 +780,13 @@ func (ad *ApplyDeploymentsUtil) GetAppliedObjects() []*result.RefAndObject { func (ad *ApplyDeploymentsUtil) GetAppliedObjectsMap() map[k8s2.ObjectRef]*uo.UnstructuredObject { ret := make(map[k8s2.ObjectRef]*uo.UnstructuredObject) - for _, ro := range ad.GetAppliedObjects() { - ret[ro.Ref] = ro.Object + for _, o := range ad.GetAppliedObjects() { + ret[o.GetK8sRef()] = o } return ret } -func (ad *ApplyDeploymentsUtil) GetAppliedHookObjects() []*result.RefAndObject { +func (ad *ApplyDeploymentsUtil) GetAppliedHookObjects() []*uo.UnstructuredObject { return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { return au.appliedHookObjects }) diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index 1bcb7d96a..10f0080d0 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -24,13 +24,13 @@ func (o *Obfuscator) ObfuscateResult(r *result.CommandResult) error { } } for _, n := range r.NewObjects { - err := o.ObfuscateObject(n.Object) + err := o.ObfuscateObject(n) if err != nil { return err } } for _, h := range r.HookObjects { - err := o.ObfuscateObject(h.Object) + err := o.ObfuscateObject(h) if err != nil { return err } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index c04523a59..0c1251b5d 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -19,11 +19,6 @@ type ChangedObject struct { Changes []Change `json:"changes,omitempty"` } -type RefAndObject struct { - Ref k8s.ObjectRef `json:"ref"` - Object *uo.UnstructuredObject `json:"object,omitempty"` -} - type DeploymentError struct { Ref k8s.ObjectRef `json:"ref"` Error string `json:"error"` @@ -68,14 +63,14 @@ type CommandResult struct { Command *CommandInfo `json:"command,omitempty"` Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` - NewObjects []*RefAndObject `json:"newObjects,omitempty"` - ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` - HookObjects []*RefAndObject `json:"hookObjects,omitempty"` - OrphanObjects []k8s.ObjectRef `json:"orphanObjects,omitempty"` - DeletedObjects []k8s.ObjectRef `json:"deletedObjects,omitempty"` - Errors []DeploymentError `json:"errors,omitempty"` - Warnings []DeploymentError `json:"warnings,omitempty"` - SeenImages []types.FixedImage `json:"seenImages,omitempty"` + NewObjects []*uo.UnstructuredObject `json:"newObjects,omitempty"` + ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` + HookObjects []*uo.UnstructuredObject `json:"hookObjects,omitempty"` + OrphanObjects []k8s.ObjectRef `json:"orphanObjects,omitempty"` + DeletedObjects []k8s.ObjectRef `json:"deletedObjects,omitempty"` + Errors []DeploymentError `json:"errors,omitempty"` + Warnings []DeploymentError `json:"warnings,omitempty"` + SeenImages []types.FixedImage `json:"seenImages,omitempty"` } type ValidateResultEntry struct { From b3bc713e7d6d20c2fc692420e735ceb12114dc5c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 24 Mar 2023 20:57:17 +0100 Subject: [PATCH 0909/2268] fix: Ignore RenderedVars in ValidateVarsSource --- pkg/types/vars_source.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index f2c520f4e..6e09d549c 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -81,7 +81,7 @@ func ValidateVarsSource(sl validator.StructLevel) { v := reflect.ValueOf(s) for i := 0; i < v.NumField(); i++ { switch v.Type().Field(i).Name { - case "IgnoreMissing", "NoOverride", "When": + case "IgnoreMissing", "NoOverride", "When", "RenderedVars": continue } if !v.Field(i).IsNil() { From 4028a41ae7242309e0a76ddb4f1d2d6e2803d7b8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 24 Mar 2023 22:59:27 +0100 Subject: [PATCH 0910/2268] fix: Flatten ObjectRef --- cmd/kluctl/commands/cmd_flux_reconcile.go | 4 +- cmd/kluctl/commands/cmd_flux_resume.go | 2 +- cmd/kluctl/commands/cmd_flux_suspend.go | 2 +- pkg/deployment/deployment_collection.go | 15 +++++-- pkg/deployment/deployment_item.go | 2 +- pkg/deployment/images.go | 2 +- pkg/deployment/utils/apply_utils.go | 8 ++-- pkg/deployment/utils/delete_utils.go | 8 ++-- pkg/deployment/utils/remote_objects_utils.go | 14 +++++-- pkg/diff/obfuscate.go | 4 +- pkg/k8s/k8s_cluster.go | 8 ++-- pkg/k8s/resources.go | 4 +- pkg/seal/bootstrap.go | 10 +++-- pkg/types/k8s/ref.go | 44 +++++++++++++++----- pkg/utils/uo/k8s_fields.go | 5 ++- pkg/validation/validation.go | 2 +- 16 files changed, 91 insertions(+), 43 deletions(-) diff --git a/cmd/kluctl/commands/cmd_flux_reconcile.go b/cmd/kluctl/commands/cmd_flux_reconcile.go index 91e345112..d4d49ed7c 100644 --- a/cmd/kluctl/commands/cmd_flux_reconcile.go +++ b/cmd/kluctl/commands/cmd_flux_reconcile.go @@ -36,7 +36,7 @@ func (cmd *fluxReconcileCmd) Run(ctx context.Context) error { return err } - ref := k8s2.ObjectRef{GVK: args.KluctlDeploymentGVK, Name: kd, Namespace: ns} + ref := k8s2.NewObjectRef(args.KluctlDeploymentGVK.Group, args.KluctlDeploymentGVK.Version, args.KluctlDeploymentGVK.Kind, kd, ns) patch := []k8s.JsonPatch{{ Op: "replace", Path: "/metadata/annotations", @@ -59,7 +59,7 @@ func (cmd *fluxReconcileCmd) Run(ctx context.Context) error { if err != nil { return err } - ref2 := k8s2.ObjectRef{GVK: args.GitRepositoryGVK, Name: sourceName, Namespace: sourceNamespace} + ref2 := k8s2.NewObjectRef(args.GitRepositoryGVK.Group, args.GitRepositoryGVK.Version, args.GitRepositoryGVK.Kind, sourceName, sourceNamespace) s := status.Start(ctx, "Annotating Source %s in %s namespace", sourceName, sourceNamespace) defer s.Failed() diff --git a/cmd/kluctl/commands/cmd_flux_resume.go b/cmd/kluctl/commands/cmd_flux_resume.go index d2fbc7d69..8d116bc1e 100644 --- a/cmd/kluctl/commands/cmd_flux_resume.go +++ b/cmd/kluctl/commands/cmd_flux_resume.go @@ -27,7 +27,7 @@ func (cmd *fluxResumeCmd) Run(ctx context.Context) error { return err } - ref := k8s2.ObjectRef{GVK: args.KluctlDeploymentGVK, Name: kd, Namespace: ns} + ref := k8s2.NewObjectRef(args.KluctlDeploymentGVK.Group, args.KluctlDeploymentGVK.Version, args.KluctlDeploymentGVK.Kind, kd, ns) patch := []k8s.JsonPatch{{ Op: "replace", diff --git a/cmd/kluctl/commands/cmd_flux_suspend.go b/cmd/kluctl/commands/cmd_flux_suspend.go index dc90d9c2b..d7981c1a6 100644 --- a/cmd/kluctl/commands/cmd_flux_suspend.go +++ b/cmd/kluctl/commands/cmd_flux_suspend.go @@ -26,7 +26,7 @@ func (cmd *fluxSuspendCmd) Run(ctx context.Context) error { return err } - ref := k8s2.ObjectRef{GVK: args.KluctlDeploymentGVK, Name: kd, Namespace: ns} + ref := k8s2.NewObjectRef(args.KluctlDeploymentGVK.Group, args.KluctlDeploymentGVK.Version, args.KluctlDeploymentGVK.Kind, kd, ns) patch := []k8s.JsonPatch{{ Op: "replace", Path: "/spec/suspend", diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index c89c289f1..dd7bfb426 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "path/filepath" "sync" ) @@ -227,11 +228,19 @@ func (c *DeploymentCollection) buildKustomizeObjects() error { return g.ErrorOrNil() } -func (c *DeploymentCollection) LocalObjectsByRef() map[k8s2.ObjectRef]bool { - ret := make(map[k8s2.ObjectRef]bool) +func (c *DeploymentCollection) LocalObjects() []*uo.UnstructuredObject { + var ret []*uo.UnstructuredObject + for _, d := range c.Deployments { + ret = append(ret, d.Objects...) + } + return ret +} + +func (c *DeploymentCollection) LocalObjectsByRef() map[k8s2.ObjectRef]*uo.UnstructuredObject { + ret := make(map[k8s2.ObjectRef]*uo.UnstructuredObject) for _, d := range c.Deployments { for _, o := range d.Objects { - ret[o.GetK8sRef()] = true + ret[o.GetK8sRef()] = o } } return ret diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 23f1192fa..f3cb14777 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -535,7 +535,7 @@ func (di *DeploymentItem) buildKustomize() error { } o := uo.FromMap(y) di.Objects = append(di.Objects, o) - di.Config.RenderedObjects = append(di.Config.RenderedObjects, o) + di.Config.RenderedObjects = append(di.Config.RenderedObjects, o.GetK8sRef()) } return nil diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index 715f0c327..f9e6df475 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -205,7 +205,7 @@ func (images *Images) ResolvePlaceholders(ctx context.Context, k *k8s.K8sCluster } ref := o.GetK8sRef() - deployment := fmt.Sprintf("%s/%s", ref.GVK.Kind, ref.Name) + deployment := fmt.Sprintf("%s/%s", ref.Kind, ref.Name) var remoteObject *uo.UnstructuredObject triedRemoteObject := false diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 6e1b67b75..4f9d84fef 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -382,10 +382,10 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo return } } - if r != nil && ref.GVK.GroupKind().String() == "Namespace" { + if r != nil && ref.GroupKind().String() == "Namespace" { a.allNamespaces.Store(ref.Name, r) } - if r != nil && ref.GVK.GroupKind().String() == "CustomResourceDefinition.apiextensions.k8s.io" { + if r != nil && ref.GroupKind().String() == "CustomResourceDefinition.apiextensions.k8s.io" { a.handleObservedCRD(r) } a.handleApiWarnings(ref, apiWarnings) @@ -506,7 +506,9 @@ func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { for _, x := range d.Config.DeleteObjects { for _, gvk := range a.k.Resources.GetFilteredGVKs(k8s.BuildGVKFilter(x.Group, nil, x.Kind)) { ref := k8s2.ObjectRef{ - GVK: gvk, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, Name: x.Name, Namespace: x.Namespace, } diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index 82a459eac..90a24c04e 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -42,7 +42,7 @@ var deleteOrder = [][]string{ func objectRefForExclusion(k *k8s.K8sCluster, ref k8s2.ObjectRef) k8s2.ObjectRef { ref = k.Resources.FixNamespaceInRef(ref) - ref.GVK.Version = "" + ref.Version = "" return ref } @@ -84,7 +84,7 @@ func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, for _, o := range objects { ref := o.GetK8sRef() - if _, ok := filteredResources[ref.GVK.GroupKind()]; !ok { + if _, ok := filteredResources[ref.GroupKind()]; !ok { continue } @@ -189,7 +189,7 @@ func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef for _, ref_ := range refs { ref := ref_ - if ref.GVK.GroupVersion().String() == "v1" && ref.GVK.Kind == "Namespace" { + if ref.GroupVersion().String() == "v1" && ref.Kind == "Namespace" { namespaceNames[ref.Name] = true g.Run(func() { apiWarnings, err := k.DeleteSingleObject(ref, k8s.DeleteOptions{NoWait: !doWait, IgnoreNotFoundError: true}) @@ -201,7 +201,7 @@ func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef for _, ref_ := range refs { ref := ref_ - if ref.GVK.GroupVersion().String() == "v1" && ref.GVK.Kind == "Namespace" { + if ref.GroupVersion().String() == "v1" && ref.Kind == "Namespace" { continue } if _, ok := namespaceNames[ref.Namespace]; ok { diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index 37e078595..0676f86e8 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -69,7 +69,11 @@ func (u *RemoteObjectUtils) getAllByDiscriminator(k *k8s.K8sCluster, discriminat gvk := gvk g.Run(func() { l, apiWarnings, err := k.ListObjects(gvk, "", labels) - u.dew.AddApiWarnings(k8s2.ObjectRef{GVK: gvk}, apiWarnings) + u.dew.AddApiWarnings(k8s2.ObjectRef{ + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + }, apiWarnings) mutex.Lock() defer mutex.Unlock() if err != nil { @@ -81,7 +85,11 @@ func (u *RemoteObjectUtils) getAllByDiscriminator(k *k8s.K8sCluster, discriminat permissionErrCount += 1 return } - u.dew.AddWarning(k8s2.ObjectRef{GVK: gvk}, err) + u.dew.AddWarning(k8s2.ObjectRef{ + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + }, err) return } for _, o := range l { @@ -175,7 +183,7 @@ func (u *RemoteObjectUtils) UpdateRemoteObjects(k *k8s.K8sCluster, discriminator if onlyUsedGKs { usedGKs = map[schema.GroupKind]bool{} for _, ref := range refs { - usedGKs[ref.GVK.GroupKind()] = true + usedGKs[ref.GroupKind()] = true } } diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index 10f0080d0..80a16e151 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -39,7 +39,7 @@ func (o *Obfuscator) ObfuscateResult(r *result.CommandResult) error { } func (o *Obfuscator) ObfuscateChanges(ref k8s.ObjectRef, changes []result.Change) error { - if ref.GVK.GroupKind() == secretGk { + if ref.GroupKind() == secretGk { err := o.obfuscateSecretChanges(ref, changes) if err != nil { return err @@ -50,7 +50,7 @@ func (o *Obfuscator) ObfuscateChanges(ref k8s.ObjectRef, changes []result.Change func (o *Obfuscator) ObfuscateObject(x *uo.UnstructuredObject) error { ref := x.GetK8sRef() - if ref.GVK.GroupKind() == secretGk { + if ref.GroupKind() == secretGk { err := o.obfuscateSecret(x) if err != nil { return err diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 31b5a227c..e76955bb1 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -136,7 +136,7 @@ func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, func (k *K8sCluster) GetSingleObject(ref k8s.ObjectRef) (*uo.UnstructuredObject, []ApiWarning, error) { var result *uo.UnstructuredObject - apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { + apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GroupVersionKind(), ref.Namespace, func(r dynamic.ResourceInterface) error { o := v1.GetOptions{} x, err := r.Get(k.ctx, ref.Name, o) if err != nil { @@ -165,7 +165,7 @@ func (k *K8sCluster) DeleteSingleObject(ref k8s.ObjectRef, options DeleteOptions o.DryRun = []string{"All"} } - apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { + apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GroupVersionKind(), ref.Namespace, func(r dynamic.ResourceInterface) error { err := r.Delete(k.ctx, ref.Name, o) if err != nil { if options.IgnoreNotFoundError && errors.IsNotFound(err) { @@ -318,7 +318,7 @@ func (k *K8sCluster) doPatch(ref k8s.ObjectRef, data []byte, patchType types.Pat status.Trace(k.ctx, "patching %s", ref.String()) var result *uo.UnstructuredObject - apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { + apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GroupVersionKind(), ref.Namespace, func(r dynamic.ResourceInterface) error { x, err := r.Patch(k.ctx, ref.Name, patchType, data, po) if err != nil { return fmt.Errorf("failed to patch %s: %w", ref.String(), err) @@ -370,7 +370,7 @@ func (k *K8sCluster) UpdateObject(o *uo.UnstructuredObject, options UpdateOption status.Trace(k.ctx, "updating %s", ref.String()) var result *uo.UnstructuredObject - apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GVK, ref.Namespace, func(r dynamic.ResourceInterface) error { + apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GroupVersionKind(), ref.Namespace, func(r dynamic.ResourceInterface) error { x, err := r.Update(k.ctx, o.ToUnstructured(), updateOpts) if err != nil { return err diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 9a49b9093..902131e07 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -252,7 +252,7 @@ func (k *k8sResources) IsNamespaced(gv schema.GroupKind) *bool { func (k *k8sResources) FixNamespace(o *uo.UnstructuredObject, def string) { ref := o.GetK8sRef() - namespaced := k.IsNamespaced(ref.GVK.GroupKind()) + namespaced := k.IsNamespaced(ref.GroupKind()) if namespaced == nil { return } @@ -264,7 +264,7 @@ func (k *k8sResources) FixNamespace(o *uo.UnstructuredObject, def string) { } func (k *k8sResources) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { - namespaced := k.IsNamespaced(ref.GVK.GroupKind()) + namespaced := k.IsNamespaced(ref.GroupKind()) if namespaced == nil { return ref } diff --git a/pkg/seal/bootstrap.go b/pkg/seal/bootstrap.go index 84761159d..81c786346 100644 --- a/pkg/seal/bootstrap.go +++ b/pkg/seal/bootstrap.go @@ -24,8 +24,10 @@ const configMapName = "sealed-secrets-key-kluctl-bootstrap" func BootstrapSealedSecrets(ctx context.Context, k *k8s.K8sCluster, namespace string) error { existing, _, err := k.GetSingleObject(k8s2.ObjectRef{ - GVK: schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}, - Name: "sealedsecrets.bitnami.com", + Group: "apiextensions.k8s.io", + Version: "v1", + Kind: "CustomResourceDefinition", + Name: "sealedsecrets.bitnami.com", }) if existing != nil { // no bootstrap needed as the sealed-secrets operator seams to be installed already @@ -33,7 +35,9 @@ func BootstrapSealedSecrets(ctx context.Context, k *k8s.K8sCluster, namespace st } existing, _, err = k.GetSingleObject(k8s2.ObjectRef{ - GVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ConfigMap"}, + Group: "", + Version: "v1", + Kind: "ConfigMap", Name: configMapName, Namespace: namespace, }) diff --git a/pkg/types/k8s/ref.go b/pkg/types/k8s/ref.go index c3e3da2bc..fa91157e3 100644 --- a/pkg/types/k8s/ref.go +++ b/pkg/types/k8s/ref.go @@ -6,30 +6,52 @@ import ( ) type ObjectRef struct { - GVK schema.GroupVersionKind `json:"gvk,inline"` - Name string - Namespace string + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` + Name string `json:"name"` + Namespace string `json:"namespace"` } func (r ObjectRef) String() string { if r.Namespace != "" { - return fmt.Sprintf("%s/%s/%s", r.Namespace, r.GVK.Kind, r.Name) + return fmt.Sprintf("%s/%s/%s", r.Namespace, r.Kind, r.Name) } else { if r.Name != "" { - return fmt.Sprintf("%s/%s", r.GVK.Kind, r.Name) + return fmt.Sprintf("%s/%s", r.Kind, r.Name) } else { - return r.GVK.Kind + return r.Kind } } } +func (r ObjectRef) GroupVersionKind() schema.GroupVersionKind { + return schema.GroupVersionKind{ + Group: r.Group, + Version: r.Version, + Kind: r.Kind, + } +} + +func (r ObjectRef) GroupKind() schema.GroupKind { + return schema.GroupKind{ + Group: r.Group, + Kind: r.Kind, + } +} + +func (r ObjectRef) GroupVersion() schema.GroupVersion { + return schema.GroupVersion{ + Group: r.Group, + Version: r.Version, + } +} + func NewObjectRef(group string, version string, kind string, name string, namespace string) ObjectRef { return ObjectRef{ - GVK: schema.GroupVersionKind{ - Group: group, - Version: version, - Kind: kind, - }, + Group: group, + Version: version, + Kind: kind, Name: name, Namespace: namespace, } diff --git a/pkg/utils/uo/k8s_fields.go b/pkg/utils/uo/k8s_fields.go index f97296dee..88cc49884 100644 --- a/pkg/utils/uo/k8s_fields.go +++ b/pkg/utils/uo/k8s_fields.go @@ -83,8 +83,11 @@ func (uo *UnstructuredObject) SetK8sNamespace(namespace string) { } func (uo *UnstructuredObject) GetK8sRef() k8s.ObjectRef { + gvk := uo.GetK8sGVK() return k8s.ObjectRef{ - GVK: uo.GetK8sGVK(), + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, Name: uo.GetK8sName(), Namespace: uo.GetK8sNamespace(), } diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 94adf5079..eb88b480b 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -126,7 +126,7 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError // can't really say anything... return } - s, err := k.Resources.GetSchemaForGVK(ref.GVK) + s, err := k.Resources.GetSchemaForGVK(ref.GroupVersionKind()) if err != nil && !errors.IsNotFound(err) { addError(err.Error()) return From 4baec6191bfee28e8956156e8ab57b486d016844 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 27 Mar 2023 09:00:43 +0200 Subject: [PATCH 0911/2268] feat: Only store refs in RenderedObjects of DeploymentItems --- cmd/kluctl/commands/utils.go | 1 + pkg/types/deployment.go | 4 ++-- pkg/types/result/command_result.go | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index e41e9acd1..229ecd281 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -246,6 +246,7 @@ func addCommandInfo(r *result.CommandResult, command string, ctx *commandCtx, ta r.Command.AbortOnError = abortOnErrorFlags.AbortOnError } r.Deployment = &ctx.targetCtx.DeploymentProject.Config + r.RenderedObjects = ctx.targetCtx.DeploymentCollection.LocalObjects() return nil } diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index 6b39b7d0f..22517edab 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -3,7 +3,7 @@ package types import ( "encoding/json" "github.com/go-playground/validator/v10" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/yaml" ) @@ -23,7 +23,7 @@ type DeploymentItemConfig struct { // these are only allowed when writing the command result RenderedHelmChartConfig *HelmChartConfig `json:"renderedHelmChartConfig,omitempty"` - RenderedObjects []*uo.UnstructuredObject `json:"renderedObjects,omitempty"` + RenderedObjects []k8s.ObjectRef `json:"renderedObjects,omitempty"` RenderedInclude *DeploymentProjectConfig `json:"renderedInclude,omitempty"` } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 0c1251b5d..f509cf07e 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -60,8 +60,9 @@ type CommandInfo struct { } type CommandResult struct { - Command *CommandInfo `json:"command,omitempty"` - Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` + Command *CommandInfo `json:"command,omitempty"` + Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` + RenderedObjects []*uo.UnstructuredObject `json:"renderedObjects,omitempty"` NewObjects []*uo.UnstructuredObject `json:"newObjects,omitempty"` ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` From 51c32f7843a71e729025c8acbac16359b167d6ce Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 27 Mar 2023 21:22:56 +0200 Subject: [PATCH 0912/2268] feat: Rename error to message --- cmd/kluctl/commands/command_result.go | 2 +- pkg/deployment/commands/validate.go | 2 +- pkg/deployment/utils/apply_utils.go | 2 +- pkg/deployment/utils/delete_utils.go | 8 ++++---- pkg/deployment/utils/errors_holder.go | 10 +++++----- pkg/deployment/utils/remote_objects_utils_test.go | 2 +- pkg/types/result/command_result.go | 4 ++-- pkg/validation/validation.go | 12 ++++++------ 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index 384f2739d..6815a8b89 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -90,7 +90,7 @@ func prettyErrors(buf io.StringWriter, errors []result.DeploymentError) { if s := e.Ref.String(); s != "" { prefix = fmt.Sprintf("%s: ", s) } - _, _ = buf.WriteString(fmt.Sprintf(" %s%s\n", prefix, e.Error)) + _, _ = buf.WriteString(fmt.Sprintf(" %s%s\n", prefix, e.Message)) } } diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index b2dd91991..08cd62004 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -53,7 +53,7 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result remoteObject := cmd.ru.GetRemoteObject(ref) if remoteObject == nil { - r.Errors = append(r.Errors, result.DeploymentError{Ref: ref, Error: "object not found"}) + r.Errors = append(r.Errors, result.DeploymentError{Ref: ref, Message: "object not found"}) continue } r := validation.ValidateObject(k, remoteObject, true, false) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 4f9d84fef..0b610412e 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -461,7 +461,7 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo status.Warning(a.ctx, "Cancelled waiting for %s due to errors (%ds elapsed)", ref.String(), elapsed) } for _, e := range v.Errors { - a.HandleError(ref, fmt.Errorf(e.Error)) + a.HandleError(ref, fmt.Errorf(e.Message)) } return false } diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index 90a24c04e..f247f9f27 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -175,14 +175,14 @@ func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef ret.DeletedObjects = append(ret.DeletedObjects, ref) } else { ret.Errors = append(ret.Errors, result.DeploymentError{ - Ref: ref, - Error: err.Error(), + Ref: ref, + Message: err.Error(), }) } for _, w := range apiWarnings { ret.Warnings = append(ret.Warnings, result.DeploymentError{ - Ref: ref, - Error: w.Text, + Ref: ref, + Message: w.Text, }) } } diff --git a/pkg/deployment/utils/errors_holder.go b/pkg/deployment/utils/errors_holder.go index dd3c18a2d..363c5eeac 100644 --- a/pkg/deployment/utils/errors_holder.go +++ b/pkg/deployment/utils/errors_holder.go @@ -31,8 +31,8 @@ func (dew *DeploymentErrorsAndWarnings) Init() { func (dew *DeploymentErrorsAndWarnings) AddWarning(ref k8s.ObjectRef, warning error) { de := result.DeploymentError{ - Ref: ref, - Error: warning.Error(), + Ref: ref, + Message: warning.Error(), } dew.mutex.Lock() defer dew.mutex.Unlock() @@ -46,8 +46,8 @@ func (dew *DeploymentErrorsAndWarnings) AddWarning(ref k8s.ObjectRef, warning er func (dew *DeploymentErrorsAndWarnings) AddError(ref k8s.ObjectRef, err error) { de := result.DeploymentError{ - Ref: ref, - Error: err.Error(), + Ref: ref, + Message: err.Error(), } dew.mutex.Lock() defer dew.mutex.Unlock() @@ -99,7 +99,7 @@ func (dew *DeploymentErrorsAndWarnings) GetWarningsList() []result.DeploymentErr func (dew *DeploymentErrorsAndWarnings) getPlainErrorsList() []error { var ret []error for _, e := range dew.GetErrorsList() { - ret = append(ret, errors.New(e.Error)) + ret = append(ret, errors.New(e.Message)) } return ret } diff --git a/pkg/deployment/utils/remote_objects_utils_test.go b/pkg/deployment/utils/remote_objects_utils_test.go index d1ba3a0d8..449cfedf7 100644 --- a/pkg/deployment/utils/remote_objects_utils_test.go +++ b/pkg/deployment/utils/remote_objects_utils_test.go @@ -43,7 +43,7 @@ func TestRemoteObjectUtils_PermissionErrors(t *testing.T) { }, false) assert.NoError(t, err) assert.Equal(t, []result.DeploymentError{{ - Error: "at least one permission error was encountered while gathering objects by discriminator labels. This might result in orphan object detection to not work properly"}, + Message: "at least one permission error was encountered while gathering objects by discriminator labels. This might result in orphan object detection to not work properly"}, }, dew.GetWarningsList()) assert.Empty(t, dew.GetErrorsList()) } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index f509cf07e..ef1737e8e 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -20,8 +20,8 @@ type ChangedObject struct { } type DeploymentError struct { - Ref k8s.ObjectRef `json:"ref"` - Error string `json:"error"` + Ref k8s.ObjectRef `json:"ref"` + Message string `json:"message"` } type KluctlDeploymentInfo struct { diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index eb88b480b..09f23d401 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -61,10 +61,10 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError // all good } else if e, ok := r.(error); ok { err := fmt.Errorf("panic in ValidateObject: %w", e) - ret.Errors = append(ret.Errors, result.DeploymentError{Ref: ref, Error: err.Error()}) + ret.Errors = append(ret.Errors, result.DeploymentError{Ref: ref, Message: err.Error()}) } else { err := fmt.Errorf("panic in ValidateObject: %v", e) - ret.Errors = append(ret.Errors, result.DeploymentError{Ref: ref, Error: err.Error()}) + ret.Errors = append(ret.Errors, result.DeploymentError{Ref: ref, Message: err.Error()}) } ret.Ready = false } @@ -80,15 +80,15 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError addError := func(message string) { ret.Errors = append(ret.Errors, result.DeploymentError{ - Ref: ref, - Error: message, + Ref: ref, + Message: message, }) ret.Ready = false } addWarning := func(message string) { ret.Warnings = append(ret.Warnings, result.DeploymentError{ - Ref: ref, - Error: message, + Ref: ref, + Message: message, }) } addNotReady := func(message string) { From 4fe6777744e8f06fa40f5d94ab5de8c79ade24ed Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 3 Apr 2023 10:08:28 +0200 Subject: [PATCH 0913/2268] refactor: Let delete/prune commands actually perform the deletion and return a proper CommandResult --- cmd/kluctl/commands/cmd_delete.go | 20 +++++++----------- cmd/kluctl/commands/cmd_prune.go | 9 ++++---- pkg/deployment/commands/delete.go | 31 +++++++++++++++++++++++++--- pkg/deployment/commands/prune.go | 26 +++++++++++++++++++++-- pkg/deployment/utils/delete_utils.go | 21 ++++++------------- 5 files changed, 69 insertions(+), 38 deletions(-) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index bb238bff3..08c432eac 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -5,11 +5,8 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" - "github.com/kluctl/kluctl/v2/pkg/deployment/utils" - "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/types/result" ) type deleteCmd struct { @@ -51,13 +48,11 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { if cmd.Discriminator != "" { discriminator = cmd.Discriminator } - cmd2 := commands.NewDeleteCommand(discriminator, cmdCtx.targetCtx.DeploymentCollection.Inclusion) + cmd2 := commands.NewDeleteCommand(discriminator, cmdCtx.targetCtx.DeploymentCollection, cmdCtx.targetCtx.DeploymentCollection.Inclusion) - objects, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) - if err != nil { - return err - } - result, err := confirmedDeleteObjects(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) + result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, func(refs []k8s2.ObjectRef) error { + return confirmDeletion(ctx, refs, cmd.DryRun, cmd.Yes) + }) if err != nil { return err } @@ -76,7 +71,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { }) } -func confirmedDeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, dryRun bool, forceYes bool) (*result.CommandResult, error) { +func confirmDeletion(ctx context.Context, refs []k8s2.ObjectRef, dryRun bool, forceYes bool) error { if len(refs) != 0 { _, _ = getStderr(ctx).WriteString("The following objects will be deleted:\n") for _, ref := range refs { @@ -84,10 +79,9 @@ func confirmedDeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2. } if !forceYes && !dryRun { if !status.AskForConfirmation(ctx, fmt.Sprintf("Do you really want to delete %d objects?", len(refs))) { - return nil, fmt.Errorf("aborted") + return fmt.Errorf("aborted") } } } - - return utils.DeleteObjects(ctx, k, refs, true) + return nil } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index ba60b59a8..4c8b86d17 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" ) type pruneCmd struct { @@ -46,11 +47,9 @@ func (cmd *pruneCmd) Run(ctx context.Context) error { func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) - objects, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) - if err != nil { - return err - } - result, err := confirmedDeleteObjects(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, objects, cmd.DryRun, cmd.Yes) + result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, func(refs []k8s2.ObjectRef) error { + return confirmDeletion(cmdCtx.ctx, refs, cmd.DryRun, cmd.Yes) + }) if err != nil { return err } diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index da6d7e5d9..49a0e27a3 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -3,25 +3,29 @@ package commands import ( "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" ) type DeleteCommand struct { + c *deployment.DeploymentCollection discriminator string inclusion *utils.Inclusion } -func NewDeleteCommand(discriminator string, inclusion *utils.Inclusion) *DeleteCommand { +func NewDeleteCommand(discriminator string, c *deployment.DeploymentCollection, inclusion *utils.Inclusion) *DeleteCommand { return &DeleteCommand{ discriminator: discriminator, + c: c, inclusion: inclusion, } } -func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.ObjectRef, error) { +func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb func(refs []k8s2.ObjectRef) error) (*result.CommandResult, error) { if cmd.discriminator == "" { return nil, fmt.Errorf("deletion without a discriminator is not supported") } @@ -33,5 +37,26 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.Ob return nil, err } - return utils2.FindObjectsForDelete(k, ru.GetFilteredRemoteObjects(cmd.inclusion), cmd.inclusion.HasType("tags"), nil) + deleteRefs, err := utils2.FindObjectsForDelete(k, ru.GetFilteredRemoteObjects(cmd.inclusion), cmd.inclusion.HasType("tags"), nil) + if err != nil { + return nil, err + } + + if confirmCb != nil { + err = confirmCb(deleteRefs) + if err != nil { + return nil, err + } + } + + deleted, err := utils2.DeleteObjects(ctx, k, deleteRefs, dew, true) + if err != nil { + return nil, err + } + + return &result.CommandResult{ + DeletedObjects: deleted, + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + }, nil } diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index 4b7a32cfe..c285bb12a 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -7,6 +7,7 @@ import ( utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" ) type PruneCommand struct { @@ -21,7 +22,7 @@ func NewPruneCommand(discriminator string, c *deployment.DeploymentCollection) * } } -func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.ObjectRef, error) { +func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb func(refs []k8s2.ObjectRef) error) (*result.CommandResult, error) { if cmd.discriminator == "" { return nil, fmt.Errorf("pruning without a discriminator is not supported") } @@ -34,7 +35,28 @@ func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster) ([]k8s2.Obj return nil, err } - return FindOrphanObjects(k, ru, cmd.c) + deleteRefs, err := FindOrphanObjects(k, ru, cmd.c) + if err != nil { + return nil, err + } + + if confirmCb != nil { + err = confirmCb(deleteRefs) + if err != nil { + return nil, err + } + } + + deleted, err := utils2.DeleteObjects(ctx, k, deleteRefs, dew, true) + if err != nil { + return nil, err + } + + return &result.CommandResult{ + DeletedObjects: deleted, + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + }, nil } func FindOrphanObjects(k *k8s.K8sCluster, ru *utils2.RemoteObjectUtils, c *deployment.DeploymentCollection) ([]k8s2.ObjectRef, error) { diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index f247f9f27..0cab9e94e 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -4,7 +4,6 @@ import ( "context" "github.com/kluctl/kluctl/v2/pkg/k8s" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -160,10 +159,10 @@ func FindObjectsForDelete(k *k8s.K8sCluster, allClusterObjects []*uo.Unstructure return ret, nil } -func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, doWait bool) (*result.CommandResult, error) { +func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, dew *DeploymentErrorsAndWarnings, doWait bool) ([]k8s2.ObjectRef, error) { g := utils.NewGoHelper(ctx, 8) - var ret result.CommandResult + var ret []k8s2.ObjectRef namespaceNames := make(map[string]bool) var mutex sync.Mutex @@ -172,19 +171,11 @@ func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef defer mutex.Unlock() if err == nil { - ret.DeletedObjects = append(ret.DeletedObjects, ref) + ret = append(ret, ref) } else { - ret.Errors = append(ret.Errors, result.DeploymentError{ - Ref: ref, - Message: err.Error(), - }) - } - for _, w := range apiWarnings { - ret.Warnings = append(ret.Warnings, result.DeploymentError{ - Ref: ref, - Message: w.Text, - }) + dew.AddError(ref, err) } + dew.AddApiWarnings(ref, apiWarnings) } for _, ref_ := range refs { @@ -215,5 +206,5 @@ func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef } g.Wait() - return &ret, nil + return ret, nil } From b7fcc1117075c7891953c21b349c2a5dcb534c1e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 2 Apr 2023 00:46:36 +0200 Subject: [PATCH 0914/2268] fix: Preserve when fields instead of rendering+overwriting them --- pkg/deployment/deployment_collection.go | 11 +++--- pkg/deployment/deployment_project.go | 48 ++++++------------------- pkg/vars/vars.go | 17 +++++++++ pkg/vars/vars_loader.go | 15 ++++---- 4 files changed, 40 insertions(+), 51 deletions(-) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index dd7bfb426..53f31c0de 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -2,7 +2,6 @@ package deployment import ( "fmt" - "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" @@ -83,12 +82,16 @@ func findDeploymentItemIndex(project *DeploymentProject, pth *string, indexes ma func (c *DeploymentCollection) collectAllDeployments(project *DeploymentProject, indexes map[string]int) ([]*DeploymentItem, error) { var ret []*DeploymentItem - if !kluctl_jinja2.IsConditionalTrue(project.Config.When) { - return nil, nil + if x, err := project.CheckWhenTrue(); !x || err != nil { + return nil, err } for i, diConfig := range project.Config.Deployments { - if !kluctl_jinja2.IsConditionalTrue(diConfig.When) { + whenTrue, err := project.VarsCtx.CheckConditional(diConfig.When) + if err != nil { + return nil, err + } + if !whenTrue { continue } diff --git a/pkg/deployment/deployment_project.go b/pkg/deployment/deployment_project.go index 929073405..34c7d713e 100644 --- a/pkg/deployment/deployment_project.go +++ b/pkg/deployment/deployment_project.go @@ -3,7 +3,6 @@ package deployment import ( "fmt" securejoin "github.com/cyphar/filepath-securejoin" - "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -57,8 +56,8 @@ func NewDeploymentProject(ctx SharedContext, varsCtx *vars.VarsCtx, source Sourc return nil, fmt.Errorf("failed to load deployment config for %s: %w", dir, err) } - if !kluctl_jinja2.IsConditionalTrue(dp.Config.When) { - return dp, nil + if x, err := dp.CheckWhenTrue(); !x || err != nil { + return dp, err } err = dp.loadIncludes() @@ -143,11 +142,6 @@ func (p *DeploymentProject) processConfig() error { return err } - err = p.renderConditionals() - if err != nil { - return err - } - return nil } @@ -176,38 +170,12 @@ func (p *DeploymentProject) checkDeploymentDirs() error { return nil } -func (p *DeploymentProject) renderConditionals() error { +func (p *DeploymentProject) CheckWhenTrue() (bool, error) { if p.parentProject == nil && p.Config.When != "" { - return fmt.Errorf("the root deployment project can not contain 'when'") - } - - vars, err := p.VarsCtx.Vars.ToMap() - if err != nil { - return err - } - r, err := kluctl_jinja2.RenderConditional(p.VarsCtx.J2, vars, p.Config.When) - if err != nil { - return err + return false, fmt.Errorf("the root deployment project can not contain 'when'") } - p.Config.When = r - if !kluctl_jinja2.IsConditionalTrue(p.Config.When) { - // No need to render individual deployment item conditionals - return nil - } - - conditionals := make([]string, 0, len(p.Config.Deployments)) - for _, di := range p.Config.Deployments { - conditionals = append(conditionals, di.When) - } - rendered, err := kluctl_jinja2.RenderConditionals(p.VarsCtx.J2, vars, conditionals) - if err != nil { - return err - } - for i, r := range rendered { - p.Config.Deployments[i].When = r - } - return nil + return p.VarsCtx.CheckConditional(p.Config.When) } func (p *DeploymentProject) loadIncludes() error { @@ -215,7 +183,11 @@ func (p *DeploymentProject) loadIncludes() error { var err error var newProject *DeploymentProject - if !kluctl_jinja2.IsConditionalTrue(inc.When) { + whenTrue, err := p.VarsCtx.CheckConditional(inc.When) + if err != nil { + return err + } + if !whenTrue { continue } diff --git a/pkg/vars/vars.go b/pkg/vars/vars.go index 11151f24d..826233fb5 100644 --- a/pkg/vars/vars.go +++ b/pkg/vars/vars.go @@ -2,6 +2,7 @@ package vars import ( "github.com/kluctl/go-jinja2" + "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" ) @@ -98,3 +99,19 @@ func (vc *VarsCtx) RenderDirectory(sourceDir string, targetDir string, excludePa } return vc.J2.RenderDirectory(sourceDir, targetDir, excludePatterns, jinja2.WithGlobals(globals), jinja2.WithSearchDirs(searchDirs), jinja2.WithTemplateIgnoreRootDir(templateIgnoreRoot)) } + +func (vc *VarsCtx) CheckConditional(c string) (bool, error) { + if kluctl_jinja2.IsConditionalTrue(c) { + return true, nil + } + + m, err := vc.Vars.ToMap() + if err != nil { + return false, err + } + c, err = kluctl_jinja2.RenderConditional(vc.J2, m, c) + if err != nil { + return false, err + } + return kluctl_jinja2.IsConditionalTrue(c), nil +} diff --git a/pkg/vars/vars_loader.go b/pkg/vars/vars_loader.go index 9c90b4000..8ef2b6ed5 100644 --- a/pkg/vars/vars_loader.go +++ b/pkg/vars/vars_loader.go @@ -8,7 +8,6 @@ import ( types2 "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types" "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" @@ -83,14 +82,12 @@ func (v *VarsLoader) LoadVars(varsCtx *VarsCtx, sourceIn *types.VarsSource, sear return err } - if source.When != "" { - r, err := kluctl_jinja2.RenderConditional(varsCtx.J2, globals, source.When) - if err != nil { - return err - } - if !kluctl_jinja2.IsConditionalTrue(r) { - return nil - } + whenTrue, err := varsCtx.CheckConditional(source.When) + if err != nil { + return err + } + if !whenTrue { + return nil } ignoreMissing := false From 704b2800159953f7810a1842f68fce7f2518eeb9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 4 Apr 2023 09:16:59 +0200 Subject: [PATCH 0915/2268] feat: Store rendered/remote/applied objects in CommandResult --- cmd/kluctl/commands/command_result.go | 10 ++----- cmd/kluctl/commands/utils.go | 1 - pkg/deployment/commands/delete.go | 8 ++++-- pkg/deployment/commands/deploy.go | 38 +++++++++++++++----------- pkg/deployment/commands/diff.go | 19 +++++++------ pkg/deployment/commands/poke_images.go | 27 ++++++++++++------ pkg/deployment/commands/prune.go | 8 ++++-- pkg/deployment/utils/apply_utils.go | 17 ++++++++++-- pkg/diff/obfuscate.go | 18 ++++++++++-- pkg/types/result/command_result.go | 25 +++++++++-------- 10 files changed, 109 insertions(+), 62 deletions(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index 6815a8b89..f8a874cc3 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -26,11 +26,7 @@ func formatCommandResultText(cr *result.CommandResult, short bool) string { if len(cr.NewObjects) != 0 { buf.WriteString("\nNew objects:\n") - var refs []k8s.ObjectRef - for _, o := range cr.NewObjects { - refs = append(refs, o.GetK8sRef()) - } - prettyObjectRefs(buf, refs) + prettyObjectRefs(buf, cr.NewObjects) } if len(cr.ChangedObjects) != 0 { buf.WriteString("\nChanged objects:\n") @@ -57,10 +53,10 @@ func formatCommandResultText(cr *result.CommandResult, short bool) string { prettyObjectRefs(buf, cr.DeletedObjects) } - if len(cr.HookObjects) != 0 { + if len(cr.AppliedHookObjects) != 0 { buf.WriteString("\nApplied hooks:\n") var refs []k8s.ObjectRef - for _, o := range cr.HookObjects { + for _, o := range cr.AppliedHookObjects { refs = append(refs, o.GetK8sRef()) } prettyObjectRefs(buf, refs) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 229ecd281..e41e9acd1 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -246,7 +246,6 @@ func addCommandInfo(r *result.CommandResult, command string, ctx *commandCtx, ta r.Command.AbortOnError = abortOnErrorFlags.AbortOnError } r.Deployment = &ctx.targetCtx.DeploymentProject.Config - r.RenderedObjects = ctx.targetCtx.DeploymentCollection.LocalObjects() return nil } diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index 49a0e27a3..6d7ebe07b 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -55,8 +55,10 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb } return &result.CommandResult{ - DeletedObjects: deleted, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), + RenderedObjects: cmd.c.LocalObjects(), + RemoteObjects: ru.GetFilteredRemoteObjects(nil), + DeletedObjects: deleted, + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), }, nil } diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index a1f98531f..427710916 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -65,14 +65,17 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) diffResult := &result.CommandResult{ - NewObjects: au.GetNewObjects(), - ChangedObjects: du.ChangedObjects, - DeletedObjects: au.GetDeletedObjects(), - HookObjects: au.GetAppliedHookObjects(), - OrphanObjects: orphanObjects, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + RenderedObjects: cmd.c.LocalObjects(), + RemoteObjects: ru.GetFilteredRemoteObjects(nil), + AppliedObjects: au.GetAppliedObjects(), + AppliedHookObjects: au.GetAppliedHookObjects(), + NewObjects: au.GetNewObjectRefs(), + ChangedObjects: du.ChangedObjects, + DeletedObjects: au.GetDeletedObjects(), + OrphanObjects: orphanObjects, + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + SeenImages: cmd.c.Images.SeenImages(false), } err = diffResultCb(diffResult) @@ -99,13 +102,16 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult return nil, err } return &result.CommandResult{ - NewObjects: au.GetNewObjects(), - ChangedObjects: du.ChangedObjects, - DeletedObjects: au.GetDeletedObjects(), - HookObjects: au.GetAppliedHookObjects(), - OrphanObjects: orphanObjects, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + RenderedObjects: cmd.c.LocalObjects(), + RemoteObjects: ru.GetFilteredRemoteObjects(nil), + AppliedObjects: au.GetAppliedObjects(), + AppliedHookObjects: au.GetAppliedHookObjects(), + NewObjects: au.GetNewObjectRefs(), + ChangedObjects: du.ChangedObjects, + DeletedObjects: au.GetDeletedObjects(), + OrphanObjects: orphanObjects, + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + SeenImages: cmd.c.Images.SeenImages(false), }, nil } diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index 335fecc91..c15f97864 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -66,13 +66,16 @@ func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.Com return nil, err } return &result.CommandResult{ - NewObjects: au.GetNewObjects(), - ChangedObjects: du.ChangedObjects, - DeletedObjects: au.GetDeletedObjects(), - HookObjects: au.GetAppliedHookObjects(), - OrphanObjects: orphanObjects, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + RenderedObjects: cmd.c.LocalObjects(), + RemoteObjects: ru.GetFilteredRemoteObjects(nil), + AppliedObjects: au.GetAppliedObjects(), + AppliedHookObjects: au.GetAppliedHookObjects(), + NewObjects: au.GetNewObjectRefs(), + ChangedObjects: du.ChangedObjects, + DeletedObjects: au.GetDeletedObjects(), + OrphanObjects: orphanObjects, + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + SeenImages: cmd.c.Images.SeenImages(false), }, nil } diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 8af5d519d..d2c4de720 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -71,7 +71,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu return o, nil } - ad := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, &utils2.ApplyUtilOptions{}) + au := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, &utils2.ApplyUtilOptions{}) for ref, containers := range containersAndImages { ref := ref @@ -79,7 +79,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu wg.Add(1) go func() { defer wg.Done() - au := ad.NewApplyUtil(ctx, nil) + au := au.NewApplyUtil(ctx, nil) remote := ru.GetRemoteObject(ref) if remote == nil { dew.AddWarning(ref, fmt.Errorf("remote object not found, skipped image replacement")) @@ -92,14 +92,25 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu } wg.Wait() - du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, ad.GetAppliedObjectsMap()) + du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, au.GetAppliedObjectsMap()) du.Diff() + orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) + if err != nil { + return nil, err + } + return &result.CommandResult{ - NewObjects: nil, - ChangedObjects: du.ChangedObjects, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + RenderedObjects: cmd.c.LocalObjects(), + RemoteObjects: ru.GetFilteredRemoteObjects(nil), + AppliedObjects: au.GetAppliedObjects(), + AppliedHookObjects: au.GetAppliedHookObjects(), + NewObjects: au.GetNewObjectRefs(), + ChangedObjects: du.ChangedObjects, + DeletedObjects: au.GetDeletedObjects(), + OrphanObjects: orphanObjects, + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + SeenImages: cmd.c.Images.SeenImages(false), }, nil } diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index c285bb12a..5791db8d7 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -53,9 +53,11 @@ func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb f } return &result.CommandResult{ - DeletedObjects: deleted, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), + RenderedObjects: cmd.c.LocalObjects(), + RemoteObjects: ru.GetFilteredRemoteObjects(nil), + DeletedObjects: deleted, + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), }, nil } diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 0b610412e..750195855 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -768,8 +768,21 @@ func (ad *ApplyDeploymentsUtil) collectObjects(f func(au *ApplyUtil) map[k8s2.Ob return ret } -func (ad *ApplyDeploymentsUtil) GetNewObjects() []*uo.UnstructuredObject { - return ad.collectObjects(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { +func (ad *ApplyDeploymentsUtil) collectObjectRefs(f func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject) []k8s2.ObjectRef { + ad.resultsMutex.Lock() + defer ad.resultsMutex.Unlock() + + var ret []k8s2.ObjectRef + for _, a := range ad.results { + for _, o := range f(a) { + ret = append(ret, o.GetK8sRef()) + } + } + return ret +} + +func (ad *ApplyDeploymentsUtil) GetNewObjectRefs() []k8s2.ObjectRef { + return ad.collectObjectRefs(func(au *ApplyUtil) map[k8s2.ObjectRef]*uo.UnstructuredObject { return au.newObjects }) } diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index 80a16e151..d00332960 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -23,14 +23,26 @@ func (o *Obfuscator) ObfuscateResult(r *result.CommandResult) error { return err } } - for _, n := range r.NewObjects { + for _, n := range r.RenderedObjects { err := o.ObfuscateObject(n) if err != nil { return err } } - for _, h := range r.HookObjects { - err := o.ObfuscateObject(h) + for _, n := range r.RemoteObjects { + err := o.ObfuscateObject(n) + if err != nil { + return err + } + } + for _, n := range r.AppliedObjects { + err := o.ObfuscateObject(n) + if err != nil { + return err + } + } + for _, n := range r.AppliedHookObjects { + err := o.ObfuscateObject(n) if err != nil { return err } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index ef1737e8e..ea55be518 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -60,18 +60,21 @@ type CommandInfo struct { } type CommandResult struct { - Command *CommandInfo `json:"command,omitempty"` - Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` - RenderedObjects []*uo.UnstructuredObject `json:"renderedObjects,omitempty"` + Command *CommandInfo `json:"command,omitempty"` + Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` - NewObjects []*uo.UnstructuredObject `json:"newObjects,omitempty"` - ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` - HookObjects []*uo.UnstructuredObject `json:"hookObjects,omitempty"` - OrphanObjects []k8s.ObjectRef `json:"orphanObjects,omitempty"` - DeletedObjects []k8s.ObjectRef `json:"deletedObjects,omitempty"` - Errors []DeploymentError `json:"errors,omitempty"` - Warnings []DeploymentError `json:"warnings,omitempty"` - SeenImages []types.FixedImage `json:"seenImages,omitempty"` + RenderedObjects []*uo.UnstructuredObject `json:"renderedObjects,omitempty"` + RemoteObjects []*uo.UnstructuredObject `json:"remoteObjects,omitempty"` + AppliedObjects []*uo.UnstructuredObject `json:"appliedObjects,omitempty"` + AppliedHookObjects []*uo.UnstructuredObject `json:"appliedHookObjects,omitempty"` + + NewObjects []k8s.ObjectRef `json:"newObjects,omitempty"` + ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` + OrphanObjects []k8s.ObjectRef `json:"orphanObjects,omitempty"` + DeletedObjects []k8s.ObjectRef `json:"deletedObjects,omitempty"` + Errors []DeploymentError `json:"errors,omitempty"` + Warnings []DeploymentError `json:"warnings,omitempty"` + SeenImages []types.FixedImage `json:"seenImages,omitempty"` } type ValidateResultEntry struct { From 5532a019649809dab2cd69146221f43ce8551b20 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 4 Apr 2023 09:17:49 +0200 Subject: [PATCH 0916/2268] fix: Fix crash on secrets/configmaps with null data --- pkg/diff/obfuscate.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index d00332960..96a8ea254 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -119,7 +119,7 @@ func (o *Obfuscator) obfuscateSecretChanges(ref k8s.ObjectRef, changes []result. func (o *Obfuscator) obfuscateSecret(x *uo.UnstructuredObject) error { data, ok, _ := x.GetNestedField("data") - if ok { + if ok && data != nil { if m, ok := data.(map[string]any); ok { for k, _ := range m { m[k] = base64.StdEncoding.EncodeToString([]byte("*****")) @@ -129,7 +129,7 @@ func (o *Obfuscator) obfuscateSecret(x *uo.UnstructuredObject) error { } } data, ok, _ = x.GetNestedField("stringData") - if ok { + if ok && data != nil { if m, ok := data.(map[string]any); ok { for k, _ := range m { m[k] = "*****" From e441a9ad42ff7a3cc50edcb2d5fe46ee927a59c6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 4 Apr 2023 09:18:09 +0200 Subject: [PATCH 0917/2268] fix: Omit empty group/namespace from json ObjectRef --- pkg/types/k8s/ref.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/types/k8s/ref.go b/pkg/types/k8s/ref.go index fa91157e3..b257ca428 100644 --- a/pkg/types/k8s/ref.go +++ b/pkg/types/k8s/ref.go @@ -6,11 +6,11 @@ import ( ) type ObjectRef struct { - Group string `json:"group"` - Version string `json:"version"` + Group string `json:"group,omitempty"` + Version string `json:"version,omitempty"` Kind string `json:"kind"` Name string `json:"name"` - Namespace string `json:"namespace"` + Namespace string `json:"namespace,omitempty"` } func (r ObjectRef) String() string { From 29d1abdd06ad980ca441aeec74df51f9dc69ec43 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 4 Apr 2023 15:54:26 +0200 Subject: [PATCH 0918/2268] feat: Add start/end time to CommandResult --- cmd/kluctl/commands/cmd_delete.go | 4 ++- cmd/kluctl/commands/cmd_deploy.go | 8 ++++-- cmd/kluctl/commands/cmd_diff.go | 4 ++- cmd/kluctl/commands/cmd_poke_images.go | 4 ++- cmd/kluctl/commands/cmd_prune.go | 8 ++++-- cmd/kluctl/commands/utils.go | 6 +++- pkg/types/result/command_result.go | 2 ++ pkg/types/time.go | 39 ++++++++++++++++++++++++++ 8 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 pkg/types/time.go diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 08c432eac..57f7c1a74 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "time" ) type deleteCmd struct { @@ -43,6 +44,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } + startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { discriminator := cmdCtx.targetCtx.Target.Discriminator if cmd.Discriminator != "" { @@ -56,7 +58,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { if err != nil { return err } - err = addCommandInfo(result, "delete", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) + err = addCommandInfo(result, startTime, "delete", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 469d93ae1..e6e918d58 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types/result" + "time" ) type deployCmd struct { @@ -46,12 +47,13 @@ func (cmd *deployCmd) Run(ctx context.Context) error { dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } + startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - return cmd.runCmdDeploy(cmdCtx) + return cmd.runCmdDeploy(cmdCtx, startTime) }) } -func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { +func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx, startTime time.Time) error { status.Trace(cmdCtx.ctx, "enter runCmdDeploy") defer status.Trace(cmdCtx.ctx, "leave runCmdDeploy") @@ -74,7 +76,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { if err != nil { return err } - err = addCommandInfo(result, "deploy", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, &cmd.ForceApplyFlags, &cmd.ReplaceOnErrorFlags, &cmd.AbortOnErrorFlags, cmd.NoWait) + err = addCommandInfo(result, startTime, "deploy", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, &cmd.ForceApplyFlags, &cmd.ReplaceOnErrorFlags, &cmd.AbortOnErrorFlags, cmd.NoWait) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 46838fcd3..640caecee 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + "time" ) type diffCmd struct { @@ -38,6 +39,7 @@ func (cmd *diffCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, } + startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { cmd2 := commands.NewDiffCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) cmd2.ForceApply = cmd.ForceApply @@ -50,7 +52,7 @@ func (cmd *diffCmd) Run(ctx context.Context) error { if err != nil { return err } - err = addCommandInfo(result, "diff", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, nil, &cmd.ForceApplyFlags, &cmd.ReplaceOnErrorFlags, nil, false) + err = addCommandInfo(result, startTime, "diff", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, nil, &cmd.ForceApplyFlags, &cmd.ReplaceOnErrorFlags, nil, false) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index ac72efe70..fccb20bc9 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -6,6 +6,7 @@ import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/status" + "time" ) type pokeImagesCmd struct { @@ -38,6 +39,7 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } + startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { if !status.AskForConfirmation(ctx, fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", cmdCtx.targetCtx.ClusterContext)) { @@ -51,7 +53,7 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { if err != nil { return err } - err = addCommandInfo(result, "poke-images", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) + err = addCommandInfo(result, startTime, "poke-images", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 4c8b86d17..cd19d2e33 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -6,6 +6,7 @@ import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "time" ) type pruneCmd struct { @@ -40,12 +41,13 @@ func (cmd *pruneCmd) Run(ctx context.Context) error { dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, } + startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - return cmd.runCmdPrune(cmdCtx) + return cmd.runCmdPrune(cmdCtx, startTime) }) } -func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { +func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx, startTime time.Time) error { cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, func(refs []k8s2.ObjectRef) error { return confirmDeletion(cmdCtx.ctx, refs, cmd.DryRun, cmd.Yes) @@ -53,7 +55,7 @@ func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { if err != nil { return err } - err = addCommandInfo(result, "prune", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) + err = addCommandInfo(result, startTime, "prune", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) if err != nil { return err } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index e41e9acd1..91e801504 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -3,9 +3,11 @@ package commands import ( "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/result" "os" "strings" + "time" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" @@ -205,11 +207,13 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm return cb(cmdCtx) } -func addCommandInfo(r *result.CommandResult, command string, ctx *commandCtx, targetFlags *args.TargetFlags, +func addCommandInfo(r *result.CommandResult, startTime time.Time, command string, ctx *commandCtx, targetFlags *args.TargetFlags, imageFlags *args.ImageFlags, inclusionFlags *args.InclusionFlags, dryRunFlags *args.DryRunFlags, forceApplyFlags *args.ForceApplyFlags, replaceOnErrorFlags *args.ReplaceOnErrorFlags, abortOnErrorFlags *args.AbortOnErrorFlags, noWait bool) error { r.Command = &result.CommandInfo{ Initiator: result.CommandInititiator_CommandLine, + StartTime: types.FromTime(startTime), + EndTime: types.FromTime(time.Now()), Command: command, Target: ctx.targetCtx.Target, Args: ctx.targetCtx.KluctlProject.LoadArgs.ExternalArgs, diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index ea55be518..2596242b2 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -40,6 +40,8 @@ const ( type CommandInfo struct { Initiator CommandInitiator `json:"initiator" validate:"oneof=CommandLine KluctlDeployment"` + StartTime types.JsonTime `json:"startTime"` + EndTime types.JsonTime `json:"endTime"` KluctlDeployment *KluctlDeploymentInfo `json:"kluctlDeployment,omitempty"` Command string `json:"command,omitempty"` Target *types.Target `json:"target,omitempty"` diff --git a/pkg/types/time.go b/pkg/types/time.go new file mode 100644 index 000000000..1706b8606 --- /dev/null +++ b/pkg/types/time.go @@ -0,0 +1,39 @@ +package types + +import ( + "encoding/json" + "time" +) + +type JsonTime string + +func FromTime(t time.Time) JsonTime { + return JsonTime(t.Format(time.RFC3339Nano)) +} + +func (t JsonTime) ToTime() (time.Time, error) { + t2, err := time.Parse(time.RFC3339Nano, string(t)) + if err != nil { + return time.Time{}, err + } + return t2, nil +} + +func (t *JsonTime) UnmarshalJSON(b []byte) error { + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + t2 := JsonTime(s) + _, err = t2.ToTime() + if err != nil { + return err + } + *t = t2 + return nil +} + +func (t JsonTime) MarshalJSON() ([]byte, error) { + return json.Marshal(string(t)) +} From 25b32ffeed9d8e1f50375e96de67ae3fdb1b0429 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 4 Apr 2023 21:42:02 +0200 Subject: [PATCH 0919/2268] feat: Add CommandResultSummary --- pkg/types/result/summary.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 pkg/types/result/summary.go diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go new file mode 100644 index 000000000..d8d8adfb7 --- /dev/null +++ b/pkg/types/result/summary.go @@ -0,0 +1,20 @@ +package result + +type CommandResultSummary struct { + Id string `json:"id"` + Command *CommandInfo `json:"commandInfo"` + + RenderedObjects int `json:"renderedObjects"` + RemoteObjects int `json:"remoteObjects"` + AppliedObjects int `json:"appliedObjects"` + AppliedHookObjects int `json:"appliedHookObjects"` + + NewObjects int `json:"newObjects"` + ChangedObjects int `json:"changedObjects"` + OrphanObjects int `json:"orphanObjects"` + DeletedObjects int `json:"deletedObjects"` + Errors int `json:"errors"` + Warnings int `json:"warnings"` + + TotalChanges int `json:"totalChanges"` +} From da4121be687e3b9ec05da4de38161e94f8bb9b58 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 5 Apr 2023 23:16:49 +0200 Subject: [PATCH 0920/2268] feat: Compact objects in CommandResult --- cmd/kluctl/commands/command_result.go | 66 ++++++---- pkg/deployment/commands/delete.go | 8 +- pkg/deployment/commands/deploy.go | 30 ++--- pkg/deployment/commands/diff.go | 15 +-- pkg/deployment/commands/poke_images.go | 15 +-- pkg/deployment/commands/prune.go | 7 +- pkg/deployment/commands/utils.go | 87 +++++++++++++ pkg/deployment/utils/diff_utils.go | 14 +-- pkg/deployment/utils/diff_utils_test.go | 2 +- pkg/diff/obfuscate.go | 25 ++-- pkg/types/result/command_result.go | 36 ++++-- pkg/types/result/compact.go | 155 ++++++++++++++++++++++++ 12 files changed, 347 insertions(+), 113 deletions(-) create mode 100644 pkg/deployment/commands/utils.go create mode 100644 pkg/types/result/compact.go diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index f8a874cc3..c77356586 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -24,46 +24,64 @@ func formatCommandResultText(cr *result.CommandResult, short bool) string { prettyErrors(buf, cr.Warnings) } - if len(cr.NewObjects) != 0 { + var newObjects []k8s.ObjectRef + var changedObjects []k8s.ObjectRef + var deletedObjects []k8s.ObjectRef + var orphanObjects []k8s.ObjectRef + var appliedHookObjects []k8s.ObjectRef + + for _, o := range cr.Objects { + if o.New { + newObjects = append(newObjects, o.Ref) + } + if len(o.Changes) != 0 { + changedObjects = append(changedObjects, o.Ref) + } + if o.Deleted { + deletedObjects = append(deletedObjects, o.Ref) + } + if o.Orphan { + orphanObjects = append(orphanObjects, o.Ref) + } + if o.Hook { + appliedHookObjects = append(appliedHookObjects, o.Ref) + } + } + + if len(newObjects) != 0 { buf.WriteString("\nNew objects:\n") - prettyObjectRefs(buf, cr.NewObjects) + prettyObjectRefs(buf, newObjects) } - if len(cr.ChangedObjects) != 0 { + if len(changedObjects) != 0 { buf.WriteString("\nChanged objects:\n") - var refs []k8s.ObjectRef - for _, co := range cr.ChangedObjects { - refs = append(refs, co.Ref) - } - prettyObjectRefs(buf, refs) + prettyObjectRefs(buf, changedObjects) if !short { buf.WriteString("\n") - - for i, co := range cr.ChangedObjects { + for i, o := range cr.Objects { + if len(o.Changes) == 0 { + continue + } if i != 0 { buf.WriteString("\n") } - prettyChanges(buf, co.Ref, co.Changes) + prettyChanges(buf, o.Ref, o.Changes) } } } - if len(cr.DeletedObjects) != 0 { + if len(deletedObjects) != 0 { buf.WriteString("\nDeleted objects:\n") - prettyObjectRefs(buf, cr.DeletedObjects) + prettyObjectRefs(buf, deletedObjects) } - if len(cr.AppliedHookObjects) != 0 { + if len(appliedHookObjects) != 0 { buf.WriteString("\nApplied hooks:\n") - var refs []k8s.ObjectRef - for _, o := range cr.AppliedHookObjects { - refs = append(refs, o.GetK8sRef()) - } - prettyObjectRefs(buf, refs) + prettyObjectRefs(buf, appliedHookObjects) } - if len(cr.OrphanObjects) != 0 { + if len(orphanObjects) != 0 { buf.WriteString("\nOrphan objects:\n") - prettyObjectRefs(buf, cr.OrphanObjects) + prettyObjectRefs(buf, orphanObjects) } if len(cr.Errors) != 0 { @@ -104,7 +122,11 @@ func prettyChanges(buf io.StringWriter, ref k8s.ObjectRef, changes []result.Chan } func formatCommandResultYaml(cr *result.CommandResult) (string, error) { - b, err := yaml.WriteYamlString(cr) + compactedCr := *cr + compactedCr.CompactedObjects = compactedCr.Objects + compactedCr.Objects = nil + + b, err := yaml.WriteYamlString(compactedCr) if err != nil { return "", err } diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index 6d7ebe07b..970362d97 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -55,10 +55,8 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb } return &result.CommandResult{ - RenderedObjects: cmd.c.LocalObjects(), - RemoteObjects: ru.GetFilteredRemoteObjects(nil), - DeletedObjects: deleted, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), + Objects: collectObjects(cmd.c, ru, nil, nil, nil, deleted), + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), }, nil } diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index 427710916..6b58c8762 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -65,17 +65,10 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) diffResult := &result.CommandResult{ - RenderedObjects: cmd.c.LocalObjects(), - RemoteObjects: ru.GetFilteredRemoteObjects(nil), - AppliedObjects: au.GetAppliedObjects(), - AppliedHookObjects: au.GetAppliedHookObjects(), - NewObjects: au.GetNewObjectRefs(), - ChangedObjects: du.ChangedObjects, - DeletedObjects: au.GetDeletedObjects(), - OrphanObjects: orphanObjects, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + SeenImages: cmd.c.Images.SeenImages(false), } err = diffResultCb(diffResult) @@ -102,16 +95,9 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult return nil, err } return &result.CommandResult{ - RenderedObjects: cmd.c.LocalObjects(), - RemoteObjects: ru.GetFilteredRemoteObjects(nil), - AppliedObjects: au.GetAppliedObjects(), - AppliedHookObjects: au.GetAppliedHookObjects(), - NewObjects: au.GetNewObjectRefs(), - ChangedObjects: du.ChangedObjects, - DeletedObjects: au.GetDeletedObjects(), - OrphanObjects: orphanObjects, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + SeenImages: cmd.c.Images.SeenImages(false), }, nil } diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index c15f97864..66a5f5fdc 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -66,16 +66,9 @@ func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.Com return nil, err } return &result.CommandResult{ - RenderedObjects: cmd.c.LocalObjects(), - RemoteObjects: ru.GetFilteredRemoteObjects(nil), - AppliedObjects: au.GetAppliedObjects(), - AppliedHookObjects: au.GetAppliedHookObjects(), - NewObjects: au.GetNewObjectRefs(), - ChangedObjects: du.ChangedObjects, - DeletedObjects: au.GetDeletedObjects(), - OrphanObjects: orphanObjects, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + SeenImages: cmd.c.Images.SeenImages(false), }, nil } diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index d2c4de720..7d8e0d704 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -101,16 +101,9 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu } return &result.CommandResult{ - RenderedObjects: cmd.c.LocalObjects(), - RemoteObjects: ru.GetFilteredRemoteObjects(nil), - AppliedObjects: au.GetAppliedObjects(), - AppliedHookObjects: au.GetAppliedHookObjects(), - NewObjects: au.GetNewObjectRefs(), - ChangedObjects: du.ChangedObjects, - DeletedObjects: au.GetDeletedObjects(), - OrphanObjects: orphanObjects, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), + Errors: dew.GetErrorsList(), + Warnings: dew.GetWarningsList(), + SeenImages: cmd.c.Images.SeenImages(false), }, nil } diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index 5791db8d7..c60bc6bb4 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -53,11 +53,8 @@ func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb f } return &result.CommandResult{ - RenderedObjects: cmd.c.LocalObjects(), - RemoteObjects: ru.GetFilteredRemoteObjects(nil), - DeletedObjects: deleted, - Errors: dew.GetErrorsList(), - Warnings: dew.GetWarningsList(), + Objects: collectObjects(cmd.c, ru, nil, nil, nil, deleted), + Warnings: dew.GetWarningsList(), }, nil } diff --git a/pkg/deployment/commands/utils.go b/pkg/deployment/commands/utils.go new file mode 100644 index 000000000..074d3222d --- /dev/null +++ b/pkg/deployment/commands/utils.go @@ -0,0 +1,87 @@ +package commands + +import ( + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "sort" +) + +func collectObjects(c *deployment.DeploymentCollection, ru *utils.RemoteObjectUtils, au *utils.ApplyDeploymentsUtil, du *utils.DiffUtil, orphans []k8s.ObjectRef, deleted []k8s.ObjectRef) []result.ResultObject { + m := map[k8s.ObjectRef]*result.ResultObject{} + + getOrCreate := func(ref k8s.ObjectRef) *result.ResultObject { + x, ok := m[ref] + if !ok { + x = &result.ResultObject{} + x.Ref = ref + m[ref] = x + } + return x + } + + if c != nil { + for _, x := range c.LocalObjects() { + o := getOrCreate(x.GetK8sRef()) + o.Rendered = x + } + } + if ru != nil { + for _, x := range ru.GetFilteredRemoteObjects(nil) { + o := getOrCreate(x.GetK8sRef()) + o.Remote = x + } + } + + if au != nil { + for _, x := range au.GetAppliedObjects() { + o := getOrCreate(x.GetK8sRef()) + o.Applied = x + } + + for _, x := range au.GetAppliedHookObjects() { + o := getOrCreate(x.GetK8sRef()) + o.Hook = true + } + for _, x := range au.GetNewObjectRefs() { + o := getOrCreate(x) + o.New = true + } + for _, x := range au.GetDeletedObjects() { + o := getOrCreate(x) + o.Deleted = true + } + } + if du != nil { + for _, x := range du.ChangedObjects { + o := getOrCreate(x.Ref) + o.Changes = x.Changes + } + } + for _, x := range orphans { + o := getOrCreate(x) + o.Orphan = true + } + for _, x := range deleted { + o := getOrCreate(x) + o.Deleted = true + } + + for ref, o := range m { + if o.Rendered == nil && o.Applied == nil && !o.Deleted && !o.Orphan { + // exclude all objects that are clearly not under our control, but got retrieved anyway because some + // controller passed through the discriminator labels + delete(m, ref) + } + } + + ret := make([]result.ResultObject, 0, len(m)) + for _, o := range m { + ret = append(ret, *o) + } + sort.Slice(ret, func(i, j int) bool { + return ret[i].Ref.GroupVersionKind().String() < ret[j].Ref.GroupVersionKind().String() + }) + return ret +} diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index edffa3ef5..55ca28566 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -12,7 +12,7 @@ import ( "time" ) -type diffUtil struct { +type DiffUtil struct { dew *DeploymentErrorsAndWarnings deployments []*deployment.DeploymentItem appliedObjects map[k8s2.ObjectRef]*uo.UnstructuredObject @@ -27,8 +27,8 @@ type diffUtil struct { mutex sync.Mutex } -func NewDiffUtil(dew *DeploymentErrorsAndWarnings, deployments []*deployment.DeploymentItem, ru *RemoteObjectUtils, appliedObjects map[k8s2.ObjectRef]*uo.UnstructuredObject) *diffUtil { - return &diffUtil{ +func NewDiffUtil(dew *DeploymentErrorsAndWarnings, deployments []*deployment.DeploymentItem, ru *RemoteObjectUtils, appliedObjects map[k8s2.ObjectRef]*uo.UnstructuredObject) *DiffUtil { + return &DiffUtil{ dew: dew, deployments: deployments, ru: ru, @@ -36,7 +36,7 @@ func NewDiffUtil(dew *DeploymentErrorsAndWarnings, deployments []*deployment.Dep } } -func (u *diffUtil) Diff() { +func (u *DiffUtil) Diff() { var wg sync.WaitGroup u.calcRemoteObjectsForDiff() @@ -67,7 +67,7 @@ func (u *diffUtil) Diff() { }) } -func (u *diffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, ao *uo.UnstructuredObject, ro *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreForDiffItemConfig) { +func (u *DiffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, ao *uo.UnstructuredObject, ro *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreForDiffItemConfig) { if ao != nil && ro == nil { // new? return @@ -106,7 +106,7 @@ func (u *diffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, } } -func (u *diffUtil) calcRemoteObjectsForDiff() { +func (u *DiffUtil) calcRemoteObjectsForDiff() { u.remoteDiffObjects = make(map[k8s2.ObjectRef]*uo.UnstructuredObject) for _, o := range u.ru.remoteObjects { diffName := o.GetK8sAnnotation("kluctl.io/diff-name") @@ -126,7 +126,7 @@ func (u *diffUtil) calcRemoteObjectsForDiff() { } } -func (u *diffUtil) getRemoteObjectForDiff(localObject *uo.UnstructuredObject) (k8s2.ObjectRef, *uo.UnstructuredObject) { +func (u *DiffUtil) getRemoteObjectForDiff(localObject *uo.UnstructuredObject) (k8s2.ObjectRef, *uo.UnstructuredObject) { ref := localObject.GetK8sRef() diffName := localObject.GetK8sAnnotation("kluctl.io/diff-name") if diffName != nil { diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index cb319d09a..49bd2c7b7 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -19,7 +19,7 @@ type diffTestConfig struct { dew *DeploymentErrorsAndWarnings ru *RemoteObjectUtils - du *diffUtil + du *DiffUtil } func (dtc *diffTestConfig) newRemoteObjects(dew *DeploymentErrorsAndWarnings) *RemoteObjectUtils { diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index 96a8ea254..e2f299019 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -17,32 +17,20 @@ type Obfuscator struct { } func (o *Obfuscator) ObfuscateResult(r *result.CommandResult) error { - for _, c := range r.ChangedObjects { - err := o.ObfuscateChanges(c.Ref, c.Changes) + for _, x := range r.Objects { + err := o.ObfuscateObject(x.Rendered) if err != nil { return err } - } - for _, n := range r.RenderedObjects { - err := o.ObfuscateObject(n) - if err != nil { - return err - } - } - for _, n := range r.RemoteObjects { - err := o.ObfuscateObject(n) + err = o.ObfuscateObject(x.Remote) if err != nil { return err } - } - for _, n := range r.AppliedObjects { - err := o.ObfuscateObject(n) + err = o.ObfuscateObject(x.Applied) if err != nil { return err } - } - for _, n := range r.AppliedHookObjects { - err := o.ObfuscateObject(n) + err = o.ObfuscateChanges(x.Ref, x.Changes) if err != nil { return err } @@ -61,6 +49,9 @@ func (o *Obfuscator) ObfuscateChanges(ref k8s.ObjectRef, changes []result.Change } func (o *Obfuscator) ObfuscateObject(x *uo.UnstructuredObject) error { + if x == nil { + return nil + } ref := x.GetK8sRef() if ref.GroupKind() == secretGk { err := o.obfuscateSecret(x) diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 2596242b2..7990df8ee 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -61,22 +61,34 @@ type CommandInfo struct { ExcludeDeploymentDirs []string `json:"excludeDeploymentDirs,omitempty"` } +type BaseObject struct { + Ref k8s.ObjectRef `json:"ref"` + Changes []Change `json:"changes,omitempty"` + + New bool `json:"new,omitempty"` + Orphan bool `json:"orphan,omitempty"` + Deleted bool `json:"deleted,omitempty"` + Hook bool `json:"hook,omitempty"` +} + +type ResultObject struct { + BaseObject + + Rendered *uo.UnstructuredObject `json:"rendered,omitempty"` + Remote *uo.UnstructuredObject `json:"remote,omitempty"` + Applied *uo.UnstructuredObject `json:"applied,omitempty"` +} + type CommandResult struct { Command *CommandInfo `json:"command,omitempty"` Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` - RenderedObjects []*uo.UnstructuredObject `json:"renderedObjects,omitempty"` - RemoteObjects []*uo.UnstructuredObject `json:"remoteObjects,omitempty"` - AppliedObjects []*uo.UnstructuredObject `json:"appliedObjects,omitempty"` - AppliedHookObjects []*uo.UnstructuredObject `json:"appliedHookObjects,omitempty"` - - NewObjects []k8s.ObjectRef `json:"newObjects,omitempty"` - ChangedObjects []*ChangedObject `json:"changedObjects,omitempty"` - OrphanObjects []k8s.ObjectRef `json:"orphanObjects,omitempty"` - DeletedObjects []k8s.ObjectRef `json:"deletedObjects,omitempty"` - Errors []DeploymentError `json:"errors,omitempty"` - Warnings []DeploymentError `json:"warnings,omitempty"` - SeenImages []types.FixedImage `json:"seenImages,omitempty"` + Objects []ResultObject `json:"objects,omitempty"` + CompactedObjects CompactedObjects `json:"compactedObjects,omitempty"` + + Errors []DeploymentError `json:"errors,omitempty"` + Warnings []DeploymentError `json:"warnings,omitempty"` + SeenImages []types.FixedImage `json:"seenImages,omitempty"` } type ValidateResultEntry struct { diff --git a/pkg/types/result/compact.go b/pkg/types/result/compact.go new file mode 100644 index 000000000..6ae9f8dfa --- /dev/null +++ b/pkg/types/result/compact.go @@ -0,0 +1,155 @@ +package result + +import ( + "context" + "encoding/json" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "github.com/sergi/go-diff/diffmatchpatch" + "strings" + "sync" +) + +type CompactedObjects []ResultObject + +type CompactedObject struct { + BaseObject + + Rendered string `json:"rendered,omitempty"` + Remote string `json:"remote,omitempty"` + Applied string `json:"applied,omitempty"` +} + +func (l CompactedObjects) MarshalJSON() ([]byte, error) { + compactedList := make([]CompactedObject, len(l)) + + d := diffmatchpatch.New() + + createPatchOrFull := func(prevJson *string, o *uo.UnstructuredObject) string { + if o == nil { + return "" + } + j, err := yaml.WriteJsonString(o) + if err != nil { + // we are a point where this was parsed/written so many times, it really shouldn't error + panic(err) + } + if *prevJson == "" { + *prevJson = j + return "full: " + j + } + + diff := d.DiffMain(*prevJson, j, false) + delta := d.DiffToDelta(diff) + *prevJson = j + + if len(delta) < len(j) { + return "delta: " + delta + } else { + return "full: " + j + } + } + + var wg sync.WaitGroup + for i, o := range l { + i := i + o := o + wg.Add(1) + go func() { + defer wg.Done() + var prevJson string + compactedList[i].BaseObject = o.BaseObject + compactedList[i].Rendered = createPatchOrFull(&prevJson, o.Rendered) + compactedList[i].Remote = createPatchOrFull(&prevJson, o.Remote) + compactedList[i].Applied = createPatchOrFull(&prevJson, o.Applied) + }() + } + wg.Wait() + + return json.Marshal(compactedList) +} + +func (l *CompactedObjects) UnmarshalJSON(b []byte) error { + var compactedList []CompactedObject + err := json.Unmarshal(b, &compactedList) + if err != nil { + return err + } + + d := diffmatchpatch.New() + + patchAndUnmarshal := func(prevJson *string, s string) (*uo.UnstructuredObject, error) { + if s == "" { + return nil, nil + } + + if strings.HasPrefix(s, "full: ") { + full := s[6:] + *prevJson = full + return uo.FromString(full) + } else if strings.HasPrefix(s, "delta: ") { + if *prevJson == "" { + return nil, fmt.Errorf("prevJson empty") + } + delta := s[7:] + diff, err := d.DiffFromDelta(*prevJson, delta) + if err != nil { + return nil, err + } + patch := d.PatchMake(diff) + newJson, result := d.PatchApply(patch, *prevJson) + for _, b := range result { + if !b { + return nil, fmt.Errorf("patch did not fully apply") + } + } + o, err := uo.FromString(newJson) + if err != nil { + return nil, err + } + *prevJson = newJson + return o, nil + } else { + return nil, fmt.Errorf("unexpected object/delta") + } + } + + ret := make([]ResultObject, len(compactedList)) + + gh := utils.NewGoHelper(context.Background(), -1) + for i, o := range compactedList { + i := i + o := o + gh.RunE(func() error { + var err error + + o2 := ResultObject{} + o2.BaseObject = o.BaseObject + + prevJson := "" + o2.Rendered, err = patchAndUnmarshal(&prevJson, o.Rendered) + if err != nil { + return err + } + o2.Remote, err = patchAndUnmarshal(&prevJson, o.Remote) + if err != nil { + return err + } + o2.Applied, err = patchAndUnmarshal(&prevJson, o.Applied) + if err != nil { + return err + } + ret[i] = o2 + return nil + }) + } + gh.Wait() + err = gh.ErrorOrNil() + if err != nil { + return err + } + *l = ret + return nil +} From a6a9b0757aeae7fecf070544e0de7993441e7d52 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 6 Apr 2023 16:01:37 +0200 Subject: [PATCH 0921/2268] feat: Add ProjectSummary and GitInfo --- cmd/kluctl/commands/utils.go | 82 +++++++++++++++++++++++++++++- pkg/types/result/command_result.go | 19 ++++++- pkg/types/result/summary.go | 15 +++++- 3 files changed, 112 insertions(+), 4 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 91e801504..bb2d01751 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -3,9 +3,12 @@ package commands import ( "context" "fmt" + git2 "github.com/go-git/go-git/v5" + "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/result" "os" + "path/filepath" "strings" "time" @@ -210,7 +213,8 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm func addCommandInfo(r *result.CommandResult, startTime time.Time, command string, ctx *commandCtx, targetFlags *args.TargetFlags, imageFlags *args.ImageFlags, inclusionFlags *args.InclusionFlags, dryRunFlags *args.DryRunFlags, forceApplyFlags *args.ForceApplyFlags, replaceOnErrorFlags *args.ReplaceOnErrorFlags, abortOnErrorFlags *args.AbortOnErrorFlags, noWait bool) error { - r.Command = &result.CommandInfo{ + r.Command = result.CommandInfo{ + Id: uuid.New().String(), Initiator: result.CommandInititiator_CommandLine, StartTime: types.FromTime(startTime), EndTime: types.FromTime(time.Now()), @@ -250,6 +254,82 @@ func addCommandInfo(r *result.CommandResult, startTime time.Time, command string r.Command.AbortOnError = abortOnErrorFlags.AbortOnError } r.Deployment = &ctx.targetCtx.DeploymentProject.Config + + err := addGitInfo(r, ctx) + if err != nil { + return err + } + + return nil +} + +func addGitInfo(r *result.CommandResult, ctx *commandCtx) error { + if ctx.targetCtx.KluctlProject.LoadArgs.RepoRoot == "" { + return nil + } + + projectDirAbs, err := filepath.Abs(ctx.targetCtx.KluctlProject.LoadArgs.ProjectDir) + if err != nil { + return err + } + + subDir, err := filepath.Rel(ctx.targetCtx.KluctlProject.LoadArgs.RepoRoot, projectDirAbs) + if err != nil { + return err + } + if subDir == "." { + subDir = "" + } + + g, err := git2.PlainOpen(ctx.targetCtx.KluctlProject.LoadArgs.RepoRoot) + if err != nil { + return err + } + + w, err := g.Worktree() + if err != nil { + return err + } + + s, err := w.Status() + if err != nil { + return err + } + + head, err := g.Head() + if err != nil { + return err + } + + remotes, err := g.Remotes() + if err != nil { + return err + } + + var originUrl *git_url.GitUrl + for _, r := range remotes { + if r.Config().Name == "origin" { + originUrl, err = git_url.Parse(r.Config().URLs[0]) + if err != nil { + return err + } + } + } + + var normaliedUrl string + if originUrl != nil { + normaliedUrl = originUrl.NormalizedRepoKey() + } + + r.GitInfo = &result.GitInfo{ + Url: originUrl, + Ref: head.Name().String(), + SubDir: subDir, + Commit: head.Hash().String(), + Dirty: !s.IsClean(), + } + r.Project.NormalizedGitUrl = normaliedUrl + r.Project.SubDir = subDir return nil } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 7990df8ee..20efd9906 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -1,6 +1,7 @@ package result import ( + git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -38,8 +39,14 @@ const ( CommandInititiator_KluctlDeployment = "KluctlDeployment" ) +type ProjectKey struct { + NormalizedGitUrl string `json:"normalizedGitUrl,omitempty"` + SubDir string `json:"subDir,omitempty"` +} + type CommandInfo struct { Initiator CommandInitiator `json:"initiator" validate:"oneof=CommandLine KluctlDeployment"` + Id string `json:"id"` StartTime types.JsonTime `json:"startTime"` EndTime types.JsonTime `json:"endTime"` KluctlDeployment *KluctlDeploymentInfo `json:"kluctlDeployment,omitempty"` @@ -61,6 +68,14 @@ type CommandInfo struct { ExcludeDeploymentDirs []string `json:"excludeDeploymentDirs,omitempty"` } +type GitInfo struct { + Url *git_url.GitUrl `json:"url"` + Ref string `json:"ref"` + SubDir string `json:"subDir"` + Commit string `json:"commit"` + Dirty bool `json:"dirty"` +} + type BaseObject struct { Ref k8s.ObjectRef `json:"ref"` Changes []Change `json:"changes,omitempty"` @@ -80,7 +95,9 @@ type ResultObject struct { } type CommandResult struct { - Command *CommandInfo `json:"command,omitempty"` + Project ProjectKey `json:"project"` + Command CommandInfo `json:"command,omitempty"` + GitInfo *GitInfo `json:"gitInfo,omitempty"` Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` Objects []ResultObject `json:"objects,omitempty"` diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index d8d8adfb7..39e1e3765 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -1,8 +1,9 @@ package result type CommandResultSummary struct { - Id string `json:"id"` - Command *CommandInfo `json:"commandInfo"` + Project ProjectKey `json:"project"` + Command CommandInfo `json:"commandInfo"` + GitInfo *GitInfo `json:"gitInfo,omitempty"` RenderedObjects int `json:"renderedObjects"` RemoteObjects int `json:"remoteObjects"` @@ -18,3 +19,13 @@ type CommandResultSummary struct { TotalChanges int `json:"totalChanges"` } + +type ProjectSummary struct { + Project ProjectKey `json:"project"` + + LastValidateResult *ValidateResult `json:"lastValidateResult,omitempty"` + + LastDeployCommand *CommandResultSummary `json:"lastDeployCommand,omitempty"` + LastDeleteCommand *CommandResultSummary `json:"LastDeleteCommand,omitempty"` + LastPruneCommand *CommandResultSummary `json:"lastPruneCommand,omitempty"` +} From bebc2c6e92f350a05196c904af74038b8a028100 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 7 Apr 2023 23:21:08 +0200 Subject: [PATCH 0922/2268] refactor: Clearly separate between CommandResult and CompactedCommandResult --- cmd/kluctl/commands/command_result.go | 6 +----- pkg/types/result/command_result.go | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index c77356586..c9710d7a5 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -122,11 +122,7 @@ func prettyChanges(buf io.StringWriter, ref k8s.ObjectRef, changes []result.Chan } func formatCommandResultYaml(cr *result.CommandResult) (string, error) { - compactedCr := *cr - compactedCr.CompactedObjects = compactedCr.Objects - compactedCr.Objects = nil - - b, err := yaml.WriteYamlString(compactedCr) + b, err := yaml.WriteYamlString(cr.ToCompacted()) if err != nil { return "", err } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 20efd9906..0d3d319e0 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -100,14 +100,34 @@ type CommandResult struct { GitInfo *GitInfo `json:"gitInfo,omitempty"` Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` - Objects []ResultObject `json:"objects,omitempty"` - CompactedObjects CompactedObjects `json:"compactedObjects,omitempty"` + Objects []ResultObject `json:"objects,omitempty"` Errors []DeploymentError `json:"errors,omitempty"` Warnings []DeploymentError `json:"warnings,omitempty"` SeenImages []types.FixedImage `json:"seenImages,omitempty"` } +func (cr *CommandResult) ToCompacted() *CompactedCommandResult { + ret := &CompactedCommandResult{ + CommandResult: *cr, + } + ret.CompactedObjects = ret.Objects + ret.Objects = nil + return ret +} + +type CompactedCommandResult struct { + CommandResult + + CompactedObjects CompactedObjects `json:"compactedObjects,omitempty"` +} + +func (ccr *CompactedCommandResult) ToNonCompacted() *CommandResult { + ret := ccr.CommandResult + ret.Objects = ccr.CompactedObjects + return &ret +} + type ValidateResultEntry struct { Ref k8s.ObjectRef `json:"ref"` Annotation string `json:"annotation"` From eb19472960a788d0756b940f605d7e1ee1976be0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 7 Apr 2023 23:56:21 +0200 Subject: [PATCH 0923/2268] feat: Store id directly in result --- cmd/kluctl/commands/utils.go | 2 -- pkg/deployment/commands/delete.go | 2 ++ pkg/deployment/commands/deploy.go | 3 +++ pkg/deployment/commands/diff.go | 2 ++ pkg/deployment/commands/poke_images.go | 2 ++ pkg/deployment/commands/prune.go | 2 ++ pkg/deployment/commands/validate.go | 7 +++++-- pkg/types/result/command_result.go | 3 ++- pkg/types/result/summary.go | 1 + 9 files changed, 19 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index bb2d01751..c9fdcdc1b 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -4,7 +4,6 @@ import ( "context" "fmt" git2 "github.com/go-git/go-git/v5" - "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/result" "os" @@ -214,7 +213,6 @@ func addCommandInfo(r *result.CommandResult, startTime time.Time, command string imageFlags *args.ImageFlags, inclusionFlags *args.InclusionFlags, dryRunFlags *args.DryRunFlags, forceApplyFlags *args.ForceApplyFlags, replaceOnErrorFlags *args.ReplaceOnErrorFlags, abortOnErrorFlags *args.AbortOnErrorFlags, noWait bool) error { r.Command = result.CommandInfo{ - Id: uuid.New().String(), Initiator: result.CommandInititiator_CommandLine, StartTime: types.FromTime(startTime), EndTime: types.FromTime(time.Now()), diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index 970362d97..e87cffa50 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -55,6 +56,7 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb } return &result.CommandResult{ + Id: uuid.New().String(), Objects: collectObjects(cmd.c, ru, nil, nil, nil, deleted), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index 6b58c8762..417e2e27d 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -65,6 +66,7 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) diffResult := &result.CommandResult{ + Id: uuid.New().String(), Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), @@ -95,6 +97,7 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult return nil, err } return &result.CommandResult{ + Id: uuid.New().String(), Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index 66a5f5fdc..aa2f185c5 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -66,6 +67,7 @@ func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.Com return nil, err } return &result.CommandResult{ + Id: uuid.New().String(), Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 7d8e0d704..14fdb9e0c 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -101,6 +102,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu } return &result.CommandResult{ + Id: uuid.New().String(), Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index c60bc6bb4..e989b120c 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -53,6 +54,7 @@ func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb f } return &result.CommandResult{ + Id: uuid.New().String(), Objects: collectObjects(cmd.c, ru, nil, nil, nil, deleted), Warnings: dew.GetWarningsList(), }, nil diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 08cd62004..938e9d4f2 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -2,6 +2,7 @@ package commands import ( "context" + "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" @@ -29,8 +30,10 @@ func NewValidateCommand(ctx context.Context, discriminator string, c *deployment } func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.ValidateResult, error) { - var r result.ValidateResult - r.Ready = true + r := result.ValidateResult{ + Id: uuid.New().String(), + Ready: true, + } cmd.dew.Init() diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 0d3d319e0..3ae943626 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -46,7 +46,6 @@ type ProjectKey struct { type CommandInfo struct { Initiator CommandInitiator `json:"initiator" validate:"oneof=CommandLine KluctlDeployment"` - Id string `json:"id"` StartTime types.JsonTime `json:"startTime"` EndTime types.JsonTime `json:"endTime"` KluctlDeployment *KluctlDeploymentInfo `json:"kluctlDeployment,omitempty"` @@ -95,6 +94,7 @@ type ResultObject struct { } type CommandResult struct { + Id string `json:"id"` Project ProjectKey `json:"project"` Command CommandInfo `json:"command,omitempty"` GitInfo *GitInfo `json:"gitInfo,omitempty"` @@ -135,6 +135,7 @@ type ValidateResultEntry struct { } type ValidateResult struct { + Id string `json:"id"` Ready bool `json:"ready"` Warnings []DeploymentError `json:"warnings,omitempty"` Errors []DeploymentError `json:"errors,omitempty"` diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index 39e1e3765..01e4a1b65 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -1,6 +1,7 @@ package result type CommandResultSummary struct { + Id string `json:"id"` Project ProjectKey `json:"project"` Command CommandInfo `json:"commandInfo"` GitInfo *GitInfo `json:"gitInfo,omitempty"` From bbf51bfbc5494a9816434c640987128816f824fb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 8 Apr 2023 00:06:40 +0200 Subject: [PATCH 0924/2268] feat: Introduce BuildSummary function --- pkg/types/result/summary.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index 01e4a1b65..1fad0f1cb 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -30,3 +30,36 @@ type ProjectSummary struct { LastDeleteCommand *CommandResultSummary `json:"LastDeleteCommand,omitempty"` LastPruneCommand *CommandResultSummary `json:"lastPruneCommand,omitempty"` } + +func (cr *CommandResult) BuildSummary() *CommandResultSummary { + count := func(f func(o ResultObject) bool) int { + cnt := 0 + for _, o := range cr.Objects { + if f(o) { + cnt++ + } + } + return cnt + } + + ret := &CommandResultSummary{ + Id: cr.Id, + Project: cr.Project, + Command: cr.Command, + GitInfo: cr.GitInfo, + RenderedObjects: count(func(o ResultObject) bool { return o.Rendered != nil }), + RemoteObjects: count(func(o ResultObject) bool { return o.Remote != nil }), + AppliedObjects: count(func(o ResultObject) bool { return o.Applied != nil }), + AppliedHookObjects: count(func(o ResultObject) bool { return o.Hook }), + NewObjects: count(func(o ResultObject) bool { return o.New }), + ChangedObjects: count(func(o ResultObject) bool { return len(o.Changes) != 0 }), + OrphanObjects: count(func(o ResultObject) bool { return o.Orphan }), + DeletedObjects: count(func(o ResultObject) bool { return o.Deleted }), + Errors: len(cr.Errors), + Warnings: len(cr.Warnings), + } + for _, o := range cr.Objects { + ret.TotalChanges += len(o.Changes) + } + return ret +} From 4f058a5a5bf291a691dcf50f8e9207ade07826cb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 8 Apr 2023 23:47:44 +0200 Subject: [PATCH 0925/2268] feat: Implement result store --- pkg/k8s/client.go | 11 +- pkg/k8s/client_factory.go | 8 + pkg/k8s/fake_client_factory.go | 17 +- pkg/k8s/k8s_cluster.go | 36 ++++ pkg/results/result-store-secrets.go | 290 ++++++++++++++++++++++++++++ pkg/results/result-store.go | 27 +++ pkg/types/result/command_result.go | 12 ++ pkg/types/result/compact.go | 25 +++ pkg/utils/go_helper.go | 9 + pkg/utils/gzip.go | 39 ++++ 10 files changed, 468 insertions(+), 6 deletions(-) create mode 100644 pkg/results/result-store-secrets.go create mode 100644 pkg/results/result-store.go create mode 100644 pkg/utils/gzip.go diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index 829240fc5..2d8069549 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -6,6 +6,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/metadata" ) type k8sClients struct { @@ -16,8 +17,9 @@ type k8sClients struct { } type parallelClientEntry struct { - corev1 corev1.CoreV1Interface - dynamicClient dynamic.Interface + corev1 corev1.CoreV1Interface + dynamicClient dynamic.Interface + metadataClient metadata.Interface warnings []ApiWarning } @@ -59,6 +61,11 @@ func newK8sClients(ctx context.Context, clientFactory ClientFactory, count int) return nil, err } + p.metadataClient, err = clientFactory.MetadataClient(p) + if err != nil { + return nil, err + } + k.clientPool <- p } return k, nil diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go index f5e1f4b7a..2edff9b44 100644 --- a/pkg/k8s/client_factory.go +++ b/pkg/k8s/client_factory.go @@ -7,6 +7,7 @@ import ( "k8s.io/client-go/discovery/cached/disk" "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/metadata" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "net/http" @@ -24,6 +25,7 @@ type ClientFactory interface { DiscoveryClient() (discovery.DiscoveryInterface, error) CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1Interface, error) DynamicClient(wh rest.WarningHandler) (dynamic.Interface, error) + MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) } type realClientFactory struct { @@ -65,6 +67,12 @@ func (r *realClientFactory) DynamicClient(wh rest.WarningHandler) (dynamic.Inter return dynamic.NewForConfigAndClient(config, r.httpClient) } +func (r *realClientFactory) MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) { + config := rest.CopyConfig(r.config) + config.WarningHandler = wh + return metadata.NewForConfigAndClient(config, r.httpClient) +} + func (r *realClientFactory) CloseIdleConnections() { r.httpClient.CloseIdleConnections() } diff --git a/pkg/k8s/fake_client_factory.go b/pkg/k8s/fake_client_factory.go index 6ee62d1e4..2de043f74 100644 --- a/pkg/k8s/fake_client_factory.go +++ b/pkg/k8s/fake_client_factory.go @@ -12,6 +12,8 @@ import ( fake_dynamic "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes/fake" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/metadata" + fake_metadata "k8s.io/client-go/metadata/fake" "k8s.io/client-go/rest" "k8s.io/client-go/testing" "sigs.k8s.io/kustomize/kyaml/openapi" @@ -20,8 +22,9 @@ import ( ) type fakeClientFactory struct { - clientSet *fake.Clientset - dynamicClient *fake_dynamic.FakeDynamicClient + clientSet *fake.Clientset + dynamicClient *fake_dynamic.FakeDynamicClient + metadataClient *fake_metadata.FakeMetadataClient } func (f *fakeClientFactory) RESTConfig() *rest.Config { @@ -47,6 +50,10 @@ func (f *fakeClientFactory) DynamicClient(wh rest.WarningHandler) (dynamic.Inter return f.dynamicClient, nil } +func (f *fakeClientFactory) MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) { + return f.metadataClient, nil +} + func NewFakeClientFactory(objects ...runtime.Object) *fakeClientFactory { scheme := runtime.NewScheme() _ = v1.AddToScheme(scheme) @@ -56,10 +63,12 @@ func NewFakeClientFactory(objects ...runtime.Object) *fakeClientFactory { clientSet.Fake.Resources = ConvertSchemeToAPIResources(scheme) dynamicClient := fake_dynamic.NewSimpleDynamicClient(scheme, objects...) + metadataClient := fake_metadata.NewSimpleMetadataClient(scheme, objects...) return &fakeClientFactory{ - clientSet: clientSet, - dynamicClient: dynamicClient, + clientSet: clientSet, + dynamicClient: dynamicClient, + metadataClient: metadataClient, } } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index e76955bb1..eb7e931f5 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "k8s.io/client-go/metadata" "net/http" "strings" "sync" @@ -134,6 +135,41 @@ func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, return result, apiWarnings, err } +func (k *K8sCluster) ListMetadata(gvk schema.GroupVersionKind, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { + var result []*uo.UnstructuredObject + + gvr, err := k.Resources.GetGVRForGVK(gvk) + if err != nil { + return nil, nil, err + } + + apiWarnings, err := k.clients.withClientFromPool(func(p *parallelClientEntry) error { + var r metadata.ResourceInterface + if namespace == "" { + r = p.metadataClient.Resource(*gvr) + } else { + r = p.metadataClient.Resource(*gvr).Namespace(namespace) + } + o := v1.ListOptions{ + LabelSelector: k.buildLabelSelector(labels), + } + x, err := r.List(k.ctx, o) + if err != nil { + return err + } + for _, o := range x.Items { + u, err := uo.FromStruct(o) + if err != nil { + return err + } + u.SetK8sGVK(gvk) + result = append(result, u) + } + return nil + }) + return result, apiWarnings, err +} + func (k *K8sCluster) GetSingleObject(ref k8s.ObjectRef) (*uo.UnstructuredObject, []ApiWarning, error) { var result *uo.UnstructuredObject apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GroupVersionKind(), ref.Namespace, func(r dynamic.ResourceInterface) error { diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go new file mode 100644 index 000000000..c409bf552 --- /dev/null +++ b/pkg/results/result-store-secrets.go @@ -0,0 +1,290 @@ +package results + +import ( + "compress/gzip" + "context" + "encoding/base64" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/k8s" + k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "path" + "regexp" + "sort" + "strings" +) + +type ResultStoreSecrets struct { + k *k8s.K8sCluster + writeNamespace string +} + +func NewResultStoreSecrets(k *k8s.K8sCluster, writeNamespace string) (*ResultStoreSecrets, error) { + s := &ResultStoreSecrets{ + k: k, + writeNamespace: writeNamespace, + } + + if s.writeNamespace != "" { + _, _, err := k.GetSingleObject(k8s2.NewObjectRef("", "v1", "Namespace", s.writeNamespace, "")) + if err != nil && errors.IsNotFound(err) { + ns := uo.FromMap(map[string]interface{}{ + "apiVersion": "v1", + "kind": "Namespace", + "metadata": map[string]any{ + "name": s.writeNamespace, + }, + }) + _, _, err = k.ReadWrite().PatchObject(ns, k8s.PatchOptions{}) + if err != nil { + return nil, err + } + } else if err != nil { + return nil, err + } + } + + return s, nil +} + +var invalidChars = regexp.MustCompile(`[^a-zA-Z0-9-]`) + +func (s *ResultStoreSecrets) buildName(cr *result.CommandResult) string { + var name string + + if cr.Project.NormalizedGitUrl != "" { + s := path.Base(cr.Project.NormalizedGitUrl) + if s != "" { + name = s + "-" + } + } + + name = strings.ReplaceAll(name, "_", "-") + name = invalidChars.ReplaceAllString(name, "") + + nameWithId := name + cr.Id + if len(nameWithId) > 63 { + trimmedName := []byte(name[:63-len(cr.Id)]) + trimmedName[len(trimmedName)-1] = '-' + nameWithId = string(trimmedName) + cr.Id + } + + return nameWithId +} + +func (s *ResultStoreSecrets) WriteCommandResult(ctx context.Context, cr *result.CommandResult) error { + crJson, err := yaml.WriteJsonString(cr.ToReducedObjects()) + if err != nil { + return err + } + compressedCr, err := utils.CompressGzip([]byte(crJson), gzip.BestCompression) + if err != nil { + return err + } + + objectsJson, err := yaml.WriteJsonString(result.CompactedObjects(cr.Objects)) + if err != nil { + return err + } + compressedObjects, err := utils.CompressGzip([]byte(objectsJson), gzip.BestCompression) + if err != nil { + return err + } + + summary := cr.BuildSummary() + summaryJson, err := yaml.WriteJsonString(summary) + if err != nil { + return err + } + + secret := uo.FromMap(map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]any{ + "name": s.buildName(cr), + "namespace": s.writeNamespace, + "labels": map[string]any{ + "kluctl.io/result-id": cr.Id, + }, + "annotations": map[string]any{ + "kluctl.io/result-summary": summaryJson, + }, + }, + "data": map[string]any{ + "reducedResult": compressedCr, + "compactedObjects": compressedObjects, + }, + }) + if cr.Project.NormalizedGitUrl != "" { + secret.SetK8sAnnotation("kluctl.io/result-project-normalized-url", cr.Project.NormalizedGitUrl) + } + if cr.Project.SubDir != "" { + secret.SetK8sAnnotation("kluctl.io/result-project-subdir", cr.Project.SubDir) + } + + _, _, err = s.k.ReadWrite().PatchObject(secret, k8s.PatchOptions{}) + return err +} + +func (s *ResultStoreSecrets) ListProjects(ctx context.Context, options ListProjectsOptions) ([]result.ProjectSummary, error) { + summaries, err := s.ListCommandResultSummaries(ctx, ListCommandResultSummariesOptions{ + ProjectFilter: options.ProjectFilter, + }) + if err != nil { + return nil, err + } + + m := map[result.ProjectKey]result.ProjectSummary{} + for _, s := range summaries { + if _, ok := m[s.Project]; !ok { + m[s.Project] = result.ProjectSummary{Project: s.Project} + } + } + + ret := make([]result.ProjectSummary, 0, len(m)) + for _, p := range m { + for _, rs := range summaries { + switch rs.Command.Command { + case "deploy": + if p.LastDeployCommand == nil { + rs := rs + p.LastDeployCommand = &rs + } + case "delete": + if p.LastDeleteCommand == nil { + rs := rs + p.LastDeleteCommand = &rs + } + case "prune": + if p.LastPruneCommand == nil { + rs := rs + p.LastPruneCommand = &rs + } + } + } + ret = append(ret, p) + } + + return ret, nil +} + +func (s *ResultStoreSecrets) ListCommandResultSummaries(ctx context.Context, options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) { + l, _, err := s.k.ListMetadata(schema.GroupVersionKind{ + Version: "v1", + Kind: "Secret", + }, "", map[string]string{ + "kluctl.io/result-id": "", + }) + if err != nil { + return nil, err + } + + ret := make([]result.CommandResultSummary, 0, len(l)) + + for _, x := range l { + summaryJson := x.GetK8sAnnotation("kluctl.io/result-summary") + if summaryJson == nil || *summaryJson == "" { + continue + } + + var summary result.CommandResultSummary + err = yaml.ReadYamlString(*summaryJson, &summary) + if err != nil { + continue + } + + if options.ProjectFilter != nil { + if summary.Project != *options.ProjectFilter { + continue + } + } + + ret = append(ret, summary) + } + + sort.Slice(ret, func(i, j int) bool { + return ret[i].Command.StartTime >= ret[j].Command.StartTime + }) + + return ret, nil +} + +func (s *ResultStoreSecrets) GetCommandResult(ctx context.Context, options GetCommandResultOptions) (*result.CommandResult, error) { + l, _, err := s.k.ListObjects(schema.GroupVersionKind{ + Version: "v1", + Kind: "Secret", + }, "", map[string]string{ + "kluctl.io/result-id": options.Id, + }) + if err != nil { + return nil, err + } + if len(l) == 0 { + return nil, nil + } + + var crJson, objectsJson []byte + err = utils.RunParallelE(ctx, func() error { + s, ok, err := l[0].GetNestedString("data", "reducedResult") + if err != nil { + return err + } + if !ok { + return fmt.Errorf("reducedResult field not present for %s", options.Id) + } + crJson, err = base64.StdEncoding.DecodeString(s) + if err != nil { + return err + } + crJson, err = utils.UncompressGzip(crJson) + if err != nil { + return err + } + return nil + }, func() error { + if options.Reduced { + return nil + } + s, ok, err := l[0].GetNestedString("data", "compactedObjects") + if err != nil { + return err + } + if !ok { + return fmt.Errorf("compactedObjects field not present for %s", options.Id) + } + objectsJson, err = base64.StdEncoding.DecodeString(s) + if err != nil { + return err + } + + objectsJson, err = utils.UncompressGzip(objectsJson) + if err != nil { + return err + } + return err + }) + if err != nil { + return nil, err + } + + var cr result.CommandResult + err = yaml.ReadYamlBytes(crJson, &cr) + if err != nil { + return nil, err + } + if !options.Reduced { + var objects result.CompactedObjects + err = yaml.ReadYamlBytes(objectsJson, &objects) + if err != nil { + return nil, err + } + cr.Objects = objects + } + + return &cr, nil +} diff --git a/pkg/results/result-store.go b/pkg/results/result-store.go new file mode 100644 index 000000000..6d4b633ee --- /dev/null +++ b/pkg/results/result-store.go @@ -0,0 +1,27 @@ +package results + +import ( + "context" + "github.com/kluctl/kluctl/v2/pkg/types/result" +) + +type ListProjectsOptions struct { + ProjectFilter *result.ProjectKey `json:"projectFilter,omitempty"` +} + +type ListCommandResultSummariesOptions struct { + ProjectFilter *result.ProjectKey `json:"projectFilter,omitempty"` +} + +type GetCommandResultOptions struct { + Id string `json:"id"` + Reduced bool `json:"reduced,omitempty"` +} + +type ResultStore interface { + WriteCommandResult(ctx context.Context, cr *result.CommandResult) error + + ListProjects(ctx context.Context, options ListProjectsOptions) ([]result.ProjectSummary, error) + ListCommandResultSummaries(ctx context.Context, options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) + GetCommandResult(ctx context.Context, options GetCommandResultOptions) (*result.CommandResult, error) +} diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 3ae943626..f5b673122 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -116,6 +116,18 @@ func (cr *CommandResult) ToCompacted() *CompactedCommandResult { return ret } +func (cr *CommandResult) ToReducedObjects() *CommandResult { + ret := *cr + ret.Objects = make([]ResultObject, len(ret.Objects)) + for i, o := range cr.Objects { + ret.Objects[i] = o + ret.Objects[i].Rendered = buildReducedObject(o.Rendered) + ret.Objects[i].Remote = buildReducedObject(o.Remote) + ret.Objects[i].Applied = buildReducedObject(o.Applied) + } + return &ret +} + type CompactedCommandResult struct { CommandResult diff --git a/pkg/types/result/compact.go b/pkg/types/result/compact.go index 6ae9f8dfa..ae94847c3 100644 --- a/pkg/types/result/compact.go +++ b/pkg/types/result/compact.go @@ -153,3 +153,28 @@ func (l *CompactedObjects) UnmarshalJSON(b []byte) error { *l = ret return nil } + +func buildReducedObject(o *uo.UnstructuredObject) *uo.UnstructuredObject { + if o == nil { + return nil + } + ref := o.GetK8sRef() + m := map[string]any{ + "apiVersion": ref.GroupVersion().String(), + "kind": ref.Kind, + "metadata": map[string]any{ + "name": ref.Name, + }, + } + ret := uo.FromMap(m) + if ref.Namespace != "" { + ret.SetK8sNamespace(ref.Namespace) + } + if len(o.GetK8sLabels()) != 0 { + ret.SetK8sLabels(o.GetK8sLabels()) + } + if len(o.GetK8sAnnotations()) != 0 { + ret.SetK8sAnnotations(o.GetK8sAnnotations()) + } + return ret +} diff --git a/pkg/utils/go_helper.go b/pkg/utils/go_helper.go index b37a0a4de..eb10fc9fc 100644 --- a/pkg/utils/go_helper.go +++ b/pkg/utils/go_helper.go @@ -66,3 +66,12 @@ func (g *goHelper) Wait() { func (g *goHelper) ErrorOrNil() error { return g.errs.ErrorOrNil() } + +func RunParallelE(ctx context.Context, fs ...func() error) error { + g := NewGoHelper(ctx, 0) + for _, f := range fs { + g.RunE(f) + } + g.Wait() + return g.ErrorOrNil() +} diff --git a/pkg/utils/gzip.go b/pkg/utils/gzip.go new file mode 100644 index 000000000..7d331f5ed --- /dev/null +++ b/pkg/utils/gzip.go @@ -0,0 +1,39 @@ +package utils + +import ( + "bytes" + "compress/gzip" + "io" +) + +func CompressGzip(input []byte, level int) ([]byte, error) { + buf := bytes.NewBuffer(make([]byte, 0, len(input))) + w, err := gzip.NewWriterLevel(buf, level) + if err != nil { + return nil, err + } + _, err = w.Write(input) + if err != nil { + return nil, err + } + err = w.Close() + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func UncompressGzip(input []byte) ([]byte, error) { + r, err := gzip.NewReader(bytes.NewReader(input)) + if err != nil { + return nil, err + } + + buf := bytes.NewBuffer(nil) + _, err = io.Copy(buf, r) + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} From 4f07bd54fcae97de6bd5a3b9b11fcaf2d4a8384e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 10 Apr 2023 21:42:25 +0200 Subject: [PATCH 0926/2268] fix: Fix empty label selectors --- pkg/k8s/k8s_cluster.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index eb7e931f5..be727ffd8 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -111,7 +111,11 @@ func (k *K8sCluster) buildLabelSelector(labels map[string]string) string { if len(ret) != 0 { ret += "," } - ret += fmt.Sprintf("%s=%s", k, v) + if v == "" { + ret += k + } else { + ret += fmt.Sprintf("%s=%s", k, v) + } } return ret } From 5335ca579ba108ded3d59389f251f38bf1a4fe49 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 10 Apr 2023 23:04:44 +0200 Subject: [PATCH 0927/2268] feat: Automatically record results to the kluctl-results namespace --- cmd/kluctl/commands/cmd_delete.go | 3 ++- cmd/kluctl/commands/cmd_deploy.go | 13 +++++++------ cmd/kluctl/commands/cmd_diff.go | 2 +- cmd/kluctl/commands/cmd_poke_images.go | 3 ++- cmd/kluctl/commands/cmd_prune.go | 3 ++- cmd/kluctl/commands/command_result.go | 16 +++++++++++++--- cmd/kluctl/commands/utils.go | 24 ++++++++++++++++++------ 7 files changed, 45 insertions(+), 19 deletions(-) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 57f7c1a74..83ff899ad 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -43,6 +43,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, + needsResultStore: true, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { @@ -62,7 +63,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(ctx, cmd.OutputFormatFlags, result) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index e6e918d58..f53dd2dfb 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -46,6 +46,7 @@ func (cmd *deployCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, + needsResultStore: true, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { @@ -66,7 +67,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx, startTime time.Time) erro cmd2.NoWait = cmd.NoWait cb := func(diffResult *result.CommandResult) error { - return cmd.diffResultCb(cmdCtx.ctx, diffResult) + return cmd.diffResultCb(cmdCtx, diffResult) } if cmd.Yes || cmd.DryRun { cb = nil @@ -80,7 +81,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx, startTime time.Time) erro if err != nil { return err } - err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormatFlags, result) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) if err != nil { return err } @@ -90,11 +91,11 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx, startTime time.Time) erro return nil } -func (cmd *deployCmd) diffResultCb(ctx context.Context, diffResult *result.CommandResult) error { +func (cmd *deployCmd) diffResultCb(ctx *commandCtx, diffResult *result.CommandResult) error { flags := cmd.OutputFormatFlags flags.OutputFormat = nil // use default output format - err := outputCommandResult(ctx, flags, diffResult) + err := outputCommandResult(ctx, flags, diffResult, false) if err != nil { return err } @@ -102,11 +103,11 @@ func (cmd *deployCmd) diffResultCb(ctx context.Context, diffResult *result.Comma return nil } if len(diffResult.Errors) != 0 { - if !status.AskForConfirmation(ctx, "The diff resulted in errors, do you still want to proceed?") { + if !status.AskForConfirmation(ctx.ctx, "The diff resulted in errors, do you still want to proceed?") { return fmt.Errorf("aborted") } } else { - if !status.AskForConfirmation(ctx, "The diff succeeded, do you want to proceed?") { + if !status.AskForConfirmation(ctx.ctx, "The diff succeeded, do you want to proceed?") { return fmt.Errorf("aborted") } } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 640caecee..91be42dfa 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -56,7 +56,7 @@ func (cmd *diffCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(ctx, cmd.OutputFormatFlags, result) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, false) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index fccb20bc9..c08e60966 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -38,6 +38,7 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, + needsResultStore: true, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { @@ -57,7 +58,7 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(ctx, cmd.OutputFormatFlags, result) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index cd19d2e33..cd3b57697 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -40,6 +40,7 @@ func (cmd *pruneCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, + needsResultStore: true, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { @@ -59,7 +60,7 @@ func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx, startTime time.Time) error if err != nil { return err } - err = outputCommandResult(cmdCtx.ctx, cmd.OutputFormatFlags, result) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) if err != nil { return err } diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index c9710d7a5..167745f9c 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -220,8 +220,8 @@ func outputHelper(ctx context.Context, output []string, cb func(format string) ( return nil } -func outputCommandResult(ctx context.Context, flags args.OutputFormatFlags, cr *result.CommandResult) error { - status.Flush(ctx) +func outputCommandResult(ctx *commandCtx, flags args.OutputFormatFlags, cr *result.CommandResult, writeToResultStore bool) error { + status.Flush(ctx.ctx) if !flags.NoObfuscate { var obfuscator diff.Obfuscator @@ -231,7 +231,17 @@ func outputCommandResult(ctx context.Context, flags args.OutputFormatFlags, cr * } } - return outputHelper(ctx, flags.OutputFormat, func(format string) (string, error) { + if writeToResultStore && ctx.resultStore != nil { + s := status.Start(ctx.ctx, "Writing command result") + defer s.Failed() + err := ctx.resultStore.WriteCommandResult(ctx.ctx, cr) + if err != nil { + return err + } + s.Success() + } + + return outputHelper(ctx.ctx, flags.OutputFormat, func(format string) (string, error) { return formatCommandResult(cr, format, flags.ShortOutput) }) } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index c9fdcdc1b..adeb7d792 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -4,6 +4,7 @@ import ( "context" "fmt" git2 "github.com/go-git/go-git/v5" + "github.com/kluctl/kluctl/v2/pkg/results" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/result" "os" @@ -134,12 +135,14 @@ type projectTargetCommandArgs struct { forCompletion bool offlineKubernetes bool kubernetesVersion string + needsResultStore bool } type commandCtx struct { - ctx context.Context - targetCtx *kluctl_project.TargetContext - images *deployment.Images + ctx context.Context + targetCtx *kluctl_project.TargetContext + images *deployment.Images + resultStore results.ResultStore } func withProjectCommandContext(ctx context.Context, args projectTargetCommandArgs, cb func(cmdCtx *commandCtx) error) error { @@ -200,10 +203,19 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm } } + var resultStore results.ResultStore + if args.needsResultStore { + resultStore, err = results.NewResultStoreSecrets(targetCtx.SharedContext.K, "kluctl-results") + if err != nil { + return err + } + } + cmdCtx := &commandCtx{ - ctx: ctx, - targetCtx: targetCtx, - images: images, + ctx: ctx, + targetCtx: targetCtx, + images: images, + resultStore: resultStore, } return cb(cmdCtx) From 627e9f353c09bb95c7f861320258a18e1d3e7926 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 11 Apr 2023 11:22:39 +0200 Subject: [PATCH 0928/2268] chore: Run go mod tidy --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index baacdc9e8..42fbe969b 100644 --- a/go.mod +++ b/go.mod @@ -58,10 +58,12 @@ require ( github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7 github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.4 + github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 github.com/onsi/gomega v1.27.6 github.com/otiai10/copy v1.11.0 + github.com/sergi/go-diff v1.2.0 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.5 sigs.k8s.io/kustomize/api v0.13.2 @@ -149,7 +151,6 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -207,7 +208,6 @@ require ( github.com/rubenv/sql-migrate v1.3.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/skeema/knownhosts v1.1.0 // indirect github.com/spf13/afero v1.9.3 // indirect From b2df735d9a87a22b899790015c00cffd346243e6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 11 Apr 2023 11:50:47 +0200 Subject: [PATCH 0929/2268] fix: Still print result in case storing to the result store failed --- cmd/kluctl/commands/command_result.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index 167745f9c..482ff068e 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -231,19 +231,25 @@ func outputCommandResult(ctx *commandCtx, flags args.OutputFormatFlags, cr *resu } } + var resultStoreErr error if writeToResultStore && ctx.resultStore != nil { s := status.Start(ctx.ctx, "Writing command result") defer s.Failed() - err := ctx.resultStore.WriteCommandResult(ctx.ctx, cr) - if err != nil { - return err + resultStoreErr = ctx.resultStore.WriteCommandResult(ctx.ctx, cr) + if resultStoreErr != nil { + s.FailedWithMessage("Failed to write result to result store: %s", resultStoreErr.Error()) + } else { + s.Success() } - s.Success() } - return outputHelper(ctx.ctx, flags.OutputFormat, func(format string) (string, error) { + err := outputHelper(ctx.ctx, flags.OutputFormat, func(format string) (string, error) { return formatCommandResult(cr, format, flags.ShortOutput) }) + if err == nil && resultStoreErr != nil { + return resultStoreErr + } + return err } func outputValidateResult(ctx context.Context, output []string, vr *result.ValidateResult) error { From ecf2220bcac4722b2249c0596d58de0df542e622 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 11 Apr 2023 13:28:22 +0200 Subject: [PATCH 0930/2268] feat: Only write command result when --write-command-result is specified --- cmd/kluctl/args/project.go | 5 +++++ cmd/kluctl/commands/cmd_delete.go | 3 ++- cmd/kluctl/commands/cmd_deploy.go | 3 ++- cmd/kluctl/commands/cmd_diff.go | 4 +++- cmd/kluctl/commands/cmd_poke_images.go | 3 ++- cmd/kluctl/commands/cmd_prune.go | 3 ++- cmd/kluctl/commands/root.go | 1 + cmd/kluctl/commands/utils.go | 6 +++--- docs/reference/commands/common-arguments.md | 17 +++++++++++++++++ 9 files changed, 37 insertions(+), 8 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index fc896f813..272b51723 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -41,3 +41,8 @@ type TargetFlags struct { TargetNameOverride string `group:"project" short:"T" help:"Overrides the target name. If -t is used at the same time, then the target will be looked up based on -t and then renamed to the value of -T. If no target is specified via -t, then the no-name target is renamed to the value of -T."` Context string `group:"project" help:"Overrides the context name specified in the target. If the selected target does not specify a context or the no-name target is used, --context will override the currently active context."` } + +type CommandResultFlags struct { + WriteCommandResult bool `group:"results" help:"Enable experimental writing of command results into the cluster. Command results will be written into ConfigMaps in the kluctl-results namespace."` + CommandResultNamespace string `group:"results" help:"Override the namespace to be used when writing command results." default:"kluctl-results"` +} diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 83ff899ad..39ab7f6a5 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -21,6 +21,7 @@ type deleteCmd struct { args.DryRunFlags args.OutputFormatFlags args.RenderOutputDirFlags + args.CommandResultFlags Discriminator string `group:"misc" help:"Override the discriminator used to find objects for deletion."` } @@ -43,7 +44,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, - needsResultStore: true, + commandResultFlags: &cmd.CommandResultFlags, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index f53dd2dfb..c3ba95bd4 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -25,6 +25,7 @@ type deployCmd struct { args.HookFlags args.OutputFormatFlags args.RenderOutputDirFlags + args.CommandResultFlags NoWait bool `group:"misc" help:"Don't wait for objects readiness'"` } @@ -46,7 +47,7 @@ func (cmd *deployCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, - needsResultStore: true, + commandResultFlags: &cmd.CommandResultFlags, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 91be42dfa..fd6fb7689 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -20,6 +20,7 @@ type diffCmd struct { args.IgnoreFlags args.OutputFormatFlags args.RenderOutputDirFlags + args.CommandResultFlags } func (cmd *diffCmd) Help() string { @@ -38,6 +39,7 @@ func (cmd *diffCmd) Run(ctx context.Context) error { inclusionFlags: cmd.InclusionFlags, helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, + commandResultFlags: &cmd.CommandResultFlags, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { @@ -56,7 +58,7 @@ func (cmd *diffCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, false) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index c08e60966..888ecddde 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -20,6 +20,7 @@ type pokeImagesCmd struct { args.DryRunFlags args.OutputFormatFlags args.RenderOutputDirFlags + args.CommandResultFlags } func (cmd *pokeImagesCmd) Help() string { @@ -38,7 +39,7 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, - needsResultStore: true, + commandResultFlags: &cmd.CommandResultFlags, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index cd3b57697..567458b99 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -20,6 +20,7 @@ type pruneCmd struct { args.DryRunFlags args.OutputFormatFlags args.RenderOutputDirFlags + args.CommandResultFlags } func (cmd *pruneCmd) Help() string { @@ -40,7 +41,7 @@ func (cmd *pruneCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, - needsResultStore: true, + commandResultFlags: &cmd.CommandResultFlags, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 5a4b2589d..7ec3fb4b1 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -78,6 +78,7 @@ var flagGroups = []groupInfo{ {group: "inclusion", title: "Inclusion/Exclusion arguments:", description: "Control inclusion/exclusion."}, {group: "misc", title: "Misc arguments:", description: "Command specific arguments."}, {group: "flux", title: "Flux arguments:", description: "EXPERIMENTAL: Subcommands for interaction with flux-kluctl-controller"}, + {group: "results", title: "Command Results:", description: "Configure how command results are stored."}, } var origStderr = os.Stderr diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index adeb7d792..420624f47 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -130,12 +130,12 @@ type projectTargetCommandArgs struct { helmCredentials args.HelmCredentials dryRunArgs *args.DryRunFlags renderOutputDirFlags args.RenderOutputDirFlags + commandResultFlags *args.CommandResultFlags forSeal bool forCompletion bool offlineKubernetes bool kubernetesVersion string - needsResultStore bool } type commandCtx struct { @@ -204,8 +204,8 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm } var resultStore results.ResultStore - if args.needsResultStore { - resultStore, err = results.NewResultStoreSecrets(targetCtx.SharedContext.K, "kluctl-results") + if args.commandResultFlags != nil && args.commandResultFlags.WriteCommandResult { + resultStore, err = results.NewResultStoreSecrets(targetCtx.SharedContext.K, args.commandResultFlags.CommandResultNamespace) if err != nil { return err } diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index 2f2e85433..2263c15b2 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -124,3 +124,20 @@ Inclusion/Exclusion arguments: ``` + +## Command Results arguments + +These arguments control how command results are stored. + + +``` +Command Results: + Configure how command results are stored. + + --command-result-namespace string Override the namespace to be used when writing command results. (default + "kluctl-results") + --write-command-result Enable experimental writing of command results into the cluster. Command + results will be written into ConfigMaps in the kluctl-results namespace. + +``` + From cd3d4b786efb0af2d956ea2e73b938b23e2ba3b2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 11 Apr 2023 22:58:58 +0200 Subject: [PATCH 0931/2268] feat: Store cluster info in command result --- cmd/kluctl/commands/utils.go | 23 +++++++++++++++++++++++ pkg/types/result/command_result.go | 15 ++++++++++----- pkg/types/result/summary.go | 10 ++++++---- pkg/utils/uo/k8s_fields.go | 9 ++++++++- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 420624f47..0b783d808 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -6,6 +6,7 @@ import ( git2 "github.com/go-git/go-git/v5" "github.com/kluctl/kluctl/v2/pkg/results" "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" "os" "path/filepath" @@ -270,6 +271,11 @@ func addCommandInfo(r *result.CommandResult, startTime time.Time, command string return err } + err = addClusterInfo(r, ctx) + if err != nil { + return err + } + return nil } @@ -343,6 +349,23 @@ func addGitInfo(r *result.CommandResult, ctx *commandCtx) error { return nil } +func addClusterInfo(r *result.CommandResult, ctx *commandCtx) error { + kubeSystemNs, _, err := ctx.targetCtx.SharedContext.K.GetSingleObject( + k8s.NewObjectRef("", "v1", "Namespace", "kube-system", "")) + if err != nil { + return err + } + // we reuse the kube-system namespace uid as global cluster id + clusterId := kubeSystemNs.GetK8sUid() + if clusterId == "" { + return fmt.Errorf("kube-system namespace has no uid") + } + r.ClusterInfo = &result.ClusterInfo{ + ClusterId: clusterId, + } + return nil +} + func clientConfigGetter(forCompletion bool) func(context *string) (*rest.Config, *api.Config, error) { return func(context *string) (*rest.Config, *api.Config, error) { if forCompletion { diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index f5b673122..c7047fa63 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -75,6 +75,10 @@ type GitInfo struct { Dirty bool `json:"dirty"` } +type ClusterInfo struct { + ClusterId string `json:"clusterId"` +} + type BaseObject struct { Ref k8s.ObjectRef `json:"ref"` Changes []Change `json:"changes,omitempty"` @@ -94,11 +98,12 @@ type ResultObject struct { } type CommandResult struct { - Id string `json:"id"` - Project ProjectKey `json:"project"` - Command CommandInfo `json:"command,omitempty"` - GitInfo *GitInfo `json:"gitInfo,omitempty"` - Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` + Id string `json:"id"` + Project ProjectKey `json:"project"` + Command CommandInfo `json:"command,omitempty"` + GitInfo *GitInfo `json:"gitInfo,omitempty"` + ClusterInfo *ClusterInfo `json:"clusterInfo,omitempty"` + Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` Objects []ResultObject `json:"objects,omitempty"` diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index 1fad0f1cb..3dfa79a75 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -1,10 +1,11 @@ package result type CommandResultSummary struct { - Id string `json:"id"` - Project ProjectKey `json:"project"` - Command CommandInfo `json:"commandInfo"` - GitInfo *GitInfo `json:"gitInfo,omitempty"` + Id string `json:"id"` + Project ProjectKey `json:"project"` + Command CommandInfo `json:"commandInfo"` + GitInfo *GitInfo `json:"gitInfo,omitempty"` + ClusterInfo *ClusterInfo `json:"clusterInfo,omitempty"` RenderedObjects int `json:"renderedObjects"` RemoteObjects int `json:"remoteObjects"` @@ -47,6 +48,7 @@ func (cr *CommandResult) BuildSummary() *CommandResultSummary { Project: cr.Project, Command: cr.Command, GitInfo: cr.GitInfo, + ClusterInfo: cr.ClusterInfo, RenderedObjects: count(func(o ResultObject) bool { return o.Rendered != nil }), RemoteObjects: count(func(o ResultObject) bool { return o.Remote != nil }), AppliedObjects: count(func(o ResultObject) bool { return o.Applied != nil }), diff --git a/pkg/utils/uo/k8s_fields.go b/pkg/utils/uo/k8s_fields.go index 88cc49884..c45e23f57 100644 --- a/pkg/utils/uo/k8s_fields.go +++ b/pkg/utils/uo/k8s_fields.go @@ -79,7 +79,6 @@ func (uo *UnstructuredObject) SetK8sNamespace(namespace string) { panic(err) } } - } func (uo *UnstructuredObject) GetK8sRef() k8s.ObjectRef { @@ -93,6 +92,14 @@ func (uo *UnstructuredObject) GetK8sRef() k8s.ObjectRef { } } +func (uo *UnstructuredObject) GetK8sUid() string { + s, _, err := uo.GetNestedString("metadata", "uid") + if err != nil { + panic(err) + } + return s +} + func (uo *UnstructuredObject) GetK8sLabels() map[string]string { ret, ok, err := uo.GetNestedStringMapCopy("metadata", "labels") if err != nil { From 39bfb51a0539090c67d9ffa403044c4ab11d1b8a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 14 Apr 2023 21:46:27 +0200 Subject: [PATCH 0932/2268] refactor: Move ListProjects out of the ResultStore interface --- pkg/results/result-store-secrets.go | 42 ---------------------------- pkg/results/result-store.go | 43 ++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index c409bf552..bc4782a1c 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -131,48 +131,6 @@ func (s *ResultStoreSecrets) WriteCommandResult(ctx context.Context, cr *result. return err } -func (s *ResultStoreSecrets) ListProjects(ctx context.Context, options ListProjectsOptions) ([]result.ProjectSummary, error) { - summaries, err := s.ListCommandResultSummaries(ctx, ListCommandResultSummariesOptions{ - ProjectFilter: options.ProjectFilter, - }) - if err != nil { - return nil, err - } - - m := map[result.ProjectKey]result.ProjectSummary{} - for _, s := range summaries { - if _, ok := m[s.Project]; !ok { - m[s.Project] = result.ProjectSummary{Project: s.Project} - } - } - - ret := make([]result.ProjectSummary, 0, len(m)) - for _, p := range m { - for _, rs := range summaries { - switch rs.Command.Command { - case "deploy": - if p.LastDeployCommand == nil { - rs := rs - p.LastDeployCommand = &rs - } - case "delete": - if p.LastDeleteCommand == nil { - rs := rs - p.LastDeleteCommand = &rs - } - case "prune": - if p.LastPruneCommand == nil { - rs := rs - p.LastPruneCommand = &rs - } - } - } - ret = append(ret, p) - } - - return ret, nil -} - func (s *ResultStoreSecrets) ListCommandResultSummaries(ctx context.Context, options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) { l, _, err := s.k.ListMetadata(schema.GroupVersionKind{ Version: "v1", diff --git a/pkg/results/result-store.go b/pkg/results/result-store.go index 6d4b633ee..464dc6594 100644 --- a/pkg/results/result-store.go +++ b/pkg/results/result-store.go @@ -21,7 +21,48 @@ type GetCommandResultOptions struct { type ResultStore interface { WriteCommandResult(ctx context.Context, cr *result.CommandResult) error - ListProjects(ctx context.Context, options ListProjectsOptions) ([]result.ProjectSummary, error) ListCommandResultSummaries(ctx context.Context, options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) GetCommandResult(ctx context.Context, options GetCommandResultOptions) (*result.CommandResult, error) } + +func ListProjects(ctx context.Context, rs ResultStore, options ListProjectsOptions) ([]result.ProjectSummary, error) { + summaries, err := rs.ListCommandResultSummaries(ctx, ListCommandResultSummariesOptions{ + ProjectFilter: options.ProjectFilter, + }) + if err != nil { + return nil, err + } + + m := map[result.ProjectKey]result.ProjectSummary{} + for _, s := range summaries { + if _, ok := m[s.Project]; !ok { + m[s.Project] = result.ProjectSummary{Project: s.Project} + } + } + + ret := make([]result.ProjectSummary, 0, len(m)) + for _, p := range m { + for _, rs := range summaries { + switch rs.Command.Command { + case "deploy": + if p.LastDeployCommand == nil { + rs := rs + p.LastDeployCommand = &rs + } + case "delete": + if p.LastDeleteCommand == nil { + rs := rs + p.LastDeleteCommand = &rs + } + case "prune": + if p.LastPruneCommand == nil { + rs := rs + p.LastPruneCommand = &rs + } + } + } + ret = append(ret, p) + } + + return ret, nil +} From a6f27f9b63a940fa3ab52a26b9c6c29c99556d86 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 19 Apr 2023 09:38:10 +0200 Subject: [PATCH 0933/2268] fix: Fix error message when apply gets cancelled --- pkg/deployment/utils/apply_utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 750195855..d66580227 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -492,7 +492,7 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo a.HandleError(ref, err) return false case <-a.ctx.Done(): - err := fmt.Errorf("failed waiting for readiness of %s: %w", ref.String(), err) + err := fmt.Errorf("context cancelled while waiting for readiness of %s", ref.String()) status.Warning(a.ctx, "%s (%ds elapsed)", err.Error(), elapsed) a.HandleError(ref, err) return false From a040aee28fcaccbed5ce74683bb92aae26b65dac Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 19 Apr 2023 09:38:35 +0200 Subject: [PATCH 0934/2268] fix: Clone object before obfuscating to avoid manipulating reused objects --- pkg/diff/obfuscate.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index e2f299019..ea16017d9 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -18,15 +18,16 @@ type Obfuscator struct { func (o *Obfuscator) ObfuscateResult(r *result.CommandResult) error { for _, x := range r.Objects { - err := o.ObfuscateObject(x.Rendered) + var err error + x.Rendered, err = o.ObfuscateObject(x.Rendered) if err != nil { return err } - err = o.ObfuscateObject(x.Remote) + x.Remote, err = o.ObfuscateObject(x.Remote) if err != nil { return err } - err = o.ObfuscateObject(x.Applied) + x.Applied, err = o.ObfuscateObject(x.Applied) if err != nil { return err } @@ -48,18 +49,19 @@ func (o *Obfuscator) ObfuscateChanges(ref k8s.ObjectRef, changes []result.Change return nil } -func (o *Obfuscator) ObfuscateObject(x *uo.UnstructuredObject) error { +func (o *Obfuscator) ObfuscateObject(x *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { if x == nil { - return nil + return nil, nil } ref := x.GetK8sRef() if ref.GroupKind() == secretGk { - err := o.obfuscateSecret(x) + var err error + x, err = o.obfuscateSecret(x) if err != nil { - return err + return x, err } } - return nil + return x, nil } func (o *Obfuscator) obfuscateSecretChanges(ref k8s.ObjectRef, changes []result.Change) error { @@ -108,26 +110,30 @@ func (o *Obfuscator) obfuscateSecretChanges(ref k8s.ObjectRef, changes []result. return nil } -func (o *Obfuscator) obfuscateSecret(x *uo.UnstructuredObject) error { +func (o *Obfuscator) obfuscateSecret(x *uo.UnstructuredObject) (*uo.UnstructuredObject, error) { data, ok, _ := x.GetNestedField("data") if ok && data != nil { + x = x.Clone() + data, _, _ = x.GetNestedField("data") if m, ok := data.(map[string]any); ok { for k, _ := range m { m[k] = base64.StdEncoding.EncodeToString([]byte("*****")) } } else { - return fmt.Errorf("'data' is not a map of strings") + return x, fmt.Errorf("'data' is not a map of strings") } } data, ok, _ = x.GetNestedField("stringData") if ok && data != nil { + x = x.Clone() + data, _, _ = x.GetNestedField("stringData") if m, ok := data.(map[string]any); ok { for k, _ := range m { m[k] = "*****" } } else { - return fmt.Errorf("'data' is not a map of strings") + return x, fmt.Errorf("'data' is not a map of strings") } } - return nil + return x, nil } From a26c175f92c3d8b425243c727e9d1b1c4694ddde Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 20 Apr 2023 17:14:52 +0200 Subject: [PATCH 0935/2268] fix: Fix merging of validate results --- pkg/deployment/commands/validate.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 938e9d4f2..64223b2fe 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -30,7 +30,7 @@ func NewValidateCommand(ctx context.Context, discriminator string, c *deployment } func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.ValidateResult, error) { - r := result.ValidateResult{ + ret := result.ValidateResult{ Id: uuid.New().String(), Ready: true, } @@ -56,23 +56,23 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result remoteObject := cmd.ru.GetRemoteObject(ref) if remoteObject == nil { - r.Errors = append(r.Errors, result.DeploymentError{Ref: ref, Message: "object not found"}) + ret.Errors = append(ret.Errors, result.DeploymentError{Ref: ref, Message: "object not found"}) continue } r := validation.ValidateObject(k, remoteObject, true, false) if !r.Ready { - r.Ready = false + ret.Ready = false } - r.Errors = append(r.Errors, r.Errors...) - r.Warnings = append(r.Warnings, r.Warnings...) - r.Results = append(r.Results, r.Results...) + ret.Errors = append(ret.Errors, r.Errors...) + ret.Warnings = append(ret.Warnings, r.Warnings...) + ret.Results = append(ret.Results, r.Results...) } } - r.Warnings = append(r.Warnings, cmd.dew.GetWarningsList()...) - r.Errors = append(r.Errors, cmd.dew.GetErrorsList()...) + ret.Warnings = append(ret.Warnings, cmd.dew.GetWarningsList()...) + ret.Errors = append(ret.Errors, cmd.dew.GetErrorsList()...) - return &r, nil + return &ret, nil } func (cmd *ValidateCommand) ForgetRemoteObject(ref k8s2.ObjectRef) { From b1a0d8c78f60e66812a681e932d4170fdbf747e5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 24 Apr 2023 23:52:54 +0200 Subject: [PATCH 0936/2268] refactor: Allow to create DiffUtil and ApplyDeploymentsUtil without a deployment collection --- pkg/deployment/commands/deploy.go | 16 +++---- pkg/deployment/commands/diff.go | 8 ++-- pkg/deployment/commands/poke_images.go | 6 +-- pkg/deployment/utils/apply_utils.go | 28 ++++++------ pkg/deployment/utils/diff_utils.go | 61 +++++++++++++++---------- pkg/deployment/utils/diff_utils_test.go | 4 +- 6 files changed, 67 insertions(+), 56 deletions(-) diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index 417e2e27d..ec814632c 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -58,11 +58,11 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult } if diffResultCb != nil { - au := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, o) - au.ApplyDeployments() + au := utils2.NewApplyDeploymentsUtil(ctx, dew, ru, k, o) + au.ApplyDeployments(cmd.c.Deployments) - du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, au.GetAppliedObjectsMap()) - du.Diff() + du := utils2.NewDiffUtil(dew, ru, au.GetAppliedObjectsMap()) + du.DiffDeploymentItems(cmd.c.Deployments) orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) diffResult := &result.CommandResult{ @@ -86,11 +86,11 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult o.DryRun = k.DryRun o.AbortOnError = cmd.AbortOnError - au := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, o) - au.ApplyDeployments() + au := utils2.NewApplyDeploymentsUtil(ctx, dew, ru, k, o) + au.ApplyDeployments(cmd.c.Deployments) - du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, au.GetAppliedObjectsMap()) - du.Diff() + du := utils2.NewDiffUtil(dew, ru, au.GetAppliedObjectsMap()) + du.DiffDeploymentItems(cmd.c.Deployments) orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) if err != nil { diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index aa2f185c5..dd4a4ecd3 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -53,14 +53,14 @@ func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.Com AbortOnError: false, ReadinessTimeout: 0, } - au := utils.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, o) - au.ApplyDeployments() + au := utils.NewApplyDeploymentsUtil(ctx, dew, ru, k, o) + au.ApplyDeployments(cmd.c.Deployments) - du := utils.NewDiffUtil(dew, cmd.c.Deployments, ru, au.GetAppliedObjectsMap()) + du := utils.NewDiffUtil(dew, ru, au.GetAppliedObjectsMap()) du.IgnoreTags = cmd.IgnoreTags du.IgnoreLabels = cmd.IgnoreLabels du.IgnoreAnnotations = cmd.IgnoreAnnotations - du.Diff() + du.DiffDeploymentItems(cmd.c.Deployments) orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) if err != nil { diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 14fdb9e0c..7144f2c2f 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -72,7 +72,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu return o, nil } - au := utils2.NewApplyDeploymentsUtil(ctx, dew, cmd.c.Deployments, ru, k, &utils2.ApplyUtilOptions{}) + au := utils2.NewApplyDeploymentsUtil(ctx, dew, ru, k, &utils2.ApplyUtilOptions{}) for ref, containers := range containersAndImages { ref := ref @@ -93,8 +93,8 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu } wg.Wait() - du := utils2.NewDiffUtil(dew, cmd.c.Deployments, ru, au.GetAppliedObjectsMap()) - du.Diff() + du := utils2.NewDiffUtil(dew, ru, au.GetAppliedObjectsMap()) + du.DiffDeploymentItems(cmd.c.Deployments) orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) if err != nil { diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index d66580227..6a7847e51 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -61,11 +61,10 @@ type ApplyUtil struct { type ApplyDeploymentsUtil struct { ctx context.Context - dew *DeploymentErrorsAndWarnings - deployments []*deployment.DeploymentItem - ru *RemoteObjectUtils - k *k8s.K8sCluster - o *ApplyUtilOptions + dew *DeploymentErrorsAndWarnings + ru *RemoteObjectUtils + k *k8s.K8sCluster + o *ApplyUtilOptions abortSignal atomic.Value @@ -79,14 +78,13 @@ type ApplyDeploymentsUtil struct { results []*ApplyUtil } -func NewApplyDeploymentsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnings, deployments []*deployment.DeploymentItem, ru *RemoteObjectUtils, k *k8s.K8sCluster, o *ApplyUtilOptions) *ApplyDeploymentsUtil { +func NewApplyDeploymentsUtil(ctx context.Context, dew *DeploymentErrorsAndWarnings, ru *RemoteObjectUtils, k *k8s.K8sCluster, o *ApplyUtilOptions) *ApplyDeploymentsUtil { ret := &ApplyDeploymentsUtil{ - ctx: ctx, - dew: dew, - deployments: deployments, - ru: ru, - k: k, - o: o, + ctx: ctx, + dew: dew, + ru: ru, + k: k, + o: o, } ret.abortSignal.Store(false) return ret @@ -642,7 +640,7 @@ func (a *ApplyDeploymentsUtil) buildProgressName(d *deployment.DeploymentItem) * return nil } -func (a *ApplyDeploymentsUtil) ApplyDeployments() { +func (a *ApplyDeploymentsUtil) ApplyDeployments(deployments []*deployment.DeploymentItem) { s := status.Start(a.ctx, "Running server-side apply for all objects") defer s.Failed() @@ -650,7 +648,7 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { sem := semaphore.NewWeighted(8) maxNameLen := 0 - for _, d := range a.deployments { + for _, d := range deployments { name := a.buildProgressName(d) if name != nil { if len(*name) > maxNameLen { @@ -659,7 +657,7 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments() { } } - for _, d_ := range a.deployments { + for _, d_ := range deployments { d := d_ if a.abortSignal.Load().(bool) { break diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index 55ca28566..8e0b00bc2 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -14,7 +14,6 @@ import ( type DiffUtil struct { dew *DeploymentErrorsAndWarnings - deployments []*deployment.DeploymentItem appliedObjects map[k8s2.ObjectRef]*uo.UnstructuredObject ru *RemoteObjectUtils @@ -27,46 +26,60 @@ type DiffUtil struct { mutex sync.Mutex } -func NewDiffUtil(dew *DeploymentErrorsAndWarnings, deployments []*deployment.DeploymentItem, ru *RemoteObjectUtils, appliedObjects map[k8s2.ObjectRef]*uo.UnstructuredObject) *DiffUtil { - return &DiffUtil{ +func NewDiffUtil(dew *DeploymentErrorsAndWarnings, ru *RemoteObjectUtils, appliedObjects map[k8s2.ObjectRef]*uo.UnstructuredObject) *DiffUtil { + u := &DiffUtil{ dew: dew, - deployments: deployments, ru: ru, appliedObjects: appliedObjects, } + u.calcRemoteObjectsForDiff() + return u } -func (u *DiffUtil) Diff() { +func (u *DiffUtil) DiffDeploymentItems(deployments []*deployment.DeploymentItem) { var wg sync.WaitGroup - u.calcRemoteObjectsForDiff() - - for _, d := range u.deployments { + for _, d := range deployments { ignoreForDiffs := d.Project.GetIgnoreForDiffs(u.IgnoreTags, u.IgnoreLabels, u.IgnoreAnnotations) - for _, o := range d.Objects { - o := o - ref := o.GetK8sRef() - ao, ok := u.appliedObjects[ref] - if !ok { - // if we can't even find it in appliedObjects, it probably ran into an error - continue - } - diffRef, ro := u.getRemoteObjectForDiff(o) - - wg.Add(1) - go func() { - defer wg.Done() - u.diffObject(o, diffRef, ao, ro, ignoreForDiffs) - }() - } + u.diffObjects(d.Objects, ignoreForDiffs, &wg) } wg.Wait() + u.sortChanges() +} + +func (u *DiffUtil) DiffObjects(objects []*uo.UnstructuredObject) { + var wg sync.WaitGroup + u.diffObjects(objects, nil, &wg) + wg.Wait() + u.sortChanges() +} + +func (u *DiffUtil) sortChanges() { sort.Slice(u.ChangedObjects, func(i, j int) bool { return u.ChangedObjects[i].Ref.String() < u.ChangedObjects[j].Ref.String() }) } +func (u *DiffUtil) diffObjects(objects []*uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreForDiffItemConfig, wg *sync.WaitGroup) { + for _, o := range objects { + o := o + ref := o.GetK8sRef() + ao, ok := u.appliedObjects[ref] + if !ok { + // if we can't even find it in appliedObjects, it probably ran into an error + continue + } + diffRef, ro := u.getRemoteObjectForDiff(o) + + wg.Add(1) + go func() { + defer wg.Done() + u.diffObject(o, diffRef, ao, ro, ignoreForDiffs) + }() + } +} + func (u *DiffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, ao *uo.UnstructuredObject, ro *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreForDiffItemConfig) { if ao != nil && ro == nil { // new? diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index 49bd2c7b7..430d39427 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -126,8 +126,8 @@ func TestDiff(t *testing.T) { t.Run(test.name, func(t *testing.T) { test.dew = NewDeploymentErrorsAndWarnings() test.ru = test.newRemoteObjects(test.dew) - test.du = NewDiffUtil(test.dew, test.newDeploymentItems(), test.ru, test.appliedObjectsMap()) - test.du.Diff() + test.du = NewDiffUtil(test.dew, test.ru, test.appliedObjectsMap()) + test.du.DiffDeploymentItems(test.newDeploymentItems()) test.a(t, test) }) } From 9b850a6b6d955153a93e756385310b538e7f42f8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Apr 2023 00:02:43 +0200 Subject: [PATCH 0937/2268] refactor: Make ExistingXXXTypes public --- cmd/kluctl/args/cobra_types.go | 30 +++++++++++++++--------------- cmd/kluctl/args/images.go | 2 +- cmd/kluctl/args/project.go | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cmd/kluctl/args/cobra_types.go b/cmd/kluctl/args/cobra_types.go index c6907ea51..ade735c6e 100644 --- a/cmd/kluctl/args/cobra_types.go +++ b/cmd/kluctl/args/cobra_types.go @@ -5,27 +5,27 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" ) -type existingPathType string +type ExistingPathType string -func (s *existingPathType) Set(val string) error { +func (s *ExistingPathType) Set(val string) error { if val != "-" { val = utils.ExpandPath(val) } if !utils.Exists(val) { return fmt.Errorf("%s does not exist", val) } - *s = existingPathType(val) + *s = ExistingPathType(val) return nil } -func (s *existingPathType) Type() string { +func (s *ExistingPathType) Type() string { return "existingpath" } -func (s *existingPathType) String() string { return string(*s) } +func (s *ExistingPathType) String() string { return string(*s) } -type existingFileType string +type ExistingFileType string -func (s *existingFileType) Set(val string) error { +func (s *ExistingFileType) Set(val string) error { if val != "-" { val = utils.ExpandPath(val) } @@ -35,18 +35,18 @@ func (s *existingFileType) Set(val string) error { if utils.IsDirectory(val) { return fmt.Errorf("%s exists but is a directory", val) } - *s = existingFileType(val) + *s = ExistingFileType(val) return nil } -func (s *existingFileType) Type() string { +func (s *ExistingFileType) Type() string { return "existingfile" } -func (s *existingFileType) String() string { return string(*s) } +func (s *ExistingFileType) String() string { return string(*s) } -type existingDirType string +type ExistingDirType string -func (s *existingDirType) Set(val string) error { +func (s *ExistingDirType) Set(val string) error { if val != "-" { val = utils.ExpandPath(val) } @@ -56,11 +56,11 @@ func (s *existingDirType) Set(val string) error { if !utils.IsDirectory(val) { return fmt.Errorf("%s exists but is not a directory", val) } - *s = existingDirType(val) + *s = ExistingDirType(val) return nil } -func (s *existingDirType) Type() string { +func (s *ExistingDirType) Type() string { return "existingdir" } -func (s *existingDirType) String() string { return string(*s) } +func (s *ExistingDirType) String() string { return string(*s) } diff --git a/cmd/kluctl/args/images.go b/cmd/kluctl/args/images.go index 1c6fe4b79..6c19f0f8d 100644 --- a/cmd/kluctl/args/images.go +++ b/cmd/kluctl/args/images.go @@ -9,7 +9,7 @@ import ( type ImageFlags struct { FixedImage []string `group:"images" short:"F" help:"Pin an image to a given version. Expects '--fixed-image=image<:namespace:deployment:container>=result'"` - FixedImagesFile existingFileType `group:"images" help:"Use .yaml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format" exts:"yml,yaml"` + FixedImagesFile ExistingFileType `group:"images" help:"Use .yaml file to pin image versions. See output of list-images sub-command or read the documentation for details about the output format" exts:"yml,yaml"` } func (args *ImageFlags) LoadFixedImagesFromArgs() ([]types.FixedImage, error) { diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 272b51723..e3ed5ab0e 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -6,7 +6,7 @@ import ( ) type ProjectDir struct { - ProjectDir existingDirType `group:"project" help:"Specify the project directory. Defaults to the current working directory."` + ProjectDir ExistingDirType `group:"project" help:"Specify the project directory. Defaults to the current working directory."` } func (a ProjectDir) GetProjectDir() (string, error) { @@ -23,7 +23,7 @@ func (a ProjectDir) GetProjectDir() (string, error) { type ProjectFlags struct { ProjectDir - ProjectConfig existingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` + ProjectConfig ExistingFileType `group:"project" short:"c" help:"Location of the .kluctl.yaml config file. Defaults to $PROJECT/.kluctl.yaml" exts:"yml,yaml"` Timeout time.Duration `group:"project" help:"Specify timeout for all operations, including loading of the project, all external api calls and waiting for readiness." default:"10m"` GitCacheUpdateInterval time.Duration `group:"project" help:"Specify the time to wait between git cache updates. Defaults to not wait at all and always updating caches."` From f47553fe6bf7e7f06ca29f2f525437566e1e3d8a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Apr 2023 01:25:07 +0200 Subject: [PATCH 0938/2268] feat: Implement drift detection via validate command --- cmd/kluctl/commands/cmd_validate.go | 114 ++++++++++------ cmd/kluctl/commands/command_result.go | 17 +++ e2e/utils.go | 8 +- e2e/validate_test.go | 183 ++++++++++++++++++++++++++ pkg/deployment/commands/validate.go | 88 +++++++++---- pkg/deployment/utils/diff_utils.go | 11 +- pkg/types/result/command_result.go | 6 +- pkg/yaml/yaml.go | 8 ++ 8 files changed, 369 insertions(+), 66 deletions(-) create mode 100644 e2e/validate_test.go diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index 532f52e28..03471c507 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -5,6 +5,9 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + k8s2 "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/yaml" "time" ) @@ -17,6 +20,8 @@ type validateCmd struct { args.OutputFlags args.RenderOutputDirFlags + CommandResult args.ExistingFileType `group:"misc" help:"Specify a command result to use instead of loading a project. This will also perform drift detection."` + Wait time.Duration `group:"misc" help:"Wait for the given amount of time until the deployment validates"` Sleep time.Duration `group:"misc" help:"Sleep duration between validation attempts" default:"5s"` WarningsAsErrors bool `group:"misc" help:"Consider warnings as failures"` @@ -37,43 +42,76 @@ func (cmd *validateCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, } - return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - startTime := time.Now() - cmd2 := commands.NewValidateCommand(cmdCtx.ctx, cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) - for true { - result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) - if err != nil { - return err - } - failed := len(result.Errors) != 0 || (cmd.WarningsAsErrors && len(result.Warnings) != 0) - - err = outputValidateResult(ctx, cmd.Output, result) - if err != nil { - return err - } - - if !failed { - _, _ = getStderr(ctx).WriteString("Validation succeeded\n") - return nil - } - - if cmd.Wait <= 0 || time.Now().Sub(startTime) > cmd.Wait { - return fmt.Errorf("Validation failed") - } - - time.Sleep(cmd.Sleep) - - // Need to force re-requesting these objects - for _, e := range result.Results { - cmd2.ForgetRemoteObject(e.Ref) - } - for _, e := range result.Warnings { - cmd2.ForgetRemoteObject(e.Ref) - } - for _, e := range result.Errors { - cmd2.ForgetRemoteObject(e.Ref) - } + + if cmd.CommandResult.String() != "" { + var ccr result.CompactedCommandResult + err := yaml.ReadYamlFile(cmd.CommandResult.String(), &ccr) + if err != nil { + return err + } + commandResult := ccr.ToNonCompacted() + + var k8sContext *string + if cmd.Context != "" { + k8sContext = &cmd.Context + } else if commandResult.Command.Target != nil { + k8sContext = commandResult.Command.Target.Context + } + clientFactory, err := k8s2.NewClientFactoryFromDefaultConfig(ctx, k8sContext) + if err != nil { + return err + } + k, err := k8s2.NewK8sCluster(ctx, clientFactory, true) + if err != nil { + return err + } + + cmd2 := commands.NewValidateCommand(ctx, "", nil, commandResult) + return cmd.doValidate(ctx, k, cmd2) + + } else { + return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { + cmd2 := commands.NewValidateCommand(cmdCtx.ctx, cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection, nil) + return cmd.doValidate(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, cmd2) + }) + } +} + +func (cmd *validateCmd) doValidate(ctx context.Context, k *k8s2.K8sCluster, cmd2 *commands.ValidateCommand) error { + startTime := time.Now() + for true { + result, err := cmd2.Run(ctx, k) + if err != nil { + return err } - return nil - }) + failed := len(result.Errors) != 0 || (cmd.WarningsAsErrors && len(result.Warnings) != 0) + + err = outputValidateResult(ctx, cmd.Output, result) + if err != nil { + return err + } + + if !failed { + _, _ = getStderr(ctx).WriteString("Validation succeeded\n") + return nil + } + + if cmd.Wait <= 0 || time.Now().Sub(startTime) > cmd.Wait { + return fmt.Errorf("Validation failed") + } + + time.Sleep(cmd.Sleep) + + // Need to force re-requesting these objects + for _, e := range result.Results { + cmd2.ForgetRemoteObject(e.Ref) + } + for _, e := range result.Warnings { + cmd2.ForgetRemoteObject(e.Ref) + } + for _, e := range result.Errors { + cmd2.ForgetRemoteObject(e.Ref) + } + } + return nil } diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index 482ff068e..cedfb9319 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -174,6 +174,23 @@ func formatValidateResultText(vr *result.ValidateResult) string { buf.WriteString("Results:\n") prettyValidationResults(buf, vr.Results) } + + if len(vr.Drift) != 0 { + if buf.Len() != 0 { + buf.WriteString("\n") + } + buf.WriteString("Drift:\n") + for i, o := range vr.Drift { + if len(o.Changes) == 0 { + continue + } + if i != 0 { + buf.WriteString("\n") + } + prettyChanges(buf, o.Ref, o.Changes) + } + } + return buf.String() } diff --git a/e2e/utils.go b/e2e/utils.go index 270c273fa..9909bc07a 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -25,14 +25,18 @@ func createNamespace(t *testing.T, k *test_utils.EnvTestCluster, namespace strin } } -func assertConfigMapExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, name string) *uo.UnstructuredObject { - x, err := k.Get(v1.SchemeGroupVersion.WithResource("configmaps"), namespace, name) +func assertObjectExists(t *testing.T, k *test_utils.EnvTestCluster, gvr schema.GroupVersionResource, namespace string, name string) *uo.UnstructuredObject { + x, err := k.Get(gvr, namespace, name) if err != nil { t.Fatalf("unexpected error '%v' while getting ConfigMap %s/%s", err, namespace, name) } return x } +func assertConfigMapExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, name string) *uo.UnstructuredObject { + return assertObjectExists(t, k, v1.SchemeGroupVersion.WithResource("configmaps"), namespace, name) +} + func assertConfigMapNotExists(t *testing.T, k *test_utils.EnvTestCluster, namespace string, name string) { _, err := k.Get(v1.SchemeGroupVersion.WithResource("configmaps"), namespace, name) if err == nil { diff --git a/e2e/validate_test.go b/e2e/validate_test.go new file mode 100644 index 000000000..9fdb6cf5c --- /dev/null +++ b/e2e/validate_test.go @@ -0,0 +1,183 @@ +package e2e + +import ( + "context" + "fmt" + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "github.com/stretchr/testify/assert" + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "path/filepath" + "testing" +) + +func buildDeployment(name string, namespace string, ready bool) *uo.UnstructuredObject { + deployment := uo.FromStringMust(fmt.Sprintf(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %s + namespace: %s + labels: + app: nginx +spec: + replicas: 1 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 +`, name, namespace)) + if ready { + deployment.Merge(uo.FromStringMust(` +status: + availableReplicas: 1 + conditions: + - lastTransitionTime: "2023-03-29T19:23:12Z" + lastUpdateTime: "2023-03-29T19:23:12Z" + message: Deployment has minimum availability. + reason: MinimumReplicasAvailable + status: "True" + type: Available + - lastTransitionTime: "2023-03-29T19:22:30Z" + lastUpdateTime: "2023-03-29T19:23:12Z" + message: ReplicaSet "argocd-redis-8f7689686" has successfully progressed. + reason: NewReplicaSetAvailable + status: "True" + type: Progressing + observedGeneration: 1 + readyReplicas: 1 + replicas: 1 +`)) + } + return deployment +} + +func prepareValidateTest(t *testing.T, k *test_utils.EnvTestCluster) *test_utils.TestProject { + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + p.AddKustomizeDeployment("d1", []test_utils.KustomizeResource{ + {fmt.Sprintf("configmap-%s.yml", "d1"), "", buildDeployment("d1", p.TestSlug(), false)}, + }, nil) + + return p +} + +func assertValidate(t *testing.T, p *test_utils.TestProject, commandResult *string, succeed bool) (string, string) { + args := []string{"validate"} + if commandResult != nil { + args = append(args, "--command-result", *commandResult) + } else { + args = append(args, "-t", "test") + } + stdout, stderr, err := p.Kluctl(args...) + + if succeed { + assert.NoError(t, err) + assert.NotContains(t, stdout, fmt.Sprintf("%s/Deployment/d1: readyReplicas field not in status or empty", p.TestSlug())) + assert.NotContains(t, stderr, "Validation failed") + } else { + assert.Error(t, err) + assert.Contains(t, stdout, fmt.Sprintf("%s/Deployment/d1: readyReplicas field not in status or empty", p.TestSlug())) + assert.Contains(t, stderr, "Validation failed") + } + + return stdout, stderr +} + +func TestValidate(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := prepareValidateTest(t, k) + + p.KluctlMust("deploy", "--yes", "-t", "test") + assertObjectExists(t, k, appsv1.SchemeGroupVersion.WithResource("deployments"), p.TestSlug(), "d1") + + assertValidate(t, p, nil, false) + + readyDeployment := buildDeployment("d1", p.TestSlug(), true) + + _, err := k.DynamicClient.Resource(appsv1.SchemeGroupVersion.WithResource("deployments")).Namespace(p.TestSlug()). + Patch(context.Background(), "d1", types.ApplyPatchType, []byte(yaml.WriteJsonStringMust(readyDeployment)), metav1.PatchOptions{ + FieldManager: "test", + }, "status") + assert.NoError(t, err) + + assertValidate(t, p, nil, true) +} + +func TestValidateCommandResult(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := prepareValidateTest(t, k) + + resultPath := filepath.Join(t.TempDir(), "result.yaml") + + p.KluctlMust("deploy", "--yes", "-t", "test", "-oyaml="+resultPath) + assertObjectExists(t, k, appsv1.SchemeGroupVersion.WithResource("deployments"), p.TestSlug(), "d1") + + assertValidate(t, p, &resultPath, false) + + readyDeployment := buildDeployment("d1", p.TestSlug(), true) + + _, err := k.DynamicClient.Resource(appsv1.SchemeGroupVersion.WithResource("deployments")).Namespace(p.TestSlug()). + Patch(context.Background(), "d1", types.ApplyPatchType, []byte(yaml.WriteJsonStringMust(readyDeployment)), metav1.PatchOptions{ + FieldManager: "test", + }, "status") + assert.NoError(t, err) + + assertValidate(t, p, &resultPath, true) +} + +func TestValidateDrift(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := prepareValidateTest(t, k) + + resultPath := filepath.Join(t.TempDir(), "result.yaml") + + p.KluctlMust("deploy", "--yes", "-t", "test", "-oyaml="+resultPath) + assertObjectExists(t, k, appsv1.SchemeGroupVersion.WithResource("deployments"), p.TestSlug(), "d1") + + stdout, _ := assertValidate(t, p, &resultPath, false) + assert.NotContains(t, stdout, "Drift:") + + changedDeployment := buildDeployment("d1", p.TestSlug(), false) + changedDeployment.Merge(uo.FromStringMust(` +spec: + replicas: 2 +`)) + + b := true + _, err := k.DynamicClient.Resource(appsv1.SchemeGroupVersion.WithResource("deployments")).Namespace(p.TestSlug()). + Patch(context.Background(), "d1", types.ApplyPatchType, []byte(yaml.WriteJsonStringMust(changedDeployment)), metav1.PatchOptions{ + FieldManager: "test", + Force: &b, + }) + assert.NoError(t, err) + + stdout, _ = assertValidate(t, p, &resultPath, false) + assert.Contains(t, stdout, "Drift:") + assert.Contains(t, stdout, "spec.replicas") +} diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 64223b2fe..e3000a09d 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -2,26 +2,30 @@ package commands import ( "context" + "fmt" "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" ) type ValidateCommand struct { c *deployment.DeploymentCollection + r *result.CommandResult discriminator string dew *utils2.DeploymentErrorsAndWarnings ru *utils2.RemoteObjectUtils } -func NewValidateCommand(ctx context.Context, discriminator string, c *deployment.DeploymentCollection) *ValidateCommand { +func NewValidateCommand(ctx context.Context, discriminator string, c *deployment.DeploymentCollection, r *result.CommandResult) *ValidateCommand { cmd := &ValidateCommand{ c: c, + r: r, discriminator: discriminator, dew: utils2.NewDeploymentErrorsAndWarnings(), } @@ -37,41 +41,81 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result cmd.dew.Init() - err := cmd.ru.UpdateRemoteObjects(k, &cmd.discriminator, cmd.c.LocalObjectRefs(), true) + var refs []k8s2.ObjectRef + var renderedObjects []*uo.UnstructuredObject + appliedObjects := map[k8s2.ObjectRef]*uo.UnstructuredObject{} + var discriminator string + + if cmd.c != nil && cmd.r != nil { + return nil, fmt.Errorf("passing both deployment collection and command result is not allowed") + } else if cmd.c != nil { + for _, d := range cmd.c.Deployments { + for _, o := range d.Objects { + ref := o.GetK8sRef() + refs = append(refs, ref) + renderedObjects = append(renderedObjects, o) + } + } + } else if cmd.r != nil { + for _, o := range cmd.r.Objects { + refs = append(refs, o.Ref) + if o.Rendered != nil { + renderedObjects = append(renderedObjects, o.Rendered) + } + if o.Applied != nil { + appliedObjects[o.Ref] = o.Applied + } + } + discriminator = cmd.r.Target.Discriminator + } else { + return nil, fmt.Errorf("either deployment collection or command result must be passed") + } + + if discriminator == "" { + discriminator = cmd.discriminator + } + + err := cmd.ru.UpdateRemoteObjects(k, &cmd.discriminator, refs, true) if err != nil { return nil, err } - ad := utils2.NewApplyDeploymentsUtil(ctx, cmd.dew, cmd.c.Deployments, cmd.ru, k, &utils2.ApplyUtilOptions{}) - for _, d := range cmd.c.Deployments { + ad := utils2.NewApplyDeploymentsUtil(ctx, cmd.dew, cmd.ru, k, &utils2.ApplyUtilOptions{}) + for _, o := range renderedObjects { au := ad.NewApplyUtil(ctx, nil) h := utils2.NewHooksUtil(au) - for _, o := range d.Objects { - hook := h.GetHook(o) - if hook != nil && !hook.IsPersistent() { - continue - } + hook := h.GetHook(o) + if hook != nil && !hook.IsPersistent() { + continue + } - ref := o.GetK8sRef() + ref := o.GetK8sRef() - remoteObject := cmd.ru.GetRemoteObject(ref) - if remoteObject == nil { - ret.Errors = append(ret.Errors, result.DeploymentError{Ref: ref, Message: "object not found"}) - continue - } - r := validation.ValidateObject(k, remoteObject, true, false) - if !r.Ready { - ret.Ready = false - } - ret.Errors = append(ret.Errors, r.Errors...) - ret.Warnings = append(ret.Warnings, r.Warnings...) - ret.Results = append(ret.Results, r.Results...) + remoteObject := cmd.ru.GetRemoteObject(ref) + if remoteObject == nil { + ret.Errors = append(ret.Errors, result.DeploymentError{Ref: ref, Message: "object not found"}) + continue } + r := validation.ValidateObject(k, remoteObject, true, false) + if !r.Ready { + ret.Ready = false + } + ret.Errors = append(ret.Errors, r.Errors...) + ret.Warnings = append(ret.Warnings, r.Warnings...) + ret.Results = append(ret.Results, r.Results...) } + du := utils2.NewDiffUtil(cmd.dew, cmd.ru, appliedObjects) + du.Swapped = true + du.DiffObjects(renderedObjects) + ret.Warnings = append(ret.Warnings, cmd.dew.GetWarningsList()...) ret.Errors = append(ret.Errors, cmd.dew.GetErrorsList()...) + if len(du.ChangedObjects) != 0 { + ret.Drift = du.ChangedObjects + } + return &ret, nil } diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index 8e0b00bc2..57c37b981 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -20,9 +20,10 @@ type DiffUtil struct { IgnoreTags bool IgnoreLabels bool IgnoreAnnotations bool + Swapped bool remoteDiffObjects map[k8s2.ObjectRef]*uo.UnstructuredObject - ChangedObjects []*result.ChangedObject + ChangedObjects []result.ChangedObject mutex sync.Mutex } @@ -75,7 +76,11 @@ func (u *DiffUtil) diffObjects(objects []*uo.UnstructuredObject, ignoreForDiffs wg.Add(1) go func() { defer wg.Done() - u.diffObject(o, diffRef, ao, ro, ignoreForDiffs) + if u.Swapped { + u.diffObject(o, diffRef, ro, ao, ignoreForDiffs) + } else { + u.diffObject(o, diffRef, ao, ro, ignoreForDiffs) + } }() } } @@ -112,7 +117,7 @@ func (u *DiffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, u.mutex.Lock() defer u.mutex.Unlock() - u.ChangedObjects = append(u.ChangedObjects, &result.ChangedObject{ + u.ChangedObjects = append(u.ChangedObjects, result.ChangedObject{ Ref: diffRef, Changes: changes, }) diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index c7047fa63..59dbddf81 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -141,7 +141,9 @@ type CompactedCommandResult struct { func (ccr *CompactedCommandResult) ToNonCompacted() *CommandResult { ret := ccr.CommandResult - ret.Objects = ccr.CompactedObjects + if ccr.CompactedObjects != nil { + ret.Objects = ccr.CompactedObjects + } return &ret } @@ -157,4 +159,6 @@ type ValidateResult struct { Warnings []DeploymentError `json:"warnings,omitempty"` Errors []DeploymentError `json:"errors,omitempty"` Results []ValidateResultEntry `json:"results,omitempty"` + + Drift []ChangedObject `json:"drift,omitempty"` } diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index b28c789eb..029c968bb 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -193,6 +193,14 @@ func WriteJsonString(o interface{}) (string, error) { return string(b), nil } +func WriteJsonStringMust(o interface{}) string { + b, err := json.Marshal(o) + if err != nil { + panic(err) + } + return string(b) +} + // RemoveDuplicateFields is a helper/hack to remove duplicate fields from yaml maps/structs. The yaml spec explicitly // forbids duplicate keys, but yaml.v2 ignored those by default, leading to some tools (e.g. Helm) ignoring these func RemoveDuplicateFields(r io.Reader) ([]byte, error) { From bc4b8df7398b6b5e165ff5d09b3972ec830bb8e6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Apr 2023 01:25:37 +0200 Subject: [PATCH 0939/2268] fix: Normalize float64 values to be interpreted as ints if possible --- pkg/diff/normalize.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/diff/normalize.go b/pkg/diff/normalize.go index f32f7a2d7..08555cef5 100644 --- a/pkg/diff/normalize.go +++ b/pkg/diff/normalize.go @@ -117,6 +117,18 @@ func normalizeMisc(o *uo.UnstructuredObject) { _ = o.RemoveNestedField("status") } +func normalizeFloats(o *uo.UnstructuredObject) { + _ = o.NewIterator().IterateLeafs(func(it *uo.ObjectIterator) error { + if f, ok := it.Value().(float64); ok { + i := int64(f) + if f == float64(i) { + _ = it.SetValue(i) + } + } + return nil + }) +} + var ignoreDiffFieldAnnotationRegex = regexp.MustCompile(`^kluctl.io/ignore-diff-field(-\d*)?$`) var ignoreDiffFieldRegexAnnotationRegex = regexp.MustCompile(`^kluctl.io/ignore-diff-field-regex(-\d*)?$`) @@ -127,6 +139,7 @@ func NormalizeObject(o_ *uo.UnstructuredObject, ignoreForDiffs []*types.IgnoreFo ns := o_.GetK8sNamespace() o := o_.Clone() + normalizeFloats(o) normalizeMetadata(o) normalizeMisc(o) From edcee7cb22c97a7fbf6d0db6260960b068fcace0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 25 Apr 2023 13:48:07 +0200 Subject: [PATCH 0940/2268] fix: Make target name optional via json tags and verify it later This allows to represent no-target deployments in command results. --- pkg/kluctl_project/targets.go | 7 ++++++- pkg/types/kluctl_project.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/kluctl_project/targets.go b/pkg/kluctl_project/targets.go index a9a8b714d..9ef106813 100644 --- a/pkg/kluctl_project/targets.go +++ b/pkg/kluctl_project/targets.go @@ -14,7 +14,12 @@ func (c *LoadedKluctlProject) loadTargets() error { targetNames := make(map[string]bool) c.Targets = nil - for _, configTarget := range c.Config.Targets { + for i, configTarget := range c.Config.Targets { + if configTarget.Name == "" { + status.Error(c.ctx, "Target at index %d has no name", i) + continue + } + target, err := c.buildTarget(configTarget) if err != nil { status.Warning(c.ctx, "Failed to load target config for project: %v", err) diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 063a41398..cde09e90c 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -11,7 +11,7 @@ type SealingConfig struct { } type Target struct { - Name string `json:"name" validate:"required"` + Name string `json:"name,omitempty"` Context *string `json:"context,omitempty"` Args *uo.UnstructuredObject `json:"args,omitempty"` SealingConfig *SealingConfig `json:"sealingConfig,omitempty"` From 7a6b68e2c96b785facd2b50ef9b666b0cd1def03 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 12 Apr 2023 08:41:44 +0200 Subject: [PATCH 0941/2268] feat: Implement TargetSummary and use it inside ProjectSummary --- cmd/kluctl/commands/utils.go | 14 ++++++- pkg/types/result/command_result.go | 30 +++++++++++++++ pkg/types/result/summary.go | 60 +++++++++++++++++++++++++++--- 3 files changed, 98 insertions(+), 6 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 0b783d808..23375c1e8 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -206,7 +206,11 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm var resultStore results.ResultStore if args.commandResultFlags != nil && args.commandResultFlags.WriteCommandResult { - resultStore, err = results.NewResultStoreSecrets(targetCtx.SharedContext.K, args.commandResultFlags.CommandResultNamespace) + rc, err := targetCtx.SharedContext.K.ToRESTConfig() + if err != nil { + return err + } + resultStore, err = results.NewResultStoreSecrets(ctx, rc, args.commandResultFlags.CommandResultNamespace) if err != nil { return err } @@ -276,6 +280,14 @@ func addCommandInfo(r *result.CommandResult, startTime time.Time, command string return err } + if ctx.targetCtx.Target != nil { + r.Target.TargetName = ctx.targetCtx.Target.Name + r.Target.Discriminator = ctx.targetCtx.Target.Discriminator + } + if r.ClusterInfo != nil { + r.Target.ClusterId = r.ClusterInfo.ClusterId + } + return nil } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 59dbddf81..b84b552f4 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -44,6 +44,35 @@ type ProjectKey struct { SubDir string `json:"subDir,omitempty"` } +func (k ProjectKey) Less(o ProjectKey) bool { + if k.NormalizedGitUrl != o.NormalizedGitUrl { + return k.NormalizedGitUrl < o.NormalizedGitUrl + } + if k.SubDir != o.SubDir { + return k.SubDir < o.SubDir + } + return false +} + +type TargetKey struct { + TargetName string `json:"targetName,omitempty"` + ClusterId string `json:"clusterId"` + Discriminator string `json:"discriminator,omitempty"` +} + +func (k TargetKey) Less(o TargetKey) bool { + if k.TargetName != o.TargetName { + return k.TargetName < o.TargetName + } + if k.ClusterId != o.ClusterId { + return k.ClusterId < o.ClusterId + } + if k.Discriminator != o.Discriminator { + return k.Discriminator < o.Discriminator + } + return false +} + type CommandInfo struct { Initiator CommandInitiator `json:"initiator" validate:"oneof=CommandLine KluctlDeployment"` StartTime types.JsonTime `json:"startTime"` @@ -100,6 +129,7 @@ type ResultObject struct { type CommandResult struct { Id string `json:"id"` Project ProjectKey `json:"project"` + Target TargetKey `json:"target"` Command CommandInfo `json:"command,omitempty"` GitInfo *GitInfo `json:"gitInfo,omitempty"` ClusterInfo *ClusterInfo `json:"clusterInfo,omitempty"` diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index 3dfa79a75..fbc4c25b2 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -1,8 +1,13 @@ package result +import ( + "sort" +) + type CommandResultSummary struct { Id string `json:"id"` Project ProjectKey `json:"project"` + Target TargetKey `json:"target"` Command CommandInfo `json:"commandInfo"` GitInfo *GitInfo `json:"gitInfo,omitempty"` ClusterInfo *ClusterInfo `json:"clusterInfo,omitempty"` @@ -22,14 +27,17 @@ type CommandResultSummary struct { TotalChanges int `json:"totalChanges"` } +type TargetSummary struct { + Target TargetKey `json:"target"` + + LastValidateResult *ValidateResult `json:"lastValidateResult,omitempty"` + CommandResults []CommandResultSummary `json:"commandResults,omitempty"` +} + type ProjectSummary struct { Project ProjectKey `json:"project"` - LastValidateResult *ValidateResult `json:"lastValidateResult,omitempty"` - - LastDeployCommand *CommandResultSummary `json:"lastDeployCommand,omitempty"` - LastDeleteCommand *CommandResultSummary `json:"LastDeleteCommand,omitempty"` - LastPruneCommand *CommandResultSummary `json:"lastPruneCommand,omitempty"` + Targets []*TargetSummary `json:"targets"` } func (cr *CommandResult) BuildSummary() *CommandResultSummary { @@ -46,6 +54,7 @@ func (cr *CommandResult) BuildSummary() *CommandResultSummary { ret := &CommandResultSummary{ Id: cr.Id, Project: cr.Project, + Target: cr.Target, Command: cr.Command, GitInfo: cr.GitInfo, ClusterInfo: cr.ClusterInfo, @@ -65,3 +74,44 @@ func (cr *CommandResult) BuildSummary() *CommandResultSummary { } return ret } + +func BuildProjectSummaries(summaries []CommandResultSummary) []*ProjectSummary { + m := map[ProjectKey]*ProjectSummary{} + for _, rs := range summaries { + p, ok := m[rs.Project] + if !ok { + p = &ProjectSummary{Project: rs.Project} + m[rs.Project] = p + } + + var target *TargetSummary + for i, t := range p.Targets { + if t.Target == rs.Target { + target = p.Targets[i] + break + } + } + if target == nil { + target = &TargetSummary{ + Target: rs.Target, + } + p.Targets = append(p.Targets, target) + } + + target.CommandResults = append(target.CommandResults, rs) + } + + ret := make([]*ProjectSummary, 0, len(m)) + for _, p := range m { + sort.Slice(p.Targets, func(i, j int) bool { + return p.Targets[i].Target.Less(p.Targets[j].Target) + }) + ret = append(ret, p) + } + + sort.Slice(ret, func(i, j int) bool { + return ret[i].Project.Less(ret[j].Project) + }) + + return ret +} From e2de36f7b0591e2ae89e8fce9d7d5e77bd4e1797 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 2 May 2023 21:40:18 +0200 Subject: [PATCH 0942/2268] feat: Implement ResultCollector --- cmd/kluctl/commands/command_result.go | 2 +- pkg/results/result-store-secrets.go | 346 +++++++++++++++++++------- pkg/results/result-store.go | 59 ++--- pkg/results/results-collector.go | 174 +++++++++++++ 4 files changed, 454 insertions(+), 127 deletions(-) create mode 100644 pkg/results/results-collector.go diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index cedfb9319..25e4a9e5e 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -252,7 +252,7 @@ func outputCommandResult(ctx *commandCtx, flags args.OutputFormatFlags, cr *resu if writeToResultStore && ctx.resultStore != nil { s := status.Start(ctx.ctx, "Writing command result") defer s.Failed() - resultStoreErr = ctx.resultStore.WriteCommandResult(ctx.ctx, cr) + resultStoreErr = ctx.resultStore.WriteCommandResult(cr) if resultStoreErr != nil { s.FailedWithMessage("Failed to write result to result store: %s", resultStoreErr.Error()) } else { diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index bc4782a1c..4eb5f9ead 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -3,53 +3,92 @@ package results import ( "compress/gzip" "context" - "encoding/base64" "fmt" - "github.com/kluctl/kluctl/v2/pkg/k8s" - k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/rest" + toolscache "k8s.io/client-go/tools/cache" "path" "regexp" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" "sort" "strings" + "sync" ) type ResultStoreSecrets struct { - k *k8s.K8sCluster + ctx context.Context + + config *rest.Config writeNamespace string + + mutex sync.Mutex + client client.Client + cache cache.Cache + informer cache.Informer + cancelCache context.CancelFunc } -func NewResultStoreSecrets(k *k8s.K8sCluster, writeNamespace string) (*ResultStoreSecrets, error) { +func NewResultStoreSecrets(ctx context.Context, config *rest.Config, writeNamespace string) (*ResultStoreSecrets, error) { s := &ResultStoreSecrets{ - k: k, + ctx: ctx, + config: config, writeNamespace: writeNamespace, } + return s, nil +} - if s.writeNamespace != "" { - _, _, err := k.GetSingleObject(k8s2.NewObjectRef("", "v1", "Namespace", s.writeNamespace, "")) - if err != nil && errors.IsNotFound(err) { - ns := uo.FromMap(map[string]interface{}{ - "apiVersion": "v1", - "kind": "Namespace", - "metadata": map[string]any{ - "name": s.writeNamespace, - }, - }) - _, _, err = k.ReadWrite().PatchObject(ns, k8s.PatchOptions{}) - if err != nil { - return nil, err - } - } else if err != nil { - return nil, err - } +func (s *ResultStoreSecrets) ensureClientAndCache() error { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.client != nil { + return nil } - return s, nil + c, err := client.New(s.config, client.Options{}) + if err != nil { + return err + } + + labelSelector, _ := labels.Parse("kluctl.io/result-id") + + var partialSecret metav1.PartialObjectMetadata + partialSecret.SetGroupVersionKind(schema.GroupVersionKind{Version: "v1", Kind: "Secret"}) + + ca, err := cache.New(s.config, cache.Options{ + DefaultLabelSelector: labelSelector, + }) + if err != nil { + return err + } + + informer, err := ca.GetInformer(s.ctx, &partialSecret) + if err != nil { + return err + } + + s.client = c + s.cache = ca + s.informer = informer + + newCtx, cancel := context.WithCancel(s.ctx) + s.cancelCache = cancel + + go func() { + _ = ca.Start(newCtx) + }() + + s.cache.WaitForCacheSync(s.ctx) + + return nil } var invalidChars = regexp.MustCompile(`[^a-zA-Z0-9-]`) @@ -77,7 +116,40 @@ func (s *ResultStoreSecrets) buildName(cr *result.CommandResult) string { return nameWithId } -func (s *ResultStoreSecrets) WriteCommandResult(ctx context.Context, cr *result.CommandResult) error { +func (s *ResultStoreSecrets) ensureWriteNamespace() error { + if s.writeNamespace == "" { + return fmt.Errorf("missing writeNamespace") + } + var ns corev1.Namespace + err := s.client.Get(s.ctx, client.ObjectKey{Name: s.writeNamespace}, &ns) + if err != nil && errors.IsNotFound(err) { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: s.writeNamespace, + }, + } + err = s.client.Create(s.ctx, ns) + if err != nil { + return err + } + } else if err != nil { + return err + } + + return nil +} + +func (s *ResultStoreSecrets) WriteCommandResult(cr *result.CommandResult) error { + err := s.ensureClientAndCache() + if err != nil { + return err + } + + err = s.ensureWriteNamespace() + if err != nil { + return err + } + crJson, err := yaml.WriteJsonString(cr.ToReducedObjects()) if err != nil { return err @@ -102,67 +174,65 @@ func (s *ResultStoreSecrets) WriteCommandResult(ctx context.Context, cr *result. return err } - secret := uo.FromMap(map[string]interface{}{ - "apiVersion": "v1", - "kind": "Secret", - "metadata": map[string]any{ - "name": s.buildName(cr), - "namespace": s.writeNamespace, - "labels": map[string]any{ + secret := corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: s.buildName(cr), + Namespace: s.writeNamespace, + Labels: map[string]string{ "kluctl.io/result-id": cr.Id, }, - "annotations": map[string]any{ + Annotations: map[string]string{ "kluctl.io/result-summary": summaryJson, }, }, - "data": map[string]any{ + Data: map[string][]byte{ "reducedResult": compressedCr, "compactedObjects": compressedObjects, }, - }) + } if cr.Project.NormalizedGitUrl != "" { - secret.SetK8sAnnotation("kluctl.io/result-project-normalized-url", cr.Project.NormalizedGitUrl) + secret.Annotations["kluctl.io/result-project-normalized-url"] = cr.Project.NormalizedGitUrl } if cr.Project.SubDir != "" { - secret.SetK8sAnnotation("kluctl.io/result-project-subdir", cr.Project.SubDir) + secret.Annotations["kluctl.io/result-project-subdir"] = cr.Project.SubDir } - _, _, err = s.k.ReadWrite().PatchObject(secret, k8s.PatchOptions{}) - return err + err = s.client.Patch(s.ctx, &secret, client.Apply, client.FieldOwner("kluctl-results")) + if err != nil { + return err + } + return nil } -func (s *ResultStoreSecrets) ListCommandResultSummaries(ctx context.Context, options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) { - l, _, err := s.k.ListMetadata(schema.GroupVersionKind{ - Version: "v1", - Kind: "Secret", - }, "", map[string]string{ - "kluctl.io/result-id": "", - }) +func (s *ResultStoreSecrets) ListCommandResultSummaries(options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) { + err := s.ensureClientAndCache() if err != nil { return nil, err } - ret := make([]result.CommandResultSummary, 0, len(l)) + var l metav1.PartialObjectMetadataList + l.SetGroupVersionKind(schema.GroupVersionKind{Version: "v1", Kind: "SecretList"}) + err = s.cache.List(s.ctx, &l, client.HasLabels{"kluctl.io/result-id"}) + if err != nil { + return nil, err + } - for _, x := range l { - summaryJson := x.GetK8sAnnotation("kluctl.io/result-summary") - if summaryJson == nil || *summaryJson == "" { - continue - } + ret := make([]result.CommandResultSummary, 0, len(l.Items)) - var summary result.CommandResultSummary - err = yaml.ReadYamlString(*summaryJson, &summary) + for _, x := range l.Items { + summary, err := s.parseSummary(&x) if err != nil { continue } - - if options.ProjectFilter != nil { - if summary.Project != *options.ProjectFilter { - continue - } + if !s.filterSummary(summary, options.ProjectFilter) { + continue } - ret = append(ret, summary) + ret = append(ret, *summary) } sort.Slice(ret, func(i, j int) bool { @@ -172,33 +242,146 @@ func (s *ResultStoreSecrets) ListCommandResultSummaries(ctx context.Context, opt return ret, nil } -func (s *ResultStoreSecrets) GetCommandResult(ctx context.Context, options GetCommandResultOptions) (*result.CommandResult, error) { - l, _, err := s.k.ListObjects(schema.GroupVersionKind{ - Version: "v1", - Kind: "Secret", - }, "", map[string]string{ +func (s *ResultStoreSecrets) parseSummary(obj client.Object) (*result.CommandResultSummary, error) { + if len(obj.GetAnnotations()) == 0 { + return nil, nil + } + summaryJson := obj.GetAnnotations()["kluctl.io/result-summary"] + if summaryJson == "" { + return nil, nil + } + + var summary result.CommandResultSummary + err := yaml.ReadYamlString(summaryJson, &summary) + if err != nil { + return nil, err + } + + return &summary, nil +} + +func (s *ResultStoreSecrets) filterSummary(summary *result.CommandResultSummary, project *result.ProjectKey) bool { + if project != nil { + if project.NormalizedGitUrl != "" && summary.Project.NormalizedGitUrl != project.NormalizedGitUrl { + return false + } + if project.SubDir != "" && summary.Project.SubDir != project.SubDir { + return false + } + } + + return true +} + +func (s *ResultStoreSecrets) WatchCommandResultSummaries(options ListCommandResultSummariesOptions, update func(summary *result.CommandResultSummary), delete func(id string)) (func(), error) { + err := s.ensureClientAndCache() + if err != nil { + return nil, err + } + + handler := func(obj any, deleted bool) { + obj2, ok := obj.(client.Object) + if !ok { + return + } + summary, err := s.parseSummary(obj2) + if err != nil { + return + } + if !s.filterSummary(summary, options.ProjectFilter) { + return + } + if deleted { + delete(summary.Id) + } else { + update(summary) + } + } + + r, err := s.informer.AddEventHandler(toolscache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + handler(obj, false) + }, + UpdateFunc: func(oldObj, newObj interface{}) { + handler(newObj, false) + }, + DeleteFunc: func(obj interface{}) { + handler(obj, true) + }, + }) + if err != nil { + return nil, err + } + + cancel := func() { + _ = s.informer.RemoveEventHandler(r) + } + return cancel, nil +} + +func (s *ResultStoreSecrets) getCommandResultSecret(id string) (*metav1.PartialObjectMetadata, error) { + err := s.ensureClientAndCache() + if err != nil { + return nil, err + } + + var l metav1.PartialObjectMetadataList + l.SetGroupVersionKind(schema.GroupVersionKind{Version: "v1", Kind: "SecretList"}) + err = s.cache.List(s.ctx, &l, client.MatchingLabels{"kluctl.io/result-id": id}) + if err != nil { + return nil, err + } + if len(l.Items) == 0 { + return nil, nil + } + return &l.Items[0], nil +} + +func (s *ResultStoreSecrets) HasCommandResult(id string) (bool, error) { + secret, err := s.getCommandResultSecret(id) + if err != nil { + return false, err + } + return secret != nil, nil +} + +func (s *ResultStoreSecrets) GetCommandResultSummary(id string) (*result.CommandResultSummary, error) { + secret, err := s.getCommandResultSecret(id) + if err != nil { + return nil, err + } + return s.parseSummary(secret) +} + +func (s *ResultStoreSecrets) GetCommandResult(options GetCommandResultOptions) (*result.CommandResult, error) { + has, err := s.HasCommandResult(options.Id) + if err != nil { + return nil, err + } + if !has { + return nil, nil + } + + var l corev1.SecretList + err = s.client.List(s.ctx, &l, client.MatchingLabels{ "kluctl.io/result-id": options.Id, }) if err != nil { return nil, err } - if len(l) == 0 { + if len(l.Items) == 0 { return nil, nil } + secret := l.Items[0] + var crJson, objectsJson []byte - err = utils.RunParallelE(ctx, func() error { - s, ok, err := l[0].GetNestedString("data", "reducedResult") - if err != nil { - return err - } + err = utils.RunParallelE(s.ctx, func() error { + var ok bool + crJson, ok = secret.Data["reducedResult"] if !ok { return fmt.Errorf("reducedResult field not present for %s", options.Id) } - crJson, err = base64.StdEncoding.DecodeString(s) - if err != nil { - return err - } crJson, err = utils.UncompressGzip(crJson) if err != nil { return err @@ -208,18 +391,11 @@ func (s *ResultStoreSecrets) GetCommandResult(ctx context.Context, options GetCo if options.Reduced { return nil } - s, ok, err := l[0].GetNestedString("data", "compactedObjects") - if err != nil { - return err - } + var ok bool + objectsJson, ok = secret.Data["compactedObjects"] if !ok { return fmt.Errorf("compactedObjects field not present for %s", options.Id) } - objectsJson, err = base64.StdEncoding.DecodeString(s) - if err != nil { - return err - } - objectsJson, err = utils.UncompressGzip(objectsJson) if err != nil { return err diff --git a/pkg/results/result-store.go b/pkg/results/result-store.go index 464dc6594..46d002d5d 100644 --- a/pkg/results/result-store.go +++ b/pkg/results/result-store.go @@ -1,7 +1,6 @@ package results import ( - "context" "github.com/kluctl/kluctl/v2/pkg/types/result" ) @@ -9,6 +8,10 @@ type ListProjectsOptions struct { ProjectFilter *result.ProjectKey `json:"projectFilter,omitempty"` } +type ListTargetsOptions struct { + ProjectFilter *result.ProjectKey `json:"projectFilter,omitempty"` +} + type ListCommandResultSummariesOptions struct { ProjectFilter *result.ProjectKey `json:"projectFilter,omitempty"` } @@ -19,50 +22,24 @@ type GetCommandResultOptions struct { } type ResultStore interface { - WriteCommandResult(ctx context.Context, cr *result.CommandResult) error + WriteCommandResult(cr *result.CommandResult) error - ListCommandResultSummaries(ctx context.Context, options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) - GetCommandResult(ctx context.Context, options GetCommandResultOptions) (*result.CommandResult, error) + ListCommandResultSummaries(options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) + WatchCommandResultSummaries(options ListCommandResultSummariesOptions, update func(summary *result.CommandResultSummary), delete func(id string)) (func(), error) + HasCommandResult(id string) (bool, error) + GetCommandResultSummary(id string) (*result.CommandResultSummary, error) + GetCommandResult(options GetCommandResultOptions) (*result.CommandResult, error) } -func ListProjects(ctx context.Context, rs ResultStore, options ListProjectsOptions) ([]result.ProjectSummary, error) { - summaries, err := rs.ListCommandResultSummaries(ctx, ListCommandResultSummariesOptions{ - ProjectFilter: options.ProjectFilter, - }) - if err != nil { - return nil, err +func FilterSummary(x *result.CommandResultSummary, filter *result.ProjectKey) bool { + if filter == nil { + return true } - - m := map[result.ProjectKey]result.ProjectSummary{} - for _, s := range summaries { - if _, ok := m[s.Project]; !ok { - m[s.Project] = result.ProjectSummary{Project: s.Project} - } + if x.Project.NormalizedGitUrl != filter.NormalizedGitUrl { + return false } - - ret := make([]result.ProjectSummary, 0, len(m)) - for _, p := range m { - for _, rs := range summaries { - switch rs.Command.Command { - case "deploy": - if p.LastDeployCommand == nil { - rs := rs - p.LastDeployCommand = &rs - } - case "delete": - if p.LastDeleteCommand == nil { - rs := rs - p.LastDeleteCommand = &rs - } - case "prune": - if p.LastPruneCommand == nil { - rs := rs - p.LastPruneCommand = &rs - } - } - } - ret = append(ret, p) + if x.Project.SubDir != filter.SubDir { + return false } - - return ret, nil + return true } diff --git a/pkg/results/results-collector.go b/pkg/results/results-collector.go new file mode 100644 index 000000000..583a5b9b2 --- /dev/null +++ b/pkg/results/results-collector.go @@ -0,0 +1,174 @@ +package results + +import ( + "context" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "sort" + "sync" + "time" +) + +type ResultsCollector struct { + ctx context.Context + + stores []ResultStore + + resultSummaries map[string]summaryEntry + handlers []*handlerEntry + + mutex sync.Mutex +} + +type summaryEntry struct { + store ResultStore + summary *result.CommandResultSummary +} + +type handlerEntry struct { + options ListCommandResultSummariesOptions + update func(result *result.CommandResultSummary) + delete func(id string) +} + +func NewResultsCollector(ctx context.Context, stores []ResultStore) *ResultsCollector { + ret := &ResultsCollector{ + ctx: ctx, + stores: stores, + resultSummaries: map[string]summaryEntry{}, + } + + return ret +} + +func (rc *ResultsCollector) Start() { + rc.startWatchResults() +} + +func (rc *ResultsCollector) startWatchResults() { + for _, store := range rc.stores { + go rc.runWatchResults(store) + } +} + +func (rc *ResultsCollector) runWatchResults(store ResultStore) { + for { + _, err := store.WatchCommandResultSummaries(ListCommandResultSummariesOptions{}, func(summary *result.CommandResultSummary) { + rc.handleUpdate(store, summary) + }, func(id string) { + rc.handleDelete(store, id) + }) + if err == nil { + break + } + time.Sleep(5 * time.Second) + } +} + +func (rc *ResultsCollector) handleUpdate(store ResultStore, summary *result.CommandResultSummary) { + rc.mutex.Lock() + defer rc.mutex.Unlock() + + rc.resultSummaries[summary.Id] = summaryEntry{ + store: store, + summary: summary, + } + for _, h := range rc.handlers { + if FilterSummary(summary, h.options.ProjectFilter) { + h.update(summary) + } + } +} + +func (rc *ResultsCollector) handleDelete(store ResultStore, id string) { + rc.mutex.Lock() + defer rc.mutex.Unlock() + se, ok := rc.resultSummaries[id] + if !ok { + return + } + + delete(rc.resultSummaries, id) + for _, h := range rc.handlers { + if FilterSummary(se.summary, h.options.ProjectFilter) { + h.delete(id) + } + } +} + +func (rc *ResultsCollector) WriteCommandResult(cr *result.CommandResult) error { + return fmt.Errorf("WriteCommandResult is not supported in ResultsCollector") +} + +func (rc *ResultsCollector) ListCommandResultSummaries(options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) { + rc.mutex.Lock() + defer rc.mutex.Unlock() + summaries := make([]result.CommandResultSummary, 0, len(rc.resultSummaries)) + for _, x := range rc.resultSummaries { + if !FilterSummary(x.summary, options.ProjectFilter) { + continue + } + summaries = append(summaries, *x.summary) + } + sort.Slice(summaries, func(i, j int) bool { + return summaries[i].Command.StartTime >= summaries[j].Command.StartTime + }) + return summaries, nil +} + +func (rc *ResultsCollector) WatchCommandResultSummaries(options ListCommandResultSummariesOptions, update func(summary *result.CommandResultSummary), delete func(id string)) (func(), error) { + rc.mutex.Lock() + defer rc.mutex.Unlock() + + h := &handlerEntry{ + options: options, + update: update, + delete: delete, + } + + rc.handlers = append(rc.handlers, h) + + for _, se := range rc.resultSummaries { + if FilterSummary(se.summary, h.options.ProjectFilter) { + h.update(se.summary) + } + } + + return func() { + rc.mutex.Lock() + defer rc.mutex.Unlock() + for i, h2 := range rc.handlers { + if h2 == h { + rc.handlers = append(rc.handlers[:i], rc.handlers[i+1:]...) + break + } + } + }, nil +} + +func (rc *ResultsCollector) HasCommandResult(id string) (bool, error) { + rc.mutex.Lock() + defer rc.mutex.Unlock() + _, ok := rc.resultSummaries[id] + return ok, nil +} + +func (rc *ResultsCollector) GetCommandResultSummary(id string) (*result.CommandResultSummary, error) { + rc.mutex.Lock() + defer rc.mutex.Unlock() + rs, ok := rc.resultSummaries[id] + if !ok { + return nil, nil + } + return rs.summary, nil +} + +func (rc *ResultsCollector) GetCommandResult(options GetCommandResultOptions) (*result.CommandResult, error) { + rc.mutex.Lock() + se, ok := rc.resultSummaries[options.Id] + rc.mutex.Unlock() + if !ok { + return nil, nil + } + return se.store.GetCommandResult(options) +} From 3636581d43cace6dd3e3c8f77dafa40e321d34fb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 2 May 2023 21:40:38 +0200 Subject: [PATCH 0943/2268] fix: Exclude hooks from validation --- pkg/deployment/commands/validate.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index e3000a09d..32a3dda68 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -59,6 +59,9 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result } else if cmd.r != nil { for _, o := range cmd.r.Objects { refs = append(refs, o.Ref) + if o.Hook { + continue + } if o.Rendered != nil { renderedObjects = append(renderedObjects, o.Rendered) } From 8ab60327ae39e5c8d6edaab54a6a7c4a806ce755 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 2 May 2023 21:48:27 +0200 Subject: [PATCH 0944/2268] feat: Enable writing of command results by default --- cmd/kluctl/args/project.go | 2 +- cmd/kluctl/commands/utils.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index e3ed5ab0e..b2b7c6189 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -43,6 +43,6 @@ type TargetFlags struct { } type CommandResultFlags struct { - WriteCommandResult bool `group:"results" help:"Enable experimental writing of command results into the cluster. Command results will be written into ConfigMaps in the kluctl-results namespace."` + NoWriteCommandResult bool `group:"results" help:"Disable writing of command results into the cluster."` CommandResultNamespace string `group:"results" help:"Override the namespace to be used when writing command results." default:"kluctl-results"` } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 23375c1e8..9857b57b2 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -205,7 +205,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm } var resultStore results.ResultStore - if args.commandResultFlags != nil && args.commandResultFlags.WriteCommandResult { + if args.commandResultFlags != nil && !args.commandResultFlags.NoWriteCommandResult { rc, err := targetCtx.SharedContext.K.ToRESTConfig() if err != nil { return err From 7daee7ff209551276ed37376e770fd11dc08ad13 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 3 May 2023 22:58:24 +0200 Subject: [PATCH 0945/2268] fix: Make ClusterInfo mandatory --- cmd/kluctl/commands/utils.go | 6 ++---- pkg/types/result/command_result.go | 2 +- pkg/types/result/summary.go | 12 ++++++------ 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 9857b57b2..d0fa4450e 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -284,9 +284,7 @@ func addCommandInfo(r *result.CommandResult, startTime time.Time, command string r.Target.TargetName = ctx.targetCtx.Target.Name r.Target.Discriminator = ctx.targetCtx.Target.Discriminator } - if r.ClusterInfo != nil { - r.Target.ClusterId = r.ClusterInfo.ClusterId - } + r.Target.ClusterId = r.ClusterInfo.ClusterId return nil } @@ -372,7 +370,7 @@ func addClusterInfo(r *result.CommandResult, ctx *commandCtx) error { if clusterId == "" { return fmt.Errorf("kube-system namespace has no uid") } - r.ClusterInfo = &result.ClusterInfo{ + r.ClusterInfo = result.ClusterInfo{ ClusterId: clusterId, } return nil diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index b84b552f4..06cee1183 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -132,7 +132,7 @@ type CommandResult struct { Target TargetKey `json:"target"` Command CommandInfo `json:"command,omitempty"` GitInfo *GitInfo `json:"gitInfo,omitempty"` - ClusterInfo *ClusterInfo `json:"clusterInfo,omitempty"` + ClusterInfo ClusterInfo `json:"clusterInfo"` Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` Objects []ResultObject `json:"objects,omitempty"` diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index fbc4c25b2..900645e66 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -5,12 +5,12 @@ import ( ) type CommandResultSummary struct { - Id string `json:"id"` - Project ProjectKey `json:"project"` - Target TargetKey `json:"target"` - Command CommandInfo `json:"commandInfo"` - GitInfo *GitInfo `json:"gitInfo,omitempty"` - ClusterInfo *ClusterInfo `json:"clusterInfo,omitempty"` + Id string `json:"id"` + Project ProjectKey `json:"project"` + Target TargetKey `json:"target"` + Command CommandInfo `json:"commandInfo"` + GitInfo *GitInfo `json:"gitInfo,omitempty"` + ClusterInfo ClusterInfo `json:"clusterInfo,omitempty"` RenderedObjects int `json:"renderedObjects"` RemoteObjects int `json:"remoteObjects"` From 36396a03b27f5a7543e704a94a7dd8db9c46daea Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 3 May 2023 23:04:30 +0200 Subject: [PATCH 0946/2268] feat: Don't write CommandResult in diff command or dryRun mode --- cmd/kluctl/args/project.go | 5 +++-- cmd/kluctl/commands/cmd_delete.go | 2 +- cmd/kluctl/commands/cmd_deploy.go | 2 +- cmd/kluctl/commands/cmd_diff.go | 4 +--- cmd/kluctl/commands/cmd_poke_images.go | 2 +- cmd/kluctl/commands/cmd_prune.go | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index b2b7c6189..3714186cb 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -43,6 +43,7 @@ type TargetFlags struct { } type CommandResultFlags struct { - NoWriteCommandResult bool `group:"results" help:"Disable writing of command results into the cluster."` - CommandResultNamespace string `group:"results" help:"Override the namespace to be used when writing command results." default:"kluctl-results"` + NoWriteCommandResult bool `group:"results" help:"Disable writing of command results into the cluster."` + ForceWriteCommandResult bool `group:"results" help:"Force writing of command results, even if the command is run in dry-run mode."` + CommandResultNamespace string `group:"results" help:"Override the namespace to be used when writing command results." default:"kluctl-results"` } diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 39ab7f6a5..70d455375 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -64,7 +64,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, !cmd.DryRun || cmd.ForceWriteCommandResult) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index c3ba95bd4..7b70de7a2 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -82,7 +82,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx, startTime time.Time) erro if err != nil { return err } - err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, !cmd.DryRun || cmd.ForceWriteCommandResult) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index fd6fb7689..91be42dfa 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -20,7 +20,6 @@ type diffCmd struct { args.IgnoreFlags args.OutputFormatFlags args.RenderOutputDirFlags - args.CommandResultFlags } func (cmd *diffCmd) Help() string { @@ -39,7 +38,6 @@ func (cmd *diffCmd) Run(ctx context.Context) error { inclusionFlags: cmd.InclusionFlags, helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, - commandResultFlags: &cmd.CommandResultFlags, } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { @@ -58,7 +56,7 @@ func (cmd *diffCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, false) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 888ecddde..571b501cd 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -59,7 +59,7 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { if err != nil { return err } - err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, !cmd.DryRun || cmd.ForceWriteCommandResult) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 567458b99..3fe34a470 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -61,7 +61,7 @@ func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx, startTime time.Time) error if err != nil { return err } - err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, true) + err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, !cmd.DryRun || cmd.ForceWriteCommandResult) if err != nil { return err } From 6de4583de1e7a006eeff1c0e8c348da99219c88f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 3 May 2023 23:14:16 +0200 Subject: [PATCH 0947/2268] chore: Run make replace-commands-help --- docs/reference/commands/common-arguments.md | 4 ++-- docs/reference/commands/validate.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index 2263c15b2..e77bf78e3 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -136,8 +136,8 @@ Command Results: --command-result-namespace string Override the namespace to be used when writing command results. (default "kluctl-results") - --write-command-result Enable experimental writing of command results into the cluster. Command - results will be written into ConfigMaps in the kluctl-results namespace. + --force-write-command-result Force writing of command results, even if the command is run in dry-run mode. + --no-write-command-result Disable writing of command results into the cluster. ``` diff --git a/docs/reference/commands/validate.md b/docs/reference/commands/validate.md index 77a0abb98..ef79bc0ba 100644 --- a/docs/reference/commands/validate.md +++ b/docs/reference/commands/validate.md @@ -31,6 +31,8 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. + --command-result existingfile Specify a command result to use instead of loading a project. + This will also perform drift detection. --helm-insecure-skip-tls-verify stringArray Controls skipping of TLS verification. Must be in the form --helm-insecure-skip-tls-verify=, where must match the id specified in the helm-chart.yaml. From 9ba8da18f526c9fb34686e0f0e7103421430eabc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 3 May 2023 23:25:22 +0200 Subject: [PATCH 0948/2268] fix: Don't omit empty target name --- pkg/types/kluctl_project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index cde09e90c..83aa87f8d 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -11,7 +11,7 @@ type SealingConfig struct { } type Target struct { - Name string `json:"name,omitempty"` + Name string `json:"name"` Context *string `json:"context,omitempty"` Args *uo.UnstructuredObject `json:"args,omitempty"` SealingConfig *SealingConfig `json:"sealingConfig,omitempty"` From c1b599039eb74d6fa7637ac3812a3e9c4be21564 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 22:36:54 +0200 Subject: [PATCH 0949/2268] chore(deps): Bump sigs.k8s.io/kustomize/kyaml from 0.14.1 to 0.14.2 (#460) Bumps [sigs.k8s.io/kustomize/kyaml](https://github.com/kubernetes-sigs/kustomize) from 0.14.1 to 0.14.2. - [Release notes](https://github.com/kubernetes-sigs/kustomize/releases) - [Commits](https://github.com/kubernetes-sigs/kustomize/compare/kyaml/v0.14.1...kyaml/v0.14.2) --- updated-dependencies: - dependency-name: sigs.k8s.io/kustomize/kyaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index baacdc9e8..8a38efb6a 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( k8s.io/apimachinery v0.27.1 k8s.io/client-go v0.27.1 k8s.io/klog/v2 v2.100.1 - sigs.k8s.io/kustomize/kyaml v0.14.1 + sigs.k8s.io/kustomize/kyaml v0.14.2 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 ) diff --git a/go.sum b/go.sum index 10c068ca4..6ff437002 100644 --- a/go.sum +++ b/go.sum @@ -1382,8 +1382,8 @@ sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMm sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.13.2 h1:kejWfLeJhUsTGioDoFNJET5LQe/ajzXhJGYoU+pJsiA= sigs.k8s.io/kustomize/api v0.13.2/go.mod h1:DUp325VVMFVcQSq+ZxyDisA8wtldwHxLZbr1g94UHsw= -sigs.k8s.io/kustomize/kyaml v0.14.1 h1:c8iibius7l24G2wVAGZn/Va2wNys03GXLjYVIcFVxKA= -sigs.k8s.io/kustomize/kyaml v0.14.1/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4= +sigs.k8s.io/kustomize/kyaml v0.14.2 h1:9WSwztbzwGszG1bZTziQUmVMrJccnyrLb5ZMKpJGvXw= +sigs.k8s.io/kustomize/kyaml v0.14.2/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4= 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.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= From 751a0f181eaa5ac48f51fd27ebebd5abf66b1176 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 22:37:43 +0200 Subject: [PATCH 0950/2268] chore(deps): Bump github.com/google/go-containerregistry (#462) Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.14.0 to 0.15.1. - [Release notes](https://github.com/google/go-containerregistry/releases) - [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml) - [Commits](https://github.com/google/go-containerregistry/compare/v0.14.0...v0.15.1) --- updated-dependencies: - dependency-name: github.com/google/go-containerregistry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 34 +++++++++++++++-------------- go.sum | 69 ++++++++++++++++++++++++++++++---------------------------- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/go.mod b/go.mod index 8a38efb6a..a4dcb7e46 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/go-playground/validator/v10 v10.13.0 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/google/go-containerregistry v0.14.0 + github.com/google/go-containerregistry v0.15.1 github.com/hashicorp/vault/api v1.9.1 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.15 @@ -72,10 +72,10 @@ require ( replace sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22 require ( - cloud.google.com/go/compute v1.18.0 // indirect + cloud.google.com/go/compute v1.19.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.12.0 // indirect - cloud.google.com/go/kms v1.9.0 // indirect + cloud.google.com/go/iam v0.13.0 // indirect + cloud.google.com/go/kms v1.10.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect @@ -89,7 +89,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect - github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect @@ -111,10 +111,11 @@ require ( github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.3.0 // indirect github.com/containerd/containerd v1.7.0 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v23.0.1+incompatible // indirect - github.com/docker/docker v23.0.1+incompatible // indirect + github.com/docker/cli v23.0.5+incompatible // indirect + github.com/docker/docker v23.0.5+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -151,7 +152,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect + github.com/googleapis/gax-go/v2 v2.7.1 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect @@ -170,7 +171,7 @@ require ( github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.16.0 // indirect + github.com/klauspost/compress v1.16.5 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect @@ -193,7 +194,7 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect + github.com/opencontainers/image-spec v1.1.0-rc3 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect @@ -215,6 +216,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/urfave/cli v1.22.12 // indirect + github.com/vbatts/tar-split v0.11.3 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -226,15 +228,15 @@ require ( go.opentelemetry.io/otel/trace v1.14.0 // indirect go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/oauth2 v0.6.0 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.8.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/api v0.110.0 // indirect + google.golang.org/api v0.114.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect - google.golang.org/grpc v1.53.0 // indirect + google.golang.org/genproto v0.0.0-20230323212658-478b75c54725 // indirect + google.golang.org/grpc v1.54.0 // 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 diff --git a/go.sum b/go.sum index 6ff437002..356be0ab3 100644 --- a/go.sum +++ b/go.sum @@ -27,17 +27,17 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf 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.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= +cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/kms v1.9.0 h1:b0votJQa/9DSsxgHwN33/tTLA7ZHVzfWhDCrfiXijSo= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/kms v1.10.0 h1:Imrtp8792uqNP9bdfPrjtUkjjqOMBcAJ2bdFaAnLhnk= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= 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= @@ -89,8 +89,8 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -191,6 +191,7 @@ github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXT github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= +github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -213,12 +214,12 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= -github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM= -github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE= +github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY= -github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v23.0.5+incompatible h1:DaxtlTJjFSnLOXVNUBU1+6kXGz2lpDoEAH6QoxaSg8k= +github.com/docker/docker v23.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -396,8 +397,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-containerregistry v0.14.0 h1:z58vMqHxuwvAsVwvKEkmVBz2TlgBgH5k6koEXBtlYkw= -github.com/google/go-containerregistry v0.14.0/go.mod h1:aiJ2fp/SXvkWgmYHioXnbMdlgB8eXiiYOY55gfN91Wk= +github.com/google/go-containerregistry v0.15.1 h1:RsJ9NbfxYWF8Wl4VmvkpN3zYATwuvlPq2j20zmcs63E= +github.com/google/go-containerregistry v0.15.1/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= github.com/google/gofuzz v1.0.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= @@ -428,8 +429,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9 github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= 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/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -536,8 +537,8 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= -github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 h1:K96SwIr8MzBQ3kFFz2H/pA2y+EEk04vZ4fWj/YZghBU= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2/go.mod h1:vzngsPshNKUtq0gxkYQKNJafrcH7Qy7Qt6yGNt7JmQI= github.com/kluctl/go-jinja2 v0.0.0-20230428103343-a832225dc94c h1:qAIvhYamCEU/tY6NaENEIQCynGV5sdON7zgZKnbrhhw= @@ -666,8 +667,8 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= +github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= @@ -813,7 +814,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= -github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= +github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= @@ -935,8 +937,8 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1001,8 +1003,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= 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= @@ -1083,6 +1085,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/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-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1179,8 +1182,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= +golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= 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= @@ -1209,8 +1212,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= 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= @@ -1263,8 +1266,8 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725 h1:VmCWItVXcKboEMCwZaWge+1JLiTCQSngZeINF+wzO+g= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1287,8 +1290,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/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.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= 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= From 17918dad58867dc5a4645a37112e979b1f80cc65 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 7 May 2023 21:54:29 +0200 Subject: [PATCH 0951/2268] feat: Clean up old command results --- cmd/kluctl/args/project.go | 1 + cmd/kluctl/commands/utils.go | 2 +- docs/reference/commands/common-arguments.md | 1 + pkg/results/result-store-secrets.go | 52 ++++++++++++++++++--- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 3714186cb..893c5062e 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -46,4 +46,5 @@ type CommandResultFlags struct { NoWriteCommandResult bool `group:"results" help:"Disable writing of command results into the cluster."` ForceWriteCommandResult bool `group:"results" help:"Force writing of command results, even if the command is run in dry-run mode."` CommandResultNamespace string `group:"results" help:"Override the namespace to be used when writing command results." default:"kluctl-results"` + KeepCommandResultsCount int `group:"results" help:"Configure how many old command results to keep." default:"10"` } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index d0fa4450e..949db7a20 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -210,7 +210,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm if err != nil { return err } - resultStore, err = results.NewResultStoreSecrets(ctx, rc, args.commandResultFlags.CommandResultNamespace) + resultStore, err = results.NewResultStoreSecrets(ctx, rc, args.commandResultFlags.CommandResultNamespace, args.commandResultFlags.KeepCommandResultsCount) if err != nil { return err } diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index e77bf78e3..055c8e2cb 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -137,6 +137,7 @@ Command Results: --command-result-namespace string Override the namespace to be used when writing command results. (default "kluctl-results") --force-write-command-result Force writing of command results, even if the command is run in dry-run mode. + --keep-command-results-count int Configure how many old command results to keep. (default 10) --no-write-command-result Disable writing of command results into the cluster. ``` diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 4eb5f9ead..7edb7a87d 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -4,6 +4,7 @@ import ( "compress/gzip" "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -26,8 +27,9 @@ import ( type ResultStoreSecrets struct { ctx context.Context - config *rest.Config - writeNamespace string + config *rest.Config + writeNamespace string + keepResultsCount int mutex sync.Mutex client client.Client @@ -36,11 +38,12 @@ type ResultStoreSecrets struct { cancelCache context.CancelFunc } -func NewResultStoreSecrets(ctx context.Context, config *rest.Config, writeNamespace string) (*ResultStoreSecrets, error) { +func NewResultStoreSecrets(ctx context.Context, config *rest.Config, writeNamespace string, keepResultsCount int) (*ResultStoreSecrets, error) { s := &ResultStoreSecrets{ - ctx: ctx, - config: config, - writeNamespace: writeNamespace, + ctx: ctx, + config: config, + writeNamespace: writeNamespace, + keepResultsCount: keepResultsCount, } return s, nil } @@ -205,6 +208,43 @@ func (s *ResultStoreSecrets) WriteCommandResult(cr *result.CommandResult) error if err != nil { return err } + + err = s.cleanupResults(cr.Project, cr.Target) + if err != nil { + return err + } + + return nil +} + +func (s *ResultStoreSecrets) cleanupResults(project result.ProjectKey, target result.TargetKey) error { + results, err := s.ListCommandResultSummaries(ListCommandResultSummariesOptions{ + ProjectFilter: &project, + }) + if err != nil { + return err + } + + cnt := 0 + for _, rs := range results { + rs := rs + + if rs.Target != target { + continue + } + cnt++ + + if cnt > s.keepResultsCount { + err := s.client.DeleteAllOf(s.ctx, &corev1.Secret{}, client.InNamespace(s.writeNamespace), client.MatchingLabels{ + "kluctl.io/result-id": rs.Id, + }) + if err != nil { + status.Warning(s.ctx, "Failed to delete old command result %s: %s", rs.Id, err) + } else { + status.Info(s.ctx, "Deleted old command result %s", rs.Id) + } + } + } return nil } From 9d6dc946578a35a11c8a598103317496d64e4f69 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 7 May 2023 22:47:40 +0200 Subject: [PATCH 0952/2268] fix: Use apimachinery YAMLReader utils to parsy multi-doc YAML (#463) This fixes issues with new-lines being lost in ConfigMaps. --- pkg/yaml/yaml.go | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index b28c789eb..7ce32045d 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -1,6 +1,7 @@ package yaml import ( + "bufio" "bytes" "encoding/json" "fmt" @@ -8,6 +9,7 @@ import ( "golang.org/x/text/encoding/unicode" "golang.org/x/text/transform" "io" + apimachinery_yaml "k8s.io/apimachinery/pkg/util/yaml" "os" "path/filepath" "regexp" @@ -85,30 +87,23 @@ func ReadYamlAllStream(r io.Reader) ([]interface{}, error) { func readYamlAllStream(r io.Reader, strict bool) ([]interface{}, error) { r = newUnicodeReader(r) - b, err := io.ReadAll(r) - if err != nil { - return nil, err - } - s := string(b) - s = strings.TrimSpace(s) - - if s == "" { - return nil, nil - } + yr := apimachinery_yaml.NewYAMLReader(bufio.NewReader(r)) - docs := docsSep.Split(s, -1) - - ret := make([]any, 0, len(docs)) - for _, doc := range docs { - if doc == "" { - continue + var ret []any + for { + doc, err := yr.Read() + if err != nil { + if err == io.EOF { + break + } + return nil, err } var x any if strict { - err = yaml.UnmarshalStrict([]byte(doc), &x) + err = yaml.UnmarshalStrict(doc, &x) } else { - err = yaml.Unmarshal([]byte(doc), &x) + err = yaml.Unmarshal(doc, &x) } if err != nil { return nil, err From f927c26d2e9efcde65269e5d4ba293a3180e2b01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 17:26:47 +0200 Subject: [PATCH 0953/2268] chore(deps): Bump github.com/sergi/go-diff from 1.2.0 to 1.3.1 (#465) Bumps [github.com/sergi/go-diff](https://github.com/sergi/go-diff) from 1.2.0 to 1.3.1. - [Commits](https://github.com/sergi/go-diff/compare/v1.2.0...v1.3.1) --- updated-dependencies: - dependency-name: github.com/sergi/go-diff dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d32c66cd1..c9b74a84d 100644 --- a/go.mod +++ b/go.mod @@ -63,7 +63,7 @@ require ( github.com/huandu/xstrings v1.4.0 github.com/onsi/gomega v1.27.6 github.com/otiai10/copy v1.11.0 - github.com/sergi/go-diff v1.2.0 + github.com/sergi/go-diff v1.3.1 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.5 sigs.k8s.io/kustomize/api v0.13.2 diff --git a/go.sum b/go.sum index 356be0ab3..d736f2c84 100644 --- a/go.sum +++ b/go.sum @@ -750,8 +750,8 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= From 13e464033128153c64f57c35f724856867689040 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 17:26:59 +0200 Subject: [PATCH 0954/2268] chore(deps): Bump golang.org/x/net from 0.9.0 to 0.10.0 (#467) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.9.0 to 0.10.0. - [Commits](https://github.com/golang/net/compare/v0.9.0...v0.10.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c9b74a84d..fdee8993b 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/stretchr/testify v1.8.2 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.8.0 - golang.org/x/net v0.9.0 + golang.org/x/net v0.10.0 golang.org/x/sync v0.2.0 golang.org/x/sys v0.8.0 golang.org/x/term v0.8.0 diff --git a/go.sum b/go.sum index d736f2c84..856909ddd 100644 --- a/go.sum +++ b/go.sum @@ -989,8 +989,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +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= From 9fd93f4c980d3ce03ea03f545da386609cca32b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 17:27:08 +0200 Subject: [PATCH 0955/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/config (#471) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.23 to 1.18.25. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.23...config/v1.18.25) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index fdee8993b..44f28861a 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.18.0 - github.com/aws/aws-sdk-go-v2/config v1.18.23 + github.com/aws/aws-sdk-go-v2/config v1.18.25 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7 github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.4 @@ -95,7 +95,7 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.22 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.24 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect @@ -104,7 +104,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.11 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index 856909ddd..85288bd3f 100644 --- a/go.sum +++ b/go.sum @@ -121,10 +121,10 @@ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:W github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.23 h1:gc3lPsAnZpwfi2exupmgHfva0JiAY2BWDg5JWYlmA28= -github.com/aws/aws-sdk-go-v2/config v1.18.23/go.mod h1:rx0ruaQ+gk3OrLFHRRx56lA//XxP8K8uPzeNiKNuWVY= -github.com/aws/aws-sdk-go-v2/credentials v1.13.22 h1:Hp9rwJS4giQ48xqonRV/s7QcDf/wxF6UY7osRmBabvI= -github.com/aws/aws-sdk-go-v2/credentials v1.13.22/go.mod h1:BfNcm6A9nSd+bzejDcMJ5RE+k6WbkCwWkQil7q4heRk= +github.com/aws/aws-sdk-go-v2/config v1.18.25 h1:JuYyZcnMPBiFqn87L2cRppo+rNwgah6YwD3VuyvaW6Q= +github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4= +github.com/aws/aws-sdk-go-v2/credentials v1.13.24 h1:PjiYyls3QdCrzqUN35jMWtUK1vqVZ+zLfdOa/UPFDp0= +github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= @@ -145,8 +145,8 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGX github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.11 h1:uBE+Zj478pfxV98L6SEpvxYiADNjTlMNY714PJLE7uo= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.11/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 h1:2DQLAKDteoEDI8zpCzqBMaZlJuoE9iTYD0gFmXVax9E= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From 71748e3a9d20f9a24aa6ee8e5350aa9b794a8702 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 17:27:34 +0200 Subject: [PATCH 0956/2268] chore(deps): Bump sigs.k8s.io/kustomize/api from 0.13.2 to 0.13.4 (#472) Bumps [sigs.k8s.io/kustomize/api](https://github.com/kubernetes-sigs/kustomize) from 0.13.2 to 0.13.4. - [Release notes](https://github.com/kubernetes-sigs/kustomize/releases) - [Commits](https://github.com/kubernetes-sigs/kustomize/compare/api/v0.13.2...api/v0.13.4) --- updated-dependencies: - dependency-name: sigs.k8s.io/kustomize/api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 44f28861a..27a4a39d1 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/sergi/go-diff v1.3.1 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/controller-runtime v0.14.5 - sigs.k8s.io/kustomize/api v0.13.2 + sigs.k8s.io/kustomize/api v0.13.4 sigs.k8s.io/yaml v1.3.0 ) diff --git a/go.sum b/go.sum index 85288bd3f..a55c70084 100644 --- a/go.sum +++ b/go.sum @@ -1383,8 +1383,8 @@ sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22 h1:KnAZ+ITT sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22/go.mod h1:ujEX5tSkpg5cCOhcwDWLsXwNuMCO+j4rpmmkIn6BGGc= 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/kustomize/api v0.13.2 h1:kejWfLeJhUsTGioDoFNJET5LQe/ajzXhJGYoU+pJsiA= -sigs.k8s.io/kustomize/api v0.13.2/go.mod h1:DUp325VVMFVcQSq+ZxyDisA8wtldwHxLZbr1g94UHsw= +sigs.k8s.io/kustomize/api v0.13.4 h1:E38Hfx0G9R9v7vRgKshviPotJQETG0S2gD3JdHLCAsI= +sigs.k8s.io/kustomize/api v0.13.4/go.mod h1:Bkaavz5RKK6ZzP0zgPrB7QbpbBJKiHuD3BB0KujY7Ls= sigs.k8s.io/kustomize/kyaml v0.14.2 h1:9WSwztbzwGszG1bZTziQUmVMrJccnyrLb5ZMKpJGvXw= sigs.k8s.io/kustomize/kyaml v0.14.2/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= From f9a5c0ef531eb4f0174b4b6bcb0a5df330969847 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 22:18:44 +0200 Subject: [PATCH 0957/2268] chore(deps): Bump golang.org/x/crypto from 0.8.0 to 0.9.0 (#473) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.8.0 to 0.9.0. - [Commits](https://github.com/golang/crypto/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 27a4a39d1..39a495786 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.2 github.com/xanzy/ssh-agent v0.3.3 - golang.org/x/crypto v0.8.0 + golang.org/x/crypto v0.9.0 golang.org/x/net v0.10.0 golang.org/x/sync v0.2.0 golang.org/x/sys v0.8.0 diff --git a/go.sum b/go.sum index a55c70084..e7da825eb 100644 --- a/go.sum +++ b/go.sum @@ -898,8 +898,8 @@ golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +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= From 22ae21caac010a7b9a23dcd1ab00fba1b1b4b6ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 13 May 2023 16:44:32 +0200 Subject: [PATCH 0958/2268] chore(deps): Bump helm.sh/helm/v3 from 3.11.3 to 3.12.0 (#475) Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.11.3 to 3.12.0. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.11.3...v3.12.0) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 39a495786..0b8c91a8f 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( golang.org/x/sys v0.8.0 golang.org/x/term v0.8.0 golang.org/x/text v0.9.0 - helm.sh/helm/v3 v3.11.3 + helm.sh/helm/v3 v3.12.0 k8s.io/api v0.27.1 k8s.io/apiextensions-apiserver v0.27.1 k8s.io/apimachinery v0.27.1 @@ -248,7 +248,7 @@ require ( k8s.io/cli-runtime v0.27.1 // indirect k8s.io/component-base v0.27.1 // indirect k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // indirect - k8s.io/kubectl v0.26.0 // indirect + k8s.io/kubectl v0.27.1 // indirect k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect oras.land/oras-go v1.2.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/go.sum b/go.sum index e7da825eb..f98f646a7 100644 --- a/go.sum +++ b/go.sum @@ -266,6 +266,7 @@ github.com/fluxcd/pkg/kustomize v1.1.1 h1:hYFJGi+fiaecY4gXvx52fumlvDEq/1RdFbaev6 github.com/fluxcd/pkg/kustomize v1.1.1/go.mod h1:i+Z9iPAoSz28oH0FmDI73iqZ3oXZxQR2O3HfhdsWhfo= github.com/fluxcd/pkg/sourceignore v0.3.3 h1:Ue29JAuPECEYdvIqdpXpQaDxpeySn7amarLArp7XoIs= github.com/fluxcd/pkg/sourceignore v0.3.3/go.mod h1:yuJzKggph0Bdbk9LgXjJQhvJZSTJV/1vS7mJuB7mPa0= +github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -610,6 +611,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= @@ -1342,8 +1344,8 @@ 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 v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -helm.sh/helm/v3 v3.11.3 h1:n1X5yaQTP5DYywlBOZMl2gX398Gp6YwFp/IAVj6+5D4= -helm.sh/helm/v3 v3.11.3/go.mod h1:S+sOdQc3BLvt09a9rSlKKVs9x0N/yx+No0y3qFw+FQ8= +helm.sh/helm/v3 v3.12.0 h1:rOq2TPVzg5jt4q5ermAZGZFxNW2uQhKjRhBneAutMEM= +helm.sh/helm/v3 v3.12.0/go.mod h1:8K/469yxjUMu6BaD2EagCitkPjELUL/l2AgCO142G94= 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= @@ -1369,8 +1371,8 @@ k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a h1:gmovKNur38vgoWfGtP5QOGNOA7ki4n6qNYoFAgMlNvg= k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= -k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= -k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= +k8s.io/kubectl v0.27.1 h1:9T5c5KdpburYiW8XKQSH0Uly1kMNE90aGSnbYUZNdcA= +k8s.io/kubectl v0.27.1/go.mod h1:QsAkSmrRsKTPlAFzF8kODGDl4p35BIwQnc9XFhkcsy8= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.3 h1:v8PJl+gEAntI1pJ/LCrDgsuk+1PKVavVEPsYIHFE5uY= From f818c01f563a25c633f3d97f39bfa9f7f301579b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 13 May 2023 16:45:03 +0200 Subject: [PATCH 0959/2268] chore(deps): Bump github.com/docker/distribution (#476) Bumps [github.com/docker/distribution](https://github.com/docker/distribution) from 2.8.1+incompatible to 2.8.2+incompatible. - [Release notes](https://github.com/docker/distribution/releases) - [Commits](https://github.com/docker/distribution/compare/v2.8.1...v2.8.2) --- updated-dependencies: - dependency-name: github.com/docker/distribution dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0b8c91a8f..53d6566c9 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/bitnami-labs/sealed-secrets v0.20.5 github.com/cyphar/filepath-securejoin v0.2.3 - github.com/docker/distribution v2.8.1+incompatible + github.com/docker/distribution v2.8.2+incompatible github.com/fluxcd/pkg/kustomize v1.1.1 github.com/go-playground/validator/v10 v10.13.0 github.com/gobwas/glob v0.2.3 // indirect diff --git a/go.sum b/go.sum index f98f646a7..992d6212a 100644 --- a/go.sum +++ b/go.sum @@ -216,8 +216,8 @@ github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aB github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE= github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v23.0.5+incompatible h1:DaxtlTJjFSnLOXVNUBU1+6kXGz2lpDoEAH6QoxaSg8k= github.com/docker/docker v23.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= From e44bcc2b5da2e64356062ded13e0918488f8ac62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 May 2023 07:18:29 +0200 Subject: [PATCH 0960/2268] chore(deps): Bump k8s.io/api from 0.27.1 to 0.27.2 (#487) Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.27.1 to 0.27.2. - [Commits](https://github.com/kubernetes/api/compare/v0.27.1...v0.27.2) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 53d6566c9..8343b592c 100644 --- a/go.mod +++ b/go.mod @@ -42,9 +42,9 @@ require ( golang.org/x/term v0.8.0 golang.org/x/text v0.9.0 helm.sh/helm/v3 v3.12.0 - k8s.io/api v0.27.1 + k8s.io/api v0.27.2 k8s.io/apiextensions-apiserver v0.27.1 - k8s.io/apimachinery v0.27.1 + k8s.io/apimachinery v0.27.2 k8s.io/client-go v0.27.1 k8s.io/klog/v2 v2.100.1 sigs.k8s.io/kustomize/kyaml v0.14.2 @@ -247,7 +247,7 @@ require ( k8s.io/apiserver v0.27.1 // indirect k8s.io/cli-runtime v0.27.1 // indirect k8s.io/component-base v0.27.1 // indirect - k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/kubectl v0.27.1 // indirect k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect oras.land/oras-go v1.2.3 // indirect diff --git a/go.sum b/go.sum index 992d6212a..3d3691e21 100644 --- a/go.sum +++ b/go.sum @@ -1353,12 +1353,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.27.1 h1:Z6zUGQ1Vd10tJ+gHcNNNgkV5emCyW+v2XTmn+CLjSd0= -k8s.io/api v0.27.1/go.mod h1:z5g/BpAiD+f6AArpqNjkY+cji8ueZDU/WV1jcj5Jk4E= +k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= +k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4= k8s.io/apiextensions-apiserver v0.27.1 h1:Hp7B3KxKHBZ/FxmVFVpaDiXI6CCSr49P1OJjxKO6o4g= k8s.io/apiextensions-apiserver v0.27.1/go.mod h1:8jEvRDtKjVtWmdkhOqE84EcNWJt/uwF8PC4627UZghY= -k8s.io/apimachinery v0.27.1 h1:EGuZiLI95UQQcClhanryclaQE6xjg1Bts6/L3cD7zyc= -k8s.io/apimachinery v0.27.1/go.mod h1:5ikh59fK3AJ287GUvpUsryoMFtH9zj/ARfWCo3AyXTM= +k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= +k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/apiserver v0.27.1 h1:phY+BtXjjzd+ta3a4kYbomC81azQSLa1K8jo9RBw7Lg= k8s.io/apiserver v0.27.1/go.mod h1:UGrOjLY2KsieA9Fw6lLiTObxTb8Z1xEba4uqSuMY0WU= k8s.io/cli-runtime v0.27.1 h1:MMzp5Q/Xmr5L1Lrowuc+Y/r95XINC6c6/fE3aN7JDRM= @@ -1369,8 +1369,8 @@ k8s.io/component-base v0.27.1 h1:kEB8p8lzi4gCs5f2SPU242vOumHJ6EOsOnDM3tTuDTM= k8s.io/component-base v0.27.1/go.mod h1:UGEd8+gxE4YWoigz5/lb3af3Q24w98pDseXcXZjw+E0= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a h1:gmovKNur38vgoWfGtP5QOGNOA7ki4n6qNYoFAgMlNvg= -k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/kubectl v0.27.1 h1:9T5c5KdpburYiW8XKQSH0Uly1kMNE90aGSnbYUZNdcA= k8s.io/kubectl v0.27.1/go.mod h1:QsAkSmrRsKTPlAFzF8kODGDl4p35BIwQnc9XFhkcsy8= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= From 87b267ae546a120145b8a8b67b9faa054ad12316 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 May 2023 07:18:42 +0200 Subject: [PATCH 0961/2268] chore(deps): Bump github.com/sirupsen/logrus from 1.9.0 to 1.9.2 (#485) Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.0 to 1.9.2. - [Release notes](https://github.com/sirupsen/logrus/releases) - [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md) - [Commits](https://github.com/sirupsen/logrus/compare/v1.9.0...v1.9.2) --- updated-dependencies: - dependency-name: github.com/sirupsen/logrus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 8343b592c..f97c74f98 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.10.0 - github.com/sirupsen/logrus v1.9.0 + github.com/sirupsen/logrus v1.9.2 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 diff --git a/go.sum b/go.sum index 3d3691e21..6eacedad4 100644 --- a/go.sum +++ b/go.sum @@ -761,8 +761,9 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= From 7af7c84e0dabff9708e94663fc41355ead3178d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 May 2023 07:18:56 +0200 Subject: [PATCH 0962/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/service/secretsmanager (#483) Bumps [github.com/aws/aws-sdk-go-v2/service/secretsmanager](https://github.com/aws/aws-sdk-go-v2) from 1.19.7 to 1.19.8. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/efs/v1.19.7...service/efs/v1.19.8) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/secretsmanager dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f97c74f98..d28c52fb1 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( filippo.io/age v1.1.1 github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2/config v1.18.25 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.8 github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.4 github.com/google/uuid v1.3.0 diff --git a/go.sum b/go.sum index 6eacedad4..8ac7cac3a 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAc github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7 h1:W88E2kZGo+NHOsyvQbsOZYqxXJdLIqRzKadeVlv5J7k= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.7/go.mod h1:3ARttS6G6U3auEdKfaN4GlnfS9UxYE9nqub1+0YGycA= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.8 h1:eB91eEYUlh8+O2dXr189W8GJJd+/T8N/c5HocH2KzVo= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.8/go.mod h1:3ARttS6G6U3auEdKfaN4GlnfS9UxYE9nqub1+0YGycA= github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY= github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA= From da97e257d9621a5642dfb5c072bc3ddc33d17f81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 May 2023 07:19:11 +0200 Subject: [PATCH 0963/2268] chore(deps): Bump github.com/google/go-containerregistry (#482) Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.15.1 to 0.15.2. - [Release notes](https://github.com/google/go-containerregistry/releases) - [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml) - [Commits](https://github.com/google/go-containerregistry/compare/v0.15.1...v0.15.2) --- updated-dependencies: - dependency-name: github.com/google/go-containerregistry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d28c52fb1..21eccb931 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/go-playground/validator/v10 v10.13.0 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/google/go-containerregistry v0.15.1 + github.com/google/go-containerregistry v0.15.2 github.com/hashicorp/vault/api v1.9.1 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.15 diff --git a/go.sum b/go.sum index 8ac7cac3a..5f809c948 100644 --- a/go.sum +++ b/go.sum @@ -398,8 +398,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-containerregistry v0.15.1 h1:RsJ9NbfxYWF8Wl4VmvkpN3zYATwuvlPq2j20zmcs63E= -github.com/google/go-containerregistry v0.15.1/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= +github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= +github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= github.com/google/gofuzz v1.0.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= From 786baf47050bb626354177f305257deb0c696141 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 May 2023 07:19:49 +0200 Subject: [PATCH 0964/2268] chore(deps): Bump github.com/bitnami-labs/sealed-secrets (#480) Bumps [github.com/bitnami-labs/sealed-secrets](https://github.com/bitnami-labs/sealed-secrets) from 0.20.5 to 0.21.0. - [Release notes](https://github.com/bitnami-labs/sealed-secrets/releases) - [Changelog](https://github.com/bitnami-labs/sealed-secrets/blob/main/RELEASE-NOTES.md) - [Commits](https://github.com/bitnami-labs/sealed-secrets/compare/v0.20.5...v0.21.0) --- updated-dependencies: - dependency-name: github.com/bitnami-labs/sealed-secrets dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 21eccb931..0095a9bbc 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/Masterminds/semver/v3 v3.2.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - github.com/bitnami-labs/sealed-secrets v0.20.5 + github.com/bitnami-labs/sealed-secrets v0.21.0 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.2+incompatible github.com/fluxcd/pkg/kustomize v1.1.1 @@ -201,7 +201,7 @@ require ( github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.15.0 // indirect + github.com/prometheus/client_golang v1.15.1 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect diff --git a/go.sum b/go.sum index 5f809c948..d29697d79 100644 --- a/go.sum +++ b/go.sum @@ -155,8 +155,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitnami-labs/sealed-secrets v0.20.5 h1:ejHSbd7GcJOWgAjBnkn+QFPTFSsNOZAJOomS4bM/LWk= -github.com/bitnami-labs/sealed-secrets v0.20.5/go.mod h1:EAN3XxSWbJOi2AwF0wAEo0bCAReAD0ToTP9euRw5nVQ= +github.com/bitnami-labs/sealed-secrets v0.21.0 h1:oFghyEDmsPjXgEOgBnplBjA0NqpMUFMj3BNFs4E1lZ4= +github.com/bitnami-labs/sealed-secrets v0.21.0/go.mod h1:+nLA44ZWp/05ILUN7Fu3UiqNZJBwdTi/FHfEJ5btN7I= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -664,7 +664,7 @@ github.com/ohler55/ojg v1.18.5 h1:tzn5LJtkSyXowCo8SlGieU0zEc7WF4143Ri9MYlQy7Q= github.com/ohler55/ojg v1.18.5/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= +github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -708,8 +708,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= 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= From 5f359220a068059668984841ccdc9379225e461a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 May 2023 22:04:05 +0200 Subject: [PATCH 0965/2268] chore(deps): Bump github.com/onsi/gomega from 1.27.6 to 1.27.7 (#489) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.6 to 1.27.7. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.27.6...v1.27.7) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 0095a9bbc..ba7e8fa79 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.27.6 + github.com/onsi/gomega v1.27.7 github.com/otiai10/copy v1.11.0 github.com/sergi/go-diff v1.3.1 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 @@ -231,7 +231,7 @@ require ( golang.org/x/mod v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.8.0 // indirect + golang.org/x/tools v0.9.1 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/api v0.114.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index d29697d79..5226d801a 100644 --- a/go.sum +++ b/go.sum @@ -664,9 +664,9 @@ github.com/ohler55/ojg v1.18.5 h1:tzn5LJtkSyXowCo8SlGieU0zEc7WF4143Ri9MYlQy7Q= github.com/ohler55/ojg v1.18.5/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= @@ -1185,8 +1185,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= +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= From 38f09f037c229ff576ba71e5f9aa7f8abb34b643 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 May 2023 22:04:19 +0200 Subject: [PATCH 0966/2268] chore(deps): Bump github.com/stretchr/testify from 1.8.2 to 1.8.3 (#488) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.2 to 1.8.3. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.2...v1.8.3) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index ba7e8fa79..4dae43c9f 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.3 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.9.0 golang.org/x/net v0.10.0 diff --git a/go.sum b/go.sum index 5226d801a..df231b097 100644 --- a/go.sum +++ b/go.sum @@ -808,8 +808,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 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/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= From c6475a23ca3667d08f013c3c52906ca724c05bc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 May 2023 22:04:42 +0200 Subject: [PATCH 0967/2268] chore(deps): Bump k8s.io/client-go from 0.27.1 to 0.27.2 (#490) Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.27.1 to 0.27.2. - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.27.1...v0.27.2) --- updated-dependencies: - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4dae43c9f..5cdf9ca67 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( k8s.io/api v0.27.2 k8s.io/apiextensions-apiserver v0.27.1 k8s.io/apimachinery v0.27.2 - k8s.io/client-go v0.27.1 + k8s.io/client-go v0.27.2 k8s.io/klog/v2 v2.100.1 sigs.k8s.io/kustomize/kyaml v0.14.2 sigs.k8s.io/structured-merge-diff/v4 v4.2.3 diff --git a/go.sum b/go.sum index df231b097..948d07f7c 100644 --- a/go.sum +++ b/go.sum @@ -1365,8 +1365,8 @@ k8s.io/apiserver v0.27.1 h1:phY+BtXjjzd+ta3a4kYbomC81azQSLa1K8jo9RBw7Lg= k8s.io/apiserver v0.27.1/go.mod h1:UGrOjLY2KsieA9Fw6lLiTObxTb8Z1xEba4uqSuMY0WU= k8s.io/cli-runtime v0.27.1 h1:MMzp5Q/Xmr5L1Lrowuc+Y/r95XINC6c6/fE3aN7JDRM= k8s.io/cli-runtime v0.27.1/go.mod h1:tEbTB1XP/nTH3wujsi52bw91gWpErtWiS15R6CwYsAI= -k8s.io/client-go v0.27.1 h1:oXsfhW/qncM1wDmWBIuDzRHNS2tLhK3BZv512Nc59W8= -k8s.io/client-go v0.27.1/go.mod h1:f8LHMUkVb3b9N8bWturc+EDtVVVwZ7ueTVquFAJb2vA= +k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE= +k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ= k8s.io/component-base v0.27.1 h1:kEB8p8lzi4gCs5f2SPU242vOumHJ6EOsOnDM3tTuDTM= k8s.io/component-base v0.27.1/go.mod h1:UGEd8+gxE4YWoigz5/lb3af3Q24w98pDseXcXZjw+E0= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= From 59dd1f01985272a026f01583125694ce273038fd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 22 May 2023 12:07:30 +0200 Subject: [PATCH 0968/2268] chore: Upgrade controller-runtime to v0.15.0-beta.0 --- e2e/test-utils/envtest_cluster_callback.go | 49 +++++++++++++++------- go.mod | 13 +++--- go.sum | 22 +++++----- 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/e2e/test-utils/envtest_cluster_callback.go b/e2e/test-utils/envtest_cluster_callback.go index d28a532a9..17d9518e0 100644 --- a/e2e/test-utils/envtest_cluster_callback.go +++ b/e2e/test-utils/envtest_cluster_callback.go @@ -7,10 +7,12 @@ import ( admissionv1 "k8s.io/api/admissionregistration/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "net" "net/http" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "time" ) type CallbackHandler func(request admission.Request) @@ -19,10 +21,10 @@ type CallbackHandlerEntry struct { Callback CallbackHandler } -func (k *EnvTestCluster) buildServeCallback(gvr schema.GroupVersionResource, cb CallbackHandler) http.Handler { +func (k *EnvTestCluster) buildServeCallback() http.Handler { wh := &webhook.Admission{ Handler: admission.HandlerFunc(func(ctx context.Context, request admission.Request) admission.Response { - k.serveCallback(gvr, request, cb) + k.handleWebhook(request) return admission.Allowed("") }), } @@ -32,18 +34,17 @@ func (k *EnvTestCluster) buildServeCallback(gvr schema.GroupVersionResource, cb return wh } -func (k *EnvTestCluster) serveCallback(gvr schema.GroupVersionResource, request admission.Request, cb CallbackHandler) { - if request.Resource.Group != gvr.Group || request.Resource.Version != gvr.Version || request.Resource.Resource != gvr.Resource { - return - } - - cb(request) -} - func (k *EnvTestCluster) startCallbackServer() error { - k.callbackServer.Host = k.env.WebhookInstallOptions.LocalServingHost - k.callbackServer.Port = k.env.WebhookInstallOptions.LocalServingPort - k.callbackServer.CertDir = k.env.WebhookInstallOptions.LocalServingCertDir + k.callbackServer = webhook.NewServer(webhook.Options{ + Host: k.env.WebhookInstallOptions.LocalServingHost, + Port: k.env.WebhookInstallOptions.LocalServingPort, + CertDir: k.env.WebhookInstallOptions.LocalServingCertDir, + }) + + for _, vwh := range k.env.WebhookInstallOptions.ValidatingWebhooks { + path := "/" + vwh.Name + k.callbackServer.Register(path, k.buildServeCallback()) + } ctx, cancel := context.WithCancel(context.Background()) k.callbackServerStop = cancel @@ -52,6 +53,25 @@ func (k *EnvTestCluster) startCallbackServer() error { _ = k.callbackServer.Start(ctx) }() + tcpAddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(k.env.WebhookInstallOptions.LocalServingHost, fmt.Sprintf("%d", k.env.WebhookInstallOptions.LocalServingPort))) + if err != nil { + return err + } + + endTime := time.Now().Add(time.Second * 10) + for true { + if time.Now().After(endTime) { + return fmt.Errorf("timeout while waiting for webhook server") + } + c, err := net.DialTCP("tcp", nil, tcpAddr) + if err != nil { + time.Sleep(100 * time.Millisecond) + continue + } + _ = c.Close() + break + } + return nil } @@ -71,7 +91,6 @@ func (k *EnvTestCluster) InitWebhookCallback(gvr schema.GroupVersionResource, is group = "none" } name := fmt.Sprintf("%s-%s-%s-callback", group, gvr.Version, gvr.Resource) - path := "/" + name k.env.WebhookInstallOptions.ValidatingWebhooks = append(k.env.WebhookInstallOptions.ValidatingWebhooks, &admissionv1.ValidatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ @@ -108,8 +127,6 @@ func (k *EnvTestCluster) InitWebhookCallback(gvr schema.GroupVersionResource, is }, }, }) - - k.callbackServer.Register(path, k.buildServeCallback(gvr, k.handleWebhook)) } func (k *EnvTestCluster) handleWebhook(request admission.Request) { diff --git a/go.mod b/go.mod index 5cdf9ca67..a815bcfcb 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( golang.org/x/text v0.9.0 helm.sh/helm/v3 v3.12.0 k8s.io/api v0.27.2 - k8s.io/apiextensions-apiserver v0.27.1 + k8s.io/apiextensions-apiserver v0.27.2 k8s.io/apimachinery v0.27.2 k8s.io/client-go v0.27.2 k8s.io/klog/v2 v2.100.1 @@ -65,14 +65,11 @@ require ( github.com/otiai10/copy v1.11.0 github.com/sergi/go-diff v1.3.1 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 - sigs.k8s.io/controller-runtime v0.14.5 + sigs.k8s.io/controller-runtime v0.15.0-beta.0 sigs.k8s.io/kustomize/api v0.13.4 sigs.k8s.io/yaml v1.3.0 ) -// TODO: when controller-runtime past v0.14.6 is released, remove this line -replace sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22 - require ( cloud.google.com/go/compute v1.19.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect @@ -202,7 +199,7 @@ require ( github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.15.1 // indirect - github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/rivo/uniseg v0.4.3 // indirect @@ -244,9 +241,9 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.27.1 // indirect + k8s.io/apiserver v0.27.2 // indirect k8s.io/cli-runtime v0.27.1 // indirect - k8s.io/component-base v0.27.1 // indirect + k8s.io/component-base v0.27.2 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/kubectl v0.27.1 // indirect k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect diff --git a/go.sum b/go.sum index 948d07f7c..40eb65340 100644 --- a/go.sum +++ b/go.sum @@ -303,7 +303,7 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/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.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= @@ -713,8 +713,8 @@ github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt 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.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1357,18 +1357,18 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4= -k8s.io/apiextensions-apiserver v0.27.1 h1:Hp7B3KxKHBZ/FxmVFVpaDiXI6CCSr49P1OJjxKO6o4g= -k8s.io/apiextensions-apiserver v0.27.1/go.mod h1:8jEvRDtKjVtWmdkhOqE84EcNWJt/uwF8PC4627UZghY= +k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo= +k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ= k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= -k8s.io/apiserver v0.27.1 h1:phY+BtXjjzd+ta3a4kYbomC81azQSLa1K8jo9RBw7Lg= -k8s.io/apiserver v0.27.1/go.mod h1:UGrOjLY2KsieA9Fw6lLiTObxTb8Z1xEba4uqSuMY0WU= +k8s.io/apiserver v0.27.2 h1:p+tjwrcQEZDrEorCZV2/qE8osGTINPuS5ZNqWAvKm5E= +k8s.io/apiserver v0.27.2/go.mod h1:EsOf39d75rMivgvvwjJ3OW/u9n1/BmUMK5otEOJrb1Y= k8s.io/cli-runtime v0.27.1 h1:MMzp5Q/Xmr5L1Lrowuc+Y/r95XINC6c6/fE3aN7JDRM= k8s.io/cli-runtime v0.27.1/go.mod h1:tEbTB1XP/nTH3wujsi52bw91gWpErtWiS15R6CwYsAI= k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE= k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ= -k8s.io/component-base v0.27.1 h1:kEB8p8lzi4gCs5f2SPU242vOumHJ6EOsOnDM3tTuDTM= -k8s.io/component-base v0.27.1/go.mod h1:UGEd8+gxE4YWoigz5/lb3af3Q24w98pDseXcXZjw+E0= +k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo= +k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= @@ -1383,8 +1383,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22 h1:KnAZ+ITT57UpjlUM9AsuuPzzF0OjZAhtSgEPeQUHRzg= -sigs.k8s.io/controller-runtime v0.13.1-0.20230426175615-dca0be70fd22/go.mod h1:ujEX5tSkpg5cCOhcwDWLsXwNuMCO+j4rpmmkIn6BGGc= +sigs.k8s.io/controller-runtime v0.15.0-beta.0 h1:pkhYMops8jZrVuI0kBHeF6q9UVu1JljIGGG4Ox5ZJmk= +sigs.k8s.io/controller-runtime v0.15.0-beta.0/go.mod h1:YUTa+du31rqOu4mJaijiuhGFax9ecCJgO/v0/yW09gE= 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/kustomize/api v0.13.4 h1:E38Hfx0G9R9v7vRgKshviPotJQETG0S2gD3JdHLCAsI= From 69f9ed1d67efcc4d89cff9fadf3eff51d4c08e57 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 22 May 2023 14:23:47 +0200 Subject: [PATCH 0969/2268] fix: Ignore AlreadyExists errors in ensureWriteNamespace --- pkg/results/result-store-secrets.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 7edb7a87d..206c3c2d0 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -132,7 +132,7 @@ func (s *ResultStoreSecrets) ensureWriteNamespace() error { }, } err = s.client.Create(s.ctx, ns) - if err != nil { + if err != nil && !errors.IsAlreadyExists(err) { return err } } else if err != nil { From 40993d0ee3a6c5dc4ab3d941ad44e79e10044882 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 12 May 2023 15:40:45 +0200 Subject: [PATCH 0970/2268] refactor: Move GitUrl into types package --- cmd/kluctl/commands/utils.go | 9 ++++----- e2e/git_include_test.go | 8 ++++---- pkg/git/auth/auth_provider.go | 6 +++--- pkg/git/auth/env_auth_provider.go | 4 ++-- pkg/git/auth/git_credentials_file.go | 8 ++++---- pkg/git/auth/list_auth_provider.go | 4 ++-- pkg/git/auth/ssh_auth_provider.go | 12 ++++++------ pkg/git/{git-url => }/giturls/LICENSE | 0 pkg/git/{git-url => }/giturls/urls.go | 0 pkg/git/{git-url => }/giturls/urls_test.go | 0 pkg/git/list_refs.go | 8 ++++---- pkg/git/mirrored_repo.go | 10 +++++----- pkg/repocache/cache.go | 10 +++++----- pkg/types/git_project.go | 7 +++---- pkg/{git/git-url/url.go => types/git_url.go} | 18 +++++++++++++----- pkg/types/result/command_result.go | 11 +++++------ pkg/types/vars_source.go | 7 +++---- pkg/types/{url.go => yaml_url.go} | 0 pkg/vars/vars_loader_test.go | 7 +++---- 19 files changed, 66 insertions(+), 63 deletions(-) rename pkg/git/{git-url => }/giturls/LICENSE (100%) rename pkg/git/{git-url => }/giturls/urls.go (100%) rename pkg/git/{git-url => }/giturls/urls_test.go (100%) rename pkg/{git/git-url/url.go => types/git_url.go} (86%) rename pkg/types/{url.go => yaml_url.go} (100%) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 949db7a20..046f5609c 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -17,7 +17,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" @@ -332,10 +331,10 @@ func addGitInfo(r *result.CommandResult, ctx *commandCtx) error { return err } - var originUrl *git_url.GitUrl + var originUrl *types.GitUrl for _, r := range remotes { if r.Config().Name == "origin" { - originUrl, err = git_url.Parse(r.Config().URLs[0]) + originUrl, err = types.ParseGitUrl(r.Config().URLs[0]) if err != nil { return err } @@ -411,11 +410,11 @@ func parseRepoOverride(s string, isGroup bool) (ret repocache.RepoOverride, err return repocache.RepoOverride{}, fmt.Errorf("%s", s) } - u, err := git_url.Parse(sp[0]) + u, err := types.ParseGitUrl(sp[0]) if err != nil { // we need to prepend a dummy scheme to the repo key so that it is properly parsed dummyUrl := fmt.Sprintf("git://%s", sp[0]) - u, err = git_url.Parse(dummyUrl) + u, err = types.ParseGitUrl(dummyUrl) if err != nil { return repocache.RepoOverride{}, fmt.Errorf("%s: %w", s, err) } diff --git a/e2e/git_include_test.go b/e2e/git_include_test.go index fc24635e1..5330ff33f 100644 --- a/e2e/git_include_test.go +++ b/e2e/git_include_test.go @@ -4,7 +4,7 @@ import ( "fmt" test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" git2 "github.com/kluctl/kluctl/v2/pkg/git" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" + "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" cp "github.com/otiai10/copy" @@ -88,8 +88,8 @@ func TestLocalGitOverride(t *testing.T) { _ = cm.SetNestedField("o2", "data", "a") _ = yaml.WriteYamlFile(filepath.Join(override2, "subDir", "cm", "configmap-include2-cm.yml"), cm) - u1, _ := git_url.Parse(ip1.GitUrl()) - u2, _ := git_url.Parse(ip2.GitUrl()) + u1, _ := types.ParseGitUrl(ip1.GitUrl()) + u2, _ := types.ParseGitUrl(ip2.GitUrl()) k1 := u1.NormalizedRepoKey() k2 := u2.NormalizedRepoKey() @@ -129,7 +129,7 @@ func TestLocalGitGroupOverride(t *testing.T) { _ = cm.SetNestedField("o2", "data", "a") _ = yaml.WriteYamlFile(filepath.Join(override2, "subDir", "cm", "configmap-include2-cm.yml"), cm) - u1, _ := git_url.Parse(p.GitServer().GitUrl() + "/repos") + u1, _ := types.ParseGitUrl(p.GitServer().GitUrl() + "/repos") k1 := u1.NormalizedRepoKey() p.KluctlMust("deploy", "--yes", "-t", "test", diff --git a/pkg/git/auth/auth_provider.go b/pkg/git/auth/auth_provider.go index ab2607dba..8e9c3430b 100644 --- a/pkg/git/auth/auth_provider.go +++ b/pkg/git/auth/auth_provider.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/go-git/go-git/v5/plumbing/transport" ssh2 "github.com/go-git/go-git/v5/plumbing/transport/ssh" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" + "github.com/kluctl/kluctl/v2/pkg/types" "golang.org/x/crypto/ssh" ) @@ -26,7 +26,7 @@ func (a *AuthMethodAndCA) SshClientConfig() (*ssh.ClientConfig, error) { } type GitAuthProvider interface { - BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA + BuildAuth(ctx context.Context, gitUrl types.GitUrl) AuthMethodAndCA } type GitAuthProviders struct { @@ -41,7 +41,7 @@ func (a *GitAuthProviders) RegisterAuthProvider(p GitAuthProvider, last bool) { } } -func (a *GitAuthProviders) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *GitAuthProviders) BuildAuth(ctx context.Context, gitUrl types.GitUrl) AuthMethodAndCA { for _, p := range a.authProviders { auth := p.BuildAuth(ctx, gitUrl) if auth.AuthMethod != nil { diff --git a/pkg/git/auth/env_auth_provider.go b/pkg/git/auth/env_auth_provider.go index d88c54e89..359aaa224 100644 --- a/pkg/git/auth/env_auth_provider.go +++ b/pkg/git/auth/env_auth_provider.go @@ -3,8 +3,8 @@ package auth import ( "context" "fmt" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" + "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "os" ) @@ -15,7 +15,7 @@ type GitEnvAuthProvider struct { Prefix string } -func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *GitEnvAuthProvider) BuildAuth(ctx context.Context, gitUrl types.GitUrl) AuthMethodAndCA { var la ListAuthProvider for _, m := range utils.ParseEnvConfigSets(a.Prefix) { diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index cd9231154..58fff60c3 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -4,9 +4,9 @@ import ( "bufio" "context" "github.com/go-git/go-git/v5/plumbing/transport/http" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" - "github.com/kluctl/kluctl/v2/pkg/git/git-url/giturls" + "github.com/kluctl/kluctl/v2/pkg/git/giturls" "github.com/kluctl/kluctl/v2/pkg/git/messages" + "github.com/kluctl/kluctl/v2/pkg/types" "net/url" "os" "path/filepath" @@ -16,7 +16,7 @@ type GitCredentialsFileAuthProvider struct { MessageCallbacks messages.MessageCallbacks } -func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl types.GitUrl) AuthMethodAndCA { if gitUrl.Scheme != "http" && gitUrl.Scheme != "https" { return AuthMethodAndCA{} } @@ -46,7 +46,7 @@ func (a *GitCredentialsFileAuthProvider) BuildAuth(ctx context.Context, gitUrl g return AuthMethodAndCA{} } -func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl git_url.GitUrl, gitCredentialsPath string) *AuthMethodAndCA { +func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl types.GitUrl, gitCredentialsPath string) *AuthMethodAndCA { st, err := os.Stat(gitCredentialsPath) if err != nil || st.Mode().IsDir() { return nil diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index 0e2d2b453..b69f0b2df 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -4,8 +4,8 @@ import ( "context" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/plumbing/transport/ssh" - "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" + "github.com/kluctl/kluctl/v2/pkg/types" ssh2 "golang.org/x/crypto/ssh" "strings" ) @@ -54,7 +54,7 @@ func (a *ListAuthProvider) AddEntry(e AuthEntry) { a.entries = append(a.entries, e) } -func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl types.GitUrl) AuthMethodAndCA { a.MessageCallbacks.Trace("ListAuthProvider: BuildAuth for %s", gitUrl.String()) a.MessageCallbacks.Trace("ListAuthProvider: path=%s, username=%s, scheme=%s", gitUrl.Path, gitUrl.User.Username(), gitUrl.Scheme) for _, e := range a.entries { diff --git a/pkg/git/auth/ssh_auth_provider.go b/pkg/git/auth/ssh_auth_provider.go index 313e342ef..7793f5747 100644 --- a/pkg/git/auth/ssh_auth_provider.go +++ b/pkg/git/auth/ssh_auth_provider.go @@ -6,8 +6,8 @@ import ( "encoding/binary" "fmt" "github.com/kevinburke/ssh_config" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/git/messages" + "github.com/kluctl/kluctl/v2/pkg/types" sshagent "github.com/xanzy/ssh-agent" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" @@ -56,7 +56,7 @@ func (a *sshDefaultIdentityAndAgent) Signers() ([]ssh.Signer, error) { return a.signers, nil } -func (a *sshDefaultIdentityAndAgent) addDefaultIdentities(gitUrl git_url.GitUrl) { +func (a *sshDefaultIdentityAndAgent) addDefaultIdentities(gitUrl types.GitUrl) { a.authProvider.MessageCallbacks.Trace("trying to add default identity") u, err := user.Current() if err != nil { @@ -82,7 +82,7 @@ func (a *sshDefaultIdentityAndAgent) addDefaultIdentities(gitUrl git_url.GitUrl) doAdd("id_dsa") } -func (a *sshDefaultIdentityAndAgent) addConfigIdentities(gitUrl git_url.GitUrl) { +func (a *sshDefaultIdentityAndAgent) addConfigIdentities(gitUrl types.GitUrl) { a.authProvider.MessageCallbacks.Trace("trying to add identities from ssh config") for _, id := range ssh_config.GetAll(gitUrl.Hostname(), "IdentityFile") { expanded := expandHomeDir(id) @@ -97,7 +97,7 @@ func (a *sshDefaultIdentityAndAgent) addConfigIdentities(gitUrl git_url.GitUrl) } } -func (a *sshDefaultIdentityAndAgent) createAgent(gitUrl git_url.GitUrl) (agent.Agent, error) { +func (a *sshDefaultIdentityAndAgent) createAgent(gitUrl types.GitUrl) (agent.Agent, error) { if runtime.GOOS == "windows" { a, _, err := sshagent.New() return a, err @@ -125,7 +125,7 @@ func (a *sshDefaultIdentityAndAgent) createAgent(gitUrl git_url.GitUrl) (agent.A return agent.NewClient(conn), nil } -func (a *sshDefaultIdentityAndAgent) addAgentIdentities(gitUrl git_url.GitUrl) { +func (a *sshDefaultIdentityAndAgent) addAgentIdentities(gitUrl types.GitUrl) { a.authProvider.MessageCallbacks.Trace("trying to add agent keys") agent, err := a.createAgent(gitUrl) if err != nil { @@ -144,7 +144,7 @@ func (a *sshDefaultIdentityAndAgent) addAgentIdentities(gitUrl git_url.GitUrl) { } } -func (a *GitSshAuthProvider) BuildAuth(ctx context.Context, gitUrl git_url.GitUrl) AuthMethodAndCA { +func (a *GitSshAuthProvider) BuildAuth(ctx context.Context, gitUrl types.GitUrl) AuthMethodAndCA { if !gitUrl.IsSsh() { return AuthMethodAndCA{} } diff --git a/pkg/git/git-url/giturls/LICENSE b/pkg/git/giturls/LICENSE similarity index 100% rename from pkg/git/git-url/giturls/LICENSE rename to pkg/git/giturls/LICENSE diff --git a/pkg/git/git-url/giturls/urls.go b/pkg/git/giturls/urls.go similarity index 100% rename from pkg/git/git-url/giturls/urls.go rename to pkg/git/giturls/urls.go diff --git a/pkg/git/git-url/giturls/urls_test.go b/pkg/git/giturls/urls_test.go similarity index 100% rename from pkg/git/git-url/giturls/urls_test.go rename to pkg/git/giturls/urls_test.go diff --git a/pkg/git/list_refs.go b/pkg/git/list_refs.go index 735234d6b..d5a199fc5 100644 --- a/pkg/git/list_refs.go +++ b/pkg/git/list_refs.go @@ -10,13 +10,13 @@ import ( "github.com/go-git/go-git/v5/plumbing/protocol/packp" "github.com/go-git/go-git/v5/storage/memory" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" + "github.com/kluctl/kluctl/v2/pkg/types" "strconv" ) // ListRemoteRefsFastSsh will reuse existing ssh connections from a pool -func ListRemoteRefsFastSsh(ctx context.Context, url git_url.GitUrl, sshPool *ssh_pool.SshPool, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { +func ListRemoteRefsFastSsh(ctx context.Context, url types.GitUrl, sshPool *ssh_pool.SshPool, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { var portInt int64 = 22 if url.Port() != "" { var err error @@ -75,7 +75,7 @@ func ListRemoteRefsFastSsh(ctx context.Context, url git_url.GitUrl, sshPool *ssh return resultRefs, nil } -func ListRemoteRefsSlow(ctx context.Context, url git_url.GitUrl, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { +func ListRemoteRefsSlow(ctx context.Context, url types.GitUrl, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { storage := memory.NewStorage() remote := git.NewRemote(storage, &config.RemoteConfig{ Name: "origin", @@ -93,7 +93,7 @@ func ListRemoteRefsSlow(ctx context.Context, url git_url.GitUrl, auth auth2.Auth return remoteRefs, nil } -func ListRemoteRefs(ctx context.Context, url git_url.GitUrl, sshPool *ssh_pool.SshPool, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { +func ListRemoteRefs(ctx context.Context, url types.GitUrl, sshPool *ssh_pool.SshPool, auth auth2.AuthMethodAndCA) ([]*plumbing.Reference, error) { if url.IsSsh() { refs, err := ListRemoteRefsFastSsh(ctx, url, sshPool, auth) if err == nil { diff --git a/pkg/git/mirrored_repo.go b/pkg/git/mirrored_repo.go index 10e2e805c..b00becabb 100644 --- a/pkg/git/mirrored_repo.go +++ b/pkg/git/mirrored_repo.go @@ -10,9 +10,9 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" auth2 "github.com/kluctl/kluctl/v2/pkg/git/auth" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" _ "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" + "github.com/kluctl/kluctl/v2/pkg/types" "github.com/rogpeppe/go-internal/lockedfile" "os" "path/filepath" @@ -33,7 +33,7 @@ type MirroredGitRepo struct { sshPool *ssh_pool.SshPool authProviders *auth2.GitAuthProviders - url git_url.GitUrl + url types.GitUrl mirrorDir string hasUpdated bool @@ -44,7 +44,7 @@ type MirroredGitRepo struct { mutex sync.Mutex } -func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl, baseDir string, sshPool *ssh_pool.SshPool, authProviders *auth2.GitAuthProviders) (*MirroredGitRepo, error) { +func NewMirroredGitRepo(ctx context.Context, u types.GitUrl, baseDir string, sshPool *ssh_pool.SshPool, authProviders *auth2.GitAuthProviders) (*MirroredGitRepo, error) { mirrorRepoName := buildMirrorRepoName(u) o := &MirroredGitRepo{ ctx: ctx, @@ -69,7 +69,7 @@ func NewMirroredGitRepo(ctx context.Context, u git_url.GitUrl, baseDir string, s return o, nil } -func (g *MirroredGitRepo) Url() git_url.GitUrl { +func (g *MirroredGitRepo) Url() types.GitUrl { return g.url } @@ -389,7 +389,7 @@ func (g *MirroredGitRepo) GetGitTreeByCommit(commitHash string) (*object.Tree, e return tree, nil } -func buildMirrorRepoName(u git_url.GitUrl) string { +func buildMirrorRepoName(u types.GitUrl) string { h := sha256.New() h.Write([]byte(u.String())) h2 := hex.EncodeToString(h.Sum(nil)) diff --git a/pkg/repocache/cache.go b/pkg/repocache/cache.go index b3ca0907f..a31d37938 100644 --- a/pkg/repocache/cache.go +++ b/pkg/repocache/cache.go @@ -3,6 +3,7 @@ package repocache import ( "context" "fmt" + "github.com/kluctl/kluctl/v2/pkg/types" "os" "path" "path/filepath" @@ -12,7 +13,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/utils" @@ -36,7 +36,7 @@ type GitRepoCache struct { type CacheEntry struct { rp *GitRepoCache - url git_url.GitUrl + url types.GitUrl mr *git.MirroredGitRepo defaultRef string refs map[string]string @@ -47,13 +47,13 @@ type CacheEntry struct { } type RepoInfo struct { - Url git_url.GitUrl `json:"url"` + Url types.GitUrl `json:"url"` RemoteRefs map[string]string `json:"remoteRefs"` DefaultRef string `json:"defaultRef"` } type RepoOverride struct { - RepoUrl git_url.GitUrl + RepoUrl types.GitUrl Ref string Override string IsGroup bool @@ -85,7 +85,7 @@ func (rp *GitRepoCache) Clear() { rp.cleanupDirs = nil } -func (rp *GitRepoCache) GetEntry(url git_url.GitUrl) (*CacheEntry, error) { +func (rp *GitRepoCache) GetEntry(url types.GitUrl) (*CacheEntry, error) { rp.reposMutex.Lock() defer rp.reposMutex.Unlock() diff --git a/pkg/types/git_project.go b/pkg/types/git_project.go index e43997353..32a785541 100644 --- a/pkg/types/git_project.go +++ b/pkg/types/git_project.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/go-playground/validator/v10" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/yaml" ) @@ -15,9 +14,9 @@ import ( var gitDirPatternNeg = regexp.MustCompile(`[\\\/:\*?"<>|[:cntrl:]\0^]`) type GitProject struct { - Url git_url.GitUrl `json:"url" validate:"required"` - Ref string `json:"ref,omitempty"` - SubDir string `json:"subDir,omitempty"` + Url GitUrl `json:"url" validate:"required"` + Ref string `json:"ref,omitempty"` + SubDir string `json:"subDir,omitempty"` } func (gp *GitProject) UnmarshalJSON(b []byte) error { diff --git a/pkg/git/git-url/url.go b/pkg/types/git_url.go similarity index 86% rename from pkg/git/git-url/url.go rename to pkg/types/git_url.go index 57f5cfa0f..f7fa8c6a7 100644 --- a/pkg/git/git-url/url.go +++ b/pkg/types/git_url.go @@ -1,18 +1,18 @@ -package git_url +package types import ( "encoding/json" "fmt" - "github.com/kluctl/kluctl/v2/pkg/git/git-url/giturls" + "github.com/kluctl/kluctl/v2/pkg/git/giturls" "net/url" "strings" ) type GitUrl struct { - url.URL + url.URL `json:"-"` } -func Parse(u string) (*GitUrl, error) { +func ParseGitUrl(u string) (*GitUrl, error) { u2, err := giturls.Parse(u) if err != nil { return nil, err @@ -20,13 +20,21 @@ func Parse(u string) (*GitUrl, error) { return &GitUrl{*u2}, nil } +func ParseGitUrlMust(u string) *GitUrl { + u2, err := ParseGitUrl(u) + if err != nil { + panic(err) + } + return u2 +} + func (u *GitUrl) UnmarshalJSON(b []byte) error { var s string err := json.Unmarshal(b, &s) if err != nil { return err } - u2, err := Parse(s) + u2, err := ParseGitUrl(s) if err != nil { return err } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 06cee1183..4d2ec8dcb 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -1,7 +1,6 @@ package result import ( - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -97,11 +96,11 @@ type CommandInfo struct { } type GitInfo struct { - Url *git_url.GitUrl `json:"url"` - Ref string `json:"ref"` - SubDir string `json:"subDir"` - Commit string `json:"commit"` - Dirty bool `json:"dirty"` + Url *types.GitUrl `json:"url"` + Ref string `json:"ref"` + SubDir string `json:"subDir"` + Commit string `json:"commit"` + Dirty bool `json:"dirty"` } type ClusterInfo struct { diff --git a/pkg/types/vars_source.go b/pkg/types/vars_source.go index 6e09d549c..7eacffe4e 100644 --- a/pkg/types/vars_source.go +++ b/pkg/types/vars_source.go @@ -2,16 +2,15 @@ package types import ( "github.com/go-playground/validator/v10" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" "reflect" ) type VarsSourceGit struct { - Url git_url.GitUrl `json:"url" validate:"required"` - Ref string `json:"ref,omitempty"` - Path string `json:"path" validate:"required"` + Url GitUrl `json:"url" validate:"required"` + Ref string `json:"ref,omitempty"` + Path string `json:"path" validate:"required"` } type VarsSourceClusterConfigMapOrSecret struct { diff --git a/pkg/types/url.go b/pkg/types/yaml_url.go similarity index 100% rename from pkg/types/url.go rename to pkg/types/yaml_url.go diff --git a/pkg/vars/vars_loader_test.go b/pkg/vars/vars_loader_test.go index b119e5278..205a68c2c 100644 --- a/pkg/vars/vars_loader_test.go +++ b/pkg/vars/vars_loader_test.go @@ -16,7 +16,6 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/kluctl/kluctl/v2/pkg/git" "github.com/kluctl/kluctl/v2/pkg/git/auth" - git_url "github.com/kluctl/kluctl/v2/pkg/git/git-url" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/repocache" @@ -223,7 +222,7 @@ func TestVarsLoader_Git(t *testing.T) { }, "") testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { - url, _ := git_url.Parse(gs.GitRepoUrl("repo")) + url, _ := types.ParseGitUrl(gs.GitRepoUrl("repo")) err := vl.LoadVars(vc, &types.VarsSource{ Git: &types.VarsSourceGit{ Url: *url, @@ -237,7 +236,7 @@ func TestVarsLoader_Git(t *testing.T) { }) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { - url, _ := git_url.Parse(gs.GitRepoUrl("repo")) + url, _ := types.ParseGitUrl(gs.GitRepoUrl("repo")) b := true err := vl.LoadVars(vc, &types.VarsSource{ IgnoreMissing: &b, @@ -274,7 +273,7 @@ func TestVarsLoader_GitBranch(t *testing.T) { assert.NoError(t, err) testVarsLoader(t, func(vl *VarsLoader, vc *VarsCtx, aws *aws.FakeAwsClientFactory) { - url, _ := git_url.Parse(gs.GitRepoUrl("repo")) + url, _ := types.ParseGitUrl(gs.GitRepoUrl("repo")) err = vl.LoadVars(vc, &types.VarsSource{ Git: &types.VarsSourceGit{ Url: *url, From 6f4ecf337a79d11b0fea532ff8cc917ddd8c5787 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 12 May 2023 16:28:30 +0200 Subject: [PATCH 0971/2268] refactor: Pass targetCtx to command objects --- cmd/kluctl/commands/cmd_delete.go | 8 ++--- cmd/kluctl/commands/cmd_deploy.go | 4 +-- cmd/kluctl/commands/cmd_diff.go | 4 +-- cmd/kluctl/commands/cmd_poke_images.go | 4 +-- cmd/kluctl/commands/cmd_prune.go | 4 +-- pkg/deployment/commands/delete.go | 32 ++++++++--------- pkg/deployment/commands/deploy.go | 48 ++++++++++++-------------- pkg/deployment/commands/diff.go | 34 ++++++++---------- pkg/deployment/commands/poke_images.go | 32 ++++++++--------- pkg/deployment/commands/prune.go | 20 +++++------ pkg/kluctl_project/target_context.go | 2 +- 11 files changed, 89 insertions(+), 103 deletions(-) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 70d455375..28aafcd2d 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -48,13 +48,9 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - discriminator := cmdCtx.targetCtx.Target.Discriminator - if cmd.Discriminator != "" { - discriminator = cmd.Discriminator - } - cmd2 := commands.NewDeleteCommand(discriminator, cmdCtx.targetCtx.DeploymentCollection, cmdCtx.targetCtx.DeploymentCollection.Inclusion) + cmd2 := commands.NewDeleteCommand(cmd.Discriminator, cmdCtx.targetCtx) - result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, func(refs []k8s2.ObjectRef) error { + result, err := cmd2.Run(func(refs []k8s2.ObjectRef) error { return confirmDeletion(ctx, refs, cmd.DryRun, cmd.Yes) }) if err != nil { diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index 7b70de7a2..da8f92439 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -59,7 +59,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx, startTime time.Time) erro status.Trace(cmdCtx.ctx, "enter runCmdDeploy") defer status.Trace(cmdCtx.ctx, "leave runCmdDeploy") - cmd2 := commands.NewDeployCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) + cmd2 := commands.NewDeployCommand(cmdCtx.targetCtx) cmd2.ForceApply = cmd.ForceApply cmd2.ReplaceOnError = cmd.ReplaceOnError cmd2.ForceReplaceOnError = cmd.ForceReplaceOnError @@ -74,7 +74,7 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx, startTime time.Time) erro cb = nil } - result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, cb) + result, err := cmd2.Run(cb) if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 91be42dfa..429d61b39 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -41,14 +41,14 @@ func (cmd *diffCmd) Run(ctx context.Context) error { } startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - cmd2 := commands.NewDiffCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) + cmd2 := commands.NewDiffCommand(cmdCtx.targetCtx) cmd2.ForceApply = cmd.ForceApply cmd2.ReplaceOnError = cmd.ReplaceOnError cmd2.ForceReplaceOnError = cmd.ForceReplaceOnError cmd2.IgnoreTags = cmd.IgnoreTags cmd2.IgnoreLabels = cmd.IgnoreLabels cmd2.IgnoreAnnotations = cmd.IgnoreAnnotations - result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K) + result, err := cmd2.Run() if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 571b501cd..4d3f2a982 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -49,9 +49,9 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { } } - cmd2 := commands.NewPokeImagesCommand(cmdCtx.targetCtx.DeploymentCollection) + cmd2 := commands.NewPokeImagesCommand(cmdCtx.targetCtx) - result, err := cmd2.Run(ctx, cmdCtx.targetCtx.SharedContext.K) + result, err := cmd2.Run() if err != nil { return err } diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 3fe34a470..43b785ed1 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -50,8 +50,8 @@ func (cmd *pruneCmd) Run(ctx context.Context) error { } func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx, startTime time.Time) error { - cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx.DeploymentCollection) - result, err := cmd2.Run(cmdCtx.ctx, cmdCtx.targetCtx.SharedContext.K, func(refs []k8s2.ObjectRef) error { + cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx) + result, err := cmd2.Run(func(refs []k8s2.ObjectRef) error { return confirmDeletion(cmdCtx.ctx, refs, cmd.DryRun, cmd.Yes) }) if err != nil { diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index e87cffa50..349028e96 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -1,44 +1,44 @@ package commands import ( - "context" "fmt" "github.com/google/uuid" - "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" - "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" - "github.com/kluctl/kluctl/v2/pkg/utils" ) type DeleteCommand struct { - c *deployment.DeploymentCollection discriminator string - inclusion *utils.Inclusion + targetCtx *kluctl_project.TargetContext } -func NewDeleteCommand(discriminator string, c *deployment.DeploymentCollection, inclusion *utils.Inclusion) *DeleteCommand { +func NewDeleteCommand(discriminator string, targetCtx *kluctl_project.TargetContext) *DeleteCommand { return &DeleteCommand{ discriminator: discriminator, - c: c, - inclusion: inclusion, + targetCtx: targetCtx, } } -func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb func(refs []k8s2.ObjectRef) error) (*result.CommandResult, error) { - if cmd.discriminator == "" { +func (cmd *DeleteCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*result.CommandResult, error) { + discriminator := cmd.targetCtx.Target.Discriminator + if cmd.discriminator != "" { + discriminator = cmd.discriminator + } + + if discriminator == "" { return nil, fmt.Errorf("deletion without a discriminator is not supported") } dew := utils2.NewDeploymentErrorsAndWarnings() - ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, &cmd.discriminator, nil, false) + ru := utils2.NewRemoteObjectsUtil(cmd.targetCtx.SharedContext.Ctx, dew) + err := ru.UpdateRemoteObjects(cmd.targetCtx.SharedContext.K, &discriminator, nil, false) if err != nil { return nil, err } - deleteRefs, err := utils2.FindObjectsForDelete(k, ru.GetFilteredRemoteObjects(cmd.inclusion), cmd.inclusion.HasType("tags"), nil) + deleteRefs, err := utils2.FindObjectsForDelete(cmd.targetCtx.SharedContext.K, ru.GetFilteredRemoteObjects(cmd.targetCtx.DeploymentCollection.Inclusion), cmd.targetCtx.DeploymentCollection.Inclusion.HasType("tags"), nil) if err != nil { return nil, err } @@ -50,14 +50,14 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb } } - deleted, err := utils2.DeleteObjects(ctx, k, deleteRefs, dew, true) + deleted, err := utils2.DeleteObjects(cmd.targetCtx.SharedContext.Ctx, cmd.targetCtx.SharedContext.K, deleteRefs, dew, true) if err != nil { return nil, err } return &result.CommandResult{ Id: uuid.New().String(), - Objects: collectObjects(cmd.c, ru, nil, nil, nil, deleted), + Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, nil, nil, nil, deleted), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), }, nil diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index ec814632c..291d05464 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -1,12 +1,10 @@ package commands import ( - "context" "fmt" "github.com/google/uuid" - "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" - "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" @@ -14,8 +12,7 @@ import ( ) type DeployCommand struct { - c *deployment.DeploymentCollection - discriminator string + targetCtx *kluctl_project.TargetContext ForceApply bool ReplaceOnError bool @@ -25,23 +22,22 @@ type DeployCommand struct { NoWait bool } -func NewDeployCommand(discriminator string, c *deployment.DeploymentCollection) *DeployCommand { +func NewDeployCommand(targetCtx *kluctl_project.TargetContext) *DeployCommand { return &DeployCommand{ - discriminator: discriminator, - c: c, + targetCtx: targetCtx, } } -func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResultCb func(diffResult *result.CommandResult) error) (*result.CommandResult, error) { +func (cmd *DeployCommand) Run(diffResultCb func(diffResult *result.CommandResult) error) (*result.CommandResult, error) { dew := utils2.NewDeploymentErrorsAndWarnings() - if cmd.discriminator == "" { - status.Warning(ctx, "No discriminator configured. Orphan object detection will not work") + if cmd.targetCtx.Target.Discriminator == "" { + status.Warning(cmd.targetCtx.SharedContext.Ctx, "No discriminator configured. Orphan object detection will not work") dew.AddWarning(k8s2.ObjectRef{}, fmt.Errorf("no discriminator configured. Orphan object detection will not work")) } - ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, &cmd.discriminator, cmd.c.LocalObjectRefs(), false) + ru := utils2.NewRemoteObjectsUtil(cmd.targetCtx.SharedContext.Ctx, dew) + err := ru.UpdateRemoteObjects(cmd.targetCtx.SharedContext.K, &cmd.targetCtx.Target.Discriminator, cmd.targetCtx.DeploymentCollection.LocalObjectRefs(), false) if err != nil { return nil, err } @@ -58,19 +54,19 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult } if diffResultCb != nil { - au := utils2.NewApplyDeploymentsUtil(ctx, dew, ru, k, o) - au.ApplyDeployments(cmd.c.Deployments) + au := utils2.NewApplyDeploymentsUtil(cmd.targetCtx.SharedContext.Ctx, dew, ru, cmd.targetCtx.SharedContext.K, o) + au.ApplyDeployments(cmd.targetCtx.DeploymentCollection.Deployments) du := utils2.NewDiffUtil(dew, ru, au.GetAppliedObjectsMap()) - du.DiffDeploymentItems(cmd.c.Deployments) + du.DiffDeploymentItems(cmd.targetCtx.DeploymentCollection.Deployments) - orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) + orphanObjects, err := FindOrphanObjects(cmd.targetCtx.SharedContext.K, ru, cmd.targetCtx.DeploymentCollection) diffResult := &result.CommandResult{ Id: uuid.New().String(), - Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), + Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + SeenImages: cmd.targetCtx.DeploymentCollection.Images.SeenImages(false), } err = diffResultCb(diffResult) @@ -83,24 +79,24 @@ func (cmd *DeployCommand) Run(ctx context.Context, k *k8s.K8sCluster, diffResult dew.Init() // modify options to become a deploy - o.DryRun = k.DryRun + o.DryRun = cmd.targetCtx.SharedContext.K.DryRun o.AbortOnError = cmd.AbortOnError - au := utils2.NewApplyDeploymentsUtil(ctx, dew, ru, k, o) - au.ApplyDeployments(cmd.c.Deployments) + au := utils2.NewApplyDeploymentsUtil(cmd.targetCtx.SharedContext.Ctx, dew, ru, cmd.targetCtx.SharedContext.K, o) + au.ApplyDeployments(cmd.targetCtx.DeploymentCollection.Deployments) du := utils2.NewDiffUtil(dew, ru, au.GetAppliedObjectsMap()) - du.DiffDeploymentItems(cmd.c.Deployments) + du.DiffDeploymentItems(cmd.targetCtx.DeploymentCollection.Deployments) - orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) + orphanObjects, err := FindOrphanObjects(cmd.targetCtx.SharedContext.K, ru, cmd.targetCtx.DeploymentCollection) if err != nil { return nil, err } return &result.CommandResult{ Id: uuid.New().String(), - Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), + Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + SeenImages: cmd.targetCtx.DeploymentCollection.Images.SeenImages(false), }, nil } diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index dd4a4ecd3..7ce43d317 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -1,20 +1,17 @@ package commands import ( - "context" "fmt" "github.com/google/uuid" - "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/deployment/utils" - "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" ) type DiffCommand struct { - c *deployment.DeploymentCollection - discriminator string + targetCtx *kluctl_project.TargetContext ForceApply bool ReplaceOnError bool @@ -24,23 +21,22 @@ type DiffCommand struct { IgnoreAnnotations bool } -func NewDiffCommand(discriminator string, c *deployment.DeploymentCollection) *DiffCommand { +func NewDiffCommand(targetCtx *kluctl_project.TargetContext) *DiffCommand { return &DiffCommand{ - discriminator: discriminator, - c: c, + targetCtx: targetCtx, } } -func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.CommandResult, error) { +func (cmd *DiffCommand) Run() (*result.CommandResult, error) { dew := utils.NewDeploymentErrorsAndWarnings() - if cmd.discriminator == "" { - status.Warning(ctx, "No discriminator configured. Orphan object detection will not work") + if cmd.targetCtx.Target.Discriminator == "" { + status.Warning(cmd.targetCtx.SharedContext.Ctx, "No discriminator configured. Orphan object detection will not work") dew.AddWarning(k8s2.ObjectRef{}, fmt.Errorf("no discriminator configured. Orphan object detection will not work")) } - ru := utils.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, &cmd.discriminator, cmd.c.LocalObjectRefs(), false) + ru := utils.NewRemoteObjectsUtil(cmd.targetCtx.SharedContext.Ctx, dew) + err := ru.UpdateRemoteObjects(cmd.targetCtx.SharedContext.K, &cmd.targetCtx.Target.Discriminator, cmd.targetCtx.DeploymentCollection.LocalObjectRefs(), false) if err != nil { return nil, err } @@ -53,24 +49,24 @@ func (cmd *DiffCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.Com AbortOnError: false, ReadinessTimeout: 0, } - au := utils.NewApplyDeploymentsUtil(ctx, dew, ru, k, o) - au.ApplyDeployments(cmd.c.Deployments) + au := utils.NewApplyDeploymentsUtil(cmd.targetCtx.SharedContext.Ctx, dew, ru, cmd.targetCtx.SharedContext.K, o) + au.ApplyDeployments(cmd.targetCtx.DeploymentCollection.Deployments) du := utils.NewDiffUtil(dew, ru, au.GetAppliedObjectsMap()) du.IgnoreTags = cmd.IgnoreTags du.IgnoreLabels = cmd.IgnoreLabels du.IgnoreAnnotations = cmd.IgnoreAnnotations - du.DiffDeploymentItems(cmd.c.Deployments) + du.DiffDeploymentItems(cmd.targetCtx.DeploymentCollection.Deployments) - orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) + orphanObjects, err := FindOrphanObjects(cmd.targetCtx.SharedContext.K, ru, cmd.targetCtx.DeploymentCollection) if err != nil { return nil, err } return &result.CommandResult{ Id: uuid.New().String(), - Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), + Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + SeenImages: cmd.targetCtx.DeploymentCollection.Images.SeenImages(false), }, nil } diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 7144f2c2f..994b1bca1 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -1,12 +1,10 @@ package commands import ( - "context" "fmt" "github.com/google/uuid" - "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" - "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" @@ -15,35 +13,35 @@ import ( ) type PokeImagesCommand struct { - c *deployment.DeploymentCollection + targetCtx *kluctl_project.TargetContext } -func NewPokeImagesCommand(c *deployment.DeploymentCollection) *PokeImagesCommand { +func NewPokeImagesCommand(targetCtx *kluctl_project.TargetContext) *PokeImagesCommand { return &PokeImagesCommand{ - c: c, + targetCtx: targetCtx, } } -func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.CommandResult, error) { +func (cmd *PokeImagesCommand) Run() (*result.CommandResult, error) { var wg sync.WaitGroup dew := utils2.NewDeploymentErrorsAndWarnings() - ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, nil, cmd.c.LocalObjectRefs(), false) + ru := utils2.NewRemoteObjectsUtil(cmd.targetCtx.SharedContext.Ctx, dew) + err := ru.UpdateRemoteObjects(cmd.targetCtx.SharedContext.K, nil, cmd.targetCtx.DeploymentCollection.LocalObjectRefs(), false) if err != nil { return nil, err } allObjects := make(map[k8s2.ObjectRef]*uo.UnstructuredObject) - for _, d := range cmd.c.Deployments { + for _, d := range cmd.targetCtx.DeploymentCollection.Deployments { for _, o := range d.Objects { allObjects[o.GetK8sRef()] = o } } containersAndImages := make(map[k8s2.ObjectRef][]types.FixedImage) - for _, fi := range cmd.c.Images.SeenImages(false) { + for _, fi := range cmd.targetCtx.DeploymentCollection.Images.SeenImages(false) { _, ok := allObjects[*fi.Object] if !ok { dew.AddError(*fi.Object, fmt.Errorf("object not found while trying to associate image with deployed object")) @@ -72,7 +70,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu return o, nil } - au := utils2.NewApplyDeploymentsUtil(ctx, dew, ru, k, &utils2.ApplyUtilOptions{}) + au := utils2.NewApplyDeploymentsUtil(cmd.targetCtx.SharedContext.Ctx, dew, ru, cmd.targetCtx.SharedContext.K, &utils2.ApplyUtilOptions{}) for ref, containers := range containersAndImages { ref := ref @@ -80,7 +78,7 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu wg.Add(1) go func() { defer wg.Done() - au := au.NewApplyUtil(ctx, nil) + au := au.NewApplyUtil(cmd.targetCtx.SharedContext.Ctx, nil) remote := ru.GetRemoteObject(ref) if remote == nil { dew.AddWarning(ref, fmt.Errorf("remote object not found, skipped image replacement")) @@ -94,18 +92,18 @@ func (cmd *PokeImagesCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*resu wg.Wait() du := utils2.NewDiffUtil(dew, ru, au.GetAppliedObjectsMap()) - du.DiffDeploymentItems(cmd.c.Deployments) + du.DiffDeploymentItems(cmd.targetCtx.DeploymentCollection.Deployments) - orphanObjects, err := FindOrphanObjects(k, ru, cmd.c) + orphanObjects, err := FindOrphanObjects(cmd.targetCtx.SharedContext.K, ru, cmd.targetCtx.DeploymentCollection) if err != nil { return nil, err } return &result.CommandResult{ Id: uuid.New().String(), - Objects: collectObjects(cmd.c, ru, au, du, orphanObjects, nil), + Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), - SeenImages: cmd.c.Images.SeenImages(false), + SeenImages: cmd.targetCtx.DeploymentCollection.Images.SeenImages(false), }, nil } diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index e989b120c..595582298 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -1,42 +1,42 @@ package commands import ( - "context" "fmt" "github.com/google/uuid" "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" ) type PruneCommand struct { discriminator string - c *deployment.DeploymentCollection + targetCtx *kluctl_project.TargetContext } -func NewPruneCommand(discriminator string, c *deployment.DeploymentCollection) *PruneCommand { +func NewPruneCommand(discriminator string, targetCtx *kluctl_project.TargetContext) *PruneCommand { return &PruneCommand{ discriminator: discriminator, - c: c, + targetCtx: targetCtx, } } -func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb func(refs []k8s2.ObjectRef) error) (*result.CommandResult, error) { +func (cmd *PruneCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*result.CommandResult, error) { if cmd.discriminator == "" { return nil, fmt.Errorf("pruning without a discriminator is not supported") } dew := utils2.NewDeploymentErrorsAndWarnings() - ru := utils2.NewRemoteObjectsUtil(ctx, dew) - err := ru.UpdateRemoteObjects(k, &cmd.discriminator, nil, false) + ru := utils2.NewRemoteObjectsUtil(cmd.targetCtx.SharedContext.Ctx, dew) + err := ru.UpdateRemoteObjects(cmd.targetCtx.SharedContext.K, &cmd.discriminator, nil, false) if err != nil { return nil, err } - deleteRefs, err := FindOrphanObjects(k, ru, cmd.c) + deleteRefs, err := FindOrphanObjects(cmd.targetCtx.SharedContext.K, ru, cmd.targetCtx.DeploymentCollection) if err != nil { return nil, err } @@ -48,14 +48,14 @@ func (cmd *PruneCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb f } } - deleted, err := utils2.DeleteObjects(ctx, k, deleteRefs, dew, true) + deleted, err := utils2.DeleteObjects(cmd.targetCtx.SharedContext.Ctx, cmd.targetCtx.SharedContext.K, deleteRefs, dew, true) if err != nil { return nil, err } return &result.CommandResult{ Id: uuid.New().String(), - Objects: collectObjects(cmd.c, ru, nil, nil, nil, deleted), + Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, nil, nil, nil, deleted), Warnings: dew.GetWarningsList(), }, nil } diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index c431fc0e0..453bfa022 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -21,7 +21,7 @@ type TargetContext struct { SharedContext deployment.SharedContext KluctlProject *LoadedKluctlProject - Target *types.Target + Target types.Target ClusterContext string DeploymentProject *deployment.DeploymentProject DeploymentCollection *deployment.DeploymentCollection From 229bc58bde2dc9d84bbec1f81721d92a61901458 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 12 May 2023 17:04:16 +0200 Subject: [PATCH 0972/2268] refactor: Move command info gathering into command objects --- cmd/kluctl/commands/cmd_delete.go | 6 - cmd/kluctl/commands/cmd_deploy.go | 10 +- cmd/kluctl/commands/cmd_diff.go | 6 - cmd/kluctl/commands/cmd_poke_images.go | 6 - cmd/kluctl/commands/cmd_prune.go | 10 +- cmd/kluctl/commands/cmd_validate.go | 4 +- cmd/kluctl/commands/command_result.go | 2 + cmd/kluctl/commands/utils.go | 164 +----------------------- pkg/deployment/commands/delete.go | 9 +- pkg/deployment/commands/deploy.go | 14 +- pkg/deployment/commands/diff.go | 12 +- pkg/deployment/commands/poke_images.go | 9 +- pkg/deployment/commands/prune.go | 17 ++- pkg/deployment/commands/result_utils.go | 134 +++++++++++++++++++ pkg/deployment/commands/validate.go | 2 +- pkg/deployment/images.go | 10 ++ pkg/kluctl_project/load.go | 2 + pkg/kluctl_project/project.go | 2 + pkg/kluctl_project/target_context.go | 5 +- pkg/results/result-store-secrets.go | 14 +- pkg/types/result/command_result.go | 7 +- pkg/types/result/summary.go | 4 +- pkg/utils/inclusion.go | 26 ++++ 23 files changed, 253 insertions(+), 222 deletions(-) create mode 100644 pkg/deployment/commands/result_utils.go diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index 28aafcd2d..f485c134c 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -7,7 +7,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/status" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "time" ) type deleteCmd struct { @@ -46,7 +45,6 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { renderOutputDirFlags: cmd.RenderOutputDirFlags, commandResultFlags: &cmd.CommandResultFlags, } - startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { cmd2 := commands.NewDeleteCommand(cmd.Discriminator, cmdCtx.targetCtx) @@ -56,10 +54,6 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { if err != nil { return err } - err = addCommandInfo(result, startTime, "delete", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) - if err != nil { - return err - } err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, !cmd.DryRun || cmd.ForceWriteCommandResult) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index da8f92439..e292dc27d 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -7,7 +7,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types/result" - "time" ) type deployCmd struct { @@ -49,13 +48,12 @@ func (cmd *deployCmd) Run(ctx context.Context) error { renderOutputDirFlags: cmd.RenderOutputDirFlags, commandResultFlags: &cmd.CommandResultFlags, } - startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - return cmd.runCmdDeploy(cmdCtx, startTime) + return cmd.runCmdDeploy(cmdCtx) }) } -func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx, startTime time.Time) error { +func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { status.Trace(cmdCtx.ctx, "enter runCmdDeploy") defer status.Trace(cmdCtx.ctx, "leave runCmdDeploy") @@ -78,10 +76,6 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx, startTime time.Time) erro if err != nil { return err } - err = addCommandInfo(result, startTime, "deploy", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, &cmd.ForceApplyFlags, &cmd.ReplaceOnErrorFlags, &cmd.AbortOnErrorFlags, cmd.NoWait) - if err != nil { - return err - } err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, !cmd.DryRun || cmd.ForceWriteCommandResult) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_diff.go b/cmd/kluctl/commands/cmd_diff.go index 429d61b39..c0801e80c 100644 --- a/cmd/kluctl/commands/cmd_diff.go +++ b/cmd/kluctl/commands/cmd_diff.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" - "time" ) type diffCmd struct { @@ -39,7 +38,6 @@ func (cmd *diffCmd) Run(ctx context.Context) error { helmCredentials: cmd.HelmCredentials, renderOutputDirFlags: cmd.RenderOutputDirFlags, } - startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { cmd2 := commands.NewDiffCommand(cmdCtx.targetCtx) cmd2.ForceApply = cmd.ForceApply @@ -52,10 +50,6 @@ func (cmd *diffCmd) Run(ctx context.Context) error { if err != nil { return err } - err = addCommandInfo(result, startTime, "diff", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, nil, &cmd.ForceApplyFlags, &cmd.ReplaceOnErrorFlags, nil, false) - if err != nil { - return err - } err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, false) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_poke_images.go b/cmd/kluctl/commands/cmd_poke_images.go index 4d3f2a982..35df34c91 100644 --- a/cmd/kluctl/commands/cmd_poke_images.go +++ b/cmd/kluctl/commands/cmd_poke_images.go @@ -6,7 +6,6 @@ import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" "github.com/kluctl/kluctl/v2/pkg/status" - "time" ) type pokeImagesCmd struct { @@ -41,7 +40,6 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { renderOutputDirFlags: cmd.RenderOutputDirFlags, commandResultFlags: &cmd.CommandResultFlags, } - startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { if !cmd.Yes && !cmd.DryRun { if !status.AskForConfirmation(ctx, fmt.Sprintf("Do you really want to poke images to the context/cluster %s?", cmdCtx.targetCtx.ClusterContext)) { @@ -55,10 +53,6 @@ func (cmd *pokeImagesCmd) Run(ctx context.Context) error { if err != nil { return err } - err = addCommandInfo(result, startTime, "poke-images", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) - if err != nil { - return err - } err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, !cmd.DryRun || cmd.ForceWriteCommandResult) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 43b785ed1..9e3eea413 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -6,7 +6,6 @@ import ( "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment/commands" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "time" ) type pruneCmd struct { @@ -43,13 +42,12 @@ func (cmd *pruneCmd) Run(ctx context.Context) error { renderOutputDirFlags: cmd.RenderOutputDirFlags, commandResultFlags: &cmd.CommandResultFlags, } - startTime := time.Now() return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - return cmd.runCmdPrune(cmdCtx, startTime) + return cmd.runCmdPrune(cmdCtx) }) } -func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx, startTime time.Time) error { +func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx) result, err := cmd2.Run(func(refs []k8s2.ObjectRef) error { return confirmDeletion(cmdCtx.ctx, refs, cmd.DryRun, cmd.Yes) @@ -57,10 +55,6 @@ func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx, startTime time.Time) error if err != nil { return err } - err = addCommandInfo(result, startTime, "prune", cmdCtx, &cmd.TargetFlags, &cmd.ImageFlags, &cmd.InclusionFlags, &cmd.DryRunFlags, nil, nil, nil, false) - if err != nil { - return err - } err = outputCommandResult(cmdCtx, cmd.OutputFormatFlags, result, !cmd.DryRun || cmd.ForceWriteCommandResult) if err != nil { return err diff --git a/cmd/kluctl/commands/cmd_validate.go b/cmd/kluctl/commands/cmd_validate.go index 03471c507..80387c674 100644 --- a/cmd/kluctl/commands/cmd_validate.go +++ b/cmd/kluctl/commands/cmd_validate.go @@ -54,8 +54,8 @@ func (cmd *validateCmd) Run(ctx context.Context) error { var k8sContext *string if cmd.Context != "" { k8sContext = &cmd.Context - } else if commandResult.Command.Target != nil { - k8sContext = commandResult.Command.Target.Context + } else if commandResult.Target.Context != nil { + k8sContext = commandResult.Target.Context } clientFactory, err := k8s2.NewClientFactoryFromDefaultConfig(ctx, k8sContext) if err != nil { diff --git a/cmd/kluctl/commands/command_result.go b/cmd/kluctl/commands/command_result.go index 25e4a9e5e..a08e79722 100644 --- a/cmd/kluctl/commands/command_result.go +++ b/cmd/kluctl/commands/command_result.go @@ -240,6 +240,8 @@ func outputHelper(ctx context.Context, output []string, cb func(format string) ( func outputCommandResult(ctx *commandCtx, flags args.OutputFormatFlags, cr *result.CommandResult, writeToResultStore bool) error { status.Flush(ctx.ctx) + cr.Command.Initiator = result.CommandInititiator_CommandLine + if !flags.NoObfuscate { var obfuscator diff.Obfuscator err := obfuscator.ObfuscateResult(cr) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 046f5609c..cd2e6a923 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -3,16 +3,6 @@ package commands import ( "context" "fmt" - git2 "github.com/go-git/go-git/v5" - "github.com/kluctl/kluctl/v2/pkg/results" - "github.com/kluctl/kluctl/v2/pkg/types" - "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/types/result" - "os" - "path/filepath" - "strings" - "time" - "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/deployment" "github.com/kluctl/kluctl/v2/pkg/git" @@ -22,12 +12,16 @@ import ( "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/repocache" + "github.com/kluctl/kluctl/v2/pkg/results" "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" + "os" + "strings" ) func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFlags, argsFlags *args.ArgsFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { @@ -225,156 +219,6 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm return cb(cmdCtx) } -func addCommandInfo(r *result.CommandResult, startTime time.Time, command string, ctx *commandCtx, targetFlags *args.TargetFlags, - imageFlags *args.ImageFlags, inclusionFlags *args.InclusionFlags, - dryRunFlags *args.DryRunFlags, forceApplyFlags *args.ForceApplyFlags, replaceOnErrorFlags *args.ReplaceOnErrorFlags, abortOnErrorFlags *args.AbortOnErrorFlags, noWait bool) error { - r.Command = result.CommandInfo{ - Initiator: result.CommandInititiator_CommandLine, - StartTime: types.FromTime(startTime), - EndTime: types.FromTime(time.Now()), - Command: command, - Target: ctx.targetCtx.Target, - Args: ctx.targetCtx.KluctlProject.LoadArgs.ExternalArgs, - NoWait: noWait, - } - if targetFlags != nil { - r.Command.TargetNameOverride = targetFlags.TargetNameOverride - r.Command.ContextOverride = targetFlags.Context - } - if imageFlags != nil { - var err error - r.Command.Images, err = imageFlags.LoadFixedImagesFromArgs() - if err != nil { - return err - } - } - if inclusionFlags != nil { - r.Command.IncludeTags = inclusionFlags.IncludeTag - r.Command.ExcludeTags = inclusionFlags.ExcludeTag - r.Command.IncludeDeploymentDirs = inclusionFlags.IncludeDeploymentDir - r.Command.ExcludeDeploymentDirs = inclusionFlags.ExcludeDeploymentDir - } - if dryRunFlags != nil { - r.Command.DryRun = dryRunFlags.DryRun - } - if forceApplyFlags != nil { - r.Command.ForceApply = forceApplyFlags.ForceApply - } - if replaceOnErrorFlags != nil { - r.Command.ReplaceOnError = replaceOnErrorFlags.ReplaceOnError - r.Command.ForceReplaceOnError = replaceOnErrorFlags.ForceReplaceOnError - } - if abortOnErrorFlags != nil { - r.Command.AbortOnError = abortOnErrorFlags.AbortOnError - } - r.Deployment = &ctx.targetCtx.DeploymentProject.Config - - err := addGitInfo(r, ctx) - if err != nil { - return err - } - - err = addClusterInfo(r, ctx) - if err != nil { - return err - } - - if ctx.targetCtx.Target != nil { - r.Target.TargetName = ctx.targetCtx.Target.Name - r.Target.Discriminator = ctx.targetCtx.Target.Discriminator - } - r.Target.ClusterId = r.ClusterInfo.ClusterId - - return nil -} - -func addGitInfo(r *result.CommandResult, ctx *commandCtx) error { - if ctx.targetCtx.KluctlProject.LoadArgs.RepoRoot == "" { - return nil - } - - projectDirAbs, err := filepath.Abs(ctx.targetCtx.KluctlProject.LoadArgs.ProjectDir) - if err != nil { - return err - } - - subDir, err := filepath.Rel(ctx.targetCtx.KluctlProject.LoadArgs.RepoRoot, projectDirAbs) - if err != nil { - return err - } - if subDir == "." { - subDir = "" - } - - g, err := git2.PlainOpen(ctx.targetCtx.KluctlProject.LoadArgs.RepoRoot) - if err != nil { - return err - } - - w, err := g.Worktree() - if err != nil { - return err - } - - s, err := w.Status() - if err != nil { - return err - } - - head, err := g.Head() - if err != nil { - return err - } - - remotes, err := g.Remotes() - if err != nil { - return err - } - - var originUrl *types.GitUrl - for _, r := range remotes { - if r.Config().Name == "origin" { - originUrl, err = types.ParseGitUrl(r.Config().URLs[0]) - if err != nil { - return err - } - } - } - - var normaliedUrl string - if originUrl != nil { - normaliedUrl = originUrl.NormalizedRepoKey() - } - - r.GitInfo = &result.GitInfo{ - Url: originUrl, - Ref: head.Name().String(), - SubDir: subDir, - Commit: head.Hash().String(), - Dirty: !s.IsClean(), - } - r.Project.NormalizedGitUrl = normaliedUrl - r.Project.SubDir = subDir - return nil -} - -func addClusterInfo(r *result.CommandResult, ctx *commandCtx) error { - kubeSystemNs, _, err := ctx.targetCtx.SharedContext.K.GetSingleObject( - k8s.NewObjectRef("", "v1", "Namespace", "kube-system", "")) - if err != nil { - return err - } - // we reuse the kube-system namespace uid as global cluster id - clusterId := kubeSystemNs.GetK8sUid() - if clusterId == "" { - return fmt.Errorf("kube-system namespace has no uid") - } - r.ClusterInfo = result.ClusterInfo{ - ClusterId: clusterId, - } - return nil -} - func clientConfigGetter(forCompletion bool) func(context *string) (*rest.Config, *api.Config, error) { return func(context *string) (*rest.Config, *api.Config, error) { if forCompletion { diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index 349028e96..ffd08dae1 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -55,10 +55,15 @@ func (cmd *DeleteCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*res return nil, err } - return &result.CommandResult{ + r := &result.CommandResult{ Id: uuid.New().String(), Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, nil, nil, nil, deleted), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), - }, nil + } + err = addBaseCommandInfoToResult(cmd.targetCtx, r, "delete") + if err != nil { + return r, err + } + return r, nil } diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index 291d05464..f2fd0a5b7 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -92,11 +92,21 @@ func (cmd *DeployCommand) Run(diffResultCb func(diffResult *result.CommandResult if err != nil { return nil, err } - return &result.CommandResult{ + r := &result.CommandResult{ Id: uuid.New().String(), Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), SeenImages: cmd.targetCtx.DeploymentCollection.Images.SeenImages(false), - }, nil + } + r.Command.ForceApply = cmd.ForceApply + r.Command.ReplaceOnError = cmd.ReplaceOnError + r.Command.ForceReplaceOnError = cmd.ForceReplaceOnError + r.Command.AbortOnError = cmd.AbortOnError + r.Command.NoWait = cmd.NoWait + err = addBaseCommandInfoToResult(cmd.targetCtx, r, "deploy") + if err != nil { + return r, err + } + return r, nil } diff --git a/pkg/deployment/commands/diff.go b/pkg/deployment/commands/diff.go index 7ce43d317..188937786 100644 --- a/pkg/deployment/commands/diff.go +++ b/pkg/deployment/commands/diff.go @@ -62,11 +62,19 @@ func (cmd *DiffCommand) Run() (*result.CommandResult, error) { if err != nil { return nil, err } - return &result.CommandResult{ + r := &result.CommandResult{ Id: uuid.New().String(), Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), SeenImages: cmd.targetCtx.DeploymentCollection.Images.SeenImages(false), - }, nil + } + r.Command.ForceApply = cmd.ForceApply + r.Command.ReplaceOnError = cmd.ReplaceOnError + r.Command.ForceReplaceOnError = cmd.ForceReplaceOnError + err = addBaseCommandInfoToResult(cmd.targetCtx, r, "diff") + if err != nil { + return r, err + } + return r, nil } diff --git a/pkg/deployment/commands/poke_images.go b/pkg/deployment/commands/poke_images.go index 994b1bca1..5197687b3 100644 --- a/pkg/deployment/commands/poke_images.go +++ b/pkg/deployment/commands/poke_images.go @@ -99,11 +99,16 @@ func (cmd *PokeImagesCommand) Run() (*result.CommandResult, error) { return nil, err } - return &result.CommandResult{ + r := &result.CommandResult{ Id: uuid.New().String(), Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, au, du, orphanObjects, nil), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), SeenImages: cmd.targetCtx.DeploymentCollection.Images.SeenImages(false), - }, nil + } + err = addBaseCommandInfoToResult(cmd.targetCtx, r, "deploy") + if err != nil { + return r, err + } + return r, nil } diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index 595582298..1c3bed093 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -24,14 +24,18 @@ func NewPruneCommand(discriminator string, targetCtx *kluctl_project.TargetConte } func (cmd *PruneCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*result.CommandResult, error) { - if cmd.discriminator == "" { + discriminator := cmd.discriminator + if discriminator == "" && cmd.targetCtx != nil { + discriminator = cmd.targetCtx.Target.Discriminator + } + if discriminator == "" { return nil, fmt.Errorf("pruning without a discriminator is not supported") } dew := utils2.NewDeploymentErrorsAndWarnings() ru := utils2.NewRemoteObjectsUtil(cmd.targetCtx.SharedContext.Ctx, dew) - err := ru.UpdateRemoteObjects(cmd.targetCtx.SharedContext.K, &cmd.discriminator, nil, false) + err := ru.UpdateRemoteObjects(cmd.targetCtx.SharedContext.K, &discriminator, nil, false) if err != nil { return nil, err } @@ -53,11 +57,16 @@ func (cmd *PruneCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*resu return nil, err } - return &result.CommandResult{ + r := &result.CommandResult{ Id: uuid.New().String(), Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, nil, nil, nil, deleted), Warnings: dew.GetWarningsList(), - }, nil + } + err = addBaseCommandInfoToResult(cmd.targetCtx, r, "prune") + if err != nil { + return r, err + } + return r, nil } func FindOrphanObjects(k *k8s.K8sCluster, ru *utils2.RemoteObjectUtils, c *deployment.DeploymentCollection) ([]k8s2.ObjectRef, error) { diff --git a/pkg/deployment/commands/result_utils.go b/pkg/deployment/commands/result_utils.go new file mode 100644 index 000000000..dba10686f --- /dev/null +++ b/pkg/deployment/commands/result_utils.go @@ -0,0 +1,134 @@ +package commands + +import ( + "fmt" + git2 "github.com/go-git/go-git/v5" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "path/filepath" + "time" +) + +func addBaseCommandInfoToResult(targetCtx *kluctl_project.TargetContext, r *result.CommandResult, command string) error { + r.Target = targetCtx.Target + r.Command = result.CommandInfo{ + StartTime: types.FromTime(targetCtx.KluctlProject.LoadTime), + EndTime: types.FromTime(time.Now()), + Command: command, + Args: targetCtx.KluctlProject.LoadArgs.ExternalArgs, + } + r.Command.TargetNameOverride = targetCtx.Params.TargetNameOverride + r.Command.ContextOverride = targetCtx.Params.ContextOverride + r.Command.Images = targetCtx.Params.Images.FixedImages() + r.Command.IncludeTags = targetCtx.Params.Inclusion.GetIncludes("tags") + r.Command.ExcludeTags = targetCtx.Params.Inclusion.GetExcludes("tags") + r.Command.IncludeDeploymentDirs = targetCtx.Params.Inclusion.GetIncludes("deploymentItemDir") + r.Command.ExcludeDeploymentDirs = targetCtx.Params.Inclusion.GetExcludes("deploymentItemDir") + r.Command.DryRun = targetCtx.Params.DryRun + + r.Deployment = &targetCtx.DeploymentProject.Config + + err := addGitInfo(targetCtx, r) + if err != nil { + return err + } + + err = addClusterInfo(targetCtx, r) + if err != nil { + return err + } + + r.TargetKey.TargetName = targetCtx.Target.Name + r.TargetKey.Discriminator = targetCtx.Target.Discriminator + r.TargetKey.ClusterId = r.ClusterInfo.ClusterId + return nil +} + +func addGitInfo(targetCtx *kluctl_project.TargetContext, r *result.CommandResult) error { + if targetCtx.KluctlProject.LoadArgs.RepoRoot == "" { + return nil + } + + projectDirAbs, err := filepath.Abs(targetCtx.KluctlProject.LoadArgs.ProjectDir) + if err != nil { + return err + } + + subDir, err := filepath.Rel(targetCtx.KluctlProject.LoadArgs.RepoRoot, projectDirAbs) + if err != nil { + return err + } + if subDir == "." { + subDir = "" + } + + g, err := git2.PlainOpen(targetCtx.KluctlProject.LoadArgs.RepoRoot) + if err != nil { + return err + } + + w, err := g.Worktree() + if err != nil { + return err + } + + s, err := w.Status() + if err != nil { + return err + } + + head, err := g.Head() + if err != nil { + return err + } + + remotes, err := g.Remotes() + if err != nil { + return err + } + + var originUrl *types.GitUrl + for _, r := range remotes { + if r.Config().Name == "origin" { + originUrl, err = types.ParseGitUrl(r.Config().URLs[0]) + if err != nil { + return err + } + } + } + + var normaliedUrl string + if originUrl != nil { + normaliedUrl = originUrl.NormalizedRepoKey() + } + + r.GitInfo = &result.GitInfo{ + Url: originUrl, + Ref: head.Name().String(), + SubDir: subDir, + Commit: head.Hash().String(), + Dirty: !s.IsClean(), + } + r.ProjectKey.NormalizedGitUrl = normaliedUrl + r.ProjectKey.SubDir = subDir + return nil +} + +func addClusterInfo(targetCtx *kluctl_project.TargetContext, r *result.CommandResult) error { + kubeSystemNs, _, err := targetCtx.SharedContext.K.GetSingleObject( + k8s.NewObjectRef("", "v1", "Namespace", "kube-system", "")) + if err != nil { + return err + } + // we reuse the kube-system namespace uid as global cluster id + clusterId := kubeSystemNs.GetK8sUid() + if clusterId == "" { + return fmt.Errorf("kube-system namespace has no uid") + } + r.ClusterInfo = result.ClusterInfo{ + ClusterId: clusterId, + } + return nil +} diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 32a3dda68..1a1fee162 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -69,7 +69,7 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result appliedObjects[o.Ref] = o.Applied } } - discriminator = cmd.r.Target.Discriminator + discriminator = cmd.r.TargetKey.Discriminator } else { return nil, fmt.Errorf("either deployment collection or command result must be passed") } diff --git a/pkg/deployment/images.go b/pkg/deployment/images.go index f9e6df475..5eb899b59 100644 --- a/pkg/deployment/images.go +++ b/pkg/deployment/images.go @@ -38,7 +38,17 @@ func (images *Images) PrependFixedImages(fis []types.FixedImage) { images.fixedImages = newFixedImages } +func (images *Images) FixedImages() []types.FixedImage { + if images == nil { + return nil + } + return images.fixedImages +} + func (images *Images) SeenImages(simple bool) []types.FixedImage { + if images == nil { + return nil + } var ret []types.FixedImage for _, fi := range images.seenImages { if simple { diff --git a/pkg/kluctl_project/load.go b/pkg/kluctl_project/load.go index 0535cbc61..5c98eff20 100644 --- a/pkg/kluctl_project/load.go +++ b/pkg/kluctl_project/load.go @@ -5,6 +5,7 @@ import ( "github.com/kluctl/go-jinja2" "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "github.com/kluctl/kluctl/v2/pkg/status" + "time" ) func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir string, j2 *jinja2.Jinja2) (*LoadedKluctlProject, error) { @@ -14,6 +15,7 @@ func LoadKluctlProject(ctx context.Context, args LoadKluctlProjectArgs, tmpDir s p := &LoadedKluctlProject{ ctx: ctx, LoadArgs: args, + LoadTime: time.Now(), TmpDir: tmpDir, J2: j2, RP: args.RP, diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 4fe0161ad..ad2d230c9 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -7,12 +7,14 @@ import ( "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" types2 "github.com/kluctl/kluctl/v2/pkg/types" + "time" ) type LoadedKluctlProject struct { ctx context.Context LoadArgs LoadKluctlProjectArgs + LoadTime time.Time TmpDir string diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 453bfa022..93d92ca94 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -18,6 +18,8 @@ import ( ) type TargetContext struct { + Params TargetContextParams + SharedContext deployment.SharedContext KluctlProject *LoadedKluctlProject @@ -138,9 +140,10 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe } targetCtx := &TargetContext{ + Params: params, SharedContext: dctx, KluctlProject: p, - Target: target, + Target: *target, ClusterContext: clusterContext, DeploymentProject: d, DeploymentCollection: c, diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 206c3c2d0..ab1db402a 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -99,8 +99,8 @@ var invalidChars = regexp.MustCompile(`[^a-zA-Z0-9-]`) func (s *ResultStoreSecrets) buildName(cr *result.CommandResult) string { var name string - if cr.Project.NormalizedGitUrl != "" { - s := path.Base(cr.Project.NormalizedGitUrl) + if cr.ProjectKey.NormalizedGitUrl != "" { + s := path.Base(cr.ProjectKey.NormalizedGitUrl) if s != "" { name = s + "-" } @@ -197,11 +197,11 @@ func (s *ResultStoreSecrets) WriteCommandResult(cr *result.CommandResult) error "compactedObjects": compressedObjects, }, } - if cr.Project.NormalizedGitUrl != "" { - secret.Annotations["kluctl.io/result-project-normalized-url"] = cr.Project.NormalizedGitUrl + if cr.ProjectKey.NormalizedGitUrl != "" { + secret.Annotations["kluctl.io/result-project-normalized-url"] = cr.ProjectKey.NormalizedGitUrl } - if cr.Project.SubDir != "" { - secret.Annotations["kluctl.io/result-project-subdir"] = cr.Project.SubDir + if cr.ProjectKey.SubDir != "" { + secret.Annotations["kluctl.io/result-project-subdir"] = cr.ProjectKey.SubDir } err = s.client.Patch(s.ctx, &secret, client.Apply, client.FieldOwner("kluctl-results")) @@ -209,7 +209,7 @@ func (s *ResultStoreSecrets) WriteCommandResult(cr *result.CommandResult) error return err } - err = s.cleanupResults(cr.Project, cr.Target) + err = s.cleanupResults(cr.ProjectKey, cr.TargetKey) if err != nil { return err } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 4d2ec8dcb..141bd055d 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -78,7 +78,7 @@ type CommandInfo struct { EndTime types.JsonTime `json:"endTime"` KluctlDeployment *KluctlDeploymentInfo `json:"kluctlDeployment,omitempty"` Command string `json:"command,omitempty"` - Target *types.Target `json:"target,omitempty"` + Target string `json:"target,omitempty"` TargetNameOverride string `json:"targetNameOverride,omitempty"` ContextOverride string `json:"contextOverride,omitempty"` Args *uo.UnstructuredObject `json:"args,omitempty"` @@ -127,8 +127,9 @@ type ResultObject struct { type CommandResult struct { Id string `json:"id"` - Project ProjectKey `json:"project"` - Target TargetKey `json:"target"` + ProjectKey ProjectKey `json:"projectKey"` + TargetKey TargetKey `json:"targetKey"` + Target types.Target `json:"target"` Command CommandInfo `json:"command,omitempty"` GitInfo *GitInfo `json:"gitInfo,omitempty"` ClusterInfo ClusterInfo `json:"clusterInfo"` diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index 900645e66..dc05e095a 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -53,8 +53,8 @@ func (cr *CommandResult) BuildSummary() *CommandResultSummary { ret := &CommandResultSummary{ Id: cr.Id, - Project: cr.Project, - Target: cr.Target, + Project: cr.ProjectKey, + Target: cr.TargetKey, Command: cr.Command, GitInfo: cr.GitInfo, ClusterInfo: cr.ClusterInfo, diff --git a/pkg/utils/inclusion.go b/pkg/utils/inclusion.go index 5300f269f..41de49e69 100644 --- a/pkg/utils/inclusion.go +++ b/pkg/utils/inclusion.go @@ -42,6 +42,32 @@ func (inc *Inclusion) HasType(typ string) bool { return false } +func (inc *Inclusion) GetIncludes(typ string) []string { + if inc == nil { + return nil + } + var ret []string + for e, _ := range inc.includes { + if e.Type == typ { + ret = append(ret, e.Value) + } + } + return ret +} + +func (inc *Inclusion) GetExcludes(typ string) []string { + if inc == nil { + return nil + } + var ret []string + for e, _ := range inc.excludes { + if e.Type == typ { + ret = append(ret, e.Value) + } + } + return ret +} + func (inc *Inclusion) checkList(l []InclusionEntry, m map[InclusionEntry]bool) bool { for _, e := range l { if _, ok := m[e]; ok { From edb5bf7eab38a89a9bb22502a3745646adc383e0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 14 May 2023 22:27:37 +0200 Subject: [PATCH 0973/2268] refactor: Get rid of fluxcd dependency --- pkg/deployment/deployment_item.go | 4 +- .../flux_utils/kustomize/filesys/fs_secure.go | 286 ++++++++++++++++++ .../kustomize/kustomize_generator.go | 81 +++++ 3 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 pkg/utils/flux_utils/kustomize/filesys/fs_secure.go create mode 100644 pkg/utils/flux_utils/kustomize/kustomize_generator.go diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index f3cb14777..982be5d05 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -2,14 +2,14 @@ package deployment import ( "fmt" - "github.com/fluxcd/pkg/kustomize" - securefs "github.com/fluxcd/pkg/kustomize/filesys" "github.com/hashicorp/go-multierror" "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/sops" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/kustomize" + securefs "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/kustomize/filesys" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" diff --git a/pkg/utils/flux_utils/kustomize/filesys/fs_secure.go b/pkg/utils/flux_utils/kustomize/filesys/fs_secure.go new file mode 100644 index 000000000..267d72616 --- /dev/null +++ b/pkg/utils/flux_utils/kustomize/filesys/fs_secure.go @@ -0,0 +1,286 @@ +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package filesys + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +var ( + // tmpConfirmedDirPrefix is the prefix used by filesys.NewTmpConfirmedDir(), + // the absolute prefix is the value prefixed with os.TempDir(). + tmpConfirmedDirPrefix = "kustomize-" +) + +// MakeFsOnDiskSecure returns a secure file system which asserts any paths it +// handles to be inside root. When allowed prefixes are provided, followed +// absolute links are allowed to traverse into these directories. +// The root is not allowed to match an allowed prefix, this to ensure an FS +// instance can not reach into another FS when the allowed prefix configuration +// is static. +func MakeFsOnDiskSecure(root string, allowPrefixes ...string) (filesys.FileSystem, error) { + unsafeFS := filesys.MakeFsOnDisk() + cleanedAbs, _, err := unsafeFS.CleanedAbs(root) + if err != nil { + return nil, err + } + if ok, prefix := hasOneOfPrefixes(cleanedAbs.String(), allowPrefixes); ok { + return nil, fmt.Errorf("root '%s' cannot be prefixed with '%s'", root, prefix) + } + return fsSecure{root: cleanedAbs, unsafeFS: unsafeFS, allowPrefixes: allowPrefixes}, nil +} + +// TmpConfirmedDirPrefix returns the absolute prefix path as constructed by +// filesys.NewTmpConfirmedDir(). +func TmpConfirmedDirPrefix() (string, error) { + // Some OS-es seem to symlink TempDir. + evaluated, err := filepath.EvalSymlinks(os.TempDir()) + if err != nil { + return "", err + } + return filepath.Join(evaluated, tmpConfirmedDirPrefix), nil +} + +// MakeFsOnDiskSecureBuild calls MakeFsOnDiskSecure, but configures the +// TmpConfirmedDirPrefix as an allowed prefix. +// NOTE: When e.g. being able to load remote bases is not a concern, opt to use +// MakeFsOnDiskSecure instead, unless running into specific traversal issues. +func MakeFsOnDiskSecureBuild(root string, allowPrefixes ...string) (filesys.FileSystem, error) { + dirPrefix, err := TmpConfirmedDirPrefix() + if err != nil { + return nil, err + } + allowPrefixes = append([]string{dirPrefix}, allowPrefixes...) + return MakeFsOnDiskSecure(root, allowPrefixes...) +} + +// fsSecure wraps an unsafe FileSystem implementation, and secures it +// by confirming paths are inside root. +type fsSecure struct { + root filesys.ConfirmedDir + unsafeFS filesys.FileSystem + allowPrefixes []string +} + +// ConstraintError records an error and the operation and file that +// violated it. +type ConstraintError struct { + Op string + Path string + Err error +} + +func (e *ConstraintError) Error() string { + return "fs-security-constraint " + e.Op + " " + e.Path + ": " + e.Err.Error() +} + +func (e *ConstraintError) Unwrap() error { return e.Err } + +// Create delegates to the embedded unsafe FS after having confirmed the path +// to be inside root. If the provided path violates this constraint, an error +// of type ConstraintError is returned. +func (fs fsSecure) Create(path string) (filesys.File, error) { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return nil, &ConstraintError{Op: "create", Path: path, Err: err} + } + return fs.unsafeFS.Create(path) +} + +// Mkdir delegates to the embedded unsafe FS after having confirmed the path +// to be inside root. If the provided path violates this constraint, an error +// of type ConstraintError is returned. +func (fs fsSecure) Mkdir(path string) error { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return &ConstraintError{Op: "mkdir", Path: path, Err: err} + } + return fs.unsafeFS.Mkdir(path) +} + +// MkdirAll delegates to the embedded unsafe FS after having confirmed the path +// to be inside root. If the provided path violates this constraint, an error +// type ConstraintError is returned. +func (fs fsSecure) MkdirAll(path string) error { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return &ConstraintError{Op: "mkdir", Path: path, Err: err} + } + return fs.unsafeFS.MkdirAll(path) +} + +// RemoveAll delegates to the embedded unsafe FS after having confirmed the +// path to be inside root. If the provided path violates this constraint, an +// error of type ConstraintError is returned. +func (fs fsSecure) RemoveAll(path string) error { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return &ConstraintError{Op: "remove", Path: path, Err: err} + } + return fs.unsafeFS.RemoveAll(path) +} + +// Open delegates to the embedded unsafe FS after having confirmed the path +// to be inside root. If the provided path violates this constraint, an error +// of type ConstraintError is returned. +func (fs fsSecure) Open(path string) (filesys.File, error) { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return nil, &ConstraintError{Op: "open", Path: path, Err: err} + } + return fs.unsafeFS.Open(path) +} + +// IsDir delegates to the embedded unsafe FS after having confirmed the path +// to be inside root. If the provided path violates this constraint, it returns +// false. +func (fs fsSecure) IsDir(path string) bool { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return false + } + return fs.unsafeFS.IsDir(path) +} + +// ReadDir delegates to the embedded unsafe FS after having confirmed the path +// to be inside root. If the provided path violates this constraint, an error +// of type ConstraintError is returned. +func (fs fsSecure) ReadDir(path string) ([]string, error) { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return nil, &ConstraintError{Op: "open", Path: path, Err: err} + } + return fs.unsafeFS.ReadDir(path) +} + +// CleanedAbs delegates to the embedded unsafe FS, but confirms the returned +// result to be within root. If the results violates this constraint, an error +// of type ConstraintError is returned. +// In essence, it functions the same as Kustomize's loader.RestrictionRootOnly, +// but on FS levels, and while allowing file paths. +func (fs fsSecure) CleanedAbs(path string) (filesys.ConfirmedDir, string, error) { + d, f, err := fs.unsafeFS.CleanedAbs(path) + if err != nil { + return d, f, err + } + if ok, _ := hasOneOfPrefixes(d.String(), fs.allowPrefixes); ok { + return d, f, err + } + if !d.HasPrefix(fs.root) { + return "", "", &ConstraintError{Op: "abs", Path: path, Err: rootConstraintErr(path, fs.root.String())} + } + return d, f, err +} + +// Exists delegates to the embedded unsafe FS after having confirmed the path +// to be inside root. If the provided path violates this constraint, it returns +// false. +func (fs fsSecure) Exists(path string) bool { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return false + } + return fs.unsafeFS.Exists(path) +} + +// Glob delegates to the embedded unsafe FS, but filters the returned paths to +// only include items inside root. +func (fs fsSecure) Glob(pattern string) ([]string, error) { + paths, err := fs.unsafeFS.Glob(pattern) + if err != nil { + return nil, err + } + var securePaths []string + for _, p := range paths { + if err := isSecurePath(fs.unsafeFS, fs.root, p, fs.allowPrefixes...); err == nil { + securePaths = append(securePaths, p) + } + } + return securePaths, err +} + +// ReadFile delegates to the embedded unsafe FS after having confirmed the path +// to be inside root. If the provided path violates this constraint, an error +// of type ConstraintError is returned. +func (fs fsSecure) ReadFile(path string) ([]byte, error) { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return nil, &ConstraintError{Op: "read", Path: path, Err: err} + } + return fs.unsafeFS.ReadFile(path) +} + +// WriteFile delegates to the embedded unsafe FS after having confirmed the +// path to be inside root. If the provided path violates this constraint, an +// error of type ConstraintError is returned. +func (fs fsSecure) WriteFile(path string, data []byte) error { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return &ConstraintError{Op: "write", Path: path, Err: err} + } + return fs.unsafeFS.WriteFile(path, data) +} + +// Walk delegates to the embedded unsafe FS, wrapping falkFn in a callback which +// confirms the path to be inside root. If the path violates this constraint, +// an error of type ConstraintError is returned and walkFn is not called. +func (fs fsSecure) Walk(path string, walkFn filepath.WalkFunc) error { + wrapWalkFn := func(path string, info os.FileInfo, err error) error { + if err := isSecurePath(fs.unsafeFS, fs.root, path, fs.allowPrefixes...); err != nil { + return &ConstraintError{Op: "walk", Path: path, Err: err} + } + return walkFn(path, info, err) + } + return fs.unsafeFS.Walk(path, wrapWalkFn) +} + +// isSecurePath confirms the given path is inside root using the provided file +// system. At present, it assumes the file system implementation to be on disk +// and makes use of filepath.EvalSymlinks. +func isSecurePath(fs filesys.FileSystem, root filesys.ConfirmedDir, path string, allowedPrefixes ...string) error { + absRoot, err := filepath.Abs(path) + if err != nil { + return fmt.Errorf("abs path error on '%s': %v", path, err) + } + d := filesys.ConfirmedDir(absRoot) + if fs.Exists(absRoot) { + evaluated, err := filepath.EvalSymlinks(absRoot) + if err != nil { + return fmt.Errorf("evalsymlink failure on '%s': %w", path, err) + } + evaluatedDir := evaluated + if !fs.IsDir(evaluatedDir) { + evaluatedDir = filepath.Dir(evaluatedDir) + } + d = filesys.ConfirmedDir(evaluatedDir) + } + if ok, _ := hasOneOfPrefixes(d.String(), allowedPrefixes); ok { + return nil + } + if !d.HasPrefix(root) { + return rootConstraintErr(path, root.String()) + } + return nil +} + +func rootConstraintErr(path, root string) error { + return fmt.Errorf("path '%s' is not in or below '%s'", path, root) +} + +func hasOneOfPrefixes(s string, prefixes []string) (bool, string) { + for _, p := range prefixes { + if strings.HasPrefix(s, p) { + return true, p + } + } + return false, "" +} diff --git a/pkg/utils/flux_utils/kustomize/kustomize_generator.go b/pkg/utils/flux_utils/kustomize/kustomize_generator.go new file mode 100644 index 000000000..2a21e8b69 --- /dev/null +++ b/pkg/utils/flux_utils/kustomize/kustomize_generator.go @@ -0,0 +1,81 @@ +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kustomize + +import ( + "fmt" + "sync" + + securefs "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/kustomize/filesys" + "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/api/resmap" + kustypes "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// buildMutex protects against kustomize concurrent map read/write panic +var kustomizeBuildMutex sync.Mutex + +// Secure Build wraps krusty.MakeKustomizer with the following settings: +// - secure on-disk FS denying operations outside root +// - load files from outside the kustomization dir path +// (but not outside root) +// - disable plugins except for the builtin ones +func SecureBuild(root, dirPath string, allowRemoteBases bool) (res resmap.ResMap, err error) { + var fs filesys.FileSystem + + // Create secure FS for root with or without remote base support + if allowRemoteBases { + fs, err = securefs.MakeFsOnDiskSecureBuild(root) + if err != nil { + return nil, err + } + } else { + fs, err = securefs.MakeFsOnDiskSecure(root) + if err != nil { + return nil, err + } + } + return Build(fs, dirPath) +} + +// Build wraps krusty.MakeKustomizer with the following settings: +// - load files from outside the kustomization.yaml root +// - disable plugins except for the builtin ones +func Build(fs filesys.FileSystem, dirPath string) (res resmap.ResMap, err error) { + // temporary workaround for concurrent map read and map write bug + // https://github.com/kubernetes-sigs/kustomize/issues/3659 + kustomizeBuildMutex.Lock() + defer kustomizeBuildMutex.Unlock() + + // Kustomize tends to panic in unpredicted ways due to (accidental) + // invalid object data; recover when this happens to ensure continuity of + // operations + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("recovered from kustomize build panic: %v", r) + } + }() + + buildOptions := &krusty.Options{ + LoadRestrictions: kustypes.LoadRestrictionsNone, + PluginConfig: kustypes.DisabledPluginConfig(), + } + + k := krusty.MakeKustomizer(buildOptions) + return k.Run(fs, dirPath) +} From 51a0116e3a286b9316031cb55cbba23123276675 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 14 May 2023 22:33:50 +0200 Subject: [PATCH 0974/2268] refactor: Use metav1.Time instead of own JsonTime --- pkg/deployment/commands/result_utils.go | 5 ++-- pkg/types/result/command_result.go | 5 ++-- pkg/types/time.go | 39 ------------------------- 3 files changed, 6 insertions(+), 43 deletions(-) delete mode 100644 pkg/types/time.go diff --git a/pkg/deployment/commands/result_utils.go b/pkg/deployment/commands/result_utils.go index dba10686f..9e06da59d 100644 --- a/pkg/deployment/commands/result_utils.go +++ b/pkg/deployment/commands/result_utils.go @@ -7,6 +7,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "path/filepath" "time" ) @@ -14,8 +15,8 @@ import ( func addBaseCommandInfoToResult(targetCtx *kluctl_project.TargetContext, r *result.CommandResult, command string) error { r.Target = targetCtx.Target r.Command = result.CommandInfo{ - StartTime: types.FromTime(targetCtx.KluctlProject.LoadTime), - EndTime: types.FromTime(time.Now()), + StartTime: metav1.NewTime(targetCtx.KluctlProject.LoadTime), + EndTime: metav1.Now(), Command: command, Args: targetCtx.KluctlProject.LoadArgs.ExternalArgs, } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 141bd055d..44629c3d5 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -4,6 +4,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type Change struct { @@ -74,8 +75,8 @@ func (k TargetKey) Less(o TargetKey) bool { type CommandInfo struct { Initiator CommandInitiator `json:"initiator" validate:"oneof=CommandLine KluctlDeployment"` - StartTime types.JsonTime `json:"startTime"` - EndTime types.JsonTime `json:"endTime"` + StartTime metav1.Time `json:"startTime"` + EndTime metav1.Time `json:"endTime"` KluctlDeployment *KluctlDeploymentInfo `json:"kluctlDeployment,omitempty"` Command string `json:"command,omitempty"` Target string `json:"target,omitempty"` diff --git a/pkg/types/time.go b/pkg/types/time.go deleted file mode 100644 index 1706b8606..000000000 --- a/pkg/types/time.go +++ /dev/null @@ -1,39 +0,0 @@ -package types - -import ( - "encoding/json" - "time" -) - -type JsonTime string - -func FromTime(t time.Time) JsonTime { - return JsonTime(t.Format(time.RFC3339Nano)) -} - -func (t JsonTime) ToTime() (time.Time, error) { - t2, err := time.Parse(time.RFC3339Nano, string(t)) - if err != nil { - return time.Time{}, err - } - return t2, nil -} - -func (t *JsonTime) UnmarshalJSON(b []byte) error { - var s string - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - t2 := JsonTime(s) - _, err = t2.ToTime() - if err != nil { - return err - } - *t = t2 - return nil -} - -func (t JsonTime) MarshalJSON() ([]byte, error) { - return json.Marshal(string(t)) -} From 54822d5869a0a3e9298cf8d97c9109b376e3660b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 14 May 2023 22:35:29 +0200 Subject: [PATCH 0975/2268] refactor: Allow delete command without targetCtx --- cmd/kluctl/commands/cmd_delete.go | 4 +- pkg/deployment/commands/delete.go | 52 ++++++++++++++++++------- pkg/deployment/commands/result_utils.go | 29 ++++++++++++-- 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index f485c134c..f93e08d14 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -46,9 +46,9 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { commandResultFlags: &cmd.CommandResultFlags, } return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - cmd2 := commands.NewDeleteCommand(cmd.Discriminator, cmdCtx.targetCtx) + cmd2 := commands.NewDeleteCommand(cmd.Discriminator, cmdCtx.targetCtx, nil) - result, err := cmd2.Run(func(refs []k8s2.ObjectRef) error { + result, err := cmd2.Run(cmdCtx.targetCtx.SharedContext.Ctx, cmdCtx.targetCtx.SharedContext.K, func(refs []k8s2.ObjectRef) error { return confirmDeletion(ctx, refs, cmd.DryRun, cmd.Yes) }) if err != nil { diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index ffd08dae1..11c49e8bb 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -1,30 +1,44 @@ package commands import ( + "context" "fmt" "github.com/google/uuid" + "github.com/kluctl/kluctl/v2/pkg/deployment" utils2 "github.com/kluctl/kluctl/v2/pkg/deployment/utils" + "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils" + "time" ) type DeleteCommand struct { discriminator string targetCtx *kluctl_project.TargetContext + inclusion *utils.Inclusion } -func NewDeleteCommand(discriminator string, targetCtx *kluctl_project.TargetContext) *DeleteCommand { +func NewDeleteCommand(discriminator string, targetCtx *kluctl_project.TargetContext, inclusion *utils.Inclusion) *DeleteCommand { return &DeleteCommand{ discriminator: discriminator, targetCtx: targetCtx, + inclusion: inclusion, } } -func (cmd *DeleteCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*result.CommandResult, error) { - discriminator := cmd.targetCtx.Target.Discriminator - if cmd.discriminator != "" { - discriminator = cmd.discriminator +func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb func(refs []k8s2.ObjectRef) error) (*result.CommandResult, error) { + startTime := time.Now() + + inclusion := cmd.inclusion + if inclusion == nil && cmd.targetCtx != nil { + inclusion = cmd.targetCtx.DeploymentCollection.Inclusion + } + + discriminator := cmd.discriminator + if discriminator == "" && cmd.targetCtx != nil { + discriminator = cmd.targetCtx.Target.Discriminator } if discriminator == "" { @@ -32,13 +46,13 @@ func (cmd *DeleteCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*res } dew := utils2.NewDeploymentErrorsAndWarnings() - ru := utils2.NewRemoteObjectsUtil(cmd.targetCtx.SharedContext.Ctx, dew) - err := ru.UpdateRemoteObjects(cmd.targetCtx.SharedContext.K, &discriminator, nil, false) + ru := utils2.NewRemoteObjectsUtil(ctx, dew) + err := ru.UpdateRemoteObjects(k, &discriminator, nil, false) if err != nil { return nil, err } - deleteRefs, err := utils2.FindObjectsForDelete(cmd.targetCtx.SharedContext.K, ru.GetFilteredRemoteObjects(cmd.targetCtx.DeploymentCollection.Inclusion), cmd.targetCtx.DeploymentCollection.Inclusion.HasType("tags"), nil) + deleteRefs, err := utils2.FindObjectsForDelete(k, ru.GetFilteredRemoteObjects(inclusion), inclusion.HasType("tags"), nil) if err != nil { return nil, err } @@ -50,20 +64,32 @@ func (cmd *DeleteCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*res } } - deleted, err := utils2.DeleteObjects(cmd.targetCtx.SharedContext.Ctx, cmd.targetCtx.SharedContext.K, deleteRefs, dew, true) + deleted, err := utils2.DeleteObjects(ctx, k, deleteRefs, dew, true) if err != nil { return nil, err } + var c *deployment.DeploymentCollection + if cmd.targetCtx != nil { + c = cmd.targetCtx.DeploymentCollection + } + r := &result.CommandResult{ Id: uuid.New().String(), - Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, nil, nil, nil, deleted), + Objects: collectObjects(c, ru, nil, nil, nil, deleted), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), } - err = addBaseCommandInfoToResult(cmd.targetCtx, r, "delete") - if err != nil { - return r, err + if cmd.targetCtx != nil { + err = addBaseCommandInfoToResult(cmd.targetCtx, r, "delete") + if err != nil { + return r, err + } + } else { + err = addDeleteCommandInfoToResult(r, k, startTime, inclusion) + if err != nil { + return r, err + } } return r, nil } diff --git a/pkg/deployment/commands/result_utils.go b/pkg/deployment/commands/result_utils.go index 9e06da59d..ecd108d56 100644 --- a/pkg/deployment/commands/result_utils.go +++ b/pkg/deployment/commands/result_utils.go @@ -3,10 +3,12 @@ package commands import ( "fmt" git2 "github.com/go-git/go-git/v5" + k8s2 "github.com/kluctl/kluctl/v2/pkg/k8s" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "path/filepath" "time" @@ -36,7 +38,7 @@ func addBaseCommandInfoToResult(targetCtx *kluctl_project.TargetContext, r *resu return err } - err = addClusterInfo(targetCtx, r) + err = addClusterInfo(targetCtx.SharedContext.K, r) if err != nil { return err } @@ -44,6 +46,27 @@ func addBaseCommandInfoToResult(targetCtx *kluctl_project.TargetContext, r *resu r.TargetKey.TargetName = targetCtx.Target.Name r.TargetKey.Discriminator = targetCtx.Target.Discriminator r.TargetKey.ClusterId = r.ClusterInfo.ClusterId + + return nil +} + +func addDeleteCommandInfoToResult(r *result.CommandResult, k *k8s2.K8sCluster, startTime time.Time, inclusion *utils.Inclusion) error { + r.Command = result.CommandInfo{ + StartTime: metav1.NewTime(startTime), + EndTime: metav1.Now(), + Command: "delete", + } + + r.Command.IncludeTags = inclusion.GetIncludes("tags") + r.Command.ExcludeTags = inclusion.GetExcludes("tags") + r.Command.IncludeDeploymentDirs = inclusion.GetIncludes("deploymentItemDir") + r.Command.ExcludeDeploymentDirs = inclusion.GetExcludes("deploymentItemDir") + + err := addClusterInfo(k, r) + if err != nil { + return err + } + return nil } @@ -117,8 +140,8 @@ func addGitInfo(targetCtx *kluctl_project.TargetContext, r *result.CommandResult return nil } -func addClusterInfo(targetCtx *kluctl_project.TargetContext, r *result.CommandResult) error { - kubeSystemNs, _, err := targetCtx.SharedContext.K.GetSingleObject( +func addClusterInfo(k *k8s2.K8sCluster, r *result.CommandResult) error { + kubeSystemNs, _, err := k.GetSingleObject( k8s.NewObjectRef("", "v1", "Namespace", "kube-system", "")) if err != nil { return err From 1b7645e261aa2804aa912a4ab7b87568eaaafbf2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 14 May 2023 22:55:45 +0200 Subject: [PATCH 0976/2268] refactor: Use apiextensionsv1.JSON for any types --- e2e/args_test.go | 12 ++++---- pkg/deployment/external_args.go | 8 ++++- pkg/deployment/utils/diff_utils_test.go | 40 ++++++++++++++++++++----- pkg/diff/diff.go | 40 ++++++++++++++++++++++--- pkg/diff/obfuscate.go | 25 ++++++++++++---- pkg/results/result-store-secrets.go | 2 +- pkg/results/results-collector.go | 2 +- pkg/types/kluctl_project.go | 5 ++-- pkg/types/result/command_result.go | 11 +++---- 9 files changed, 113 insertions(+), 32 deletions(-) diff --git a/e2e/args_test.go b/e2e/args_test.go index 5f689f624..b52698b47 100644 --- a/e2e/args_test.go +++ b/e2e/args_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -func testArgs(t *testing.T) { +func TestArgs(t *testing.T) { t.Parallel() k := defaultCluster1 @@ -34,6 +34,10 @@ func testArgs(t *testing.T) { "nested": "default", }, }, + map[string]any{ + "name": "e", + "default": 42, + }, } p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { @@ -46,6 +50,7 @@ func testArgs(t *testing.T) { "b": `{{ args.b | default("na") }}`, "c": `{{ args.c | default("na") }}`, "d": "{{ args.d | to_json }}", + "e": "{{ args.e + 1 }}", }, resourceOpts{ name: "cm", namespace: p.TestSlug(), @@ -57,6 +62,7 @@ func testArgs(t *testing.T) { assertNestedFieldEquals(t, cm, "default", "data", "b") assertNestedFieldEquals(t, cm, "na", "data", "c") assertNestedFieldEquals(t, cm, `{"nested": "default"}`, "data", "d") + assertNestedFieldEquals(t, cm, `43`, "data", "e") p.KluctlMust("deploy", "--yes", "-t", "test", "-aa=a", "-ab=b") cm = k.MustGetCoreV1(t, "configmaps", p.TestSlug(), "cm") @@ -112,10 +118,6 @@ d: assertNestedFieldEquals(t, cm, `{"nested": {"nested2": "d4"}}`, "data", "d") } -func TestArgs(t *testing.T) { - testArgs(t) -} - func TestArgsFromEnv(t *testing.T) { k := defaultCluster1 diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index e041ebdfb..808d65dbf 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -1,6 +1,7 @@ package deployment import ( + "encoding/json" "fmt" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -59,8 +60,13 @@ func LoadDefaultArgs(args []*types.DeploymentArg, deployArgs *uo.UnstructuredObj defaults := uo.New() for _, a := range args { if a.Default != nil { + var v any + err := json.Unmarshal(a.Default.Raw, &v) + if err != nil { + return err + } a2 := uo.FromMap(map[string]interface{}{ - a.Name: a.Default, + a.Name: v, }) defaults.Merge(a2) } diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index 430d39427..60e3fa6e9 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -2,11 +2,13 @@ package utils import ( "context" + "encoding/json" "github.com/kluctl/kluctl/v2/pkg/deployment" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "testing" ) @@ -56,6 +58,28 @@ func newTestConfigMap(name string, data map[string]interface{}) *uo.Unstructured } func TestDiff(t *testing.T) { + buildRaw := func(x any) *apiextensionsv1.JSON { + if x == nil { + return nil + } + b, err := json.Marshal(x) + if err != nil { + t.Fatal(err) + } + return &apiextensionsv1.JSON{ + Raw: b, + } + } + buildChange := func(typ string, jsonPath string, oldValue any, newValue any, unifiedDiff string) result.Change { + return result.Change{ + Type: typ, + JsonPath: jsonPath, + NewValue: buildRaw(newValue), + OldValue: buildRaw(oldValue), + UnifiedDiff: unifiedDiff, + } + } + tests := []*diffTestConfig{ { name: "One changed object (changed field)", @@ -65,7 +89,7 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ - result.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v2", UnifiedDiff: "-v1\n+v2"}, + buildChange("update", "data.d1", buildRaw("v1"), buildRaw("v2"), "-v1\n+v2"), }, dtc.du.ChangedObjects[0].Changes) }, }, @@ -77,7 +101,7 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ - result.Change{Type: "insert", JsonPath: "data.d2", OldValue: interface{}(nil), NewValue: "v2", UnifiedDiff: "+v2"}, + buildChange("insert", "data.d2", nil, buildRaw("v2"), "+v2"), }, dtc.du.ChangedObjects[0].Changes) }, }, @@ -89,7 +113,7 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ - result.Change{Type: "delete", JsonPath: "data.d2", OldValue: "v2", NewValue: interface{}(nil), UnifiedDiff: "-v2"}, + buildChange("delete", "data.d2", buildRaw("v2"), nil, "-v2"), }, dtc.du.ChangedObjects[0].Changes) }, }, @@ -101,8 +125,8 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ - result.Change{Type: "delete", JsonPath: "data.d1", OldValue: "v1", NewValue: interface{}(nil), UnifiedDiff: "-v1"}, - result.Change{Type: "insert", JsonPath: "data.d2", OldValue: interface{}(nil), NewValue: "v2", UnifiedDiff: "+v2"}, + buildChange("delete", "data.d1", buildRaw("v1"), nil, "-v1"), + buildChange("insert", "data.d2", nil, buildRaw("v2"), "+v2"), }, dtc.du.ChangedObjects[0].Changes) }, }, @@ -114,9 +138,9 @@ func TestDiff(t *testing.T) { a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ - result.Change{Type: "update", JsonPath: "data.d1", OldValue: "v1", NewValue: "v12", UnifiedDiff: "-v1\n+v12"}, - result.Change{Type: "delete", JsonPath: "data.d2", OldValue: "v2", NewValue: interface{}(nil), UnifiedDiff: "-v2"}, - result.Change{Type: "insert", JsonPath: "data.d3", OldValue: interface{}(nil), NewValue: "v3", UnifiedDiff: "+v3"}, + buildChange("update", "data.d1", buildRaw("v1"), buildRaw("v12"), "-v1\n+v12"), + buildChange("delete", "data.d2", buildRaw("v2"), nil, "-v2"), + buildChange("insert", "data.d3", nil, buildRaw("v3"), "+v3"), }, dtc.du.ChangedObjects[0].Changes) }, }, diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index f654b0507..0d9accd67 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -9,6 +9,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" diff2 "github.com/r3labs/diff/v2" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "reflect" "sort" "strconv" @@ -73,31 +74,47 @@ func convertChange(c diff2.Change, oldObject *uo.UnstructuredObject, newObject * if err != nil { return nil, err } + jto, err := yaml.WriteJsonString(c.To) + if err != nil { + return nil, err + } return &result.Change{ Type: "insert", JsonPath: p, - NewValue: c.To, + NewValue: &apiextensionsv1.JSON{Raw: []byte(jto)}, }, nil case "delete": p, err := convertPath(c.Path, oldObject.Object) if err != nil { return nil, err } + jfrom, err := yaml.WriteJsonString(c.From) + if err != nil { + return nil, err + } return &result.Change{ Type: "delete", JsonPath: p, - OldValue: c.From, + OldValue: &apiextensionsv1.JSON{Raw: []byte(jfrom)}, }, nil case "update": p, err := convertPath(c.Path, newObject.Object) if err != nil { return nil, err } + jto, err := yaml.WriteJsonString(c.To) + if err != nil { + return nil, err + } + jfrom, err := yaml.WriteJsonString(c.From) + if err != nil { + return nil, err + } return &result.Change{ Type: "update", JsonPath: p, - NewValue: c.To, - OldValue: c.From, + NewValue: &apiextensionsv1.JSON{Raw: []byte(jto)}, + OldValue: &apiextensionsv1.JSON{Raw: []byte(jfrom)}, }, nil } return nil, fmt.Errorf("unknown change type %s", c.Type) @@ -204,6 +221,21 @@ func objectToDiffableStringNoType(o interface{}) (string, error) { if o == notPresent { return "", nil } + + if reflect.TypeOf(reflect.Indirect(reflect.ValueOf(o))).Kind() == reflect.Struct { + // writing and re-reading yaml to normalise custom serialization + s, err := yaml.WriteYamlString(o) + if err != nil { + return "", err + } + var o2 any + err = yaml.ReadYamlString(s, &o2) + if err != nil { + return "", err + } + o = o2 + } + if v, ok := o.(string); ok { return v, nil } diff --git a/pkg/diff/obfuscate.go b/pkg/diff/obfuscate.go index ea16017d9..f8f1a7fc8 100644 --- a/pkg/diff/obfuscate.go +++ b/pkg/diff/obfuscate.go @@ -2,11 +2,13 @@ package diff import ( "encoding/base64" + "encoding/json" "fmt" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/ohler55/ojg/jp" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime/schema" "strings" ) @@ -65,22 +67,35 @@ func (o *Obfuscator) ObfuscateObject(x *uo.UnstructuredObject) (*uo.Unstructured } func (o *Obfuscator) obfuscateSecretChanges(ref k8s.ObjectRef, changes []result.Change) error { - replaceValues := func(x any, v string) any { - if x == nil { + replaceValues := func(j *apiextensionsv1.JSON, v string) *apiextensionsv1.JSON { + if j == nil { return nil } + + var x any + err := json.Unmarshal(j.Raw, &x) + if err != nil { + return nil + } + if m, ok := x.(map[string]any); ok { for k, _ := range m { m[k] = v } - return m } else if a, ok := x.([]any); ok { for i, _ := range a { a[i] = v } - return a + } else { + x = v } - return v + + b, err := json.Marshal(x) + if err != nil { + return nil + } + + return &apiextensionsv1.JSON{Raw: b} } for i, _ := range changes { diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index ab1db402a..f918e5b2e 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -276,7 +276,7 @@ func (s *ResultStoreSecrets) ListCommandResultSummaries(options ListCommandResul } sort.Slice(ret, func(i, j int) bool { - return ret[i].Command.StartTime >= ret[j].Command.StartTime + return ret[i].Command.StartTime.After(ret[j].Command.StartTime.Time) }) return ret, nil diff --git a/pkg/results/results-collector.go b/pkg/results/results-collector.go index 583a5b9b2..5ae9cbd4a 100644 --- a/pkg/results/results-collector.go +++ b/pkg/results/results-collector.go @@ -111,7 +111,7 @@ func (rc *ResultsCollector) ListCommandResultSummaries(options ListCommandResult summaries = append(summaries, *x.summary) } sort.Slice(summaries, func(i, j int) bool { - return summaries[i].Command.StartTime >= summaries[j].Command.StartTime + return summaries[i].Command.StartTime.After(summaries[j].Command.StartTime.Time) }) return summaries, nil } diff --git a/pkg/types/kluctl_project.go b/pkg/types/kluctl_project.go index 83aa87f8d..c8293da48 100644 --- a/pkg/types/kluctl_project.go +++ b/pkg/types/kluctl_project.go @@ -2,6 +2,7 @@ package types import ( "github.com/kluctl/kluctl/v2/pkg/utils/uo" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) type SealingConfig struct { @@ -20,8 +21,8 @@ type Target struct { } type DeploymentArg struct { - Name string `json:"name" validate:"required"` - Default interface{} `json:"default,omitempty"` + Name string `json:"name" validate:"required"` + Default *apiextensionsv1.JSON `json:"default,omitempty"` } type SecretSet struct { diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 44629c3d5..80d30e378 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -4,15 +4,16 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type Change struct { - Type string `json:"type" validate:"required"` - JsonPath string `json:"jsonPath" validate:"required"` - OldValue interface{} `json:"oldValue,omitempty"` - NewValue interface{} `json:"newValue,omitempty"` - UnifiedDiff string `json:"unifiedDiff,omitempty"` + Type string `json:"type" validate:"required"` + JsonPath string `json:"jsonPath" validate:"required"` + OldValue *apiextensionsv1.JSON `json:"oldValue,omitempty"` + NewValue *apiextensionsv1.JSON `json:"newValue,omitempty"` + UnifiedDiff string `json:"unifiedDiff,omitempty"` } type ChangedObject struct { From 2618516cb084cc0591ccfa9830fedd53cf370de7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 12:05:19 +0200 Subject: [PATCH 0977/2268] refactor: Allow to call KluctlExecute without a project --- e2e/test-utils/kluctl_execute.go | 46 ++++++++++++++++++++++++++++++++ e2e/test-utils/project.go | 38 +------------------------- 2 files changed, 47 insertions(+), 37 deletions(-) create mode 100644 e2e/test-utils/kluctl_execute.go diff --git a/e2e/test-utils/kluctl_execute.go b/e2e/test-utils/kluctl_execute.go new file mode 100644 index 000000000..bab9a76e2 --- /dev/null +++ b/e2e/test-utils/kluctl_execute.go @@ -0,0 +1,46 @@ +package test_utils + +import ( + "bytes" + "context" + "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" + "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/utils" + "strings" + "sync" + "testing" +) + +func KluctlExecute(t *testing.T, ctx context.Context, args ...string) (string, string, error) { + t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) + + var m sync.Mutex + stdoutBuf := bytes.NewBuffer(nil) + stdout := status.NewLineRedirector(func(line string) { + m.Lock() + defer m.Unlock() + t.Log(line) + stdoutBuf.WriteString(line + "\n") + }) + stderrBuf := bytes.NewBuffer(nil) + + ctx = utils.WithTmpBaseDir(ctx, t.TempDir()) + ctx = commands.WithStdStreams(ctx, stdout, stderrBuf) + sh := status.NewSimpleStatusHandler(func(message string) { + m.Lock() + defer m.Unlock() + t.Log(message) + stderrBuf.WriteString(message + "\n") + }, false, true) + defer func() { + if sh != nil { + sh.Stop() + } + }() + ctx = status.NewContext(ctx, sh) + err := commands.Execute(ctx, args, nil) + sh.Stop() + sh = nil + _ = stdout.Close() + return stdoutBuf.String(), stderrBuf.String(), err +} diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index ae402a1b9..e8c33d1ec 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -1,16 +1,12 @@ package test_utils import ( - "bytes" "context" "fmt" "github.com/go-git/go-git/v5" "github.com/huandu/xstrings" "github.com/jinzhu/copier" - "github.com/kluctl/kluctl/v2/cmd/kluctl/commands" git2 "github.com/kluctl/kluctl/v2/pkg/git" - "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/yaml" registry2 "helm.sh/helm/v3/pkg/registry" @@ -22,7 +18,6 @@ import ( "path/filepath" "reflect" "strings" - "sync" "testing" ) @@ -459,38 +454,7 @@ func (p *TestProject) KluctlExecute(argsIn ...string) (string, string, error) { args = append(args, "--project-dir", p.LocalProjectDir()) args = append(args, argsIn...) - p.t.Logf("Runnning kluctl: %s", strings.Join(args, " ")) - - var m sync.Mutex - stdoutBuf := bytes.NewBuffer(nil) - stdout := status.NewLineRedirector(func(line string) { - m.Lock() - defer m.Unlock() - p.t.Log(line) - stdoutBuf.WriteString(line + "\n") - }) - stderrBuf := bytes.NewBuffer(nil) - - ctx := context.Background() - ctx = utils.WithTmpBaseDir(ctx, p.t.TempDir()) - ctx = commands.WithStdStreams(ctx, stdout, stderrBuf) - sh := status.NewSimpleStatusHandler(func(message string) { - m.Lock() - defer m.Unlock() - p.t.Log(message) - stderrBuf.WriteString(message + "\n") - }, false, true) - defer func() { - if sh != nil { - sh.Stop() - } - }() - ctx = status.NewContext(ctx, sh) - err := commands.Execute(ctx, args, nil) - sh.Stop() - sh = nil - _ = stdout.Close() - return stdoutBuf.String(), stderrBuf.String(), err + return KluctlExecute(p.t, context.Background(), args...) } func (p *TestProject) Kluctl(argsIn ...string) (string, string, error) { From 732378e75386212cb697759a7486036498928b59 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 3 May 2023 23:59:56 +0200 Subject: [PATCH 0978/2268] fix: Allow to use non-exported fields in command structs --- cmd/kluctl/commands/cobra_utils.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index 173303bfe..d4b4fb013 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -100,6 +100,9 @@ func (c *rootCommand) buildCobraSubCommands(cg *commandAndGroups, cmdStruct inte t := v.Type() for i := 0; i < t.NumField(); i++ { f := t.Field(i) + if !f.IsExported() { + continue + } v2 := v.Field(i).Addr().Interface() name := buildCobraName(f.Name) @@ -126,6 +129,9 @@ func (c *rootCommand) buildCobraArgs(cg *commandAndGroups, cmdStruct interface{} t := v.Type() for i := 0; i < t.NumField(); i++ { f := t.Field(i) + if !f.IsExported() { + continue + } if _, ok := f.Tag.Lookup("cmd"); ok { continue } From 4a6aee2e7054cb7940ff4ce092dbfdc14e59559e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 14:07:21 +0200 Subject: [PATCH 0979/2268] refactor: Move main.go to cmd/ --- main.go => cmd/main.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename main.go => cmd/main.go (100%) diff --git a/main.go b/cmd/main.go similarity index 100% rename from main.go rename to cmd/main.go From b1c73cc7ed05e042062fb56e602a4b4435a06a4c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 14:09:37 +0200 Subject: [PATCH 0980/2268] fix: Make code controller-gen compatible --- e2e/default_clusters.go | 1 - e2e/hooks_test.go | 2 +- e2e/utils_resources.go | 4 ++-- e2e/validate_test.go | 2 +- pkg/types/doc.go | 19 +++++++++++++++++++ pkg/types/git_url.go | 8 ++++++++ pkg/types/k8s/ref.go | 1 + pkg/types/result/doc.go | 19 +++++++++++++++++++ pkg/types/yaml_url.go | 7 +++++++ pkg/utils/uo/k8s_fields.go | 1 - pkg/utils/uo/uo.go | 11 ++++++++++- 11 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 pkg/types/doc.go create mode 100644 pkg/types/result/doc.go diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index b50159416..373957a89 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -1,6 +1,5 @@ package e2e -import "C" import ( "fmt" "github.com/imdario/mergo" diff --git a/e2e/hooks_test.go b/e2e/hooks_test.go index 504559c23..994174ff3 100644 --- a/e2e/hooks_test.go +++ b/e2e/hooks_test.go @@ -96,7 +96,7 @@ func (s *hooksTestContext) addConfigMap(dir string, opts resourceOpts) { mergeMetadata(o, opts) o.SetNestedField(map[string]interface{}{}, "data") s.p.AddKustomizeResources(dir, []test_utils.KustomizeResource{ - {fmt.Sprintf("%s.yml", opts.name), "", o}, + {Name: fmt.Sprintf("%s.yml", opts.name), Content: o}, }) } diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index 928627aa6..a78a2d0fd 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -57,7 +57,7 @@ func createSecretObject(data map[string]string, opts resourceOpts) *uo.Unstructu func addConfigMapDeployment(p *test_utils.TestProject, dir string, data map[string]string, opts resourceOpts) { o := createConfigMapObject(data, opts) p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ - {fmt.Sprintf("configmap-%s.yml", opts.name), "", o}, + {Name: fmt.Sprintf("configmap-%s.yml", opts.name), Content: o}, }, opts.tags) if opts.when != "" { p.UpdateDeploymentItems(filepath.Dir(dir), func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { @@ -75,7 +75,7 @@ func addSecretDeployment(p *test_utils.TestProject, dir string, data map[string] o := createSecretObject(data, opts) fname := fmt.Sprintf("secret-%s.yml", opts.name) p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ - {fname, fname + sealmeExt, o}, + {Name: fname, FileName: fname + sealmeExt, Content: o}, }, opts.tags) if opts.when != "" { p.UpdateDeploymentItems(filepath.Dir(dir), func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { diff --git a/e2e/validate_test.go b/e2e/validate_test.go index 9fdb6cf5c..1eec3fd3e 100644 --- a/e2e/validate_test.go +++ b/e2e/validate_test.go @@ -72,7 +72,7 @@ func prepareValidateTest(t *testing.T, k *test_utils.EnvTestCluster) *test_utils p.UpdateTarget("test", nil) p.AddKustomizeDeployment("d1", []test_utils.KustomizeResource{ - {fmt.Sprintf("configmap-%s.yml", "d1"), "", buildDeployment("d1", p.TestSlug(), false)}, + {Name: fmt.Sprintf("configmap-%s.yml", "d1"), Content: buildDeployment("d1", p.TestSlug(), false)}, }, nil) return p diff --git a/pkg/types/doc.go b/pkg/types/doc.go new file mode 100644 index 000000000..b4756cc8a --- /dev/null +++ b/pkg/types/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package types contains types used in Kluctl projects +// +kubebuilder:object:generate=true +package types diff --git a/pkg/types/git_url.go b/pkg/types/git_url.go index f7fa8c6a7..44db3e65c 100644 --- a/pkg/types/git_url.go +++ b/pkg/types/git_url.go @@ -8,6 +8,7 @@ import ( "strings" ) +// +kubebuilder:validation:Type=string type GitUrl struct { url.URL `json:"-"` } @@ -28,6 +29,13 @@ func ParseGitUrlMust(u string) *GitUrl { return u2 } +func (in *GitUrl) DeepCopyInto(out *GitUrl) { + out.URL = in.URL + if out.URL.User != nil { + out.URL.User = &*in.URL.User + } +} + func (u *GitUrl) UnmarshalJSON(b []byte) error { var s string err := json.Unmarshal(b, &s) diff --git a/pkg/types/k8s/ref.go b/pkg/types/k8s/ref.go index b257ca428..478e2bc42 100644 --- a/pkg/types/k8s/ref.go +++ b/pkg/types/k8s/ref.go @@ -1,3 +1,4 @@ +// +kubebuilder:object:generate=true package k8s import ( diff --git a/pkg/types/result/doc.go b/pkg/types/result/doc.go new file mode 100644 index 000000000..ec02a69c4 --- /dev/null +++ b/pkg/types/result/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package result contains result types used in Kluctl projects +// +kubebuilder:object:generate=true +package result diff --git a/pkg/types/yaml_url.go b/pkg/types/yaml_url.go index 59560e2df..f2ffac1df 100644 --- a/pkg/types/yaml_url.go +++ b/pkg/types/yaml_url.go @@ -26,3 +26,10 @@ func (u *YamlUrl) UnmarshalJSON(b []byte) error { func (u YamlUrl) MarshalJSON() ([]byte, error) { return json.Marshal(u.String()) } + +func (in *YamlUrl) DeepCopyInto(out *YamlUrl) { + out.URL = in.URL + if out.URL.User != nil { + out.URL.User = &*in.URL.User + } +} diff --git a/pkg/utils/uo/k8s_fields.go b/pkg/utils/uo/k8s_fields.go index c45e23f57..e3b507121 100644 --- a/pkg/utils/uo/k8s_fields.go +++ b/pkg/utils/uo/k8s_fields.go @@ -251,5 +251,4 @@ func (ui *UnstructuredObject) getRegexp(r interface{}) *regexp.Regexp { } } panic(fmt.Sprintf("unknown type %s", reflect.TypeOf(r).String())) - return nil } diff --git a/pkg/utils/uo/uo.go b/pkg/utils/uo/uo.go index 7983e3f23..92d2e73d7 100644 --- a/pkg/utils/uo/uo.go +++ b/pkg/utils/uo/uo.go @@ -9,8 +9,9 @@ import ( "reflect" ) +// +kubebuilder:pruning:PreserveUnknownFields type UnstructuredObject struct { - Object map[string]interface{} `json:"object,omitempty,inline"` + Object map[string]interface{} `json:"-"` } func (uo *UnstructuredObject) MarshalJSON() ([]byte, error) { @@ -145,6 +146,14 @@ func (uo *UnstructuredObject) Clone() *UnstructuredObject { return FromMap(c) } +func (uo *UnstructuredObject) DeepCopyInto(out *UnstructuredObject) { + *out = *uo.Clone() +} + +func (uo *UnstructuredObject) DeepCopy() *UnstructuredObject { + return uo.Clone() +} + func (uo *UnstructuredObject) Merge(other *UnstructuredObject) { MergeMap(uo.Object, other.Object) } From dc3e5fa3a3031691187573f0861026213be828c4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 14:10:42 +0200 Subject: [PATCH 0981/2268] tests: Add controller-runtime client and CRD loading to EnvTestCluster --- e2e/test-utils/envtest_cluster.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/e2e/test-utils/envtest_cluster.go b/e2e/test-utils/envtest_cluster.go index df6893c7d..158b79e65 100644 --- a/e2e/test-utils/envtest_cluster.go +++ b/e2e/test-utils/envtest_cluster.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" "github.com/kluctl/kluctl/v2/pkg/utils/uo" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -13,6 +14,7 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/util/flowcontrol" "net/http" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" "sigs.k8s.io/controller-runtime/pkg/webhook" "sync" @@ -20,6 +22,8 @@ import ( ) type EnvTestCluster struct { + CRDDirectoryPaths []string + env envtest.Environment started bool @@ -30,6 +34,7 @@ type EnvTestCluster struct { HttpClient *http.Client DynamicClient dynamic.Interface + Client client.Client ServerVersion *version.Info callbackServer webhook.Server @@ -47,12 +52,16 @@ func CreateEnvTestCluster(context string) *EnvTestCluster { } func (k *EnvTestCluster) Start() error { + k.env.CRDDirectoryPaths = k.CRDDirectoryPaths + _, err := k.env.Start() if err != nil { return err } k.started = true + _ = kluctlv1.AddToScheme(k.env.Scheme) + err = k.startCallbackServer() if err != nil { return err @@ -76,11 +85,11 @@ func (k *EnvTestCluster) Start() error { k.Kubeconfig = kcfg - client, err := rest.HTTPClientFor(k.config) + httpClient, err := rest.HTTPClientFor(k.config) if err != nil { return err } - k.HttpClient = client + k.HttpClient = httpClient dynamicClient, err := dynamic.NewForConfigAndClient(k.config, k.HttpClient) if err != nil { @@ -88,7 +97,12 @@ func (k *EnvTestCluster) Start() error { } k.DynamicClient = dynamicClient - discoveryClient, err := discovery.NewDiscoveryClientForConfigAndClient(k.config, client) + c, err := client.New(k.config, client.Options{ + HTTPClient: httpClient, + }) + k.Client = c + + discoveryClient, err := discovery.NewDiscoveryClientForConfigAndClient(k.config, httpClient) if err != nil { return err } From 3ce754edbcc1b912fbb04ad3efb8649a9bda86c2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 14:12:50 +0200 Subject: [PATCH 0982/2268] feat: Remove flux subcommands --- cmd/kluctl/commands/cmd_flux.go | 70 - cmd/kluctl/commands/cmd_flux_reconcile.go | 108 - cmd/kluctl/commands/cmd_flux_resume.go | 48 - cmd/kluctl/commands/cmd_flux_suspend.go | 47 - cmd/kluctl/commands/root.go | 2 - e2e/flux_test.go | 94 - e2e/test_resources/README.md | 15 - e2e/test_resources/flux-source-crd.yaml | 2623 --------------------- e2e/test_resources/kluctl-crds.yaml | 567 ----- e2e/test_resources/kluctl-deployment.yaml | 35 - 10 files changed, 3609 deletions(-) delete mode 100644 cmd/kluctl/commands/cmd_flux.go delete mode 100644 cmd/kluctl/commands/cmd_flux_reconcile.go delete mode 100644 cmd/kluctl/commands/cmd_flux_resume.go delete mode 100644 cmd/kluctl/commands/cmd_flux_suspend.go delete mode 100644 e2e/flux_test.go delete mode 100644 e2e/test_resources/flux-source-crd.yaml delete mode 100644 e2e/test_resources/kluctl-crds.yaml delete mode 100644 e2e/test_resources/kluctl-deployment.yaml diff --git a/cmd/kluctl/commands/cmd_flux.go b/cmd/kluctl/commands/cmd_flux.go deleted file mode 100644 index 2b68aa514..000000000 --- a/cmd/kluctl/commands/cmd_flux.go +++ /dev/null @@ -1,70 +0,0 @@ -package commands - -import ( - "context" - "fmt" - "time" - - kube "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types/k8s" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" -) - -type fluxCmd struct { - Reconcile fluxReconcileCmd `cmd:"" help:"Reconcile KluctlDeployment"` - Suspend fluxSuspendCmd `cmd:"" help:"Suspend KluctlDeployment"` - Resume fluxResumeCmd `cmd:"" help:"Resume KluctlDeployment"` -} - -func WaitForReady(k *kube.K8sCluster, ref k8s.ObjectRef) (bool, error) { - o, _, err := k.GetSingleObject(ref) - if o == nil { - return false, err - } - retry := 0 - finalStatus := true - var errorMsg error - for s, err := GetObjectStatus(o); s != true; { - if retry >= 5 || err != nil { - finalStatus = false - errorMsg = err - break - } - retry++ - time.Sleep(8 * time.Second) - } - return finalStatus, errorMsg -} - -func GetObjectStatus(o *uo.UnstructuredObject) (bool, error) { - conditions, ok, err := o.GetNestedField("status", "conditions") - if !ok { - return false, err - } - for _, v := range conditions.([]interface{}) { - if (v.(map[string]interface{})["type"]) == "Ready" { - return true, nil - } - } - - return false, err -} - -func GetObjectSource(o *uo.UnstructuredObject, name string, namespace string, ctx context.Context) (string, string, error) { - status.Trace(ctx, "fetching %s in %s", name, namespace) - - sourceName, ok, err := o.GetNestedField("spec", "sourceRef", "name") - if !ok { - return "", "", err - } - - sourceNamespace, ok, err := o.GetNestedField("spec", "sourceRef", "namespace") - if !ok { - return "", "", err - } - - return fmt.Sprintf("%v", sourceName), - fmt.Sprintf("%v", sourceNamespace), - err -} diff --git a/cmd/kluctl/commands/cmd_flux_reconcile.go b/cmd/kluctl/commands/cmd_flux_reconcile.go deleted file mode 100644 index d4d49ed7c..000000000 --- a/cmd/kluctl/commands/cmd_flux_reconcile.go +++ /dev/null @@ -1,108 +0,0 @@ -package commands - -import ( - "context" - "fmt" - "os" - "time" - - "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/status" - k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" -) - -type fluxReconcileCmd struct { - args.KluctlDeploymentFlags -} - -func (cmd *fluxReconcileCmd) Run(ctx context.Context) error { - var ( - sourceNamespace string - sourceName string - ) - ns := cmd.KluctlDeploymentFlags.Namespace - kd := cmd.KluctlDeploymentFlags.KluctlDeployment - source := cmd.KluctlDeploymentFlags.WithSource - noWait := cmd.KluctlDeploymentFlags.NoWait - timestamp := time.Now().Format(time.RFC3339) - - cf, err := k8s.NewClientFactoryFromDefaultConfig(ctx, nil) - if err != nil { - return err - } - k, err := k8s.NewK8sCluster(context.TODO(), cf, false) - if err != nil { - return err - } - - ref := k8s2.NewObjectRef(args.KluctlDeploymentGVK.Group, args.KluctlDeploymentGVK.Version, args.KluctlDeploymentGVK.Kind, kd, ns) - patch := []k8s.JsonPatch{{ - Op: "replace", - Path: "/metadata/annotations", - Value: map[string]string{ - "fluxcd.io/reconcileAt": timestamp, - }, - }} - - if source { - if !cmd.VerifyFlags() { - fmt.Println("KluctlDeployment name flag not provided..") - os.Exit(1) - } - o, _, err := k.GetSingleObject(ref) - if o == nil { - return err - } - - sourceName, sourceNamespace, err = GetObjectSource(o, kd, ns, context.TODO()) - if err != nil { - return err - } - ref2 := k8s2.NewObjectRef(args.GitRepositoryGVK.Group, args.GitRepositoryGVK.Version, args.GitRepositoryGVK.Kind, sourceName, sourceNamespace) - - s := status.Start(ctx, "Annotating Source %s in %s namespace", sourceName, sourceNamespace) - defer s.Failed() - - _, _, err = k.PatchObjectWithJsonPatch(ref2, patch, k8s.PatchOptions{}) - if err != nil { - return err - } - s.Success() - - s = status.Start(ctx, "Waiting for Source %s to finish reconciliation", sourceName) - - if !noWait { - ready, err := WaitForReady(k, ref2) - if !ready { - s.FailedWithMessage("Failed while waiting for Source %s to get ready..", sourceName) - return err - } - } - - s.Success() - } - - s := status.Start(ctx, "Annotating KluctlDeployment %s in %s namespace", kd, ns) - defer s.Failed() - - _, _, err = k.PatchObjectWithJsonPatch(ref, patch, k8s.PatchOptions{}) - if err != nil { - return err - } - s.Success() - - s = status.Start(ctx, "Waiting for KluctlDeployment %s in %s namespace to finish reconciliation", kd, ns) - - if !noWait { - ready, err := WaitForReady(k, ref) - if !ready { - s.FailedWithMessage("Failed while waiting for KluctlDeployment %s to get ready..", kd) - return err - } - } - - s.Success() - - return err -} diff --git a/cmd/kluctl/commands/cmd_flux_resume.go b/cmd/kluctl/commands/cmd_flux_resume.go deleted file mode 100644 index 8d116bc1e..000000000 --- a/cmd/kluctl/commands/cmd_flux_resume.go +++ /dev/null @@ -1,48 +0,0 @@ -package commands - -import ( - "context" - - "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/status" - k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" -) - -type fluxResumeCmd struct { - args.KluctlDeploymentFlags -} - -// TODO add reconciliation after resume -func (cmd *fluxResumeCmd) Run(ctx context.Context) error { - ns := cmd.KluctlDeploymentFlags.Namespace - kd := cmd.KluctlDeploymentFlags.KluctlDeployment - - cf, err := k8s.NewClientFactoryFromDefaultConfig(ctx, nil) - if err != nil { - return err - } - k, err := k8s.NewK8sCluster(context.TODO(), cf, false) - if err != nil { - return err - } - - ref := k8s2.NewObjectRef(args.KluctlDeploymentGVK.Group, args.KluctlDeploymentGVK.Version, args.KluctlDeploymentGVK.Kind, kd, ns) - - patch := []k8s.JsonPatch{{ - Op: "replace", - Path: "/spec/suspend", - Value: false, - }} - - s := status.Start(ctx, "Resuming KluctlDeployment %s in %s namespace", kd, ns) - defer s.Failed() - - _, _, err = k.PatchObjectWithJsonPatch(ref, patch, k8s.PatchOptions{}) - if err != nil { - return err - } - s.Success() - - return err -} diff --git a/cmd/kluctl/commands/cmd_flux_suspend.go b/cmd/kluctl/commands/cmd_flux_suspend.go deleted file mode 100644 index d7981c1a6..000000000 --- a/cmd/kluctl/commands/cmd_flux_suspend.go +++ /dev/null @@ -1,47 +0,0 @@ -package commands - -import ( - "context" - - "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/k8s" - "github.com/kluctl/kluctl/v2/pkg/status" - k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" -) - -type fluxSuspendCmd struct { - args.KluctlDeploymentFlags -} - -func (cmd *fluxSuspendCmd) Run(ctx context.Context) error { - ns := cmd.KluctlDeploymentFlags.Namespace - kd := cmd.KluctlDeploymentFlags.KluctlDeployment - - cf, err := k8s.NewClientFactoryFromDefaultConfig(ctx, nil) - if err != nil { - return err - } - k, err := k8s.NewK8sCluster(context.TODO(), cf, false) - if err != nil { - return err - } - - ref := k8s2.NewObjectRef(args.KluctlDeploymentGVK.Group, args.KluctlDeploymentGVK.Version, args.KluctlDeploymentGVK.Kind, kd, ns) - patch := []k8s.JsonPatch{{ - Op: "replace", - Path: "/spec/suspend", - Value: true, - }} - - s := status.Start(ctx, "Suspending KluctlDeployment %s in %s namespace", kd, ns) - defer s.Failed() - - _, _, err = k.PatchObjectWithJsonPatch(ref, patch, k8s.PatchOptions{}) - if err != nil { - return err - } - - s.Success() - - return err -} diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 7ec3fb4b1..200d0b9d7 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -66,7 +66,6 @@ type cli struct { Render renderCmd `cmd:"" help:"Renders all resources and configuration files"` Seal sealCmd `cmd:"" help:"Seal secrets based on target's sealingConfig"` Validate validateCmd `cmd:"" help:"Validates the already deployed deployment"` - Flux fluxCmd `cmd:"" help:"Flux sub-commands"` Version versionCmd `cmd:"" help:"Print kluctl version"` } @@ -77,7 +76,6 @@ var flagGroups = []groupInfo{ {group: "images", title: "Image arguments:", description: "Control fixed images and update behaviour."}, {group: "inclusion", title: "Inclusion/Exclusion arguments:", description: "Control inclusion/exclusion."}, {group: "misc", title: "Misc arguments:", description: "Command specific arguments."}, - {group: "flux", title: "Flux arguments:", description: "EXPERIMENTAL: Subcommands for interaction with flux-kluctl-controller"}, {group: "results", title: "Command Results:", description: "Configure how command results are stored."}, } diff --git a/e2e/flux_test.go b/e2e/flux_test.go deleted file mode 100644 index 5967270c4..000000000 --- a/e2e/flux_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package e2e - -import ( - "context" - "fmt" - "github.com/kluctl/kluctl/v2/e2e/test-utils" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "sync" - "testing" - - "github.com/kluctl/kluctl/v2/e2e/test_resources" - "github.com/stretchr/testify/assert" -) - -var kluctlDeploymentGVR = schema.GroupVersionResource{ - Group: "flux.kluctl.io", - Version: "v1alpha1", - Resource: "kluctldeployments", -} - -func getKluctlDeploymentObject(t *testing.T, k *test_utils.EnvTestCluster) *uo.UnstructuredObject { - kd, err := k.DynamicClient.Resource(kluctlDeploymentGVR).Namespace("flux-test").Get(context.Background(), "microservices-demo-test", v1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - if kd == nil { - t.Fatal("kluctldeployment not found") - } - return uo.FromUnstructured(kd) -} - -func TestFluxCommands(t *testing.T) { - t.Parallel() - - k := defaultCluster1 - - p := test_utils.NewTestProject(t, test_utils.WithUseProcess(true)) - - var wg sync.WaitGroup - wg.Add(2) - go func() { - test_resources.ApplyYaml("flux-source-crd.yaml", k) - wg.Done() - }() - go func() { - test_resources.ApplyYaml("kluctl-crds.yaml", k) - wg.Done() - }() - wg.Wait() - test_resources.ApplyYaml("kluctl-deployment.yaml", k) - - // assert that it was created - _ = getKluctlDeploymentObject(t, k) - - p.KluctlMust("flux", "suspend", "--namespace", "flux-test", "--kluctl-deployment", "microservices-demo-test") - suspend := getKluctlSuspendField(t, k) - assert.Equal(t, true, suspend, "Field status.suspend is not false") - - p.KluctlMust("flux", "resume", "--namespace", "flux-test", "--kluctl-deployment", "microservices-demo-test", "--no-wait") - resume := getKluctlSuspendField(t, k) - assert.Equal(t, false, resume, "Field status.suspend is not true") - - p.KluctlMust("flux", "reconcile", "--namespace", "flux-test", "--kluctl-deployment", "microservices-demo-test", "--with-source", "--no-wait") - annotation := getKluctlAnnotations(t, k) - assert.Len(t, annotation, 1, "Annotation not present") -} - -func getKluctlSuspendField(t *testing.T, k *test_utils.EnvTestCluster) interface{} { - o := getKluctlDeploymentObject(t, k) - result, ok, err := o.GetNestedField("spec", "suspend") - fmt.Println(result) - if err != nil { - t.Fatal(err) - } - if !ok { - t.Fatalf("result not found") - } - return result -} - -func getKluctlAnnotations(t *testing.T, k *test_utils.EnvTestCluster) interface{} { - o := getKluctlDeploymentObject(t, k) - result, ok, err := o.GetNestedField("metadata", "annotations") - if err != nil { - t.Fatal(err) - } - if !ok { - t.Fatalf("result not found") - } - fmt.Println(result) - return result -} diff --git a/e2e/test_resources/README.md b/e2e/test_resources/README.md index 742fd70c2..9be43ed76 100644 --- a/e2e/test_resources/README.md +++ b/e2e/test_resources/README.md @@ -3,18 +3,3 @@ helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets helm template sealed-secrets-controller sealed-secrets/sealed-secrets -n kube-system --include-crds --skip-tests > sealed-secrets.yaml ``` - -# kluctl-crds.yaml -Fetches flux-kluctl-controller crd definitions -```bash -kustomize build https://github.com/kluctl/flux-kluctl-controller/config/crd > kluctl-crds.yaml -``` - -# kluctl-deployment.yaml -This is a simple KluctlDeployment that is not really valid. It points to a non-existing GitRepository. This object is only used to test `kluctl flux` subcommands (which only sets annotations and updates field 'suspend'). - -# flux-source-crd.yaml -Fetches flux-source-controller crd definitions -```bash -kustomize build https://github.com/fluxcd/source-controller/config/crd > flux-source-crd.yaml -``` \ No newline at end of file diff --git a/e2e/test_resources/flux-source-crd.yaml b/e2e/test_resources/flux-source-crd.yaml deleted file mode 100644 index 4eda189ac..000000000 --- a/e2e/test_resources/flux-source-crd.yaml +++ /dev/null @@ -1,2623 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: buckets.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: Bucket - listKind: BucketList - plural: buckets - singular: bucket - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.endpoint - name: Endpoint - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Bucket is the Schema for the buckets API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BucketSpec defines the desired state of an S3 compatible - bucket - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - bucketName: - description: The bucket name. - type: string - endpoint: - description: The bucket endpoint address. - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS S3 HTTP endpoint. - type: boolean - interval: - description: The interval at which to check for bucket updates. - type: string - provider: - default: generic - description: The S3 compatible storage provider name, default ('generic'). - enum: - - generic - - aws - - gcp - type: string - region: - description: The bucket region. - type: string - secretRef: - description: The name of the secret containing authentication credentials - for the Bucket. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout for download operations, defaults to 60s. - type: string - required: - - bucketName - - endpoint - - interval - type: object - status: - default: - observedGeneration: -1 - description: BucketStatus defines the observed state of a bucket - properties: - artifact: - description: Artifact represents the output of the last successful - Bucket sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the Bucket. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the artifact output of the - last Bucket sync. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.endpoint - name: Endpoint - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: Bucket is the Schema for the buckets API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BucketSpec specifies the required configuration to produce - an Artifact for an object storage bucket. - properties: - accessFrom: - description: 'AccessFrom specifies an Access Control List for allowing - cross-namespace references to this object. NOTE: Not implemented, - provisional as of https://github.com/fluxcd/flux2/pull/2092' - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - bucketName: - description: BucketName is the name of the object storage bucket. - type: string - endpoint: - description: Endpoint is the object storage address the BucketName - is located at. - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS HTTP Endpoint. - type: boolean - interval: - description: Interval at which to check the Endpoint for updates. - type: string - provider: - default: generic - description: Provider of the object storage bucket. Defaults to 'generic', - which expects an S3 (API) compatible object storage. - enum: - - generic - - aws - - gcp - - azure - type: string - region: - description: Region of the Endpoint where the BucketName is located - in. - type: string - secretRef: - description: SecretRef specifies the Secret containing authentication - credentials for the Bucket. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: Suspend tells the controller to suspend the reconciliation - of this Bucket. - type: boolean - timeout: - default: 60s - description: Timeout for fetch operations, defaults to 60s. - type: string - required: - - bucketName - - endpoint - - interval - type: object - status: - default: - observedGeneration: -1 - description: BucketStatus records the observed state of a Bucket. - properties: - artifact: - description: Artifact represents the last successful Bucket reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the Bucket. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of - the Bucket object. - format: int64 - type: integer - url: - description: URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise BucketStatus.Artifact - data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: gitrepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: GitRepository - listKind: GitRepositoryList - plural: gitrepositories - shortNames: - - gitrepo - singular: gitrepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: GitRepository is the Schema for the gitrepositories API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: GitRepositorySpec defines the desired state of a Git repository. - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - gitImplementation: - default: go-git - description: Determines which git client library to use. Defaults - to go-git, valid values are ('go-git', 'libgit2'). - enum: - - go-git - - libgit2 - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - include: - description: Extra git repositories to map into the repository - items: - description: GitRepositoryInclude defines a source with a from and - to path. - properties: - fromPath: - description: The path to copy contents from, defaults to the - root directory. - type: string - repository: - description: Reference to a GitRepository to include. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - toPath: - description: The path to copy contents to, defaults to the name - of the source ref. - type: string - required: - - repository - type: object - type: array - interval: - description: The interval at which to check for repository updates. - type: string - recurseSubmodules: - description: When enabled, after the clone is created, initializes - all submodules within, using their default settings. This option - is available only when using the 'go-git' GitImplementation. - type: boolean - ref: - description: The Git reference to checkout and monitor for changes, - defaults to master branch. - properties: - branch: - description: The Git branch to checkout, defaults to master. - type: string - commit: - description: The Git commit SHA to checkout, if specified Tag - filters will be ignored. - type: string - semver: - description: The Git tag semver expression, takes precedence over - Tag. - type: string - tag: - description: The Git tag to checkout, takes precedence over Branch. - type: string - type: object - secretRef: - description: The secret name containing the Git credentials. For HTTPS - repositories the secret must contain username and password fields. - For SSH repositories the secret must contain identity and known_hosts - fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout for remote Git operations like cloning, defaults - to 60s. - type: string - url: - description: The repository URL, can be a HTTP/S or SSH address. - pattern: ^(http|https|ssh)://.*$ - type: string - verify: - description: Verify OpenPGP signature for the Git commit HEAD points - to. - properties: - mode: - description: Mode describes what git object should be verified, - currently ('head'). - enum: - - head - type: string - secretRef: - description: The secret name containing the public keys of all - trusted Git authors. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - mode - type: object - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: GitRepositoryStatus defines the observed state of a Git repository. - properties: - artifact: - description: Artifact represents the output of the last successful - repository sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the GitRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - includedArtifacts: - description: IncludedArtifacts represents the included artifacts from - the last successful repository sync. - items: - description: Artifact represents the output of a source synchronisation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the artifact output of the - last repository sync. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: GitRepository is the Schema for the gitrepositories API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: GitRepositorySpec specifies the required configuration to - produce an Artifact for a Git repository. - properties: - accessFrom: - description: 'AccessFrom specifies an Access Control List for allowing - cross-namespace references to this object. NOTE: Not implemented, - provisional as of https://github.com/fluxcd/flux2/pull/2092' - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - gitImplementation: - default: go-git - description: GitImplementation specifies which Git client library - implementation to use. Defaults to 'go-git', valid values are ('go-git', - 'libgit2'). - enum: - - go-git - - libgit2 - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - include: - description: Include specifies a list of GitRepository resources which - Artifacts should be included in the Artifact produced for this GitRepository. - items: - description: GitRepositoryInclude specifies a local reference to - a GitRepository which Artifact (sub-)contents must be included, - and where they should be placed. - properties: - fromPath: - description: FromPath specifies the path to copy contents from, - defaults to the root of the Artifact. - type: string - repository: - description: GitRepositoryRef specifies the GitRepository which - Artifact contents must be included. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - toPath: - description: ToPath specifies the path to copy contents to, - defaults to the name of the GitRepositoryRef. - type: string - required: - - repository - type: object - type: array - interval: - description: Interval at which to check the GitRepository for updates. - type: string - recurseSubmodules: - description: RecurseSubmodules enables the initialization of all submodules - within the GitRepository as cloned from the URL, using their default - settings. This option is available only when using the 'go-git' - GitImplementation. - type: boolean - ref: - description: Reference specifies the Git reference to resolve and - monitor for changes, defaults to the 'master' branch. - properties: - branch: - description: "Branch to check out, defaults to 'master' if no - other field is defined. \n When GitRepositorySpec.GitImplementation - is set to 'go-git', a shallow clone of the specified branch - is performed." - type: string - commit: - description: "Commit SHA to check out, takes precedence over all - reference fields. \n When GitRepositorySpec.GitImplementation - is set to 'go-git', this can be combined with Branch to shallow - clone the branch, in which the commit is expected to exist." - type: string - semver: - description: SemVer tag expression to check out, takes precedence - over Tag. - type: string - tag: - description: Tag to check out, takes precedence over Branch. - type: string - type: object - secretRef: - description: SecretRef specifies the Secret containing authentication - credentials for the GitRepository. For HTTPS repositories the Secret - must contain 'username' and 'password' fields. For SSH repositories - the Secret must contain 'identity' and 'known_hosts' fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: Suspend tells the controller to suspend the reconciliation - of this GitRepository. - type: boolean - timeout: - default: 60s - description: Timeout for Git operations like cloning, defaults to - 60s. - type: string - url: - description: URL specifies the Git repository URL, it can be an HTTP/S - or SSH address. - pattern: ^(http|https|ssh)://.*$ - type: string - verify: - description: Verification specifies the configuration to verify the - Git commit signature(s). - properties: - mode: - description: Mode specifies what Git object should be verified, - currently ('head'). - enum: - - head - type: string - secretRef: - description: SecretRef specifies the Secret containing the public - keys of trusted Git authors. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - mode - type: object - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: GitRepositoryStatus records the observed state of a Git repository. - properties: - artifact: - description: Artifact represents the last successful GitRepository - reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the GitRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - contentConfigChecksum: - description: 'ContentConfigChecksum is a checksum of all the configurations - related to the content of the source artifact: - .spec.ignore - - .spec.recurseSubmodules - .spec.included and the checksum of the - included artifacts observed in .status.observedGeneration version - of the object. This can be used to determine if the content of the - included repository has changed. It has the format of `:`, - for example: `sha256:`.' - type: string - includedArtifacts: - description: IncludedArtifacts contains a list of the last successfully - included Artifacts as instructed by GitRepositorySpec.Include. - items: - description: Artifact represents the output of a Source reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact - file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI - annotations. - type: object - path: - description: Path is the relative file path of the Artifact. - It can be used to locate the file in the root of the Artifact - storage on the local file system of the controller managing - the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of - the GitRepository object. - format: int64 - type: integer - url: - description: URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise GitRepositoryStatus.Artifact - data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: helmcharts.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: HelmChart - listKind: HelmChartList - plural: helmcharts - shortNames: - - hc - singular: helmchart - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.chart - name: Chart - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.sourceRef.kind - name: Source Kind - type: string - - jsonPath: .spec.sourceRef.name - name: Source Name - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: HelmChart is the Schema for the helmcharts API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmChartSpec defines the desired state of a Helm chart. - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - chart: - description: The name or path the Helm chart is available at in the - SourceRef. - type: string - interval: - description: The interval at which to check the Source for updates. - type: string - reconcileStrategy: - default: ChartVersion - description: Determines what enables the creation of a new artifact. - Valid values are ('ChartVersion', 'Revision'). See the documentation - of the values for an explanation on their behavior. Defaults to - ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: The reference to the Source the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent, valid values are ('HelmRepository', - 'GitRepository', 'Bucket'). - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - required: - - kind - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - valuesFile: - description: Alternative values file to use as the default chart values, - expected to be a relative path in the SourceRef. Deprecated in favor - of ValuesFiles, for backwards compatibility the file defined here - is merged before the ValuesFiles items. Ignored when omitted. - type: string - valuesFiles: - description: Alternative list of values files to use as the chart - values (values.yaml is not included by default), expected to be - a relative path in the SourceRef. Values files are merged in the - order of this list with the last file overriding the first. Ignored - when omitted. - items: - type: string - type: array - version: - default: '*' - description: The chart version semver expression, ignored for charts - from GitRepository and Bucket sources. Defaults to latest when omitted. - type: string - required: - - chart - - interval - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: HelmChartStatus defines the observed state of the HelmChart. - properties: - artifact: - description: Artifact represents the output of the last successful - chart sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmChart. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the last chart pulled. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.chart - name: Chart - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.sourceRef.kind - name: Source Kind - type: string - - jsonPath: .spec.sourceRef.name - name: Source Name - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: HelmChart is the Schema for the helmcharts API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmChartSpec specifies the desired state of a Helm chart. - properties: - accessFrom: - description: 'AccessFrom specifies an Access Control List for allowing - cross-namespace references to this object. NOTE: Not implemented, - provisional as of https://github.com/fluxcd/flux2/pull/2092' - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - chart: - description: Chart is the name or path the Helm chart is available - at in the SourceRef. - type: string - interval: - description: Interval is the interval at which to check the Source - for updates. - type: string - reconcileStrategy: - default: ChartVersion - description: ReconcileStrategy determines what enables the creation - of a new artifact. Valid values are ('ChartVersion', 'Revision'). - See the documentation of the values for an explanation on their - behavior. Defaults to ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: SourceRef is the reference to the Source the chart is - available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent, valid values are ('HelmRepository', - 'GitRepository', 'Bucket'). - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - required: - - kind - - name - type: object - suspend: - description: Suspend tells the controller to suspend the reconciliation - of this source. - type: boolean - valuesFile: - description: ValuesFile is an alternative values file to use as the - default chart values, expected to be a relative path in the SourceRef. - Deprecated in favor of ValuesFiles, for backwards compatibility - the file specified here is merged before the ValuesFiles items. - Ignored when omitted. - type: string - valuesFiles: - description: ValuesFiles is an alternative list of values files to - use as the chart values (values.yaml is not included by default), - expected to be a relative path in the SourceRef. Values files are - merged in the order of this list with the last file overriding the - first. Ignored when omitted. - items: - type: string - type: array - version: - default: '*' - description: Version is the chart version semver expression, ignored - for charts from GitRepository and Bucket sources. Defaults to latest - when omitted. - type: string - required: - - chart - - interval - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: HelmChartStatus records the observed state of the HelmChart. - properties: - artifact: - description: Artifact represents the output of the last successful - reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmChart. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedChartName: - description: ObservedChartName is the last observed chart name as - specified by the resolved chart reference. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of - the HelmChart object. - format: int64 - type: integer - observedSourceArtifactRevision: - description: ObservedSourceArtifactRevision is the last observed Artifact.Revision - of the HelmChartSpec.SourceRef. - type: string - url: - description: URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise BucketStatus.Artifact - data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: helmrepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: HelmRepository - listKind: HelmRepositoryList - plural: helmrepositories - shortNames: - - helmrepo - singular: helmrepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: HelmRepository is the Schema for the helmrepositories API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmRepositorySpec defines the reference to a Helm repository. - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - interval: - description: The interval at which to check the upstream for updates. - type: string - passCredentials: - description: PassCredentials allows the credentials from the SecretRef - to be passed on to a host that does not match the host as defined - in URL. This may be required if the host of the advertised chart - URLs in the index differ from the defined URL. Enabling this should - be done with caution, as it can potentially result in credentials - getting stolen in a MITM-attack. - type: boolean - secretRef: - description: The name of the secret containing authentication credentials - for the Helm repository. For HTTP/S basic auth the secret must contain - username and password fields. For TLS the secret must contain a - certFile and keyFile, and/or caCert fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout of index downloading, defaults to 60s. - type: string - url: - description: The Helm repository URL, a valid URL contains at least - a protocol and host. - type: string - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: HelmRepositoryStatus defines the observed state of the HelmRepository. - properties: - artifact: - description: Artifact represents the output of the last successful - repository sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the last index fetched. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: HelmRepository is the Schema for the helmrepositories API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmRepositorySpec specifies the required configuration to - produce an Artifact for a Helm repository index YAML. - properties: - accessFrom: - description: 'AccessFrom specifies an Access Control List for allowing - cross-namespace references to this object. NOTE: Not implemented, - provisional as of https://github.com/fluxcd/flux2/pull/2092' - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - interval: - description: Interval at which to check the URL for updates. - type: string - passCredentials: - description: PassCredentials allows the credentials from the SecretRef - to be passed on to a host that does not match the host as defined - in URL. This may be required if the host of the advertised chart - URLs in the index differ from the defined URL. Enabling this should - be done with caution, as it can potentially result in credentials - getting stolen in a MITM-attack. - type: boolean - provider: - default: generic - description: Provider used for authentication, can be 'aws', 'azure', - 'gcp' or 'generic'. This field is optional, and only taken into - account if the .spec.type field is set to 'oci'. When not specified, - defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - secretRef: - description: SecretRef specifies the Secret containing authentication - credentials for the HelmRepository. For HTTP/S basic auth the secret - must contain 'username' and 'password' fields. For TLS the secret - must contain a 'certFile' and 'keyFile', and/or 'caCert' fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: Suspend tells the controller to suspend the reconciliation - of this HelmRepository. - type: boolean - timeout: - default: 60s - description: Timeout is used for the index fetch operation for an - HTTPS helm repository, and for remote OCI Repository operations - like pulling for an OCI helm repository. Its default value is 60s. - type: string - type: - description: Type of the HelmRepository. When this field is set to "oci", - the URL field value must be prefixed with "oci://". - enum: - - default - - oci - type: string - url: - description: URL of the Helm repository, a valid URL contains at least - a protocol and host. - type: string - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: HelmRepositoryStatus records the observed state of the HelmRepository. - properties: - artifact: - description: Artifact represents the last successful HelmRepository - reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of - the HelmRepository object. - format: int64 - type: integer - url: - description: URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise HelmRepositoryStatus.Artifact - data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: ocirepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: OCIRepository - listKind: OCIRepositoryList - plural: ocirepositories - shortNames: - - ocirepo - singular: ocirepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta2 - schema: - openAPIV3Schema: - description: OCIRepository is the Schema for the ocirepositories API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: OCIRepositorySpec defines the desired state of OCIRepository - properties: - certSecretRef: - description: "CertSecretRef can be given the name of a secret containing - either or both of \n - a PEM-encoded client certificate (`certFile`) - and private key (`keyFile`); - a PEM-encoded CA certificate (`caFile`) - \n and whichever are supplied, will be used for connecting to the - \ registry. The client cert and key are useful if you are authenticating - with a certificate; the CA cert is useful if you are using a self-signed - server certificate." - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS HTTP container - registry. - type: boolean - interval: - description: The interval at which to check for image updates. - type: string - layerSelector: - description: LayerSelector specifies which layer should be extracted - from the OCI artifact. When not specified, the first layer found - in the artifact is selected. - properties: - mediaType: - description: MediaType specifies the OCI media type of the layer - which should be extracted from the OCI Artifact. The first layer - matching this type is selected. - type: string - type: object - provider: - default: generic - description: The provider used for authentication, can be 'aws', 'azure', - 'gcp' or 'generic'. When not specified, defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - ref: - description: The OCI reference to pull and monitor for changes, defaults - to the latest tag. - properties: - digest: - description: Digest is the image digest to pull, takes precedence - over SemVer. The value should be in the format 'sha256:'. - type: string - semver: - description: SemVer is the range of tags to pull selecting the - latest within the range, takes precedence over Tag. - type: string - tag: - description: Tag is the image tag to pull, defaults to latest. - type: string - type: object - secretRef: - description: SecretRef contains the secret name containing the registry - login credentials to resolve image metadata. The secret must be - of type kubernetes.io/dockerconfigjson. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: 'ServiceAccountName is the name of the Kubernetes ServiceAccount - used to authenticate the image pull if the service account has attached - pull secrets. For more information: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account' - type: string - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout for remote OCI Repository operations like - pulling, defaults to 60s. - type: string - url: - description: URL is a reference to an OCI artifact repository hosted - on a remote container registry. - pattern: ^oci://.*$ - type: string - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: OCIRepositoryStatus defines the observed state of OCIRepository - properties: - artifact: - description: Artifact represents the output of the last successful - OCI Repository sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the OCIRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the artifact output of the - last OCI Repository sync. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/e2e/test_resources/kluctl-crds.yaml b/e2e/test_resources/kluctl-crds.yaml deleted file mode 100644 index 83b9cffcc..000000000 --- a/e2e/test_resources/kluctl-crds.yaml +++ /dev/null @@ -1,567 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - name: kluctldeployments.flux.kluctl.io -spec: - group: flux.kluctl.io - names: - kind: KluctlDeployment - listKind: KluctlDeploymentList - plural: kluctldeployments - singular: kluctldeployment - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.lastDeployResult.time - name: Deployed - type: date - - jsonPath: .status.lastPruneResult.time - name: Pruned - type: date - - jsonPath: .status.lastValidateResult.time - name: Validated - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: KluctlDeployment is the Schema for the kluctldeployments API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - properties: - abortOnError: - default: false - description: ForceReplaceOnError instructs kluctl to abort deployments - immediately when something fails. Equivalent to using '--abort-on-error' - when calling kluctl. - type: boolean - args: - description: Args specifies dynamic target args. - type: object - x-kubernetes-preserve-unknown-fields: true - context: - description: If specified, overrides the context to be used. This - will effectively make kluctl ignore the context specified in the - target. - type: string - decryption: - description: Decrypt Kubernetes secrets before applying them on the - cluster. - properties: - provider: - description: Provider is the name of the decryption engine. - enum: - - sops - type: string - secretRef: - description: The secret name containing the private OpenPGP keys - used for decryption. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - deployInterval: - description: DeployInterval specifies the interval at which to deploy - the KluctlDeployment. It defaults to the Interval value, meaning - that it will re-deploy on every reconciliation. If you set DeployInterval - to a different value, - pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ - type: string - deployMode: - default: full-deploy - description: DeployMode specifies what deploy mode should be used - enum: - - full-deploy - - poke-images - type: string - deployOnChanges: - default: true - description: DeployOnChanges will cause a re-deployment whenever the - rendered resources change in the deployment. This check is performed - on every reconciliation. This means that a deployment will be triggered - even before the DeployInterval has passed in case something has - changed in the rendered resources. - type: boolean - dryRun: - default: false - description: DryRun instructs kluctl to run everything in dry-run - mode. Equivalent to using '--dry-run' when calling kluctl. - type: boolean - excludeDeploymentDirs: - description: ExcludeDeploymentDirs instructs kluctl to exclude deployments - with the given dir. Equivalent to using '--exclude-deployment-dir' - when calling kluctl. - items: - type: string - type: array - excludeTags: - description: ExcludeTags instructs kluctl to exclude deployments with - given tags. Equivalent to using '--exclude-tag' when calling kluctl. - items: - type: string - type: array - forceApply: - default: false - description: ForceApply instructs kluctl to force-apply in case of - SSA conflicts. Equivalent to using '--force-apply' when calling - kluctl. - type: boolean - forceReplaceOnError: - default: false - description: ForceReplaceOnError instructs kluctl to force-replace - resources in case a normal replace fails. Equivalent to using '--force-replace-on-error' - when calling kluctl. - type: boolean - images: - description: Images contains a list of fixed image overrides. Equivalent - to using '--fixed-images-file' when calling kluctl. - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - description: ObjectRef contains the information necessary to - locate a resource within a cluster. - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - group - - kind - - name - - namespace - - version - type: object - registryImage: - type: string - resultImage: - type: string - versionFilter: - type: string - required: - - image - - resultImage - type: object - type: array - includeDeploymentDirs: - description: IncludeDeploymentDirs instructs kluctl to only include - deployments with the given dir. Equivalent to using '--include-deployment-dir' - when calling kluctl. - items: - type: string - type: array - includeTags: - description: IncludeTags instructs kluctl to only include deployments - with given tags. Equivalent to using '--include-tag' when calling - kluctl. - items: - type: string - type: array - interval: - description: The interval at which to reconcile the KluctlDeployment. - By default, the controller will re-deploy and validate the deployment - on each reconciliation. To override this behavior, change the DeployInterval - and/or ValidateInterval values. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - kubeConfig: - description: The KubeConfig for deploying to the target cluster. Specifies - the kubeconfig to be used when invoking kluctl. Contexts in this - kubeconfig must match the context found in the kluctl target. As - an alternative, specify the context to be used via 'context' - properties: - secretRef: - description: SecretRef holds the name of a secret that contains - a key with the kubeconfig file as the value. If no key is set, - the key will default to 'value'. The secret must be in the same - namespace as the Kustomization. It is recommended that the kubeconfig - is self-contained, and the secret is regularly updated if credentials - such as a cloud-access-token expire. Cloud specific `cmd-path` - auth helpers will not function without adding binaries and credentials - to the Pod that is responsible for reconciling the KluctlDeployment. - properties: - key: - description: Key in the Secret, when not specified an implementation-specific - default key is used. - type: string - name: - description: Name of the Secret. - type: string - required: - - name - type: object - type: object - noWait: - default: false - description: NoWait instructs kluctl to not wait for any resources - to become ready, including hooks. Equivalent to using '--no-wait' - when calling kluctl. - type: boolean - path: - description: Path to the directory containing the .kluctl.yaml file, - or the Defaults to 'None', which translates to the root path of - the SourceRef. - type: string - prune: - default: false - description: Prune enables pruning after deploying. - type: boolean - registrySecrets: - description: RegistrySecrets is a list of secret references to be - used for image registry authentication. The secrets must either - have ".dockerconfigjson" included or "registry", "username" and - "password". Additionally, "caFile" and "insecure" can be specified. - items: - description: LocalObjectReference contains enough information to - locate the referenced Kubernetes resource object. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - type: array - renameContexts: - description: RenameContexts specifies a list of context rename operations. - This is useful when the kluctl target's context does not match with - the contexts found in the kubeconfig while deploying. This is the - case when using kubeconfigs generated from service accounts, in - which case the context name is always "default". - items: - description: RenameContext specifies a single rename of a context - properties: - newContext: - description: NewContext is the new name of the context - type: string - oldContext: - description: OldContext is the name of the context to be renamed - type: string - required: - - newContext - - oldContext - type: object - type: array - replaceOnError: - default: false - description: ReplaceOnError instructs kluctl to replace resources - on error. Equivalent to using '--replace-on-error' when calling - kluctl. - type: boolean - retryInterval: - description: The interval at which to retry a previously failed reconciliation. - When not specified, the controller uses the Interval value to retry - failures. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - serviceAccountName: - description: The name of the Kubernetes service account to use while - deploying. If not specified, the default service account is used. - type: string - sourceRef: - description: Reference of the source where the kluctl project is. - The authentication secrets from the source are also used to authenticate - dependent git repositories which are cloned while deploying the - kluctl project. - properties: - apiVersion: - description: API version of the referent, if not specified the - Kubernetes preferred version will be used. - type: string - kind: - description: Kind of the referent. - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - kind - - name - type: object - suspend: - description: This flag tells the controller to suspend subsequent - kluctl executions, it does not apply to already started executions. - Defaults to false. - type: boolean - target: - description: Target specifies the kluctl target to deploy. If not - specified, an empty target is used that has no name and no context. - Use 'TargetName' and 'Context' to specify the name and context in - that case. - maxLength: 63 - minLength: 1 - type: string - targetNameOverride: - description: TargetNameOverride sets or overrides the target name. - This is especially useful when deployment without a target. - maxLength: 63 - minLength: 1 - type: string - timeout: - description: Timeout for all operations. Defaults to 'Interval' duration. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - updateImages: - default: false - description: UpdateImages instructs kluctl to update dynamic images. - Equivalent to using '-u' when calling kluctl. - type: boolean - validate: - default: true - description: Validate enables validation after deploying - type: boolean - validateInterval: - description: ValidateInterval specifies the interval at which to validate - the KluctlDeployment. Validation is performed the same way as with - 'kluctl validate -t '. Defaults to the same value as specified - in Interval. Validate is also performed whenever a deployment is - performed, independent of the value of ValidateInterval - pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ - type: string - required: - - interval - - sourceRef - type: object - status: - description: KluctlDeploymentStatus defines the observed state of KluctlDeployment - properties: - commonLabels: - additionalProperties: - type: string - description: CommonLabels are the commonLabels found in the deployment - project when the last deployment was done. This is used to perform - cleanup/deletion in case the KluctlDeployment project is deleted - type: object - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: 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. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - 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])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation - attempt. - type: string - lastDeployResult: - description: LastDeployResult is the result of the last deploy command - properties: - error: - type: string - objectsHash: - description: ObjectsHash is the hash of all rendered objects - type: string - rawResult: - type: string - revision: - description: Revision is the source revision. Please note that - kluctl projects have dependent git repositories which are not - considered in the source revision - type: string - target: - type: string - targetNameOverride: - type: string - time: - description: AttemptedAt is the time when the attempt was performed - format: date-time - type: string - required: - - time - type: object - lastHandledDeployAt: - type: string - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - lastPruneResult: - description: LastDeployResult is the result of the last prune command - properties: - error: - type: string - objectsHash: - description: ObjectsHash is the hash of all rendered objects - type: string - rawResult: - type: string - revision: - description: Revision is the source revision. Please note that - kluctl projects have dependent git repositories which are not - considered in the source revision - type: string - target: - type: string - targetNameOverride: - type: string - time: - description: AttemptedAt is the time when the attempt was performed - format: date-time - type: string - required: - - time - type: object - lastValidateResult: - description: LastValidateResult is the result of the last validate - command - properties: - error: - type: string - objectsHash: - description: ObjectsHash is the hash of all rendered objects - type: string - rawResult: - type: string - revision: - description: Revision is the source revision. Please note that - kluctl projects have dependent git repositories which are not - considered in the source revision - type: string - target: - type: string - targetNameOverride: - type: string - time: - description: AttemptedAt is the time when the attempt was performed - format: date-time - type: string - required: - - time - type: object - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - rawTarget: - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/e2e/test_resources/kluctl-deployment.yaml b/e2e/test_resources/kluctl-deployment.yaml deleted file mode 100644 index eae1bec80..000000000 --- a/e2e/test_resources/kluctl-deployment.yaml +++ /dev/null @@ -1,35 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: flux-test ---- -apiVersion: flux.kluctl.io/v1alpha1 -kind: KluctlDeployment -metadata: - name: microservices-demo-test - namespace: flux-test -spec: - interval: 10m - path: ./simple - prune: true - renameContexts: - - newContext: kind-kind - oldContext: default - sourceRef: - kind: GitRepository - name: microservices-demo - namespace: flux-test - target: simple - timeout: 2m ---- -apiVersion: source.toolkit.fluxcd.io/v1beta1 -kind: GitRepository -metadata: - name: microservices-demo - namespace: flux-test -spec: - interval: 5m - ref: - branch: main - url: https://github.com/gitbluf/kluctl-examples.git From 39b6b2e11ed12869a2b1c5c28b7db0f62e980ec6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 14:25:45 +0200 Subject: [PATCH 0983/2268] chore: Add flux_utils package This package contains some code from the flux codebase. It is used to keep some compatibility to the old flux-kluctl-controller codebase. The plan is to remove this code as well in the long-term. --- pkg/utils/flux_utils/client.go | 84 +++++++++++++++++ pkg/utils/flux_utils/conditions/doc.go | 24 +++++ pkg/utils/flux_utils/conditions/getter.go | 52 +++++++++++ pkg/utils/flux_utils/conditions/setter.go | 16 ++++ pkg/utils/flux_utils/meta/conditions.go | 93 +++++++++++++++++++ pkg/utils/flux_utils/metrics.go | 106 +++++++++++++++++++++ pkg/utils/flux_utils/metrics/doc.go | 18 ++++ pkg/utils/flux_utils/metrics/recorder.go | 108 ++++++++++++++++++++++ 8 files changed, 501 insertions(+) create mode 100644 pkg/utils/flux_utils/client.go create mode 100644 pkg/utils/flux_utils/conditions/doc.go create mode 100644 pkg/utils/flux_utils/conditions/getter.go create mode 100644 pkg/utils/flux_utils/conditions/setter.go create mode 100644 pkg/utils/flux_utils/meta/conditions.go create mode 100644 pkg/utils/flux_utils/metrics.go create mode 100644 pkg/utils/flux_utils/metrics/doc.go create mode 100644 pkg/utils/flux_utils/metrics/recorder.go diff --git a/pkg/utils/flux_utils/client.go b/pkg/utils/flux_utils/client.go new file mode 100644 index 000000000..05e796cc7 --- /dev/null +++ b/pkg/utils/flux_utils/client.go @@ -0,0 +1,84 @@ +/* +Copyright 2021 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package flux_utils + +import ( + "context" + + "github.com/spf13/pflag" + "k8s.io/client-go/rest" + "sigs.k8s.io/cli-utils/pkg/flowcontrol" + ctrl "sigs.k8s.io/controller-runtime" +) + +const ( + flagQPS = "kube-api-qps" + flagBurst = "kube-api-burst" +) + +// Options contains the runtime configuration for a Kubernetes client. +// +// The struct can be used in the main.go file of your controller by binding it to the main flag set, and then utilizing +// the configured options later: +// +// func main() { +// var ( +// // other controller specific configuration variables +// clientOptions client.Options +// ) +// +// // Bind the options to the main flag set, and parse it +// clientOptions.BindFlags(flag.CommandLine) +// flag.Parse() +// +// // Get a runtime Kubernetes client configuration with the options set +// restConfig := client.GetConfigOrDie(clientOptions) +// } +type Options struct { + // QPS indicates the maximum queries-per-second of requests sent to the Kubernetes API, defaults to 50. + QPS float32 + + // Burst indicates the maximum burst queries-per-second of requests sent to the Kubernetes API, defaults to 300. + Burst int +} + +// BindFlags will parse the given pflag.FlagSet for Kubernetes client option flags and set the Options accordingly. +func (o *Options) BindFlags(fs *pflag.FlagSet) { + fs.Float32Var(&o.QPS, flagQPS, 50.0, + "The maximum queries-per-second of requests sent to the Kubernetes API.") + fs.IntVar(&o.Burst, flagBurst, 300, + "The maximum burst queries-per-second of requests sent to the Kubernetes API.") +} + +// GetConfigOrDie wraps ctrl.GetConfigOrDie and checks if the Kubernetes apiserver +// has PriorityAndFairness flow control filter enabled. If true, it returns a rest.Config +// with client side throttling disabled. Otherwise, it returns a modified rest.Config +// configured with the provided Options. +func GetConfigOrDie(opts Options) *rest.Config { + config := ctrl.GetConfigOrDie() + enabled, err := flowcontrol.IsEnabled(context.Background(), config) + if err == nil && enabled { + // A negative QPS and Burst indicates that the client should not have a rate limiter. + // Ref: https://github.com/kubernetes/kubernetes/blob/v1.24.0/staging/src/k8s.io/client-go/rest/config.go#L354-L364 + config.QPS = -1 + config.Burst = -1 + return config + } + config.QPS = opts.QPS + config.Burst = opts.Burst + return config +} diff --git a/pkg/utils/flux_utils/conditions/doc.go b/pkg/utils/flux_utils/conditions/doc.go new file mode 100644 index 000000000..9dfbc882f --- /dev/null +++ b/pkg/utils/flux_utils/conditions/doc.go @@ -0,0 +1,24 @@ +/* +Copyright 2021 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package conditions provides utilities for manipulating the status conditions of Kubernetes resource objects that +// implement the Getter and/or Setter interfaces. +// +// Usage of this package within GitOps Toolkit components working with conditions is RECOMMENDED, as it provides a wide +// range of helpers to work around common reconciler problems, like setting a Condition status based on a +// summarization of other conditions, producing an aggregate Condition based on the conditions of a list of Kubernetes +// resources objects, recognition of negative polarity or "abnormal-true" conditions, etc. +package conditions diff --git a/pkg/utils/flux_utils/conditions/getter.go b/pkg/utils/flux_utils/conditions/getter.go new file mode 100644 index 000000000..594625f39 --- /dev/null +++ b/pkg/utils/flux_utils/conditions/getter.go @@ -0,0 +1,52 @@ +/* +Copyright 2020 The Kubernetes Authors. +Copyright 2021 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +This file is modified from the source at +https://github.com/kubernetes-sigs/cluster-api/tree/7478817225e0a75acb6e14fc7b438231578073d2/util/conditions/getter.go, +and initially adapted to work with the `metav1.Condition` and `metav1.ConditionStatus` types. +More concretely, this includes the removal of "condition severity" related functionalities, as this is not supported by +the `metav1.Condition` type. +*/ + +package conditions + +import ( + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// Getter interface defines methods that a Kubernetes resource object should implement in order to use the conditions +// package for getting conditions. +type Getter interface { + client.Object + meta.ObjectWithConditions +} + +// Get returns the condition with the given type, if the condition does not exists, it returns nil. +func Get(from Getter, t string) *metav1.Condition { + conditions := from.GetConditions() + if conditions == nil { + return nil + } + + for _, condition := range conditions { + if condition.Type == t { + return &condition + } + } + return nil +} diff --git a/pkg/utils/flux_utils/conditions/setter.go b/pkg/utils/flux_utils/conditions/setter.go new file mode 100644 index 000000000..85aab127e --- /dev/null +++ b/pkg/utils/flux_utils/conditions/setter.go @@ -0,0 +1,16 @@ +package conditions + +import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// UnknownCondition returns a condition with Status=Unknown and the given type, reason and message. +func UnknownCondition(t, reason, messageFormat string, messageArgs ...interface{}) *metav1.Condition { + return &metav1.Condition{ + Type: t, + Status: metav1.ConditionUnknown, + Reason: reason, + Message: fmt.Sprintf(messageFormat, messageArgs...), + } +} diff --git a/pkg/utils/flux_utils/meta/conditions.go b/pkg/utils/flux_utils/meta/conditions.go new file mode 100644 index 000000000..fe798792f --- /dev/null +++ b/pkg/utils/flux_utils/meta/conditions.go @@ -0,0 +1,93 @@ +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package meta + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// These constants define generic Condition types to be used by GitOps Toolkit components. +// +// The ReadyCondition SHOULD be implemented by all components' Kubernetes resources to indicate they have been fully +// reconciled by their respective reconciler. This MAY suffice for simple resources, e.g. a resource that just declares +// state once and is not expected to receive any updates afterwards. +// +// For Kubernetes resources that are expected to receive spec updates over time, take a longer time to reconcile, or +// deal with more complex logic in which for example a finite error state can be observed, it is RECOMMENDED to +// implement the StalledCondition and ReconcilingCondition. +// +// By doing this, observers making use of kstatus to determine the current state of the resource will have a better +// experience while they are e.g. waiting for a change to be reconciled, and will be able to stop waiting for a change +// if a StalledCondition is observed, without having to rely on a timeout. +// +// For more information on kstatus, see: +// https://github.com/kubernetes-sigs/cli-utils/blob/v0.25.0/pkg/kstatus/README.md +const ( + // ReadyCondition indicates the resource is ready and fully reconciled. + // If the Condition is False, the resource SHOULD be considered to be in the process of reconciling and not a + // representation of actual state. + ReadyCondition string = "Ready" + + // StalledCondition indicates the reconciliation of the resource has stalled, e.g. because the controller has + // encountered an error during the reconcile process or it has made insufficient progress (timeout). + // The Condition adheres to an "abnormal-true" polarity pattern, and MUST only be present on the resource if the + // Condition is True. + // For more information about polarity patterns, see: + // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + StalledCondition string = "Stalled" + + // ReconcilingCondition indicates the controller is currently working on reconciling the latest changes. This MAY be + // True for multiple reconciliation attempts, e.g. when an transient error occurred. + // The Condition adheres to an "abnormal-true" polarity pattern, and MUST only be present on the resource if the + // Condition is True. + // For more information about polarity patterns, see: + // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + ReconcilingCondition string = "Reconciling" +) + +// These constants define generic Condition reasons to be used by GitOps Toolkit components. +// +// Making use of a generic Reason is RECOMMENDED whenever it can be applied to a Condition in which it provides +// sufficient context together with the type to summarize the meaning of the Condition cause. +// +// Where any of the generic Condition reasons does not suffice, GitOps Toolkit components can introduce new reasons to +// their API specification, or use an arbitrary PascalCase string when setting the Condition. +// Declaration of domain common Condition reasons in the API specification is RECOMMENDED, as it eases observations +// for user and computer. +// +// For more information on Condition reason conventions, see: +// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties +const ( + + // ProgressingReason indicates a condition or event observed progression, for example when the reconciliation of a + // resource or an action has started. + // + // When this reason is given, other conditions and types MAY no longer be considered as an up-to-date observation. + // Producers of the specific condition type or event SHOULD provide more information about the expectations and + // precise meaning in their API specification. + // + // More information about the reason or the current state of the progression MAY be available as additional metadata + // in an attached message. + ProgressingReason string = "Progressing" +) + +// ObjectWithConditions describes a Kubernetes resource object with status conditions. +// +k8s:deepcopy-gen=false +type ObjectWithConditions interface { + // GetConditions returns a slice of metav1.Condition + GetConditions() []metav1.Condition +} diff --git a/pkg/utils/flux_utils/metrics.go b/pkg/utils/flux_utils/metrics.go new file mode 100644 index 000000000..afb122d74 --- /dev/null +++ b/pkg/utils/flux_utils/metrics.go @@ -0,0 +1,106 @@ +/* +Copyright 2020 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package flux_utils + +import ( + "context" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/conditions" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" + "time" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/reference" +) + +// Metrics is a helper struct that adds the capability for recording GitOps Toolkit standard metrics to a reconciler. +// +// Use it by embedding it in your reconciler struct: +// +// type MyTypeReconciler { +// client.Client +// // ... etc. +// controller.Metrics +// } +// +// Following the GitOps Toolkit conventions, API types used in GOTK SHOULD implement conditions.Getter to work with +// status condition types, and this convention MUST be followed to be able to record metrics using this helper. +// +// Use MustMakeMetrics to create a working Metrics value; you can supply the same value to all reconcilers. +// +// Once initialised, metrics can be recorded by calling one of the available `Record*` methods. +type Metrics struct { + Scheme *runtime.Scheme + MetricsRecorder *metrics.Recorder +} + +// RecordDuration records the duration of a reconcile attempt for the given obj based on the given startTime. +func (m Metrics) RecordDuration(ctx context.Context, obj conditions.Getter, startTime time.Time) { + if m.MetricsRecorder != nil { + ref, err := reference.GetReference(m.Scheme, obj) + if err != nil { + logr.FromContextOrDiscard(ctx).Error(err, "unable to get object reference to record duration") + return + } + m.MetricsRecorder.RecordDuration(*ref, startTime) + } +} + +// RecordSuspend records the suspension of the given obj based on the given suspend value. +func (m Metrics) RecordSuspend(ctx context.Context, obj conditions.Getter, suspend bool) { + if m.MetricsRecorder != nil { + ref, err := reference.GetReference(m.Scheme, obj) + if err != nil { + logr.FromContextOrDiscard(ctx).Error(err, "unable to get object reference to record suspend") + return + } + m.MetricsRecorder.RecordSuspend(*ref, suspend) + } +} + +// RecordReadiness records the flux_utils.ReadyCondition status for the given obj. +func (m Metrics) RecordReadiness(ctx context.Context, obj conditions.Getter) { + m.RecordCondition(ctx, obj, meta.ReadyCondition) +} + +// RecordReconciling records the flux_utils.ReconcilingCondition status for the given obj. +func (m Metrics) RecordReconciling(ctx context.Context, obj conditions.Getter) { + m.RecordCondition(ctx, obj, meta.ReconcilingCondition) +} + +// RecordStalled records the flux_utils.StalledCondition status for the given obj. +func (m Metrics) RecordStalled(ctx context.Context, obj conditions.Getter) { + m.RecordCondition(ctx, obj, meta.StalledCondition) +} + +// RecordCondition records the status of the given conditionType for the given obj. +func (m Metrics) RecordCondition(ctx context.Context, obj conditions.Getter, conditionType string) { + if m.MetricsRecorder == nil { + return + } + ref, err := reference.GetReference(m.Scheme, obj) + if err != nil { + logr.FromContextOrDiscard(ctx).Error(err, "unable to get object reference to record condition metric") + return + } + rc := conditions.Get(obj, conditionType) + if rc == nil { + rc = conditions.UnknownCondition(conditionType, "", "") + } + m.MetricsRecorder.RecordCondition(*ref, *rc, !obj.GetDeletionTimestamp().IsZero()) +} diff --git a/pkg/utils/flux_utils/metrics/doc.go b/pkg/utils/flux_utils/metrics/doc.go new file mode 100644 index 000000000..19e456747 --- /dev/null +++ b/pkg/utils/flux_utils/metrics/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2021 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package metrics contains a Recorder and helpers for recoding standard metrics for all GitOps Toolkit components. +package metrics diff --git a/pkg/utils/flux_utils/metrics/recorder.go b/pkg/utils/flux_utils/metrics/recorder.go new file mode 100644 index 000000000..88ead2fe6 --- /dev/null +++ b/pkg/utils/flux_utils/metrics/recorder.go @@ -0,0 +1,108 @@ +/* +Copyright 2021 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package metrics + +import ( + "time" + + "github.com/prometheus/client_golang/prometheus" + corev1 "k8s.io/api/core/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + ConditionDeleted = "Deleted" +) + +// Recorder is a struct for recording GitOps Toolkit metrics for a controller. +// +// Use NewRecorder to initialise it with properly configured metric names. +type Recorder struct { + conditionGauge *prometheus.GaugeVec + suspendGauge *prometheus.GaugeVec + durationHistogram *prometheus.HistogramVec +} + +// NewRecorder returns a new Recorder with all metric names configured confirm GitOps Toolkit standards. +func NewRecorder() *Recorder { + return &Recorder{ + conditionGauge: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "gotk_reconcile_condition", + Help: "The current condition status of a GitOps Toolkit resource reconciliation.", + }, + []string{"kind", "name", "namespace", "type", "status"}, + ), + suspendGauge: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "gotk_suspend_status", + Help: "The current suspend status of a GitOps Toolkit resource.", + }, + []string{"kind", "name", "namespace"}, + ), + durationHistogram: prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "gotk_reconcile_duration_seconds", + Help: "The duration in seconds of a GitOps Toolkit resource reconciliation.", + // Use a histogram with 10 count buckets between 1ms - 1hour + Buckets: prometheus.ExponentialBucketsRange(10e-3, 1800, 10), + }, + []string{"kind", "name", "namespace"}, + ), + } +} + +// Collectors returns a slice of Prometheus collectors, which can be used to register them in a metrics registry. +func (r *Recorder) Collectors() []prometheus.Collector { + return []prometheus.Collector{ + r.conditionGauge, + r.suspendGauge, + r.durationHistogram, + } +} + +// RecordCondition records the condition as given for the ref. +func (r *Recorder) RecordCondition(ref corev1.ObjectReference, condition metav1.Condition, deleted bool) { + for _, status := range []string{string(metav1.ConditionTrue), string(metav1.ConditionFalse), string(metav1.ConditionUnknown), ConditionDeleted} { + var value float64 + if deleted { + if status == ConditionDeleted { + value = 1 + } + } else { + if status == string(condition.Status) { + value = 1 + } + } + r.conditionGauge.WithLabelValues(ref.Kind, ref.Name, ref.Namespace, condition.Type, status).Set(value) + } +} + +// RecordSuspend records the suspend status as given for the ref. +func (r *Recorder) RecordSuspend(ref corev1.ObjectReference, suspend bool) { + var value float64 + if suspend { + value = 1 + } + r.suspendGauge.WithLabelValues(ref.Kind, ref.Name, ref.Namespace).Set(value) +} + +// RecordDuration records the duration since start for the given ref. +func (r *Recorder) RecordDuration(ref corev1.ObjectReference, start time.Time) { + r.durationHistogram.WithLabelValues(ref.Kind, ref.Name, ref.Namespace).Observe(time.Since(start).Seconds()) +} From 0a5e70197573b8d035a2f90ed21019dde3870555 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 15:20:00 +0200 Subject: [PATCH 0984/2268] feat: Implement Kluctl controller inside Kluctl itself --- PROJECT | 20 + api/v1beta1/condition_types.go | 38 + api/v1beta1/doc.go | 20 + api/v1beta1/groupversion_info.go | 36 + api/v1beta1/kluctldeployment_types.go | 370 ++++++ api/v1beta1/util_types.go | 60 + cmd/kluctl/commands/cmd_controller.go | 140 +++ cmd/kluctl/commands/root.go | 5 +- .../gitops.kluctl.io_kluctldeployments.yaml | 1015 +++++++++++++++++ config/crd/kustomization.yaml | 21 + config/crd/kustomizeconfig.yaml | 19 + .../cainjection_in_kluctldeployments.yaml | 7 + .../patches/webhook_in_kluctldeployments.yaml | 16 + config/default/kustomization.yaml | 137 +++ config/default/manager_config_patch.yaml | 10 + config/manager/kustomization.yaml | 2 + config/manager/manager.yaml | 102 ++ config/prometheus/kustomization.yaml | 2 + config/prometheus/monitor.yaml | 26 + config/rbac/kluctldeployment_editor_role.yaml | 31 + config/rbac/kluctldeployment_viewer_role.yaml | 27 + config/rbac/kustomization.yaml | 11 + config/rbac/leader_election_role.yaml | 44 + config/rbac/leader_election_role_binding.yaml | 19 + config/rbac/role.yaml | 54 + config/rbac/role_binding.yaml | 19 + config/rbac/service_account.yaml | 12 + docs/reference/commands/controller.md | 25 + hack/boilerplate.go.txt | 15 + .../internal/metrics/kluctl_project.go | 122 ++ .../metrics/kluctldeployment_controller.go | 89 ++ pkg/controllers/internal/sops/LICENSE | 373 ++++++ pkg/controllers/internal/sops/aws_config.go | 24 + pkg/controllers/internal/sops/azure_config.go | 156 +++ .../internal/sops/key_server_from_secret.go | 106 ++ .../sops/key_server_from_service_account.go | 91 ++ .../internal/sops/keyservice/keyservice.go | 23 + .../internal/sops/keyservice/options.go | 87 ++ .../internal/sops/keyservice/server.go | 363 ++++++ .../internal/sops/keyservice/server_test.go | 249 ++++ .../sops/keyservice/testdata/private.gpg | 81 ++ .../sops/keyservice/testdata/public.gpg | 41 + .../internal/sops/keyservice/utils_test.go | 105 ++ pkg/controllers/kluctl_project.go | 820 +++++++++++++ .../kluctldeployment_controller.go | 506 ++++++++ .../kluctldeployment_controller_setup.go | 28 + .../kluctldeployment_controller_source.go | 109 ++ .../kluctldeployment_controller_utils.go | 72 ++ pkg/controllers/predicates.go | 66 ++ pkg/controllers/utils.go | 31 + pkg/deployment/commands/result_utils.go | 2 +- pkg/deployment/deployment_collection.go | 41 + pkg/types/result/command_result.go | 2 +- pkg/types/result/summary.go | 6 +- 54 files changed, 5892 insertions(+), 4 deletions(-) create mode 100644 PROJECT create mode 100644 api/v1beta1/condition_types.go create mode 100644 api/v1beta1/doc.go create mode 100644 api/v1beta1/groupversion_info.go create mode 100644 api/v1beta1/kluctldeployment_types.go create mode 100644 api/v1beta1/util_types.go create mode 100644 cmd/kluctl/commands/cmd_controller.go create mode 100644 config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml create mode 100644 config/crd/kustomization.yaml create mode 100644 config/crd/kustomizeconfig.yaml create mode 100644 config/crd/patches/cainjection_in_kluctldeployments.yaml create mode 100644 config/crd/patches/webhook_in_kluctldeployments.yaml create mode 100644 config/default/kustomization.yaml create mode 100644 config/default/manager_config_patch.yaml create mode 100644 config/manager/kustomization.yaml create mode 100644 config/manager/manager.yaml create mode 100644 config/prometheus/kustomization.yaml create mode 100644 config/prometheus/monitor.yaml create mode 100644 config/rbac/kluctldeployment_editor_role.yaml create mode 100644 config/rbac/kluctldeployment_viewer_role.yaml create mode 100644 config/rbac/kustomization.yaml create mode 100644 config/rbac/leader_election_role.yaml create mode 100644 config/rbac/leader_election_role_binding.yaml create mode 100644 config/rbac/role.yaml create mode 100644 config/rbac/role_binding.yaml create mode 100644 config/rbac/service_account.yaml create mode 100644 docs/reference/commands/controller.md create mode 100644 hack/boilerplate.go.txt create mode 100644 pkg/controllers/internal/metrics/kluctl_project.go create mode 100644 pkg/controllers/internal/metrics/kluctldeployment_controller.go create mode 100644 pkg/controllers/internal/sops/LICENSE create mode 100644 pkg/controllers/internal/sops/aws_config.go create mode 100644 pkg/controllers/internal/sops/azure_config.go create mode 100644 pkg/controllers/internal/sops/key_server_from_secret.go create mode 100644 pkg/controllers/internal/sops/key_server_from_service_account.go create mode 100644 pkg/controllers/internal/sops/keyservice/keyservice.go create mode 100644 pkg/controllers/internal/sops/keyservice/options.go create mode 100644 pkg/controllers/internal/sops/keyservice/server.go create mode 100644 pkg/controllers/internal/sops/keyservice/server_test.go create mode 100644 pkg/controllers/internal/sops/keyservice/testdata/private.gpg create mode 100644 pkg/controllers/internal/sops/keyservice/testdata/public.gpg create mode 100644 pkg/controllers/internal/sops/keyservice/utils_test.go create mode 100644 pkg/controllers/kluctl_project.go create mode 100644 pkg/controllers/kluctldeployment_controller.go create mode 100644 pkg/controllers/kluctldeployment_controller_setup.go create mode 100644 pkg/controllers/kluctldeployment_controller_source.go create mode 100644 pkg/controllers/kluctldeployment_controller_utils.go create mode 100644 pkg/controllers/predicates.go create mode 100644 pkg/controllers/utils.go diff --git a/PROJECT b/PROJECT new file mode 100644 index 000000000..9a4660f2d --- /dev/null +++ b/PROJECT @@ -0,0 +1,20 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html +domain: kluctl.io +layout: +- go.kubebuilder.io/v4 +projectName: controller +repo: github.com/kluctl/kluctl/v2 +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kluctl.io + group: gitops + kind: KluctlDeployment + path: github.com/kluctl/kluctl/v2/api/v1beta1 + version: v1beta1 +version: "3" diff --git a/api/v1beta1/condition_types.go b/api/v1beta1/condition_types.go new file mode 100644 index 000000000..e559651c3 --- /dev/null +++ b/api/v1beta1/condition_types.go @@ -0,0 +1,38 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +const ( + // DeployFailedReason represents the fact that the + // kluctl deploy command failed. + DeployFailedReason string = "DeployFailed" + + // PruneFailedReason represents the fact that the + // pruning of the KluctlDeployment failed. + PruneFailedReason string = "PruneFailed" + + // ValidateFailedReason represents the fact that the + // validate of the KluctlDeployment failed. + ValidateFailedReason string = "ValidateFailed" + + // PrepareFailedReason represents failure in the kluctl preparation phase + PrepareFailedReason string = "PrepareFailed" + + // ReconciliationSucceededReason represents the fact that + // the reconciliation succeeded. + ReconciliationSucceededReason string = "ReconciliationSucceeded" +) diff --git a/api/v1beta1/doc.go b/api/v1beta1/doc.go new file mode 100644 index 000000000..7dae3af55 --- /dev/null +++ b/api/v1beta1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains API Schema definitions for the flux.kluctl.io v1beta1 API group. +// +kubebuilder:object:generate=true +// +groupName=gitops.kluctl.io +package v1beta1 diff --git a/api/v1beta1/groupversion_info.go b/api/v1beta1/groupversion_info.go new file mode 100644 index 000000000..1092fbc63 --- /dev/null +++ b/api/v1beta1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains API Schema definitions for the v1beta1 API group +// +kubebuilder:object:generate=true +// +groupName=gitops.kluctl.io +package v1beta1 + +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: "gitops.kluctl.io", Version: "v1beta1"} + + // 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 +) diff --git a/api/v1beta1/kluctldeployment_types.go b/api/v1beta1/kluctldeployment_types.go new file mode 100644 index 000000000..3da415e20 --- /dev/null +++ b/api/v1beta1/kluctldeployment_types.go @@ -0,0 +1,370 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/result" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "time" +) + +const ( + KluctlDeploymentKind = "KluctlDeployment" + KluctlDeploymentFinalizer = "finalizers.gitops.kluctl.io" + MaxConditionMessageLength = 20000 + + KluctlDeployModeFull = "full-deploy" + KluctlDeployPokeImages = "poke-images" + + KluctlRequestReconcileAnnotation = "kluctl.io/request-reconcile" + KluctlRequestDeployAnnotation = "kluctl.io/request-deploy" +) + +type KluctlDeploymentSpec struct { + // Specifies the project source location + Source ProjectSource `json:"source"` + + // Decrypt Kubernetes secrets before applying them on the cluster. + // +optional + Decryption *Decryption `json:"decryption,omitempty"` + + // The interval at which to reconcile the KluctlDeployment. + // Reconciliation means that the deployment is fully rendered and only deployed when the result changes compared + // to the last deployment. + // To override this behavior, set the DeployInterval value. + // +required + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + Interval metav1.Duration `json:"interval"` + + // The interval at which to retry a previously failed reconciliation. + // When not specified, the controller uses the Interval + // value to retry failures. + // +optional + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + RetryInterval *metav1.Duration `json:"retryInterval,omitempty"` + + // DeployInterval specifies the interval at which to deploy the KluctlDeployment, even in cases the rendered + // result does not change. + // +optional + DeployInterval *SafeDuration `json:"deployInterval,omitempty"` + + // ValidateInterval specifies the interval at which to validate the KluctlDeployment. + // Validation is performed the same way as with 'kluctl validate -t '. + // Defaults to the same value as specified in Interval. + // Validate is also performed whenever a deployment is performed, independent of the value of ValidateInterval + // +optional + ValidateInterval *SafeDuration `json:"validateInterval,omitempty"` + + // Timeout for all operations. + // Defaults to 'Interval' duration. + // +optional + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$" + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // This flag tells the controller to suspend subsequent kluctl executions, + // it does not apply to already started executions. Defaults to false. + // +optional + Suspend bool `json:"suspend,omitempty"` + + // HelmCredentials is a list of Helm credentials used when non pre-pulled Helm Charts are used inside a + // Kluctl deployment. + // +optional + HelmCredentials []HelmCredentials `json:"helmCredentials,omitempty"` + + // The name of the Kubernetes service account to use while deploying. + // If not specified, the default service account is used. + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` + + // The KubeConfig for deploying to the target cluster. + // Specifies the kubeconfig to be used when invoking kluctl. Contexts in this kubeconfig must match + // the context found in the kluctl target. As an alternative, specify the context to be used via 'context' + // +optional + KubeConfig *KubeConfig `json:"kubeConfig"` + + // Target specifies the kluctl target to deploy. If not specified, an empty target is used that has no name and no + // context. Use 'TargetName' and 'Context' to specify the name and context in that case. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +optional + Target *string `json:"target,omitempty"` + + // TargetNameOverride sets or overrides the target name. This is especially useful when deployment without a target. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +optional + TargetNameOverride *string `json:"targetNameOverride,omitempty"` + + // If specified, overrides the context to be used. This will effectively make kluctl ignore the context specified + // in the target. + // +optional + Context *string `json:"context,omitempty"` + + // Args specifies dynamic target args. + // +optional + // +kubebuilder:pruning:PreserveUnknownFields + Args runtime.RawExtension `json:"args,omitempty"` + + // Images contains a list of fixed image overrides. + // Equivalent to using '--fixed-images-file' when calling kluctl. + // +optional + Images []types.FixedImage `json:"images,omitempty"` + + // DryRun instructs kluctl to run everything in dry-run mode. + // Equivalent to using '--dry-run' when calling kluctl. + // +kubebuilder:default:=false + // +optional + DryRun bool `json:"dryRun,omitempty"` + + // NoWait instructs kluctl to not wait for any resources to become ready, including hooks. + // Equivalent to using '--no-wait' when calling kluctl. + // +kubebuilder:default:=false + // +optional + NoWait bool `json:"noWait,omitempty"` + + // ForceApply instructs kluctl to force-apply in case of SSA conflicts. + // Equivalent to using '--force-apply' when calling kluctl. + // +kubebuilder:default:=false + // +optional + ForceApply bool `json:"forceApply,omitempty"` + + // ReplaceOnError instructs kluctl to replace resources on error. + // Equivalent to using '--replace-on-error' when calling kluctl. + // +kubebuilder:default:=false + // +optional + ReplaceOnError bool `json:"replaceOnError,omitempty"` + + // ForceReplaceOnError instructs kluctl to force-replace resources in case a normal replace fails. + // Equivalent to using '--force-replace-on-error' when calling kluctl. + // +kubebuilder:default:=false + // +optional + ForceReplaceOnError bool `json:"forceReplaceOnError,omitempty"` + + // ForceReplaceOnError instructs kluctl to abort deployments immediately when something fails. + // Equivalent to using '--abort-on-error' when calling kluctl. + // +kubebuilder:default:=false + // +optional + AbortOnError bool `json:"abortOnError,omitempty"` + + // IncludeTags instructs kluctl to only include deployments with given tags. + // Equivalent to using '--include-tag' when calling kluctl. + // +optional + IncludeTags []string `json:"includeTags,omitempty"` + + // ExcludeTags instructs kluctl to exclude deployments with given tags. + // Equivalent to using '--exclude-tag' when calling kluctl. + // +optional + ExcludeTags []string `json:"excludeTags,omitempty"` + + // IncludeDeploymentDirs instructs kluctl to only include deployments with the given dir. + // Equivalent to using '--include-deployment-dir' when calling kluctl. + // +optional + IncludeDeploymentDirs []string `json:"includeDeploymentDirs,omitempty"` + + // ExcludeDeploymentDirs instructs kluctl to exclude deployments with the given dir. + // Equivalent to using '--exclude-deployment-dir' when calling kluctl. + // +optional + ExcludeDeploymentDirs []string `json:"excludeDeploymentDirs,omitempty"` + + // DeployMode specifies what deploy mode should be used. + // The options 'full-deploy' and 'poke-images' are supported. + // With the 'poke-images' option, only images are patched into the target without performing a full deployment. + // +kubebuilder:default:=full-deploy + // +kubebuilder:validation:Enum=full-deploy;poke-images + // +optional + DeployMode string `json:"deployMode,omitempty"` + + // Validate enables validation after deploying + // +kubebuilder:default:=true + // +optional + Validate bool `json:"validate"` + + // Prune enables pruning after deploying. + // +kubebuilder:default:=false + // +optional + Prune bool `json:"prune,omitempty"` + + // Delete enables deletion of the specified target when the KluctlDeployment object gets deleted. + // +kubebuilder:default:=false + // +optional + Delete bool `json:"delete,omitempty"` +} + +// GetRetryInterval returns the retry interval +func (in KluctlDeploymentSpec) GetRetryInterval() time.Duration { + if in.RetryInterval != nil { + return in.RetryInterval.Duration + } + return in.Interval.Duration +} + +type ProjectSource struct { + // Url specifies the Git url where the project source is located + // +required + URL types.GitUrl `json:"url"` + + // Ref specifies the branch, tag or commit that should be used. If omitted, the default branch of the repo is used. + // +optional + Ref *GitRef `json:"ref,omitempty"` + + // Path specifies the sub-directory to be used as project directory + // +optional + Path string `json:"path,omitempty"` + + // SecretRef specifies the Secret containing authentication credentials for + // the git repository. + // For HTTPS repositories the Secret must contain 'username' and 'password' + // fields. + // For SSH repositories the Secret must contain 'identity' + // and 'known_hosts' fields. + // +optional + SecretRef *LocalObjectReference `json:"secretRef,omitempty"` +} + +// Decryption defines how decryption is handled for Kubernetes manifests. +type Decryption struct { + // Provider is the name of the decryption engine. + // +kubebuilder:validation:Enum=sops + // +required + Provider string `json:"provider"` + + // The secret name containing the private OpenPGP keys used for decryption. + // +optional + SecretRef *LocalObjectReference `json:"secretRef,omitempty"` + + // ServiceAccount specifies the service account used to authenticate against cloud providers. + // This is currently only usable for AWS KMS keys. The specified service account will be used to authenticate to AWS + // by signing a token in an IRSA compliant way. + // +optional + ServiceAccount string `json:"serviceAccount,omitempty"` +} + +type HelmCredentials struct { + // SecretRef holds the name of a secret that contains the Helm credentials. + // The secret must either contain the fields `credentialsId` which refers to the credentialsId + // found in https://kluctl.io/docs/kluctl/reference/deployments/helm/#private-chart-repositories or an `url` used + // to match the credentials found in Kluctl projects helm-chart.yaml files. + // The secret can either container basic authentication credentials via `username` and `password` or + // TLS authentication via `certFile` and `keyFile`. `caFile` can be specified to override the CA to use while + // contacting the repository. + // The secret can also contain `insecureSkipTlsVerify: "true"`, which will disable TLS verification. + // `passCredentialsAll: "true"` can be specified to make the controller pass credentials to all requests, even if + // the hostname changes in-between. + // +required + SecretRef LocalObjectReference `json:"secretRef,omitempty"` +} + +// KubeConfig references a Kubernetes secret that contains a kubeconfig file. +type KubeConfig struct { + // SecretRef holds the name of a secret that contains a key with + // the kubeconfig file as the value. If no key is set, the key will default + // to 'value'. The secret must be in the same namespace as + // the Kustomization. + // It is recommended that the kubeconfig is self-contained, and the secret + // is regularly updated if credentials such as a cloud-access-token expire. + // Cloud specific `cmd-path` auth helpers will not function without adding + // binaries and credentials to the Pod that is responsible for reconciling + // the KluctlDeployment. + // +required + SecretRef SecretKeyReference `json:"secretRef,omitempty"` +} + +// KluctlDeploymentStatus defines the observed state of KluctlDeployment +type KluctlDeploymentStatus struct { + // LastHandledReconcileAt holds the value of the most recent + // reconcile request value, so a change of the annotation value + // can be detected. + // +optional + LastHandledReconcileAt string `json:"lastHandledReconcileAt,omitempty"` + + // +optional + LastHandledDeployAt string `json:"LastHandledDeployAt,omitempty"` + + // ObservedGeneration is the last reconciled generation. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // +optional + ProjectKey *result.ProjectKey `json:"projectKey,omitempty"` + + // +optional + TargetKey *result.TargetKey `json:"targetKey,omitempty"` + + // +optional + LastObjectsHash string `json:"lastObjectsHash,omitempty"` + + // LastDeployResult is the result of the last deploy command + // +optional + LastDeployResult *result.CommandResultSummary `json:"lastDeployResult,omitempty"` + + // LastDeployResult is the result of the last prune command + // +optional + LastPruneResult *result.CommandResultSummary `json:"lastPruneResult,omitempty"` + + // LastValidateResult is the result of the last validate command + // +optional + LastValidateResult *result.ValidateResult `json:"lastValidateResult,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="DryRun",type="boolean",JSONPath=".spec.dryRun",description="" +//+kubebuilder:printcolumn:name="Deployed",type="date",JSONPath=".status.lastDeployResult.command.endTime",description="" +//+kubebuilder:printcolumn:name="Pruned",type="date",JSONPath=".status.lastPruneResult.command.endTime",description="" +//+kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" +//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="" +//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" + +// KluctlDeployment is the Schema for the kluctldeployments API +type KluctlDeployment struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KluctlDeploymentSpec `json:"spec,omitempty"` + Status KluctlDeploymentStatus `json:"status,omitempty"` +} + +// GetConditions returns the status conditions of the object. +func (in *KluctlDeployment) GetConditions() []metav1.Condition { + return in.Status.Conditions +} + +// SetConditions sets the status conditions on the object. +func (in *KluctlDeployment) SetConditions(conditions []metav1.Condition) { + in.Status.Conditions = conditions +} + +//+kubebuilder:object:root=true + +// KluctlDeploymentList contains a list of KluctlDeployment +type KluctlDeploymentList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KluctlDeployment `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KluctlDeployment{}, &KluctlDeploymentList{}) +} diff --git a/api/v1beta1/util_types.go b/api/v1beta1/util_types.go new file mode 100644 index 000000000..efe1e987a --- /dev/null +++ b/api/v1beta1/util_types.go @@ -0,0 +1,60 @@ +package v1beta1 + +import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type LocalObjectReference struct { + // Name of the referent. + // +required + Name string `json:"name"` +} + +// SecretKeyReference contains enough information to locate the referenced Kubernetes Secret object in the same +// namespace. Optionally a key can be specified. +// Use this type instead of core/v1 SecretKeySelector when the Key is optional and the Optional field is not +// applicable. +type SecretKeyReference struct { + // Name of the Secret. + // +required + Name string `json:"name"` + + // Key in the Secret, when not specified an implementation-specific default key is used. + // +optional + Key string `json:"key,omitempty"` +} + +// +kubebuilder:validation:Type=string +// +kubebuilder:validation:Pattern="^(([0-9]+(\\.[0-9]+)?(ms|s|m|h))+)|never$" +type SafeDuration struct { + metav1.Duration +} + +type GitRef struct { + // Branch to filter for. Can also be a regex. + // +optional + Branch string `json:"branch,omitempty"` + + // Branch to filter for. Can also be a regex. + // +optional + Tag string `json:"tag,omitempty"` + + // TODO + // Commit SHA to check out, takes precedence over all reference fields. + // +optional + // Commit string `json:"commit,omitempty"` +} + +func (r *GitRef) String() string { + if r == nil { + return "" + } + if r.Tag != "" { + return fmt.Sprintf("refs/tags/%s", r.Tag) + } else if r.Branch != "" { + return fmt.Sprintf("refs/heads/%s", r.Branch) + } else { + return "" + } +} diff --git a/cmd/kluctl/commands/cmd_controller.go b/cmd/kluctl/commands/cmd_controller.go new file mode 100644 index 000000000..655a5071d --- /dev/null +++ b/cmd/kluctl/commands/cmd_controller.go @@ -0,0 +1,140 @@ +package commands + +import ( + "context" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + "github.com/kluctl/kluctl/v2/pkg/controllers" + ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/kubernetes" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "os" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + crtlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" +) + +var ( + setupLog = ctrl.Log.WithName("setup") + metricsRecorder = metrics.NewRecorder() +) + +func init() { + crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...) +} + +const controllerName = "kluctl-controller" + +type controllerCmd struct { + scheme *runtime.Scheme + + MetricsBindAddress string `group:"controller" help:"The address the metric endpoint binds to." default:":8080"` + HealthProbeBindAddress string `group:"controller" help:"The address the probe endpoint binds to." default:":8081"` + LeaderElect bool `group:"controller" help:"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager."` + + DefaultServiceAccount string `group:"controller" help:"Default service account used for impersonation."` + DryRun bool `group:"controller" help:"Run all deployments in dryRun=true mode."` +} + +func (cmd *controllerCmd) Help() string { + return `This command will run the Kluctl Controller. This is usually meant to be run inside a cluster and not from your local machine. +` +} + +func (cmd *controllerCmd) initScheme() { + cmd.scheme = runtime.NewScheme() + scheme := cmd.scheme + + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + utilruntime.Must(kluctlv1.AddToScheme(scheme)) + + //+kubebuilder:scaffold:scheme +} + +func (cmd *controllerCmd) Run(ctx context.Context) error { + cmd.initScheme() + + opts := zap.Options{ + Development: true, + } + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + restConfig := flux_utils.GetConfigOrDie(flux_utils.Options{}) + clientSet, err := kubernetes.NewForConfig(restConfig) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: cmd.scheme, + MetricsBindAddress: cmd.MetricsBindAddress, + Port: 9443, + HealthProbeBindAddress: cmd.HealthProbeBindAddress, + LeaderElection: cmd.LeaderElect, + LeaderElectionID: "5ab5d0f9.kluctl.io", + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + eventRecorder := mgr.GetEventRecorderFor(controllerName) + + sshPool := &ssh_pool.SshPool{} + + r := controllers.KluctlDeploymentReconciler{ + ControllerName: controllerName, + DefaultServiceAccount: cmd.DefaultServiceAccount, + DryRun: cmd.DryRun, + RestConfig: restConfig, + Client: mgr.GetClient(), + ClientSet: clientSet, + Scheme: mgr.GetScheme(), + EventRecorder: eventRecorder, + MetricsRecorder: metricsRecorder, + SshPool: sshPool, + } + + if err = r.SetupWithManager(mgr, controllers.KluctlDeploymentReconcilerOpts{ + HTTPRetry: 9, + }); err != nil { + setupLog.Error(err, "unable to create controller", "controller", kluctlv1.KluctlDeploymentKind) + os.Exit(1) + } + + //+kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctx); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } + + return nil +} diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 200d0b9d7..51b37957c 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -25,6 +25,7 @@ import ( "os" "path/filepath" "runtime/pprof" + ctrl "sigs.k8s.io/controller-runtime" "strings" "time" @@ -66,6 +67,7 @@ type cli struct { Render renderCmd `cmd:"" help:"Renders all resources and configuration files"` Seal sealCmd `cmd:"" help:"Seal secrets based on target's sealingConfig"` Validate validateCmd `cmd:"" help:"Validates the already deployed deployment"` + Controller controllerCmd `cmd:"" help:"Run the Kluctl controller"` Version versionCmd `cmd:"" help:"Print kluctl version"` } @@ -77,6 +79,7 @@ var flagGroups = []groupInfo{ {group: "inclusion", title: "Inclusion/Exclusion arguments:", description: "Control inclusion/exclusion."}, {group: "misc", title: "Misc arguments:", description: "Command specific arguments."}, {group: "results", title: "Command Results:", description: "Configure how command results are stored."}, + {group: "controller", title: "Controller:", description: "Controller arguments."}, } var origStderr = os.Stderr @@ -219,7 +222,7 @@ func initViper(ctx context.Context) { func Main() { colorable.EnableColorsStdout(nil) - ctx := context.Background() + ctx := ctrl.SetupSignalHandler() ctx = initStatusHandler(ctx, false, true) redirectLogsAndStderr(func() context.Context { diff --git a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml new file mode 100644 index 000000000..3e81ebc5e --- /dev/null +++ b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml @@ -0,0 +1,1015 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.3 + creationTimestamp: null + name: kluctldeployments.gitops.kluctl.io +spec: + group: gitops.kluctl.io + names: + kind: KluctlDeployment + listKind: KluctlDeploymentList + plural: kluctldeployments + singular: kluctldeployment + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.dryRun + name: DryRun + type: boolean + - jsonPath: .status.lastDeployResult.command.endTime + name: Deployed + type: date + - jsonPath: .status.lastPruneResult.command.endTime + name: Pruned + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: KluctlDeployment is the Schema for the kluctldeployments API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + abortOnError: + default: false + description: ForceReplaceOnError instructs kluctl to abort deployments + immediately when something fails. Equivalent to using '--abort-on-error' + when calling kluctl. + type: boolean + args: + description: Args specifies dynamic target args. + type: object + x-kubernetes-preserve-unknown-fields: true + context: + description: If specified, overrides the context to be used. This + will effectively make kluctl ignore the context specified in the + target. + type: string + decryption: + description: Decrypt Kubernetes secrets before applying them on the + cluster. + properties: + provider: + description: Provider is the name of the decryption engine. + enum: + - sops + type: string + secretRef: + description: The secret name containing the private OpenPGP keys + used for decryption. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccount: + description: ServiceAccount specifies the service account used + to authenticate against cloud providers. This is currently only + usable for AWS KMS keys. The specified service account will + be used to authenticate to AWS by signing a token in an IRSA + compliant way. + type: string + required: + - provider + type: object + delete: + default: false + description: Delete enables deletion of the specified target when + the KluctlDeployment object gets deleted. + type: boolean + deployInterval: + description: DeployInterval specifies the interval at which to deploy + the KluctlDeployment, even in cases the rendered result does not + change. + pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ + type: string + deployMode: + default: full-deploy + description: DeployMode specifies what deploy mode should be used. + The options 'full-deploy' and 'poke-images' are supported. With + the 'poke-images' option, only images are patched into the target + without performing a full deployment. + enum: + - full-deploy + - poke-images + type: string + dryRun: + default: false + description: DryRun instructs kluctl to run everything in dry-run + mode. Equivalent to using '--dry-run' when calling kluctl. + type: boolean + excludeDeploymentDirs: + description: ExcludeDeploymentDirs instructs kluctl to exclude deployments + with the given dir. Equivalent to using '--exclude-deployment-dir' + when calling kluctl. + items: + type: string + type: array + excludeTags: + description: ExcludeTags instructs kluctl to exclude deployments with + given tags. Equivalent to using '--exclude-tag' when calling kluctl. + items: + type: string + type: array + forceApply: + default: false + description: ForceApply instructs kluctl to force-apply in case of + SSA conflicts. Equivalent to using '--force-apply' when calling + kluctl. + type: boolean + forceReplaceOnError: + default: false + description: ForceReplaceOnError instructs kluctl to force-replace + resources in case a normal replace fails. Equivalent to using '--force-replace-on-error' + when calling kluctl. + type: boolean + helmCredentials: + description: HelmCredentials is a list of Helm credentials used when + non pre-pulled Helm Charts are used inside a Kluctl deployment. + items: + properties: + secretRef: + description: 'SecretRef holds the name of a secret that contains + the Helm credentials. The secret must either contain the fields + `credentialsId` which refers to the credentialsId found in + https://kluctl.io/docs/kluctl/reference/deployments/helm/#private-chart-repositories + or an `url` used to match the credentials found in Kluctl + projects helm-chart.yaml files. The secret can either container + basic authentication credentials via `username` and `password` + or TLS authentication via `certFile` and `keyFile`. `caFile` + can be specified to override the CA to use while contacting + the repository. The secret can also contain `insecureSkipTlsVerify: + "true"`, which will disable TLS verification. `passCredentialsAll: + "true"` can be specified to make the controller pass credentials + to all requests, even if the hostname changes in-between.' + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: object + type: array + images: + description: Images contains a list of fixed image overrides. Equivalent + to using '--fixed-images-file' when calling kluctl. + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + includeDeploymentDirs: + description: IncludeDeploymentDirs instructs kluctl to only include + deployments with the given dir. Equivalent to using '--include-deployment-dir' + when calling kluctl. + items: + type: string + type: array + includeTags: + description: IncludeTags instructs kluctl to only include deployments + with given tags. Equivalent to using '--include-tag' when calling + kluctl. + items: + type: string + type: array + interval: + description: The interval at which to reconcile the KluctlDeployment. + Reconciliation means that the deployment is fully rendered and only + deployed when the result changes compared to the last deployment. + To override this behavior, set the DeployInterval value. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: The KubeConfig for deploying to the target cluster. Specifies + the kubeconfig to be used when invoking kluctl. Contexts in this + kubeconfig must match the context found in the kluctl target. As + an alternative, specify the context to be used via 'context' + properties: + secretRef: + description: SecretRef holds the name of a secret that contains + a key with the kubeconfig file as the value. If no key is set, + the key will default to 'value'. The secret must be in the same + namespace as the Kustomization. It is recommended that the kubeconfig + is self-contained, and the secret is regularly updated if credentials + such as a cloud-access-token expire. Cloud specific `cmd-path` + auth helpers will not function without adding binaries and credentials + to the Pod that is responsible for reconciling the KluctlDeployment. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific + default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + type: object + noWait: + default: false + description: NoWait instructs kluctl to not wait for any resources + to become ready, including hooks. Equivalent to using '--no-wait' + when calling kluctl. + type: boolean + prune: + default: false + description: Prune enables pruning after deploying. + type: boolean + replaceOnError: + default: false + description: ReplaceOnError instructs kluctl to replace resources + on error. Equivalent to using '--replace-on-error' when calling + kluctl. + type: boolean + retryInterval: + description: The interval at which to retry a previously failed reconciliation. + When not specified, the controller uses the Interval value to retry + failures. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + serviceAccountName: + description: The name of the Kubernetes service account to use while + deploying. If not specified, the default service account is used. + type: string + source: + description: Specifies the project source location + properties: + path: + description: Path specifies the sub-directory to be used as project + directory + type: string + ref: + description: Ref specifies the branch, tag or commit that should + be used. If omitted, the default branch of the repo is used. + properties: + branch: + description: Branch to filter for. Can also be a regex. + type: string + tag: + description: Branch to filter for. Can also be a regex. + type: string + type: object + secretRef: + description: SecretRef specifies the Secret containing authentication + credentials for the git repository. For HTTPS repositories the + Secret must contain 'username' and 'password' fields. For SSH + repositories the Secret must contain 'identity' and 'known_hosts' + fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + url: + description: Url specifies the Git url where the project source + is located + type: string + required: + - url + type: object + suspend: + description: This flag tells the controller to suspend subsequent + kluctl executions, it does not apply to already started executions. + Defaults to false. + type: boolean + target: + description: Target specifies the kluctl target to deploy. If not + specified, an empty target is used that has no name and no context. + Use 'TargetName' and 'Context' to specify the name and context in + that case. + maxLength: 63 + minLength: 1 + type: string + targetNameOverride: + description: TargetNameOverride sets or overrides the target name. + This is especially useful when deployment without a target. + maxLength: 63 + minLength: 1 + type: string + timeout: + description: Timeout for all operations. Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + validate: + default: true + description: Validate enables validation after deploying + type: boolean + validateInterval: + description: ValidateInterval specifies the interval at which to validate + the KluctlDeployment. Validation is performed the same way as with + 'kluctl validate -t '. Defaults to the same value as specified + in Interval. Validate is also performed whenever a deployment is + performed, independent of the value of ValidateInterval + pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ + type: string + required: + - interval + - source + type: object + status: + description: KluctlDeploymentStatus defines the observed state of KluctlDeployment + properties: + LastHandledDeployAt: + type: string + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastDeployResult: + description: LastDeployResult is the result of the last deploy command + properties: + appliedHookObjects: + type: integer + appliedObjects: + type: integer + changedObjects: + type: integer + clusterInfo: + properties: + clusterId: + type: string + required: + - clusterId + type: object + commandInfo: + properties: + abortOnError: + type: boolean + args: + type: object + x-kubernetes-preserve-unknown-fields: true + command: + type: string + contextOverride: + type: string + dryRun: + type: boolean + endTime: + format: date-time + type: string + excludeDeploymentDirs: + items: + type: string + type: array + excludeTags: + items: + type: string + type: array + forceApply: + type: boolean + forceReplaceOnError: + type: boolean + images: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + includeDeploymentDirs: + items: + type: string + type: array + includeTags: + items: + type: string + type: array + initiator: + type: string + kluctlDeployment: + properties: + gitRef: + type: string + gitUrl: + type: string + name: + type: string + namespace: + type: string + required: + - gitRef + - gitUrl + - name + - namespace + type: object + noWait: + type: boolean + replaceOnError: + type: boolean + startTime: + format: date-time + type: string + target: + type: string + targetNameOverride: + type: string + required: + - endTime + - initiator + - startTime + type: object + deletedObjects: + type: integer + errors: + type: integer + gitInfo: + properties: + commit: + type: string + dirty: + type: boolean + ref: + type: string + subDir: + type: string + url: + type: string + required: + - commit + - dirty + - ref + - subDir + - url + type: object + id: + type: string + newObjects: + type: integer + orphanObjects: + type: integer + project: + properties: + normalizedGitUrl: + type: string + subDir: + type: string + type: object + remoteObjects: + type: integer + renderedObjects: + type: integer + target: + properties: + clusterId: + type: string + discriminator: + type: string + targetName: + type: string + required: + - clusterId + type: object + totalChanges: + type: integer + warnings: + type: integer + required: + - appliedHookObjects + - appliedObjects + - changedObjects + - commandInfo + - deletedObjects + - errors + - id + - newObjects + - orphanObjects + - project + - remoteObjects + - renderedObjects + - target + - totalChanges + - warnings + type: object + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + lastObjectsHash: + type: string + lastPruneResult: + description: LastDeployResult is the result of the last prune command + properties: + appliedHookObjects: + type: integer + appliedObjects: + type: integer + changedObjects: + type: integer + clusterInfo: + properties: + clusterId: + type: string + required: + - clusterId + type: object + commandInfo: + properties: + abortOnError: + type: boolean + args: + type: object + x-kubernetes-preserve-unknown-fields: true + command: + type: string + contextOverride: + type: string + dryRun: + type: boolean + endTime: + format: date-time + type: string + excludeDeploymentDirs: + items: + type: string + type: array + excludeTags: + items: + type: string + type: array + forceApply: + type: boolean + forceReplaceOnError: + type: boolean + images: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + includeDeploymentDirs: + items: + type: string + type: array + includeTags: + items: + type: string + type: array + initiator: + type: string + kluctlDeployment: + properties: + gitRef: + type: string + gitUrl: + type: string + name: + type: string + namespace: + type: string + required: + - gitRef + - gitUrl + - name + - namespace + type: object + noWait: + type: boolean + replaceOnError: + type: boolean + startTime: + format: date-time + type: string + target: + type: string + targetNameOverride: + type: string + required: + - endTime + - initiator + - startTime + type: object + deletedObjects: + type: integer + errors: + type: integer + gitInfo: + properties: + commit: + type: string + dirty: + type: boolean + ref: + type: string + subDir: + type: string + url: + type: string + required: + - commit + - dirty + - ref + - subDir + - url + type: object + id: + type: string + newObjects: + type: integer + orphanObjects: + type: integer + project: + properties: + normalizedGitUrl: + type: string + subDir: + type: string + type: object + remoteObjects: + type: integer + renderedObjects: + type: integer + target: + properties: + clusterId: + type: string + discriminator: + type: string + targetName: + type: string + required: + - clusterId + type: object + totalChanges: + type: integer + warnings: + type: integer + required: + - appliedHookObjects + - appliedObjects + - changedObjects + - commandInfo + - deletedObjects + - errors + - id + - newObjects + - orphanObjects + - project + - remoteObjects + - renderedObjects + - target + - totalChanges + - warnings + type: object + lastValidateResult: + description: LastValidateResult is the result of the last validate + command + properties: + drift: + items: + properties: + changes: + items: + properties: + jsonPath: + type: string + newValue: + x-kubernetes-preserve-unknown-fields: true + oldValue: + x-kubernetes-preserve-unknown-fields: true + type: + type: string + unifiedDiff: + type: string + required: + - jsonPath + - type + type: object + type: array + ref: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + required: + - ref + type: object + type: array + errors: + items: + properties: + message: + type: string + ref: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + required: + - message + - ref + type: object + type: array + id: + type: string + ready: + type: boolean + results: + items: + properties: + annotation: + type: string + message: + type: string + ref: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + required: + - annotation + - message + - ref + type: object + type: array + warnings: + items: + properties: + message: + type: string + ref: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + required: + - message + - ref + type: object + type: array + required: + - id + - ready + type: object + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + projectKey: + properties: + normalizedGitUrl: + type: string + subDir: + type: string + type: object + targetKey: + properties: + clusterId: + type: string + discriminator: + type: string + targetName: + type: string + required: + - clusterId + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml new file mode 100644 index 000000000..d5f5b903f --- /dev/null +++ b/config/crd/kustomization.yaml @@ -0,0 +1,21 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/gitops.kluctl.io_kluctldeployments.yaml +#+kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_kluctldeployments.yaml +#+kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_kluctldeployments.yaml +#+kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml new file mode 100644 index 000000000..ec5c150a9 --- /dev/null +++ b/config/crd/kustomizeconfig.yaml @@ -0,0 +1,19 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/config/crd/patches/cainjection_in_kluctldeployments.yaml b/config/crd/patches/cainjection_in_kluctldeployments.yaml new file mode 100644 index 000000000..4adca8433 --- /dev/null +++ b/config/crd/patches/cainjection_in_kluctldeployments.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME + name: kluctldeployments.gitops.kluctl.io diff --git a/config/crd/patches/webhook_in_kluctldeployments.yaml b/config/crd/patches/webhook_in_kluctldeployments.yaml new file mode 100644 index 000000000..fa1689a1d --- /dev/null +++ b/config/crd/patches/webhook_in_kluctldeployments.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: kluctldeployments.gitops.kluctl.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml new file mode 100644 index 000000000..fc2356a99 --- /dev/null +++ b/config/default/kustomization.yaml @@ -0,0 +1,137 @@ +# Adds namespace to all resources. +namespace: controller-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: controller- + +# Labels to add to all resources and selectors. +#labels: +#- includeSelectors: true +# pairs: +# someName: someValue + +resources: +- ../crd +- ../rbac +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus + + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- manager_webhook_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. +# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# 'CERTMANAGER' needs to be enabled to use ca injection +#- webhookcainjection_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +# Uncomment the following replacements to add the cert-manager CA injection annotations +#replacements: +# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - select: +# kind: CustomResourceDefinition +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# - select: +# kind: CustomResourceDefinition +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# - source: # Add cert-manager annotation to the webhook Service +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml new file mode 100644 index 000000000..f6f589169 --- /dev/null +++ b/config/default/manager_config_patch.yaml @@ -0,0 +1,10 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml new file mode 100644 index 000000000..5c5f0b84c --- /dev/null +++ b/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- manager.yaml diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml new file mode 100644 index 000000000..d0a98b913 --- /dev/null +++ b/config/manager/manager.yaml @@ -0,0 +1,102 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: namespace + app.kubernetes.io/instance: system + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: controller + app.kubernetes.io/part-of: controller + app.kubernetes.io/managed-by: kustomize + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager + app.kubernetes.io/name: deployment + app.kubernetes.io/instance: controller-manager + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: controller + app.kubernetes.io/part-of: controller + app.kubernetes.io/managed-by: kustomize +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + control-plane: controller-manager + spec: + # TODO(user): Uncomment the following code to configure the nodeAffinity expression + # according to the platforms which are supported by your solution. + # It is considered best practice to support multiple architectures. You can + # build your manager image using the makefile target docker-buildx. + # affinity: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/arch + # operator: In + # values: + # - amd64 + # - arm64 + # - ppc64le + # - s390x + # - key: kubernetes.io/os + # operator: In + # values: + # - linux + securityContext: + runAsNonRoot: true + # TODO(user): For common cases that do not require escalating privileges + # it is recommended to ensure that all your Pods/Containers are restrictive. + # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + # Please uncomment the following code if your project does NOT have to work on old Kubernetes + # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). + # seccompProfile: + # type: RuntimeDefault + containers: + - command: + - /manager + args: + - --leader-elect + image: controller:latest + name: manager + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + # TODO(user): Configure the resources accordingly based on the project requirements. + # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + serviceAccountName: controller-manager + terminationGracePeriodSeconds: 10 diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml new file mode 100644 index 000000000..ed137168a --- /dev/null +++ b/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml new file mode 100644 index 000000000..62e8a7ccb --- /dev/null +++ b/config/prometheus/monitor.yaml @@ -0,0 +1,26 @@ + +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: servicemonitor + app.kubernetes.io/instance: controller-manager-metrics-monitor + app.kubernetes.io/component: metrics + app.kubernetes.io/created-by: controller + app.kubernetes.io/part-of: controller + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + scheme: https + bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + tlsConfig: + insecureSkipVerify: true + selector: + matchLabels: + control-plane: controller-manager diff --git a/config/rbac/kluctldeployment_editor_role.yaml b/config/rbac/kluctldeployment_editor_role.yaml new file mode 100644 index 000000000..8304fdd0c --- /dev/null +++ b/config/rbac/kluctldeployment_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit kluctldeployments. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: kluctldeployment-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/part-of: controller + app.kubernetes.io/managed-by: kustomize + name: kluctldeployment-editor-role +rules: +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments/status + verbs: + - get diff --git a/config/rbac/kluctldeployment_viewer_role.yaml b/config/rbac/kluctldeployment_viewer_role.yaml new file mode 100644 index 000000000..50685c666 --- /dev/null +++ b/config/rbac/kluctldeployment_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view kluctldeployments. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: kluctldeployment-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/part-of: controller + app.kubernetes.io/managed-by: kustomize + name: kluctldeployment-viewer-role +rules: +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments + verbs: + - get + - list + - watch +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments/status + verbs: + - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml new file mode 100644 index 000000000..166fe7986 --- /dev/null +++ b/config/rbac/kustomization.yaml @@ -0,0 +1,11 @@ +resources: +# All RBAC will be applied under this service account in +# the deployment namespace. You may comment out this resource +# if your manager will use a service account that exists at +# runtime. Be sure to update RoleBinding and ClusterRoleBinding +# subjects if changing service account names. +- service_account.yaml +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml new file mode 100644 index 000000000..9bf1a8ac0 --- /dev/null +++ b/config/rbac/leader_election_role.yaml @@ -0,0 +1,44 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/name: role + app.kubernetes.io/instance: leader-election-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/part-of: controller + app.kubernetes.io/managed-by: kustomize + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 000000000..d8cfaffd6 --- /dev/null +++ b/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/name: rolebinding + app.kubernetes.io/instance: leader-election-rolebinding + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/part-of: controller + app.kubernetes.io/managed-by: kustomize + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml new file mode 100644 index 000000000..5e7aaeef7 --- /dev/null +++ b/config/rbac/role.yaml @@ -0,0 +1,54 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: manager-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - secrets + - serviceaccounts + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments/status + verbs: + - get + - patch + - update diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml new file mode 100644 index 000000000..5c650c9a8 --- /dev/null +++ b/config/rbac/role_binding.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/instance: manager-rolebinding + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/part-of: controller + app.kubernetes.io/managed-by: kustomize + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml new file mode 100644 index 000000000..a0e977167 --- /dev/null +++ b/config/rbac/service_account.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: serviceaccount + app.kubernetes.io/instance: controller-manager-sa + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/part-of: controller + app.kubernetes.io/managed-by: kustomize + name: controller-manager + namespace: system diff --git a/docs/reference/commands/controller.md b/docs/reference/commands/controller.md new file mode 100644 index 000000000..ff5fe4938 --- /dev/null +++ b/docs/reference/commands/controller.md @@ -0,0 +1,25 @@ + + +## Command + +Usage: kluctl controller [flags] + + + +## Arguments + +The following arguments are available: + +``` + +``` + diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt new file mode 100644 index 000000000..65b862271 --- /dev/null +++ b/hack/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ \ No newline at end of file diff --git a/pkg/controllers/internal/metrics/kluctl_project.go b/pkg/controllers/internal/metrics/kluctl_project.go new file mode 100644 index 000000000..c1cdcde09 --- /dev/null +++ b/pkg/controllers/internal/metrics/kluctl_project.go @@ -0,0 +1,122 @@ +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "sigs.k8s.io/controller-runtime/pkg/metrics" +) + +const ( + DeploymentDurationKey = "deployment_duration_seconds" + NumberOfChangedObjectsKey = "number_of_changed_objects" + NumberOfDeletedObjectsKey = "number_of_deleted_objects" + NumberOfErrorsKey = "number_of_errors" + NumberOfOrphanObjectsKey = "number_of_orphan_objects" + NumberOfWarningsKey = "number_of_warnings" + PruneDurationKey = "prune_duration_seconds" + DeleteDurationKey = "delete_duration_seconds" + ValidateDurationKey = "validate_duration_seconds" +) + +var ( + deploymentDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: DeploymentDurationKey, + Help: "How long a single deployment takes in seconds.", + }, []string{"namespace", "name", "mode"}) + + numberOfChangedObjects = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: NumberOfChangedObjectsKey, + Help: "How many objects have been changed by a single project.", + }, []string{"namespace", "name"}) + + numberOfDeletedObjects = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: NumberOfDeletedObjectsKey, + Help: "How many things has been deleted by a single project.", + }, []string{"namespace", "name"}) + + numberOfErrors = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: NumberOfErrorsKey, + Help: "How many errors are related to a single project.", + }, []string{"namespace", "name", "action"}) + + numberOfOrphanObjects = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: NumberOfOrphanObjectsKey, + Help: "How many orphans are related to a single project.", + }, []string{"namespace", "name"}) + + numberOfWarnings = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: NumberOfWarningsKey, + Help: "How many warnings are related to a single project.", + }, []string{"namespace", "name", "action"}) + + pruneDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: PruneDurationKey, + Help: "How long a single prune takes in seconds.", + }, []string{"namespace", "name"}) + + deleteDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: DeleteDurationKey, + Help: "How long a single delete takes in seconds.", + }, []string{"namespace", "name"}) + + validateDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: ValidateDurationKey, + Help: "How long a single validate takes in seconds.", + }, []string{"namespace", "name"}) +) + +func init() { + metrics.Registry.MustRegister(deploymentDuration) + metrics.Registry.MustRegister(numberOfChangedObjects) + metrics.Registry.MustRegister(numberOfDeletedObjects) + metrics.Registry.MustRegister(numberOfErrors) + metrics.Registry.MustRegister(numberOfOrphanObjects) + metrics.Registry.MustRegister(numberOfWarnings) + metrics.Registry.MustRegister(pruneDuration) + metrics.Registry.MustRegister(deleteDuration) + metrics.Registry.MustRegister(validateDuration) +} + +func NewKluctlDeploymentDuration(namespace string, name string, mode string) prometheus.Observer { + return deploymentDuration.WithLabelValues(namespace, name, mode) +} + +func NewKluctlNumberOfChangedObjects(namespace string, name string) prometheus.Gauge { + return numberOfChangedObjects.WithLabelValues(namespace, name) +} + +func NewKluctlNumberOfDeletedObjects(namespace string, name string) prometheus.Gauge { + return numberOfDeletedObjects.WithLabelValues(namespace, name) +} + +func NewKluctlNumberOfErrors(namespace string, name string, action string) prometheus.Gauge { + return numberOfErrors.WithLabelValues(namespace, name, action) +} + +func NewKluctlNumberOfOrphanObjects(namespace string, name string) prometheus.Gauge { + return numberOfOrphanObjects.WithLabelValues(namespace, name) +} + +func NewKluctlNumberOfWarnings(namespace string, name string, action string) prometheus.Gauge { + return numberOfWarnings.WithLabelValues(namespace, name, action) +} + +func NewKluctlPruneDuration(namespace string, name string) prometheus.Observer { + return pruneDuration.WithLabelValues(namespace, name) +} + +func NewKluctlDeleteDuration(namespace string, name string) prometheus.Observer { + return deleteDuration.WithLabelValues(namespace, name) +} + +func NewKluctlValidateDuration(namespace string, name string) prometheus.Observer { + return validateDuration.WithLabelValues(namespace, name) +} diff --git a/pkg/controllers/internal/metrics/kluctldeployment_controller.go b/pkg/controllers/internal/metrics/kluctldeployment_controller.go new file mode 100644 index 000000000..8900146ad --- /dev/null +++ b/pkg/controllers/internal/metrics/kluctldeployment_controller.go @@ -0,0 +1,89 @@ +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "sigs.k8s.io/controller-runtime/pkg/metrics" +) + +// Metrics subsystem and all keys used by the kluctldeployment controller. +const ( + KluctlDeploymentControllerSubsystem = "kluctldeployments" + + DeploymentIntervalKey = "deployment_interval_seconds" + DryRunEnabledKey = "dry_run_enabled" + LastObjectStatusKey = "last_object_status" + PruneEnabledKey = "prune_enabled" + DeleteEnabledKey = "delete_enabled" + SourceSpecKey = "source_spec" +) + +var ( + deploymentInterval = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: DeploymentIntervalKey, + Help: "The configured deployment interval of a single deployment.", + }, []string{"namespace", "name"}) + + dryRunEnabled = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: DryRunEnabledKey, + Help: "Is dry-run enabled for a single deployment.", + }, []string{"namespace", "name"}) + + lastObjectStatus = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: LastObjectStatusKey, + Help: "Last object status of a single deployment.", + }, []string{"namespace", "name"}) + + pruneEnabled = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: PruneEnabledKey, + Help: "Is pruning enabled for a single deployment.", + }, []string{"namespace", "name"}) + + deleteEnabled = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: DeleteEnabledKey, + Help: "Is deletion enabled for a single deployment.", + }, []string{"namespace", "name"}) + + sourceSpec = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Subsystem: KluctlDeploymentControllerSubsystem, + Name: SourceSpecKey, + Help: "The configured source spec of a single deployment.", + }, []string{"namespace", "name", "url", "path", "ref"}) +) + +func init() { + metrics.Registry.MustRegister(deploymentInterval) + metrics.Registry.MustRegister(dryRunEnabled) + metrics.Registry.MustRegister(lastObjectStatus) + metrics.Registry.MustRegister(pruneEnabled) + metrics.Registry.MustRegister(deleteEnabled) + metrics.Registry.MustRegister(sourceSpec) +} + +func NewKluctlDeploymentInterval(namespace string, name string) prometheus.Gauge { + return dryRunEnabled.WithLabelValues(namespace, name) +} + +func NewKluctlDryRunEnabled(namespace string, name string) prometheus.Gauge { + return dryRunEnabled.WithLabelValues(namespace, name) +} + +func NewKluctlLastObjectStatus(namespace string, name string) prometheus.Gauge { + return lastObjectStatus.WithLabelValues(namespace, name) +} + +func NewKluctlPruneEnabled(namespace string, name string) prometheus.Gauge { + return pruneEnabled.WithLabelValues(namespace, name) +} + +func NewKluctlDeleteEnabled(namespace string, name string) prometheus.Gauge { + return deleteEnabled.WithLabelValues(namespace, name) +} + +func NewKluctlSourceSpec(namespace string, name string, url string, path string, ref string) prometheus.Gauge { + return sourceSpec.WithLabelValues(namespace, name, url, path, ref) +} diff --git a/pkg/controllers/internal/sops/LICENSE b/pkg/controllers/internal/sops/LICENSE new file mode 100644 index 000000000..a612ad981 --- /dev/null +++ b/pkg/controllers/internal/sops/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/pkg/controllers/internal/sops/aws_config.go b/pkg/controllers/internal/sops/aws_config.go new file mode 100644 index 000000000..fe00c00c2 --- /dev/null +++ b/pkg/controllers/internal/sops/aws_config.go @@ -0,0 +1,24 @@ +package sops + +import ( + "fmt" + "github.com/aws/aws-sdk-go-v2/credentials" + "go.mozilla.org/sops/v3/kms" + "sigs.k8s.io/yaml" +) + +// LoadCredsProviderFromYaml parses the given YAML returns a CredsProvider object +// which contains the credentials provider used for authenticating towards AWS KMS. +func LoadCredsProviderFromYaml(b []byte) (*kms.CredentialsProvider, error) { + credInfo := struct { + AccessKeyID string `json:"aws_access_key_id"` + SecretAccessKey string `json:"aws_secret_access_key"` + SessionToken string `json:"aws_session_token"` + }{} + if err := yaml.Unmarshal(b, &credInfo); err != nil { + return nil, fmt.Errorf("failed to unmarshal AWS credentials file: %w", err) + } + cp := kms.NewCredentialsProvider(credentials.NewStaticCredentialsProvider(credInfo.AccessKeyID, credInfo.SecretAccessKey, credInfo.SessionToken)) + + return cp, nil +} diff --git a/pkg/controllers/internal/sops/azure_config.go b/pkg/controllers/internal/sops/azure_config.go new file mode 100644 index 000000000..0b654b7b0 --- /dev/null +++ b/pkg/controllers/internal/sops/azure_config.go @@ -0,0 +1,156 @@ +// Copyright (C) 2022 The Flux authors +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package sops + +import ( + "bytes" + "encoding/binary" + "fmt" + "github.com/dimchansky/utfbom" + "io/ioutil" + "unicode/utf16" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "sigs.k8s.io/yaml" +) + +// LoadAADConfigFromBytes attempts to load the given bytes into the given AADConfig. +// By first decoding it if UTF-16, and then unmarshalling it into the given struct. +// It returns an error for any failure. +func LoadAADConfigFromBytes(b []byte, s *AADConfig) error { + b, err := decode(b) + if err != nil { + return fmt.Errorf("failed to decode Azure authentication file bytes: %w", err) + } + if err = yaml.Unmarshal(b, s); err != nil { + err = fmt.Errorf("failed to unmarshal Azure authentication file: %w", err) + } + return err +} + +// AADConfig contains the selection of fields from an Azure authentication file +// required for Active Directory authentication. +type AADConfig struct { + AZConfig + TenantID string `json:"tenantId,omitempty"` + ClientID string `json:"clientId,omitempty"` + ClientSecret string `json:"clientSecret,omitempty"` + ClientCertificate string `json:"clientCertificate,omitempty"` + ClientCertificatePassword string `json:"clientCertificatePassword,omitempty"` + ClientCertificateSendChain bool `json:"clientCertificateSendChain,omitempty"` + AuthorityHost string `json:"authorityHost,omitempty"` +} + +// AZConfig contains the Service Principal fields as generated by `az`. +// Ref: https://docs.microsoft.com/en-us/azure/aks/kubernetes-service-principal?tabs=azure-cli#manually-create-a-service-principal +type AZConfig struct { + AppID string `json:"appId,omitempty"` + Tenant string `json:"tenant,omitempty"` + Password string `json:"password,omitempty"` +} + +// TokenFromAADConfig attempts to construct a Token using the AADConfig values. +// It detects credentials in the following order: +// +// - azidentity.ClientSecretCredential when `tenantId`, `clientId` and +// `clientSecret` fields are found. +// - azidentity.ClientCertificateCredential when `tenantId`, +// `clientCertificate` (and optionally `clientCertificatePassword`) fields +// are found. +// - azidentity.ClientSecretCredential when AZConfig fields are found. +// - azidentity.ManagedIdentityCredential for a User ID, when a `clientId` +// field but no `tenantId` is found. +// +// If no set of credentials is found or the azcore.TokenCredential can not be +// created, an error is returned. +func TokenFromAADConfig(c AADConfig) (_ azcore.TokenCredential, err error) { + var token azcore.TokenCredential + if c.TenantID != "" && c.ClientID != "" { + if c.ClientSecret != "" { + if token, err = azidentity.NewClientSecretCredential(c.TenantID, c.ClientID, c.ClientSecret, &azidentity.ClientSecretCredentialOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: c.GetCloudConfig(), + }, + }); err != nil { + return + } + return token, nil + } + if c.ClientCertificate != "" { + certs, pk, err := azidentity.ParseCertificates([]byte(c.ClientCertificate), []byte(c.ClientCertificatePassword)) + if err != nil { + return nil, err + } + if token, err = azidentity.NewClientCertificateCredential(c.TenantID, c.ClientID, certs, pk, &azidentity.ClientCertificateCredentialOptions{ + SendCertificateChain: c.ClientCertificateSendChain, + ClientOptions: azcore.ClientOptions{ + Cloud: c.GetCloudConfig(), + }, + }); err != nil { + return nil, err + } + return token, nil + } + } + + switch { + case c.Tenant != "" && c.AppID != "" && c.Password != "": + if token, err = azidentity.NewClientSecretCredential(c.Tenant, c.AppID, c.Password, &azidentity.ClientSecretCredentialOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: c.GetCloudConfig(), + }, + }); err != nil { + return + } + return token, nil + case c.ClientID != "": + if token, err = azidentity.NewManagedIdentityCredential(&azidentity.ManagedIdentityCredentialOptions{ + ID: azidentity.ClientID(c.ClientID), + }); err != nil { + return + } + return token, nil + default: + return nil, fmt.Errorf("invalid data: requires a '%s' field, a combination of '%s', '%s' and '%s', or '%s', '%s' and '%s'", + "clientId", "tenantId", "clientId", "clientSecret", "tenantId", "clientId", "clientCertificate") + } +} + +// GetCloudConfig returns a cloud.Configuration with the AuthorityHost, or the +// Azure Public Cloud default. +func (s AADConfig) GetCloudConfig() cloud.Configuration { + if s.AuthorityHost != "" { + return cloud.Configuration{ + ActiveDirectoryAuthorityHost: s.AuthorityHost, + Services: map[cloud.ServiceName]cloud.ServiceConfiguration{}, + } + } + return cloud.AzurePublic +} + +func decode(b []byte) ([]byte, error) { + reader, enc := utfbom.Skip(bytes.NewReader(b)) + switch enc { + case utfbom.UTF16LittleEndian: + u16 := make([]uint16, (len(b)/2)-1) + err := binary.Read(reader, binary.LittleEndian, &u16) + if err != nil { + return nil, err + } + return []byte(string(utf16.Decode(u16))), nil + case utfbom.UTF16BigEndian: + u16 := make([]uint16, (len(b)/2)-1) + err := binary.Read(reader, binary.BigEndian, &u16) + if err != nil { + return nil, err + } + return []byte(string(utf16.Decode(u16))), nil + } + return ioutil.ReadAll(reader) +} diff --git a/pkg/controllers/internal/sops/key_server_from_secret.go b/pkg/controllers/internal/sops/key_server_from_secret.go new file mode 100644 index 000000000..f687825d9 --- /dev/null +++ b/pkg/controllers/internal/sops/key_server_from_secret.go @@ -0,0 +1,106 @@ +package sops + +import ( + "bytes" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + intkeyservice "github.com/kluctl/kluctl/v2/pkg/controllers/internal/sops/keyservice" + "go.mozilla.org/sops/v3/age" + "go.mozilla.org/sops/v3/azkv" + "go.mozilla.org/sops/v3/hcvault" + "go.mozilla.org/sops/v3/keyservice" + "go.mozilla.org/sops/v3/kms" + "go.mozilla.org/sops/v3/pgp" + corev1 "k8s.io/api/core/v1" + "path/filepath" + "strings" +) + +const ( + // DecryptionPGPExt is the extension of the file containing an armored PGP + // key. + DecryptionPGPExt = ".asc" + // DecryptionAgeExt is the extension of the file containing an age key + // file. + DecryptionAgeExt = ".agekey" + // DecryptionVaultTokenFileName is the name of the file containing the + // Hashicorp Vault token. + DecryptionVaultTokenFileName = "sops.vault-token" + // DecryptionAWSKmsFile is the name of the file containing the AWS KMS + // credentials. + DecryptionAWSKmsFile = "sops.aws-kms" + // DecryptionAzureAuthFile is the name of the file containing the Azure + // credentials. + DecryptionAzureAuthFile = "sops.azure-kv" + // DecryptionGCPCredsFile is the name of the file containing the GCP + // credentials. + DecryptionGCPCredsFile = "sops.gcp-kms" +) + +func BuildSopsKeyServerFromSecret(secret *corev1.Secret, gnuPGHomeDir string, opts ...intkeyservice.ServerOption) (keyservice.KeyServiceClient, error) { + gnuPGHome := pgp.GnuPGHome(gnuPGHomeDir) + + var ageIdentities age.ParsedIdentities + var vaultToken hcvault.Token + var awsCredsProvider *kms.CredentialsProvider + var azureToken azcore.TokenCredential + var gcpCredsJSON []byte + + var err error + + for name, value := range secret.Data { + switch filepath.Ext(name) { + case DecryptionPGPExt: + if err = gnuPGHome.Import(value); err != nil { + return nil, fmt.Errorf("failed to import '%s' data from decryption Secret: %w", name, err) + } + case DecryptionAgeExt: + if err = ageIdentities.Import(string(value)); err != nil { + return nil, fmt.Errorf("failed to import '%s' data from decryption Secret: %w", name, err) + } + case filepath.Ext(DecryptionVaultTokenFileName): + // Make sure we have the absolute name + if name == DecryptionVaultTokenFileName { + token := string(value) + token = strings.Trim(strings.TrimSpace(token), "\n") + vaultToken = hcvault.Token(token) + } + case filepath.Ext(DecryptionAWSKmsFile): + if name == DecryptionAWSKmsFile { + if awsCredsProvider, err = LoadCredsProviderFromYaml(value); err != nil { + return nil, fmt.Errorf("failed to import data from decryption Secret '%s': %w", name, err) + } + } + case filepath.Ext(DecryptionAzureAuthFile): + // Make sure we have the absolute name + if name == DecryptionAzureAuthFile { + conf := AADConfig{} + if err = LoadAADConfigFromBytes(value, &conf); err != nil { + return nil, fmt.Errorf("failed to import '%s' data from decryption Secret: %w", name, err) + } + if azureToken, err = TokenFromAADConfig(conf); err != nil { + return nil, fmt.Errorf("failed to import '%s' data from decryption Secret: %w", name, err) + } + } + case filepath.Ext(DecryptionGCPCredsFile): + if name == DecryptionGCPCredsFile { + gcpCredsJSON = bytes.Trim(value, "\n") + } + } + } + + serverOpts := []intkeyservice.ServerOption{ + intkeyservice.WithGnuPGHome(gnuPGHome), + intkeyservice.WithVaultToken(vaultToken), + intkeyservice.WithAgeIdentities(ageIdentities), + intkeyservice.WithGCPCredsJSON(gcpCredsJSON), + } + serverOpts = append(serverOpts, opts...) + if azureToken != nil { + serverOpts = append(serverOpts, intkeyservice.WithAzureToken{Token: azkv.NewTokenCredential(azureToken)}) + } + serverOpts = append(serverOpts, intkeyservice.WithAWSKeys{CredsProvider: awsCredsProvider}) + server := intkeyservice.NewServer(serverOpts...) + + return keyservice.NewCustomLocalClient(server), nil +} diff --git a/pkg/controllers/internal/sops/key_server_from_service_account.go b/pkg/controllers/internal/sops/key_server_from_service_account.go new file mode 100644 index 000000000..105e27ea5 --- /dev/null +++ b/pkg/controllers/internal/sops/key_server_from_service_account.go @@ -0,0 +1,91 @@ +package sops + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/arn" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/aws/smithy-go/logging" + intkeyservice "github.com/kluctl/kluctl/v2/pkg/controllers/internal/sops/keyservice" + "go.mozilla.org/sops/v3/keyservice" + "go.mozilla.org/sops/v3/kms" + authenticationv1 "k8s.io/api/authentication/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "os" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func BuildSopsKeyServerFromServiceAccount(ctx context.Context, client client.Client, sa *corev1.ServiceAccount) (keyservice.KeyServiceClient, error) { + var serverOpts []intkeyservice.ServerOption + + x, err := withAWSWebIdentity(ctx, client, sa) + if err != nil { + return nil, err + } + serverOpts = append(serverOpts, x...) + + server := intkeyservice.NewServer(serverOpts...) + return keyservice.NewCustomLocalClient(server), nil +} + +type webIdentityToken string + +func (t webIdentityToken) GetIdentityToken() ([]byte, error) { + return []byte(t), nil +} + +func withAWSWebIdentity(ctx context.Context, client client.Client, sa *corev1.ServiceAccount) ([]intkeyservice.ServerOption, error) { + roleArnStr := "" + if sa.GetAnnotations() != nil { + roleArnStr, _ = sa.GetAnnotations()["eks.amazonaws.com/role-arn"] + } + if roleArnStr == "" { + return nil, nil + } + roleArn, err := arn.Parse(roleArnStr) + if err != nil { + return nil, err + } + + exp := int64(60 * 10) + + tokenRequest := authenticationv1.TokenRequest{ + ObjectMeta: metav1.ObjectMeta{ + Name: sa.Name, + Namespace: sa.Namespace, + }, + Spec: authenticationv1.TokenRequestSpec{ + Audiences: []string{"sts.amazonaws.com"}, + ExpirationSeconds: &exp, + }, + } + + err = client.SubResource("token").Create(ctx, sa, &tokenRequest) + if err != nil { + return nil, fmt.Errorf("failed to create token for AWS STS: %w", err) + } + + cfg := aws.Config{ + Credentials: aws.AnonymousCredentials{}, + Logger: logging.NewStandardLogger(os.Stderr), + Region: roleArn.Region, + } + if cfg.Region == "" { + cfg.Region = "aws-global" + } + + optFns := []func(*stscreds.WebIdentityRoleOptions){ + func(options *stscreds.WebIdentityRoleOptions) { + options.RoleSessionName = "kluctl-sops-decrypter" + }, + } + + provider := stscreds.NewWebIdentityRoleProvider(sts.NewFromConfig(cfg), roleArnStr, webIdentityToken(tokenRequest.Status.Token), optFns...) + + var serverOpts []intkeyservice.ServerOption + serverOpts = append(serverOpts, intkeyservice.WithAWSKeys{CredsProvider: kms.NewCredentialsProvider(provider)}) + return serverOpts, nil +} diff --git a/pkg/controllers/internal/sops/keyservice/keyservice.go b/pkg/controllers/internal/sops/keyservice/keyservice.go new file mode 100644 index 000000000..4d2dda569 --- /dev/null +++ b/pkg/controllers/internal/sops/keyservice/keyservice.go @@ -0,0 +1,23 @@ +// Copyright (C) 2022 The Flux authors +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package keyservice + +import ( + "go.mozilla.org/sops/v3/age" + "go.mozilla.org/sops/v3/keys" + "go.mozilla.org/sops/v3/pgp" +) + +// IsOfflineMethod returns true for offline decrypt methods or false otherwise +func IsOfflineMethod(mk keys.MasterKey) bool { + switch mk.(type) { + case *pgp.MasterKey, *age.MasterKey: + return true + default: + return false + } +} diff --git a/pkg/controllers/internal/sops/keyservice/options.go b/pkg/controllers/internal/sops/keyservice/options.go new file mode 100644 index 000000000..555452ceb --- /dev/null +++ b/pkg/controllers/internal/sops/keyservice/options.go @@ -0,0 +1,87 @@ +// Copyright (C) 2022 The Flux authors +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package keyservice + +import ( + extage "filippo.io/age" + "go.mozilla.org/sops/v3/age" + "go.mozilla.org/sops/v3/azkv" + "go.mozilla.org/sops/v3/gcpkms" + "go.mozilla.org/sops/v3/hcvault" + "go.mozilla.org/sops/v3/keyservice" + "go.mozilla.org/sops/v3/kms" + "go.mozilla.org/sops/v3/pgp" +) + +// ServerOption is some configuration that modifies the Server. +type ServerOption interface { + // ApplyToServer applies this configuration to the given Server. + ApplyToServer(s *Server) +} + +// WithGnuPGHome configures the GnuPG home directory on the Server. +type WithGnuPGHome string + +// ApplyToServer applies this configuration to the given Server. +func (o WithGnuPGHome) ApplyToServer(s *Server) { + s.gnuPGHome = pgp.GnuPGHome(o) +} + +// WithVaultToken configures the Hashicorp Vault token on the Server. +type WithVaultToken string + +// ApplyToServer applies this configuration to the given Server. +func (o WithVaultToken) ApplyToServer(s *Server) { + s.vaultToken = hcvault.Token(o) +} + +// WithAgeIdentities configures the parsed age identities on the Server. +type WithAgeIdentities []extage.Identity + +// ApplyToServer applies this configuration to the given Server. +func (o WithAgeIdentities) ApplyToServer(s *Server) { + s.ageIdentities = age.ParsedIdentities(o) +} + +// WithAWSKeys configures the AWS credentials on the Server +type WithAWSKeys struct { + CredsProvider *kms.CredentialsProvider +} + +// ApplyToServer applies this configuration to the given Server. +func (o WithAWSKeys) ApplyToServer(s *Server) { + s.awsCredsProvider = o.CredsProvider +} + +// WithGCPCredsJSON configures the GCP service account credentials JSON on the +// Server. +type WithGCPCredsJSON []byte + +// ApplyToServer applies this configuration to the given Server. +func (o WithGCPCredsJSON) ApplyToServer(s *Server) { + s.gcpCredsJSON = gcpkms.CredentialJSON(o) +} + +// WithAzureToken configures the Azure credential token on the Server. +type WithAzureToken struct { + Token *azkv.TokenCredential +} + +// ApplyToServer applies this configuration to the given Server. +func (o WithAzureToken) ApplyToServer(s *Server) { + s.azureToken = o.Token +} + +// WithDefaultServer configures the fallback default server on the Server. +type WithDefaultServer struct { + Server keyservice.KeyServiceServer +} + +// ApplyToServer applies this configuration to the given Server. +func (o WithDefaultServer) ApplyToServer(s *Server) { + s.defaultServer = o.Server +} diff --git a/pkg/controllers/internal/sops/keyservice/server.go b/pkg/controllers/internal/sops/keyservice/server.go new file mode 100644 index 000000000..584516957 --- /dev/null +++ b/pkg/controllers/internal/sops/keyservice/server.go @@ -0,0 +1,363 @@ +// Copyright (C) 2022 The Flux authors +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package keyservice + +import ( + "fmt" + "go.mozilla.org/sops/v3/age" + "go.mozilla.org/sops/v3/azkv" + "go.mozilla.org/sops/v3/gcpkms" + "go.mozilla.org/sops/v3/hcvault" + "go.mozilla.org/sops/v3/keyservice" + "go.mozilla.org/sops/v3/kms" + "go.mozilla.org/sops/v3/pgp" + + "golang.org/x/net/context" +) + +// Server is a key service server that uses SOPS MasterKeys to fulfill +// requests. It intercepts Encrypt and Decrypt requests made for key types +// that need to run in a contained environment, instead of the default +// implementation which heavily utilizes environment variables or the runtime +// environment. Any request not handled by the Server is forwarded to the +// embedded default server. +type Server struct { + // gnuPGHome is the GnuPG home directory used for the Encrypt and Decrypt + // operations for PGP key types. + // When empty, the requests will be handled using the systems' runtime + // keyring. + gnuPGHome pgp.GnuPGHome + + // ageIdentities are the parsed age identities used for Decrypt + // operations for age key types. + ageIdentities age.ParsedIdentities + + // vaultToken is the token used for Encrypt and Decrypt operations of + // Hashicorp Vault requests. + // When empty, the request will be handled by defaultServer. + vaultToken hcvault.Token + + // azureToken is the credential token used for Encrypt and Decrypt + // operations of Azure Key Vault requests. + // When nil, the request will be handled by defaultServer. + azureToken *azkv.TokenCredential + + // awsCredsProvider is the Credentials object used for Encrypt and Decrypt + // operations of AWS KMS requests. + // When nil, the request will be handled by defaultServer. + awsCredsProvider *kms.CredentialsProvider + + // gcpCredsJSON is the JSON credentials used for Decrypt and Encrypt + // operations of GCP KMS requests. When nil, a default client with + // environmental runtime settings will be used. + gcpCredsJSON gcpkms.CredentialJSON + + // defaultServer is the fallback server, used to handle any request that + // is not eligible to be handled by this Server. + defaultServer keyservice.KeyServiceServer +} + +// NewServer constructs a new Server, configuring it with the provided options +// before returning the result. +// When WithDefaultServer() is not provided as an option, the SOPS server +// implementation is configured as default. +func NewServer(options ...ServerOption) keyservice.KeyServiceServer { + s := &Server{} + for _, opt := range options { + opt.ApplyToServer(s) + } + if s.defaultServer == nil { + s.defaultServer = &keyservice.Server{ + Prompt: false, + } + } + return s +} + +// Encrypt takes an encrypt request and encrypts the provided plaintext with +// the provided key, returning the encrypted result. +func (ks Server) Encrypt(ctx context.Context, req *keyservice.EncryptRequest) (*keyservice.EncryptResponse, error) { + key := req.Key + switch k := key.KeyType.(type) { + case *keyservice.Key_PgpKey: + ciphertext, err := ks.encryptWithPgp(k.PgpKey, req.Plaintext) + if err != nil { + return nil, err + } + return &keyservice.EncryptResponse{ + Ciphertext: ciphertext, + }, nil + case *keyservice.Key_AgeKey: + ciphertext, err := ks.encryptWithAge(k.AgeKey, req.Plaintext) + if err != nil { + return nil, err + } + return &keyservice.EncryptResponse{ + Ciphertext: ciphertext, + }, nil + case *keyservice.Key_VaultKey: + if ks.vaultToken != "" { + ciphertext, err := ks.encryptWithHCVault(k.VaultKey, req.Plaintext) + if err != nil { + return nil, err + } + return &keyservice.EncryptResponse{ + Ciphertext: ciphertext, + }, nil + } + case *keyservice.Key_KmsKey: + cipherText, err := ks.encryptWithAWSKMS(k.KmsKey, req.Plaintext) + if err != nil { + return nil, err + } + return &keyservice.EncryptResponse{ + Ciphertext: cipherText, + }, nil + case *keyservice.Key_AzureKeyvaultKey: + if ks.azureToken != nil { + ciphertext, err := ks.encryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Plaintext) + if err != nil { + return nil, err + } + return &keyservice.EncryptResponse{ + Ciphertext: ciphertext, + }, nil + } + case *keyservice.Key_GcpKmsKey: + ciphertext, err := ks.encryptWithGCPKMS(k.GcpKmsKey, req.Plaintext) + if err != nil { + return nil, err + } + return &keyservice.EncryptResponse{ + Ciphertext: ciphertext, + }, nil + case nil: + return nil, fmt.Errorf("must provide a key") + } + // Fallback to default server for any other request. + return ks.defaultServer.Encrypt(ctx, req) +} + +// Decrypt takes a decrypt request and decrypts the provided ciphertext with +// the provided key, returning the decrypted result. +func (ks Server) Decrypt(ctx context.Context, req *keyservice.DecryptRequest) (*keyservice.DecryptResponse, error) { + key := req.Key + switch k := key.KeyType.(type) { + case *keyservice.Key_PgpKey: + plaintext, err := ks.decryptWithPgp(k.PgpKey, req.Ciphertext) + if err != nil { + return nil, err + } + return &keyservice.DecryptResponse{ + Plaintext: plaintext, + }, nil + case *keyservice.Key_AgeKey: + plaintext, err := ks.decryptWithAge(k.AgeKey, req.Ciphertext) + if err != nil { + return nil, err + } + return &keyservice.DecryptResponse{ + Plaintext: plaintext, + }, nil + case *keyservice.Key_VaultKey: + if ks.vaultToken != "" { + plaintext, err := ks.decryptWithHCVault(k.VaultKey, req.Ciphertext) + if err != nil { + return nil, err + } + return &keyservice.DecryptResponse{ + Plaintext: plaintext, + }, nil + } + case *keyservice.Key_KmsKey: + plaintext, err := ks.decryptWithAWSKMS(k.KmsKey, req.Ciphertext) + if err != nil { + return nil, err + } + return &keyservice.DecryptResponse{ + Plaintext: plaintext, + }, nil + case *keyservice.Key_AzureKeyvaultKey: + if ks.azureToken != nil { + plaintext, err := ks.decryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Ciphertext) + if err != nil { + return nil, err + } + return &keyservice.DecryptResponse{ + Plaintext: plaintext, + }, nil + } + case *keyservice.Key_GcpKmsKey: + plaintext, err := ks.decryptWithGCPKMS(k.GcpKmsKey, req.Ciphertext) + if err != nil { + return nil, err + } + return &keyservice.DecryptResponse{ + Plaintext: plaintext, + }, nil + case nil: + return nil, fmt.Errorf("must provide a key") + } + // Fallback to default server for any other request. + return ks.defaultServer.Decrypt(ctx, req) +} + +func (ks *Server) encryptWithPgp(key *keyservice.PgpKey, plaintext []byte) ([]byte, error) { + pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint) + if ks.gnuPGHome != "" { + ks.gnuPGHome.ApplyToMasterKey(pgpKey) + } + err := pgpKey.Encrypt(plaintext) + if err != nil { + return nil, err + } + return []byte(pgpKey.EncryptedKey), nil +} + +func (ks *Server) decryptWithPgp(key *keyservice.PgpKey, ciphertext []byte) ([]byte, error) { + pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint) + if ks.gnuPGHome != "" { + ks.gnuPGHome.ApplyToMasterKey(pgpKey) + } + pgpKey.EncryptedKey = string(ciphertext) + plaintext, err := pgpKey.Decrypt() + return plaintext, err +} + +func (ks Server) encryptWithAge(key *keyservice.AgeKey, plaintext []byte) ([]byte, error) { + // Unlike the other encrypt and decrypt methods, validation of configuration + // is not required here. As the encryption happens purely based on the + // Recipient from the key. + + ageKey := age.MasterKey{ + Recipient: key.Recipient, + } + if err := ageKey.Encrypt(plaintext); err != nil { + return nil, err + } + return []byte(ageKey.EncryptedKey), nil +} + +func (ks *Server) decryptWithAge(key *keyservice.AgeKey, ciphertext []byte) ([]byte, error) { + ageKey := age.MasterKey{ + Recipient: key.Recipient, + } + ks.ageIdentities.ApplyToMasterKey(&ageKey) + ageKey.EncryptedKey = string(ciphertext) + plaintext, err := ageKey.Decrypt() + return plaintext, err +} + +func (ks *Server) encryptWithHCVault(key *keyservice.VaultKey, plaintext []byte) ([]byte, error) { + vaultKey := hcvault.MasterKey{ + VaultAddress: key.VaultAddress, + EnginePath: key.EnginePath, + KeyName: key.KeyName, + } + ks.vaultToken.ApplyToMasterKey(&vaultKey) + if err := vaultKey.Encrypt(plaintext); err != nil { + return nil, err + } + return []byte(vaultKey.EncryptedKey), nil +} + +func (ks *Server) decryptWithHCVault(key *keyservice.VaultKey, ciphertext []byte) ([]byte, error) { + vaultKey := hcvault.MasterKey{ + VaultAddress: key.VaultAddress, + EnginePath: key.EnginePath, + KeyName: key.KeyName, + } + vaultKey.EncryptedKey = string(ciphertext) + ks.vaultToken.ApplyToMasterKey(&vaultKey) + plaintext, err := vaultKey.Decrypt() + return plaintext, err +} + +func (ks *Server) encryptWithAWSKMS(key *keyservice.KmsKey, plaintext []byte) ([]byte, error) { + context := make(map[string]*string) + for key, val := range key.Context { + val := val + context[key] = &val + } + awsKey := kms.MasterKey{ + Arn: key.Arn, + Role: key.Role, + EncryptionContext: context, + } + if ks.awsCredsProvider != nil { + ks.awsCredsProvider.ApplyToMasterKey(&awsKey) + } + if err := awsKey.Encrypt(plaintext); err != nil { + return nil, err + } + return []byte(awsKey.EncryptedKey), nil +} + +func (ks *Server) decryptWithAWSKMS(key *keyservice.KmsKey, cipherText []byte) ([]byte, error) { + context := make(map[string]*string) + for key, val := range key.Context { + val := val + context[key] = &val + } + awsKey := kms.MasterKey{ + Arn: key.Arn, + Role: key.Role, + EncryptionContext: context, + } + awsKey.EncryptedKey = string(cipherText) + + if ks.awsCredsProvider != nil { + ks.awsCredsProvider.ApplyToMasterKey(&awsKey) + } + return awsKey.Decrypt() +} + +func (ks *Server) encryptWithAzureKeyVault(key *keyservice.AzureKeyVaultKey, plaintext []byte) ([]byte, error) { + azureKey := azkv.MasterKey{ + VaultURL: key.VaultUrl, + Name: key.Name, + Version: key.Version, + } + ks.azureToken.ApplyToMasterKey(&azureKey) + if err := azureKey.Encrypt(plaintext); err != nil { + return nil, err + } + return []byte(azureKey.EncryptedKey), nil +} + +func (ks *Server) decryptWithAzureKeyVault(key *keyservice.AzureKeyVaultKey, ciphertext []byte) ([]byte, error) { + azureKey := azkv.MasterKey{ + VaultURL: key.VaultUrl, + Name: key.Name, + Version: key.Version, + } + ks.azureToken.ApplyToMasterKey(&azureKey) + azureKey.EncryptedKey = string(ciphertext) + plaintext, err := azureKey.Decrypt() + return plaintext, err +} + +func (ks *Server) encryptWithGCPKMS(key *keyservice.GcpKmsKey, plaintext []byte) ([]byte, error) { + gcpKey := gcpkms.MasterKey{ + ResourceID: key.ResourceId, + } + ks.gcpCredsJSON.ApplyToMasterKey(&gcpKey) + if err := gcpKey.Encrypt(plaintext); err != nil { + return nil, err + } + return gcpKey.EncryptedDataKey(), nil +} + +func (ks *Server) decryptWithGCPKMS(key *keyservice.GcpKmsKey, ciphertext []byte) ([]byte, error) { + gcpKey := gcpkms.MasterKey{ + ResourceID: key.ResourceId, + } + ks.gcpCredsJSON.ApplyToMasterKey(&gcpKey) + gcpKey.EncryptedKey = string(ciphertext) + plaintext, err := gcpKey.Decrypt() + return plaintext, err +} diff --git a/pkg/controllers/internal/sops/keyservice/server_test.go b/pkg/controllers/internal/sops/keyservice/server_test.go new file mode 100644 index 000000000..d7e0b7b33 --- /dev/null +++ b/pkg/controllers/internal/sops/keyservice/server_test.go @@ -0,0 +1,249 @@ +// Copyright (C) 2022 The Flux authors +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package keyservice + +import ( + "fmt" + "go.mozilla.org/sops/v3/age" + "go.mozilla.org/sops/v3/azkv" + "go.mozilla.org/sops/v3/gcpkms" + "go.mozilla.org/sops/v3/hcvault" + "go.mozilla.org/sops/v3/kms" + "go.mozilla.org/sops/v3/pgp" + "os" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/aws/aws-sdk-go-v2/credentials" + . "github.com/onsi/gomega" + "go.mozilla.org/sops/v3/keyservice" + "golang.org/x/net/context" +) + +func TestServer_EncryptDecrypt_PGP(t *testing.T) { + const ( + mockPublicKey = "testdata/public.gpg" + mockPrivateKey = "testdata/private.gpg" + mockFingerprint = "B59DAF469E8C948138901A649732075EA221A7EA" + ) + + g := NewWithT(t) + + gnuPGHome, err := pgp.NewGnuPGHome() + g.Expect(err).ToNot(HaveOccurred()) + t.Cleanup(func() { + _ = os.RemoveAll(gnuPGHome.String()) + }) + g.Expect(gnuPGHome.ImportFile(mockPublicKey)).To(Succeed()) + + s := NewServer(WithGnuPGHome(gnuPGHome)) + key := KeyFromMasterKey(pgp.NewMasterKeyFromFingerprint(mockFingerprint)) + dataKey := []byte("some data key") + encResp, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ + Key: &key, + Plaintext: dataKey, + }) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(encResp.Ciphertext).ToNot(BeEmpty()) + g.Expect(encResp.Ciphertext).ToNot(Equal(dataKey)) + + g.Expect(gnuPGHome.ImportFile(mockPrivateKey)).To(Succeed()) + decResp, err := s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ + Key: &key, + Ciphertext: encResp.Ciphertext, + }) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(decResp.Plaintext).To(Equal(dataKey)) +} + +func TestServer_EncryptDecrypt_age(t *testing.T) { + g := NewWithT(t) + + const ( + mockRecipient string = "age1lzd99uklcjnc0e7d860axevet2cz99ce9pq6tzuzd05l5nr28ams36nvun" + mockIdentity string = "AGE-SECRET-KEY-1G0Q5K9TV4REQ3ZSQRMTMG8NSWQGYT0T7TZ33RAZEE0GZYVZN0APSU24RK7" + ) + + s := NewServer() + key := KeyFromMasterKey(&age.MasterKey{Recipient: mockRecipient}) + dataKey := []byte("some data key") + encResp, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ + Key: &key, + Plaintext: dataKey, + }) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(encResp.Ciphertext).ToNot(BeEmpty()) + g.Expect(encResp.Ciphertext).ToNot(Equal(dataKey)) + + i := make(age.ParsedIdentities, 0) + g.Expect(i.Import(mockIdentity)).To(Succeed()) + + s = NewServer(WithAgeIdentities(i)) + decResp, err := s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ + Key: &key, + Ciphertext: encResp.Ciphertext, + }) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(decResp.Plaintext).To(Equal(dataKey)) +} + +func TestServer_EncryptDecrypt_HCVault(t *testing.T) { + g := NewWithT(t) + + s := NewServer(WithVaultToken("token")) + key := KeyFromMasterKey(hcvault.NewMasterKey("https://example.com", "engine-path", "key-name")) + _, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ + Key: &key, + }) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to encrypt sops data key to Vault transit backend")) + + _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ + Key: &key, + }) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to decrypt sops data key from Vault transit backend")) +} + +func TestServer_EncryptDecrypt_HCVault_Fallback(t *testing.T) { + g := NewWithT(t) + + fallback := NewMockKeyServer() + s := NewServer(WithDefaultServer{Server: fallback}) + + key := KeyFromMasterKey(hcvault.NewMasterKey("https://example.com", "engine-path", "key-name")) + encReq := &keyservice.EncryptRequest{ + Key: &key, + Plaintext: []byte("some data key"), + } + _, err := s.Encrypt(context.TODO(), encReq) + g.Expect(err).To(HaveOccurred()) + g.Expect(fallback.encryptReqs).To(HaveLen(1)) + g.Expect(fallback.encryptReqs).To(ContainElement(encReq)) + g.Expect(fallback.decryptReqs).To(HaveLen(0)) + + fallback = NewMockKeyServer() + s = NewServer(WithDefaultServer{Server: fallback}) + decReq := &keyservice.DecryptRequest{ + Key: &key, + Ciphertext: []byte("some ciphertext"), + } + _, err = s.Decrypt(context.TODO(), decReq) + g.Expect(fallback.decryptReqs).To(HaveLen(1)) + g.Expect(fallback.decryptReqs).To(ContainElement(decReq)) + g.Expect(fallback.encryptReqs).To(HaveLen(0)) +} + +func TestServer_EncryptDecrypt_awskms(t *testing.T) { + g := NewWithT(t) + s := NewServer(WithAWSKeys{ + CredsProvider: kms.NewCredentialsProvider(credentials.StaticCredentialsProvider{}), + }) + + key := KeyFromMasterKey(kms.NewMasterKeyFromArn("arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48", nil, "")) + _, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ + Key: &key, + }) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to encrypt sops data key with AWS KMS")) + + _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ + Key: &key, + }) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to decrypt sops data key with AWS KMS")) +} + +func TestServer_EncryptDecrypt_azkv(t *testing.T) { + g := NewWithT(t) + + identity, err := azidentity.NewDefaultAzureCredential(nil) + g.Expect(err).ToNot(HaveOccurred()) + s := NewServer(WithAzureToken{Token: azkv.NewTokenCredential(identity)}) + + key := KeyFromMasterKey(azkv.NewMasterKey("", "", "")) + _, err = s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ + Key: &key, + }) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to encrypt sops data key with Azure Key Vault")) + + _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ + Key: &key, + }) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to decrypt sops data key with Azure Key Vault")) + +} + +func TestServer_EncryptDecrypt_azkv_Fallback(t *testing.T) { + g := NewWithT(t) + + fallback := NewMockKeyServer() + s := NewServer(WithDefaultServer{Server: fallback}) + + key := KeyFromMasterKey(azkv.NewMasterKey("", "", "")) + encReq := &keyservice.EncryptRequest{ + Key: &key, + Plaintext: []byte("some data key"), + } + _, err := s.Encrypt(context.TODO(), encReq) + g.Expect(err).To(HaveOccurred()) + g.Expect(fallback.encryptReqs).To(HaveLen(1)) + g.Expect(fallback.encryptReqs).To(ContainElement(encReq)) + g.Expect(fallback.decryptReqs).To(HaveLen(0)) + + fallback = NewMockKeyServer() + s = NewServer(WithDefaultServer{Server: fallback}) + + decReq := &keyservice.DecryptRequest{ + Key: &key, + Ciphertext: []byte("some ciphertext"), + } + _, err = s.Decrypt(context.TODO(), decReq) + g.Expect(fallback.decryptReqs).To(HaveLen(1)) + g.Expect(fallback.decryptReqs).To(ContainElement(decReq)) + g.Expect(fallback.encryptReqs).To(HaveLen(0)) +} + +func TestServer_EncryptDecrypt_gcpkms(t *testing.T) { + g := NewWithT(t) + + creds := `{ "client_id": ".apps.googleusercontent.com", + "client_secret": "", + "type": "authorized_user"}` + s := NewServer(WithGCPCredsJSON([]byte(creds))) + + resourceID := "projects/test-flux/locations/global/keyRings/test-flux/cryptoKeys/sops" + key := KeyFromMasterKey(gcpkms.NewMasterKeyFromResourceID(resourceID)) + _, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ + Key: &key, + }) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to encrypt sops data key with GCP KMS")) + + _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ + Key: &key, + }) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to decrypt sops data key with GCP KMS")) + +} + +func TestServer_EncryptDecrypt_Nil_KeyType(t *testing.T) { + g := NewWithT(t) + + s := NewServer(WithDefaultServer{NewMockKeyServer()}) + + expectErr := fmt.Errorf("must provide a key") + + _, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{Key: &keyservice.Key{KeyType: nil}}) + g.Expect(err).To(Equal(expectErr)) + + _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{Key: &keyservice.Key{KeyType: nil}}) + g.Expect(err).To(Equal(expectErr)) +} diff --git a/pkg/controllers/internal/sops/keyservice/testdata/private.gpg b/pkg/controllers/internal/sops/keyservice/testdata/private.gpg new file mode 100644 index 000000000..f3ca23e77 --- /dev/null +++ b/pkg/controllers/internal/sops/keyservice/testdata/private.gpg @@ -0,0 +1,81 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQVYBGJGqrsBDAC7OxFP6Z2E+AkVZpQySjLFAeYJWdnadx0GOHnckOOFkQvVJauz +9KibgzLUkO9h0oIoP7dLyPEiRPhKgmbrktyCDfysvNeKCgI5XemJCJqCmwA/vWwp +GnVltcgsVVjVZ3vvD8VMfhKF77pkmMDj7mnCPw9x39R8SVpe2K9RO0QLk/Dt+o8t +MO+sXTz4ba4aMdjJvMoaoQKw6RXAouZa4H09i6tiAgXrRLxQDxJ58sGg/ZCWa5G4 +aI6PdObY41fzQlcobtifCbktbICVb1Ms1s0iZWttFmr0oTSkJTv3FPWhf6n126w4 +LEkF9d6YW+/0H9cqXa4GMfxXg4XBmJNJfYkLDVUlbp3xi+I+Lg1Sit6QlqkW93EW +etpYPK1KmDcW3IA6ausYnkyrcQbt1m5/hh9KJoQb6He/RytXEBxp90+v9y7THZGr +2U49ZEHQg6DAIj4j1p9NAGgqjKr9am6yk2pvpK3ZWmHQ6CZfCiBrEPCvdmEhrx4U +lj6wyd00YJknpDkAEQEAAQAL/iU3C+1g55TvAks1LP7D/cxn4LP6HpHMfEHoxtwf +FoJNftcamkL2Pe9PSDK1Lke44nMimwnewoNHxzx0KAXqFpdpNVCWZpdC/wctEgbR +ZXjRW17QBWg0IKKbW9LoEfS1EY7GiTZ3lrH1oQxuymRj1rSr+SNu1Jrxr5tLoalZ +SOCuQsTiuUPHxtPxYnWUw3bkco1Cz780QscsRU0ZdAUbOvmZQfMEqO2HJ5EYNdl0 +daVM0Uj8z6Wibre4CkyQ/8HT7Qy+Z7fgGzcSfwSzqqVJ5GtJimHK8CfX3HnHovHm +o7MtZGnr9fiug8m3XN8b1zM/ADXbVMXAmQY+QsI44bQPMxocLij3/HL5/oJvXGJ8 +rBc5xFbzvclteD2MPTHx7hpZP+ys84LpieMw9Fojk9/k1tfNNJEESVp1OTI42YFA +bmQ52Qgehn6skcs1oAygsSjaoCkhFMnhHVNtOnoSUG+2O7xpaT6cSNV4ySo/0stQ +85RrEu0skjzChI0D1Tb4o2qI2wYAyUQtUxwN46uqfo/NpzzBHujpizdQ9VJxfof5 +2xCrLZ2AzLAhhpiqUiNTu7PPC+fXh3T1ru4pOg6JZYc6Ok0R3Huu16S51wpK66LE +xgSkzGRET1kZFYg4G99g7jgmhhOyKOeh7J1LL5raOETX7hoPfO9M99tmRSVBuHFr +48e0SQpb/mf6yYEr5ndsX3uvONo+86rerWDigZhOyvIYY8OyMzof0h7UP1jOg0uG +JGj80rQDdqb48mTZW+d4ZC+1EstzBgDuJcG+Z/ufe2cymUwZUS6gpWuFddhab09a +ZeS4ojlg40Jv8LcBtX29tuFSk228YWWa10u7KOzVSc9MIIVYyfTNhUXyKr+tyAFg +jwVXWqKsDo53uwWClCwB7cvmep0sArR2hx/JdO2zAOrc0Myhx0XhwFdvV3lnNzZr +8hbXcwLtT/HGJVl3ivmiXyfWo2MZDlY7mAw5+84WaBqcmKe0+ugRFIOhvO9GR5cU +NysXNfEur7qRuEKqFU6BPePg8olc/qMF/3undrcOcPfyME1VG1mKkBJDFek15VxZ +URiLhKyQtDSR1BJKeicGiVPVVeLoqyQvslXihm3c7EPPnz+Q8fQiR4sUWbh2BgPv +Ckv0CP5a4RqiuV9B9pXqew2voJtRB1fU95JWIV08CLlcqLVArZFlxvUAVmQDF64Z +EW+2dvXr42+KwBWIteyblvlirVztknqoO3gyk3AvYe16uX9K1Mu+7xLUByg6Rv83 +duS2YUjm6xj/ogYD6wU0zUXQ5Lx0JhKzA+jktBVGbHV4IDxzb3BzQGZsdXhjZC5p +bz6JAc4EEwEIADgWIQS1na9GnoyUgTiQGmSXMgdeoiGn6gUCYkaquwIbAwULCQgH +AgYVCgkICwIEFgIDAQIeAQIXgAAKCRCXMgdeoiGn6ppqDACakBksOfI9xkcV2J4o +KkElDPzPMVlWeuulegHbS8R6srEJDxdR4jUpFVIlDp88xwMurpAZkwJdxzWLWj8N +GuW5Z0s3WuM1h17BYE/ZKGc4pBf7/A2OzDhS8IrqNuE7kNfupPgorVwbuNs5C8ho +w6MP7yqrxVOkWRcz1lx29FJIes+I46P+H/5rAv+4fiGWLj33wHhHpxTo4JWViYyl +8S3aKN0yrpNqzU/b/cQoEydsNks8cuHBh4QMjH+1sh3s0ery2Z5VPBBccxGe94Sp +vbh1fkhe2LIZKdfrh47WVPJVyUaUJC/JzCcINCNpGjpOxAIM8NxfUIF2k+SlgREN +TQztXAnHYWHcTSP8ojbN8vODka6LMgEtOo0WB/H7/NvBDuc09izP1HwdNM2dTkPu +plbmEdg9NkFgp+H8QgAxHGWVN22gzYaxJVO9PFrpF2D83Zch4zAYt/wYEWC/SK9A +cdbevVdetFPCCV5dSJCWq+e9llnJaxR4bDbTf3EQW7aulg2dBVgEYkaquwEMAMQV +snEOeBV1iX6hCiMWwgjnyS+ggIZN2F15NgM76VMLYMyOt7Y3nkBAFCZgEA9IymrC +UiPSk+YzDtebWgprqAgNgqovSl2c1xuacjuHgpG7DQiV20sb752BMMDDEUY05YyQ +a5NmCUqJIB2F0mxtIqbUpPgdHLSidgRX/5VjugKSlkD+JqURIpW6lmAaJ5RgbWbX +Puuy8yFsHtd75jp5fQFDSMxSG30ZBQAky+4vw8zTxbOLdXS3FrZr6YvLfmAMafoG +aZKAKEOxAZCp152JxUm6yvgXGIlgDDPLHyt5tpWwi98vw633NVE3MkKwic0HuSHE +PXemwZJi3z4yaBsofv7HCo9KtGx4I+t/cqEk8qri3dPqsEkiWKfN3FdzKndgd894 +GtFlO22y9/8pcjpeG3ErTq4rTmo3lkLnxbGxgENDoPEcJ8Q/xu+ZAdjqs5kRnivQ +57Qk0KCRU8HrzBDBWs13Sac8qbgR+Hvyq29UQJOQo0phKVOfb6oqym9ZsB8q7wAR +AQABAAv/VMgu2exQJrMl6o8V03slFXWmywWCXM+u1CezH23ZojMCvR+eNlbRAWXT +cI5Lk1g9UTDJFD0Z/sgnzDibE3Nd+XFiBFSjOlu0tHYwmyWp4nn2ljY5Vb3z+m2g +F1CgmPMJJ6BQKzDMpqIotSsmAwSjHXBHDhKEVWQDVDh6RW0TwcYA2oQpUGjaw9Oj +7lSQtXqGAxfhWEcNEe/uW+xx7OmXj6K4iMOdqBbXzyqZ1FhpuBf+3PVZKUh6tRBu +sCeh8kSbEOh4xpPFcs17EAcZfXTfdo9vtqjQkRUASuEzctR/91qI3c7IPMFilSHo +HdVQLyUiJTuY0k/00Je1QtPgh7lZtffkq6Bd11I53cfD+44l7g7Lcc2zFzuHjpyp +F+SDBs3tD8uKqbam8Hnop49/BRe1mgNdzobUEem39zKNSXWqUFemr15sAW2J4SKM +m657y0hdpGDE/QlD0ruE6sFFa8zk+92UElnzgyLl69YO139Sbjm+jSZfMVxm+nO6 +vrW16U75BgDIIH6PgZshXxMO1LzWxMP+d+6MWKpgeWYes/tkI6bfmM+LBh5/zzd2 +lV+9Zae/YJYMn30BrpWfE1uVcWVxlAilHYM92wtH8CjXIYkLhez5uTQQ7UKcLqyS +3nmO+u5ADqwPeaygS+gTTWhRFX0F5dTYhXFQABiK94lfyMJ8tzmjAuwWxPINLL0m +IdilK8crZayDarDBe2amiP/gG6W7zzIV8DvJn2ZZtlraFxTV8+mqq9Lh0cKJHT6/ +1/Lt90eubhcGAPrUTKv/jvMEZWlsw1HNzPfP+VyAtebmgDY0o2Rkpikaie/bsnAR +umPuRdGNMct5UAG8WLrgO/RIFb0dsJy1CA+zvZdluAHFaq89ikwFrvcvegc0325w +Iom8xiH0C9pLPiPcyFoFedQ3ZRaQB4oLhhikNDvD2ANA9HIY+k7dpfXP8LWY5jSs +UM3NdXC8RIdBW7DllfDNjWi/xaAyBDxcXRBPuWjIYWlLcHjmqablB/xdmZgIEJoU +VMPCRf7JAl3I6QX9Htkv4ocJyzRDxmhTuFdc7YOc3zmTqwx7/q+kuJDGROA1VeFT +HCtWrSF7Ax5WNIIRRFH1AEk8j//2yoycVCWKNMXV0d8xeHblyGVX+Huhq8rsokNU +MFbDY4wFDTzTK8F8fCa6Z0Q1nts6HOf5ZXv2xYMjyh93gJKF2/NhobX7noDMe/oR +CUzbd6Ogg3JLqnlrfIhR/Kh3yk+w/FhGRiQsV1rIqx4FrWvA3CTkr2zHTAH/gQvt +1CKrnh3iKiqJ9uV26yiJAbYEGAEIACAWIQS1na9GnoyUgTiQGmSXMgdeoiGn6gUC +YkaquwIbDAAKCRCXMgdeoiGn6jSVDACYkZWrhX/TM6bBVCGvhzl3EmwHqMuMT/Qx +N5Sc5QVawRD36+L/yuFYzK+MK9s9p5Z/9VmTsO/KQxcaPiuYub5vsJ38AxsaSiPE +VCtXY1QH0R3AYMh7tCGW+qhyf8IZyynkiOIZmo8PdrSwRnBCWGPvHYqJEr7c5LJD +0RYZFwR+ujPhr5mavERVziF2EfUor33la5vpax+CD+XLeMQaWorGegFN6wEpoGoQ +1rP10xtM+txU7/w0fkYHaEzvQfnRN4QVNg/EgQx7U+HyklAM36tGYgj2CRF5qm+K +Whv+ipymfmAngrjNMqcM15uXi1MF3UGFG7QkbKUBqpeK9UfG4lnZKHcSwhffcgL6 +clz1mGfriCEJvw9CfvlLm7RDM2m/MRxFr2yNQgpIJFoXgDVCthBCuH1dIMvhgCYA +frIIYzTK2ZKLJlTv3O8SCTf1Zhjru2f3z85YAqOXmQUGYKrQZL2T9NE2mQnXL0n+ +sgS+XwT2h+fdCBJHJYmboxXpxC02xHY= +=G8JF +-----END PGP PRIVATE KEY BLOCK----- diff --git a/pkg/controllers/internal/sops/keyservice/testdata/public.gpg b/pkg/controllers/internal/sops/keyservice/testdata/public.gpg new file mode 100644 index 000000000..c59ba01a6 --- /dev/null +++ b/pkg/controllers/internal/sops/keyservice/testdata/public.gpg @@ -0,0 +1,41 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQGNBGJGqrsBDAC7OxFP6Z2E+AkVZpQySjLFAeYJWdnadx0GOHnckOOFkQvVJauz +9KibgzLUkO9h0oIoP7dLyPEiRPhKgmbrktyCDfysvNeKCgI5XemJCJqCmwA/vWwp +GnVltcgsVVjVZ3vvD8VMfhKF77pkmMDj7mnCPw9x39R8SVpe2K9RO0QLk/Dt+o8t +MO+sXTz4ba4aMdjJvMoaoQKw6RXAouZa4H09i6tiAgXrRLxQDxJ58sGg/ZCWa5G4 +aI6PdObY41fzQlcobtifCbktbICVb1Ms1s0iZWttFmr0oTSkJTv3FPWhf6n126w4 +LEkF9d6YW+/0H9cqXa4GMfxXg4XBmJNJfYkLDVUlbp3xi+I+Lg1Sit6QlqkW93EW +etpYPK1KmDcW3IA6ausYnkyrcQbt1m5/hh9KJoQb6He/RytXEBxp90+v9y7THZGr +2U49ZEHQg6DAIj4j1p9NAGgqjKr9am6yk2pvpK3ZWmHQ6CZfCiBrEPCvdmEhrx4U +lj6wyd00YJknpDkAEQEAAbQVRmx1eCA8c29wc0BmbHV4Y2QuaW8+iQHOBBMBCAA4 +FiEEtZ2vRp6MlIE4kBpklzIHXqIhp+oFAmJGqrsCGwMFCwkIBwIGFQoJCAsCBBYC +AwECHgECF4AACgkQlzIHXqIhp+qaagwAmpAZLDnyPcZHFdieKCpBJQz8zzFZVnrr +pXoB20vEerKxCQ8XUeI1KRVSJQ6fPMcDLq6QGZMCXcc1i1o/DRrluWdLN1rjNYde +wWBP2ShnOKQX+/wNjsw4UvCK6jbhO5DX7qT4KK1cG7jbOQvIaMOjD+8qq8VTpFkX +M9ZcdvRSSHrPiOOj/h/+awL/uH4hli4998B4R6cU6OCVlYmMpfEt2ijdMq6Tas1P +2/3EKBMnbDZLPHLhwYeEDIx/tbId7NHq8tmeVTwQXHMRnveEqb24dX5IXtiyGSnX +64eO1lTyVclGlCQvycwnCDQjaRo6TsQCDPDcX1CBdpPkpYERDU0M7VwJx2Fh3E0j +/KI2zfLzg5GuizIBLTqNFgfx+/zbwQ7nNPYsz9R8HTTNnU5D7qZW5hHYPTZBYKfh +/EIAMRxllTdtoM2GsSVTvTxa6Rdg/N2XIeMwGLf8GBFgv0ivQHHW3r1XXrRTwgle +XUiQlqvnvZZZyWsUeGw2039xEFu2rpYNuQGNBGJGqrsBDADEFbJxDngVdYl+oQoj +FsII58kvoICGTdhdeTYDO+lTC2DMjre2N55AQBQmYBAPSMpqwlIj0pPmMw7Xm1oK +a6gIDYKqL0pdnNcbmnI7h4KRuw0IldtLG++dgTDAwxFGNOWMkGuTZglKiSAdhdJs +bSKm1KT4HRy0onYEV/+VY7oCkpZA/ialESKVupZgGieUYG1m1z7rsvMhbB7Xe+Y6 +eX0BQ0jMUht9GQUAJMvuL8PM08Wzi3V0txa2a+mLy35gDGn6BmmSgChDsQGQqded +icVJusr4FxiJYAwzyx8rebaVsIvfL8Ot9zVRNzJCsInNB7khxD13psGSYt8+Mmgb +KH7+xwqPSrRseCPrf3KhJPKq4t3T6rBJIlinzdxXcyp3YHfPeBrRZTttsvf/KXI6 +XhtxK06uK05qN5ZC58WxsYBDQ6DxHCfEP8bvmQHY6rOZEZ4r0Oe0JNCgkVPB68wQ +wVrNd0mnPKm4Efh78qtvVECTkKNKYSlTn2+qKspvWbAfKu8AEQEAAYkBtgQYAQgA +IBYhBLWdr0aejJSBOJAaZJcyB16iIafqBQJiRqq7AhsMAAoJEJcyB16iIafqNJUM +AJiRlauFf9MzpsFUIa+HOXcSbAeoy4xP9DE3lJzlBVrBEPfr4v/K4VjMr4wr2z2n +ln/1WZOw78pDFxo+K5i5vm+wnfwDGxpKI8RUK1djVAfRHcBgyHu0IZb6qHJ/whnL +KeSI4hmajw92tLBGcEJYY+8diokSvtzkskPRFhkXBH66M+GvmZq8RFXOIXYR9Siv +feVrm+lrH4IP5ct4xBpaisZ6AU3rASmgahDWs/XTG0z63FTv/DR+RgdoTO9B+dE3 +hBU2D8SBDHtT4fKSUAzfq0ZiCPYJEXmqb4paG/6KnKZ+YCeCuM0ypwzXm5eLUwXd +QYUbtCRspQGql4r1R8biWdkodxLCF99yAvpyXPWYZ+uIIQm/D0J++UubtEMzab8x +HEWvbI1CCkgkWheANUK2EEK4fV0gy+GAJgB+sghjNMrZkosmVO/c7xIJN/VmGOu7 +Z/fPzlgCo5eZBQZgqtBkvZP00TaZCdcvSf6yBL5fBPaH590IEkcliZujFenELTbE +dg== +=05GI +-----END PGP PUBLIC KEY BLOCK----- diff --git a/pkg/controllers/internal/sops/keyservice/utils_test.go b/pkg/controllers/internal/sops/keyservice/utils_test.go new file mode 100644 index 000000000..097dc5fbd --- /dev/null +++ b/pkg/controllers/internal/sops/keyservice/utils_test.go @@ -0,0 +1,105 @@ +// Copyright (C) 2022 The Flux authors +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package keyservice + +import ( + "context" + "fmt" + "go.mozilla.org/sops/v3/kms" + + "go.mozilla.org/sops/v3/keys" + "go.mozilla.org/sops/v3/keyservice" + + "go.mozilla.org/sops/v3/age" + "go.mozilla.org/sops/v3/azkv" + "go.mozilla.org/sops/v3/gcpkms" + "go.mozilla.org/sops/v3/hcvault" + "go.mozilla.org/sops/v3/pgp" +) + +// KeyFromMasterKey converts a SOPS internal MasterKey to an RPC Key that can +// be serialized with Protocol Buffers. +func KeyFromMasterKey(k keys.MasterKey) keyservice.Key { + switch mk := k.(type) { + case *pgp.MasterKey: + return keyservice.Key{ + KeyType: &keyservice.Key_PgpKey{ + PgpKey: &keyservice.PgpKey{ + Fingerprint: mk.Fingerprint, + }, + }, + } + case *hcvault.MasterKey: + return keyservice.Key{ + KeyType: &keyservice.Key_VaultKey{ + VaultKey: &keyservice.VaultKey{ + VaultAddress: mk.VaultAddress, + EnginePath: mk.EnginePath, + KeyName: mk.KeyName, + }, + }, + } + case *kms.MasterKey: + return keyservice.Key{ + KeyType: &keyservice.Key_KmsKey{ + KmsKey: &keyservice.KmsKey{ + Arn: mk.Arn, + }, + }, + } + case *azkv.MasterKey: + return keyservice.Key{ + KeyType: &keyservice.Key_AzureKeyvaultKey{ + AzureKeyvaultKey: &keyservice.AzureKeyVaultKey{ + VaultUrl: mk.VaultURL, + Name: mk.Name, + Version: mk.Version, + }, + }, + } + case *age.MasterKey: + return keyservice.Key{ + KeyType: &keyservice.Key_AgeKey{ + AgeKey: &keyservice.AgeKey{ + Recipient: mk.Recipient, + }, + }, + } + case *gcpkms.MasterKey: + return keyservice.Key{ + KeyType: &keyservice.Key_GcpKmsKey{ + GcpKmsKey: &keyservice.GcpKmsKey{ + ResourceId: mk.ResourceID, + }, + }, + } + default: + panic(fmt.Sprintf("tried to convert unknown MasterKey type %T to keyservice.Key", mk)) + } +} + +type MockKeyServer struct { + encryptReqs []*keyservice.EncryptRequest + decryptReqs []*keyservice.DecryptRequest +} + +func NewMockKeyServer() *MockKeyServer { + return &MockKeyServer{ + encryptReqs: make([]*keyservice.EncryptRequest, 0), + decryptReqs: make([]*keyservice.DecryptRequest, 0), + } +} + +func (ks *MockKeyServer) Encrypt(_ context.Context, req *keyservice.EncryptRequest) (*keyservice.EncryptResponse, error) { + ks.encryptReqs = append(ks.encryptReqs, req) + return nil, fmt.Errorf("not actually implemented") +} + +func (ks *MockKeyServer) Decrypt(_ context.Context, req *keyservice.DecryptRequest) (*keyservice.DecryptResponse, error) { + ks.decryptReqs = append(ks.decryptReqs, req) + return nil, fmt.Errorf("not actually implemented") +} diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go new file mode 100644 index 000000000..41396bd18 --- /dev/null +++ b/pkg/controllers/kluctl_project.go @@ -0,0 +1,820 @@ +package controllers + +import ( + "context" + "fmt" + internal_metrics "github.com/kluctl/kluctl/v2/pkg/controllers/internal/metrics" + "github.com/kluctl/kluctl/v2/pkg/controllers/internal/sops" + "github.com/kluctl/kluctl/v2/pkg/helm" + "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" + "github.com/kluctl/kluctl/v2/pkg/repocache" + "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/prometheus/client_golang/prometheus" + "helm.sh/helm/v3/pkg/repo" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "os" + "path/filepath" + "strings" + "time" + + securejoin "github.com/cyphar/filepath-securejoin" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + "github.com/kluctl/kluctl/v2/pkg/deployment" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + k8s2 "github.com/kluctl/kluctl/v2/pkg/k8s" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/utils" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/clientcmd/api" + ctrl "sigs.k8s.io/controller-runtime" +) + +type preparedProject struct { + r *KluctlDeploymentReconciler + obj *kluctlv1.KluctlDeployment + + startTime time.Time + + rp *repocache.GitRepoCache + + tmpDir string + repoDir string + projectDir string +} + +type preparedTarget struct { + pp *preparedProject +} + +func prepareProject(ctx context.Context, + r *KluctlDeploymentReconciler, + obj *kluctlv1.KluctlDeployment, + doCloneSource bool) (*preparedProject, error) { + + pp := &preparedProject{ + r: r, + obj: obj, + startTime: time.Now(), + } + + // create tmp dir + tmpDir, err := os.MkdirTemp("", obj.GetName()+"-") + if err != nil { + return pp, fmt.Errorf("failed to create temp dir for kluctl project: %w", err) + } + cleanup := true + defer func() { + if !cleanup { + return + } + pp.cleanup() + }() + + pp.tmpDir = tmpDir + + gitSecret, err := r.getGitSecret(ctx, &pp.obj.Spec.Source, obj.GetNamespace()) + if err != nil { + return nil, err + } + + pp.rp, err = r.buildRepoCache(ctx, gitSecret) + if err != nil { + return nil, err + } + + if doCloneSource { + rpEntry, err := pp.rp.GetEntry(pp.obj.Spec.Source.URL) + if err != nil { + return nil, fmt.Errorf("failed clone source: %w", err) + } + + clonedDir, _, err := rpEntry.GetClonedDir(pp.obj.Spec.Source.Ref.String()) + if err != nil { + return nil, err + } + + pp.repoDir = clonedDir + + // check kluctl project path exists + pp.projectDir, err = securejoin.SecureJoin(pp.repoDir, pp.obj.Spec.Source.Path) + if err != nil { + return pp, err + } + if _, err := os.Stat(pp.projectDir); err != nil { + return pp, fmt.Errorf("kluctlDeployment path not found: %w", err) + } + } + + cleanup = false + return pp, nil +} + +func (pp *preparedProject) cleanup() { + _ = os.RemoveAll(pp.tmpDir) + if pp.rp != nil { + pp.rp.Clear() + pp.rp = nil + } +} + +func (pp *preparedProject) newTarget() (*preparedTarget, error) { + pt := preparedTarget{ + pp: pp, + } + + return &pt, nil +} + +func (pt *preparedTarget) restConfigToKubeconfig(restConfig *rest.Config) *api.Config { + kubeConfig := api.NewConfig() + cluster := api.NewCluster() + cluster.Server = restConfig.Host + cluster.CertificateAuthority = restConfig.TLSClientConfig.CAFile + cluster.CertificateAuthorityData = restConfig.TLSClientConfig.CAData + cluster.InsecureSkipTLSVerify = restConfig.TLSClientConfig.Insecure + kubeConfig.Clusters["default"] = cluster + + user := api.NewAuthInfo() + user.ClientKey = restConfig.KeyFile + user.ClientKeyData = restConfig.KeyData + user.ClientCertificate = restConfig.CertFile + user.ClientCertificateData = restConfig.CertData + user.TokenFile = restConfig.BearerTokenFile + user.Token = restConfig.BearerToken + user.Impersonate = restConfig.Impersonate.UserName + user.ImpersonateUID = restConfig.Impersonate.UID + user.ImpersonateUserExtra = restConfig.Impersonate.Extra + user.ImpersonateGroups = restConfig.Impersonate.Groups + user.Username = restConfig.Username + user.Password = restConfig.Password + user.AuthProvider = restConfig.AuthProvider + user.Exec = restConfig.ExecProvider + kubeConfig.AuthInfos["default"] = user + + kctx := api.NewContext() + kctx.Cluster = "default" + kctx.AuthInfo = "default" + kubeConfig.Contexts["default"] = kctx + kubeConfig.CurrentContext = "default" + + return kubeConfig +} + +func (pt *preparedTarget) getKubeconfigFromSecret(ctx context.Context) ([]byte, error) { + secretName := types.NamespacedName{ + Namespace: pt.pp.obj.GetNamespace(), + Name: pt.pp.obj.Spec.KubeConfig.SecretRef.Name, + } + + var secret corev1.Secret + if err := pt.pp.r.Get(ctx, secretName, &secret); err != nil { + return nil, fmt.Errorf("unable to read KubeConfig secret '%s' error: %w", secretName.String(), err) + } + + var kubeConfig []byte + switch { + case pt.pp.obj.Spec.KubeConfig.SecretRef.Key != "": + key := pt.pp.obj.Spec.KubeConfig.SecretRef.Key + kubeConfig = secret.Data[key] + if kubeConfig == nil { + return nil, fmt.Errorf("KubeConfig secret '%s' does not contain a '%s' key with a kubeconfig", secretName, key) + } + case secret.Data["value"] != nil: + kubeConfig = secret.Data["value"] + case secret.Data["value.yaml"] != nil: + kubeConfig = secret.Data["value.yaml"] + default: + // User did not specify a key, and the 'value' key was not defined. + return nil, fmt.Errorf("KubeConfig secret '%s' does not contain a 'value' key with a kubeconfig", secretName) + } + + return kubeConfig, nil +} + +func (pt *preparedTarget) setImpersonationConfig(restConfig *rest.Config) { + name := pt.pp.r.DefaultServiceAccount + if sa := pt.pp.obj.Spec.ServiceAccountName; sa != "" { + name = sa + } + if name != "" { + username := fmt.Sprintf("system:serviceaccount:%s:%s", pt.pp.obj.GetNamespace(), name) + restConfig.Impersonate = rest.ImpersonationConfig{UserName: username} + } +} + +func (pt *preparedTarget) buildRestConfig(ctx context.Context) (*rest.Config, error) { + var restConfig *rest.Config + + if pt.pp.obj.Spec.KubeConfig != nil { + kubeConfig, err := pt.getKubeconfigFromSecret(ctx) + if err != nil { + return nil, err + } + restConfig, err = clientcmd.RESTConfigFromKubeConfig(kubeConfig) + if err != nil { + return nil, err + } + } else { + restConfig = rest.CopyConfig(pt.pp.r.RestConfig) + } + + pt.setImpersonationConfig(restConfig) + + return restConfig, nil +} + +func (pt *preparedTarget) buildKubeconfig(ctx context.Context) (*api.Config, error) { + restConfig, err := pt.buildRestConfig(ctx) + if err != nil { + return nil, err + } + + kubeConfig := pt.restConfigToKubeconfig(restConfig) + return kubeConfig, nil +} + +/* +func (pt *preparedTarget) getRegistrySecrets(ctx context.Context) ([]*corev1.Secret, error) { + var ret []*corev1.Secret + for _, ref := range pt.pp.obj.Spec.RegistrySecrets { + name := types.NamespacedName{ + Namespace: pt.pp.obj.GetNamespace(), + Name: ref.Name, + } + var secret corev1.Secret + if err := pt.pp.r.Get(ctx, name, &secret); err != nil { + return nil, fmt.Errorf("failed to get secret '%s': %w", name.String(), err) + } + ret = append(ret, &secret) + } + return ret, nil +} + +func (pt *preparedTarget) buildRegistryHelper(ctx context.Context) (*registries.RegistryHelper, error) { + secrets, err := pt.getRegistrySecrets(ctx) + if err != nil { + return nil, err + } + + rh := registries.NewRegistryHelper(ctx) + err = rh.ParseAuthEntriesFromEnv() + if err != nil { + return nil, err + } + + for _, s := range secrets { + caFile := s.Data["caFile"] + insecure := false + if x, ok := s.Data["insecure"]; ok { + insecure, err = strconv.ParseBool(string(x)) + if err != nil { + return nil, fmt.Errorf("failed parsing insecure flag from secret %s: %w", s.Name, err) + } + } + + if dockerConfig, ok := s.Data[".dockerconfigjson"]; ok { + maxFields := 1 + if _, ok := s.Data["caFile"]; ok { + maxFields++ + } + if _, ok := s.Data["insecure"]; ok { + maxFields++ + } + if len(s.Data) != maxFields { + return nil, fmt.Errorf("when using .dockerconfigjson in registry secret, only caFile and insecure fields are allowed additionally") + } + + c := configfile.New(".dockerconfigjson") + err = c.LoadFromReader(bytes.NewReader(dockerConfig)) + if err != nil { + return nil, fmt.Errorf("failed to parse .dockerconfigjson from secret %s: %w", s.Name, err) + } + for registry, ac := range c.GetAuthConfigs() { + var e registries.AuthEntry + e.Registry = registry + e.Username = ac.Username + e.Password = ac.Password + e.Auth = ac.Auth + e.CABundle = caFile + e.Insecure = insecure + + rh.AddAuthEntry(e) + } + } else { + var e registries.AuthEntry + e.Registry = string(s.Data["registry"]) + e.Username = string(s.Data["username"]) + e.Password = string(s.Data["password"]) + e.Auth = string(s.Data["auth"]) + e.CABundle = caFile + e.Insecure = insecure + + if e.Registry == "" || (e.Username == "" && e.Auth == "") { + return nil, fmt.Errorf("registry secret is incomplete") + } + rh.AddAuthEntry(e) + } + } + return rh, nil +} +*/ + +func (pt *preparedTarget) buildImages(ctx context.Context) (*deployment.Images, error) { + /*rh, err := pt.buildRegistryHelper(ctx) + if err != nil { + return nil, err + } + offline := !pt.pp.obj.Spec.UpdateImages + images, err := deployment.NewImages(rh, pt.pp.obj.Spec.UpdateImages, offline)*/ + + images, err := deployment.NewImages() + if err != nil { + return nil, err + } + for _, fi := range pt.pp.obj.Spec.Images { + images.AddFixedImage(fi) + } + return images, nil +} + +type helmCredentialsProvider []repo.Entry + +func (p helmCredentialsProvider) FindCredentials(repoUrl string, credentialsId *string) *repo.Entry { + if credentialsId != nil { + for _, e := range p { + if e.Name != "" && e.Name == *credentialsId { + return &e + } + } + } + if repoUrl == "" { + return nil + } + for _, e := range p { + if e.URL == repoUrl { + return &e + } + } + return nil +} + +func (pt *preparedTarget) buildHelmCredentials(ctx context.Context) (helm.HelmCredentialsProvider, error) { + var creds []repo.Entry + + tmpDirBase := filepath.Join(pt.pp.tmpDir, "helm-certs") + _ = os.MkdirAll(tmpDirBase, 0o700) + + var writeTmpFilErr error + writeTmpFile := func(secretData map[string][]byte, name string) string { + b, ok := secretData["certFile"] + if ok { + tmpFile, err := os.CreateTemp(tmpDirBase, name+"-") + if err != nil { + writeTmpFilErr = err + return "" + } + defer tmpFile.Close() + _, err = tmpFile.Write(b) + if err != nil { + writeTmpFilErr = err + } + return tmpFile.Name() + } + return "" + } + + for _, e := range pt.pp.obj.Spec.HelmCredentials { + var secret corev1.Secret + err := pt.pp.r.Client.Get(ctx, types.NamespacedName{ + Namespace: pt.pp.obj.GetNamespace(), + Name: e.SecretRef.Name, + }, &secret) + if err != nil { + return nil, err + } + + var entry repo.Entry + + credentialsId := string(secret.Data["credentialsId"]) + url := string(secret.Data["url"]) + if credentialsId == "" && url == "" { + return nil, fmt.Errorf("secret %s must at least contain 'credentialsId' or 'url'", e.SecretRef.Name) + } + entry.Name = credentialsId + entry.URL = url + entry.Username = string(secret.Data["username"]) + entry.Password = string(secret.Data["password"]) + entry.CertFile = writeTmpFile(secret.Data, "certFile") + entry.KeyFile = writeTmpFile(secret.Data, "keyFile") + entry.CAFile = writeTmpFile(secret.Data, "caFile") + if writeTmpFilErr != nil { + return nil, writeTmpFilErr + } + + b, _ := secret.Data["insecureSkipTlsVerify"] + s := string(b) + if utils.ParseBoolOrFalse(&s) { + entry.InsecureSkipTLSverify = true + } + b, _ = secret.Data["passCredentialsAll"] + s = string(b) + if utils.ParseBoolOrFalse(&s) { + entry.PassCredentialsAll = true + } + creds = append(creds, entry) + } + + return helmCredentialsProvider(creds), nil +} + +func (pt *preparedTarget) buildInclusion() *utils.Inclusion { + inc := utils.NewInclusion() + for _, x := range pt.pp.obj.Spec.IncludeTags { + inc.AddInclude("tag", x) + } + for _, x := range pt.pp.obj.Spec.ExcludeTags { + inc.AddExclude("tag", x) + } + for _, x := range pt.pp.obj.Spec.IncludeDeploymentDirs { + inc.AddInclude("deploymentItemDir", x) + } + for _, x := range pt.pp.obj.Spec.ExcludeDeploymentDirs { + inc.AddExclude("deploymentItemDir", x) + } + return inc +} + +func (pt *preparedTarget) clientConfigGetter(ctx context.Context) func(context *string) (*rest.Config, *api.Config, error) { + return func(context *string) (*rest.Config, *api.Config, error) { + kubeConfig, err := pt.buildKubeconfig(ctx) + if err != nil { + return nil, nil, err + } + + configOverrides := &clientcmd.ConfigOverrides{} + if context != nil { + configOverrides.CurrentContext = *context + } + clientConfig := clientcmd.NewDefaultClientConfig(*kubeConfig, configOverrides) + rawConfig, err := clientConfig.RawConfig() + if err != nil { + return nil, nil, err + } + if context != nil { + rawConfig.CurrentContext = *context + } + restConfig, err := clientConfig.ClientConfig() + if err != nil { + return nil, nil, err + } + return restConfig, &rawConfig, nil + } +} + +func (pp *preparedProject) buildSopsDecrypter(ctx context.Context) (*decryptor.Decryptor, error) { + if pp.obj.Spec.Decryption == nil { + return nil, nil + } + if pp.obj.Spec.Decryption.Provider != "sops" { + return nil, fmt.Errorf("not supported decryption provider %s", pp.obj.Spec.Decryption.Provider) + } + + d := decryptor.NewDecryptor(filepath.Join(pp.tmpDir, "project"), decryptor.MaxEncryptedFileSize) + + err := pp.addSecretBasedKeyServers(ctx, d) + if err != nil { + return nil, err + } + err = pp.addServiceAccountBasedKeyServers(ctx, d) + if err != nil { + return nil, err + } + return d, nil +} + +func (pp *preparedProject) addSecretBasedKeyServers(ctx context.Context, d *decryptor.Decryptor) error { + if pp.obj.Spec.Decryption.SecretRef == nil { + return nil + } + + secretName := types.NamespacedName{ + Namespace: pp.obj.GetNamespace(), + Name: pp.obj.Spec.Decryption.SecretRef.Name, + } + + var secret corev1.Secret + if err := pp.r.Get(ctx, secretName, &secret); err != nil { + if apierrors.IsNotFound(err) { + return err + } + return fmt.Errorf("cannot get decryption Secret '%s': %w", secretName, err) + } + + gnuPGHome := filepath.Join(pp.tmpDir, "sops-gnupghome") + err := os.MkdirAll(gnuPGHome, 0o700) + if err != nil { + return err + } + + ks, err := sops.BuildSopsKeyServerFromSecret(&secret, gnuPGHome) + if err != nil { + return err + } + if ks != nil { + d.AddKeyServiceClient(ks) + } + return nil +} + +func (pp *preparedProject) addServiceAccountBasedKeyServers(ctx context.Context, d *decryptor.Decryptor) error { + name := pp.r.DefaultServiceAccount + if pp.obj.Spec.Decryption != nil && pp.obj.Spec.Decryption.ServiceAccount != "" { + name = pp.obj.Spec.Decryption.ServiceAccount + } else if sa := pp.obj.Spec.ServiceAccountName; sa != "" { + name = sa + } + if name == "" { + return nil + } + sa, err := pp.r.ClientSet.CoreV1().ServiceAccounts(pp.obj.Namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("failed to retrieve service account %s: %w", name, err) + } + + ks, err := sops.BuildSopsKeyServerFromServiceAccount(ctx, pp.r.Client, sa) + if err != nil { + return err + } + if ks != nil { + d.AddKeyServiceClient(ks) + } + return nil +} + +func (pp *preparedProject) withKluctlProject(ctx context.Context, pt *preparedTarget, cb func(p *kluctl_project.LoadedKluctlProject) error) error { + j2, err := kluctl_jinja2.NewKluctlJinja2(true) + if err != nil { + return err + } + defer j2.Close() + + var sopsDecrypter *decryptor.Decryptor + if pp.obj.Spec.Decryption != nil { + sopsDecrypter, err = pp.buildSopsDecrypter(ctx) + if err != nil { + return err + } + } + + externalArgs, err := uo.FromString(string(pt.pp.obj.Spec.Args.Raw)) + if err != nil { + return err + } + + loadArgs := kluctl_project.LoadKluctlProjectArgs{ + RepoRoot: pp.repoDir, + ExternalArgs: externalArgs, + ProjectDir: pp.projectDir, + RP: pp.rp, + SopsDecrypter: sopsDecrypter, + } + if pt != nil { + loadArgs.ClientConfigGetter = pt.clientConfigGetter(ctx) + } + + p, err := kluctl_project.LoadKluctlProject(ctx, loadArgs, filepath.Join(pp.tmpDir, "project"), j2) + if err != nil { + return err + } + + return cb(p) +} + +func (pt *preparedTarget) withKluctlProjectTarget(ctx context.Context, cb func(targetContext *kluctl_project.TargetContext) error) error { + return pt.pp.withKluctlProject(ctx, pt, func(p *kluctl_project.LoadedKluctlProject) error { + renderOutputDir, err := os.MkdirTemp(pt.pp.tmpDir, "render-") + if err != nil { + return err + } + images, err := pt.buildImages(ctx) + if err != nil { + return err + } + helmCredentials, err := pt.buildHelmCredentials(ctx) + if err != nil { + return err + } + inclusion := pt.buildInclusion() + + props := kluctl_project.TargetContextParams{ + DryRun: pt.pp.r.DryRun || pt.pp.obj.Spec.DryRun, + Images: images, + Inclusion: inclusion, + HelmCredentials: helmCredentials, + RenderOutputDir: renderOutputDir, + } + if pt.pp.obj.Spec.Target != nil { + props.TargetName = *pt.pp.obj.Spec.Target + } + if pt.pp.obj.Spec.TargetNameOverride != nil { + props.TargetNameOverride = *pt.pp.obj.Spec.TargetNameOverride + } + if pt.pp.obj.Spec.Context != nil { + props.ContextOverride = *pt.pp.obj.Spec.Context + } + targetContext, err := p.NewTargetContext(ctx, props) + if err != nil { + return err + } + err = targetContext.DeploymentCollection.Prepare() + if err != nil { + return err + } + return cb(targetContext) + }) +} + +func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, cmdResult *result.CommandResult, commandName string) error { + log := ctrl.LoggerFrom(ctx) + + cmdResult.Command.Initiator = result.CommandInititiator_KluctlDeployment + cmdResult.GitInfo.Url = &pt.pp.obj.Spec.Source.URL + cmdResult.GitInfo.Ref = pt.pp.obj.Spec.Source.Ref.String() + cmdResult.ProjectKey.NormalizedGitUrl = pt.pp.obj.Spec.Source.URL.NormalizedRepoKey() + + summary := cmdResult.BuildSummary() + + log.Info(fmt.Sprintf("command finished with err=%v", cmdErr)) + defer pt.exportCommandResultMetricsToProm(summary) + if cmdErr != nil { + pt.pp.r.event(ctx, pt.pp.obj, true, fmt.Sprintf("%s failed. %s", commandName, cmdErr.Error()), nil) + return cmdErr + } + + msg := fmt.Sprintf("%s succeeded.", commandName) + if summary.NewObjects != 0 { + msg += fmt.Sprintf(" %d new objects.", summary.NewObjects) + } + if summary.ChangedObjects != 0 { + msg += fmt.Sprintf(" %d changed objects.", summary.ChangedObjects) + } + if summary.AppliedHookObjects != 0 { + msg += fmt.Sprintf(" %d hooks run.", summary.AppliedHookObjects) + } + if summary.DeletedObjects != 0 { + msg += fmt.Sprintf(" %d deleted objects.", summary.DeletedObjects) + } + if summary.OrphanObjects != 0 { + msg += fmt.Sprintf(" %d orphan objects.", summary.OrphanObjects) + } + if len(cmdResult.Errors) != 0 { + msg += fmt.Sprintf(" %d errors.", len(cmdResult.Errors)) + } + if len(cmdResult.Warnings) != 0 { + msg += fmt.Sprintf(" %d warnings.", len(cmdResult.Warnings)) + } + + warning := false + var err error + if len(cmdResult.Errors) != 0 { + warning = true + err = fmt.Errorf("%s failed with %d errors", commandName, len(cmdResult.Errors)) + } + pt.pp.r.event(ctx, pt.pp.obj, warning, msg, nil) + + return err +} + +func (pt *preparedTarget) kluctlDeploy(ctx context.Context, targetContext *kluctl_project.TargetContext) (*result.CommandResult, error) { + timer := prometheus.NewTimer(internal_metrics.NewKluctlDeploymentDuration(pt.pp.obj.ObjectMeta.Namespace, pt.pp.obj.ObjectMeta.Name, pt.pp.obj.Spec.DeployMode)) + defer timer.ObserveDuration() + cmd := commands.NewDeployCommand(targetContext) + cmd.ForceApply = pt.pp.obj.Spec.ForceApply + cmd.ReplaceOnError = pt.pp.obj.Spec.ReplaceOnError + cmd.ForceReplaceOnError = pt.pp.obj.Spec.ForceReplaceOnError + cmd.AbortOnError = pt.pp.obj.Spec.AbortOnError + cmd.ReadinessTimeout = time.Minute * 10 + cmd.NoWait = pt.pp.obj.Spec.NoWait + + cmdResult, err := cmd.Run(nil) + err = pt.handleCommandResult(ctx, err, cmdResult, "deploy") + return cmdResult, err +} + +func (pt *preparedTarget) kluctlPokeImages(ctx context.Context, targetContext *kluctl_project.TargetContext) (*result.CommandResult, error) { + timer := prometheus.NewTimer(internal_metrics.NewKluctlDeploymentDuration(pt.pp.obj.ObjectMeta.Namespace, pt.pp.obj.ObjectMeta.Name, pt.pp.obj.Spec.DeployMode)) + defer timer.ObserveDuration() + cmd := commands.NewPokeImagesCommand(targetContext) + + cmdResult, err := cmd.Run() + err = pt.handleCommandResult(ctx, err, cmdResult, "poke-images") + return cmdResult, err +} + +func (pt *preparedTarget) kluctlPrune(ctx context.Context, targetContext *kluctl_project.TargetContext) (*result.CommandResult, error) { + if !pt.pp.obj.Spec.Prune { + return nil, nil + } + + timer := prometheus.NewTimer(internal_metrics.NewKluctlPruneDuration(pt.pp.obj.ObjectMeta.Namespace, pt.pp.obj.ObjectMeta.Name)) + defer timer.ObserveDuration() + + cmd := commands.NewPruneCommand("", targetContext) + cmdResult, err := cmd.Run(func(refs []k8s.ObjectRef) error { + pt.printDeletedRefs(ctx, refs) + return nil + }) + if err != nil { + return nil, err + } + err = pt.handleCommandResult(ctx, err, cmdResult, "prune") + return cmdResult, err +} + +func (pt *preparedTarget) kluctlValidate(ctx context.Context, targetContext *kluctl_project.TargetContext, cmdResult *result.CommandResult) (*result.ValidateResult, error) { + timer := prometheus.NewTimer(internal_metrics.NewKluctlValidateDuration(pt.pp.obj.ObjectMeta.Namespace, pt.pp.obj.ObjectMeta.Name)) + defer timer.ObserveDuration() + + c := targetContext.DeploymentCollection + if cmdResult != nil { + c = nil + } + cmd := commands.NewValidateCommand(ctx, targetContext.Target.Discriminator, c, cmdResult) + + validateResult, err := cmd.Run(ctx, targetContext.SharedContext.K) + return validateResult, err +} + +func (pt *preparedTarget) kluctlDelete(ctx context.Context, discriminator string) (*result.CommandResult, error) { + if !pt.pp.obj.Spec.Delete { + return nil, nil + } + + timer := prometheus.NewTimer(internal_metrics.NewKluctlDeleteDuration(pt.pp.obj.ObjectMeta.Namespace, pt.pp.obj.ObjectMeta.Name)) + defer timer.ObserveDuration() + + inclusion := pt.buildInclusion() + + cmd := commands.NewDeleteCommand(discriminator, nil, inclusion) + + restConfig, err := pt.buildRestConfig(ctx) + if err != nil { + return nil, err + } + clientFactory, err := k8s2.NewClientFactory(ctx, restConfig) + if err != nil { + return nil, err + } + k, err := k8s2.NewK8sCluster(ctx, clientFactory, pt.pp.r.DryRun || pt.pp.obj.Spec.DryRun) + if err != nil { + return nil, err + } + + cmdResult, err := cmd.Run(ctx, k, func(refs []k8s.ObjectRef) error { + pt.printDeletedRefs(ctx, refs) + return nil + }) + if err != nil { + return nil, err + } + + if pt.pp.obj.Spec.Target != nil { + cmdResult.TargetKey.TargetName = *pt.pp.obj.Spec.Target + } + if pt.pp.obj.Spec.TargetNameOverride != nil { + cmdResult.TargetKey.TargetName = *pt.pp.obj.Spec.TargetNameOverride + } + + cmdResult.TargetKey.Discriminator = discriminator + cmdResult.TargetKey.ClusterId = cmdResult.ClusterInfo.ClusterId + + err = pt.handleCommandResult(ctx, err, cmdResult, "delete") + return cmdResult, err +} + +func (pt *preparedTarget) printDeletedRefs(ctx context.Context, refs []k8s.ObjectRef) { + log := ctrl.LoggerFrom(ctx) + + var refStrs []string + for _, ref := range refs { + refStrs = append(refStrs, ref.String()) + } + if len(refStrs) != 0 { + log.Info(fmt.Sprintf("deleting (without waiting): %s", strings.Join(refStrs, ", "))) + } +} + +func (pt *preparedTarget) exportCommandResultMetricsToProm(summary *result.CommandResultSummary) { + internal_metrics.NewKluctlNumberOfDeletedObjects(pt.pp.obj.Namespace, pt.pp.obj.Name).Set(float64(summary.DeletedObjects)) + internal_metrics.NewKluctlNumberOfChangedObjects(pt.pp.obj.Namespace, pt.pp.obj.Name).Set(float64(summary.ChangedObjects)) + internal_metrics.NewKluctlNumberOfOrphanObjects(pt.pp.obj.Namespace, pt.pp.obj.Name).Set(float64(summary.OrphanObjects)) + internal_metrics.NewKluctlNumberOfWarnings(pt.pp.obj.Namespace, pt.pp.obj.Name, summary.Command.Command).Set(float64(summary.Warnings)) + internal_metrics.NewKluctlNumberOfErrors(pt.pp.obj.Namespace, pt.pp.obj.Name, summary.Command.Command).Set(float64(summary.Errors)) +} diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go new file mode 100644 index 000000000..69670165a --- /dev/null +++ b/pkg/controllers/kluctldeployment_controller.go @@ -0,0 +1,506 @@ +package controllers + +import ( + "context" + "fmt" + "github.com/hashicorp/go-retryablehttp" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + internal_metrics "github.com/kluctl/kluctl/v2/pkg/controllers/internal/metrics" + ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" + "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + kuberecorder "k8s.io/client-go/tools/record" + "k8s.io/client-go/tools/reference" + "path" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "time" +) + +type KluctlDeploymentReconciler struct { + client.Client + RestConfig *rest.Config + ClientSet *kubernetes.Clientset + httpClient *retryablehttp.Client + requeueDependency time.Duration + Scheme *runtime.Scheme + EventRecorder kuberecorder.EventRecorder + MetricsRecorder *metrics.Recorder + ControllerName string + DefaultServiceAccount string + DryRun bool + + SshPool *ssh_pool.SshPool +} + +// KluctlDeploymentReconcilerOpts contains options for the BaseReconciler. +type KluctlDeploymentReconcilerOpts struct { + HTTPRetry int +} + +// +kubebuilder:rbac:groups=gitops.kluctl.io,resources=kluctldeployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=gitops.kluctl.io,resources=kluctldeployments/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=gitops.kluctl.io,resources=kluctldeployments/finalizers,verbs=get;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=configmaps;secrets;serviceaccounts,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch + +func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := ctrl.LoggerFrom(ctx) + reconcileStart := time.Now() + + ctx = status.NewContext(ctx, status.NewSimpleStatusHandler(func(message string) { + log.Info(message) + }, false, false)) + + obj := &kluctlv1.KluctlDeployment{} + if err := r.Get(ctx, req.NamespacedName, obj); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, r.calcTimeout(obj)) + defer cancel() + + retryInterval := obj.Spec.GetRetryInterval() + interval := obj.Spec.Interval.Duration + + // Record suspended status metric + defer r.recordSuspension(ctx, obj) + + // Add our finalizer if it does not exist + if !controllerutil.ContainsFinalizer(obj, kluctlv1.KluctlDeploymentFinalizer) { + patch := client.MergeFrom(obj.DeepCopy()) + controllerutil.AddFinalizer(obj, kluctlv1.KluctlDeploymentFinalizer) + if err := r.Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { + log.Error(err, "unable to register finalizer") + return ctrl.Result{}, err + } + } + + // Examine if the object is under deletion + if !obj.GetDeletionTimestamp().IsZero() { + return r.finalize(ctx, obj) + } + + // Return early if the KluctlDeployment is suspended. + if obj.Spec.Suspend { + log.Info("Reconciliation is suspended for this object") + return ctrl.Result{}, nil + } + + // record reconciliation duration + if r.MetricsRecorder != nil { + objRef, err := reference.GetReference(r.Scheme, obj) + if err != nil { + return ctrl.Result{}, err + } + defer r.MetricsRecorder.RecordDuration(*objRef, reconcileStart) + } + + // set the reconciliation status to progressing + if obj.Status.ObservedGeneration == 0 { + patch := client.MergeFrom(obj.DeepCopy()) + setReadiness(obj, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress") + if err := r.Status().Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { + return ctrl.Result{Requeue: true}, err + } + + r.recordReadiness(ctx, obj) + } + + // record the value of the reconciliation request, if any + if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { + obj.Status.LastHandledReconcileAt = v + } + + // reconcile kluctlDeployment by applying the latest revision + patch := client.MergeFrom(obj.DeepCopy()) + ctrlResult, reconcileErr := r.doReconcile(ctx, obj) + if err := r.Status().Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { + return ctrl.Result{Requeue: true}, err + } + + r.recordReadiness(ctx, obj) + + if ctrlResult == nil { + if reconcileErr != nil { + ctrlResult = &ctrl.Result{RequeueAfter: retryInterval} + } else { + ctrlResult = &ctrl.Result{RequeueAfter: interval} + } + } + + // broadcast the reconciliation failure and requeue at the specified retry interval + if reconcileErr != nil { + log.Info(fmt.Sprintf("Reconciliation failed after %s, next try in %s: %s", + time.Since(reconcileStart).String(), + ctrlResult.RequeueAfter.String(), reconcileErr.Error(), + )) + r.event(ctx, obj, true, + reconcileErr.Error(), nil) + return *ctrlResult, nil + } + + // broadcast the reconciliation result and requeue at the specified interval + msg := fmt.Sprintf("Reconciliation finished in %s, next run in %s", + time.Since(reconcileStart).String(), + ctrlResult.RequeueAfter.String()) + log.Info(msg) + r.event(ctx, obj, true, + msg, map[string]string{kluctlv1.GroupVersion.Group + "/commit_status": "update"}) + return *ctrlResult, nil +} + +func (r *KluctlDeploymentReconciler) doReconcile( + ctx context.Context, + obj *kluctlv1.KluctlDeployment) (*ctrl.Result, error) { + + r.exportDeploymentObjectToProm(obj) + + pp, err := prepareProject(ctx, r, obj, true) + if err != nil { + setReadiness(obj, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, err.Error()) + return nil, err + } + defer pp.cleanup() + + pt, err := pp.newTarget() + if err != nil { + setReadiness(obj, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, err.Error()) + return nil, err + } + + err = pt.withKluctlProjectTarget(ctx, func(targetContext *kluctl_project.TargetContext) error { + obj.Status.ProjectKey = &result.ProjectKey{ + NormalizedGitUrl: obj.Spec.Source.URL.NormalizedRepoKey(), + SubDir: path.Clean(obj.Spec.Source.Path), + } + obj.Status.TargetKey = &result.TargetKey{ + TargetName: targetContext.Target.Name, + Discriminator: targetContext.Target.Discriminator, + } + + if obj.Status.ProjectKey.SubDir == "." { + obj.Status.ProjectKey.SubDir = "" + } + + objectsHash, err := targetContext.DeploymentCollection.CalcObjectsHash() + if err != nil { + return err + } + + needDeploy := false + needPrune := false + needValidate := false + + if obj.Status.LastDeployResult == nil || obj.Status.LastObjectsHash != objectsHash { + // either never deployed or source code changed + needDeploy = true + } else if r.checkRequestedDeploy(obj) { + // explicitly requested a deploy + needDeploy = true + } else if obj.Status.ObservedGeneration != obj.GetGeneration() { + // spec has changed + needDeploy = true + } else { + // was deployed before, let's check if we need to do periodic deployments + nextDeployTime := r.nextDeployTime(obj) + if nextDeployTime != nil { + needDeploy = nextDeployTime.Before(time.Now()) + } + } + + if obj.Spec.Validate { + if obj.Status.LastValidateResult == nil || needDeploy { + // either never validated before or a deployment requested (which required re-validation) + needValidate = true + } else { + nextValidateTime := r.nextValidateTime(obj) + if nextValidateTime != nil { + needValidate = nextValidateTime.Before(time.Now()) + } + } + } else { + obj.Status.LastValidateResult = nil + } + + if obj.Spec.Prune { + needPrune = needDeploy + } else { + obj.Status.LastPruneResult = nil + } + + obj.Status.LastObjectsHash = objectsHash + + var deployResult *result.CommandResult + if needDeploy { + // deploy the kluctl project + if obj.Spec.DeployMode == kluctlv1.KluctlDeployModeFull { + deployResult, err = pt.kluctlDeploy(ctx, targetContext) + } else if obj.Spec.DeployMode == kluctlv1.KluctlDeployPokeImages { + deployResult, err = pt.kluctlPokeImages(ctx, targetContext) + } else { + err = fmt.Errorf("deployMode '%s' not supported", obj.Spec.DeployMode) + setReadiness(obj, metav1.ConditionFalse, kluctlv1.DeployFailedReason, err.Error()) + return nil + } + obj.Status.LastDeployResult = deployResult.BuildSummary() + if err != nil { + setReadiness(obj, metav1.ConditionFalse, kluctlv1.DeployFailedReason, err.Error()) + return nil + } + } + + if needPrune { + // run garbage collection for stale objects that do not have pruning disabled + pruneResult, err := pt.kluctlPrune(ctx, targetContext) + obj.Status.LastPruneResult = pruneResult.BuildSummary() + if err != nil { + setReadiness(obj, metav1.ConditionFalse, kluctlv1.PruneFailedReason, err.Error()) + return nil + } + } + + if needValidate { + validateResult, err := pt.kluctlValidate(ctx, targetContext, deployResult) + obj.Status.LastValidateResult = validateResult + if err != nil { + setReadiness(obj, metav1.ConditionFalse, kluctlv1.ValidateFailedReason, err.Error()) + return nil + } + } + return nil + }) + obj.Status.ObservedGeneration = obj.GetGeneration() + if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; ok { + obj.Status.LastHandledDeployAt = v + } + if err != nil { + setReadiness(obj, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, err.Error()) + return nil, err + } + + var ctrlResult ctrl.Result + ctrlResult.RequeueAfter = r.nextReconcileTime(obj).Sub(time.Now()) + if ctrlResult.RequeueAfter < 0 { + ctrlResult.RequeueAfter = 0 + ctrlResult.Requeue = true + } + + finalStatus, reason := r.buildFinalStatus(obj) + if reason != kluctlv1.ReconciliationSucceededReason { + setReadiness(obj, metav1.ConditionFalse, reason, finalStatus) + internal_metrics.NewKluctlLastObjectStatus(obj.Namespace, obj.Name).Set(0.0) + return &ctrlResult, fmt.Errorf(finalStatus) + } + setReadiness(obj, metav1.ConditionTrue, reason, finalStatus) + internal_metrics.NewKluctlLastObjectStatus(obj.Namespace, obj.Name).Set(1.0) + return &ctrlResult, nil +} + +func (r *KluctlDeploymentReconciler) buildFinalStatus(obj *kluctlv1.KluctlDeployment) (finalStatus string, reason string) { + deployOk := obj.Status.LastDeployResult != nil && obj.Status.LastDeployResult.Errors == 0 + pruneOk := obj.Status.LastPruneResult != nil && obj.Status.LastPruneResult.Errors == 0 + validateOk := obj.Status.LastValidateResult != nil && len(obj.Status.LastValidateResult.Errors) == 0 && obj.Status.LastValidateResult.Ready + + if !obj.Spec.Prune { + pruneOk = true + } + if !obj.Spec.Validate { + validateOk = true + } + + if obj.Status.LastDeployResult != nil { + finalStatus += "deploy: " + if deployOk { + finalStatus += "ok" + } else { + finalStatus += "failed" + } + } + if obj.Spec.Prune && obj.Status.LastPruneResult != nil { + if finalStatus != "" { + finalStatus += ", " + } + finalStatus += "prune: " + if pruneOk { + finalStatus += "ok" + } else { + finalStatus += "failed" + } + } + if obj.Spec.Validate && obj.Status.LastValidateResult != nil { + if finalStatus != "" { + finalStatus += ", " + } + finalStatus += "validate: " + if validateOk { + finalStatus += "ok" + } else { + finalStatus += "failed" + } + } + + if deployOk && pruneOk { + if validateOk { + reason = kluctlv1.ReconciliationSucceededReason + } else { + reason = kluctlv1.ValidateFailedReason + return + } + } else { + reason = kluctlv1.DeployFailedReason + return + } + return +} + +func (r *KluctlDeploymentReconciler) calcTimeout(obj *kluctlv1.KluctlDeployment) time.Duration { + var d time.Duration + if obj.Spec.Timeout != nil { + d = obj.Spec.Timeout.Duration + } else if obj.Spec.DeployInterval != nil { + d = obj.Spec.DeployInterval.Duration.Duration + } else { + d = obj.Spec.Interval.Duration + } + if d < time.Second*30 { + d = time.Second * 30 + } + return d +} + +func (r *KluctlDeploymentReconciler) nextReconcileTime(obj *kluctlv1.KluctlDeployment) time.Time { + t1 := time.Now().Add(obj.Spec.Interval.Duration) + t2 := r.nextDeployTime(obj) + t3 := r.nextValidateTime(obj) + if t2 != nil && t2.Before(t1) { + t1 = *t2 + } + if obj.Spec.Validate && t3 != nil && t3.Before(t1) { + t1 = *t3 + } + return t1 +} + +func (r *KluctlDeploymentReconciler) nextDeployTime(obj *kluctlv1.KluctlDeployment) *time.Time { + if obj.Status.LastDeployResult == nil { + // was never deployed before. Return early. + return nil + } + if obj.Spec.DeployInterval == nil { + // periodic deployments disabled + return nil + } + + t := obj.Status.LastDeployResult.Command.EndTime.Add(obj.Spec.DeployInterval.Duration.Duration) + return &t +} + +func (r *KluctlDeploymentReconciler) checkRequestedDeploy(obj *kluctlv1.KluctlDeployment) bool { + v, ok := obj.Annotations[kluctlv1.KluctlRequestDeployAnnotation] + if !ok { + return false + } + if v != obj.Status.LastHandledDeployAt { + return true + } + return false +} + +func (r *KluctlDeploymentReconciler) nextValidateTime(obj *kluctlv1.KluctlDeployment) *time.Time { + if obj.Status.LastValidateResult == nil { + // was never validated before. Return early. + return nil + } + d := obj.Spec.Interval.Duration + if obj.Spec.ValidateInterval != nil { + d = obj.Spec.ValidateInterval.Duration.Duration + } + + t := obj.Status.LastValidateResult.EndTime.Add(d) + return &t +} + +func (r *KluctlDeploymentReconciler) finalize(ctx context.Context, obj *kluctlv1.KluctlDeployment) (ctrl.Result, error) { + r.doFinalize(ctx, obj) + + // Record deleted status + r.recordReadiness(ctx, obj) + + // Remove our finalizer from the list and update it + patch := client.MergeFrom(obj.DeepCopy()) + controllerutil.RemoveFinalizer(obj, kluctlv1.KluctlDeploymentFinalizer) + if err := r.Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { + return ctrl.Result{}, err + } + + // Stop reconciliation as the object is being deleted + return ctrl.Result{}, nil +} + +func (r *KluctlDeploymentReconciler) doFinalize(ctx context.Context, obj *kluctlv1.KluctlDeployment) { + log := ctrl.LoggerFrom(ctx) + + if !obj.Spec.Delete || obj.Spec.Suspend { + return + } + + if obj.Status.ProjectKey == nil || obj.Status.TargetKey == nil { + log.V(1).Info("No project/target key set, skipping deletion") + return + } + + log.V(1).Info("Deleting target") + + pp, err := prepareProject(ctx, r, obj, false) + if err != nil { + return + } + defer pp.cleanup() + + pt, err := pp.newTarget() + if err != nil { + return + } + + _, _ = pt.kluctlDelete(ctx, obj.Status.TargetKey.Discriminator) +} + +func (r *KluctlDeploymentReconciler) exportDeploymentObjectToProm(obj *kluctlv1.KluctlDeployment) { + pruneEnabled := 0.0 + deleteEnabled := 0.0 + dryRunEnabled := 0.0 + deploymentInterval := 0.0 + + if obj.Spec.Prune { + pruneEnabled = 1.0 + } + if obj.Spec.Delete { + deleteEnabled = 1.0 + } + if obj.Spec.DryRun { + dryRunEnabled = 1.0 + } + //If not set, it defaults to interval + if obj.Spec.DeployInterval == nil { + deploymentInterval = obj.Spec.Interval.Seconds() + } else { + deploymentInterval = obj.Spec.DeployInterval.Duration.Seconds() + } + + //Export as Prometheus metric + internal_metrics.NewKluctlPruneEnabled(obj.Namespace, obj.Name).Set(pruneEnabled) + internal_metrics.NewKluctlDeleteEnabled(obj.Namespace, obj.Name).Set(deleteEnabled) + internal_metrics.NewKluctlDryRunEnabled(obj.Namespace, obj.Name).Set(dryRunEnabled) + internal_metrics.NewKluctlDeploymentInterval(obj.Namespace, obj.Name).Set(deploymentInterval) + internal_metrics.NewKluctlSourceSpec(obj.Namespace, obj.Name, obj.Spec.Source.URL.String(), obj.Spec.Source.Path, obj.Spec.Source.Ref.String()).Set(0.0) +} diff --git a/pkg/controllers/kluctldeployment_controller_setup.go b/pkg/controllers/kluctldeployment_controller_setup.go new file mode 100644 index 000000000..cb6af53f7 --- /dev/null +++ b/pkg/controllers/kluctldeployment_controller_setup.go @@ -0,0 +1,28 @@ +package controllers + +import ( + "github.com/hashicorp/go-retryablehttp" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "time" +) + +// SetupWithManager sets up the controller with the Manager. +func (r *KluctlDeploymentReconciler) SetupWithManager(mgr ctrl.Manager, opts KluctlDeploymentReconcilerOpts) error { + // Configure the retryable http client used for fetching artifacts. + // By default it retries 10 times within a 3.5 minutes window. + httpClient := retryablehttp.NewClient() + httpClient.RetryWaitMin = 5 * time.Second + httpClient.RetryWaitMax = 30 * time.Second + httpClient.RetryMax = opts.HTTPRetry + httpClient.Logger = nil + r.httpClient = httpClient + + return ctrl.NewControllerManagedBy(mgr). + For(&kluctlv1.KluctlDeployment{}, builder.WithPredicates( + predicate.Or(predicate.GenerationChangedPredicate{}, ReconcileRequestedPredicate{}, DeployRequestedPredicate{}), + )). + Complete(r) +} diff --git a/pkg/controllers/kluctldeployment_controller_source.go b/pkg/controllers/kluctldeployment_controller_source.go new file mode 100644 index 000000000..d206217b4 --- /dev/null +++ b/pkg/controllers/kluctldeployment_controller_source.go @@ -0,0 +1,109 @@ +package controllers + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + "github.com/kluctl/kluctl/v2/pkg/git/auth" + "github.com/kluctl/kluctl/v2/pkg/git/messages" + "github.com/kluctl/kluctl/v2/pkg/repocache" + "github.com/kluctl/kluctl/v2/pkg/utils" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "os" + "path/filepath" + ctrl "sigs.k8s.io/controller-runtime" +) + +func (r *KluctlDeploymentReconciler) getGitSecret(ctx context.Context, source *kluctlv1.ProjectSource, objNs string) (*corev1.Secret, error) { + if source == nil || source.SecretRef == nil { + return nil, nil + } + + // Attempt to retrieve secret + name := types.NamespacedName{ + Namespace: objNs, + Name: source.SecretRef.Name, + } + var secret corev1.Secret + if err := r.Get(ctx, name, &secret); err != nil { + return nil, fmt.Errorf("failed to get secret '%s': %w", name.String(), err) + } + return &secret, nil +} + +func (r *KluctlDeploymentReconciler) buildGitAuth(ctx context.Context, gitSecret *corev1.Secret) (*auth.GitAuthProviders, error) { + log := ctrl.LoggerFrom(ctx) + ga := auth.NewDefaultAuthProviders("KLUCTL_GIT", &messages.MessageCallbacks{ + WarningFn: func(s string) { + log.Info(s) + }, + TraceFn: func(s string) { + log.V(1).Info(s) + }, + }) + + if gitSecret == nil { + return ga, nil + } + + e := auth.AuthEntry{ + Host: "*", + Username: "*", + } + + if x, ok := gitSecret.Data["username"]; ok { + e.Username = string(x) + } + if x, ok := gitSecret.Data["password"]; ok { + e.Password = string(x) + } + if x, ok := gitSecret.Data["caFile"]; ok { + e.CABundle = x + } + if x, ok := gitSecret.Data["known_hosts"]; ok { + e.KnownHosts = x + } + if x, ok := gitSecret.Data["identity"]; ok { + e.SshKey = x + } + + var la auth.ListAuthProvider + la.AddEntry(e) + ga.RegisterAuthProvider(&la, false) + return ga, nil +} + +func (r *KluctlDeploymentReconciler) buildRepoCache(ctx context.Context, secret *corev1.Secret) (*repocache.GitRepoCache, error) { + // make sure we use a unique repo cache per set of credentials + h := sha256.New() + if secret == nil { + h.Write([]byte("no-secret")) + } else { + m := json.NewEncoder(h) + err := m.Encode(secret.Data) + if err != nil { + return nil, err + } + } + h2 := hex.EncodeToString(h.Sum(nil)) + + tmpBaseDir := filepath.Join(os.TempDir(), "kluctl-controller-repo-cache", h2) + err := os.MkdirAll(tmpBaseDir, 0o700) + if err != nil { + return nil, err + } + + ctx = utils.WithTmpBaseDir(ctx, tmpBaseDir) + + ga, err := r.buildGitAuth(ctx, secret) + if err != nil { + return nil, err + } + + rc := repocache.NewGitRepoCache(ctx, r.SshPool, ga, nil, 0) + return rc, nil +} diff --git a/pkg/controllers/kluctldeployment_controller_utils.go b/pkg/controllers/kluctldeployment_controller_utils.go new file mode 100644 index 000000000..7489c3b2d --- /dev/null +++ b/pkg/controllers/kluctldeployment_controller_utils.go @@ -0,0 +1,72 @@ +package controllers + +import ( + "context" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/reference" + ctrl "sigs.k8s.io/controller-runtime" +) + +func (r *KluctlDeploymentReconciler) event(ctx context.Context, obj *kluctlv1.KluctlDeployment, warning bool, msg string, metadata map[string]string) { + if metadata == nil { + metadata = map[string]string{} + } + + reason := "info" + if warning { + reason = "warning" + } + if c := apimeta.FindStatusCondition(obj.GetConditions(), meta.ReadyCondition); c != nil { + reason = c.Reason + } + + eventtype := "Normal" + if warning { + eventtype = "Warning" + } + + r.EventRecorder.AnnotatedEventf(obj, metadata, eventtype, reason, msg) +} + +func (r *KluctlDeploymentReconciler) recordReadiness(ctx context.Context, obj *kluctlv1.KluctlDeployment) { + if r.MetricsRecorder == nil { + return + } + log := ctrl.LoggerFrom(ctx) + + objRef, err := reference.GetReference(r.Scheme, obj) + if err != nil { + log.Error(err, "unable to record readiness metric") + return + } + if rc := apimeta.FindStatusCondition(obj.GetConditions(), meta.ReadyCondition); rc != nil { + r.MetricsRecorder.RecordCondition(*objRef, *rc, !obj.GetDeletionTimestamp().IsZero()) + } else { + r.MetricsRecorder.RecordCondition(*objRef, metav1.Condition{ + Type: meta.ReadyCondition, + Status: metav1.ConditionUnknown, + }, !obj.GetDeletionTimestamp().IsZero()) + } +} + +func (r *KluctlDeploymentReconciler) recordSuspension(ctx context.Context, obj *kluctlv1.KluctlDeployment) { + if r.MetricsRecorder == nil { + return + } + log := ctrl.LoggerFrom(ctx) + + objRef, err := reference.GetReference(r.Scheme, obj) + if err != nil { + log.Error(err, "unable to record suspended metric") + return + } + + if !obj.GetDeletionTimestamp().IsZero() { + r.MetricsRecorder.RecordSuspend(*objRef, false) + } else { + r.MetricsRecorder.RecordSuspend(*objRef, obj.Spec.Suspend) + } +} diff --git a/pkg/controllers/predicates.go b/pkg/controllers/predicates.go new file mode 100644 index 000000000..1c6f52c30 --- /dev/null +++ b/pkg/controllers/predicates.go @@ -0,0 +1,66 @@ +/* +Copyright 2020 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +type ReconcileRequestedPredicate struct { + predicate.Funcs +} + +// Update implements the default UpdateEvent filter for validating flux_utils.ReconcileRequestAnnotation changes. +func (ReconcileRequestedPredicate) Update(e event.UpdateEvent) bool { + if e.ObjectOld == nil || e.ObjectNew == nil { + return false + } + + if val, ok := e.ObjectNew.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { + if valOld, okOld := e.ObjectOld.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; okOld { + return val != valOld + } + return true + } + return false +} + +type DeployRequestedPredicate struct { + predicate.Funcs +} + +func (DeployRequestedPredicate) Update(e event.UpdateEvent) bool { + if e.ObjectOld == nil || e.ObjectNew == nil { + return false + } + + if val, ok := e.ObjectNew.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { + if valOld, okOld := e.ObjectOld.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; okOld { + return val != valOld + } + return true + } + if val, ok := e.ObjectNew.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; ok { + if valOld, okOld := e.ObjectOld.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; okOld { + return val != valOld + } + return true + } + return false +} diff --git a/pkg/controllers/utils.go b/pkg/controllers/utils.go new file mode 100644 index 000000000..7d4476e8d --- /dev/null +++ b/pkg/controllers/utils.go @@ -0,0 +1,31 @@ +package controllers + +import ( + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + meta "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func setReadiness(obj *kluctlv1.KluctlDeployment, status metav1.ConditionStatus, reason, message string) { + newCondition := metav1.Condition{ + Type: meta.ReadyCondition, + Status: status, + Reason: reason, + Message: trimString(message, kluctlv1.MaxConditionMessageLength), + } + + c := obj.GetConditions() + apimeta.SetStatusCondition(&c, newCondition) + obj.SetConditions(c) + + obj.Status.ObservedGeneration = obj.GetGeneration() +} + +func trimString(str string, limit int) string { + if len(str) <= limit { + return str + } + + return str[0:limit] + "..." +} diff --git a/pkg/deployment/commands/result_utils.go b/pkg/deployment/commands/result_utils.go index ecd108d56..43c26f685 100644 --- a/pkg/deployment/commands/result_utils.go +++ b/pkg/deployment/commands/result_utils.go @@ -128,7 +128,7 @@ func addGitInfo(targetCtx *kluctl_project.TargetContext, r *result.CommandResult normaliedUrl = originUrl.NormalizedRepoKey() } - r.GitInfo = &result.GitInfo{ + r.GitInfo = result.GitInfo{ Url: originUrl, Ref: head.Name().String(), SubDir: subDir, diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 53f31c0de..17faff1a6 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -1,12 +1,16 @@ package deployment import ( + "context" + "crypto/sha256" + "encoding/hex" "fmt" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" k8s2 "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/yaml" "path/filepath" "sync" ) @@ -297,3 +301,40 @@ func (c *DeploymentCollection) FindRenderedImages() map[k8s2.ObjectRef][]string } return ret } + +func (c *DeploymentCollection) CalcObjectsHash() (string, error) { + cnt := 0 + for _, di := range c.Deployments { + cnt += len(di.Objects) + } + + hashes := make([][32]byte, cnt) + gh := utils.NewGoHelper(context.Background(), 8) + i := 0 + for _, di := range c.Deployments { + for _, o := range di.Objects { + i2 := i + o := o + gh.RunE(func() error { + j, err := yaml.WriteJsonString(o) + if err != nil { + return err + } + hashes[i2] = sha256.Sum256([]byte(j)) + return nil + }) + i++ + } + } + gh.Wait() + if gh.ErrorOrNil() != nil { + return "", gh.ErrorOrNil() + } + + h := sha256.New() + for _, x := range hashes { + h.Write(x[:]) + } + + return hex.EncodeToString(h.Sum(nil)), nil +} diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 80d30e378..3f8a948ac 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -133,7 +133,7 @@ type CommandResult struct { TargetKey TargetKey `json:"targetKey"` Target types.Target `json:"target"` Command CommandInfo `json:"command,omitempty"` - GitInfo *GitInfo `json:"gitInfo,omitempty"` + GitInfo GitInfo `json:"gitInfo,omitempty"` ClusterInfo ClusterInfo `json:"clusterInfo"` Deployment *types.DeploymentProjectConfig `json:"deployment,omitempty"` diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index dc05e095a..5cf639762 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -9,7 +9,7 @@ type CommandResultSummary struct { Project ProjectKey `json:"project"` Target TargetKey `json:"target"` Command CommandInfo `json:"commandInfo"` - GitInfo *GitInfo `json:"gitInfo,omitempty"` + GitInfo GitInfo `json:"gitInfo,omitempty"` ClusterInfo ClusterInfo `json:"clusterInfo,omitempty"` RenderedObjects int `json:"renderedObjects"` @@ -41,6 +41,10 @@ type ProjectSummary struct { } func (cr *CommandResult) BuildSummary() *CommandResultSummary { + if cr == nil { + return nil + } + count := func(f func(o ResultObject) bool) int { cnt := 0 for _, o := range cr.Objects { From 6c38f31d751882a0098989c23fec3be1089adf29 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 15:34:16 +0200 Subject: [PATCH 0985/2268] chore: Run make generate --- api/v1beta1/zz_generated.deepcopy.go | 369 ++++++++ .../gitops.kluctl.io_kluctldeployments.yaml | 3 +- config/rbac/role.yaml | 1 - pkg/types/k8s/zz_generated.deepcopy.go | 39 + pkg/types/result/zz_generated.deepcopy.go | 512 +++++++++++ pkg/types/zz_generated.deepcopy.go | 863 ++++++++++++++++++ 6 files changed, 1784 insertions(+), 3 deletions(-) create mode 100644 api/v1beta1/zz_generated.deepcopy.go create mode 100644 pkg/types/k8s/zz_generated.deepcopy.go create mode 100644 pkg/types/result/zz_generated.deepcopy.go create mode 100644 pkg/types/zz_generated.deepcopy.go diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 000000000..79c1afc75 --- /dev/null +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,369 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "k8s.io/apimachinery/pkg/apis/meta/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 *Decryption) DeepCopyInto(out *Decryption) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(LocalObjectReference) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Decryption. +func (in *Decryption) DeepCopy() *Decryption { + if in == nil { + return nil + } + out := new(Decryption) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitRef) DeepCopyInto(out *GitRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRef. +func (in *GitRef) DeepCopy() *GitRef { + if in == nil { + return nil + } + out := new(GitRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmCredentials) DeepCopyInto(out *HelmCredentials) { + *out = *in + out.SecretRef = in.SecretRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmCredentials. +func (in *HelmCredentials) DeepCopy() *HelmCredentials { + if in == nil { + return nil + } + out := new(HelmCredentials) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KluctlDeployment) DeepCopyInto(out *KluctlDeployment) { + *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 KluctlDeployment. +func (in *KluctlDeployment) DeepCopy() *KluctlDeployment { + if in == nil { + return nil + } + out := new(KluctlDeployment) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KluctlDeployment) 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 *KluctlDeploymentList) DeepCopyInto(out *KluctlDeploymentList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KluctlDeployment, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KluctlDeploymentList. +func (in *KluctlDeploymentList) DeepCopy() *KluctlDeploymentList { + if in == nil { + return nil + } + out := new(KluctlDeploymentList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KluctlDeploymentList) 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 *KluctlDeploymentSpec) DeepCopyInto(out *KluctlDeploymentSpec) { + *out = *in + in.Source.DeepCopyInto(&out.Source) + if in.Decryption != nil { + in, out := &in.Decryption, &out.Decryption + *out = new(Decryption) + (*in).DeepCopyInto(*out) + } + out.Interval = in.Interval + if in.RetryInterval != nil { + in, out := &in.RetryInterval, &out.RetryInterval + *out = new(v1.Duration) + **out = **in + } + if in.DeployInterval != nil { + in, out := &in.DeployInterval, &out.DeployInterval + *out = new(SafeDuration) + **out = **in + } + if in.ValidateInterval != nil { + in, out := &in.ValidateInterval, &out.ValidateInterval + *out = new(SafeDuration) + **out = **in + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } + if in.HelmCredentials != nil { + in, out := &in.HelmCredentials, &out.HelmCredentials + *out = make([]HelmCredentials, len(*in)) + copy(*out, *in) + } + if in.KubeConfig != nil { + in, out := &in.KubeConfig, &out.KubeConfig + *out = new(KubeConfig) + **out = **in + } + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(string) + **out = **in + } + if in.TargetNameOverride != nil { + in, out := &in.TargetNameOverride, &out.TargetNameOverride + *out = new(string) + **out = **in + } + if in.Context != nil { + in, out := &in.Context, &out.Context + *out = new(string) + **out = **in + } + in.Args.DeepCopyInto(&out.Args) + if in.Images != nil { + in, out := &in.Images, &out.Images + *out = make([]types.FixedImage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.IncludeTags != nil { + in, out := &in.IncludeTags, &out.IncludeTags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExcludeTags != nil { + in, out := &in.ExcludeTags, &out.ExcludeTags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.IncludeDeploymentDirs != nil { + in, out := &in.IncludeDeploymentDirs, &out.IncludeDeploymentDirs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExcludeDeploymentDirs != nil { + in, out := &in.ExcludeDeploymentDirs, &out.ExcludeDeploymentDirs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KluctlDeploymentSpec. +func (in *KluctlDeploymentSpec) DeepCopy() *KluctlDeploymentSpec { + if in == nil { + return nil + } + out := new(KluctlDeploymentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KluctlDeploymentStatus) DeepCopyInto(out *KluctlDeploymentStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ProjectKey != nil { + in, out := &in.ProjectKey, &out.ProjectKey + *out = new(result.ProjectKey) + **out = **in + } + if in.TargetKey != nil { + in, out := &in.TargetKey, &out.TargetKey + *out = new(result.TargetKey) + **out = **in + } + if in.LastDeployResult != nil { + in, out := &in.LastDeployResult, &out.LastDeployResult + *out = new(result.CommandResultSummary) + (*in).DeepCopyInto(*out) + } + if in.LastPruneResult != nil { + in, out := &in.LastPruneResult, &out.LastPruneResult + *out = new(result.CommandResultSummary) + (*in).DeepCopyInto(*out) + } + if in.LastValidateResult != nil { + in, out := &in.LastValidateResult, &out.LastValidateResult + *out = new(result.ValidateResult) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KluctlDeploymentStatus. +func (in *KluctlDeploymentStatus) DeepCopy() *KluctlDeploymentStatus { + if in == nil { + return nil + } + out := new(KluctlDeploymentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeConfig) DeepCopyInto(out *KubeConfig) { + *out = *in + out.SecretRef = in.SecretRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeConfig. +func (in *KubeConfig) DeepCopy() *KubeConfig { + if in == nil { + return nil + } + out := new(KubeConfig) + 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 *ProjectSource) DeepCopyInto(out *ProjectSource) { + *out = *in + in.URL.DeepCopyInto(&out.URL) + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + *out = new(GitRef) + **out = **in + } + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(LocalObjectReference) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSource. +func (in *ProjectSource) DeepCopy() *ProjectSource { + if in == nil { + return nil + } + out := new(ProjectSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SafeDuration) DeepCopyInto(out *SafeDuration) { + *out = *in + out.Duration = in.Duration +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SafeDuration. +func (in *SafeDuration) DeepCopy() *SafeDuration { + if in == nil { + return nil + } + out := new(SafeDuration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretKeyReference) DeepCopyInto(out *SecretKeyReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeyReference. +func (in *SecretKeyReference) DeepCopy() *SecretKeyReference { + if in == nil { + return nil + } + out := new(SecretKeyReference) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml index 3e81ebc5e..4abd9c5ce 100644 --- a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml +++ b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: kluctldeployments.gitops.kluctl.io spec: group: gitops.kluctl.io diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 5e7aaeef7..e043f192e 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,7 +2,6 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: diff --git a/pkg/types/k8s/zz_generated.deepcopy.go b/pkg/types/k8s/zz_generated.deepcopy.go new file mode 100644 index 000000000..a753e48cd --- /dev/null +++ b/pkg/types/k8s/zz_generated.deepcopy.go @@ -0,0 +1,39 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package k8s + +import () + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectRef) DeepCopyInto(out *ObjectRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectRef. +func (in *ObjectRef) DeepCopy() *ObjectRef { + if in == nil { + return nil + } + out := new(ObjectRef) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/types/result/zz_generated.deepcopy.go b/pkg/types/result/zz_generated.deepcopy.go new file mode 100644 index 000000000..c7c90926a --- /dev/null +++ b/pkg/types/result/zz_generated.deepcopy.go @@ -0,0 +1,512 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package result + +import ( + "github.com/kluctl/kluctl/v2/pkg/types" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BaseObject) DeepCopyInto(out *BaseObject) { + *out = *in + out.Ref = in.Ref + if in.Changes != nil { + in, out := &in.Changes, &out.Changes + *out = make([]Change, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BaseObject. +func (in *BaseObject) DeepCopy() *BaseObject { + if in == nil { + return nil + } + out := new(BaseObject) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Change) DeepCopyInto(out *Change) { + *out = *in + if in.OldValue != nil { + in, out := &in.OldValue, &out.OldValue + *out = new(v1.JSON) + (*in).DeepCopyInto(*out) + } + if in.NewValue != nil { + in, out := &in.NewValue, &out.NewValue + *out = new(v1.JSON) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Change. +func (in *Change) DeepCopy() *Change { + if in == nil { + return nil + } + out := new(Change) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ChangedObject) DeepCopyInto(out *ChangedObject) { + *out = *in + out.Ref = in.Ref + if in.Changes != nil { + in, out := &in.Changes, &out.Changes + *out = make([]Change, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChangedObject. +func (in *ChangedObject) DeepCopy() *ChangedObject { + if in == nil { + return nil + } + out := new(ChangedObject) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterInfo) DeepCopyInto(out *ClusterInfo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInfo. +func (in *ClusterInfo) DeepCopy() *ClusterInfo { + if in == nil { + return nil + } + out := new(ClusterInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CommandInfo) DeepCopyInto(out *CommandInfo) { + *out = *in + in.StartTime.DeepCopyInto(&out.StartTime) + in.EndTime.DeepCopyInto(&out.EndTime) + if in.KluctlDeployment != nil { + in, out := &in.KluctlDeployment, &out.KluctlDeployment + *out = new(KluctlDeploymentInfo) + **out = **in + } + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = (*in).DeepCopy() + } + if in.Images != nil { + in, out := &in.Images, &out.Images + *out = make([]types.FixedImage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.IncludeTags != nil { + in, out := &in.IncludeTags, &out.IncludeTags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExcludeTags != nil { + in, out := &in.ExcludeTags, &out.ExcludeTags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.IncludeDeploymentDirs != nil { + in, out := &in.IncludeDeploymentDirs, &out.IncludeDeploymentDirs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ExcludeDeploymentDirs != nil { + in, out := &in.ExcludeDeploymentDirs, &out.ExcludeDeploymentDirs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandInfo. +func (in *CommandInfo) DeepCopy() *CommandInfo { + if in == nil { + return nil + } + out := new(CommandInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CommandResult) DeepCopyInto(out *CommandResult) { + *out = *in + out.ProjectKey = in.ProjectKey + out.TargetKey = in.TargetKey + in.Target.DeepCopyInto(&out.Target) + in.Command.DeepCopyInto(&out.Command) + in.GitInfo.DeepCopyInto(&out.GitInfo) + out.ClusterInfo = in.ClusterInfo + if in.Deployment != nil { + in, out := &in.Deployment, &out.Deployment + *out = new(types.DeploymentProjectConfig) + (*in).DeepCopyInto(*out) + } + if in.Objects != nil { + in, out := &in.Objects, &out.Objects + *out = make([]ResultObject, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Errors != nil { + in, out := &in.Errors, &out.Errors + *out = make([]DeploymentError, len(*in)) + copy(*out, *in) + } + if in.Warnings != nil { + in, out := &in.Warnings, &out.Warnings + *out = make([]DeploymentError, len(*in)) + copy(*out, *in) + } + if in.SeenImages != nil { + in, out := &in.SeenImages, &out.SeenImages + *out = make([]types.FixedImage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandResult. +func (in *CommandResult) DeepCopy() *CommandResult { + if in == nil { + return nil + } + out := new(CommandResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CommandResultSummary) DeepCopyInto(out *CommandResultSummary) { + *out = *in + out.Project = in.Project + out.Target = in.Target + in.Command.DeepCopyInto(&out.Command) + in.GitInfo.DeepCopyInto(&out.GitInfo) + out.ClusterInfo = in.ClusterInfo +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandResultSummary. +func (in *CommandResultSummary) DeepCopy() *CommandResultSummary { + if in == nil { + return nil + } + out := new(CommandResultSummary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompactedCommandResult) DeepCopyInto(out *CompactedCommandResult) { + *out = *in + in.CommandResult.DeepCopyInto(&out.CommandResult) + if in.CompactedObjects != nil { + in, out := &in.CompactedObjects, &out.CompactedObjects + *out = make(CompactedObjects, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompactedCommandResult. +func (in *CompactedCommandResult) DeepCopy() *CompactedCommandResult { + if in == nil { + return nil + } + out := new(CompactedCommandResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompactedObject) DeepCopyInto(out *CompactedObject) { + *out = *in + in.BaseObject.DeepCopyInto(&out.BaseObject) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompactedObject. +func (in *CompactedObject) DeepCopy() *CompactedObject { + if in == nil { + return nil + } + out := new(CompactedObject) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in CompactedObjects) DeepCopyInto(out *CompactedObjects) { + { + in := &in + *out = make(CompactedObjects, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompactedObjects. +func (in CompactedObjects) DeepCopy() CompactedObjects { + if in == nil { + return nil + } + out := new(CompactedObjects) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentError) DeepCopyInto(out *DeploymentError) { + *out = *in + out.Ref = in.Ref +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentError. +func (in *DeploymentError) DeepCopy() *DeploymentError { + if in == nil { + return nil + } + out := new(DeploymentError) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitInfo) DeepCopyInto(out *GitInfo) { + *out = *in + if in.Url != nil { + in, out := &in.Url, &out.Url + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitInfo. +func (in *GitInfo) DeepCopy() *GitInfo { + if in == nil { + return nil + } + out := new(GitInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KluctlDeploymentInfo) DeepCopyInto(out *KluctlDeploymentInfo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KluctlDeploymentInfo. +func (in *KluctlDeploymentInfo) DeepCopy() *KluctlDeploymentInfo { + if in == nil { + return nil + } + out := new(KluctlDeploymentInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectKey) DeepCopyInto(out *ProjectKey) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectKey. +func (in *ProjectKey) DeepCopy() *ProjectKey { + if in == nil { + return nil + } + out := new(ProjectKey) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectSummary) DeepCopyInto(out *ProjectSummary) { + *out = *in + out.Project = in.Project + if in.Targets != nil { + in, out := &in.Targets, &out.Targets + *out = make([]*TargetSummary, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TargetSummary) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSummary. +func (in *ProjectSummary) DeepCopy() *ProjectSummary { + if in == nil { + return nil + } + out := new(ProjectSummary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResultObject) DeepCopyInto(out *ResultObject) { + *out = *in + in.BaseObject.DeepCopyInto(&out.BaseObject) + if in.Rendered != nil { + in, out := &in.Rendered, &out.Rendered + *out = (*in).DeepCopy() + } + if in.Remote != nil { + in, out := &in.Remote, &out.Remote + *out = (*in).DeepCopy() + } + if in.Applied != nil { + in, out := &in.Applied, &out.Applied + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResultObject. +func (in *ResultObject) DeepCopy() *ResultObject { + if in == nil { + return nil + } + out := new(ResultObject) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetKey) DeepCopyInto(out *TargetKey) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetKey. +func (in *TargetKey) DeepCopy() *TargetKey { + if in == nil { + return nil + } + out := new(TargetKey) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetSummary) DeepCopyInto(out *TargetSummary) { + *out = *in + out.Target = in.Target + if in.LastValidateResult != nil { + in, out := &in.LastValidateResult, &out.LastValidateResult + *out = new(ValidateResult) + (*in).DeepCopyInto(*out) + } + if in.CommandResults != nil { + in, out := &in.CommandResults, &out.CommandResults + *out = make([]CommandResultSummary, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSummary. +func (in *TargetSummary) DeepCopy() *TargetSummary { + if in == nil { + return nil + } + out := new(TargetSummary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValidateResult) DeepCopyInto(out *ValidateResult) { + *out = *in + if in.Warnings != nil { + in, out := &in.Warnings, &out.Warnings + *out = make([]DeploymentError, len(*in)) + copy(*out, *in) + } + if in.Errors != nil { + in, out := &in.Errors, &out.Errors + *out = make([]DeploymentError, len(*in)) + copy(*out, *in) + } + if in.Results != nil { + in, out := &in.Results, &out.Results + *out = make([]ValidateResultEntry, len(*in)) + copy(*out, *in) + } + if in.Drift != nil { + in, out := &in.Drift, &out.Drift + *out = make([]ChangedObject, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValidateResult. +func (in *ValidateResult) DeepCopy() *ValidateResult { + if in == nil { + return nil + } + out := new(ValidateResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValidateResultEntry) DeepCopyInto(out *ValidateResultEntry) { + *out = *in + out.Ref = in.Ref +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValidateResultEntry. +func (in *ValidateResultEntry) DeepCopy() *ValidateResultEntry { + if in == nil { + return nil + } + out := new(ValidateResultEntry) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/types/zz_generated.deepcopy.go b/pkg/types/zz_generated.deepcopy.go new file mode 100644 index 000000000..ec75fae41 --- /dev/null +++ b/pkg/types/zz_generated.deepcopy.go @@ -0,0 +1,863 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package types + +import ( + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeleteObjectItemConfig) DeepCopyInto(out *DeleteObjectItemConfig) { + *out = *in + if in.Group != nil { + in, out := &in.Group, &out.Group + *out = new(string) + **out = **in + } + if in.Kind != nil { + in, out := &in.Kind, &out.Kind + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeleteObjectItemConfig. +func (in *DeleteObjectItemConfig) DeepCopy() *DeleteObjectItemConfig { + if in == nil { + return nil + } + out := new(DeleteObjectItemConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentArg) DeepCopyInto(out *DeploymentArg) { + *out = *in + if in.Default != nil { + in, out := &in.Default, &out.Default + *out = new(v1.JSON) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentArg. +func (in *DeploymentArg) DeepCopy() *DeploymentArg { + if in == nil { + return nil + } + out := new(DeploymentArg) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentItemConfig) DeepCopyInto(out *DeploymentItemConfig) { + *out = *in + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(string) + **out = **in + } + if in.Include != nil { + in, out := &in.Include, &out.Include + *out = new(string) + **out = **in + } + if in.Git != nil { + in, out := &in.Git, &out.Git + *out = new(GitProject) + (*in).DeepCopyInto(*out) + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Vars != nil { + in, out := &in.Vars, &out.Vars + *out = make([]*VarsSource, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(VarsSource) + (*in).DeepCopyInto(*out) + } + } + } + if in.DeleteObjects != nil { + in, out := &in.DeleteObjects, &out.DeleteObjects + *out = make([]DeleteObjectItemConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RenderedHelmChartConfig != nil { + in, out := &in.RenderedHelmChartConfig, &out.RenderedHelmChartConfig + *out = new(HelmChartConfig) + (*in).DeepCopyInto(*out) + } + if in.RenderedObjects != nil { + in, out := &in.RenderedObjects, &out.RenderedObjects + *out = make([]k8s.ObjectRef, len(*in)) + copy(*out, *in) + } + if in.RenderedInclude != nil { + in, out := &in.RenderedInclude, &out.RenderedInclude + *out = new(DeploymentProjectConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentItemConfig. +func (in *DeploymentItemConfig) DeepCopy() *DeploymentItemConfig { + if in == nil { + return nil + } + out := new(DeploymentItemConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentProjectConfig) DeepCopyInto(out *DeploymentProjectConfig) { + *out = *in + if in.Vars != nil { + in, out := &in.Vars, &out.Vars + *out = make([]*VarsSource, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(VarsSource) + (*in).DeepCopyInto(*out) + } + } + } + if in.SealedSecrets != nil { + in, out := &in.SealedSecrets, &out.SealedSecrets + *out = new(SealedSecretsConfig) + (*in).DeepCopyInto(*out) + } + if in.Deployments != nil { + in, out := &in.Deployments, &out.Deployments + *out = make([]*DeploymentItemConfig, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(DeploymentItemConfig) + (*in).DeepCopyInto(*out) + } + } + } + if in.CommonLabels != nil { + in, out := &in.CommonLabels, &out.CommonLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.CommonAnnotations != nil { + in, out := &in.CommonAnnotations, &out.CommonAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.OverrideNamespace != nil { + in, out := &in.OverrideNamespace, &out.OverrideNamespace + *out = new(string) + **out = **in + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.IgnoreForDiff != nil { + in, out := &in.IgnoreForDiff, &out.IgnoreForDiff + *out = make([]*IgnoreForDiffItemConfig, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(IgnoreForDiffItemConfig) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentProjectConfig. +func (in *DeploymentProjectConfig) DeepCopy() *DeploymentProjectConfig { + if in == nil { + return nil + } + out := new(DeploymentProjectConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExternalProject) DeepCopyInto(out *ExternalProject) { + *out = *in + if in.Project != nil { + in, out := &in.Project, &out.Project + *out = new(GitProject) + (*in).DeepCopyInto(*out) + } + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalProject. +func (in *ExternalProject) DeepCopy() *ExternalProject { + if in == nil { + return nil + } + out := new(ExternalProject) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FixedImage) DeepCopyInto(out *FixedImage) { + *out = *in + if in.DeployedImage != nil { + in, out := &in.DeployedImage, &out.DeployedImage + *out = new(string) + **out = **in + } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(string) + **out = **in + } + if in.Object != nil { + in, out := &in.Object, &out.Object + *out = new(k8s.ObjectRef) + **out = **in + } + if in.Deployment != nil { + in, out := &in.Deployment, &out.Deployment + *out = new(string) + **out = **in + } + if in.Container != nil { + in, out := &in.Container, &out.Container + *out = new(string) + **out = **in + } + if in.DeployTags != nil { + in, out := &in.DeployTags, &out.DeployTags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.DeploymentDir != nil { + in, out := &in.DeploymentDir, &out.DeploymentDir + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FixedImage. +func (in *FixedImage) DeepCopy() *FixedImage { + if in == nil { + return nil + } + out := new(FixedImage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FixedImagesConfig) DeepCopyInto(out *FixedImagesConfig) { + *out = *in + if in.Images != nil { + in, out := &in.Images, &out.Images + *out = make([]FixedImage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FixedImagesConfig. +func (in *FixedImagesConfig) DeepCopy() *FixedImagesConfig { + if in == nil { + return nil + } + out := new(FixedImagesConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitProject) DeepCopyInto(out *GitProject) { + *out = *in + in.Url.DeepCopyInto(&out.Url) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitProject. +func (in *GitProject) DeepCopy() *GitProject { + if in == nil { + return nil + } + out := new(GitProject) + in.DeepCopyInto(out) + return out +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitUrl. +func (in *GitUrl) DeepCopy() *GitUrl { + if in == nil { + return nil + } + out := new(GitUrl) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalSealedSecretsConfig) DeepCopyInto(out *GlobalSealedSecretsConfig) { + *out = *in + if in.Bootstrap != nil { + in, out := &in.Bootstrap, &out.Bootstrap + *out = new(bool) + **out = **in + } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(string) + **out = **in + } + if in.ControllerName != nil { + in, out := &in.ControllerName, &out.ControllerName + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalSealedSecretsConfig. +func (in *GlobalSealedSecretsConfig) DeepCopy() *GlobalSealedSecretsConfig { + if in == nil { + return nil + } + out := new(GlobalSealedSecretsConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmChartConfig) DeepCopyInto(out *HelmChartConfig) { + *out = *in + in.HelmChartConfig2.DeepCopyInto(&out.HelmChartConfig2) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartConfig. +func (in *HelmChartConfig) DeepCopy() *HelmChartConfig { + if in == nil { + return nil + } + out := new(HelmChartConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HelmChartConfig2) DeepCopyInto(out *HelmChartConfig2) { + *out = *in + if in.CredentialsId != nil { + in, out := &in.CredentialsId, &out.CredentialsId + *out = new(string) + **out = **in + } + if in.UpdateConstraints != nil { + in, out := &in.UpdateConstraints, &out.UpdateConstraints + *out = new(string) + **out = **in + } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(string) + **out = **in + } + if in.Output != nil { + in, out := &in.Output, &out.Output + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartConfig2. +func (in *HelmChartConfig2) DeepCopy() *HelmChartConfig2 { + if in == nil { + return nil + } + out := new(HelmChartConfig2) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IgnoreForDiffItemConfig) DeepCopyInto(out *IgnoreForDiffItemConfig) { + *out = *in + if in.FieldPath != nil { + in, out := &in.FieldPath, &out.FieldPath + *out = make(SingleStringOrList, len(*in)) + copy(*out, *in) + } + if in.FieldPathRegex != nil { + in, out := &in.FieldPathRegex, &out.FieldPathRegex + *out = make(SingleStringOrList, len(*in)) + copy(*out, *in) + } + if in.Group != nil { + in, out := &in.Group, &out.Group + *out = new(string) + **out = **in + } + if in.Kind != nil { + in, out := &in.Kind, &out.Kind + *out = new(string) + **out = **in + } + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IgnoreForDiffItemConfig. +func (in *IgnoreForDiffItemConfig) DeepCopy() *IgnoreForDiffItemConfig { + if in == nil { + return nil + } + out := new(IgnoreForDiffItemConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KluctlProject) DeepCopyInto(out *KluctlProject) { + *out = *in + if in.Targets != nil { + in, out := &in.Targets, &out.Targets + *out = make([]*Target, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Target) + (*in).DeepCopyInto(*out) + } + } + } + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make([]*DeploymentArg, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(DeploymentArg) + (*in).DeepCopyInto(*out) + } + } + } + if in.SecretsConfig != nil { + in, out := &in.SecretsConfig, &out.SecretsConfig + *out = new(SecretsConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KluctlProject. +func (in *KluctlProject) DeepCopy() *KluctlProject { + if in == nil { + return nil + } + out := new(KluctlProject) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SealedSecretsConfig) DeepCopyInto(out *SealedSecretsConfig) { + *out = *in + if in.OutputPattern != nil { + in, out := &in.OutputPattern, &out.OutputPattern + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SealedSecretsConfig. +func (in *SealedSecretsConfig) DeepCopy() *SealedSecretsConfig { + if in == nil { + return nil + } + out := new(SealedSecretsConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SealingConfig) DeepCopyInto(out *SealingConfig) { + *out = *in + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = (*in).DeepCopy() + } + if in.SecretSets != nil { + in, out := &in.SecretSets, &out.SecretSets + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.CertFile != nil { + in, out := &in.CertFile, &out.CertFile + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SealingConfig. +func (in *SealingConfig) DeepCopy() *SealingConfig { + if in == nil { + return nil + } + out := new(SealingConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretSet) DeepCopyInto(out *SecretSet) { + *out = *in + if in.Vars != nil { + in, out := &in.Vars, &out.Vars + *out = make([]*VarsSource, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(VarsSource) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretSet. +func (in *SecretSet) DeepCopy() *SecretSet { + if in == nil { + return nil + } + out := new(SecretSet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretsConfig) DeepCopyInto(out *SecretsConfig) { + *out = *in + if in.SealedSecrets != nil { + in, out := &in.SealedSecrets, &out.SealedSecrets + *out = new(GlobalSealedSecretsConfig) + (*in).DeepCopyInto(*out) + } + if in.SecretSets != nil { + in, out := &in.SecretSets, &out.SecretSets + *out = make([]SecretSet, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretsConfig. +func (in *SecretsConfig) DeepCopy() *SecretsConfig { + if in == nil { + return nil + } + out := new(SecretsConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in SingleStringOrList) DeepCopyInto(out *SingleStringOrList) { + { + in := &in + *out = make(SingleStringOrList, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleStringOrList. +func (in SingleStringOrList) DeepCopy() SingleStringOrList { + if in == nil { + return nil + } + out := new(SingleStringOrList) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Target) DeepCopyInto(out *Target) { + *out = *in + if in.Context != nil { + in, out := &in.Context, &out.Context + *out = new(string) + **out = **in + } + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = (*in).DeepCopy() + } + if in.SealingConfig != nil { + in, out := &in.SealingConfig, &out.SealingConfig + *out = new(SealingConfig) + (*in).DeepCopyInto(*out) + } + if in.Images != nil { + in, out := &in.Images, &out.Images + *out = make([]FixedImage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Target. +func (in *Target) DeepCopy() *Target { + if in == nil { + return nil + } + out := new(Target) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VarsSource) DeepCopyInto(out *VarsSource) { + *out = *in + if in.IgnoreMissing != nil { + in, out := &in.IgnoreMissing, &out.IgnoreMissing + *out = new(bool) + **out = **in + } + if in.NoOverride != nil { + in, out := &in.NoOverride, &out.NoOverride + *out = new(bool) + **out = **in + } + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = (*in).DeepCopy() + } + if in.File != nil { + in, out := &in.File, &out.File + *out = new(string) + **out = **in + } + if in.Git != nil { + in, out := &in.Git, &out.Git + *out = new(VarsSourceGit) + (*in).DeepCopyInto(*out) + } + if in.ClusterConfigMap != nil { + in, out := &in.ClusterConfigMap, &out.ClusterConfigMap + *out = new(VarsSourceClusterConfigMapOrSecret) + (*in).DeepCopyInto(*out) + } + if in.ClusterSecret != nil { + in, out := &in.ClusterSecret, &out.ClusterSecret + *out = new(VarsSourceClusterConfigMapOrSecret) + (*in).DeepCopyInto(*out) + } + if in.SystemEnvVars != nil { + in, out := &in.SystemEnvVars, &out.SystemEnvVars + *out = (*in).DeepCopy() + } + if in.Http != nil { + in, out := &in.Http, &out.Http + *out = new(VarsSourceHttp) + (*in).DeepCopyInto(*out) + } + if in.AwsSecretsManager != nil { + in, out := &in.AwsSecretsManager, &out.AwsSecretsManager + *out = new(VarsSourceAwsSecretsManager) + (*in).DeepCopyInto(*out) + } + if in.Vault != nil { + in, out := &in.Vault, &out.Vault + *out = new(VarsSourceVault) + **out = **in + } + if in.RenderedVars != nil { + in, out := &in.RenderedVars, &out.RenderedVars + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VarsSource. +func (in *VarsSource) DeepCopy() *VarsSource { + if in == nil { + return nil + } + out := new(VarsSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VarsSourceAwsSecretsManager) DeepCopyInto(out *VarsSourceAwsSecretsManager) { + *out = *in + if in.Region != nil { + in, out := &in.Region, &out.Region + *out = new(string) + **out = **in + } + if in.Profile != nil { + in, out := &in.Profile, &out.Profile + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VarsSourceAwsSecretsManager. +func (in *VarsSourceAwsSecretsManager) DeepCopy() *VarsSourceAwsSecretsManager { + if in == nil { + return nil + } + out := new(VarsSourceAwsSecretsManager) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VarsSourceClusterConfigMapOrSecret) DeepCopyInto(out *VarsSourceClusterConfigMapOrSecret) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *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 VarsSourceClusterConfigMapOrSecret. +func (in *VarsSourceClusterConfigMapOrSecret) DeepCopy() *VarsSourceClusterConfigMapOrSecret { + if in == nil { + return nil + } + out := new(VarsSourceClusterConfigMapOrSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VarsSourceGit) DeepCopyInto(out *VarsSourceGit) { + *out = *in + in.Url.DeepCopyInto(&out.Url) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VarsSourceGit. +func (in *VarsSourceGit) DeepCopy() *VarsSourceGit { + if in == nil { + return nil + } + out := new(VarsSourceGit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VarsSourceHttp) DeepCopyInto(out *VarsSourceHttp) { + *out = *in + in.Url.DeepCopyInto(&out.Url) + if in.Method != nil { + in, out := &in.Method, &out.Method + *out = new(string) + **out = **in + } + if in.Body != nil { + in, out := &in.Body, &out.Body + *out = new(string) + **out = **in + } + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.JsonPath != nil { + in, out := &in.JsonPath, &out.JsonPath + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VarsSourceHttp. +func (in *VarsSourceHttp) DeepCopy() *VarsSourceHttp { + if in == nil { + return nil + } + out := new(VarsSourceHttp) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VarsSourceVault) DeepCopyInto(out *VarsSourceVault) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VarsSourceVault. +func (in *VarsSourceVault) DeepCopy() *VarsSourceVault { + if in == nil { + return nil + } + out := new(VarsSourceVault) + in.DeepCopyInto(out) + return out +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new YamlUrl. +func (in *YamlUrl) DeepCopy() *YamlUrl { + if in == nil { + return nil + } + out := new(YamlUrl) + in.DeepCopyInto(out) + return out +} From 011e330c56767a9259cdd2617a80d8876fcc9a41 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 15:34:30 +0200 Subject: [PATCH 0986/2268] tests: Add controller tests --- e2e/default_clusters.go | 3 + e2e/gitops_test.go | 609 ++++++++++++++++++++++++++++++++++++++++ e2e/utils.go | 15 +- 3 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 e2e/gitops_test.go diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 373957a89..8765afb63 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -22,6 +22,9 @@ func init() { return } + defaultCluster1.CRDDirectoryPaths = []string{"../config/crd/bases"} + defaultCluster2.CRDDirectoryPaths = []string{"../config/crd/bases"} + var wg sync.WaitGroup wg.Add(2) go func() { diff --git a/e2e/gitops_test.go b/e2e/gitops_test.go new file mode 100644 index 000000000..56d1cdfe0 --- /dev/null +++ b/e2e/gitops_test.go @@ -0,0 +1,609 @@ +package e2e + +import ( + "context" + "encoding/json" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + types2 "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "strings" + "testing" + "time" + + . "github.com/onsi/gomega" +) + +const ( + timeout = time.Second * 300 + interval = time.Second * 5 +) + +func startKluctlController(t *testing.T) context.CancelFunc { + ctx, ctxCancel := context.WithCancel(context.Background()) + args := []string{ + "controller", + } + done := make(chan struct{}) + go func() { + _, _, err := test_utils.KluctlExecute(t, ctx, args...) + if err != nil { + t.Error(err) + } + close(done) + }() + + cancel := func() { + ctxCancel() + <-done + } + + t.Cleanup(cancel) + return cancel +} + +func waitForReconcile(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey) { + g := gomega.NewWithT(t) + + var kd kluctlv1.KluctlDeployment + err := k.Client.Get(context.TODO(), key, &kd) + assert.NoError(t, err) + + var lastReconcileTime metav1.Time + if kd.Status.LastDeployResult != nil { + lastReconcileTime = kd.Status.LastDeployResult.Command.StartTime + } + g.Eventually(func() bool { + err = k.Client.Get(context.TODO(), key, &kd) + g.Expect(err).To(Succeed()) + if kd.Status.LastDeployResult == nil { + return false + } + return kd.Status.LastDeployResult.Command.StartTime != lastReconcileTime + }, timeout, time.Second).Should(BeTrue()) +} + +func waitForCommit(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey, commit string) { + g := gomega.NewWithT(t) + + g.Eventually(func() bool { + var obj kluctlv1.KluctlDeployment + _ = k.Client.Get(context.Background(), key, &obj) + if obj.Status.LastDeployResult == nil { + return false + } + return obj.Status.LastDeployResult.GitInfo.Commit == commit + }, timeout, time.Second).Should(BeTrue()) +} + +func createKluctlDeployment(t *testing.T, p *test_utils.TestProject, k *test_utils.EnvTestCluster, target string, args map[string]any) client.ObjectKey { + gitopsNs := p.TestSlug() + "-gitops" + createNamespace(t, k, gitopsNs) + + jargs, err := json.Marshal(args) + if err != nil { + t.Fatal(err) + } + + kluctlDeployment := &kluctlv1.KluctlDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: p.TestSlug(), + Namespace: gitopsNs, + }, + Spec: kluctlv1.KluctlDeploymentSpec{ + Interval: metav1.Duration{Duration: interval}, + Timeout: &metav1.Duration{Duration: timeout}, + Target: &target, + Args: runtime.RawExtension{ + Raw: jargs, + }, + Source: kluctlv1.ProjectSource{ + URL: *types2.ParseGitUrlMust(p.GitUrl()), + }, + }, + } + + err = k.Client.Create(context.Background(), kluctlDeployment) + if err != nil { + t.Fatal(err) + } + + return client.ObjectKeyFromObject(kluctlDeployment) +} + +func TestGitOpsFieldManager(t *testing.T) { + g := NewWithT(t) + + startKluctlController(t) + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("target1", nil) + p.AddKustomizeDeployment("d1", []test_utils.KustomizeResource{ + {Name: "cm1.yaml", Content: uo.FromStringMust(`apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 + namespace: "{{ args.namespace }}" +data: + k1: v1 + k2: "{{ args.k2 + 1 }}" +`)}, + }, nil) + + key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + "namespace": p.TestSlug(), + "k2": 42, + }) + + t.Run("initial deployment", func(t *testing.T) { + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + + var kd kluctlv1.KluctlDeployment + err := k.Client.Get(context.TODO(), key, &kd) + g.Expect(err).To(Succeed()) + + kd.Spec.DeployInterval = &kluctlv1.SafeDuration{Duration: metav1.Duration{Duration: interval}} + err = k.Client.Update(context.TODO(), &kd) + g.Expect(err).To(Succeed()) + + cm := &corev1.ConfigMap{} + + t.Run("cm1 is deployed", func(t *testing.T) { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + g.Expect(cm.Data).To(HaveKeyWithValue("k1", "v1")) + g.Expect(cm.Data).To(HaveKeyWithValue("k2", "43")) + }) + + t.Run("cm1 is modified and restored", func(t *testing.T) { + cm.Data["k1"] = "v2" + err := k.Client.Update(context.TODO(), cm, client.FieldOwner("kubectl")) + g.Expect(err).To(Succeed()) + + g.Eventually(func() bool { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + return cm.Data["k1"] == "v1" + }, timeout, time.Second).Should(BeTrue()) + }) + + t.Run("cm1 gets a key added which is not modified by the controller", func(t *testing.T) { + cm.Data["k1"] = "v2" + cm.Data["k3"] = "v3" + err := k.Client.Update(context.TODO(), cm, client.FieldOwner("kubectl")) + g.Expect(err).To(Succeed()) + + g.Eventually(func() bool { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + return cm.Data["k1"] == "v1" + }, timeout, time.Second).Should(BeTrue()) + + g.Expect(cm.Data).To(HaveKeyWithValue("k3", "v3")) + }) + + t.Run("cm1 gets modified with another field manager", func(t *testing.T) { + patch := client.MergeFrom(cm.DeepCopy()) + cm.Data["k1"] = "v2" + + err := k.Client.Patch(context.TODO(), cm, patch, client.FieldOwner("test-field-manager")) + g.Expect(err).To(Succeed()) + + for i := 0; i < 2; i++ { + waitForReconcile(t, k, key) + } + + err = k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + g.Expect(cm.Data).To(HaveKeyWithValue("k1", "v2")) + }) + + err = k.Client.Get(context.TODO(), key, &kd) + g.Expect(err).To(Succeed()) + + kd.Spec.ForceApply = true + err = k.Client.Update(context.TODO(), &kd) + g.Expect(err).To(Succeed()) + + t.Run("forceApply is true and cm1 gets restored even with another field manager", func(t *testing.T) { + patch := client.MergeFrom(cm.DeepCopy()) + cm.Data["k1"] = "v2" + + err := k.Client.Patch(context.TODO(), cm, patch, client.FieldOwner("test-field-manager")) + g.Expect(err).To(Succeed()) + + g.Eventually(func() bool { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + return cm.Data["k1"] == "v1" + }, timeout, time.Second).Should(BeTrue()) + }) +} + +func TestKluctlDeploymentReconciler_Helm(t *testing.T) { + g := NewWithT(t) + startKluctlController(t) + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("target1", nil) + + repoUrl := test_utils.CreateHelmRepo(t, []test_utils.RepoChart{ + {ChartName: "test-chart1", Version: "0.1.0"}, + }, "", "") + repoUrlWithCreds := test_utils.CreateHelmRepo(t, []test_utils.RepoChart{ + {ChartName: "test-chart2", Version: "0.1.0"}, + }, "test-user", "test-password") + ociRepoUrlWithCreds := test_utils.CreateOciRepo(t, []test_utils.RepoChart{ + {ChartName: "test-chart3", Version: "0.1.0"}, + }, "test-user", "test-password") + + p.AddHelmDeployment("d1", repoUrl, "test-chart1", "0.1.0", "test-helm-1", p.TestSlug(), nil) + p.UpdateYaml("d1/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipPrePull") + return nil + }, "") + + key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + "namespace": p.TestSlug(), + }) + + waitForCommit(t, k, key, getHeadRevision(t, p)) + + cm := &corev1.ConfigMap{} + + t.Run("chart got deployed", func(t *testing.T) { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "test-helm-1-test-chart1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + g.Expect(cm.Data).To(HaveKeyWithValue("a", "v1")) + }) + + p.AddHelmDeployment("d2", repoUrlWithCreds, "test-chart2", "0.1.0", "test-helm-2", p.TestSlug(), nil) + p.UpdateYaml("d2/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipPrePull") + return nil + }, "") + + kd := &kluctlv1.KluctlDeployment{} + + t.Run("chart with credentials fails with 401", func(t *testing.T) { + g.Eventually(func() bool { + err := k.Client.Get(context.TODO(), key, kd) + g.Expect(err).To(Succeed()) + for _, c := range kd.Status.Conditions { + _ = c + if c.Type == "Ready" && c.Reason == "PrepareFailed" && strings.Contains(c.Message, "401 Unauthorized") { + return true + } + } + return false + }, timeout, time.Second).Should(BeTrue()) + }) + + credsSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: key.Namespace, + Name: "helm-secrets-1", + }, + Data: map[string][]byte{ + "url": []byte(repoUrlWithCreds), + "username": []byte("test-user"), + "password": []byte("test-password"), + }, + } + err := k.Client.Create(context.TODO(), credsSecret) + g.Expect(err).To(Succeed()) + + kd.Spec.HelmCredentials = append(kd.Spec.HelmCredentials, kluctlv1.HelmCredentials{SecretRef: kluctlv1.LocalObjectReference{Name: "helm-secrets-1"}}) + err = k.Client.Update(context.TODO(), kd) + g.Expect(err).To(Succeed()) + + t.Run("chart with credentials succeeds", func(t *testing.T) { + g.Eventually(func() bool { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "test-helm-2-test-chart2", + Namespace: p.TestSlug(), + }, cm) + if err != nil { + return false + } + g.Expect(cm.Data).To(HaveKeyWithValue("a", "v1")) + return true + }, timeout, time.Second).Should(BeTrue()) + }) + + p.AddHelmDeployment("d3", ociRepoUrlWithCreds, "test-chart3", "0.1.0", "test-helm-3", p.TestSlug(), nil) + p.UpdateYaml("d3/helm-chart.yaml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(true, "helmChart", "skipPrePull") + return nil + }, "") + + t.Run("OCI chart with credentials fails with 401", func(t *testing.T) { + g.Eventually(func() bool { + err = k.Client.Get(context.TODO(), key, kd) + g.Expect(err).To(Succeed()) + for _, c := range kd.Status.Conditions { + _ = c + if c.Type == "Ready" && c.Reason == "PrepareFailed" && strings.Contains(c.Message, "401 Unauthorized") { + return true + } + } + return false + }, timeout, time.Second).Should(BeTrue()) + }) + + /* + TODO enable this when Kluctl supports OCI authentication + url, err := url2.Parse(ociRepoUrlWithCreds) + g.Expect(err).To(Succeed()) + + dockerJson := map[string]any{ + "auths": map[string]any{ + url.Host: map[string]any{ + "username": "test-user", + "password": "test-password,", + "auth": base64.StdEncoding.EncodeToString([]byte("test-user:test-password")), + }, + }, + } + dockerJsonStr, err := json.Marshal(dockerJson) + g.Expect(err).To(Succeed()) + + credsSecret2 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: "helm-secrets-2", + }, + Data: map[string][]byte{ + "url": []byte(ociRepoUrlWithCreds), + ".dockerconfigjson": dockerJsonStr, + }, + } + err = k.Client.Create(context.TODO(), credsSecret2) + g.Expect(err).To(Succeed()) + + kluctlDeployment.Spec.HelmCredentials = append(kluctlDeployment.Spec.HelmCredentials, flux_utils.LocalObjectReference{Name: "helm-secrets-2"}) + err = k.Client.Update(context.TODO(), kluctlDeployment) + g.Expect(err).To(Succeed()) + + t.Run("OCI chart with credentials succeeds", func(t *testing.T) { + g.Eventually(func() bool { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "test-helm-3-test-chart3", + Namespace: namespace, + }, cm) + if err != nil { + return false + } + g.Expect(cm.Data).To(HaveKeyWithValue("a", "v1")) + return true + }, timeout, time.Second).Should(BeTrue()) + })*/ +} + +func TestKluctlDeploymentReconciler_Prune(t *testing.T) { + g := NewWithT(t) + startKluctlController(t) + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("target1", nil) + + p.AddKustomizeDeployment("d1", []test_utils.KustomizeResource{ + {Name: "cm1.yaml", Content: uo.FromStringMust(`apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 + namespace: "{{ args.namespace }}" +data: + k1: v1 +`)}, + }, nil) + p.AddKustomizeDeployment("d2", []test_utils.KustomizeResource{ + {Name: "cm2.yaml", Content: uo.FromStringMust(`apiVersion: v1 +kind: ConfigMap +metadata: + name: cm2 + namespace: "{{ args.namespace }}" +data: + k1: v1 +`)}, + }, nil) + + key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + "namespace": p.TestSlug(), + }) + + waitForCommit(t, k, key, getHeadRevision(t, p)) + + cm := &corev1.ConfigMap{} + + t.Run("cm1 and cm2 got deployed", func(t *testing.T) { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + err = k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm2", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + }) + + p.UpdateDeploymentYaml("", func(o *uo.UnstructuredObject) error { + _ = o.RemoveNestedField("deployments", 1) + return nil + }) + + g.Eventually(func() bool { + var obj kluctlv1.KluctlDeployment + _ = k.Client.Get(context.Background(), key, &obj) + if obj.Status.LastDeployResult == nil { + return false + } + return obj.Status.LastDeployResult.GitInfo.Commit == getHeadRevision(t, p) + }, timeout, time.Second).Should(BeTrue()) + + t.Run("cm1 and cm2 were not deleted", func(t *testing.T) { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + err = k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm2", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + }) + + kd := &kluctlv1.KluctlDeployment{} + g.Expect(k.Client.Get(context.TODO(), key, kd)).To(Succeed()) + kd.Spec.Prune = true + + g.Expect(k.Client.Update(context.TODO(), kd)).To(Succeed()) + g.Eventually(func() bool { + var obj kluctlv1.KluctlDeployment + _ = k.Client.Get(context.Background(), key, &obj) + if obj.Status.LastDeployResult == nil { + return false + } + return obj.Status.ObservedGeneration == obj.Generation && obj.Status.LastDeployResult.GitInfo.Commit == getHeadRevision(t, p) + }, timeout, time.Second).Should(BeTrue()) + + t.Run("cm1 did not get deleted and cm2 got deleted", func(t *testing.T) { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + err = k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm2", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(MatchError("configmaps \"cm2\" not found")) + }) +} + +func doTestDelete(t *testing.T, delete bool) { + g := NewWithT(t) + startKluctlController(t) + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("target1", nil) + + p.AddKustomizeDeployment("d1", []test_utils.KustomizeResource{ + {Name: "cm1.yaml", Content: uo.FromStringMust(`apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 + namespace: "{{ args.namespace }}" +data: + k1: v1 +`)}, + }, nil) + + key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + "namespace": p.TestSlug(), + }) + + kd := &kluctlv1.KluctlDeployment{} + err := k.Client.Get(context.TODO(), key, kd) + g.Expect(err).To(Succeed()) + + kd.Spec.Delete = delete + g.Expect(k.Client.Update(context.TODO(), kd)).To(Succeed()) + + waitForCommit(t, k, key, getHeadRevision(t, p)) + + cm := &corev1.ConfigMap{} + + t.Run("cm1 got deployed", func(t *testing.T) { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + }) + + g.Expect(k.Client.Delete(context.TODO(), kd)).To(Succeed()) + + g.Eventually(func() bool { + var obj kluctlv1.KluctlDeployment + err := k.Client.Get(context.Background(), key, &obj) + if err == nil { + return false + } + if !errors.IsNotFound(err) { + return false + } + return true + }, timeout, time.Second).Should(BeTrue()) + + if delete { + t.Run("cm1 was deleted", func(t *testing.T) { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(MatchError("configmaps \"cm1\" not found")) + }) + } else { + t.Run("cm1 was not deleted", func(t *testing.T) { + err := k.Client.Get(context.TODO(), client.ObjectKey{ + Name: "cm1", + Namespace: p.TestSlug(), + }, cm) + g.Expect(err).To(Succeed()) + }) + } +} + +func TestKluctlDeploymentReconciler_Delete_True(t *testing.T) { + doTestDelete(t, true) +} + +func TestKluctlDeploymentReconciler_Delete_False(t *testing.T) { + doTestDelete(t, false) +} diff --git a/e2e/utils.go b/e2e/utils.go index 9909bc07a..061402c77 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -15,14 +15,27 @@ import ( ) func createNamespace(t *testing.T, k *test_utils.EnvTestCluster, namespace string) { + r := k.DynamicClient.Resource(v1.SchemeGroupVersion.WithResource("namespaces")) + if _, err := r.Get(context.Background(), namespace, metav1.GetOptions{}); err == nil { + return + } + var ns unstructured.Unstructured ns.SetName(namespace) + _, err := r.Create(context.Background(), &ns, metav1.CreateOptions{}) - _, err := k.DynamicClient.Resource(v1.SchemeGroupVersion.WithResource("namespaces")).Create(context.Background(), &ns, metav1.CreateOptions{}) + if err != nil { + t.Fatal(err) + } +} +func getHeadRevision(t *testing.T, p *test_utils.TestProject) string { + r := p.GetGitRepo() + h, err := r.Head() if err != nil { t.Fatal(err) } + return h.Hash().String() } func assertObjectExists(t *testing.T, k *test_utils.EnvTestCluster, gvr schema.GroupVersionResource, namespace string, name string) *uo.UnstructuredObject { From 676725d274ad2225e831a0d13270f574f2df44db Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 15:34:57 +0200 Subject: [PATCH 0987/2268] chore: Run go mod tidy --- go.mod | 24 ++++++++++++------------ go.sum | 26 ++++++++++++-------------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index a815bcfcb..fb641e2f3 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/bitnami-labs/sealed-secrets v0.21.0 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.2+incompatible - github.com/fluxcd/pkg/kustomize v1.1.1 github.com/go-playground/validator/v10 v10.13.0 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 @@ -53,18 +52,27 @@ require ( require ( filippo.io/age v1.1.1 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2/config v1.18.25 + github.com/aws/aws-sdk-go-v2/credentials v1.13.24 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.8 + github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 + github.com/aws/smithy-go v1.13.5 + github.com/dimchansky/utfbom v1.1.1 github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.4 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 + github.com/hashicorp/go-retryablehttp v0.7.1 github.com/huandu/xstrings v1.4.0 github.com/onsi/gomega v1.27.7 github.com/otiai10/copy v1.11.0 + github.com/prometheus/client_golang v1.15.1 github.com/sergi/go-diff v1.3.1 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 + sigs.k8s.io/cli-utils v0.34.0 sigs.k8s.io/controller-runtime v0.15.0-beta.0 sigs.k8s.io/kustomize/api v0.13.4 sigs.k8s.io/yaml v1.3.0 @@ -76,8 +84,6 @@ require ( cloud.google.com/go/iam v0.13.0 // indirect cloud.google.com/go/kms v1.10.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.5.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 // indirect @@ -92,7 +98,6 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.24 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect @@ -101,8 +106,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 // indirect - github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect @@ -119,22 +122,19 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/drone/envsubst v1.0.3 // indirect github.com/emicklei/go-restful/v3 v3.10.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect - github.com/fluxcd/go-git/v5 v5.0.0-20221219190809-2e5c9d01cfc4 // indirect - github.com/fluxcd/pkg/apis/kustomize v1.0.0 // indirect - github.com/fluxcd/pkg/sourceignore v0.3.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.22.3 // indirect @@ -158,7 +158,6 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.4.0 // indirect - github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect @@ -198,7 +197,6 @@ require ( github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.15.1 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect @@ -225,6 +223,8 @@ require ( go.opentelemetry.io/otel/trace v1.14.0 // indirect go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.24.0 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/go.sum b/go.sum index 40eb65340..e6e48f5e8 100644 --- a/go.sum +++ b/go.sum @@ -94,14 +94,12 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5 github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -150,6 +148,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jely github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +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= @@ -212,6 +212,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE= @@ -231,8 +233,6 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= -github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -258,14 +258,6 @@ 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/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fluxcd/go-git/v5 v5.0.0-20221219190809-2e5c9d01cfc4 h1:Gm5sGGk+/Wq6RhX4xpCZ2IqjDp5XkjlhENaAuAlpdKc= -github.com/fluxcd/go-git/v5 v5.0.0-20221219190809-2e5c9d01cfc4/go.mod h1:raWgfUV7lDQVXp4QXUaeNNJkRVKz97UQuF+0kdY7Vmo= -github.com/fluxcd/pkg/apis/kustomize v1.0.0 h1:5T2b/mRZiGWtP7fvSU8gZOApIc06H6SdLX3MlsE6LRo= -github.com/fluxcd/pkg/apis/kustomize v1.0.0/go.mod h1:XaDYlKxrf9D2zZWcZ0BnSIqGtcm8mdNtJGzZWYjCnQo= -github.com/fluxcd/pkg/kustomize v1.1.1 h1:hYFJGi+fiaecY4gXvx52fumlvDEq/1RdFbaev67oBhE= -github.com/fluxcd/pkg/kustomize v1.1.1/go.mod h1:i+Z9iPAoSz28oH0FmDI73iqZ3oXZxQR2O3HfhdsWhfo= -github.com/fluxcd/pkg/sourceignore v0.3.3 h1:Ue29JAuPECEYdvIqdpXpQaDxpeySn7amarLArp7XoIs= -github.com/fluxcd/pkg/sourceignore v0.3.3/go.mod h1:yuJzKggph0Bdbk9LgXjJQhvJZSTJV/1vS7mJuB7mPa0= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= @@ -304,6 +296,7 @@ github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV 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.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= @@ -684,7 +677,6 @@ github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= @@ -822,7 +814,6 @@ github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RV github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -874,12 +865,16 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 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.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 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/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1183,6 +1178,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= @@ -1383,6 +1379,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= +sigs.k8s.io/cli-utils v0.34.0/go.mod h1:EXyMwPMu9OL+LRnj0JEMsGG/fRvbgFadcVlSnE8RhFs= sigs.k8s.io/controller-runtime v0.15.0-beta.0 h1:pkhYMops8jZrVuI0kBHeF6q9UVu1JljIGGG4Ox5ZJmk= sigs.k8s.io/controller-runtime v0.15.0-beta.0/go.mod h1:YUTa+du31rqOu4mJaijiuhGFax9ecCJgO/v0/yW09gE= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= From 07fd5e2fd17397f75c2e1eb0d66e47bd85f55de8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 15:43:36 +0200 Subject: [PATCH 0988/2268] chore: Run make replace-commands-help --- docs/reference/commands/controller.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/reference/commands/controller.md b/docs/reference/commands/controller.md index ff5fe4938..a7584c0b7 100644 --- a/docs/reference/commands/controller.md +++ b/docs/reference/commands/controller.md @@ -13,6 +13,9 @@ description: > Usage: kluctl controller [flags] +Run the Kluctl controller +This command will run the Kluctl Controller. This is usually meant to be run inside a cluster and not from your local machine. + ## Arguments @@ -20,6 +23,15 @@ Usage: kluctl controller [flags] The following arguments are available: ``` +Controller: + Controller arguments. + + --default-service-account string Default service account used for impersonation. + --dry-run Run all deployments in dryRun=true mode. + --health-probe-bind-address string The address the probe endpoint binds to. (default ":8081") + --leader-elect Enable leader election for controller manager. Enabling this will + ensure there is only one active controller manager. + --metrics-bind-address string The address the metric endpoint binds to. (default ":8080") ``` From 805f2bfc66f1550a86dadaa59824eb39846a86ef Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 15:58:48 +0200 Subject: [PATCH 0989/2268] ci: Update Makefile to be controller-gen based --- Makefile | 231 +++++++++++++++++++++++++++---------------------------- 1 file changed, 113 insertions(+), 118 deletions(-) diff --git a/Makefile b/Makefile index d8f9ce4aa..aa370ad74 100644 --- a/Makefile +++ b/Makefile @@ -13,144 +13,139 @@ ifeq ($(GOOS), linux) RACE=-race endif -GOCMD=go -GOTEST=$(GOCMD) test -GOVET=$(GOCMD) vet -BINARY_NAME=kluctl$(EXE) -TEST_BINARY_NAME=kluctl-e2e$(EXE) -EXPORT_RESULT?=false - -# If gobin not set, create one on ./build and add to path. +# Image URL to use all building/pushing image targets +IMG ?= kluctl/kluctl:latest +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.26.1 + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) -GOBIN=$(BUILD_DIR)/gobin +GOBIN=$(shell go env GOPATH)/bin else GOBIN=$(shell go env GOBIN) endif -export PATH:=$(GOBIN):${PATH} -# Architecture to use envtest with -ENVTEST_ARCH ?= amd64 +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec -GREEN := $(shell tput -Txterm setaf 2) -YELLOW := $(shell tput -Txterm setaf 3) -WHITE := $(shell tput -Txterm setaf 7) -CYAN := $(shell tput -Txterm setaf 6) -RESET := $(shell tput -Txterm sgr0) +.PHONY: all +all: build -.PHONY: all test build +##@ General -all: help +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php -## Build: -build: build-go ## Run the complete build pipeline +.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) -build-go: ## Build your project and put the output binary in ./bin/ - mkdir -p ./bin - $(GOCMD) build -o ./bin/$(BINARY_NAME) +##@ Development -clean: ## Remove build related file - rm -fr ./bin - rm -fr ./out - rm -fr ./reports - rm -fr ./download-python +.PHONY: manifests +manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases -## Envtest setup -# Repository root based on Git metadata -REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel) -BUILD_DIR := $(REPOSITORY_ROOT)/build -ENVTEST = $(GOBIN)/setup-envtest -.PHONY: envtest -setup-envtest: ## Download envtest-setup locally if necessary. - $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." -# Download the envtest binaries to testbin -ENVTEST_ASSETS_DIR=$(BUILD_DIR)/testbin -ENVTEST_KUBERNETES_VERSION?=latest -install-envtest: setup-envtest - mkdir -p ${ENVTEST_ASSETS_DIR} - $(ENVTEST) use $(ENVTEST_KUBERNETES_VERSION) --arch=$(ENVTEST_ARCH) --bin-dir=$(ENVTEST_ASSETS_DIR) +.PHONY: fmt +fmt: ## Run go fmt against code. + go fmt ./... -## Test: -test: test-unit test-e2e ## Runs the complete test suite +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... -KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_KUBERNETES_VERSION) --bin-dir=$(ENVTEST_ASSETS_DIR) -p path)" -test-e2e: test-e2e-build test-e2e-pre-built ## Runs the end to end tests +.PHONY: test +test: manifests generate fmt vet envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(RACE) ./... -coverprofile cover.out -test-e2e-build: ## Builds the end to end tests - $(GOCMD) test $(RACE) -c ./e2e -o ./bin/$(TEST_BINARY_NAME) +replace-commands-help: ## Replace commands help in docs + go run ./internal/replace-commands-help --docs-dir ./docs/reference/commands -test-e2e-pre-built: install-envtest - KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) ./bin/$(TEST_BINARY_NAME) -test.v +MARKDOWN_LINK_CHECK_VERSION=3.11.2 +markdown-link-check: ## Check markdown files for dead links + find . -name '*.md' | xargs docker run -v ${PWD}:/tmp:ro --rm -i -w /tmp ghcr.io/tcort/markdown-link-check:$(MARKDOWN_LINK_CHECK_VERSION) -test-unit: ## Run the unit tests of the project -ifeq ($(EXPORT_RESULT), true) - mkdir -p reports/test-unit - GO111MODULE=off $(GOCMD) get -u github.com/jstemmer/go-junit-report - $(eval OUTPUT_OPTIONS = | tee /dev/tty | go-junit-report -set-exit-code > reports/test-unit/junit-report.xml) -endif - $(GOTEST) -v $(RACE) $(shell go list ./... | grep -v 'v2/e2e') $(OUTPUT_OPTIONS) - -coverage-unit: ## Run the unit tests of the project and export the coverage - $(GOTEST) -cover -covermode=count -coverprofile=reports/coverage-unit/profile.cov $(shell go list ./... | grep -v /e2e/) - $(GOCMD) tool cover -func profile.cov -ifeq ($(EXPORT_RESULT), true) - mkdir -p reports/coverage-unit - GO111MODULE=off $(GOCMD) get -u github.com/AlekSi/gocov-xml - GO111MODULE=off $(GOCMD) get -u github.com/axw/gocov/gocov - gocov convert reports/coverage-unit/profile.cov | gocov-xml > reports/coverage-unit/coverage.xml -endif +##@ Build -## Lint: -lint: lint-go ## Run all available linters +.PHONY: build +build: manifests generate fmt vet ## Build manager binary. + go build -o bin/kluctl$(EXE) cmd/main.go -lint-go: ## Use golintci-lint on your project -ifeq ($(EXPORT_RESULT), true) - mkdir -p reports/lint-go - $(eval OUTPUT_OPTIONS = $(shell echo "--out-format checkstyle ./... | tee /dev/tty > reports/lint-go/checkstyle-report.xml" )) -else - $(eval OUTPUT_OPTIONS = $(shell echo "")) -endif - docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:latest-alpine golangci-lint run $(OUTPUT_OPTIONS) +.PHONY: run +run: manifests generate fmt vet ## Run a controller from your host. + go run ./cmd/main.go -replace-commands-help: ## Replace commands help in docs - $(GOCMD) run ./internal/replace-commands-help --docs-dir ./docs/reference/commands -MARKDOWN_LINK_CHECK_VERSION=3.10.3 # warning, 3.11.x is broken -markdown-link-check: ## Check markdown files for dead links - find . -name '*.md' | xargs docker run -v ${PWD}:/tmp:ro --rm -i -w /tmp ghcr.io/tcort/markdown-link-check:$(MARKDOWN_LINK_CHECK_VERSION) +##@ Deployment -## Release: -version: ## Write next version into version file - $(GOCMD) install github.com/bvieira/sv4git/v2/cmd/git-sv@v2.7.0 - $(eval KLUCTL_VERSION:=$(shell git sv next-version)) - sed -ibak "s/0.0.0/$(KLUCTL_VERSION)/g" pkg/version/version.go - -changelog: ## Generating changelog - git sv changelog -n 1 > CHANGELOG.md - -## Help: -help: ## Show this help. - @echo '' - @echo 'Usage:' - @echo ' ${YELLOW}make${RESET} ${GREEN}${RESET}' - @echo '' - @echo 'Targets:' - @awk 'BEGIN {FS = ":.*?## "} { \ - if (/^[0-9a-zA-Z_-]+:.*?##.*$$/) {printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n", $$1, $$2} \ - else if (/^## .*$$/) {printf " ${CYAN}%s${RESET}\n", substr($$1,4)} \ - }' $(MAKEFILE_LIST) - -## Tools -# go-install-tool will 'go install' any package $2 and install it to $1. -PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) -define go-install-tool -@[ -f $(1) ] || { \ -set -e ;\ -TMP_DIR=$$(mktemp -d) ;\ -cd $$TMP_DIR ;\ -go mod init tmp ;\ -echo "Downloading $(2)" ;\ -GOBIN=$(GOBIN) go install $(2) ;\ -rm -rf $$TMP_DIR ;\ -} -endef +ifndef ignore-not-found + ignore-not-found = false +endif + +.PHONY: install +install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | kubectl apply -f - + +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: deploy +deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default | kubectl apply -f - + +.PHONY: undeploy +undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - + +##@ Build Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Tool Binaries +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest + +## Tool Versions +KUSTOMIZE_VERSION ?= v5.0.3 +CONTROLLER_TOOLS_VERSION ?= v0.12.0 + +KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" +.PHONY: kustomize +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. +$(KUSTOMIZE): $(LOCALBIN) + @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ + echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ + rm -rf $(LOCALBIN)/kustomize; \ + fi + test -s $(LOCALBIN)/kustomize || { curl -Ss $(KUSTOMIZE_INSTALL_SCRIPT) --output install_kustomize.sh && bash install_kustomize.sh $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); rm install_kustomize.sh; } + +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. +$(CONTROLLER_GEN): $(LOCALBIN) + test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + +.PHONY: envtest +envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. +$(ENVTEST): $(LOCALBIN) + test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest From e9936ce67d1e8053a0f3d11eb667ad13104ffff1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 16:15:56 +0200 Subject: [PATCH 0990/2268] ci: Verify that make generate was called and committed --- .github/workflows/tests.yml | 11 ++++++++++- Makefile | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e23e52c2b..b4d09f840 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ on: - main jobs: - docs-checks: + generate-checks: runs-on: ubuntu-latest steps: - name: Checkout @@ -39,6 +39,15 @@ jobs: git diff exit 1 fi + - name: Verify generated source is up-to-date + run: | + make generate + if [ ! -z "$(git status --porcelain)" ]; then + echo "make generate must be invoked and the result committed" + git status + git diff + exit 1 + fi tests: strategy: diff --git a/Makefile b/Makefile index aa370ad74..28db1bfd4 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and Cust .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + go generate ./... $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." .PHONY: fmt From 8b0eafb86205b6a0758ecce933625c25f5c12a1a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 16:16:07 +0200 Subject: [PATCH 0991/2268] ci: Fix .goreleaser.yaml to use correct main --- .goreleaser.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 43a80732f..8d657a092 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -6,6 +6,7 @@ builds: binary: kluctl env: - CGO_ENABLED=0 + main: ./cmd id: linux goos: - linux From 0415b74d67f77e13232ba810a6782b88c40c2ebb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 17:19:25 +0200 Subject: [PATCH 0992/2268] ci: Add .dockerignore --- .dockerignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..9b1c8b133 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +/dist From 84f09fcf161e6c9e811085dd8f2063eeff9ee2fd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 16:22:16 +0200 Subject: [PATCH 0993/2268] fix: Upgrade to alpine 3.18.0 to fix DNS resolver issues --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f3ee702f7..478b37095 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.17.1 +FROM alpine:3.18.0 RUN apk add ca-certificates From ad6241a2a8b76d0392206e74ea127abb74160078 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 16:22:42 +0200 Subject: [PATCH 0994/2268] fix: Add git to docker images and set HELM_CACHE_HOME=/tmp/helm-cache --- Dockerfile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Dockerfile b/Dockerfile index 478b37095..6a31351b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,5 +2,11 @@ FROM alpine:3.18.0 RUN apk add ca-certificates +# We need git for kustomize to support overlays from git +RUN apk add git + +# Ensure helm is not trying to access / +ENV HELM_CACHE_HOME=/tmp/helm-cache + COPY kluctl /usr/bin/ ENTRYPOINT ["/usr/bin/kluctl"] From ccedfb7ffed23e8736ffa1e40228f94324c49b85 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 16:56:40 +0200 Subject: [PATCH 0995/2268] fix: Run as non-root user --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 6a31351b5..847404a86 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,4 +9,7 @@ RUN apk add git ENV HELM_CACHE_HOME=/tmp/helm-cache COPY kluctl /usr/bin/ + +USER 65532:65532 + ENTRYPOINT ["/usr/bin/kluctl"] From 0a6a5c1a9d554ea872f098812802cb89f187bcb1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 16:57:22 +0200 Subject: [PATCH 0996/2268] chore: Easy/Fast way to deploy to kind --- Dockerfile | 3 ++- Makefile | 8 ++++++++ config/default/kustomization.yaml | 2 +- config/manager/manager.yaml | 4 +++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 847404a86..77958da58 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,8 @@ RUN apk add git # Ensure helm is not trying to access / ENV HELM_CACHE_HOME=/tmp/helm-cache -COPY kluctl /usr/bin/ +ARG BIN_PATH=kluctl +COPY $BIN_PATH /usr/bin/ USER 65532:65532 diff --git a/Makefile b/Makefile index 28db1bfd4..d640b9f2b 100644 --- a/Makefile +++ b/Makefile @@ -110,6 +110,14 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} $(KUSTOMIZE) build config/default | kubectl apply -f - +.PHONY: deploy-kind +deploy-kind: manifests kustomize + GOOS=linux GOARCH=amd64 make build + docker build -t kluctl-kind:latest --build-arg BIN_PATH=./bin/kluctl . + kind load docker-image kluctl-kind:latest + cd config/manager && $(KUSTOMIZE) edit set image controller=kluctl-kind + $(KUSTOMIZE) build config/default | kubectl apply -f - + .PHONY: undeploy undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index fc2356a99..ce073d33f 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -6,7 +6,7 @@ namespace: controller-system # "wordpress" becomes "alices-wordpress". # Note that it should also match with the prefix (text before '-') of the namespace # field above. -namePrefix: controller- +namePrefix: kluctl- # Labels to add to all resources and selectors. #labels: diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index d0a98b913..0eaa207d9 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -67,10 +67,12 @@ spec: # type: RuntimeDefault containers: - command: - - /manager + - kluctl + - controller args: - --leader-elect image: controller:latest + imagePullPolicy: IfNotPresent name: manager securityContext: allowPrivilegeEscalation: false From 01370abdfd5b0b0cd9aef6db8f346a84b8667643 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 May 2023 17:19:12 +0200 Subject: [PATCH 0997/2268] feat: Add full target object to result summary --- pkg/results/result-store-secrets.go | 6 +++--- pkg/results/result-store.go | 4 ++-- pkg/types/result/summary.go | 29 ++++++++++++++++------------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index f918e5b2e..82713e27f 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -229,7 +229,7 @@ func (s *ResultStoreSecrets) cleanupResults(project result.ProjectKey, target re for _, rs := range results { rs := rs - if rs.Target != target { + if rs.TargetKey != target { continue } cnt++ @@ -302,10 +302,10 @@ func (s *ResultStoreSecrets) parseSummary(obj client.Object) (*result.CommandRes func (s *ResultStoreSecrets) filterSummary(summary *result.CommandResultSummary, project *result.ProjectKey) bool { if project != nil { - if project.NormalizedGitUrl != "" && summary.Project.NormalizedGitUrl != project.NormalizedGitUrl { + if project.NormalizedGitUrl != "" && summary.ProjectKey.NormalizedGitUrl != project.NormalizedGitUrl { return false } - if project.SubDir != "" && summary.Project.SubDir != project.SubDir { + if project.SubDir != "" && summary.ProjectKey.SubDir != project.SubDir { return false } } diff --git a/pkg/results/result-store.go b/pkg/results/result-store.go index 46d002d5d..baa5e7313 100644 --- a/pkg/results/result-store.go +++ b/pkg/results/result-store.go @@ -35,10 +35,10 @@ func FilterSummary(x *result.CommandResultSummary, filter *result.ProjectKey) bo if filter == nil { return true } - if x.Project.NormalizedGitUrl != filter.NormalizedGitUrl { + if x.ProjectKey.NormalizedGitUrl != filter.NormalizedGitUrl { return false } - if x.Project.SubDir != filter.SubDir { + if x.ProjectKey.SubDir != filter.SubDir { return false } return true diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index 5cf639762..85327294a 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -1,16 +1,18 @@ package result import ( + "github.com/kluctl/kluctl/v2/pkg/types" "sort" ) type CommandResultSummary struct { - Id string `json:"id"` - Project ProjectKey `json:"project"` - Target TargetKey `json:"target"` - Command CommandInfo `json:"commandInfo"` - GitInfo GitInfo `json:"gitInfo,omitempty"` - ClusterInfo ClusterInfo `json:"clusterInfo,omitempty"` + Id string `json:"id"` + ProjectKey ProjectKey `json:"projectKey"` + TargetKey TargetKey `json:"targetKey"` + Target types.Target `json:"target"` + Command CommandInfo `json:"commandInfo"` + GitInfo GitInfo `json:"gitInfo,omitempty"` + ClusterInfo ClusterInfo `json:"clusterInfo,omitempty"` RenderedObjects int `json:"renderedObjects"` RemoteObjects int `json:"remoteObjects"` @@ -57,8 +59,9 @@ func (cr *CommandResult) BuildSummary() *CommandResultSummary { ret := &CommandResultSummary{ Id: cr.Id, - Project: cr.ProjectKey, - Target: cr.TargetKey, + ProjectKey: cr.ProjectKey, + TargetKey: cr.TargetKey, + Target: cr.Target, Command: cr.Command, GitInfo: cr.GitInfo, ClusterInfo: cr.ClusterInfo, @@ -82,22 +85,22 @@ func (cr *CommandResult) BuildSummary() *CommandResultSummary { func BuildProjectSummaries(summaries []CommandResultSummary) []*ProjectSummary { m := map[ProjectKey]*ProjectSummary{} for _, rs := range summaries { - p, ok := m[rs.Project] + p, ok := m[rs.ProjectKey] if !ok { - p = &ProjectSummary{Project: rs.Project} - m[rs.Project] = p + p = &ProjectSummary{Project: rs.ProjectKey} + m[rs.ProjectKey] = p } var target *TargetSummary for i, t := range p.Targets { - if t.Target == rs.Target { + if t.Target == rs.TargetKey { target = p.Targets[i] break } } if target == nil { target = &TargetSummary{ - Target: rs.Target, + Target: rs.TargetKey, } p.Targets = append(p.Targets, target) } From 6e1a948d1eb7e06893afab573c68912afa7f2bcb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 17 May 2023 12:44:17 +0200 Subject: [PATCH 0998/2268] feat: Let the controller write command results --- cmd/kluctl/commands/cmd_controller.go | 2 +- pkg/controllers/kluctl_project.go | 7 ++++++- pkg/controllers/kluctldeployment_controller.go | 3 +++ pkg/controllers/kluctldeployment_controller_setup.go | 10 +++++++++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cmd/kluctl/commands/cmd_controller.go b/cmd/kluctl/commands/cmd_controller.go index 655a5071d..3e3f9df86 100644 --- a/cmd/kluctl/commands/cmd_controller.go +++ b/cmd/kluctl/commands/cmd_controller.go @@ -112,7 +112,7 @@ func (cmd *controllerCmd) Run(ctx context.Context) error { SshPool: sshPool, } - if err = r.SetupWithManager(mgr, controllers.KluctlDeploymentReconcilerOpts{ + if err = r.SetupWithManager(ctx, mgr, controllers.KluctlDeploymentReconcilerOpts{ HTTPRetry: 9, }); err != nil { setupLog.Error(err, "unable to create controller", "controller", kluctlv1.KluctlDeploymentKind) diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 41396bd18..3eee59d44 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -649,6 +649,12 @@ func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, cmdResult.GitInfo.Ref = pt.pp.obj.Spec.Source.Ref.String() cmdResult.ProjectKey.NormalizedGitUrl = pt.pp.obj.Spec.Source.URL.NormalizedRepoKey() + log.Info(fmt.Sprintf("Writing command result %s", cmdResult.Id)) + err := pt.pp.r.ResultStore.WriteCommandResult(cmdResult) + if err != nil { + log.Error(err, "Writing command result failed") + } + summary := cmdResult.BuildSummary() log.Info(fmt.Sprintf("command finished with err=%v", cmdErr)) @@ -682,7 +688,6 @@ func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, } warning := false - var err error if len(cmdResult.Errors) != 0 { warning = true err = fmt.Errorf("%s failed with %d errors", commandName, len(cmdResult.Errors)) diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 69670165a..cb4cfa953 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -8,6 +8,7 @@ import ( internal_metrics "github.com/kluctl/kluctl/v2/pkg/controllers/internal/metrics" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/results" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" @@ -39,6 +40,8 @@ type KluctlDeploymentReconciler struct { DryRun bool SshPool *ssh_pool.SshPool + + ResultStore results.ResultStore } // KluctlDeploymentReconcilerOpts contains options for the BaseReconciler. diff --git a/pkg/controllers/kluctldeployment_controller_setup.go b/pkg/controllers/kluctldeployment_controller_setup.go index cb6af53f7..61f66cba1 100644 --- a/pkg/controllers/kluctldeployment_controller_setup.go +++ b/pkg/controllers/kluctldeployment_controller_setup.go @@ -1,8 +1,10 @@ package controllers import ( + "context" "github.com/hashicorp/go-retryablehttp" kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + "github.com/kluctl/kluctl/v2/pkg/results" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/predicate" @@ -10,7 +12,7 @@ import ( ) // SetupWithManager sets up the controller with the Manager. -func (r *KluctlDeploymentReconciler) SetupWithManager(mgr ctrl.Manager, opts KluctlDeploymentReconcilerOpts) error { +func (r *KluctlDeploymentReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opts KluctlDeploymentReconcilerOpts) error { // Configure the retryable http client used for fetching artifacts. // By default it retries 10 times within a 3.5 minutes window. httpClient := retryablehttp.NewClient() @@ -20,6 +22,12 @@ func (r *KluctlDeploymentReconciler) SetupWithManager(mgr ctrl.Manager, opts Klu httpClient.Logger = nil r.httpClient = httpClient + resultStore, err := results.NewResultStoreSecrets(ctx, mgr.GetConfig(), "kluctl-results", 10) + if err != nil { + return err + } + r.ResultStore = resultStore + return ctrl.NewControllerManagedBy(mgr). For(&kluctlv1.KluctlDeployment{}, builder.WithPredicates( predicate.Or(predicate.GenerationChangedPredicate{}, ReconcileRequestedPredicate{}, DeployRequestedPredicate{}), From 322350b80a3c73632a0720a765e5b113789f8f22 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 17 May 2023 12:45:45 +0200 Subject: [PATCH 0999/2268] fix: Make summary sorting more stable --- pkg/results/result-store-secrets.go | 2 +- pkg/results/result-store.go | 10 ++++++++++ pkg/results/results-collector.go | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 82713e27f..038133321 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -276,7 +276,7 @@ func (s *ResultStoreSecrets) ListCommandResultSummaries(options ListCommandResul } sort.Slice(ret, func(i, j int) bool { - return ret[i].Command.StartTime.After(ret[j].Command.StartTime.Time) + return lessSummary(&ret[i], &ret[j]) }) return ret, nil diff --git a/pkg/results/result-store.go b/pkg/results/result-store.go index baa5e7313..fae620b41 100644 --- a/pkg/results/result-store.go +++ b/pkg/results/result-store.go @@ -43,3 +43,13 @@ func FilterSummary(x *result.CommandResultSummary, filter *result.ProjectKey) bo } return true } + +func lessSummary(a *result.CommandResultSummary, b *result.CommandResultSummary) bool { + if a.Command.StartTime != b.Command.StartTime { + return a.Command.StartTime.After(b.Command.StartTime.Time) + } + if a.Command.EndTime != b.Command.EndTime { + return a.Command.EndTime.After(b.Command.EndTime.Time) + } + return a.Command.Command < b.Command.Command +} diff --git a/pkg/results/results-collector.go b/pkg/results/results-collector.go index 5ae9cbd4a..053cbbe37 100644 --- a/pkg/results/results-collector.go +++ b/pkg/results/results-collector.go @@ -111,7 +111,7 @@ func (rc *ResultsCollector) ListCommandResultSummaries(options ListCommandResult summaries = append(summaries, *x.summary) } sort.Slice(summaries, func(i, j int) bool { - return summaries[i].Command.StartTime.After(summaries[j].Command.StartTime.Time) + return lessSummary(&summaries[i], &summaries[j]) }) return summaries, nil } From c5ea94913f77b9e1e366ee5ad86e92a25d043ca9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 17 May 2023 12:46:06 +0200 Subject: [PATCH 1000/2268] feat: Add start/stop time to validation results --- pkg/deployment/commands/validate.go | 8 ++++++-- pkg/types/result/command_result.go | 12 +++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pkg/deployment/commands/validate.go b/pkg/deployment/commands/validate.go index 1a1fee162..deb477af1 100644 --- a/pkg/deployment/commands/validate.go +++ b/pkg/deployment/commands/validate.go @@ -11,6 +11,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type ValidateCommand struct { @@ -35,8 +36,9 @@ func NewValidateCommand(ctx context.Context, discriminator string, c *deployment func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result.ValidateResult, error) { ret := result.ValidateResult{ - Id: uuid.New().String(), - Ready: true, + Id: uuid.New().String(), + StartTime: metav1.Now(), + Ready: true, } cmd.dew.Init() @@ -119,6 +121,8 @@ func (cmd *ValidateCommand) Run(ctx context.Context, k *k8s.K8sCluster) (*result ret.Drift = du.ChangedObjects } + ret.EndTime = metav1.Now() + return &ret, nil } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 3f8a948ac..ec899dbcf 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -186,11 +186,13 @@ type ValidateResultEntry struct { } type ValidateResult struct { - Id string `json:"id"` - Ready bool `json:"ready"` - Warnings []DeploymentError `json:"warnings,omitempty"` - Errors []DeploymentError `json:"errors,omitempty"` - Results []ValidateResultEntry `json:"results,omitempty"` + Id string `json:"id"` + StartTime metav1.Time `json:"startTime"` + EndTime metav1.Time `json:"endTime"` + Ready bool `json:"ready"` + Warnings []DeploymentError `json:"warnings,omitempty"` + Errors []DeploymentError `json:"errors,omitempty"` + Results []ValidateResultEntry `json:"results,omitempty"` Drift []ChangedObject `json:"drift,omitempty"` } From a3f4d2c7ea1bbaa64c7fcacbff4bff8a342e1b34 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 17 May 2023 12:46:48 +0200 Subject: [PATCH 1001/2268] refactor: Rename NormalizedGitUrl -> GitRepoKey --- pkg/controllers/kluctl_project.go | 2 +- pkg/controllers/kluctldeployment_controller.go | 4 ++-- pkg/deployment/commands/result_utils.go | 2 +- pkg/results/result-store-secrets.go | 10 +++++----- pkg/results/result-store.go | 2 +- pkg/types/result/command_result.go | 8 ++++---- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 3eee59d44..2317adce4 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -647,7 +647,7 @@ func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, cmdResult.Command.Initiator = result.CommandInititiator_KluctlDeployment cmdResult.GitInfo.Url = &pt.pp.obj.Spec.Source.URL cmdResult.GitInfo.Ref = pt.pp.obj.Spec.Source.Ref.String() - cmdResult.ProjectKey.NormalizedGitUrl = pt.pp.obj.Spec.Source.URL.NormalizedRepoKey() + cmdResult.ProjectKey.GitRepoKey = pt.pp.obj.Spec.Source.URL.NormalizedRepoKey() log.Info(fmt.Sprintf("Writing command result %s", cmdResult.Id)) err := pt.pp.r.ResultStore.WriteCommandResult(cmdResult) diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index cb4cfa953..7f432a097 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -183,8 +183,8 @@ func (r *KluctlDeploymentReconciler) doReconcile( err = pt.withKluctlProjectTarget(ctx, func(targetContext *kluctl_project.TargetContext) error { obj.Status.ProjectKey = &result.ProjectKey{ - NormalizedGitUrl: obj.Spec.Source.URL.NormalizedRepoKey(), - SubDir: path.Clean(obj.Spec.Source.Path), + GitRepoKey: obj.Spec.Source.URL.NormalizedRepoKey(), + SubDir: path.Clean(obj.Spec.Source.Path), } obj.Status.TargetKey = &result.TargetKey{ TargetName: targetContext.Target.Name, diff --git a/pkg/deployment/commands/result_utils.go b/pkg/deployment/commands/result_utils.go index 43c26f685..ee128c9cd 100644 --- a/pkg/deployment/commands/result_utils.go +++ b/pkg/deployment/commands/result_utils.go @@ -135,7 +135,7 @@ func addGitInfo(targetCtx *kluctl_project.TargetContext, r *result.CommandResult Commit: head.Hash().String(), Dirty: !s.IsClean(), } - r.ProjectKey.NormalizedGitUrl = normaliedUrl + r.ProjectKey.GitRepoKey = normaliedUrl r.ProjectKey.SubDir = subDir return nil } diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 038133321..8425d11bd 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -99,8 +99,8 @@ var invalidChars = regexp.MustCompile(`[^a-zA-Z0-9-]`) func (s *ResultStoreSecrets) buildName(cr *result.CommandResult) string { var name string - if cr.ProjectKey.NormalizedGitUrl != "" { - s := path.Base(cr.ProjectKey.NormalizedGitUrl) + if cr.ProjectKey.GitRepoKey != "" { + s := path.Base(cr.ProjectKey.GitRepoKey) if s != "" { name = s + "-" } @@ -197,8 +197,8 @@ func (s *ResultStoreSecrets) WriteCommandResult(cr *result.CommandResult) error "compactedObjects": compressedObjects, }, } - if cr.ProjectKey.NormalizedGitUrl != "" { - secret.Annotations["kluctl.io/result-project-normalized-url"] = cr.ProjectKey.NormalizedGitUrl + if cr.ProjectKey.GitRepoKey != "" { + secret.Annotations["kluctl.io/result-project-normalized-url"] = cr.ProjectKey.GitRepoKey } if cr.ProjectKey.SubDir != "" { secret.Annotations["kluctl.io/result-project-subdir"] = cr.ProjectKey.SubDir @@ -302,7 +302,7 @@ func (s *ResultStoreSecrets) parseSummary(obj client.Object) (*result.CommandRes func (s *ResultStoreSecrets) filterSummary(summary *result.CommandResultSummary, project *result.ProjectKey) bool { if project != nil { - if project.NormalizedGitUrl != "" && summary.ProjectKey.NormalizedGitUrl != project.NormalizedGitUrl { + if project.GitRepoKey != "" && summary.ProjectKey.GitRepoKey != project.GitRepoKey { return false } if project.SubDir != "" && summary.ProjectKey.SubDir != project.SubDir { diff --git a/pkg/results/result-store.go b/pkg/results/result-store.go index fae620b41..ad01a76cd 100644 --- a/pkg/results/result-store.go +++ b/pkg/results/result-store.go @@ -35,7 +35,7 @@ func FilterSummary(x *result.CommandResultSummary, filter *result.ProjectKey) bo if filter == nil { return true } - if x.ProjectKey.NormalizedGitUrl != filter.NormalizedGitUrl { + if x.ProjectKey.GitRepoKey != filter.GitRepoKey { return false } if x.ProjectKey.SubDir != filter.SubDir { diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index ec899dbcf..e8b8bef4d 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -41,13 +41,13 @@ const ( ) type ProjectKey struct { - NormalizedGitUrl string `json:"normalizedGitUrl,omitempty"` - SubDir string `json:"subDir,omitempty"` + GitRepoKey string `json:"gitRepoKey,omitempty"` + SubDir string `json:"subDir,omitempty"` } func (k ProjectKey) Less(o ProjectKey) bool { - if k.NormalizedGitUrl != o.NormalizedGitUrl { - return k.NormalizedGitUrl < o.NormalizedGitUrl + if k.GitRepoKey != o.GitRepoKey { + return k.GitRepoKey < o.GitRepoKey } if k.SubDir != o.SubDir { return k.SubDir < o.SubDir From cfdf3a69edc28eec84bf2daefb3f4e82e054567b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 17 May 2023 17:07:04 +0200 Subject: [PATCH 1002/2268] refactor: Introduce dedicated GitRepoKey struct --- cmd/kluctl/commands/utils.go | 3 +- e2e/git_include_test.go | 6 +- pkg/controllers/kluctl_project.go | 2 +- .../kluctldeployment_controller.go | 2 +- pkg/deployment/commands/result_utils.go | 6 +- pkg/repocache/cache.go | 17 +++--- pkg/results/result-store-secrets.go | 10 +-- pkg/types/git_url.go | 61 +++++++++++++++---- pkg/types/result/command_result.go | 6 +- 9 files changed, 76 insertions(+), 37 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index cd2e6a923..83c566afd 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -264,8 +264,7 @@ func parseRepoOverride(s string, isGroup bool) (ret repocache.RepoOverride, err } } - u = u.Normalize() - ret.RepoUrl = *u + ret.RepoKey = u.RepoKey() ret.Override = sp[1] return } diff --git a/e2e/git_include_test.go b/e2e/git_include_test.go index 5330ff33f..61ab9c593 100644 --- a/e2e/git_include_test.go +++ b/e2e/git_include_test.go @@ -90,8 +90,8 @@ func TestLocalGitOverride(t *testing.T) { u1, _ := types.ParseGitUrl(ip1.GitUrl()) u2, _ := types.ParseGitUrl(ip2.GitUrl()) - k1 := u1.NormalizedRepoKey() - k2 := u2.NormalizedRepoKey() + k1 := u1.RepoKey().String() + k2 := u2.RepoKey().String() p.KluctlMust("deploy", "--yes", "-t", "test", "--local-git-override", fmt.Sprintf("%s=%s", k1, override1), @@ -130,7 +130,7 @@ func TestLocalGitGroupOverride(t *testing.T) { _ = yaml.WriteYamlFile(filepath.Join(override2, "subDir", "cm", "configmap-include2-cm.yml"), cm) u1, _ := types.ParseGitUrl(p.GitServer().GitUrl() + "/repos") - k1 := u1.NormalizedRepoKey() + k1 := u1.RepoKey().String() p.KluctlMust("deploy", "--yes", "-t", "test", "--local-git-group-override", fmt.Sprintf("%s=%s", k1, overrideGroupDir), diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 2317adce4..840b7b048 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -647,7 +647,7 @@ func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, cmdResult.Command.Initiator = result.CommandInititiator_KluctlDeployment cmdResult.GitInfo.Url = &pt.pp.obj.Spec.Source.URL cmdResult.GitInfo.Ref = pt.pp.obj.Spec.Source.Ref.String() - cmdResult.ProjectKey.GitRepoKey = pt.pp.obj.Spec.Source.URL.NormalizedRepoKey() + cmdResult.ProjectKey.GitRepoKey = pt.pp.obj.Spec.Source.URL.RepoKey() log.Info(fmt.Sprintf("Writing command result %s", cmdResult.Id)) err := pt.pp.r.ResultStore.WriteCommandResult(cmdResult) diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 7f432a097..4bf07cefa 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -183,7 +183,7 @@ func (r *KluctlDeploymentReconciler) doReconcile( err = pt.withKluctlProjectTarget(ctx, func(targetContext *kluctl_project.TargetContext) error { obj.Status.ProjectKey = &result.ProjectKey{ - GitRepoKey: obj.Spec.Source.URL.NormalizedRepoKey(), + GitRepoKey: obj.Spec.Source.URL.RepoKey(), SubDir: path.Clean(obj.Spec.Source.Path), } obj.Status.TargetKey = &result.TargetKey{ diff --git a/pkg/deployment/commands/result_utils.go b/pkg/deployment/commands/result_utils.go index ee128c9cd..4b987d949 100644 --- a/pkg/deployment/commands/result_utils.go +++ b/pkg/deployment/commands/result_utils.go @@ -123,9 +123,9 @@ func addGitInfo(targetCtx *kluctl_project.TargetContext, r *result.CommandResult } } - var normaliedUrl string + var repoKey types.GitRepoKey if originUrl != nil { - normaliedUrl = originUrl.NormalizedRepoKey() + repoKey = originUrl.RepoKey() } r.GitInfo = result.GitInfo{ @@ -135,7 +135,7 @@ func addGitInfo(targetCtx *kluctl_project.TargetContext, r *result.CommandResult Commit: head.Hash().String(), Dirty: !s.IsClean(), } - r.ProjectKey.GitRepoKey = normaliedUrl + r.ProjectKey.GitRepoKey = repoKey r.ProjectKey.SubDir = subDir return nil } diff --git a/pkg/repocache/cache.go b/pkg/repocache/cache.go index a31d37938..782c8a4b0 100644 --- a/pkg/repocache/cache.go +++ b/pkg/repocache/cache.go @@ -25,7 +25,7 @@ type GitRepoCache struct { sshPool *ssh_pool.SshPool updateInterval time.Duration - repos map[string]*CacheEntry + repos map[types.GitRepoKey]*CacheEntry reposMutex sync.Mutex repoOverrides []RepoOverride @@ -53,7 +53,7 @@ type RepoInfo struct { } type RepoOverride struct { - RepoUrl types.GitUrl + RepoKey types.GitRepoKey Ref string Override string IsGroup bool @@ -70,7 +70,7 @@ func NewGitRepoCache(ctx context.Context, sshPool *ssh_pool.SshPool, authProvide sshPool: sshPool, authProviders: authProviders, updateInterval: updateInterval, - repos: map[string]*CacheEntry{}, + repos: map[types.GitRepoKey]*CacheEntry{}, repoOverrides: repoOverrides, } } @@ -90,23 +90,24 @@ func (rp *GitRepoCache) GetEntry(url types.GitUrl) (*CacheEntry, error) { defer rp.reposMutex.Unlock() urlN := url.Normalize() - repoKey := url.NormalizedRepoKey() + repoKey := url.RepoKey() // evaluate overrides for _, ro := range rp.repoOverrides { - if ro.RepoUrl.Host != urlN.Host { + if ro.RepoKey.Host != urlN.Host { continue } var overridePath string if ro.IsGroup { - if !strings.HasPrefix(urlN.Path, ro.RepoUrl.Path+"/") { + prefix := "/" + ro.RepoKey.Path + "/" + if !strings.HasPrefix(urlN.Path, prefix) { continue } - relPath := strings.TrimPrefix(urlN.Path, ro.RepoUrl.Path+"/") + relPath := strings.TrimPrefix(urlN.Path, prefix) overridePath = path.Join(ro.Override, relPath) } else { - if ro.RepoUrl.Path != urlN.Path { + if "/"+ro.RepoKey.Path != urlN.Path { continue } overridePath = ro.Override diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 8425d11bd..7c09464d8 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -99,8 +99,8 @@ var invalidChars = regexp.MustCompile(`[^a-zA-Z0-9-]`) func (s *ResultStoreSecrets) buildName(cr *result.CommandResult) string { var name string - if cr.ProjectKey.GitRepoKey != "" { - s := path.Base(cr.ProjectKey.GitRepoKey) + if cr.ProjectKey.GitRepoKey.Path != "" { + s := path.Base(cr.ProjectKey.GitRepoKey.Path) if s != "" { name = s + "-" } @@ -197,8 +197,8 @@ func (s *ResultStoreSecrets) WriteCommandResult(cr *result.CommandResult) error "compactedObjects": compressedObjects, }, } - if cr.ProjectKey.GitRepoKey != "" { - secret.Annotations["kluctl.io/result-project-normalized-url"] = cr.ProjectKey.GitRepoKey + if cr.ProjectKey.GitRepoKey.String() != "" { + secret.Annotations["kluctl.io/result-project-repo-key"] = cr.ProjectKey.GitRepoKey.String() } if cr.ProjectKey.SubDir != "" { secret.Annotations["kluctl.io/result-project-subdir"] = cr.ProjectKey.SubDir @@ -302,7 +302,7 @@ func (s *ResultStoreSecrets) parseSummary(obj client.Object) (*result.CommandRes func (s *ResultStoreSecrets) filterSummary(summary *result.CommandResultSummary, project *result.ProjectKey) bool { if project != nil { - if project.GitRepoKey != "" && summary.ProjectKey.GitRepoKey != project.GitRepoKey { + if project.GitRepoKey.String() != "" && summary.ProjectKey.GitRepoKey != project.GitRepoKey { return false } if project.SubDir != "" && summary.ProjectKey.SubDir != project.SubDir { diff --git a/pkg/types/git_url.go b/pkg/types/git_url.go index 44db3e65c..c88947750 100644 --- a/pkg/types/git_url.go +++ b/pkg/types/git_url.go @@ -94,12 +94,9 @@ func (u *GitUrl) NormalizePort() string { func (u *GitUrl) Normalize() *GitUrl { path := strings.ToLower(u.Path) - if strings.HasSuffix(path, ".git") { - path = path[:len(path)-len(".git")] - } - if strings.HasSuffix(path, "/") { - path = path[:len(path)-1] - } + path = strings.TrimSuffix(path, ".git") + path = strings.TrimSuffix(path, "/") + hostname := strings.ToLower(u.Hostname()) port := u.NormalizePort() @@ -112,11 +109,53 @@ func (u *GitUrl) Normalize() *GitUrl { return &u2 } -func (u *GitUrl) NormalizedRepoKey() string { +func (u *GitUrl) RepoKey() GitRepoKey { u2 := u.Normalize() - username := "" - if u.User != nil && u.User.Username() != "" { - username = u.User.Username() + "@" + path := strings.TrimPrefix(u2.Path, "/") + return GitRepoKey{ + Host: u2.Host, + Path: path, } - return fmt.Sprintf("%s%s:%s%s", username, u2.Hostname(), u2.Port(), u2.Path) +} + +// +kubebuilder:validation:Type=string +type GitRepoKey struct { + Host string `json:"-"` + Path string `json:"-"` +} + +func (u GitRepoKey) String() string { + if u.Host == "" && u.Path == "" { + return "" + } + return fmt.Sprintf("%s/%s", u.Host, u.Path) +} + +func (u *GitRepoKey) UnmarshalJSON(b []byte) error { + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + if s == "" { + u.Host = "" + u.Path = "" + return nil + } + s2 := strings.SplitN(s, "/", 2) + if len(s2) != 2 { + return fmt.Errorf("invalid git repo key: %s", s) + } + u.Host = s2[0] + u.Path = s2[1] + return nil +} + +func (u GitRepoKey) MarshalJSON() ([]byte, error) { + b, err := json.Marshal(u.String()) + if err != nil { + return nil, err + } + + return b, err } diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index e8b8bef4d..7bc46c032 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -41,13 +41,13 @@ const ( ) type ProjectKey struct { - GitRepoKey string `json:"gitRepoKey,omitempty"` - SubDir string `json:"subDir,omitempty"` + GitRepoKey types.GitRepoKey `json:"gitRepoKey,omitempty"` + SubDir string `json:"subDir,omitempty"` } func (k ProjectKey) Less(o ProjectKey) bool { if k.GitRepoKey != o.GitRepoKey { - return k.GitRepoKey < o.GitRepoKey + return k.GitRepoKey.String() < o.GitRepoKey.String() } if k.SubDir != o.SubDir { return k.SubDir < o.SubDir From 25622069dfc87ef0ca3c215494c38be13838b0f0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 17 May 2023 17:07:17 +0200 Subject: [PATCH 1003/2268] fix: Don't accept local Git urls in ParseGitUrl --- pkg/git/auth/git_credentials_file.go | 3 +-- pkg/types/git_url.go | 13 +++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/git/auth/git_credentials_file.go b/pkg/git/auth/git_credentials_file.go index 58fff60c3..ca89766a8 100644 --- a/pkg/git/auth/git_credentials_file.go +++ b/pkg/git/auth/git_credentials_file.go @@ -4,7 +4,6 @@ import ( "bufio" "context" "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/kluctl/kluctl/v2/pkg/git/giturls" "github.com/kluctl/kluctl/v2/pkg/git/messages" "github.com/kluctl/kluctl/v2/pkg/types" "net/url" @@ -66,7 +65,7 @@ func (a *GitCredentialsFileAuthProvider) tryBuildAuth(gitUrl types.GitUrl, gitCr if s.Text() == "" || s.Text()[0] == '#' { continue } - u, err := giturls.Parse(s.Text()) + u, err := types.ParseGitUrl(s.Text()) if err != nil { continue } diff --git a/pkg/types/git_url.go b/pkg/types/git_url.go index c88947750..68dbc5ff1 100644 --- a/pkg/types/git_url.go +++ b/pkg/types/git_url.go @@ -14,11 +14,16 @@ type GitUrl struct { } func ParseGitUrl(u string) (*GitUrl, error) { - u2, err := giturls.Parse(u) - if err != nil { - return nil, err + // we explicitly only test ParseTransport and ParseScp to avoid parsing local Git urls (as done by giturls.Parse) + u2, err := giturls.ParseTransport(u) + if err == nil { + return &GitUrl{*u2}, nil + } + u2, err = giturls.ParseScp(u) + if err == nil { + return &GitUrl{*u2}, nil } - return &GitUrl{*u2}, nil + return nil, fmt.Errorf("failed to parse git url: %s", u) } func ParseGitUrlMust(u string) *GitUrl { From 424cc67544e3f2f53bad729447e8282547910dd0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 17 May 2023 17:08:25 +0200 Subject: [PATCH 1004/2268] chore: Run make generate manifest --- .../gitops.kluctl.io_kluctldeployments.yaml | 160 +++++++++++++++++- pkg/types/result/zz_generated.deepcopy.go | 8 +- pkg/types/zz_generated.deepcopy.go | 15 ++ 3 files changed, 174 insertions(+), 9 deletions(-) diff --git a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml index 4abd9c5ce..100a3e89a 100644 --- a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml +++ b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml @@ -600,9 +600,9 @@ spec: type: integer orphanObjects: type: integer - project: + projectKey: properties: - normalizedGitUrl: + gitRepoKey: type: string subDir: type: string @@ -612,6 +612,74 @@ spec: renderedObjects: type: integer target: + properties: + args: + type: object + x-kubernetes-preserve-unknown-fields: true + context: + type: string + discriminator: + type: string + images: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + name: + type: string + sealingConfig: + properties: + args: + type: object + x-kubernetes-preserve-unknown-fields: true + certFile: + type: string + secretSets: + items: + type: string + type: array + type: object + required: + - name + type: object + targetKey: properties: clusterId: type: string @@ -636,10 +704,11 @@ spec: - id - newObjects - orphanObjects - - project + - projectKey - remoteObjects - renderedObjects - target + - targetKey - totalChanges - warnings type: object @@ -807,9 +876,9 @@ spec: type: integer orphanObjects: type: integer - project: + projectKey: properties: - normalizedGitUrl: + gitRepoKey: type: string subDir: type: string @@ -819,6 +888,74 @@ spec: renderedObjects: type: integer target: + properties: + args: + type: object + x-kubernetes-preserve-unknown-fields: true + context: + type: string + discriminator: + type: string + images: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + name: + type: string + sealingConfig: + properties: + args: + type: object + x-kubernetes-preserve-unknown-fields: true + certFile: + type: string + secretSets: + items: + type: string + type: array + type: object + required: + - name + type: object + targetKey: properties: clusterId: type: string @@ -843,10 +980,11 @@ spec: - id - newObjects - orphanObjects - - project + - projectKey - remoteObjects - renderedObjects - target + - targetKey - totalChanges - warnings type: object @@ -895,6 +1033,9 @@ spec: - ref type: object type: array + endTime: + format: date-time + type: string errors: items: properties: @@ -954,6 +1095,9 @@ spec: - ref type: object type: array + startTime: + format: date-time + type: string warnings: items: properties: @@ -981,8 +1125,10 @@ spec: type: object type: array required: + - endTime - id - ready + - startTime type: object observedGeneration: description: ObservedGeneration is the last reconciled generation. @@ -990,7 +1136,7 @@ spec: type: integer projectKey: properties: - normalizedGitUrl: + gitRepoKey: type: string subDir: type: string diff --git a/pkg/types/result/zz_generated.deepcopy.go b/pkg/types/result/zz_generated.deepcopy.go index c7c90926a..40ade0029 100644 --- a/pkg/types/result/zz_generated.deepcopy.go +++ b/pkg/types/result/zz_generated.deepcopy.go @@ -218,8 +218,9 @@ func (in *CommandResult) DeepCopy() *CommandResult { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CommandResultSummary) DeepCopyInto(out *CommandResultSummary) { *out = *in - out.Project = in.Project - out.Target = in.Target + out.ProjectKey = in.ProjectKey + out.TargetKey = in.TargetKey + in.Target.DeepCopyInto(&out.Target) in.Command.DeepCopyInto(&out.Command) in.GitInfo.DeepCopyInto(&out.GitInfo) out.ClusterInfo = in.ClusterInfo @@ -348,6 +349,7 @@ func (in *KluctlDeploymentInfo) DeepCopy() *KluctlDeploymentInfo { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ProjectKey) DeepCopyInto(out *ProjectKey) { *out = *in + out.GitRepoKey = in.GitRepoKey } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectKey. @@ -461,6 +463,8 @@ func (in *TargetSummary) DeepCopy() *TargetSummary { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ValidateResult) DeepCopyInto(out *ValidateResult) { *out = *in + in.StartTime.DeepCopyInto(&out.StartTime) + in.EndTime.DeepCopyInto(&out.EndTime) if in.Warnings != nil { in, out := &in.Warnings, &out.Warnings *out = make([]DeploymentError, len(*in)) diff --git a/pkg/types/zz_generated.deepcopy.go b/pkg/types/zz_generated.deepcopy.go index ec75fae41..59a658fca 100644 --- a/pkg/types/zz_generated.deepcopy.go +++ b/pkg/types/zz_generated.deepcopy.go @@ -329,6 +329,21 @@ func (in *GitProject) DeepCopy() *GitProject { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitRepoKey) DeepCopyInto(out *GitRepoKey) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitRepoKey. +func (in *GitRepoKey) DeepCopy() *GitRepoKey { + if in == nil { + return nil + } + out := new(GitRepoKey) + in.DeepCopyInto(out) + return out +} + // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitUrl. func (in *GitUrl) DeepCopy() *GitUrl { if in == nil { From 7c0f3bfc397f5d8972fc6e50daf8b6497d13706f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 18 May 2023 08:27:32 +0200 Subject: [PATCH 1005/2268] ci: Fix Github tests workflow --- .github/workflows/tests.yml | 5 ----- Makefile | 11 +++++++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b4d09f840..851719d81 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -80,11 +80,6 @@ jobs: shell: bash run: | make test-unit - - name: setup-envtest - if: matrix.run_on_pull_requests || github.event_name != 'pull_request' - shell: bash - run: | - make install-envtest - name: Run e2e tests if: matrix.run_on_pull_requests || github.event_name != 'pull_request' shell: bash diff --git a/Makefile b/Makefile index d640b9f2b..c30be6440 100644 --- a/Makefile +++ b/Makefile @@ -70,8 +70,15 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(RACE) ./... -coverprofile cover.out +test: test-unit test-e2e fmt vet ## Run all tests. + +.PHONY: test-unit +test-unit: generate ## Run unit tests. + go test $(RACE) $(shell go list ./... | grep -v v2/e2e) -coverprofile cover.out + +.PHONY: test-e2e +test-e2e: manifests generate envtest ## Run e2e tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(RACE) ./e2e -coverprofile cover.out replace-commands-help: ## Replace commands help in docs go run ./internal/replace-commands-help --docs-dir ./docs/reference/commands From a19fc52e2765d4b517bf1a3ca118e78f8dd1443f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 18 May 2023 22:20:25 +0200 Subject: [PATCH 1006/2268] fix: Url normalizing should also normalize leading / --- pkg/types/git_url.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/types/git_url.go b/pkg/types/git_url.go index 68dbc5ff1..4c649bb04 100644 --- a/pkg/types/git_url.go +++ b/pkg/types/git_url.go @@ -105,6 +105,10 @@ func (u *GitUrl) Normalize() *GitUrl { hostname := strings.ToLower(u.Hostname()) port := u.NormalizePort() + if path != "" && hostname != "" && !strings.HasPrefix(path, "/") { + path = "/" + path + } + u2 := *u u2.Path = path u2.Host = hostname From e224bd610b2bce5d00000cc9e8e5fb035fd449c8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 18 May 2023 22:21:34 +0200 Subject: [PATCH 1007/2268] tests: Fix EnvTestCluster race condition --- e2e/test-utils/envtest_cluster.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/e2e/test-utils/envtest_cluster.go b/e2e/test-utils/envtest_cluster.go index 158b79e65..c6e6d9fd6 100644 --- a/e2e/test-utils/envtest_cluster.go +++ b/e2e/test-utils/envtest_cluster.go @@ -6,7 +6,9 @@ import ( "fmt" kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/discovery" @@ -47,6 +49,9 @@ type EnvTestCluster struct { func CreateEnvTestCluster(context string) *EnvTestCluster { k := &EnvTestCluster{ Context: context, + env: envtest.Environment{ + Scheme: runtime.NewScheme(), + }, } return k } @@ -61,6 +66,7 @@ func (k *EnvTestCluster) Start() error { k.started = true _ = kluctlv1.AddToScheme(k.env.Scheme) + _ = corev1.AddToScheme(k.env.Scheme) err = k.startCallbackServer() if err != nil { @@ -99,6 +105,7 @@ func (k *EnvTestCluster) Start() error { c, err := client.New(k.config, client.Options{ HTTPClient: httpClient, + Scheme: k.env.Scheme, }) k.Client = c From 99fbf56ce97d3c6668ecdf14d116d2edc0861df5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 18 May 2023 22:57:01 +0200 Subject: [PATCH 1008/2268] ci: Don't run generate for unit tests --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c30be6440..a2bbf37e2 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ vet: ## Run go vet against code. test: test-unit test-e2e fmt vet ## Run all tests. .PHONY: test-unit -test-unit: generate ## Run unit tests. +test-unit: ## Run unit tests. go test $(RACE) $(shell go list ./... | grep -v v2/e2e) -coverprofile cover.out .PHONY: test-e2e From e3359a935cb9703f57bd46b3266b50173695c4e0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 May 2023 00:02:06 +0200 Subject: [PATCH 1009/2268] tests: Skip TestServer_EncryptDecrypt_PGP on windows --- pkg/controllers/internal/sops/keyservice/server_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/controllers/internal/sops/keyservice/server_test.go b/pkg/controllers/internal/sops/keyservice/server_test.go index d7e0b7b33..ea34cecaf 100644 --- a/pkg/controllers/internal/sops/keyservice/server_test.go +++ b/pkg/controllers/internal/sops/keyservice/server_test.go @@ -15,6 +15,7 @@ import ( "go.mozilla.org/sops/v3/kms" "go.mozilla.org/sops/v3/pgp" "os" + "runtime" "testing" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" @@ -25,6 +26,11 @@ import ( ) func TestServer_EncryptDecrypt_PGP(t *testing.T) { + if runtime.GOOS == "windows" { + // skipping due to error: cannot import armored key data into GnuPG keyring: GNUPGHOME has invalid permissions: got 0777 wanted 0700 + return + } + const ( mockPublicKey = "testdata/public.gpg" mockPrivateKey = "testdata/private.gpg" From 27f4beb3ebe49f6df5d85423944b909d00d1f43d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 May 2023 08:12:50 +0200 Subject: [PATCH 1010/2268] refactor: Pass initialized client/cache instead of RESTConfig to ResultStoreSecrets --- cmd/kluctl/commands/utils.go | 19 ++++- .../kluctldeployment_controller_setup.go | 2 +- pkg/results/result-store-secrets.go | 72 +++++++------------ 3 files changed, 46 insertions(+), 47 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 83c566afd..0bcb6bcbf 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -21,6 +21,8 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" "os" + cache2 "sigs.k8s.io/controller-runtime/pkg/cache" + client2 "sigs.k8s.io/controller-runtime/pkg/client" "strings" ) @@ -203,7 +205,22 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm if err != nil { return err } - resultStore, err = results.NewResultStoreSecrets(ctx, rc, args.commandResultFlags.CommandResultNamespace, args.commandResultFlags.KeepCommandResultsCount) + client, err := client2.New(rc, client2.Options{}) + if err != nil { + return err + } + cache, err := cache2.New(rc, cache2.Options{}) + if err != nil { + return err + } + + cancelCtx, cancelFunc := context.WithCancel(ctx) + defer cancelFunc() + go func() { + _ = cache.Start(cancelCtx) + }() + + resultStore, err = results.NewResultStoreSecrets(cancelCtx, client, cache, args.commandResultFlags.CommandResultNamespace, args.commandResultFlags.KeepCommandResultsCount) if err != nil { return err } diff --git a/pkg/controllers/kluctldeployment_controller_setup.go b/pkg/controllers/kluctldeployment_controller_setup.go index 61f66cba1..0f8fe2e73 100644 --- a/pkg/controllers/kluctldeployment_controller_setup.go +++ b/pkg/controllers/kluctldeployment_controller_setup.go @@ -22,7 +22,7 @@ func (r *KluctlDeploymentReconciler) SetupWithManager(ctx context.Context, mgr c httpClient.Logger = nil r.httpClient = httpClient - resultStore, err := results.NewResultStoreSecrets(ctx, mgr.GetConfig(), "kluctl-results", 10) + resultStore, err := results.NewResultStoreSecrets(ctx, mgr.GetClient(), mgr.GetCache(), "kluctl-results", 10) if err != nil { return err } diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 7c09464d8..88527503c 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -11,9 +11,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/rest" toolscache "k8s.io/client-go/tools/cache" "path" "regexp" @@ -25,70 +23,54 @@ import ( ) type ResultStoreSecrets struct { - ctx context.Context + ctx context.Context + client client.Client + cache cache.Cache + reader client.Reader - config *rest.Config writeNamespace string keepResultsCount int - mutex sync.Mutex - client client.Client - cache cache.Cache - informer cache.Informer - cancelCache context.CancelFunc + mutex sync.Mutex + informer cache.Informer } -func NewResultStoreSecrets(ctx context.Context, config *rest.Config, writeNamespace string, keepResultsCount int) (*ResultStoreSecrets, error) { +func NewResultStoreSecrets(ctx context.Context, client client.Client, cache cache.Cache, writeNamespace string, keepResultsCount int) (*ResultStoreSecrets, error) { s := &ResultStoreSecrets{ ctx: ctx, - config: config, + client: client, + cache: cache, writeNamespace: writeNamespace, keepResultsCount: keepResultsCount, } + if cache != nil { + s.reader = cache + } else { + s.reader = client + } return s, nil } -func (s *ResultStoreSecrets) ensureClientAndCache() error { +func (s *ResultStoreSecrets) ensureInformer() error { s.mutex.Lock() defer s.mutex.Unlock() - if s.client != nil { + if s.informer != nil { return nil } - - c, err := client.New(s.config, client.Options{}) - if err != nil { - return err + if s.cache == nil { + return nil } - labelSelector, _ := labels.Parse("kluctl.io/result-id") - var partialSecret metav1.PartialObjectMetadata partialSecret.SetGroupVersionKind(schema.GroupVersionKind{Version: "v1", Kind: "Secret"}) - ca, err := cache.New(s.config, cache.Options{ - DefaultLabelSelector: labelSelector, - }) - if err != nil { - return err - } - - informer, err := ca.GetInformer(s.ctx, &partialSecret) + informer, err := s.cache.GetInformer(s.ctx, &partialSecret) if err != nil { return err } - s.client = c - s.cache = ca s.informer = informer - - newCtx, cancel := context.WithCancel(s.ctx) - s.cancelCache = cancel - - go func() { - _ = ca.Start(newCtx) - }() - s.cache.WaitForCacheSync(s.ctx) return nil @@ -124,7 +106,7 @@ func (s *ResultStoreSecrets) ensureWriteNamespace() error { return fmt.Errorf("missing writeNamespace") } var ns corev1.Namespace - err := s.client.Get(s.ctx, client.ObjectKey{Name: s.writeNamespace}, &ns) + err := s.reader.Get(s.ctx, client.ObjectKey{Name: s.writeNamespace}, &ns) if err != nil && errors.IsNotFound(err) { ns := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -143,7 +125,7 @@ func (s *ResultStoreSecrets) ensureWriteNamespace() error { } func (s *ResultStoreSecrets) WriteCommandResult(cr *result.CommandResult) error { - err := s.ensureClientAndCache() + err := s.ensureInformer() if err != nil { return err } @@ -249,14 +231,14 @@ func (s *ResultStoreSecrets) cleanupResults(project result.ProjectKey, target re } func (s *ResultStoreSecrets) ListCommandResultSummaries(options ListCommandResultSummariesOptions) ([]result.CommandResultSummary, error) { - err := s.ensureClientAndCache() + err := s.ensureInformer() if err != nil { return nil, err } var l metav1.PartialObjectMetadataList l.SetGroupVersionKind(schema.GroupVersionKind{Version: "v1", Kind: "SecretList"}) - err = s.cache.List(s.ctx, &l, client.HasLabels{"kluctl.io/result-id"}) + err = s.reader.List(s.ctx, &l, client.HasLabels{"kluctl.io/result-id"}) if err != nil { return nil, err } @@ -314,7 +296,7 @@ func (s *ResultStoreSecrets) filterSummary(summary *result.CommandResultSummary, } func (s *ResultStoreSecrets) WatchCommandResultSummaries(options ListCommandResultSummariesOptions, update func(summary *result.CommandResultSummary), delete func(id string)) (func(), error) { - err := s.ensureClientAndCache() + err := s.ensureInformer() if err != nil { return nil, err } @@ -360,14 +342,14 @@ func (s *ResultStoreSecrets) WatchCommandResultSummaries(options ListCommandResu } func (s *ResultStoreSecrets) getCommandResultSecret(id string) (*metav1.PartialObjectMetadata, error) { - err := s.ensureClientAndCache() + err := s.ensureInformer() if err != nil { return nil, err } var l metav1.PartialObjectMetadataList l.SetGroupVersionKind(schema.GroupVersionKind{Version: "v1", Kind: "SecretList"}) - err = s.cache.List(s.ctx, &l, client.MatchingLabels{"kluctl.io/result-id": id}) + err = s.reader.List(s.ctx, &l, client.MatchingLabels{"kluctl.io/result-id": id}) if err != nil { return nil, err } @@ -403,7 +385,7 @@ func (s *ResultStoreSecrets) GetCommandResult(options GetCommandResultOptions) ( } var l corev1.SecretList - err = s.client.List(s.ctx, &l, client.MatchingLabels{ + err = s.reader.List(s.ctx, &l, client.MatchingLabels{ "kluctl.io/result-id": options.Id, }) if err != nil { From 5f36b04bbe4a5d8e7b8721ae25234a25d24fe7ef Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 May 2023 11:55:14 +0200 Subject: [PATCH 1011/2268] fix: Fix controller swallowing errors --- api/v1beta1/kluctldeployment_types.go | 10 + .../gitops.kluctl.io_kluctldeployments.yaml | 9 + pkg/controllers/kluctl_project.go | 109 ++++---- .../kluctldeployment_controller.go | 238 ++++++++++-------- pkg/controllers/utils.go | 11 +- 5 files changed, 209 insertions(+), 168 deletions(-) diff --git a/api/v1beta1/kluctldeployment_types.go b/api/v1beta1/kluctldeployment_types.go index 3da415e20..b5021142c 100644 --- a/api/v1beta1/kluctldeployment_types.go +++ b/api/v1beta1/kluctldeployment_types.go @@ -303,6 +303,9 @@ type KluctlDeploymentStatus struct { // +optional ObservedGeneration int64 `json:"observedGeneration,omitempty"` + // ObservedCommit is the last commit observed + ObservedCommit string `json:"observedCommit,omitempty"` + // +optional Conditions []metav1.Condition `json:"conditions,omitempty"` @@ -315,6 +318,13 @@ type KluctlDeploymentStatus struct { // +optional LastObjectsHash string `json:"lastObjectsHash,omitempty"` + // +optional + LastDeployError string `json:"lastDeployError,omitempty"` + // +optional + LastPruneError string `json:"lastPruneError,omitempty"` + // +optional + LastValidateError string `json:"lastValidateError,omitempty"` + // LastDeployResult is the result of the last deploy command // +optional LastDeployResult *result.CommandResultSummary `json:"lastDeployResult,omitempty"` diff --git a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml index 100a3e89a..d07ed9349 100644 --- a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml +++ b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml @@ -443,6 +443,8 @@ spec: - type type: object type: array + lastDeployError: + type: string lastDeployResult: description: LastDeployResult is the result of the last deploy command properties: @@ -719,6 +721,8 @@ spec: type: string lastObjectsHash: type: string + lastPruneError: + type: string lastPruneResult: description: LastDeployResult is the result of the last prune command properties: @@ -988,6 +992,8 @@ spec: - totalChanges - warnings type: object + lastValidateError: + type: string lastValidateResult: description: LastValidateResult is the result of the last validate command @@ -1130,6 +1136,9 @@ spec: - ready - startTime type: object + observedCommit: + description: ObservedCommit is the last commit observed + type: string observedGeneration: description: ObservedGeneration is the last reconciled generation. format: int64 diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 840b7b048..d0c306032 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -3,10 +3,10 @@ package controllers import ( "context" "fmt" + "github.com/kluctl/go-jinja2" internal_metrics "github.com/kluctl/kluctl/v2/pkg/controllers/internal/metrics" "github.com/kluctl/kluctl/v2/pkg/controllers/internal/sops" "github.com/kluctl/kluctl/v2/pkg/helm" - "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" "github.com/kluctl/kluctl/v2/pkg/types/result" @@ -44,6 +44,7 @@ type preparedProject struct { rp *repocache.GitRepoCache + commit string tmpDir string repoDir string projectDir string @@ -95,11 +96,12 @@ func prepareProject(ctx context.Context, return nil, fmt.Errorf("failed clone source: %w", err) } - clonedDir, _, err := rpEntry.GetClonedDir(pp.obj.Spec.Source.Ref.String()) + clonedDir, gi, err := rpEntry.GetClonedDir(pp.obj.Spec.Source.Ref.String()) if err != nil { return nil, err } + pp.commit = gi.CheckedOutCommit pp.repoDir = clonedDir // check kluctl project path exists @@ -124,12 +126,12 @@ func (pp *preparedProject) cleanup() { } } -func (pp *preparedProject) newTarget() (*preparedTarget, error) { +func (pp *preparedProject) newTarget() *preparedTarget { pt := preparedTarget{ pp: pp, } - return &pt, nil + return &pt } func (pt *preparedTarget) restConfigToKubeconfig(restConfig *rest.Config) *api.Config { @@ -558,24 +560,19 @@ func (pp *preparedProject) addServiceAccountBasedKeyServers(ctx context.Context, return nil } -func (pp *preparedProject) withKluctlProject(ctx context.Context, pt *preparedTarget, cb func(p *kluctl_project.LoadedKluctlProject) error) error { - j2, err := kluctl_jinja2.NewKluctlJinja2(true) - if err != nil { - return err - } - defer j2.Close() - +func (pp *preparedProject) loadKluctlProject(ctx context.Context, pt *preparedTarget, j2 *jinja2.Jinja2) (*kluctl_project.LoadedKluctlProject, error) { + var err error var sopsDecrypter *decryptor.Decryptor if pp.obj.Spec.Decryption != nil { sopsDecrypter, err = pp.buildSopsDecrypter(ctx) if err != nil { - return err + return nil, err } } externalArgs, err := uo.FromString(string(pt.pp.obj.Spec.Args.Raw)) if err != nil { - return err + return nil, err } loadArgs := kluctl_project.LoadKluctlProjectArgs{ @@ -591,54 +588,52 @@ func (pp *preparedProject) withKluctlProject(ctx context.Context, pt *preparedTa p, err := kluctl_project.LoadKluctlProject(ctx, loadArgs, filepath.Join(pp.tmpDir, "project"), j2) if err != nil { - return err + return nil, err } - return cb(p) + return p, nil } -func (pt *preparedTarget) withKluctlProjectTarget(ctx context.Context, cb func(targetContext *kluctl_project.TargetContext) error) error { - return pt.pp.withKluctlProject(ctx, pt, func(p *kluctl_project.LoadedKluctlProject) error { - renderOutputDir, err := os.MkdirTemp(pt.pp.tmpDir, "render-") - if err != nil { - return err - } - images, err := pt.buildImages(ctx) - if err != nil { - return err - } - helmCredentials, err := pt.buildHelmCredentials(ctx) - if err != nil { - return err - } - inclusion := pt.buildInclusion() - - props := kluctl_project.TargetContextParams{ - DryRun: pt.pp.r.DryRun || pt.pp.obj.Spec.DryRun, - Images: images, - Inclusion: inclusion, - HelmCredentials: helmCredentials, - RenderOutputDir: renderOutputDir, - } - if pt.pp.obj.Spec.Target != nil { - props.TargetName = *pt.pp.obj.Spec.Target - } - if pt.pp.obj.Spec.TargetNameOverride != nil { - props.TargetNameOverride = *pt.pp.obj.Spec.TargetNameOverride - } - if pt.pp.obj.Spec.Context != nil { - props.ContextOverride = *pt.pp.obj.Spec.Context - } - targetContext, err := p.NewTargetContext(ctx, props) - if err != nil { - return err - } - err = targetContext.DeploymentCollection.Prepare() - if err != nil { - return err - } - return cb(targetContext) - }) +func (pt *preparedTarget) loadTarget(ctx context.Context, p *kluctl_project.LoadedKluctlProject) (*kluctl_project.TargetContext, error) { + renderOutputDir, err := os.MkdirTemp(pt.pp.tmpDir, "render-") + if err != nil { + return nil, err + } + images, err := pt.buildImages(ctx) + if err != nil { + return nil, err + } + helmCredentials, err := pt.buildHelmCredentials(ctx) + if err != nil { + return nil, err + } + inclusion := pt.buildInclusion() + + props := kluctl_project.TargetContextParams{ + DryRun: pt.pp.r.DryRun || pt.pp.obj.Spec.DryRun, + Images: images, + Inclusion: inclusion, + HelmCredentials: helmCredentials, + RenderOutputDir: renderOutputDir, + } + if pt.pp.obj.Spec.Target != nil { + props.TargetName = *pt.pp.obj.Spec.Target + } + if pt.pp.obj.Spec.TargetNameOverride != nil { + props.TargetNameOverride = *pt.pp.obj.Spec.TargetNameOverride + } + if pt.pp.obj.Spec.Context != nil { + props.ContextOverride = *pt.pp.obj.Spec.Context + } + targetContext, err := p.NewTargetContext(ctx, props) + if err != nil { + return nil, err + } + err = targetContext.DeploymentCollection.Prepare() + if err != nil { + return nil, err + } + return targetContext, nil } func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, cmdResult *result.CommandResult, commandName string) error { diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 4bf07cefa..5e9653f2d 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -7,7 +7,7 @@ import ( kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" internal_metrics "github.com/kluctl/kluctl/v2/pkg/controllers/internal/metrics" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" - "github.com/kluctl/kluctl/v2/pkg/kluctl_project" + "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/results" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types/result" @@ -68,6 +68,8 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, client.IgnoreNotFound(err) } + patch := client.MergeFrom(obj.DeepCopy()) + var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, r.calcTimeout(obj)) defer cancel() @@ -80,7 +82,6 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req // Add our finalizer if it does not exist if !controllerutil.ContainsFinalizer(obj, kluctlv1.KluctlDeploymentFinalizer) { - patch := client.MergeFrom(obj.DeepCopy()) controllerutil.AddFinalizer(obj, kluctlv1.KluctlDeploymentFinalizer) if err := r.Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { log.Error(err, "unable to register finalizer") @@ -110,7 +111,6 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req // set the reconciliation status to progressing if obj.Status.ObservedGeneration == 0 { - patch := client.MergeFrom(obj.DeepCopy()) setReadiness(obj, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress") if err := r.Status().Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { return ctrl.Result{Requeue: true}, err @@ -125,7 +125,6 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req } // reconcile kluctlDeployment by applying the latest revision - patch := client.MergeFrom(obj.DeepCopy()) ctrlResult, reconcileErr := r.doReconcile(ctx, obj) if err := r.Status().Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { return ctrl.Result{Requeue: true}, err @@ -168,127 +167,145 @@ func (r *KluctlDeploymentReconciler) doReconcile( r.exportDeploymentObjectToProm(obj) + doFail := func(reason string, err error) (*ctrl.Result, error) { + setReadiness(obj, metav1.ConditionFalse, reason, err.Error()) + internal_metrics.NewKluctlLastObjectStatus(obj.Namespace, obj.Name).Set(0.0) + return nil, err + } + + doFailPrepare := func(err error) (*ctrl.Result, error) { + return doFail(kluctlv1.PrepareFailedReason, err) + } + + oldGeneration := obj.Status.ObservedGeneration + obj.Status.ObservedGeneration = obj.GetGeneration() + if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; ok { + obj.Status.LastHandledDeployAt = v + } + pp, err := prepareProject(ctx, r, obj, true) if err != nil { - setReadiness(obj, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, err.Error()) - return nil, err + return doFailPrepare(err) } defer pp.cleanup() - pt, err := pp.newTarget() + obj.Status.ObservedCommit = pp.commit + + j2, err := kluctl_jinja2.NewKluctlJinja2(true) if err != nil { - setReadiness(obj, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, err.Error()) - return nil, err + return doFailPrepare(err) } + defer j2.Close() - err = pt.withKluctlProjectTarget(ctx, func(targetContext *kluctl_project.TargetContext) error { - obj.Status.ProjectKey = &result.ProjectKey{ - GitRepoKey: obj.Spec.Source.URL.RepoKey(), - SubDir: path.Clean(obj.Spec.Source.Path), - } - obj.Status.TargetKey = &result.TargetKey{ - TargetName: targetContext.Target.Name, - Discriminator: targetContext.Target.Discriminator, - } + pt := pp.newTarget() - if obj.Status.ProjectKey.SubDir == "." { - obj.Status.ProjectKey.SubDir = "" - } + lp, err := pp.loadKluctlProject(ctx, pt, j2) + if err != nil { + return doFailPrepare(err) + } - objectsHash, err := targetContext.DeploymentCollection.CalcObjectsHash() - if err != nil { - return err + targetContext, err := pt.loadTarget(ctx, lp) + if err != nil { + return doFailPrepare(err) + } + + obj.Status.ProjectKey = &result.ProjectKey{ + GitRepoKey: obj.Spec.Source.URL.RepoKey(), + SubDir: path.Clean(obj.Spec.Source.Path), + } + obj.Status.TargetKey = &result.TargetKey{ + TargetName: targetContext.Target.Name, + Discriminator: targetContext.Target.Discriminator, + } + + if obj.Status.ProjectKey.SubDir == "." { + obj.Status.ProjectKey.SubDir = "" + } + + objectsHash, err := targetContext.DeploymentCollection.CalcObjectsHash() + if err != nil { + return doFailPrepare(err) + } + + needDeploy := false + needPrune := false + needValidate := false + + if obj.Status.LastDeployResult == nil || obj.Status.LastObjectsHash != objectsHash { + // either never deployed or source code changed + needDeploy = true + } else if r.checkRequestedDeploy(obj) { + // explicitly requested a deploy + needDeploy = true + } else if oldGeneration != obj.GetGeneration() { + // spec has changed + needDeploy = true + } else { + // was deployed before, let's check if we need to do periodic deployments + nextDeployTime := r.nextDeployTime(obj) + if nextDeployTime != nil { + needDeploy = nextDeployTime.Before(time.Now()) } + } - needDeploy := false - needPrune := false - needValidate := false - - if obj.Status.LastDeployResult == nil || obj.Status.LastObjectsHash != objectsHash { - // either never deployed or source code changed - needDeploy = true - } else if r.checkRequestedDeploy(obj) { - // explicitly requested a deploy - needDeploy = true - } else if obj.Status.ObservedGeneration != obj.GetGeneration() { - // spec has changed - needDeploy = true + if obj.Spec.Validate { + if obj.Status.LastValidateResult == nil || needDeploy { + // either never validated before or a deployment requested (which required re-validation) + needValidate = true } else { - // was deployed before, let's check if we need to do periodic deployments - nextDeployTime := r.nextDeployTime(obj) - if nextDeployTime != nil { - needDeploy = nextDeployTime.Before(time.Now()) + nextValidateTime := r.nextValidateTime(obj) + if nextValidateTime != nil { + needValidate = nextValidateTime.Before(time.Now()) } } + } else { + obj.Status.LastValidateResult = nil + obj.Status.LastValidateError = "" + } - if obj.Spec.Validate { - if obj.Status.LastValidateResult == nil || needDeploy { - // either never validated before or a deployment requested (which required re-validation) - needValidate = true - } else { - nextValidateTime := r.nextValidateTime(obj) - if nextValidateTime != nil { - needValidate = nextValidateTime.Before(time.Now()) - } - } - } else { - obj.Status.LastValidateResult = nil - } + if obj.Spec.Prune { + needPrune = needDeploy + } else { + obj.Status.LastPruneResult = nil + obj.Status.LastPruneError = "" + } - if obj.Spec.Prune { - needPrune = needDeploy + obj.Status.LastObjectsHash = objectsHash + + var deployResult *result.CommandResult + if needDeploy { + // deploy the kluctl project + if obj.Spec.DeployMode == kluctlv1.KluctlDeployModeFull { + deployResult, err = pt.kluctlDeploy(ctx, targetContext) + } else if obj.Spec.DeployMode == kluctlv1.KluctlDeployPokeImages { + deployResult, err = pt.kluctlPokeImages(ctx, targetContext) } else { - obj.Status.LastPruneResult = nil + err = fmt.Errorf("deployMode '%s' not supported", obj.Spec.DeployMode) } - - obj.Status.LastObjectsHash = objectsHash - - var deployResult *result.CommandResult - if needDeploy { - // deploy the kluctl project - if obj.Spec.DeployMode == kluctlv1.KluctlDeployModeFull { - deployResult, err = pt.kluctlDeploy(ctx, targetContext) - } else if obj.Spec.DeployMode == kluctlv1.KluctlDeployPokeImages { - deployResult, err = pt.kluctlPokeImages(ctx, targetContext) - } else { - err = fmt.Errorf("deployMode '%s' not supported", obj.Spec.DeployMode) - setReadiness(obj, metav1.ConditionFalse, kluctlv1.DeployFailedReason, err.Error()) - return nil - } - obj.Status.LastDeployResult = deployResult.BuildSummary() - if err != nil { - setReadiness(obj, metav1.ConditionFalse, kluctlv1.DeployFailedReason, err.Error()) - return nil - } + obj.Status.LastDeployResult = deployResult.BuildSummary() + obj.Status.LastDeployError = "" + if err != nil { + obj.Status.LastDeployError = err.Error() } + } - if needPrune { - // run garbage collection for stale objects that do not have pruning disabled - pruneResult, err := pt.kluctlPrune(ctx, targetContext) - obj.Status.LastPruneResult = pruneResult.BuildSummary() - if err != nil { - setReadiness(obj, metav1.ConditionFalse, kluctlv1.PruneFailedReason, err.Error()) - return nil - } + if needPrune { + // run garbage collection for stale objects that do not have pruning disabled + pruneResult, err := pt.kluctlPrune(ctx, targetContext) + obj.Status.LastPruneResult = pruneResult.BuildSummary() + obj.Status.LastPruneError = "" + if err != nil { + obj.Status.LastPruneError = err.Error() } + } - if needValidate { - validateResult, err := pt.kluctlValidate(ctx, targetContext, deployResult) - obj.Status.LastValidateResult = validateResult - if err != nil { - setReadiness(obj, metav1.ConditionFalse, kluctlv1.ValidateFailedReason, err.Error()) - return nil - } + if needValidate { + validateResult, err := pt.kluctlValidate(ctx, targetContext, deployResult) + obj.Status.LastValidateResult = validateResult + obj.Status.LastValidateError = "" + if err != nil { + obj.Status.LastValidateError = err.Error() } - return nil - }) - obj.Status.ObservedGeneration = obj.GetGeneration() - if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; ok { - obj.Status.LastHandledDeployAt = v - } - if err != nil { - setReadiness(obj, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, err.Error()) - return nil, err } var ctrlResult ctrl.Result @@ -310,6 +327,20 @@ func (r *KluctlDeploymentReconciler) doReconcile( } func (r *KluctlDeploymentReconciler) buildFinalStatus(obj *kluctlv1.KluctlDeployment) (finalStatus string, reason string) { + if obj.Status.LastDeployError != "" { + finalStatus = obj.Status.LastDeployError + reason = kluctlv1.DeployFailedReason + return + } else if obj.Status.LastPruneError != "" { + finalStatus = obj.Status.LastPruneError + reason = kluctlv1.PruneFailedReason + return + } else if obj.Status.LastValidateError != "" { + finalStatus = obj.Status.LastValidateError + reason = kluctlv1.ValidateFailedReason + return + } + deployOk := obj.Status.LastDeployResult != nil && obj.Status.LastDeployResult.Errors == 0 pruneOk := obj.Status.LastPruneResult != nil && obj.Status.LastPruneResult.Errors == 0 validateOk := obj.Status.LastValidateResult != nil && len(obj.Status.LastValidateResult.Errors) == 0 && obj.Status.LastValidateResult.Ready @@ -470,10 +501,7 @@ func (r *KluctlDeploymentReconciler) doFinalize(ctx context.Context, obj *kluctl } defer pp.cleanup() - pt, err := pp.newTarget() - if err != nil { - return - } + pt := pp.newTarget() _, _ = pt.kluctlDelete(ctx, obj.Status.TargetKey.Discriminator) } diff --git a/pkg/controllers/utils.go b/pkg/controllers/utils.go index 7d4476e8d..050f21805 100644 --- a/pkg/controllers/utils.go +++ b/pkg/controllers/utils.go @@ -9,17 +9,16 @@ import ( func setReadiness(obj *kluctlv1.KluctlDeployment, status metav1.ConditionStatus, reason, message string) { newCondition := metav1.Condition{ - Type: meta.ReadyCondition, - Status: status, - Reason: reason, - Message: trimString(message, kluctlv1.MaxConditionMessageLength), + Type: meta.ReadyCondition, + Status: status, + Reason: reason, + Message: trimString(message, kluctlv1.MaxConditionMessageLength), + ObservedGeneration: obj.Generation, } c := obj.GetConditions() apimeta.SetStatusCondition(&c, newCondition) obj.SetConditions(c) - - obj.Status.ObservedGeneration = obj.GetGeneration() } func trimString(str string, limit int) string { From 0c5f350c1a60c44d7eb2fd1fa0be4d5a7dad01f0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 May 2023 11:55:25 +0200 Subject: [PATCH 1012/2268] tests: Implement controller error tests --- e2e/gitops_errors_test.go | 243 ++++++++++++++++++++++++++++++++++++++ e2e/gitops_test.go | 111 ++++++++--------- 2 files changed, 301 insertions(+), 53 deletions(-) create mode 100644 e2e/gitops_errors_test.go diff --git a/e2e/gitops_errors_test.go b/e2e/gitops_errors_test.go new file mode 100644 index 000000000..ffbc0e008 --- /dev/null +++ b/e2e/gitops_errors_test.go @@ -0,0 +1,243 @@ +package e2e + +import ( + "context" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "testing" +) + +func getReadiness(obj *kluctlv1.KluctlDeployment) *metav1.Condition { + for _, c := range obj.Status.Conditions { + if c.Type == meta.ReadyCondition { + return &c + } + } + return nil +} + +func assertErrors(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey, rstatus metav1.ConditionStatus, rreason string, rmessage string, expectedErrors []result.DeploymentError, expectedWarnings []result.DeploymentError) { + g := NewWithT(t) + + var kd kluctlv1.KluctlDeployment + err := k.Client.Get(context.TODO(), key, &kd) + g.Expect(err).To(Succeed()) + + g.Expect(kd.Status.LastDeployResult).ToNot(BeNil()) + + readinessCondition := getReadiness(&kd) + g.Expect(readinessCondition).ToNot(BeNil()) + + g.Expect(readinessCondition.Status).To(Equal(rstatus)) + g.Expect(readinessCondition.Reason).To(Equal(rreason)) + g.Expect(readinessCondition.Message).To(ContainSubstring(rmessage)) + + rs, err := results.NewResultStoreSecrets(context.TODO(), k.Client, nil, "", 0) + g.Expect(err).To(Succeed()) + + cr, err := rs.GetCommandResult(results.GetCommandResultOptions{ + Id: kd.Status.LastDeployResult.Id, + }) + g.Expect(err).To(Succeed()) + + g.Expect(cr.Errors).To(ConsistOf(expectedErrors)) + g.Expect(cr.Warnings).To(ConsistOf(expectedWarnings)) + + g.Expect(kd.Status.LastDeployResult.Errors).To(Equal(len(expectedErrors))) + g.Expect(kd.Status.LastDeployResult.Warnings).To(Equal(len(expectedWarnings))) +} + +func TestGitOpsErrors(t *testing.T) { + g := NewWithT(t) + _ = g + + startKluctlController(t) + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + createNamespace(t, k, p.TestSlug()) + + goodCm1 := `apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 + namespace: "{{ args.namespace }}" +data: + k1: v1 +` + badCm1_1 := `apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 + namespace: "{{ args.namespace }}" +data_error: + k1: v1 +` + badCm1_2 := `apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 + namespace: "{{ args.namespace }}" +data: + k1: { +` + + p.UpdateTarget("target1", nil) + p.AddKustomizeDeployment("d1", []test_utils.KustomizeResource{ + {Name: "cm1.yaml", Content: uo.FromStringMust(goodCm1)}, + }, nil) + + key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + "namespace": p.TestSlug(), + }) + + t.Run("initial deployment", func(t *testing.T) { + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + + cm1Ref := k8s.NewObjectRef("", "v1", "ConfigMap", "cm1", p.TestSlug()) + + t.Run("cm1 causes error while applying", func(t *testing.T) { + p.UpdateFile("d1/cm1.yaml", func(f string) (string, error) { + return badCm1_1, nil + }, "") + waitForReconcile(t, k, key) + assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.DeployFailedReason, "deploy failed with 1 errors", []result.DeploymentError{ + { + Ref: cm1Ref, + Message: "failed to patch test-git-ops-errors/ConfigMap/cm1: failed to create typed patch object (test-git-ops-errors/cm1; /v1, Kind=ConfigMap): .data_error: field not declared in schema", + }, + }, nil) + p.UpdateFile("d1/cm1.yaml", func(f string) (string, error) { + return goodCm1, nil + }, "") + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + + t.Run("cm1 causes error while loading", func(t *testing.T) { + p.UpdateFile("d1/cm1.yaml", func(f string) (string, error) { + return badCm1_2, nil + }, "") + waitForReconcile(t, k, key) + assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "MalformedYAMLError: yaml: line 7: did not find expected node content in File: cm1.yaml", nil, nil) + p.UpdateFile("d1/cm1.yaml", func(f string) (string, error) { + return goodCm1, nil + }, "") + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + + t.Run("project can't be loaded", func(t *testing.T) { + kluctlBackup := "" + p.UpdateFile(".kluctl.yml", func(f string) (string, error) { + kluctlBackup = f + return "a: b", nil + }, "") + waitForReconcile(t, k, key) + assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, ".kluctl.yml failed: error unmarshaling JSON: while decoding JSON: json: unknown field \"a\"", nil, nil) + p.UpdateFile(".kluctl.yml", func(f string) (string, error) { + return kluctlBackup, nil + }, "") + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + + t.Run("deployment can't be loaded", func(t *testing.T) { + deploymentBackup := "" + p.UpdateFile("deployment.yml", func(f string) (string, error) { + deploymentBackup = f + return "a: b", nil + }, "") + waitForReconcile(t, k, key) + assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "failed to load deployment.yml: error unmarshaling JSON: while decoding JSON: json: unknown field \"a\"", nil, nil) + p.UpdateFile("deployment.yml", func(f string) (string, error) { + return deploymentBackup, nil + }, "") + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + + t.Run("invalid target", func(t *testing.T) { + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Target = utils.StrPtr("invalid") + }) + waitForReconcile(t, k, key) + assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "target invalid not existent in kluctl project config", nil, nil) + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Target = utils.StrPtr("target1") + }) + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + + t.Run("invalid context", func(t *testing.T) { + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Context = utils.StrPtr("invalid") + }) + waitForReconcile(t, k, key) + assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "context \"invalid\" does not exist", nil, nil) + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Context = utils.StrPtr("default") + }) + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + + t.Run("non existing git repo", func(t *testing.T) { + var backup types.GitUrl + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + backup = kd.Spec.Source.URL + kd.Spec.Source.URL = *types.ParseGitUrlMust(backup.String() + "/invalid") + }) + waitForReconcile(t, k, key) + assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "failed clone source: repository not found", nil, nil) + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Source.URL = backup + }) + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + t.Run("non existing git branch", func(t *testing.T) { + var backup *kluctlv1.GitRef + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + backup = kd.Spec.Source.Ref + kd.Spec.Source.Ref = &kluctlv1.GitRef{ + Branch: "invalid", + } + }) + waitForReconcile(t, k, key) + assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "ref refs/heads/invalid not found", nil, nil) + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Source.Ref = backup + }) + waitForCommit(t, k, key, getHeadRevision(t, p)) + }) + + t.Run("prune without discriminator", func(t *testing.T) { + var backup any + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Prune = true + }) + p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { + backup, _, _ = o.GetNestedField("discriminator") + _ = o.RemoveNestedField("discriminator") + return nil + }) + waitForCommit(t, k, key, getHeadRevision(t, p)) + assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PruneFailedReason, "pruning without a discriminator is not supported", nil, nil) + p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(backup, "discriminator") + return nil + }) + waitForCommit(t, k, key, getHeadRevision(t, p)) + assertErrors(t, k, key, metav1.ConditionTrue, kluctlv1.ReconciliationSucceededReason, "deploy: ok, prune: ok", nil, nil) + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Prune = false + }) + }) +} diff --git a/e2e/gitops_test.go b/e2e/gitops_test.go index 56d1cdfe0..95e2b3631 100644 --- a/e2e/gitops_test.go +++ b/e2e/gitops_test.go @@ -3,16 +3,17 @@ package e2e import ( "context" "encoding/json" + "fmt" kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/onsi/gomega" - "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "math/rand" "sigs.k8s.io/controller-runtime/pkg/client" "strings" "testing" @@ -49,37 +50,42 @@ func startKluctlController(t *testing.T) context.CancelFunc { return cancel } +func triggerReconcile(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey) string { + reconcileId := fmt.Sprintf("%d", rand.Int63()) + + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + a := kd.GetAnnotations() + if a == nil { + a = map[string]string{} + } + a[kluctlv1.KluctlRequestReconcileAnnotation] = reconcileId + kd.SetAnnotations(a) + }) + return reconcileId +} + func waitForReconcile(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey) { g := gomega.NewWithT(t) - var kd kluctlv1.KluctlDeployment - err := k.Client.Get(context.TODO(), key, &kd) - assert.NoError(t, err) + reconcileId := triggerReconcile(t, k, key) - var lastReconcileTime metav1.Time - if kd.Status.LastDeployResult != nil { - lastReconcileTime = kd.Status.LastDeployResult.Command.StartTime - } g.Eventually(func() bool { - err = k.Client.Get(context.TODO(), key, &kd) + var kd kluctlv1.KluctlDeployment + err := k.Client.Get(context.TODO(), key, &kd) g.Expect(err).To(Succeed()) - if kd.Status.LastDeployResult == nil { - return false - } - return kd.Status.LastDeployResult.Command.StartTime != lastReconcileTime + return kd.Status.LastHandledReconcileAt == reconcileId }, timeout, time.Second).Should(BeTrue()) } func waitForCommit(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey, commit string) { g := gomega.NewWithT(t) + reconcileId := triggerReconcile(t, k, key) + g.Eventually(func() bool { - var obj kluctlv1.KluctlDeployment - _ = k.Client.Get(context.Background(), key, &obj) - if obj.Status.LastDeployResult == nil { - return false - } - return obj.Status.LastDeployResult.GitInfo.Commit == commit + var kd kluctlv1.KluctlDeployment + _ = k.Client.Get(context.Background(), key, &kd) + return kd.Status.LastHandledReconcileAt == reconcileId && kd.Status.ObservedCommit == commit }, timeout, time.Second).Should(BeTrue()) } @@ -118,6 +124,23 @@ func createKluctlDeployment(t *testing.T, p *test_utils.TestProject, k *test_uti return client.ObjectKeyFromObject(kluctlDeployment) } +func updateKluctlDeployment(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey, update func(kd *kluctlv1.KluctlDeployment)) *kluctlv1.KluctlDeployment { + g := NewWithT(t) + + var kd kluctlv1.KluctlDeployment + err := k.Client.Get(context.TODO(), key, &kd) + g.Expect(err).To(Succeed()) + + patch := client.MergeFrom(kd.DeepCopy()) + + update(&kd) + + err = k.Client.Patch(context.TODO(), &kd, patch, client.FieldOwner("kubectl")) + g.Expect(err).To(Succeed()) + + return &kd +} + func TestGitOpsFieldManager(t *testing.T) { g := NewWithT(t) @@ -150,13 +173,9 @@ data: waitForCommit(t, k, key, getHeadRevision(t, p)) }) - var kd kluctlv1.KluctlDeployment - err := k.Client.Get(context.TODO(), key, &kd) - g.Expect(err).To(Succeed()) - - kd.Spec.DeployInterval = &kluctlv1.SafeDuration{Duration: metav1.Duration{Duration: interval}} - err = k.Client.Update(context.TODO(), &kd) - g.Expect(err).To(Succeed()) + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.DeployInterval = &kluctlv1.SafeDuration{Duration: metav1.Duration{Duration: interval}} + }) cm := &corev1.ConfigMap{} @@ -222,12 +241,9 @@ data: g.Expect(cm.Data).To(HaveKeyWithValue("k1", "v2")) }) - err = k.Client.Get(context.TODO(), key, &kd) - g.Expect(err).To(Succeed()) - - kd.Spec.ForceApply = true - err = k.Client.Update(context.TODO(), &kd) - g.Expect(err).To(Succeed()) + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.ForceApply = true + }) t.Run("forceApply is true and cm1 gets restored even with another field manager", func(t *testing.T) { patch := client.MergeFrom(cm.DeepCopy()) @@ -327,9 +343,9 @@ func TestKluctlDeploymentReconciler_Helm(t *testing.T) { err := k.Client.Create(context.TODO(), credsSecret) g.Expect(err).To(Succeed()) - kd.Spec.HelmCredentials = append(kd.Spec.HelmCredentials, kluctlv1.HelmCredentials{SecretRef: kluctlv1.LocalObjectReference{Name: "helm-secrets-1"}}) - err = k.Client.Update(context.TODO(), kd) - g.Expect(err).To(Succeed()) + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.HelmCredentials = append(kd.Spec.HelmCredentials, kluctlv1.HelmCredentials{SecretRef: kluctlv1.LocalObjectReference{Name: "helm-secrets-1"}}) + }) t.Run("chart with credentials succeeds", func(t *testing.T) { g.Eventually(func() bool { @@ -494,19 +510,11 @@ data: g.Expect(err).To(Succeed()) }) - kd := &kluctlv1.KluctlDeployment{} - g.Expect(k.Client.Get(context.TODO(), key, kd)).To(Succeed()) - kd.Spec.Prune = true + updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Prune = true + }) - g.Expect(k.Client.Update(context.TODO(), kd)).To(Succeed()) - g.Eventually(func() bool { - var obj kluctlv1.KluctlDeployment - _ = k.Client.Get(context.Background(), key, &obj) - if obj.Status.LastDeployResult == nil { - return false - } - return obj.Status.ObservedGeneration == obj.Generation && obj.Status.LastDeployResult.GitInfo.Commit == getHeadRevision(t, p) - }, timeout, time.Second).Should(BeTrue()) + waitForReconcile(t, k, key) t.Run("cm1 did not get deleted and cm2 got deleted", func(t *testing.T) { err := k.Client.Get(context.TODO(), client.ObjectKey{ @@ -548,12 +556,9 @@ data: "namespace": p.TestSlug(), }) - kd := &kluctlv1.KluctlDeployment{} - err := k.Client.Get(context.TODO(), key, kd) - g.Expect(err).To(Succeed()) - - kd.Spec.Delete = delete - g.Expect(k.Client.Update(context.TODO(), kd)).To(Succeed()) + kd := updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + kd.Spec.Delete = delete + }) waitForCommit(t, k, key, getHeadRevision(t, p)) From ae204b7b1e28ad5fdd9ae7d09000ca4219a62daa Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 May 2023 12:59:57 +0200 Subject: [PATCH 1013/2268] fix: Fix race condition in GetCommandResult --- pkg/results/result-store-secrets.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 88527503c..5d244d538 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -399,30 +399,30 @@ func (s *ResultStoreSecrets) GetCommandResult(options GetCommandResultOptions) ( var crJson, objectsJson []byte err = utils.RunParallelE(s.ctx, func() error { - var ok bool - crJson, ok = secret.Data["reducedResult"] + j, ok := secret.Data["reducedResult"] if !ok { return fmt.Errorf("reducedResult field not present for %s", options.Id) } - crJson, err = utils.UncompressGzip(crJson) + j, err := utils.UncompressGzip(j) if err != nil { return err } + crJson = j return nil }, func() error { if options.Reduced { return nil } - var ok bool - objectsJson, ok = secret.Data["compactedObjects"] + j, ok := secret.Data["compactedObjects"] if !ok { return fmt.Errorf("compactedObjects field not present for %s", options.Id) } - objectsJson, err = utils.UncompressGzip(objectsJson) + j, err := utils.UncompressGzip(j) if err != nil { return err } - return err + objectsJson = j + return nil }) if err != nil { return nil, err From 07bedd0505273994e92ab331d7812afe7b812b74 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 May 2023 15:22:17 +0200 Subject: [PATCH 1014/2268] feat: Allow to override kubeconfig and context --- cmd/kluctl/commands/cmd_controller.go | 84 ++++++++++++++++++++++++++- pkg/utils/flux_utils/client.go | 84 --------------------------- 2 files changed, 81 insertions(+), 87 deletions(-) delete mode 100644 pkg/utils/flux_utils/client.go diff --git a/cmd/kluctl/commands/cmd_controller.go b/cmd/kluctl/commands/cmd_controller.go index 3e3f9df86..132e7a55b 100644 --- a/cmd/kluctl/commands/cmd_controller.go +++ b/cmd/kluctl/commands/cmd_controller.go @@ -2,16 +2,23 @@ package commands import ( "context" + "fmt" kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" "github.com/kluctl/kluctl/v2/pkg/controllers" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" - "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" + log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "os" + "os/user" + "path/filepath" + "sigs.k8s.io/cli-utils/pkg/flowcontrol" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -32,6 +39,9 @@ const controllerName = "kluctl-controller" type controllerCmd struct { scheme *runtime.Scheme + Kubeconfig string `group:"controller" help:"Override the kubeconfig to use."` + Context string `group:"controller" help:"Override the context to use."` + MetricsBindAddress string `group:"controller" help:"The address the metric endpoint binds to." default:":8080"` HealthProbeBindAddress string `group:"controller" help:"The address the probe endpoint binds to." default:":8081"` LeaderElect bool `group:"controller" help:"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager."` @@ -64,14 +74,27 @@ func (cmd *controllerCmd) Run(ctx context.Context) error { } ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) - restConfig := flux_utils.GetConfigOrDie(flux_utils.Options{}) + restConfig, err := cmd.loadConfig(cmd.Kubeconfig, cmd.Context) + if err != nil { + setupLog.Error(err, "unable to load kubeconfig") + os.Exit(1) + } + + enabled, err := flowcontrol.IsEnabled(context.Background(), restConfig) + if err == nil && enabled { + // A negative QPS and Burst indicates that the client should not have a rate limiter. + // Ref: https://github.com/kubernetes/kubernetes/blob/v1.24.0/staging/src/k8s.io/client-go/rest/config.go#L354-L364 + restConfig.QPS = -1 + restConfig.Burst = -1 + } + clientSet, err := kubernetes.NewForConfig(restConfig) if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1) } - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + mgr, err := ctrl.NewManager(restConfig, ctrl.Options{ Scheme: cmd.scheme, MetricsBindAddress: cmd.MetricsBindAddress, Port: 9443, @@ -138,3 +161,58 @@ func (cmd *controllerCmd) Run(ctx context.Context) error { return nil } + +// taken from clientcmd +func (cmd *controllerCmd) loadConfig(kubeconfig string, context string) (config *rest.Config, configErr error) { + // If a flag is specified with the config location, use that + if len(kubeconfig) > 0 { + return cmd.loadConfigWithContext("", &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, context) + } + + // If the recommended kubeconfig env variable is not specified, + // try the in-cluster config. + kubeconfigPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar) + if len(kubeconfigPath) == 0 { + c, err := rest.InClusterConfig() + if err == nil { + return c, nil + } + + defer func() { + if configErr != nil { + log.Error(err, "unable to load in-cluster config") + } + }() + } + + // If the recommended kubeconfig env variable is set, or there + // is no in-cluster config, try the default recommended locations. + // + // NOTE: For default config file locations, upstream only checks + // $HOME for the user's home directory, but we can also try + // os/user.HomeDir when $HOME is unset. + // + // TODO(jlanford): could this be done upstream? + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + if _, ok := os.LookupEnv("HOME"); !ok { + u, err := user.Current() + if err != nil { + return nil, fmt.Errorf("could not get current user: %w", err) + } + loadingRules.Precedence = append(loadingRules.Precedence, filepath.Join(u.HomeDir, clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName)) + } + + return cmd.loadConfigWithContext("", loadingRules, context) +} + +// taken from clientcmd +func (cmd *controllerCmd) loadConfigWithContext(apiServerURL string, loader clientcmd.ClientConfigLoader, context string) (*rest.Config, error) { + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + loader, + &clientcmd.ConfigOverrides{ + ClusterInfo: clientcmdapi.Cluster{ + Server: apiServerURL, + }, + CurrentContext: context, + }).ClientConfig() +} diff --git a/pkg/utils/flux_utils/client.go b/pkg/utils/flux_utils/client.go deleted file mode 100644 index 05e796cc7..000000000 --- a/pkg/utils/flux_utils/client.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright 2021 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package flux_utils - -import ( - "context" - - "github.com/spf13/pflag" - "k8s.io/client-go/rest" - "sigs.k8s.io/cli-utils/pkg/flowcontrol" - ctrl "sigs.k8s.io/controller-runtime" -) - -const ( - flagQPS = "kube-api-qps" - flagBurst = "kube-api-burst" -) - -// Options contains the runtime configuration for a Kubernetes client. -// -// The struct can be used in the main.go file of your controller by binding it to the main flag set, and then utilizing -// the configured options later: -// -// func main() { -// var ( -// // other controller specific configuration variables -// clientOptions client.Options -// ) -// -// // Bind the options to the main flag set, and parse it -// clientOptions.BindFlags(flag.CommandLine) -// flag.Parse() -// -// // Get a runtime Kubernetes client configuration with the options set -// restConfig := client.GetConfigOrDie(clientOptions) -// } -type Options struct { - // QPS indicates the maximum queries-per-second of requests sent to the Kubernetes API, defaults to 50. - QPS float32 - - // Burst indicates the maximum burst queries-per-second of requests sent to the Kubernetes API, defaults to 300. - Burst int -} - -// BindFlags will parse the given pflag.FlagSet for Kubernetes client option flags and set the Options accordingly. -func (o *Options) BindFlags(fs *pflag.FlagSet) { - fs.Float32Var(&o.QPS, flagQPS, 50.0, - "The maximum queries-per-second of requests sent to the Kubernetes API.") - fs.IntVar(&o.Burst, flagBurst, 300, - "The maximum burst queries-per-second of requests sent to the Kubernetes API.") -} - -// GetConfigOrDie wraps ctrl.GetConfigOrDie and checks if the Kubernetes apiserver -// has PriorityAndFairness flow control filter enabled. If true, it returns a rest.Config -// with client side throttling disabled. Otherwise, it returns a modified rest.Config -// configured with the provided Options. -func GetConfigOrDie(opts Options) *rest.Config { - config := ctrl.GetConfigOrDie() - enabled, err := flowcontrol.IsEnabled(context.Background(), config) - if err == nil && enabled { - // A negative QPS and Burst indicates that the client should not have a rate limiter. - // Ref: https://github.com/kubernetes/kubernetes/blob/v1.24.0/staging/src/k8s.io/client-go/rest/config.go#L354-L364 - config.QPS = -1 - config.Burst = -1 - return config - } - config.QPS = opts.QPS - config.Burst = opts.Burst - return config -} From eebfafc9ca0c37306112f4efd2e4bae59be98252 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 May 2023 15:22:35 +0200 Subject: [PATCH 1015/2268] tests: Run gitops tests in own test suite with own cluster --- e2e/default_clusters.go | 3 - e2e/gitops_errors_test.go | 127 ++++++++-------- e2e/gitops_test.go | 309 +++++++++++++++++++++++--------------- 3 files changed, 252 insertions(+), 187 deletions(-) diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 8765afb63..373957a89 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -22,9 +22,6 @@ func init() { return } - defaultCluster1.CRDDirectoryPaths = []string{"../config/crd/bases"} - defaultCluster2.CRDDirectoryPaths = []string{"../config/crd/bases"} - var wg sync.WaitGroup wg.Add(2) go func() { diff --git a/e2e/gitops_errors_test.go b/e2e/gitops_errors_test.go index ffbc0e008..56249cf9b 100644 --- a/e2e/gitops_errors_test.go +++ b/e2e/gitops_errors_test.go @@ -14,10 +14,9 @@ import ( . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" - "testing" ) -func getReadiness(obj *kluctlv1.KluctlDeployment) *metav1.Condition { +func (suite *GitopsTestSuite) getReadiness(obj *kluctlv1.KluctlDeployment) *metav1.Condition { for _, c := range obj.Status.Conditions { if c.Type == meta.ReadyCondition { return &c @@ -26,23 +25,23 @@ func getReadiness(obj *kluctlv1.KluctlDeployment) *metav1.Condition { return nil } -func assertErrors(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey, rstatus metav1.ConditionStatus, rreason string, rmessage string, expectedErrors []result.DeploymentError, expectedWarnings []result.DeploymentError) { - g := NewWithT(t) +func (suite *GitopsTestSuite) assertErrors(key client.ObjectKey, rstatus metav1.ConditionStatus, rreason string, rmessage string, expectedErrors []result.DeploymentError, expectedWarnings []result.DeploymentError) { + g := NewWithT(suite.T()) var kd kluctlv1.KluctlDeployment - err := k.Client.Get(context.TODO(), key, &kd) + err := suite.k.Client.Get(context.TODO(), key, &kd) g.Expect(err).To(Succeed()) g.Expect(kd.Status.LastDeployResult).ToNot(BeNil()) - readinessCondition := getReadiness(&kd) + readinessCondition := suite.getReadiness(&kd) g.Expect(readinessCondition).ToNot(BeNil()) g.Expect(readinessCondition.Status).To(Equal(rstatus)) g.Expect(readinessCondition.Reason).To(Equal(rreason)) g.Expect(readinessCondition.Message).To(ContainSubstring(rmessage)) - rs, err := results.NewResultStoreSecrets(context.TODO(), k.Client, nil, "", 0) + rs, err := results.NewResultStoreSecrets(context.TODO(), suite.k.Client, nil, "", 0) g.Expect(err).To(Succeed()) cr, err := rs.GetCommandResult(results.GetCommandResultOptions{ @@ -57,16 +56,12 @@ func assertErrors(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectK g.Expect(kd.Status.LastDeployResult.Warnings).To(Equal(len(expectedWarnings))) } -func TestGitOpsErrors(t *testing.T) { - g := NewWithT(t) +func (suite *GitopsTestSuite) TestGitOpsErrors() { + g := NewWithT(suite.T()) _ = g - startKluctlController(t) - - k := defaultCluster1 - - p := test_utils.NewTestProject(t) - createNamespace(t, k, p.TestSlug()) + p := test_utils.NewTestProject(suite.T()) + createNamespace(suite.T(), suite.k, p.TestSlug()) goodCm1 := `apiVersion: v1 kind: ConfigMap @@ -98,129 +93,129 @@ data: {Name: "cm1.yaml", Content: uo.FromStringMust(goodCm1)}, }, nil) - key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + key := suite.createKluctlDeployment(p, "target1", map[string]any{ "namespace": p.TestSlug(), }) - t.Run("initial deployment", func(t *testing.T) { - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.Run("initial deployment", func() { + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) cm1Ref := k8s.NewObjectRef("", "v1", "ConfigMap", "cm1", p.TestSlug()) - t.Run("cm1 causes error while applying", func(t *testing.T) { + suite.Run("cm1 causes error while applying", func() { p.UpdateFile("d1/cm1.yaml", func(f string) (string, error) { return badCm1_1, nil }, "") - waitForReconcile(t, k, key) - assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.DeployFailedReason, "deploy failed with 1 errors", []result.DeploymentError{ + suite.waitForReconcile(key) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.DeployFailedReason, "deploy failed with 1 errors", []result.DeploymentError{ { Ref: cm1Ref, - Message: "failed to patch test-git-ops-errors/ConfigMap/cm1: failed to create typed patch object (test-git-ops-errors/cm1; /v1, Kind=ConfigMap): .data_error: field not declared in schema", + Message: "failed to patch test-git-ops-test-git-ops-errors/ConfigMap/cm1: failed to create typed patch object (test-git-ops-test-git-ops-errors/cm1; /v1, Kind=ConfigMap): .data_error: field not declared in schema", }, }, nil) p.UpdateFile("d1/cm1.yaml", func(f string) (string, error) { return goodCm1, nil }, "") - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) - t.Run("cm1 causes error while loading", func(t *testing.T) { + suite.Run("cm1 causes error while loading", func() { p.UpdateFile("d1/cm1.yaml", func(f string) (string, error) { return badCm1_2, nil }, "") - waitForReconcile(t, k, key) - assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "MalformedYAMLError: yaml: line 7: did not find expected node content in File: cm1.yaml", nil, nil) + suite.waitForReconcile(key) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "MalformedYAMLError: yaml: line 7: did not find expected node content in File: cm1.yaml", nil, nil) p.UpdateFile("d1/cm1.yaml", func(f string) (string, error) { return goodCm1, nil }, "") - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) - t.Run("project can't be loaded", func(t *testing.T) { + suite.Run("project can't be loaded", func() { kluctlBackup := "" p.UpdateFile(".kluctl.yml", func(f string) (string, error) { kluctlBackup = f return "a: b", nil }, "") - waitForReconcile(t, k, key) - assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, ".kluctl.yml failed: error unmarshaling JSON: while decoding JSON: json: unknown field \"a\"", nil, nil) + suite.waitForReconcile(key) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, ".kluctl.yml failed: error unmarshaling JSON: while decoding JSON: json: unknown field \"a\"", nil, nil) p.UpdateFile(".kluctl.yml", func(f string) (string, error) { return kluctlBackup, nil }, "") - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) - t.Run("deployment can't be loaded", func(t *testing.T) { + suite.Run("deployment can't be loaded", func() { deploymentBackup := "" p.UpdateFile("deployment.yml", func(f string) (string, error) { deploymentBackup = f return "a: b", nil }, "") - waitForReconcile(t, k, key) - assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "failed to load deployment.yml: error unmarshaling JSON: while decoding JSON: json: unknown field \"a\"", nil, nil) + suite.waitForReconcile(key) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "failed to load deployment.yml: error unmarshaling JSON: while decoding JSON: json: unknown field \"a\"", nil, nil) p.UpdateFile("deployment.yml", func(f string) (string, error) { return deploymentBackup, nil }, "") - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) - t.Run("invalid target", func(t *testing.T) { - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.Run("invalid target", func() { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Target = utils.StrPtr("invalid") }) - waitForReconcile(t, k, key) - assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "target invalid not existent in kluctl project config", nil, nil) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.waitForReconcile(key) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "target invalid not existent in kluctl project config", nil, nil) + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Target = utils.StrPtr("target1") }) - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) - t.Run("invalid context", func(t *testing.T) { - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.Run("invalid context", func() { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Context = utils.StrPtr("invalid") }) - waitForReconcile(t, k, key) - assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "context \"invalid\" does not exist", nil, nil) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.waitForReconcile(key) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "context \"invalid\" does not exist", nil, nil) + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Context = utils.StrPtr("default") }) - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) - t.Run("non existing git repo", func(t *testing.T) { + suite.Run("non existing git repo", func() { var backup types.GitUrl - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { backup = kd.Spec.Source.URL kd.Spec.Source.URL = *types.ParseGitUrlMust(backup.String() + "/invalid") }) - waitForReconcile(t, k, key) - assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "failed clone source: repository not found", nil, nil) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.waitForReconcile(key) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "failed clone source: repository not found", nil, nil) + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Source.URL = backup }) - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) - t.Run("non existing git branch", func(t *testing.T) { + suite.Run("non existing git branch", func() { var backup *kluctlv1.GitRef - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { backup = kd.Spec.Source.Ref kd.Spec.Source.Ref = &kluctlv1.GitRef{ Branch: "invalid", } }) - waitForReconcile(t, k, key) - assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "ref refs/heads/invalid not found", nil, nil) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.waitForReconcile(key) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.PrepareFailedReason, "ref refs/heads/invalid not found", nil, nil) + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Source.Ref = backup }) - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) - t.Run("prune without discriminator", func(t *testing.T) { + suite.Run("prune without discriminator", func() { var backup any - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Prune = true }) p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { @@ -228,15 +223,15 @@ data: _ = o.RemoveNestedField("discriminator") return nil }) - waitForCommit(t, k, key, getHeadRevision(t, p)) - assertErrors(t, k, key, metav1.ConditionFalse, kluctlv1.PruneFailedReason, "pruning without a discriminator is not supported", nil, nil) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.PruneFailedReason, "pruning without a discriminator is not supported", nil, nil) p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { _ = o.SetNestedField(backup, "discriminator") return nil }) - waitForCommit(t, k, key, getHeadRevision(t, p)) - assertErrors(t, k, key, metav1.ConditionTrue, kluctlv1.ReconciliationSucceededReason, "deploy: ok, prune: ok", nil, nil) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) + suite.assertErrors(key, metav1.ConditionTrue, kluctlv1.ReconciliationSucceededReason, "deploy: ok, prune: ok", nil, nil) + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Prune = false }) }) diff --git a/e2e/gitops_test.go b/e2e/gitops_test.go index 95e2b3631..191a65c5b 100644 --- a/e2e/gitops_test.go +++ b/e2e/gitops_test.go @@ -9,11 +9,14 @@ import ( types2 "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/onsi/gomega" + "github.com/stretchr/testify/suite" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "math/rand" + "os" + "path/filepath" "sigs.k8s.io/controller-runtime/pkg/client" "strings" "testing" @@ -27,16 +30,82 @@ const ( interval = time.Second * 5 ) -func startKluctlController(t *testing.T) context.CancelFunc { +type GitopsTestSuite struct { + suite.Suite + + k *test_utils.EnvTestCluster + + cancelController context.CancelFunc + + deployments []client.ObjectKey +} + +func (suite *GitopsTestSuite) SetupSuite() { + suite.startCluster() + suite.startController() +} + +func (suite *GitopsTestSuite) TearDownSuite() { + if suite.cancelController != nil { + suite.cancelController() + } + + if suite.k != nil { + suite.k.Stop() + } +} + +func (suite *GitopsTestSuite) TearDownTest() { + g := NewWithT(suite.T()) + + for _, key := range suite.deployments { + suite.deleteKluctlDeployment(key) + } + + g.Eventually(func() bool { + for _, key := range suite.deployments { + var kd kluctlv1.KluctlDeployment + err := suite.k.Client.Get(context.TODO(), key, &kd) + if err == nil { + return false + } + } + return true + }, timeout, time.Second).Should(BeTrue()) + + suite.deployments = nil +} + +func (suite *GitopsTestSuite) startCluster() { + suite.k = test_utils.CreateEnvTestCluster("context1") + suite.k.CRDDirectoryPaths = []string{"../config/crd/bases"} + + err := suite.k.Start() + if err != nil { + suite.T().Fatal(err) + } +} + +func (suite *GitopsTestSuite) startController() { + tmpKubeconfig := filepath.Join(suite.T().TempDir(), "kubeconfig") + err := os.WriteFile(tmpKubeconfig, suite.k.Kubeconfig, 0o600) + if err != nil { + suite.T().Fatal(err) + } + ctx, ctxCancel := context.WithCancel(context.Background()) args := []string{ "controller", + "--kubeconfig", + tmpKubeconfig, + "--context", + "context1", } done := make(chan struct{}) go func() { - _, _, err := test_utils.KluctlExecute(t, ctx, args...) + _, _, err := test_utils.KluctlExecute(suite.T(), ctx, args...) if err != nil { - t.Error(err) + suite.T().Error(err) } close(done) }() @@ -45,15 +114,13 @@ func startKluctlController(t *testing.T) context.CancelFunc { ctxCancel() <-done } - - t.Cleanup(cancel) - return cancel + suite.cancelController = cancel } -func triggerReconcile(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey) string { +func (suite *GitopsTestSuite) triggerReconcile(key client.ObjectKey) string { reconcileId := fmt.Sprintf("%d", rand.Int63()) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { a := kd.GetAnnotations() if a == nil { a = map[string]string{} @@ -64,38 +131,38 @@ func triggerReconcile(t *testing.T, k *test_utils.EnvTestCluster, key client.Obj return reconcileId } -func waitForReconcile(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey) { - g := gomega.NewWithT(t) +func (suite *GitopsTestSuite) waitForReconcile(key client.ObjectKey) { + g := gomega.NewWithT(suite.T()) - reconcileId := triggerReconcile(t, k, key) + reconcileId := suite.triggerReconcile(key) g.Eventually(func() bool { var kd kluctlv1.KluctlDeployment - err := k.Client.Get(context.TODO(), key, &kd) + err := suite.k.Client.Get(context.TODO(), key, &kd) g.Expect(err).To(Succeed()) return kd.Status.LastHandledReconcileAt == reconcileId }, timeout, time.Second).Should(BeTrue()) } -func waitForCommit(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey, commit string) { - g := gomega.NewWithT(t) +func (suite *GitopsTestSuite) waitForCommit(key client.ObjectKey, commit string) { + g := gomega.NewWithT(suite.T()) - reconcileId := triggerReconcile(t, k, key) + reconcileId := suite.triggerReconcile(key) g.Eventually(func() bool { var kd kluctlv1.KluctlDeployment - _ = k.Client.Get(context.Background(), key, &kd) + _ = suite.k.Client.Get(context.Background(), key, &kd) return kd.Status.LastHandledReconcileAt == reconcileId && kd.Status.ObservedCommit == commit }, timeout, time.Second).Should(BeTrue()) } -func createKluctlDeployment(t *testing.T, p *test_utils.TestProject, k *test_utils.EnvTestCluster, target string, args map[string]any) client.ObjectKey { +func (suite *GitopsTestSuite) createKluctlDeployment(p *test_utils.TestProject, target string, args map[string]any) client.ObjectKey { gitopsNs := p.TestSlug() + "-gitops" - createNamespace(t, k, gitopsNs) + createNamespace(suite.T(), suite.k, gitopsNs) jargs, err := json.Marshal(args) if err != nil { - t.Fatal(err) + suite.T().Fatal(err) } kluctlDeployment := &kluctlv1.KluctlDeployment{ @@ -116,40 +183,50 @@ func createKluctlDeployment(t *testing.T, p *test_utils.TestProject, k *test_uti }, } - err = k.Client.Create(context.Background(), kluctlDeployment) + err = suite.k.Client.Create(context.Background(), kluctlDeployment) if err != nil { - t.Fatal(err) + suite.T().Fatal(err) } - return client.ObjectKeyFromObject(kluctlDeployment) + key := client.ObjectKeyFromObject(kluctlDeployment) + suite.deployments = append(suite.deployments, key) + return key } -func updateKluctlDeployment(t *testing.T, k *test_utils.EnvTestCluster, key client.ObjectKey, update func(kd *kluctlv1.KluctlDeployment)) *kluctlv1.KluctlDeployment { - g := NewWithT(t) +func (suite *GitopsTestSuite) updateKluctlDeployment(key client.ObjectKey, update func(kd *kluctlv1.KluctlDeployment)) *kluctlv1.KluctlDeployment { + g := NewWithT(suite.T()) var kd kluctlv1.KluctlDeployment - err := k.Client.Get(context.TODO(), key, &kd) + err := suite.k.Client.Get(context.TODO(), key, &kd) g.Expect(err).To(Succeed()) patch := client.MergeFrom(kd.DeepCopy()) update(&kd) - err = k.Client.Patch(context.TODO(), &kd, patch, client.FieldOwner("kubectl")) + err = suite.k.Client.Patch(context.TODO(), &kd, patch, client.FieldOwner("kubectl")) g.Expect(err).To(Succeed()) return &kd } -func TestGitOpsFieldManager(t *testing.T) { - g := NewWithT(t) +func (suite *GitopsTestSuite) deleteKluctlDeployment(key client.ObjectKey) { + g := NewWithT(suite.T()) - startKluctlController(t) + var kd kluctlv1.KluctlDeployment + kd.Name = key.Name + kd.Namespace = key.Namespace + err := suite.k.Client.Delete(context.Background(), &kd) + if err != nil && !errors.IsNotFound(err) { + g.Expect(err).To(Succeed()) + } +} - k := defaultCluster1 +func (suite *GitopsTestSuite) TestGitOpsFieldManager() { + g := NewWithT(suite.T()) - p := test_utils.NewTestProject(t) - createNamespace(t, k, p.TestSlug()) + p := test_utils.NewTestProject(suite.T()) + createNamespace(suite.T(), suite.k, p.TestSlug()) p.UpdateTarget("target1", nil) p.AddKustomizeDeployment("d1", []test_utils.KustomizeResource{ @@ -164,23 +241,23 @@ data: `)}, }, nil) - key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + key := suite.createKluctlDeployment(p, "target1", map[string]any{ "namespace": p.TestSlug(), "k2": 42, }) - t.Run("initial deployment", func(t *testing.T) { - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.Run("initial deployment", func() { + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) }) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.DeployInterval = &kluctlv1.SafeDuration{Duration: metav1.Duration{Duration: interval}} }) cm := &corev1.ConfigMap{} - t.Run("cm1 is deployed", func(t *testing.T) { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + suite.Run("cm1 is deployed", func() { + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) @@ -189,13 +266,13 @@ data: g.Expect(cm.Data).To(HaveKeyWithValue("k2", "43")) }) - t.Run("cm1 is modified and restored", func(t *testing.T) { + suite.Run("cm1 is modified and restored", func() { cm.Data["k1"] = "v2" - err := k.Client.Update(context.TODO(), cm, client.FieldOwner("kubectl")) + err := suite.k.Client.Update(context.TODO(), cm, client.FieldOwner("kubectl")) g.Expect(err).To(Succeed()) g.Eventually(func() bool { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) @@ -204,14 +281,14 @@ data: }, timeout, time.Second).Should(BeTrue()) }) - t.Run("cm1 gets a key added which is not modified by the controller", func(t *testing.T) { + suite.Run("cm1 gets a key added which is not modified by the controller", func() { cm.Data["k1"] = "v2" cm.Data["k3"] = "v3" - err := k.Client.Update(context.TODO(), cm, client.FieldOwner("kubectl")) + err := suite.k.Client.Update(context.TODO(), cm, client.FieldOwner("kubectl")) g.Expect(err).To(Succeed()) g.Eventually(func() bool { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) @@ -222,18 +299,18 @@ data: g.Expect(cm.Data).To(HaveKeyWithValue("k3", "v3")) }) - t.Run("cm1 gets modified with another field manager", func(t *testing.T) { + suite.Run("cm1 gets modified with another field manager", func() { patch := client.MergeFrom(cm.DeepCopy()) cm.Data["k1"] = "v2" - err := k.Client.Patch(context.TODO(), cm, patch, client.FieldOwner("test-field-manager")) + err := suite.k.Client.Patch(context.TODO(), cm, patch, client.FieldOwner("test-field-manager")) g.Expect(err).To(Succeed()) for i := 0; i < 2; i++ { - waitForReconcile(t, k, key) + suite.waitForReconcile(key) } - err = k.Client.Get(context.TODO(), client.ObjectKey{ + err = suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) @@ -241,19 +318,19 @@ data: g.Expect(cm.Data).To(HaveKeyWithValue("k1", "v2")) }) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.ForceApply = true }) - t.Run("forceApply is true and cm1 gets restored even with another field manager", func(t *testing.T) { + suite.Run("forceApply is true and cm1 gets restored even with another field manager", func() { patch := client.MergeFrom(cm.DeepCopy()) cm.Data["k1"] = "v2" - err := k.Client.Patch(context.TODO(), cm, patch, client.FieldOwner("test-field-manager")) + err := suite.k.Client.Patch(context.TODO(), cm, patch, client.FieldOwner("test-field-manager")) g.Expect(err).To(Succeed()) g.Eventually(func() bool { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) @@ -263,24 +340,21 @@ data: }) } -func TestKluctlDeploymentReconciler_Helm(t *testing.T) { - g := NewWithT(t) - startKluctlController(t) +func (suite *GitopsTestSuite) TestKluctlDeploymentReconciler_Helm() { + g := NewWithT(suite.T()) - k := defaultCluster1 - - p := test_utils.NewTestProject(t) - createNamespace(t, k, p.TestSlug()) + p := test_utils.NewTestProject(suite.T()) + createNamespace(suite.T(), suite.k, p.TestSlug()) p.UpdateTarget("target1", nil) - repoUrl := test_utils.CreateHelmRepo(t, []test_utils.RepoChart{ + repoUrl := test_utils.CreateHelmRepo(suite.T(), []test_utils.RepoChart{ {ChartName: "test-chart1", Version: "0.1.0"}, }, "", "") - repoUrlWithCreds := test_utils.CreateHelmRepo(t, []test_utils.RepoChart{ + repoUrlWithCreds := test_utils.CreateHelmRepo(suite.T(), []test_utils.RepoChart{ {ChartName: "test-chart2", Version: "0.1.0"}, }, "test-user", "test-password") - ociRepoUrlWithCreds := test_utils.CreateOciRepo(t, []test_utils.RepoChart{ + ociRepoUrlWithCreds := test_utils.CreateOciRepo(suite.T(), []test_utils.RepoChart{ {ChartName: "test-chart3", Version: "0.1.0"}, }, "test-user", "test-password") @@ -290,16 +364,16 @@ func TestKluctlDeploymentReconciler_Helm(t *testing.T) { return nil }, "") - key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + key := suite.createKluctlDeployment(p, "target1", map[string]any{ "namespace": p.TestSlug(), }) - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) cm := &corev1.ConfigMap{} - t.Run("chart got deployed", func(t *testing.T) { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + suite.Run("chart got deployed", func() { + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "test-helm-1-test-chart1", Namespace: p.TestSlug(), }, cm) @@ -315,9 +389,9 @@ func TestKluctlDeploymentReconciler_Helm(t *testing.T) { kd := &kluctlv1.KluctlDeployment{} - t.Run("chart with credentials fails with 401", func(t *testing.T) { + suite.Run("chart with credentials fails with 401", func() { g.Eventually(func() bool { - err := k.Client.Get(context.TODO(), key, kd) + err := suite.k.Client.Get(context.TODO(), key, kd) g.Expect(err).To(Succeed()) for _, c := range kd.Status.Conditions { _ = c @@ -340,16 +414,16 @@ func TestKluctlDeploymentReconciler_Helm(t *testing.T) { "password": []byte("test-password"), }, } - err := k.Client.Create(context.TODO(), credsSecret) + err := suite.k.Client.Create(context.TODO(), credsSecret) g.Expect(err).To(Succeed()) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.HelmCredentials = append(kd.Spec.HelmCredentials, kluctlv1.HelmCredentials{SecretRef: kluctlv1.LocalObjectReference{Name: "helm-secrets-1"}}) }) - t.Run("chart with credentials succeeds", func(t *testing.T) { + suite.Run("chart with credentials succeeds", func() { g.Eventually(func() bool { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "test-helm-2-test-chart2", Namespace: p.TestSlug(), }, cm) @@ -367,9 +441,9 @@ func TestKluctlDeploymentReconciler_Helm(t *testing.T) { return nil }, "") - t.Run("OCI chart with credentials fails with 401", func(t *testing.T) { + suite.Run("OCI chart with credentials fails with 401", func() { g.Eventually(func() bool { - err = k.Client.Get(context.TODO(), key, kd) + err = suite.k.Client.Get(context.TODO(), key, kd) g.Expect(err).To(Succeed()) for _, c := range kd.Status.Conditions { _ = c @@ -430,14 +504,11 @@ func TestKluctlDeploymentReconciler_Helm(t *testing.T) { })*/ } -func TestKluctlDeploymentReconciler_Prune(t *testing.T) { - g := NewWithT(t) - startKluctlController(t) - - k := defaultCluster1 +func (suite *GitopsTestSuite) TestKluctlDeploymentReconciler_Prune() { + g := NewWithT(suite.T()) - p := test_utils.NewTestProject(t) - createNamespace(t, k, p.TestSlug()) + p := test_utils.NewTestProject(suite.T()) + createNamespace(suite.T(), suite.k, p.TestSlug()) p.UpdateTarget("target1", nil) @@ -462,21 +533,21 @@ data: `)}, }, nil) - key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + key := suite.createKluctlDeployment(p, "target1", map[string]any{ "namespace": p.TestSlug(), }) - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) cm := &corev1.ConfigMap{} - t.Run("cm1 and cm2 got deployed", func(t *testing.T) { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + suite.Run("cm1 and cm2 got deployed", func() { + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) g.Expect(err).To(Succeed()) - err = k.Client.Get(context.TODO(), client.ObjectKey{ + err = suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm2", Namespace: p.TestSlug(), }, cm) @@ -490,39 +561,39 @@ data: g.Eventually(func() bool { var obj kluctlv1.KluctlDeployment - _ = k.Client.Get(context.Background(), key, &obj) + _ = suite.k.Client.Get(context.Background(), key, &obj) if obj.Status.LastDeployResult == nil { return false } - return obj.Status.LastDeployResult.GitInfo.Commit == getHeadRevision(t, p) + return obj.Status.LastDeployResult.GitInfo.Commit == getHeadRevision(suite.T(), p) }, timeout, time.Second).Should(BeTrue()) - t.Run("cm1 and cm2 were not deleted", func(t *testing.T) { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + suite.Run("cm1 and cm2 were not deleted", func() { + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) g.Expect(err).To(Succeed()) - err = k.Client.Get(context.TODO(), client.ObjectKey{ + err = suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm2", Namespace: p.TestSlug(), }, cm) g.Expect(err).To(Succeed()) }) - updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Prune = true }) - waitForReconcile(t, k, key) + suite.waitForReconcile(key) - t.Run("cm1 did not get deleted and cm2 got deleted", func(t *testing.T) { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + suite.Run("cm1 did not get deleted and cm2 got deleted", func() { + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) g.Expect(err).To(Succeed()) - err = k.Client.Get(context.TODO(), client.ObjectKey{ + err = suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm2", Namespace: p.TestSlug(), }, cm) @@ -530,14 +601,11 @@ data: }) } -func doTestDelete(t *testing.T, delete bool) { - g := NewWithT(t) - startKluctlController(t) - - k := defaultCluster1 +func (suite *GitopsTestSuite) doTestDelete(delete bool) { + g := NewWithT(suite.T()) - p := test_utils.NewTestProject(t) - createNamespace(t, k, p.TestSlug()) + p := test_utils.NewTestProject(suite.T()) + createNamespace(suite.T(), suite.k, p.TestSlug()) p.UpdateTarget("target1", nil) @@ -552,31 +620,31 @@ data: `)}, }, nil) - key := createKluctlDeployment(t, p, k, "target1", map[string]any{ + key := suite.createKluctlDeployment(p, "target1", map[string]any{ "namespace": p.TestSlug(), }) - kd := updateKluctlDeployment(t, k, key, func(kd *kluctlv1.KluctlDeployment) { + suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Delete = delete }) - waitForCommit(t, k, key, getHeadRevision(t, p)) + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) cm := &corev1.ConfigMap{} - t.Run("cm1 got deployed", func(t *testing.T) { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + suite.Run("cm1 got deployed", func() { + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) g.Expect(err).To(Succeed()) }) - g.Expect(k.Client.Delete(context.TODO(), kd)).To(Succeed()) + suite.deleteKluctlDeployment(key) g.Eventually(func() bool { var obj kluctlv1.KluctlDeployment - err := k.Client.Get(context.Background(), key, &obj) + err := suite.k.Client.Get(context.Background(), key, &obj) if err == nil { return false } @@ -587,16 +655,16 @@ data: }, timeout, time.Second).Should(BeTrue()) if delete { - t.Run("cm1 was deleted", func(t *testing.T) { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + suite.Run("cm1 was deleted", func() { + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) g.Expect(err).To(MatchError("configmaps \"cm1\" not found")) }) } else { - t.Run("cm1 was not deleted", func(t *testing.T) { - err := k.Client.Get(context.TODO(), client.ObjectKey{ + suite.Run("cm1 was not deleted", func() { + err := suite.k.Client.Get(context.TODO(), client.ObjectKey{ Name: "cm1", Namespace: p.TestSlug(), }, cm) @@ -605,10 +673,15 @@ data: } } -func TestKluctlDeploymentReconciler_Delete_True(t *testing.T) { - doTestDelete(t, true) +func (suite *GitopsTestSuite) Test_Delete_True() { + suite.doTestDelete(true) +} + +func (suite *GitopsTestSuite) Test_Delete_False() { + suite.doTestDelete(false) } -func TestKluctlDeploymentReconciler_Delete_False(t *testing.T) { - doTestDelete(t, false) +func TestGitOps(t *testing.T) { + t.Parallel() + suite.Run(t, new(GitopsTestSuite)) } From 8047bed47e72d36291ee86ddbc9e4a2153dacfac Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 19 May 2023 15:34:17 +0200 Subject: [PATCH 1016/2268] chore: Run make replace-commands-help --- docs/reference/commands/controller.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/commands/controller.md b/docs/reference/commands/controller.md index a7584c0b7..588b04f6e 100644 --- a/docs/reference/commands/controller.md +++ b/docs/reference/commands/controller.md @@ -26,9 +26,11 @@ The following arguments are available: Controller: Controller arguments. + --context string Override the context to use. --default-service-account string Default service account used for impersonation. --dry-run Run all deployments in dryRun=true mode. --health-probe-bind-address string The address the probe endpoint binds to. (default ":8081") + --kubeconfig string Override the kubeconfig to use. --leader-elect Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. --metrics-bind-address string The address the metric endpoint binds to. (default ":8080") From 8faca6ef582d41045b3e0ca92bf7852f94a5805e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 16:13:18 +0200 Subject: [PATCH 1017/2268] chore(deps): Bump github.com/mattn/go-isatty from 0.0.18 to 0.0.19 (#493) Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.18 to 0.0.19. - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.18...v0.0.19) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fb641e2f3..0c06edca2 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 github.com/kluctl/go-jinja2 v0.0.0-20230428103343-a832225dc94c github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.18 + github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 github.com/ohler55/ojg v1.18.5 diff --git a/go.sum b/go.sum index e6e48f5e8..1d9641d4f 100644 --- a/go.sum +++ b/go.sum @@ -591,8 +591,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky 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/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= From b14d199eb218ba1ff70533d9138238dcd797599d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 22 May 2023 17:02:42 +0200 Subject: [PATCH 1018/2268] ci: Only run manifests/generate targets on test target (#494) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a2bbf37e2..adc7a5a7c 100644 --- a/Makefile +++ b/Makefile @@ -70,14 +70,14 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: test-unit test-e2e fmt vet ## Run all tests. +test: manifests generate test-unit test-e2e fmt vet ## Run all tests. .PHONY: test-unit test-unit: ## Run unit tests. go test $(RACE) $(shell go list ./... | grep -v v2/e2e) -coverprofile cover.out .PHONY: test-e2e -test-e2e: manifests generate envtest ## Run e2e tests. +test-e2e: envtest ## Run e2e tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(RACE) ./e2e -coverprofile cover.out replace-commands-help: ## Replace commands help in docs From f35f839e22d781c7b6cbb8f67229e035e38441b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 07:35:17 +0200 Subject: [PATCH 1019/2268] chore(deps): Bump github.com/hashicorp/go-retryablehttp (#496) Bumps [github.com/hashicorp/go-retryablehttp](https://github.com/hashicorp/go-retryablehttp) from 0.7.1 to 0.7.2. - [Commits](https://github.com/hashicorp/go-retryablehttp/compare/v0.7.1...v0.7.2) --- updated-dependencies: - dependency-name: github.com/hashicorp/go-retryablehttp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0c06edca2..0cd0314ef 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/go-logr/logr v1.2.4 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/go-retryablehttp v0.7.1 + github.com/hashicorp/go-retryablehttp v0.7.2 github.com/huandu/xstrings v1.4.0 github.com/onsi/gomega v1.27.7 github.com/otiai10/copy v1.11.0 diff --git a/go.sum b/go.sum index 1d9641d4f..4bcbe7a52 100644 --- a/go.sum +++ b/go.sum @@ -458,8 +458,8 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= -github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= +github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= From 832624cd973a65fe8717a9aec6b42d31380a24b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 07:35:56 +0200 Subject: [PATCH 1020/2268] chore(deps): Bump github.com/Azure/azure-sdk-for-go/sdk/azidentity (#497) Bumps [github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://github.com/Azure/azure-sdk-for-go) from 1.1.0 to 1.3.0. - [Release notes](https://github.com/Azure/azure-sdk-for-go/releases) - [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md) - [Commits](https://github.com/Azure/azure-sdk-for-go/compare/v1.1...sdk/azcore/v1.3.0) --- updated-dependencies: - dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azidentity dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 9 ++++----- go.sum | 23 +++++++++-------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 0cd0314ef..6864047a7 100644 --- a/go.mod +++ b/go.mod @@ -52,8 +52,8 @@ require ( require ( filippo.io/age v1.1.1 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2/config v1.18.25 github.com/aws/aws-sdk-go-v2/credentials v1.13.24 @@ -84,11 +84,11 @@ require ( cloud.google.com/go/iam v0.13.0 // indirect cloud.google.com/go/kms v1.10.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.5.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect github.com/BurntSushi/toml v1.2.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -141,7 +141,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect diff --git a/go.sum b/go.sum index 4bcbe7a52..48f663318 100644 --- a/go.sum +++ b/go.sum @@ -54,12 +54,12 @@ filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1 h1:tz19qLF65vuu2ibfTqGVJxG/zZAI27NEIIbvAOQwYbw= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.5.1 h1:AXFNQ6kLaPODEpGSMWjmbkt6iP7fa1DIEzjx6JRFC9U= github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.5.1/go.mod h1:yOYJv0tO0TTNcje8ahhBHQcdAiYqRIp5fsog5FPefr4= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 h1:9cn6ICCGiWFNA/slKnrkf+ENyvaCRKHtuoGtnLIAgao= @@ -68,8 +68,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2 h1:BGX4OiGP9htYSd6M3pAZctcUUSruhIAUVkv2X0Cn9yE= -github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -215,7 +215,7 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= -github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE= github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= @@ -329,9 +329,6 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= @@ -644,7 +641,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -679,7 +675,6 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= -github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= From 1fb3178e51752d85635684798264051a63918f94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 07:36:17 +0200 Subject: [PATCH 1021/2268] chore(deps): Bump github.com/go-playground/validator/v10 (#492) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.13.0 to 10.14.0. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.13.0...v10.14.0) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 5 +++-- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6864047a7..825eafab4 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/bitnami-labs/sealed-secrets v0.21.0 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.2+incompatible - github.com/go-playground/validator/v10 v10.13.0 + github.com/go-playground/validator/v10 v10.14.0 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-containerregistry v0.15.2 @@ -129,6 +129,7 @@ require ( github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect @@ -171,7 +172,7 @@ require ( github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/leodido/go-urn v1.2.3 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/lib/pq v1.10.7 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/go.sum b/go.sum index 48f663318..b868dcef2 100644 --- a/go.sum +++ b/go.sum @@ -265,6 +265,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= @@ -308,8 +310,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ= -github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -554,8 +556,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= -github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= From e3ae4f140e14bba9305a5762a5ff3d4b6f6a5d7f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 08:20:15 +0200 Subject: [PATCH 1022/2268] ci: Fix windows tests (#495) * ci: Fix windows path being used in Makefile * ci: Reset caches --- .github/workflows/tests.yml | 4 ++-- Makefile | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 851719d81..8559911f4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -73,9 +73,9 @@ jobs: path: | ~/go/pkg/mod ~/.cache/go-build - key: tests1-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} + key: tests-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} restore-keys: | - tests1-go-${{ runner.os }}- + tests-go-${{ runner.os }}- - name: Run unit tests shell: bash run: | diff --git a/Makefile b/Makefile index adc7a5a7c..46c182d03 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,11 @@ ifeq ($(GOOS), linux) RACE=-race endif +PATHCONF=cat +ifeq ($(GOOS), windows) +PATHCONF=cygpath -f- -u +endif + # Image URL to use all building/pushing image targets IMG ?= kluctl/kluctl:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. @@ -74,11 +79,11 @@ test: manifests generate test-unit test-e2e fmt vet ## Run all tests. .PHONY: test-unit test-unit: ## Run unit tests. - go test $(RACE) $(shell go list ./... | grep -v v2/e2e) -coverprofile cover.out + go test $(RACE) $(shell go list ./... | grep -v v2/e2e) -coverprofile cover.out -test.v .PHONY: test-e2e test-e2e: envtest ## Run e2e tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(RACE) ./e2e -coverprofile cover.out + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir=$(LOCALBIN) -p path | $(PATHCONF))" go test $(RACE) ./e2e -coverprofile cover.out -test.v replace-commands-help: ## Replace commands help in docs go run ./internal/replace-commands-help --docs-dir ./docs/reference/commands From 53df330aba232e23910d8feca77d226070636bc7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 09:23:09 +0200 Subject: [PATCH 1023/2268] fix: Make metrics package non-internal and don't register metrics on init (#499) * chore: Move metrics out of internal package It needs to be used by the legacy flux-kluctl-controller * fix: Don't register metrics collectors in global init() --- cmd/kluctl/commands/cmd_controller.go | 10 ++++------ pkg/controllers/kluctl_project.go | 2 +- pkg/controllers/kluctldeployment_controller.go | 2 +- .../{internal => }/metrics/kluctl_project.go | 0 .../metrics/kluctldeployment_controller.go | 0 5 files changed, 6 insertions(+), 8 deletions(-) rename pkg/controllers/{internal => }/metrics/kluctl_project.go (100%) rename pkg/controllers/{internal => }/metrics/kluctldeployment_controller.go (100%) diff --git a/cmd/kluctl/commands/cmd_controller.go b/cmd/kluctl/commands/cmd_controller.go index 132e7a55b..e96e67773 100644 --- a/cmd/kluctl/commands/cmd_controller.go +++ b/cmd/kluctl/commands/cmd_controller.go @@ -26,14 +26,9 @@ import ( ) var ( - setupLog = ctrl.Log.WithName("setup") - metricsRecorder = metrics.NewRecorder() + setupLog = ctrl.Log.WithName("setup") ) -func init() { - crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...) -} - const controllerName = "kluctl-controller" type controllerCmd struct { @@ -69,6 +64,9 @@ func (cmd *controllerCmd) initScheme() { func (cmd *controllerCmd) Run(ctx context.Context) error { cmd.initScheme() + metricsRecorder := metrics.NewRecorder() + crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...) + opts := zap.Options{ Development: true, } diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index d0c306032..d6ca5372e 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -4,8 +4,8 @@ import ( "context" "fmt" "github.com/kluctl/go-jinja2" - internal_metrics "github.com/kluctl/kluctl/v2/pkg/controllers/internal/metrics" "github.com/kluctl/kluctl/v2/pkg/controllers/internal/sops" + internal_metrics "github.com/kluctl/kluctl/v2/pkg/controllers/metrics" "github.com/kluctl/kluctl/v2/pkg/helm" "github.com/kluctl/kluctl/v2/pkg/repocache" "github.com/kluctl/kluctl/v2/pkg/sops/decryptor" diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 5e9653f2d..86dd5e118 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/hashicorp/go-retryablehttp" kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" - internal_metrics "github.com/kluctl/kluctl/v2/pkg/controllers/internal/metrics" + internal_metrics "github.com/kluctl/kluctl/v2/pkg/controllers/metrics" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" "github.com/kluctl/kluctl/v2/pkg/kluctl_jinja2" "github.com/kluctl/kluctl/v2/pkg/results" diff --git a/pkg/controllers/internal/metrics/kluctl_project.go b/pkg/controllers/metrics/kluctl_project.go similarity index 100% rename from pkg/controllers/internal/metrics/kluctl_project.go rename to pkg/controllers/metrics/kluctl_project.go diff --git a/pkg/controllers/internal/metrics/kluctldeployment_controller.go b/pkg/controllers/metrics/kluctldeployment_controller.go similarity index 100% rename from pkg/controllers/internal/metrics/kluctldeployment_controller.go rename to pkg/controllers/metrics/kluctldeployment_controller.go From 83639459e7bc8123e39309d8a8c08ddc0149122b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 10:29:51 +0200 Subject: [PATCH 1024/2268] feat: Disable writing of command results by default --- cmd/kluctl/args/project.go | 2 +- cmd/kluctl/commands/utils.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 893c5062e..21468a46b 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -43,7 +43,7 @@ type TargetFlags struct { } type CommandResultFlags struct { - NoWriteCommandResult bool `group:"results" help:"Disable writing of command results into the cluster."` + WriteCommandResult bool `group:"results" help:"Enable writing of command results into the cluster."` ForceWriteCommandResult bool `group:"results" help:"Force writing of command results, even if the command is run in dry-run mode."` CommandResultNamespace string `group:"results" help:"Override the namespace to be used when writing command results." default:"kluctl-results"` KeepCommandResultsCount int `group:"results" help:"Configure how many old command results to keep." default:"10"` diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 0bcb6bcbf..630199ca5 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -200,7 +200,7 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm } var resultStore results.ResultStore - if args.commandResultFlags != nil && !args.commandResultFlags.NoWriteCommandResult { + if args.commandResultFlags != nil && args.commandResultFlags.WriteCommandResult { rc, err := targetCtx.SharedContext.K.ToRESTConfig() if err != nil { return err From 0a694c74758d13585b13959aec8c4ab75d9dc0c2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 10:34:21 +0200 Subject: [PATCH 1025/2268] feat: Make writing of results configurable in the controller --- cmd/kluctl/commands/cmd_controller.go | 12 ++++++++++++ pkg/controllers/kluctldeployment_controller_setup.go | 7 ------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cmd/kluctl/commands/cmd_controller.go b/cmd/kluctl/commands/cmd_controller.go index e96e67773..c0baaa66a 100644 --- a/cmd/kluctl/commands/cmd_controller.go +++ b/cmd/kluctl/commands/cmd_controller.go @@ -4,8 +4,10 @@ import ( "context" "fmt" kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" "github.com/kluctl/kluctl/v2/pkg/controllers" ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" + "github.com/kluctl/kluctl/v2/pkg/results" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime" @@ -43,6 +45,8 @@ type controllerCmd struct { DefaultServiceAccount string `group:"controller" help:"Default service account used for impersonation."` DryRun bool `group:"controller" help:"Run all deployments in dryRun=true mode."` + + args.CommandResultFlags } func (cmd *controllerCmd) Help() string { @@ -133,6 +137,14 @@ func (cmd *controllerCmd) Run(ctx context.Context) error { SshPool: sshPool, } + if cmd.WriteCommandResult { + resultStore, err := results.NewResultStoreSecrets(ctx, mgr.GetClient(), mgr.GetCache(), cmd.CommandResultNamespace, cmd.KeepCommandResultsCount) + if err != nil { + return err + } + r.ResultStore = resultStore + } + if err = r.SetupWithManager(ctx, mgr, controllers.KluctlDeploymentReconcilerOpts{ HTTPRetry: 9, }); err != nil { diff --git a/pkg/controllers/kluctldeployment_controller_setup.go b/pkg/controllers/kluctldeployment_controller_setup.go index 0f8fe2e73..c0ca400d8 100644 --- a/pkg/controllers/kluctldeployment_controller_setup.go +++ b/pkg/controllers/kluctldeployment_controller_setup.go @@ -4,7 +4,6 @@ import ( "context" "github.com/hashicorp/go-retryablehttp" kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" - "github.com/kluctl/kluctl/v2/pkg/results" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/predicate" @@ -22,12 +21,6 @@ func (r *KluctlDeploymentReconciler) SetupWithManager(ctx context.Context, mgr c httpClient.Logger = nil r.httpClient = httpClient - resultStore, err := results.NewResultStoreSecrets(ctx, mgr.GetClient(), mgr.GetCache(), "kluctl-results", 10) - if err != nil { - return err - } - r.ResultStore = resultStore - return ctrl.NewControllerManagedBy(mgr). For(&kluctlv1.KluctlDeployment{}, builder.WithPredicates( predicate.Or(predicate.GenerationChangedPredicate{}, ReconcileRequestedPredicate{}, DeployRequestedPredicate{}), From fd1b7ee1b1b034b546aea81f51879e99ceaa2dea Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 13:39:36 +0200 Subject: [PATCH 1026/2268] fix: Don't use ctrl.SetupSignalHandler() This caused ctrl+c not reacting as expected, as one needed 2 strokes to make it work. --- cmd/kluctl/commands/root.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 51b37957c..74559d894 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -25,7 +25,6 @@ import ( "os" "path/filepath" "runtime/pprof" - ctrl "sigs.k8s.io/controller-runtime" "strings" "time" @@ -111,6 +110,7 @@ func redirectLogsAndStderr(ctxGetter func() context.Context) { klog.LogToStderr(false) klog.SetOutput(lr1) log.SetOutput(lr2) + //ctrl.SetLogger(klog.NewKlogr()) pr, pw, err := os.Pipe() if err != nil { @@ -222,7 +222,7 @@ func initViper(ctx context.Context) { func Main() { colorable.EnableColorsStdout(nil) - ctx := ctrl.SetupSignalHandler() + ctx := context.Background() ctx = initStatusHandler(ctx, false, true) redirectLogsAndStderr(func() context.Context { From 5fc26667b33744ec6a58208f5665b1501572a080 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 13:39:49 +0200 Subject: [PATCH 1027/2268] tests: Add command results tests --- e2e/gitops_test.go | 1 + e2e/results_test.go | 100 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 e2e/results_test.go diff --git a/e2e/gitops_test.go b/e2e/gitops_test.go index 191a65c5b..1df30c7fa 100644 --- a/e2e/gitops_test.go +++ b/e2e/gitops_test.go @@ -100,6 +100,7 @@ func (suite *GitopsTestSuite) startController() { tmpKubeconfig, "--context", "context1", + "--write-command-result", } done := make(chan struct{}) go func() { diff --git a/e2e/results_test.go b/e2e/results_test.go new file mode 100644 index 000000000..b2ceea17d --- /dev/null +++ b/e2e/results_test.go @@ -0,0 +1,100 @@ +package e2e + +import ( + "context" + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/stretchr/testify/assert" + "testing" +) + +func assertSummary(t *testing.T, expected result.CommandResultSummary, actual result.CommandResultSummary) { + assert.Equal(t, expected.AppliedObjects, actual.AppliedObjects) + assert.Equal(t, expected.NewObjects, actual.NewObjects) + assert.Equal(t, expected.ChangedObjects, actual.ChangedObjects) + assert.Equal(t, expected.OrphanObjects, actual.OrphanObjects) + assert.Equal(t, expected.DeletedObjects, actual.DeletedObjects) + assert.Equal(t, expected.Errors, actual.Errors) + assert.Equal(t, expected.Warnings, actual.Warnings) + assert.Equal(t, expected.TotalChanges, actual.TotalChanges) +} + +func TestWriteResult(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + addConfigMapDeployment(p, "cm", map[string]string{ + "d1": "v1", + }, resourceOpts{ + name: "cm", + namespace: p.TestSlug(), + }) + p.KluctlMust("deploy", "--yes", "-t", "test", "--write-command-result") + assertConfigMapExists(t, k, p.TestSlug(), "cm") + + rs, err := results.NewResultStoreSecrets(context.Background(), k.Client, nil, "kluctl-results", 0) + assert.NoError(t, err) + + summaries, err := rs.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + assert.NoError(t, err) + assert.Len(t, summaries, 1) + assertSummary(t, result.CommandResultSummary{ + AppliedObjects: 1, + NewObjects: 1, + }, summaries[0]) + + addConfigMapDeployment(p, "cm2", nil, resourceOpts{ + name: "cm2", + namespace: p.TestSlug(), + }) + p.UpdateYaml("cm/configmap-cm.yml", func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField("v2", "data", "d1") + return nil + }, "") + p.KluctlMust("deploy", "--yes", "-t", "test", "--write-command-result") + assertConfigMapExists(t, k, p.TestSlug(), "cm2") + + summaries, err = rs.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + assert.NoError(t, err) + assert.Len(t, summaries, 2) + assertSummary(t, result.CommandResultSummary{ + AppliedObjects: 2, + NewObjects: 1, + ChangedObjects: 1, + TotalChanges: 1, + }, summaries[0]) + + p.UpdateDeploymentYaml("", func(o *uo.UnstructuredObject) error { + _ = o.RemoveNestedField("deployments", 1) + return nil + }) + p.KluctlMust("deploy", "--yes", "-t", "test", "--write-command-result") + assertConfigMapExists(t, k, p.TestSlug(), "cm2") + + summaries, err = rs.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + assert.NoError(t, err) + assert.Len(t, summaries, 3) + assertSummary(t, result.CommandResultSummary{ + AppliedObjects: 1, + OrphanObjects: 1, + }, summaries[0]) + + p.KluctlMust("prune", "--yes", "-t", "test", "--write-command-result") + assertConfigMapNotExists(t, k, p.TestSlug(), "cm2") + + summaries, err = rs.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + assert.NoError(t, err) + assert.Len(t, summaries, 4) + assertSummary(t, result.CommandResultSummary{ + DeletedObjects: 1, + }, summaries[0]) +} From 863004016fac114154d1335e836bb6b23e8ecc23 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 13:49:50 +0200 Subject: [PATCH 1028/2268] chore: Run make replace-commands-help --- docs/reference/commands/common-arguments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index 055c8e2cb..192cf6433 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -138,7 +138,7 @@ Command Results: "kluctl-results") --force-write-command-result Force writing of command results, even if the command is run in dry-run mode. --keep-command-results-count int Configure how many old command results to keep. (default 10) - --no-write-command-result Disable writing of command results into the cluster. + --write-command-result Enable writing of command results into the cluster. ``` From 756c528769045e1eaefb04404245ee648d17345d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 15:15:32 +0200 Subject: [PATCH 1029/2268] refactor: Add controller-runtime client to K8sCluster --- pkg/k8s/client.go | 8 ++++++++ pkg/k8s/client_factory.go | 13 +++++++++++++ pkg/k8s/fake_client_factory.go | 12 ++++++++++++ 3 files changed, 33 insertions(+) diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index 2d8069549..32beb055f 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -7,6 +7,7 @@ import ( "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/metadata" + "sigs.k8s.io/controller-runtime/pkg/client" ) type k8sClients struct { @@ -17,6 +18,8 @@ type k8sClients struct { } type parallelClientEntry struct { + client client.Client + corev1 corev1.CoreV1Interface dynamicClient dynamic.Interface metadataClient metadata.Interface @@ -51,6 +54,11 @@ func newK8sClients(ctx context.Context, clientFactory ClientFactory, count int) for i := 0; i < count; i++ { p := ¶llelClientEntry{} + p.client, err = clientFactory.Client(p) + if err != nil { + return nil, err + } + p.corev1, err = clientFactory.CoreV1Client(p) if err != nil { return nil, err diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go index 2edff9b44..594ddecc1 100644 --- a/pkg/k8s/client_factory.go +++ b/pkg/k8s/client_factory.go @@ -13,6 +13,7 @@ import ( "net/http" "net/url" "path/filepath" + "sigs.k8s.io/controller-runtime/pkg/client" "time" ) @@ -22,6 +23,8 @@ type ClientFactory interface { CloseIdleConnections() + Client(wh rest.WarningHandler) (client.Client, error) + DiscoveryClient() (discovery.DiscoveryInterface, error) CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1Interface, error) DynamicClient(wh rest.WarningHandler) (dynamic.Interface, error) @@ -42,6 +45,16 @@ func (r *realClientFactory) GetCA() []byte { return r.config.CAData } +func (r *realClientFactory) Client(wh rest.WarningHandler) (client.Client, error) { + config := rest.CopyConfig(r.config) + config.WarningHandler = wh + return client.New(config, client.Options{ + WarningHandler: client.WarningHandlerOptions{ + SuppressWarnings: true, + }, + }) +} + func (r *realClientFactory) DiscoveryClient() (discovery.DiscoveryInterface, error) { apiHost, err := url.Parse(r.config.Host) if err != nil { diff --git a/pkg/k8s/fake_client_factory.go b/pkg/k8s/fake_client_factory.go index 2de043f74..6ea0dd26d 100644 --- a/pkg/k8s/fake_client_factory.go +++ b/pkg/k8s/fake_client_factory.go @@ -16,6 +16,8 @@ import ( fake_metadata "k8s.io/client-go/metadata/fake" "k8s.io/client-go/rest" "k8s.io/client-go/testing" + "sigs.k8s.io/controller-runtime/pkg/client" + fake2 "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/yaml" "strings" @@ -25,6 +27,7 @@ type fakeClientFactory struct { clientSet *fake.Clientset dynamicClient *fake_dynamic.FakeDynamicClient metadataClient *fake_metadata.FakeMetadataClient + clientBuilder *fake2.ClientBuilder } func (f *fakeClientFactory) RESTConfig() *rest.Config { @@ -38,6 +41,10 @@ func (f *fakeClientFactory) GetCA() []byte { func (f *fakeClientFactory) CloseIdleConnections() { } +func (f *fakeClientFactory) Client(wh rest.WarningHandler) (client.Client, error) { + return f.clientBuilder.Build(), nil +} + func (f *fakeClientFactory) DiscoveryClient() (discovery.DiscoveryInterface, error) { return f.clientSet.Discovery(), nil } @@ -65,10 +72,15 @@ func NewFakeClientFactory(objects ...runtime.Object) *fakeClientFactory { dynamicClient := fake_dynamic.NewSimpleDynamicClient(scheme, objects...) metadataClient := fake_metadata.NewSimpleMetadataClient(scheme, objects...) + clientBuilder := fake2.NewClientBuilder(). + WithScheme(scheme). + WithRuntimeObjects(objects...) + return &fakeClientFactory{ clientSet: clientSet, dynamicClient: dynamicClient, metadataClient: metadataClient, + clientBuilder: clientBuilder, } } From 1e8daf502bfdb5e8fff3853e585e10a46b1cb3e9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 16:34:27 +0200 Subject: [PATCH 1030/2268] refactor: Use controller-runtime client for Get/List/Patch/Update/Delete --- pkg/deployment/utils/apply_utils.go | 6 +- pkg/k8s/client.go | 18 +-- pkg/k8s/k8s_cluster.go | 205 +++++++++++----------------- pkg/seal/bootstrap.go | 4 +- 4 files changed, 94 insertions(+), 139 deletions(-) diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 6a7847e51..72e1c0289 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -250,7 +250,7 @@ func (a *ApplyUtil) retryApplyForceReplace(x *uo.UnstructuredObject, hook bool, o := k8s.PatchOptions{ ForceDryRun: a.o.DryRun, } - r, apiWarnings, err := a.k.PatchObject(x, o) + r, apiWarnings, err := a.k.ApplyObject(x, o) a.handleApiWarnings(ref, apiWarnings) if err != nil { a.HandleError(ref, err) @@ -324,7 +324,7 @@ func (a *ApplyUtil) retryApplyWithConflicts(x *uo.UnstructuredObject, hook bool, ForceDryRun: a.o.DryRun, ForceApply: true, } - r, apiWarnings, err := a.k.PatchObject(x2, options) + r, apiWarnings, err := a.k.ApplyObject(x2, options) a.handleApiWarnings(ref, apiWarnings) if err != nil { // We didn't manage to solve it, better to abort (and not retry with replace!) @@ -367,7 +367,7 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo options := k8s.PatchOptions{ ForceDryRun: a.o.DryRun, } - r, apiWarnings, err := a.k.PatchObject(x, options) + r, apiWarnings, err := a.k.ApplyObject(x, options) if r != nil && usesDummyName { tmpName := r.GetK8sName() _ = r.ReplaceKeys(tmpName, ref.Name) diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index 32beb055f..fe0044ebc 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -102,6 +102,16 @@ func (k *k8sClients) withClientFromPool(cb func(p *parallelClientEntry) error) ( } } +func (k *k8sClients) withCClientFromPool(dryRun bool, cb func(c client.Client) error) ([]ApiWarning, error) { + return k.withClientFromPool(func(p *parallelClientEntry) error { + c := p.client + if dryRun { + c = client.NewDryRunClient(c) + } + return cb(c) + }) +} + func (k *k8sClients) withDynamicClientForGVR(gvr *schema.GroupVersionResource, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { return k.withClientFromPool(func(p *parallelClientEntry) error { if namespace != "" { @@ -111,11 +121,3 @@ func (k *k8sClients) withDynamicClientForGVR(gvr *schema.GroupVersionResource, n } }) } - -func (k *k8sClients) withDynamicClientForGVK(resources *k8sResources, gvk schema.GroupVersionKind, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { - gvr, err := resources.GetGVRForGVK(gvk) - if err != nil { - return nil, err - } - return k.withDynamicClientForGVR(gvr, namespace, cb) -} diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index be727ffd8..4353326ad 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -2,11 +2,11 @@ package k8s import ( "context" - "encoding/json" "fmt" "io" - "k8s.io/client-go/metadata" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "net/http" + "sigs.k8s.io/controller-runtime/pkg/client" "strings" "sync" "time" @@ -17,15 +17,12 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "github.com/kluctl/kluctl/v2/pkg/yaml" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" "k8s.io/client-go/restmapper" @@ -120,72 +117,57 @@ func (k *K8sCluster) buildLabelSelector(labels map[string]string) string { return ret } -func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { - var result []*uo.UnstructuredObject - - apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, gvk, namespace, func(r dynamic.ResourceInterface) error { - o := v1.ListOptions{ - LabelSelector: k.buildLabelSelector(labels), - } - x, err := r.List(k.ctx, o) - if err != nil { - return err - } - for _, o := range x.Items { - result = append(result, uo.FromUnstructured(&o)) - } - return nil +func (k *K8sCluster) doList(l client.ObjectList, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { + apiWarnings, err := k.clients.withCClientFromPool(true, func(c client.Client) error { + return c.List(k.ctx, l, client.InNamespace(namespace), client.MatchingLabels(labels)) }) - return result, apiWarnings, err -} - -func (k *K8sCluster) ListMetadata(gvk schema.GroupVersionKind, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { - var result []*uo.UnstructuredObject - - gvr, err := k.Resources.GetGVRForGVK(gvk) if err != nil { - return nil, nil, err + return nil, apiWarnings, err } - apiWarnings, err := k.clients.withClientFromPool(func(p *parallelClientEntry) error { - var r metadata.ResourceInterface - if namespace == "" { - r = p.metadataClient.Resource(*gvr) - } else { - r = p.metadataClient.Resource(*gvr).Namespace(namespace) - } - o := v1.ListOptions{ - LabelSelector: k.buildLabelSelector(labels), - } - x, err := r.List(k.ctx, o) - if err != nil { - return err + var result []*uo.UnstructuredObject + if l2, ok := l.(*unstructured.UnstructuredList); ok { + for _, o := range l2.Items { + result = append(result, uo.FromUnstructured(&o)) } - for _, o := range x.Items { - u, err := uo.FromStruct(o) + } else if l2, ok := l.(*v1.PartialObjectMetadataList); ok { + for _, o := range l2.Items { + x, err := uo.FromStruct(&o) if err != nil { - return err + return nil, apiWarnings, err } - u.SetK8sGVK(gvk) - result = append(result, u) + result = append(result, x) } - return nil - }) + } return result, apiWarnings, err } +func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { + var l unstructured.UnstructuredList + l.SetGroupVersionKind(gvk) + return k.doList(&l, namespace, labels) +} + +func (k *K8sCluster) ListMetadata(gvk schema.GroupVersionKind, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { + var l v1.PartialObjectMetadataList + l.SetGroupVersionKind(gvk) + return k.doList(&l, namespace, labels) +} func (k *K8sCluster) GetSingleObject(ref k8s.ObjectRef) (*uo.UnstructuredObject, []ApiWarning, error) { - var result *uo.UnstructuredObject - apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GroupVersionKind(), ref.Namespace, func(r dynamic.ResourceInterface) error { - o := v1.GetOptions{} - x, err := r.Get(k.ctx, ref.Name, o) - if err != nil { - return err - } - result = uo.FromUnstructured(x) - return nil + var o unstructured.Unstructured + o.SetGroupVersionKind(ref.GroupVersionKind()) + + apiWarnings, err := k.clients.withCClientFromPool(true, func(c client.Client) error { + return c.Get(k.ctx, client.ObjectKey{ + Name: ref.Name, + Namespace: ref.Namespace, + }, &o) + }) - return result, apiWarnings, err + if err != nil { + return nil, apiWarnings, err + } + return uo.FromUnstructured(&o), apiWarnings, nil } type DeleteOptions struct { @@ -197,25 +179,19 @@ type DeleteOptions struct { func (k *K8sCluster) DeleteSingleObject(ref k8s.ObjectRef, options DeleteOptions) ([]ApiWarning, error) { dryRun := k.DryRun || options.ForceDryRun - pp := v1.DeletePropagationBackground - o := v1.DeleteOptions{ - PropagationPolicy: &pp, - } - if dryRun { - o.DryRun = []string{"All"} - } + var o unstructured.Unstructured + o.SetGroupVersionKind(ref.GroupVersionKind()) + o.SetName(ref.Name) + o.SetNamespace(ref.Namespace) - apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GroupVersionKind(), ref.Namespace, func(r dynamic.ResourceInterface) error { - err := r.Delete(k.ctx, ref.Name, o) - if err != nil { - if options.IgnoreNotFoundError && errors.IsNotFound(err) { - return nil - } - return err - } - return nil + apiWarnings, err := k.clients.withCClientFromPool(dryRun, func(c client.Client) error { + return c.Delete(k.ctx, &o, client.PropagationPolicy(v1.DeletePropagationBackground)) }) + if err != nil { + if options.IgnoreNotFoundError && errors.IsNotFound(err) { + return apiWarnings, nil + } return apiWarnings, err } @@ -342,54 +318,35 @@ type PatchOptions struct { ForceApply bool } -func (k *K8sCluster) doPatch(ref k8s.ObjectRef, data []byte, patchType types.PatchType, options PatchOptions) (*uo.UnstructuredObject, []ApiWarning, error) { - dryRun := k.DryRun || options.ForceDryRun +func (k *K8sCluster) doPatch(ref k8s.ObjectRef, obj client.Object, patch client.Patch, options PatchOptions) ([]ApiWarning, error) { + status.Trace(k.ctx, "patching %s", ref.String()) - po := v1.PatchOptions{ - FieldManager: "kluctl", - } - if dryRun { - po.DryRun = []string{"All"} - } + var opts []client.PatchOption if options.ForceApply { - po.Force = &options.ForceApply + opts = append(opts, client.ForceOwnership) } + if options.ForceDryRun { + opts = append(opts, client.DryRunAll) + } + opts = append(opts, client.FieldOwner("kluctl")) - status.Trace(k.ctx, "patching %s", ref.String()) - - var result *uo.UnstructuredObject - apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GroupVersionKind(), ref.Namespace, func(r dynamic.ResourceInterface) error { - x, err := r.Patch(k.ctx, ref.Name, patchType, data, po) + apiWarnings, err := k.clients.withCClientFromPool(k.DryRun, func(c client.Client) error { + err := c.Patch(k.ctx, obj, patch, opts...) if err != nil { return fmt.Errorf("failed to patch %s: %w", ref.String(), err) } - result = uo.FromUnstructured(x) return nil }) - return result, apiWarnings, err + return apiWarnings, err } -func (k *K8sCluster) PatchObject(o *uo.UnstructuredObject, options PatchOptions) (*uo.UnstructuredObject, []ApiWarning, error) { - data, err := yaml.WriteYamlBytes(o) +func (k *K8sCluster) ApplyObject(o *uo.UnstructuredObject, options PatchOptions) (*uo.UnstructuredObject, []ApiWarning, error) { + obj := o.Clone().ToUnstructured() + apiWarnings, err := k.doPatch(o.GetK8sRef(), obj, client.Apply, options) if err != nil { - return nil, nil, err + return nil, apiWarnings, err } - - return k.doPatch(o.GetK8sRef(), data, types.ApplyPatchType, options) -} - -type JsonPatch struct { - Op string `json:"op"` - Path string `json:"path"` - Value any `json:"value"` -} - -func (k *K8sCluster) PatchObjectWithJsonPatch(ref k8s.ObjectRef, patch interface{}, options PatchOptions) (*uo.UnstructuredObject, []ApiWarning, error) { - data, err := json.Marshal(patch) - if err != nil { - return nil, nil, err - } - return k.doPatch(ref, data, types.JSONPatchType, options) + return uo.FromUnstructured(obj), apiWarnings, nil } type UpdateOptions struct { @@ -397,28 +354,24 @@ type UpdateOptions struct { } func (k *K8sCluster) UpdateObject(o *uo.UnstructuredObject, options UpdateOptions) (*uo.UnstructuredObject, []ApiWarning, error) { - dryRun := k.DryRun || options.ForceDryRun ref := o.GetK8sRef() - - updateOpts := v1.UpdateOptions{ - FieldManager: "kluctl", - } - if dryRun { - updateOpts.DryRun = []string{"All"} - } + obj := o.Clone().ToUnstructured() status.Trace(k.ctx, "updating %s", ref.String()) - var result *uo.UnstructuredObject - apiWarnings, err := k.clients.withDynamicClientForGVK(k.Resources, ref.GroupVersionKind(), ref.Namespace, func(r dynamic.ResourceInterface) error { - x, err := r.Update(k.ctx, o.ToUnstructured(), updateOpts) - if err != nil { - return err - } - result = uo.FromUnstructured(x) - return nil + var opts []client.UpdateOption + if options.ForceDryRun { + opts = append(opts, client.DryRunAll) + } + opts = append(opts, client.FieldOwner("kluctl")) + + apiWarnings, err := k.clients.withCClientFromPool(k.DryRun, func(c client.Client) error { + return c.Update(k.ctx, obj, opts...) }) - return result, apiWarnings, err + if err != nil { + return nil, apiWarnings, err + } + return uo.FromUnstructured(obj), apiWarnings, nil } // envtestProxyGet checks the environment variables KLUCTL_K8S_SERVICE_PROXY_XXX to enable testing of proxy requests with envtest diff --git a/pkg/seal/bootstrap.go b/pkg/seal/bootstrap.go index 81c786346..94932cafb 100644 --- a/pkg/seal/bootstrap.go +++ b/pkg/seal/bootstrap.go @@ -87,11 +87,11 @@ func writeKey(k *k8s.K8sCluster, key *rsa.PrivateKey, certs []*x509.Certificate, v1.TLSCertKey: string(certbytes), } - _, _, err := k.ReadWrite().PatchObject(secret, k8s.PatchOptions{}) + _, _, err := k.ReadWrite().ApplyObject(secret, k8s.PatchOptions{}) if err != nil { return err } - _, _, err = k.ReadWrite().PatchObject(configMap, k8s.PatchOptions{}) + _, _, err = k.ReadWrite().ApplyObject(configMap, k8s.PatchOptions{}) if err != nil { return err } From 2f6772e14bcd33ffa956ca00215e57c7cee571fc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 16:34:54 +0200 Subject: [PATCH 1031/2268] refactor: Move IsNamespae/FixNamespace into K8sCluster --- pkg/deployment/deployment_item.go | 2 +- pkg/deployment/utils/delete_utils.go | 2 +- pkg/helm/helm_release.go | 2 +- pkg/k8s/k8s_cluster.go | 45 ++++++++++++++++++++++++++++ pkg/k8s/resources.go | 35 ---------------------- 5 files changed, 48 insertions(+), 38 deletions(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index 982be5d05..b1ef0acba 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -581,7 +581,7 @@ func (di *DeploymentItem) postprocessObjects(images *Images) error { _ = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { if di.ctx.K != nil { - di.ctx.K.Resources.FixNamespace(o, "default") + di.ctx.K.FixNamespace(o, "default") } // Set common labels/annotations diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index 0cab9e94e..1ca1f49f5 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -40,7 +40,7 @@ var deleteOrder = [][]string{ } func objectRefForExclusion(k *k8s.K8sCluster, ref k8s2.ObjectRef) k8s2.ObjectRef { - ref = k.Resources.FixNamespaceInRef(ref) + ref = k.FixNamespaceInRef(ref) ref.Version = "" return ref } diff --git a/pkg/helm/helm_release.go b/pkg/helm/helm_release.go index 7295a7f29..2f6bd364a 100644 --- a/pkg/helm/helm_release.go +++ b/pkg/helm/helm_release.go @@ -260,7 +260,7 @@ func (hr *Release) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion s // add the necessary namespace in the rendered resources if k != nil { err = k8s.UnwrapListItems(o, true, func(o *uo.UnstructuredObject) error { - k.Resources.FixNamespace(o, namespace) + k.FixNamespace(o, namespace) return nil }) if err != nil { diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 4353326ad..8f0805761 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -424,6 +424,51 @@ func (k *K8sCluster) ProxyGet(scheme, namespace, name, port, path string, params return ret.Stream(k.ctx) } +func (k *K8sCluster) IsNamespaced(gvk schema.GroupVersionKind) *bool { + var obj unstructured.Unstructured + obj.SetGroupVersionKind(gvk) + + ret := false + _, err := k.clients.withCClientFromPool(true, func(c client.Client) error { + x, err := c.IsObjectNamespaced(&obj) + if err != nil { + return err + } + ret = x + return nil + }) + if err != nil { + return nil + } + return &ret +} + +func (k *K8sCluster) FixNamespace(o *uo.UnstructuredObject, def string) { + ref := o.GetK8sRef() + namespaced := k.IsNamespaced(ref.GroupVersionKind()) + if namespaced == nil { + return + } + if !*namespaced && ref.Namespace != "" { + o.SetK8sNamespace("") + } else if *namespaced && ref.Namespace == "" { + o.SetK8sNamespace(def) + } +} + +func (k *K8sCluster) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { + namespaced := k.IsNamespaced(ref.GroupVersionKind()) + if namespaced == nil { + return ref + } + if !*namespaced && ref.Namespace != "" { + ref.Namespace = "" + } else if *namespaced && ref.Namespace == "" { + ref.Namespace = "default" + } + return ref +} + func (k *K8sCluster) ToRESTConfig() (*rest.Config, error) { return k.clientFactory.RESTConfig(), nil } diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 902131e07..ec5188a7b 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -3,7 +3,6 @@ package k8s import ( "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -242,40 +241,6 @@ func (k *k8sResources) UpdateResourcesFromCRD(crd *uo.UnstructuredObject) error return nil } -func (k *k8sResources) IsNamespaced(gv schema.GroupKind) *bool { - ar := k.GetPreferredResource(gv) - if ar == nil { - return nil - } - return &ar.Namespaced -} - -func (k *k8sResources) FixNamespace(o *uo.UnstructuredObject, def string) { - ref := o.GetK8sRef() - namespaced := k.IsNamespaced(ref.GroupKind()) - if namespaced == nil { - return - } - if !*namespaced && ref.Namespace != "" { - o.SetK8sNamespace("") - } else if *namespaced && ref.Namespace == "" { - o.SetK8sNamespace(def) - } -} - -func (k *k8sResources) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { - namespaced := k.IsNamespaced(ref.GroupKind()) - if namespaced == nil { - return ref - } - if !*namespaced && ref.Namespace != "" { - ref.Namespace = "" - } else if *namespaced && ref.Namespace == "" { - ref.Namespace = "default" - } - return ref -} - func (k *k8sResources) GetAllGroupVersions() ([]schema.GroupVersion, error) { k.mutex.Lock() defer k.mutex.Unlock() From 0c7ce866024b148c203a50d22e751201db505515 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 23 May 2023 23:28:30 +0200 Subject: [PATCH 1032/2268] refactor: Use RESTMapper instead of custom k8sResources implementation --- pkg/deployment/deployment_collection.go | 11 - pkg/deployment/deployment_item.go | 27 -- pkg/deployment/utils/apply_utils.go | 20 +- pkg/deployment/utils/delete_utils.go | 6 +- pkg/deployment/utils/remote_objects_utils.go | 8 +- pkg/helm/helm_release.go | 16 +- pkg/k8s/client.go | 11 - pkg/k8s/client_factory.go | 51 ++- pkg/k8s/fake_client_factory.go | 4 + pkg/k8s/k8s_cluster.go | 93 +++-- pkg/k8s/resources.go | 347 ++----------------- pkg/validation/validation.go | 2 +- 12 files changed, 183 insertions(+), 413 deletions(-) diff --git a/pkg/deployment/deployment_collection.go b/pkg/deployment/deployment_collection.go index 17faff1a6..7e099c31e 100644 --- a/pkg/deployment/deployment_collection.go +++ b/pkg/deployment/deployment_collection.go @@ -200,17 +200,6 @@ func (c *DeploymentCollection) buildKustomizeObjects() error { s.Success() s = status.Start(c.ctx.Ctx, "Postprocessing objects") - for _, d_ := range c.Deployments { - d := d_ - g.RunE(func() error { - err := d.postprocessCRDs() - if err != nil { - return fmt.Errorf("postprocessing CRDs failed: %w", err) - } - return nil - }) - } - g.Wait() g = utils.NewGoHelper(c.ctx.Ctx, 16) for _, d_ := range c.Deployments { diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index b1ef0acba..d291acbd0 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -14,7 +14,6 @@ import ( "github.com/kluctl/kluctl/v2/pkg/vars" "github.com/kluctl/kluctl/v2/pkg/yaml" "io/fs" - "k8s.io/apimachinery/pkg/runtime/schema" "os" "path" "path/filepath" @@ -541,32 +540,6 @@ func (di *DeploymentItem) buildKustomize() error { return nil } -var crdGV = schema.GroupKind{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"} - -// postprocessCRDs will update api resources from freshly deployed CRDs -// value even if the CRD is not deployed yet. -func (di *DeploymentItem) postprocessCRDs() error { - if di.dir == nil { - return nil - } - if di.ctx.K == nil { - return nil - } - - for _, o := range di.Objects { - gvk := o.GetK8sGVK() - if gvk.GroupKind() != crdGV { - continue - } - - err := di.ctx.K.Resources.UpdateResourcesFromCRD(o) - if err != nil { - return err - } - } - return nil -} - func (di *DeploymentItem) postprocessObjects(images *Images) error { if di.dir == nil { return nil diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index 72e1c0289..a4d928ba9 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -373,11 +373,17 @@ func (a *ApplyUtil) ApplyObject(x *uo.UnstructuredObject, replaced bool, hook bo _ = r.ReplaceKeys(tmpName, ref.Name) _ = r.ReplaceValues(tmpName, ref.Name) r.SetK8sNamespace(ref.Namespace) - } else if a.o.DryRun && errors.IsNotFound(err) { + } else if meta.IsNoMatchError(err) { if _, ok := a.allCRDs.Load(x.GetK8sGVK()); ok { - a.handleResult(x, hook) - a.HandleWarning(x.GetK8sRef(), fmt.Errorf("the underyling custom resource definition for %s has not been applied yet as Kluctl is running in dry-run mode. It is not guaranteed that the object will actually sucessfully apply", x.GetK8sRef().String())) - return + if a.o.DryRun { + a.handleResult(x, hook) + a.HandleWarning(x.GetK8sRef(), fmt.Errorf("the underyling custom resource definition for %s has not been applied yet as Kluctl is running in dry-run mode. It is not guaranteed that the object will actually sucessfully apply", x.GetK8sRef().String())) + return + } else { + // retry with invalidated discovery + a.k.ResetMapper() + r, apiWarnings, err = a.k.ApplyObject(x, options) + } } } if r != nil && ref.GroupKind().String() == "Namespace" { @@ -502,7 +508,11 @@ func (a *ApplyUtil) WaitReadiness(ref k8s2.ObjectRef, timeout time.Duration) boo func (a *ApplyUtil) applyDeploymentItem(d *deployment.DeploymentItem) { toDelete := map[k8s2.ObjectRef]bool{} for _, x := range d.Config.DeleteObjects { - for _, gvk := range a.k.Resources.GetFilteredGVKs(k8s.BuildGVKFilter(x.Group, nil, x.Kind)) { + gvks, err := a.k.GetFilteredGVKs(k8s.BuildGVKFilter(x.Group, nil, x.Kind)) + if err != nil { + a.HandleError(k8s2.ObjectRef{}, err) + } + for _, gvk := range gvks { ref := k8s2.ObjectRef{ Group: gvk.Group, Version: gvk.Version, diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index 1ca1f49f5..c32f41358 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -75,7 +75,11 @@ func filterObjectsForDelete(k *k8s.K8sCluster, objects []*uo.UnstructuredObject, } filteredResources := make(map[schema.GroupKind]bool) - for _, gvk := range k.Resources.GetFilteredPreferredGVKs(filterFunc) { + gvks, err := k.GetFilteredPreferredGVKs(filterFunc) + if err != nil { + return nil, err + } + for _, gvk := range gvks { filteredResources[gvk.GroupKind()] = true } diff --git a/pkg/deployment/utils/remote_objects_utils.go b/pkg/deployment/utils/remote_objects_utils.go index 0676f86e8..6cee1e0b7 100644 --- a/pkg/deployment/utils/remote_objects_utils.go +++ b/pkg/deployment/utils/remote_objects_utils.go @@ -9,6 +9,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" errors2 "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "sync" @@ -51,7 +52,7 @@ func (u *RemoteObjectUtils) getAllByDiscriminator(k *k8s.K8sCluster, discriminat errCount := 0 permissionErrCount := 0 - gvks := k.Resources.GetFilteredPreferredGVKs(func(ar *v1.APIResource) bool { + gvks, err := k.GetFilteredPreferredGVKs(func(ar *v1.APIResource) bool { if onlyUsedGKs != nil { gk := schema.GroupKind{ Group: ar.Group, @@ -63,6 +64,9 @@ func (u *RemoteObjectUtils) getAllByDiscriminator(k *k8s.K8sCluster, discriminat } return utils.FindStrInSlice(ar.Verbs, "list") != -1 }) + if err != nil { + return err + } g := utils.NewGoHelper(u.ctx, 0) for _, gvk := range gvks { @@ -141,7 +145,7 @@ func (u *RemoteObjectUtils) getMissingObjects(k *k8s.K8sCluster, refs []k8s2.Obj r, apiWarnings, err := k.GetSingleObject(ref) u.dew.AddApiWarnings(ref, apiWarnings) if err != nil { - if errors2.IsNotFound(err) { + if errors2.IsNotFound(err) || meta.IsNoMatchError(err) { return } if errors2.IsForbidden(err) || errors2.IsUnauthorized(err) { diff --git a/pkg/helm/helm_release.go b/pkg/helm/helm_release.go index 2f6bd364a..a5f741722 100644 --- a/pkg/helm/helm_release.go +++ b/pkg/helm/helm_release.go @@ -203,7 +203,10 @@ func (hr *Release) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion s client.Replace = true client.ClientOnly = true client.KubeVersion = kubeVersion - client.APIVersions = hr.getApiVersions(k) + client.APIVersions, err = hr.getApiVersions(k) + if err != nil { + return err + } if hr.Config.SkipCRDs { client.SkipCRDs = true @@ -281,14 +284,17 @@ func (hr *Release) doRender(ctx context.Context, k *k8s.K8sCluster, k8sVersion s return nil } -func (hr *Release) getApiVersions(k *k8s.K8sCluster) chartutil.VersionSet { +func (hr *Release) getApiVersions(k *k8s.K8sCluster) (chartutil.VersionSet, error) { if k == nil { - return nil + return nil, nil } m := map[string]bool{} - gvks := k.Resources.GetFilteredGVKs(nil) + gvks, err := k.GetFilteredGVKs(nil) + if err != nil { + return nil, err + } for _, gvk := range gvks { gvStr := gvk.GroupVersion().String() m[gvStr] = true @@ -301,7 +307,7 @@ func (hr *Release) getApiVersions(k *k8s.K8sCluster) chartutil.VersionSet { ret = append(ret, id) } - return ret + return ret, nil } func (hr *Release) parseRenderedManifests(s string) ([]*uo.UnstructuredObject, error) { diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index fe0044ebc..8cf3b6b75 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -3,7 +3,6 @@ package k8s import ( "context" "fmt" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/metadata" @@ -111,13 +110,3 @@ func (k *k8sClients) withCClientFromPool(dryRun bool, cb func(c client.Client) e return cb(c) }) } - -func (k *k8sClients) withDynamicClientForGVR(gvr *schema.GroupVersionResource, namespace string, cb func(r dynamic.ResourceInterface) error) ([]ApiWarning, error) { - return k.withClientFromPool(func(p *parallelClientEntry) error { - if namespace != "" { - return cb(p.dynamicClient.Resource(*gvr).Namespace(namespace)) - } else { - return cb(p.dynamicClient.Resource(*gvr)) - } - }) -} diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go index 594ddecc1..049575e8e 100644 --- a/pkg/k8s/client_factory.go +++ b/pkg/k8s/client_factory.go @@ -3,12 +3,14 @@ package k8s import ( "context" "github.com/kluctl/kluctl/v2/pkg/utils" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/disk" "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/metadata" "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" "k8s.io/client-go/tools/clientcmd" "net/http" "net/url" @@ -23,6 +25,7 @@ type ClientFactory interface { CloseIdleConnections() + Mapper() meta.ResettableRESTMapper Client(wh rest.WarningHandler) (client.Client, error) DiscoveryClient() (discovery.DiscoveryInterface, error) @@ -35,6 +38,9 @@ type realClientFactory struct { ctx context.Context config *rest.Config httpClient *http.Client + + discoveryClient discovery.DiscoveryInterface + mapper meta.ResettableRESTMapper } func (r *realClientFactory) RESTConfig() *rest.Config { @@ -45,10 +51,16 @@ func (r *realClientFactory) GetCA() []byte { return r.config.CAData } +func (r *realClientFactory) Mapper() meta.ResettableRESTMapper { + return r.mapper +} + func (r *realClientFactory) Client(wh rest.WarningHandler) (client.Client, error) { config := rest.CopyConfig(r.config) config.WarningHandler = wh + return client.New(config, client.Options{ + Mapper: r.mapper, WarningHandler: client.WarningHandlerOptions{ SuppressWarnings: true, }, @@ -56,16 +68,7 @@ func (r *realClientFactory) Client(wh rest.WarningHandler) (client.Client, error } func (r *realClientFactory) DiscoveryClient() (discovery.DiscoveryInterface, error) { - apiHost, err := url.Parse(r.config.Host) - if err != nil { - return nil, err - } - discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(r.ctx), "kube-cache/discovery", apiHost.Hostname()) - discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(r.config), discoveryCacheDir, "", time.Hour*24) - if err != nil { - return nil, err - } - return discovery2, nil + return r.discoveryClient, nil } func (r *realClientFactory) CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1Interface, error) { @@ -90,6 +93,19 @@ func (r *realClientFactory) CloseIdleConnections() { r.httpClient.CloseIdleConnections() } +func initDiscoveryClient(ctx context.Context, config *rest.Config) (discovery.CachedDiscoveryInterface, error) { + apiHost, err := url.Parse(config.Host) + if err != nil { + return nil, err + } + discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(ctx), "kube-cache/discovery", apiHost.Hostname()) + discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(config), discoveryCacheDir, "", time.Hour*24) + if err != nil { + return nil, err + } + return discovery2, nil +} + func NewClientFactory(ctx context.Context, configIn *rest.Config) (ClientFactory, error) { restConfig := rest.CopyConfig(configIn) restConfig.QPS = 10 @@ -100,10 +116,19 @@ func NewClientFactory(ctx context.Context, configIn *rest.Config) (ClientFactory return nil, err } + dc, err := initDiscoveryClient(ctx, restConfig) + if err != nil { + return nil, err + } + + mapper := restmapper.NewDeferredDiscoveryRESTMapper(dc) + return &realClientFactory{ - ctx: ctx, - config: restConfig, - httpClient: httpClient, + ctx: ctx, + config: restConfig, + httpClient: httpClient, + discoveryClient: dc, + mapper: mapper, }, nil } diff --git a/pkg/k8s/fake_client_factory.go b/pkg/k8s/fake_client_factory.go index 6ea0dd26d..31abd072d 100644 --- a/pkg/k8s/fake_client_factory.go +++ b/pkg/k8s/fake_client_factory.go @@ -41,6 +41,10 @@ func (f *fakeClientFactory) GetCA() []byte { func (f *fakeClientFactory) CloseIdleConnections() { } +func (f *fakeClientFactory) Mapper() meta.ResettableRESTMapper { + return nil +} + func (f *fakeClientFactory) Client(wh rest.WarningHandler) (client.Client, error) { return f.clientBuilder.Build(), nil } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 8f0805761..810e495f3 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -4,11 +4,12 @@ import ( "context" "fmt" "io" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "net/http" + "runtime" "sigs.k8s.io/controller-runtime/pkg/client" "strings" - "sync" "time" "github.com/Masterminds/semver/v3" @@ -34,11 +35,11 @@ type K8sCluster struct { DryRun bool clientFactory ClientFactory - clients *k8sClients - ServerVersion *version.Info + discovery discovery.DiscoveryInterface + clients *k8sClients - Resources *k8sResources + ServerVersion *version.Info } func NewK8sCluster(ctx context.Context, clientFactory ClientFactory, dryRun bool) (*K8sCluster, error) { @@ -50,7 +51,7 @@ func NewK8sCluster(ctx context.Context, clientFactory ClientFactory, dryRun bool clientFactory: clientFactory, } - k.Resources, err = newK8sResources(ctx, clientFactory) + k.discovery, err = clientFactory.DiscoveryClient() if err != nil { return nil, err } @@ -60,34 +61,12 @@ func NewK8sCluster(ctx context.Context, clientFactory ClientFactory, dryRun bool return nil, err } - v, err := k.Resources.discovery.ServerVersion() + v, err := k.discovery.ServerVersion() if err != nil { return nil, err } k.ServerVersion = v - var wg sync.WaitGroup - wg.Add(2) - - var err1 error - var err2 error - go func() { - err1 = k.Resources.updateResources() - wg.Done() - }() - go func() { - err2 = k.Resources.updateResourcesFromCRDs(k.clients) - wg.Done() - }() - wg.Wait() - - if err1 != nil { - return nil, err1 - } - if err2 != nil { - return nil, err2 - } - return k, nil } @@ -201,6 +180,7 @@ func (k *K8sCluster) DeleteSingleObject(ref k8s.ObjectRef, options DeleteOptions return apiWarnings, err } } + return apiWarnings, nil } @@ -469,6 +449,63 @@ func (k *K8sCluster) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { return ref } +func (k *K8sCluster) GetSchemaForGVK(gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { + if gvk.Kind == "KluctlDeployment" { + runtime.Breakpoint() + } + + rms, err := k.clientFactory.Mapper().RESTMappings(gvk.GroupKind(), gvk.Version) + if err != nil { + return nil, err + } + + var rm *meta.RESTMapping + for _, x := range rms { + if x.GroupVersionKind == gvk { + rm = x + break + } + } + if rm == nil { + return nil, fmt.Errorf("rest mapping not found") + } + + ref := k8s.NewObjectRef(apiextensionsv1.GroupName, "v1", "CustomResourceDefinition", fmt.Sprintf("%s.%s", rm.Resource.Resource, gvk.Group), "") + + crd, _, err := k.GetSingleObject(ref) + if err != nil { + return nil, err + } + + versions, ok, err := crd.GetNestedObjectList("spec", "versions") + if err != nil { + return nil, err + } + if !ok { + return nil, fmt.Errorf("versions not found in CRD") + } + + for _, v := range versions { + name, _, _ := v.GetNestedString("name") + if name != gvk.Version { + continue + } + s, ok, err := v.GetNestedObject("schema", "openAPIV3Schema") + if err != nil { + return nil, err + } + if !ok { + return nil, fmt.Errorf("version %s has no schema", name) + } + return s, nil + } + return nil, fmt.Errorf("schema for %s not found", gvk.String()) +} + +func (k *K8sCluster) ResetMapper() { + k.clientFactory.Mapper().Reset() +} + func (k *K8sCluster) ToRESTConfig() (*rest.Config, error) { return k.clientFactory.RESTConfig(), nil } diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index ec5188a7b..8295b7498 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -1,20 +1,12 @@ package k8s import ( - "context" "fmt" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" - "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" - "runtime" - "sort" + "k8s.io/client-go/restmapper" "strings" - "sync" ) var ( @@ -24,49 +16,14 @@ var ( } ) -type k8sResources struct { - ctx context.Context - discovery discovery.DiscoveryInterface - - allResources map[schema.GroupVersionKind]v1.APIResource - preferredResources map[schema.GroupKind]v1.APIResource - crds map[schema.GroupKind]*uo.UnstructuredObject - mutex sync.Mutex -} - -func newK8sResources(ctx context.Context, clientFactory ClientFactory) (*k8sResources, error) { - k := &k8sResources{ - ctx: ctx, - allResources: map[schema.GroupVersionKind]v1.APIResource{}, - preferredResources: map[schema.GroupKind]v1.APIResource{}, - crds: map[schema.GroupKind]*uo.UnstructuredObject{}, - mutex: sync.Mutex{}, - } - - var err error - k.discovery, err = clientFactory.DiscoveryClient() - if err != nil { - return nil, err - } - - return k, nil -} - -func (k *k8sResources) updateResources() error { - k.mutex.Lock() - defer k.mutex.Unlock() - - k.allResources = map[schema.GroupVersionKind]v1.APIResource{} - k.preferredResources = map[schema.GroupKind]v1.APIResource{} - k.crds = map[schema.GroupKind]*uo.UnstructuredObject{} +func (k *K8sCluster) doGetApiGroupResources() ([]*restmapper.APIGroupResources, error) { + var ret []*restmapper.APIGroupResources // the discovery client doesn't support cancellation, so we need to run it in the background and wait for it - var ags []*v1.APIGroup - var arls []*v1.APIResourceList finished := make(chan error) go func() { var err error - ags, arls, err = k.discovery.ServerGroupsAndResources() + ret, err = restmapper.GetAPIGroupResources(k.discovery) if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { finished <- err return @@ -77,234 +34,65 @@ func (k *k8sResources) updateResources() error { select { case err := <-finished: if err != nil { - return err + return nil, err } case <-k.ctx.Done(): - return fmt.Errorf("failed listing api resources: %w", k.ctx.Err()) + return nil, fmt.Errorf("failed listing api resources: %w", k.ctx.Err()) } - - for _, arl := range arls { - var ag *v1.APIGroup - for _, x := range ags { - if x.Name == arl.GroupVersionKind().Group { - ag = x - break - } - } - if ag == nil { - continue - } - - for _, ar := range arl.APIResources { - if ar.Version == "__internal" { - continue - } - if strings.Index(ar.Name, "/") != -1 { - // skip subresources - continue - } - gv, err := schema.ParseGroupVersion(arl.GroupVersion) - if err != nil { - continue - } - - ar := ar - ar.Group = gv.Group - ar.Version = gv.Version - - gvk := schema.GroupVersionKind{ - Group: ar.Group, - Version: ar.Version, - Kind: ar.Kind, - } - if _, ok := deprecatedResources[gvk.GroupKind()]; ok { - continue - } - - k.allResources[gvk] = ar - - if gvk.Version == ag.PreferredVersion.Version { - k.preferredResources[gvk.GroupKind()] = ar - } - } - } - if len(k.preferredResources) == 0 { - runtime.Breakpoint() - } - - return nil + return ret, nil } -var crdGVR = schema.GroupVersionResource{Group: "apiextensions.k8s.io", Version: "v1", Resource: "customresourcedefinitions"} - -func (k *k8sResources) updateResourcesFromCRDs(clients *k8sClients) error { - var crdList *unstructured.UnstructuredList - _, err := clients.withDynamicClientForGVR(&crdGVR, "", func(r dynamic.ResourceInterface) error { - var err error - crdList, err = r.List(k.ctx, v1.ListOptions{}) - return err - }) - if err != nil { - return err - } - - for _, x := range crdList.Items { - x := x - crd := uo.FromUnstructured(&x) - err = k.UpdateResourcesFromCRD(crd) - if err != nil { - return err - } - } - - return nil -} - -func (k *k8sResources) UpdateResourcesFromCRD(crd *uo.UnstructuredObject) error { - var err error - var ar v1.APIResource - ar.Group, _, err = crd.GetNestedString("spec", "group") - if err != nil { - return err - } - ar.Name, _, err = crd.GetNestedString("spec", "names", "plural") - if err != nil { - return err - } - ar.Kind, _, err = crd.GetNestedString("spec", "names", "kind") - if err != nil { - return err - } - ar.SingularName, _, err = crd.GetNestedString("spec", "names", "singular") - if err != nil { - return err - } - scope, _, err := crd.GetNestedString("spec", "scope") - if err != nil { - return err - } - ar.Namespaced = strings.ToLower(scope) == "namespaced" - ar.ShortNames, _, err = crd.GetNestedStringList("spec", "names", "shortNames") - if err != nil { - return err - } - ar.Categories, _, err = crd.GetNestedStringList("spec", "names", "categories") - if err != nil { - return err - } - versions, _, err := crd.GetNestedObjectList("spec", "versions") - if err != nil { - return err - } - - ar.Verbs = []string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"} - - gk := schema.GroupKind{ - Group: ar.Group, - Kind: ar.Kind, - } - - k.mutex.Lock() - defer k.mutex.Unlock() - - k.crds[gk] = crd - - var versionStrs []string - for _, v := range versions { - name, _, err := v.GetNestedString("name") - if err != nil { - return err +func (k *K8sCluster) doGetFilteredGVKs(ret *[]schema.GroupVersionKind, g v1.APIGroup, v string, vrs []v1.APIResource, filter func(ar *v1.APIResource) bool) { + for _, vr := range vrs { + if strings.Index(vr.Name, "/") != -1 { + // skip sub-resources + continue } - versionStrs = append(versionStrs, name) - } - - // Sort the same way as api discovery does it. The first entry is then the preferred version - sort.Slice(versionStrs, func(i, j int) bool { - return version.CompareKubeAwareVersionStrings(versionStrs[i], versionStrs[j]) > 0 - }) - - for i, v := range versionStrs { - ar2 := ar - ar2.Version = v gvk := schema.GroupVersionKind{ - Group: gk.Group, - Version: ar2.Version, - Kind: gk.Kind, + Group: g.Name, + Version: v, + Kind: vr.Kind, } - k.allResources[gvk] = ar2 - - if i == 0 { - k.preferredResources[gk] = ar2 + if _, ok := deprecatedResources[gvk.GroupKind()]; ok { + continue } - } - - return nil -} - -func (k *k8sResources) GetAllGroupVersions() ([]schema.GroupVersion, error) { - k.mutex.Lock() - defer k.mutex.Unlock() - - m := make(map[schema.GroupVersion]bool) - var l []schema.GroupVersion - - for gvk, _ := range k.allResources { - gv := gvk.GroupVersion() - if _, ok := m[gv]; !ok { - m[gv] = true - l = append(l, gv) + if filter != nil && !filter(&vr) { + continue } + *ret = append(*ret, gvk) } - return l, nil } -func (k *k8sResources) GetPreferredResource(gk schema.GroupKind) *v1.APIResource { - k.mutex.Lock() - defer k.mutex.Unlock() - - ar, ok := k.preferredResources[gk] - if !ok { - return nil +func (k *K8sCluster) GetFilteredGVKs(filter func(ar *v1.APIResource) bool) ([]schema.GroupVersionKind, error) { + agrs, err := k.doGetApiGroupResources() + if err != nil { + return nil, err } - return &ar -} - -func (k *k8sResources) GetFilteredGVKs(filter func(ar *v1.APIResource) bool) []schema.GroupVersionKind { - k.mutex.Lock() - defer k.mutex.Unlock() var ret []schema.GroupVersionKind - for _, ar := range k.allResources { - if filter != nil && !filter(&ar) { - continue + for _, agr := range agrs { + for v, vrs := range agr.VersionedResources { + k.doGetFilteredGVKs(&ret, agr.Group, v, vrs, filter) } - gvk := schema.GroupVersionKind{ - Group: ar.Group, - Version: ar.Version, - Kind: ar.Kind, - } - ret = append(ret, gvk) } - return ret + return ret, nil } -func (k *k8sResources) GetFilteredPreferredGVKs(filter func(ar *v1.APIResource) bool) []schema.GroupVersionKind { - k.mutex.Lock() - defer k.mutex.Unlock() +func (k *K8sCluster) GetFilteredPreferredGVKs(filter func(ar *v1.APIResource) bool) ([]schema.GroupVersionKind, error) { + agrs, err := k.doGetApiGroupResources() + if err != nil { + return nil, err + } var ret []schema.GroupVersionKind - for _, ar := range k.preferredResources { - if !filter(&ar) { + for _, agr := range agrs { + vrs, ok := agr.VersionedResources[agr.Group.PreferredVersion.Version] + if !ok { continue } - gvk := schema.GroupVersionKind{ - Group: ar.Group, - Version: ar.Version, - Kind: ar.Kind, - } - ret = append(ret, gvk) + k.doGetFilteredGVKs(&ret, agr.Group, agr.Group.PreferredVersion.Version, vrs, filter) } - return ret + return ret, nil } func BuildGVKFilter(group *string, version *string, kind *string) func(ar *v1.APIResource) bool { @@ -321,62 +109,3 @@ func BuildGVKFilter(group *string, version *string, kind *string) func(ar *v1.AP return true } } - -func (k *k8sResources) GetGVRForGVK(gvk schema.GroupVersionKind) (*schema.GroupVersionResource, error) { - k.mutex.Lock() - defer k.mutex.Unlock() - - ar, ok := k.allResources[gvk] - if !ok { - return nil, &meta.NoKindMatchError{ - GroupKind: gvk.GroupKind(), - SearchedVersions: []string{gvk.Version}, - } - } - - return &schema.GroupVersionResource{ - Group: ar.Group, - Version: ar.Version, - Resource: ar.Name, - }, nil -} - -func (k *k8sResources) GetCRDForGK(gk schema.GroupKind) *uo.UnstructuredObject { - k.mutex.Lock() - defer k.mutex.Unlock() - - crd, _ := k.crds[gk] - - return crd -} - -func (k *k8sResources) GetSchemaForGVK(gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { - crd := k.GetCRDForGK(gvk.GroupKind()) - if crd == nil { - return nil, nil - } - - versions, ok, err := crd.GetNestedObjectList("spec", "versions") - if err != nil { - return nil, err - } - if !ok { - return nil, fmt.Errorf("versions not found in CRD") - } - - for _, v := range versions { - name, _, _ := v.GetNestedString("name") - if name != gvk.Version { - continue - } - s, ok, err := v.GetNestedObject("schema", "openAPIV3Schema") - if err != nil { - return nil, err - } - if !ok { - return nil, fmt.Errorf("version %s has no schema", name) - } - return s, nil - } - return nil, fmt.Errorf("schema for %s not found", gvk.String()) -} diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index 09f23d401..673a346ba 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -126,7 +126,7 @@ func ValidateObject(k *k8s.K8sCluster, o *uo.UnstructuredObject, notReadyIsError // can't really say anything... return } - s, err := k.Resources.GetSchemaForGVK(ref.GroupVersionKind()) + s, err := k.GetSchemaForGVK(ref.GroupVersionKind()) if err != nil && !errors.IsNotFound(err) { addError(err.Error()) return From d21e6b7d815e4475c143f592b2c00771ded7b3a2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 24 May 2023 09:38:06 +0200 Subject: [PATCH 1033/2268] fix: Use client from K8sCluster for ResultStore --- cmd/kluctl/commands/utils.go | 20 ++------------------ pkg/k8s/client.go | 17 +---------------- pkg/k8s/client_factory.go | 6 ++---- pkg/k8s/k8s_cluster.go | 24 +++++++++--------------- 4 files changed, 14 insertions(+), 53 deletions(-) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 630199ca5..66e6103b0 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -21,8 +21,6 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" "os" - cache2 "sigs.k8s.io/controller-runtime/pkg/cache" - client2 "sigs.k8s.io/controller-runtime/pkg/client" "strings" ) @@ -201,26 +199,12 @@ func withProjectTargetCommandContext(ctx context.Context, args projectTargetComm var resultStore results.ResultStore if args.commandResultFlags != nil && args.commandResultFlags.WriteCommandResult { - rc, err := targetCtx.SharedContext.K.ToRESTConfig() + client, err := targetCtx.SharedContext.K.ToClient() if err != nil { return err } - client, err := client2.New(rc, client2.Options{}) - if err != nil { - return err - } - cache, err := cache2.New(rc, cache2.Options{}) - if err != nil { - return err - } - - cancelCtx, cancelFunc := context.WithCancel(ctx) - defer cancelFunc() - go func() { - _ = cache.Start(cancelCtx) - }() - resultStore, err = results.NewResultStoreSecrets(cancelCtx, client, cache, args.commandResultFlags.CommandResultNamespace, args.commandResultFlags.KeepCommandResultsCount) + resultStore, err = results.NewResultStoreSecrets(ctx, client, nil, args.commandResultFlags.CommandResultNamespace, args.commandResultFlags.KeepCommandResultsCount) if err != nil { return err } diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index 8cf3b6b75..b65df6d28 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -3,9 +3,7 @@ package k8s import ( "context" "fmt" - "k8s.io/client-go/dynamic" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/metadata" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -18,10 +16,7 @@ type k8sClients struct { type parallelClientEntry struct { client client.Client - - corev1 corev1.CoreV1Interface - dynamicClient dynamic.Interface - metadataClient metadata.Interface + corev1 corev1.CoreV1Interface warnings []ApiWarning } @@ -63,16 +58,6 @@ func newK8sClients(ctx context.Context, clientFactory ClientFactory, count int) return nil, err } - p.dynamicClient, err = clientFactory.DynamicClient(p) - if err != nil { - return nil, err - } - - p.metadataClient, err = clientFactory.MetadataClient(p) - if err != nil { - return nil, err - } - k.clientPool <- p } return k, nil diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go index 049575e8e..46786fc21 100644 --- a/pkg/k8s/client_factory.go +++ b/pkg/k8s/client_factory.go @@ -25,13 +25,11 @@ type ClientFactory interface { CloseIdleConnections() - Mapper() meta.ResettableRESTMapper + Mapper() meta.RESTMapper Client(wh rest.WarningHandler) (client.Client, error) DiscoveryClient() (discovery.DiscoveryInterface, error) CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1Interface, error) - DynamicClient(wh rest.WarningHandler) (dynamic.Interface, error) - MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) } type realClientFactory struct { @@ -51,7 +49,7 @@ func (r *realClientFactory) GetCA() []byte { return r.config.CAData } -func (r *realClientFactory) Mapper() meta.ResettableRESTMapper { +func (r *realClientFactory) Mapper() meta.RESTMapper { return r.mapper } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 810e495f3..f5a974eb0 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -26,7 +26,6 @@ import ( "k8s.io/client-go/discovery" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" - "k8s.io/client-go/restmapper" ) type K8sCluster struct { @@ -503,7 +502,13 @@ func (k *K8sCluster) GetSchemaForGVK(gvk schema.GroupVersionKind) (*uo.Unstructu } func (k *K8sCluster) ResetMapper() { - k.clientFactory.Mapper().Reset() + if m, ok := k.clientFactory.Mapper().(meta.ResettableRESTMapper); ok { + m.Reset() + } +} + +func (k *K8sCluster) ToClient() (client.Client, error) { + return k.clientFactory.Client(nil) } func (k *K8sCluster) ToRESTConfig() (*rest.Config, error) { @@ -511,11 +516,7 @@ func (k *K8sCluster) ToRESTConfig() (*rest.Config, error) { } func (k *K8sCluster) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) { - d, err := k.clientFactory.DiscoveryClient() - if err != nil { - return nil, err - } - cd, ok := d.(discovery.CachedDiscoveryInterface) + cd, ok := k.discovery.(discovery.CachedDiscoveryInterface) if !ok { return nil, fmt.Errorf("not a CachedDiscoveryInterface") } @@ -523,12 +524,5 @@ func (k *K8sCluster) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, er } func (k *K8sCluster) ToRESTMapper() (meta.RESTMapper, error) { - discoveryClient, err := k.ToDiscoveryClient() - if err != nil { - return nil, err - } - - mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) - expander := restmapper.NewShortcutExpander(mapper, discoveryClient) - return expander, nil + return k.clientFactory.Mapper(), nil } From c42efce9c6898c7ad9b9b8eb24df06219da1cf4b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 24 May 2023 09:38:33 +0200 Subject: [PATCH 1034/2268] refactor: Use meta.ExtractList --- pkg/k8s/k8s_cluster.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index f5a974eb0..363054b6e 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -103,20 +103,24 @@ func (k *K8sCluster) doList(l client.ObjectList, namespace string, labels map[st return nil, apiWarnings, err } - var result []*uo.UnstructuredObject - if l2, ok := l.(*unstructured.UnstructuredList); ok { - for _, o := range l2.Items { - result = append(result, uo.FromUnstructured(&o)) - } - } else if l2, ok := l.(*v1.PartialObjectMetadataList); ok { - for _, o := range l2.Items { - x, err := uo.FromStruct(&o) + items, err := meta.ExtractList(l) + if err != nil { + return nil, apiWarnings, err + } + + result := make([]*uo.UnstructuredObject, len(items)) + for i, o := range items { + if u, ok := o.(*unstructured.Unstructured); ok { + result[i] = uo.FromUnstructured(u) + } else { + x, err := uo.FromStruct(o) if err != nil { return nil, apiWarnings, err } - result = append(result, x) + result[i] = x } } + return result, apiWarnings, err } func (k *K8sCluster) ListObjects(gvk schema.GroupVersionKind, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { From 932a1dc08737d4a60f1094424f8841763f4bab4a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 24 May 2023 09:38:44 +0200 Subject: [PATCH 1035/2268] tests: Fix fake K8sCluster tests --- pkg/k8s/fake_client_factory.go | 172 ++++++++++++++++++++++++--------- 1 file changed, 124 insertions(+), 48 deletions(-) diff --git a/pkg/k8s/fake_client_factory.go b/pkg/k8s/fake_client_factory.go index 31abd072d..f5e191d32 100644 --- a/pkg/k8s/fake_client_factory.go +++ b/pkg/k8s/fake_client_factory.go @@ -1,6 +1,7 @@ package k8s import ( + "context" v1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/meta" @@ -8,26 +9,37 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" - fake_dynamic "k8s.io/client-go/dynamic/fake" + fake4 "k8s.io/client-go/discovery/fake" + fake3 "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes/fake" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/metadata" - fake_metadata "k8s.io/client-go/metadata/fake" "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" "k8s.io/client-go/testing" "sigs.k8s.io/controller-runtime/pkg/client" fake2 "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/interceptor" "sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/yaml" "strings" ) type fakeClientFactory struct { - clientSet *fake.Clientset - dynamicClient *fake_dynamic.FakeDynamicClient - metadataClient *fake_metadata.FakeMetadataClient - clientBuilder *fake2.ClientBuilder + scheme *runtime.Scheme + mapper meta.RESTMapper + objects []runtime.Object + simpleClientSet *fake.Clientset + dynamicClientSet *fake3.FakeDynamicClient + discovery discovery.DiscoveryInterface + + errors []errorEntry +} + +type errorEntry struct { + gvr schema.GroupVersionResource + name string + namespace string + retErr error } func (f *fakeClientFactory) RESTConfig() *rest.Config { @@ -41,71 +53,135 @@ func (f *fakeClientFactory) GetCA() []byte { func (f *fakeClientFactory) CloseIdleConnections() { } -func (f *fakeClientFactory) Mapper() meta.ResettableRESTMapper { - return nil +func (f *fakeClientFactory) Mapper() meta.RESTMapper { + return f.mapper } func (f *fakeClientFactory) Client(wh rest.WarningHandler) (client.Client, error) { - return f.clientBuilder.Build(), nil + return fake2.NewClientBuilder(). + WithScheme(f.scheme). + WithRESTMapper(f.mapper). + WithObjectTracker(f.dynamicClientSet.Tracker()). + WithInterceptorFuncs(f.buildErrorInterceptor()). + Build(), nil } func (f *fakeClientFactory) DiscoveryClient() (discovery.DiscoveryInterface, error) { - return f.clientSet.Discovery(), nil + return f.discovery, nil } func (f *fakeClientFactory) CoreV1Client(wh rest.WarningHandler) (corev1.CoreV1Interface, error) { - return f.clientSet.CoreV1(), nil -} - -func (f *fakeClientFactory) DynamicClient(wh rest.WarningHandler) (dynamic.Interface, error) { - return f.dynamicClient, nil -} - -func (f *fakeClientFactory) MetadataClient(wh rest.WarningHandler) (metadata.Interface, error) { - return f.metadataClient, nil + return f.simpleClientSet.CoreV1(), nil } func NewFakeClientFactory(objects ...runtime.Object) *fakeClientFactory { scheme := runtime.NewScheme() _ = v1.AddToScheme(scheme) _ = apiextensionsv1.AddToScheme(scheme) - clientSet := fake.NewSimpleClientset(objects...) - - clientSet.Fake.Resources = ConvertSchemeToAPIResources(scheme) - - dynamicClient := fake_dynamic.NewSimpleDynamicClient(scheme, objects...) - metadataClient := fake_metadata.NewSimpleMetadataClient(scheme, objects...) - - clientBuilder := fake2.NewClientBuilder(). - WithScheme(scheme). - WithRuntimeObjects(objects...) - return &fakeClientFactory{ - clientSet: clientSet, - dynamicClient: dynamicClient, - metadataClient: metadataClient, - clientBuilder: clientBuilder, + simpleClientSet := fake.NewSimpleClientset(objects...) + dynamicClientSet := fake3.NewSimpleDynamicClient(scheme, objects...) + dynamicClientSet.Fake.Resources = ConvertSchemeToAPIResources(scheme) + dc := &fake4.FakeDiscovery{Fake: &dynamicClientSet.Fake} + + agrs, _ := restmapper.GetAPIGroupResources(dc) + mapper := restmapper.NewDiscoveryRESTMapper(agrs) + + f := &fakeClientFactory{ + scheme: scheme, + mapper: mapper, + objects: objects, + simpleClientSet: simpleClientSet, + dynamicClientSet: dynamicClientSet, + discovery: dc, } + simpleClientSet.PrependReactor("*", "*", f.errorReactor) + dynamicClientSet.PrependReactor("*", "*", f.errorReactor) + return f } type HasName interface { GetName() string } -func (f *fakeClientFactory) AddError(gvr schema.GroupVersionResource, name string, namespace string, retErr error) { - f.dynamicClient.PrependReactor("*", gvr.Resource, func(action testing.Action) (handled bool, ret runtime.Object, err error) { - if namespace != "" && namespace != action.GetNamespace() { - return false, nil, nil +func (f *fakeClientFactory) findError(gvr schema.GroupVersionResource, name string, namespace string) error { + for _, ee := range f.errors { + if ee.gvr != gvr { + continue } - switch a := action.(type) { - case HasName: - if name != "" && name != a.GetName() { - return false, nil, nil - } - return true, nil, retErr - default: - return true, nil, retErr + if ee.namespace != "" && ee.namespace != namespace { + continue + } + + if ee.name != "" && ee.namespace != name { + continue } + + return ee.retErr + } + return nil +} + +func (f *fakeClientFactory) buildErrorInterceptor() interceptor.Funcs { + h := func(key *client.ObjectKey, obj runtime.Object) error { + name := "" + namespace := "" + if key != nil { + name = key.Name + namespace = key.Namespace + } else if o, ok := obj.(client.Object); ok { + name = o.GetName() + namespace = o.GetNamespace() + } + + gk := obj.GetObjectKind().GroupVersionKind().GroupKind() + rm, err := f.mapper.RESTMapping(gk, obj.GetObjectKind().GroupVersionKind().Version) + if err != nil { + return nil + } + return f.findError(rm.Resource, name, namespace) + } + + return interceptor.Funcs{ + Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + err := h(&key, obj) + if err != nil { + return err + } + return client.Get(ctx, key, obj) + }, + List: func(ctx context.Context, client client.WithWatch, list client.ObjectList, opts ...client.ListOption) error { + err := h(nil, list) + if err != nil { + return err + } + return client.List(ctx, list, opts...) + }, + } +} + +func (f *fakeClientFactory) errorReactor(action testing.Action) (handled bool, ret runtime.Object, err error) { + a, ok := action.(HasName) + if !ok { + return false, nil, nil + } + if err != nil { + return false, nil, nil + } + + err = f.findError(action.GetResource(), a.GetName(), action.GetNamespace()) + if err != nil { + return true, nil, err + } + return false, nil, err +} + +func (f *fakeClientFactory) AddError(gvr schema.GroupVersionResource, name string, namespace string, retErr error) { + f.errors = append(f.errors, errorEntry{ + gvr: gvr, + name: name, + namespace: namespace, + retErr: retErr, }) } From 7f8cad0b211770beb07dd710b35b1601edaebf5e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 24 May 2023 09:49:48 +0200 Subject: [PATCH 1036/2268] chore: Upgrade controller-runtime to v0.15.0 --- go.mod | 4 ++-- go.sum | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 825eafab4..005915bda 100644 --- a/go.mod +++ b/go.mod @@ -73,7 +73,7 @@ require ( github.com/sergi/go-diff v1.3.1 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/cli-utils v0.34.0 - sigs.k8s.io/controller-runtime v0.15.0-beta.0 + sigs.k8s.io/controller-runtime v0.15.0 sigs.k8s.io/kustomize/api v0.13.4 sigs.k8s.io/yaml v1.3.0 ) @@ -229,7 +229,7 @@ require ( golang.org/x/oauth2 v0.7.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 + gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect google.golang.org/api v0.114.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230323212658-478b75c54725 // indirect diff --git a/go.sum b/go.sum index b868dcef2..55fc6f5d6 100644 --- a/go.sum +++ b/go.sum @@ -245,7 +245,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m 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/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= @@ -1185,8 +1184,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= +gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= +gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= 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= @@ -1378,8 +1377,8 @@ 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/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= sigs.k8s.io/cli-utils v0.34.0/go.mod h1:EXyMwPMu9OL+LRnj0JEMsGG/fRvbgFadcVlSnE8RhFs= -sigs.k8s.io/controller-runtime v0.15.0-beta.0 h1:pkhYMops8jZrVuI0kBHeF6q9UVu1JljIGGG4Ox5ZJmk= -sigs.k8s.io/controller-runtime v0.15.0-beta.0/go.mod h1:YUTa+du31rqOu4mJaijiuhGFax9ecCJgO/v0/yW09gE= +sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= +sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= 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/kustomize/api v0.13.4 h1:E38Hfx0G9R9v7vRgKshviPotJQETG0S2gD3JdHLCAsI= From 7189adad8e89da1de8279d7e2a123fc6609e2416 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 24 May 2023 10:23:31 +0200 Subject: [PATCH 1037/2268] fix: Fix race condition in parallel discovery --- pkg/k8s/k8s_cluster.go | 7 +++++-- pkg/k8s/resources.go | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 363054b6e..a6991b95e 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -10,6 +10,7 @@ import ( "runtime" "sigs.k8s.io/controller-runtime/pkg/client" "strings" + "sync" "time" "github.com/Masterminds/semver/v3" @@ -35,8 +36,10 @@ type K8sCluster struct { clientFactory ClientFactory - discovery discovery.DiscoveryInterface - clients *k8sClients + discovery discovery.DiscoveryInterface + discoveryMutex sync.Mutex + + clients *k8sClients ServerVersion *version.Info } diff --git a/pkg/k8s/resources.go b/pkg/k8s/resources.go index 8295b7498..d9dce9c21 100644 --- a/pkg/k8s/resources.go +++ b/pkg/k8s/resources.go @@ -22,6 +22,11 @@ func (k *K8sCluster) doGetApiGroupResources() ([]*restmapper.APIGroupResources, // the discovery client doesn't support cancellation, so we need to run it in the background and wait for it finished := make(chan error) go func() { + // there is a race deep inside the cached discovery client + // see https://github.com/kubernetes/apimachinery/issues/156 + k.discoveryMutex.Lock() + defer k.discoveryMutex.Unlock() + var err error ret, err = restmapper.GetAPIGroupResources(k.discovery) if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { From 1c653d0a34b673a6a589b99836c2b2e331754002 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 24 May 2023 11:00:18 +0200 Subject: [PATCH 1038/2268] fix: Don't copy mutex by value (#504) --- pkg/k8s/k8s_cluster.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index a6991b95e..b7b37d515 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -37,7 +37,7 @@ type K8sCluster struct { clientFactory ClientFactory discovery discovery.DiscoveryInterface - discoveryMutex sync.Mutex + discoveryMutex *sync.Mutex clients *k8sClients @@ -48,9 +48,10 @@ func NewK8sCluster(ctx context.Context, clientFactory ClientFactory, dryRun bool var err error k := &K8sCluster{ - ctx: ctx, - DryRun: dryRun, - clientFactory: clientFactory, + ctx: ctx, + DryRun: dryRun, + clientFactory: clientFactory, + discoveryMutex: &sync.Mutex{}, } k.discovery, err = clientFactory.DiscoveryClient() From b292ea24bb109aed2f09372cfe91f10518ffc305 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 11:03:09 +0200 Subject: [PATCH 1039/2268] chore(deps): Bump github.com/go-git/go-git/v5 from 5.6.1 to 5.7.0 (#502) Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.6.1 to 5.7.0. - [Release notes](https://github.com/go-git/go-git/releases) - [Commits](https://github.com/go-git/go-git/compare/v5.6.1...v5.7.0) --- updated-dependencies: - dependency-name: github.com/go-git/go-git/v5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 56 +++++++++++++++++++------------------------------------- 2 files changed, 24 insertions(+), 42 deletions(-) diff --git a/go.mod b/go.mod index 005915bda..0a686c573 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 github.com/aws/smithy-go v1.13.5 github.com/dimchansky/utfbom v1.1.1 - github.com/go-git/go-git/v5 v5.6.1 + github.com/go-git/go-git/v5 v5.7.0 github.com/go-logr/logr v1.2.4 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 @@ -95,7 +95,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect @@ -111,7 +111,7 @@ require ( github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/cloudflare/circl v1.3.0 // indirect + github.com/cloudflare/circl v1.3.3 // indirect github.com/containerd/containerd v1.7.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect @@ -131,7 +131,7 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-git/gcfg v1.5.0 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -205,7 +205,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect - github.com/skeema/knownhosts v1.1.0 // indirect + github.com/skeema/knownhosts v1.1.1 // 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 diff --git a/go.sum b/go.sum index 55fc6f5d6..6afa7e683 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,8 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5 github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= +github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -105,7 +105,6 @@ github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -113,7 +112,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= @@ -180,8 +178,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P 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/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= -github.com/cloudflare/circl v1.3.0 h1:Anq00jxDtoyX3+aCaYUZ0vXC5r4k4epberfWGDXV1zE= -github.com/cloudflare/circl v1.3.0/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= 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= @@ -233,6 +231,7 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -268,18 +267,15 @@ github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= -github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= -github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= -github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= -github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= +github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= +github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8= 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= @@ -503,7 +499,6 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -625,7 +620,6 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -739,7 +733,6 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -752,8 +745,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= -github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= +github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= +github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -871,7 +864,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 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/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -886,13 +878,10 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 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= @@ -931,7 +920,7 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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= @@ -978,12 +967,10 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 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= @@ -1012,6 +999,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/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-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1077,13 +1065,10 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/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-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1094,11 +1079,10 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 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= @@ -1113,6 +1097,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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= @@ -1177,7 +1162,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 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= @@ -1307,7 +1292,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= @@ -1325,7 +1309,6 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.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.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= @@ -1372,7 +1355,6 @@ k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt oras.land/oras-go v1.2.3 h1:v8PJl+gEAntI1pJ/LCrDgsuk+1PKVavVEPsYIHFE5uY= oras.land/oras-go v1.2.3/go.mod h1:M/uaPdYklze0Vf3AakfarnpoEckvw0ESbRdN8Z1vdJg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= From dfec936ddffec51318bf1fb125261608df1b78d6 Mon Sep 17 00:00:00 2001 From: Marcel Frehe <43266827+devMarci@users.noreply.github.com> Date: Thu, 25 May 2023 13:06:43 +0200 Subject: [PATCH 1040/2268] feat: Add ability to include custom message in barriers (#506) * feat: Add ability to include custom message in barriers * docs: Update documentation for optional custom messages in barriers * chore: remove debug code * refactor: Remove unnecessary property * refactor: Handle non-provided message property --- docs/reference/deployments/deployment-yml.md | 18 ++++++++++++++++++ pkg/deployment/utils/apply_utils.go | 6 +++++- pkg/types/deployment.go | 1 + pkg/types/zz_generated.deepcopy.go | 5 +++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/reference/deployments/deployment-yml.md b/docs/reference/deployments/deployment-yml.md index 45d144ad3..8689ada5a 100644 --- a/docs/reference/deployments/deployment-yml.md +++ b/docs/reference/deployments/deployment-yml.md @@ -128,6 +128,24 @@ deployments: - path: kustomizeDeployment3 ``` +To create a barrier with a custom message, include the message parameter when creating the barrier. The message parameter accepts a string value that represents the custom message. + +Example: +```yaml +deployments: +- path: kustomizeDeployment1 +- path: kustomizeDeployment2 +- include: subDeployment1 +- barrier: true + message: "Waiting for subDeployment1 to be finished" +# At this point, it's ensured that kustomizeDeployment1, kustomizeDeployment2 and all sub-deployments from +# subDeployment1 are fully deployed. +- path: kustomizeDeployment3 +``` +If no custom message is provided, the barrier will be created without a specific message, and the default behavior will be applied. + +When viewing the `kluctl deploy` status, the custom message, if provided, will be displayed along with default barrier information. + ### deleteObjects Causes kluctl to delete matching objects, specified by a list of group/kind/name/namespace dictionaries. The order/parallelization of deletion is identical to the order and parallelization of normal deployment items, diff --git a/pkg/deployment/utils/apply_utils.go b/pkg/deployment/utils/apply_utils.go index a4d928ba9..f0e323d00 100644 --- a/pkg/deployment/utils/apply_utils.go +++ b/pkg/deployment/utils/apply_utils.go @@ -699,7 +699,11 @@ func (a *ApplyDeploymentsUtil) ApplyDeployments(deployments []*deployment.Deploy barrier := d.Config.Barrier || d.Barrier if barrier { - sctx := status.StartWithOptions(a.ctx, status.WithStatus("Waiting on barrier..."), status.WithTotal(1)) + barrierMessage := "Waiting on barrier..." + if d.Config.Message != nil { + barrierMessage = fmt.Sprintf("Waiting on barrier: %s", *d.Config.Message) + } + sctx := status.StartWithOptions(a.ctx, status.WithStatus(barrierMessage), status.WithTotal(1)) wg.Wait() sctx.UpdateAndInfoFallback(fmt.Sprintf("Finished waiting")) sctx.Success() diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index 22517edab..a4f8ee8ee 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -13,6 +13,7 @@ type DeploymentItemConfig struct { Git *GitProject `json:"git,omitempty"` Tags []string `json:"tags,omitempty"` Barrier bool `json:"barrier,omitempty"` + Message *string `json:"message,omitempty"` WaitReadiness bool `json:"waitReadiness,omitempty"` Vars []*VarsSource `json:"vars,omitempty"` SkipDeleteIfTags bool `json:"skipDeleteIfTags,omitempty"` diff --git a/pkg/types/zz_generated.deepcopy.go b/pkg/types/zz_generated.deepcopy.go index 59a658fca..4f8c638e0 100644 --- a/pkg/types/zz_generated.deepcopy.go +++ b/pkg/types/zz_generated.deepcopy.go @@ -94,6 +94,11 @@ func (in *DeploymentItemConfig) DeepCopyInto(out *DeploymentItemConfig) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Message != nil { + in, out := &in.Message, &out.Message + *out = new(string) + **out = **in + } if in.Vars != nil { in, out := &in.Vars, &out.Vars *out = make([]*VarsSource, len(*in)) From 138ae848b8d88ee195b0a6c77c7b8be61d6fb703 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 25 May 2023 13:09:07 +0200 Subject: [PATCH 1041/2268] feat: Implement migration checks for legacy flux-kluctl-controller (#507) * refactor: Get rid of ClientSet * feat: Implement migration checks for legacy flux-kluctl-controller --- cmd/kluctl/commands/cmd_controller.go | 8 --- config/rbac/role.yaml | 14 +++++ pkg/controllers/kluctl_project.go | 7 +-- .../kluctldeployment_controller.go | 53 ++++++++++++++++++- pkg/utils/uo/nested_fields.go | 20 +++++++ 5 files changed, 89 insertions(+), 13 deletions(-) diff --git a/cmd/kluctl/commands/cmd_controller.go b/cmd/kluctl/commands/cmd_controller.go index c0baaa66a..241169b7f 100644 --- a/cmd/kluctl/commands/cmd_controller.go +++ b/cmd/kluctl/commands/cmd_controller.go @@ -12,7 +12,6 @@ import ( log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/client-go/kubernetes" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -90,12 +89,6 @@ func (cmd *controllerCmd) Run(ctx context.Context) error { restConfig.Burst = -1 } - clientSet, err := kubernetes.NewForConfig(restConfig) - if err != nil { - setupLog.Error(err, "unable to start manager") - os.Exit(1) - } - mgr, err := ctrl.NewManager(restConfig, ctrl.Options{ Scheme: cmd.scheme, MetricsBindAddress: cmd.MetricsBindAddress, @@ -130,7 +123,6 @@ func (cmd *controllerCmd) Run(ctx context.Context) error { DryRun: cmd.DryRun, RestConfig: restConfig, Client: mgr.GetClient(), - ClientSet: clientSet, Scheme: mgr.GetScheme(), EventRecorder: eventRecorder, MetricsRecorder: metricsRecorder, diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e043f192e..14fffd85c 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -21,6 +21,20 @@ rules: verbs: - create - patch +- apiGroups: + - flux.kluctl.io + resources: + - kluctldeployments + verbs: + - get + - list + - watch +- apiGroups: + - flux.kluctl.io + resources: + - kluctldeployments/status + verbs: + - get - apiGroups: - gitops.kluctl.io resources: diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index d6ca5372e..7af6cda94 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -14,9 +14,9 @@ import ( "github.com/prometheus/client_golang/prometheus" "helm.sh/helm/v3/pkg/repo" apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "os" "path/filepath" + "sigs.k8s.io/controller-runtime/pkg/client" "strings" "time" @@ -545,12 +545,13 @@ func (pp *preparedProject) addServiceAccountBasedKeyServers(ctx context.Context, if name == "" { return nil } - sa, err := pp.r.ClientSet.CoreV1().ServiceAccounts(pp.obj.Namespace).Get(ctx, name, metav1.GetOptions{}) + var sa corev1.ServiceAccount + err := pp.r.Client.Get(ctx, client.ObjectKey{Name: name, Namespace: pp.obj.Namespace}, &sa) if err != nil { return fmt.Errorf("failed to retrieve service account %s: %w", name, err) } - ks, err := sops.BuildSopsKeyServerFromServiceAccount(ctx, pp.r.Client, sa) + ks, err := sops.BuildSopsKeyServerFromServiceAccount(ctx, pp.r.Client, &sa) if err != nil { return err } diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 86dd5e118..2e6532208 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -2,6 +2,7 @@ package controllers import ( "context" + errors2 "errors" "fmt" "github.com/hashicorp/go-retryablehttp" kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" @@ -13,9 +14,13 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "k8s.io/apimachinery/pkg/api/errors" + meta2 "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" "k8s.io/client-go/rest" kuberecorder "k8s.io/client-go/tools/record" "k8s.io/client-go/tools/reference" @@ -29,7 +34,6 @@ import ( type KluctlDeploymentReconciler struct { client.Client RestConfig *rest.Config - ClientSet *kubernetes.Clientset httpClient *retryablehttp.Client requeueDependency time.Duration Scheme *runtime.Scheme @@ -52,6 +56,8 @@ type KluctlDeploymentReconcilerOpts struct { // +kubebuilder:rbac:groups=gitops.kluctl.io,resources=kluctldeployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=gitops.kluctl.io,resources=kluctldeployments/status,verbs=get;update;patch // +kubebuilder:rbac:groups=gitops.kluctl.io,resources=kluctldeployments/finalizers,verbs=get;create;update;patch;delete +// +kubebuilder:rbac:groups=flux.kluctl.io,resources=kluctldeployments,verbs=get;list;watch +// +kubebuilder:rbac:groups=flux.kluctl.io,resources=kluctldeployments/status,verbs=get // +kubebuilder:rbac:groups="",resources=configmaps;secrets;serviceaccounts,verbs=get;list;watch // +kubebuilder:rbac:groups="",resources=events,verbs=create;patch @@ -94,6 +100,10 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req return r.finalize(ctx, obj) } + if r.checkLegacyKluctlDeployment(ctx, obj) { + return ctrl.Result{}, nil + } + // Return early if the KluctlDeployment is suspended. if obj.Spec.Suspend { log.Info("Reconciliation is suspended for this object") @@ -506,6 +516,45 @@ func (r *KluctlDeploymentReconciler) doFinalize(ctx context.Context, obj *kluctl _, _ = pt.kluctlDelete(ctx, obj.Status.TargetKey.Discriminator) } +// checkLegacyKluctlDeployment checks if a legacy KluctlDeployment from the old flux.kluctl.io group is present. If yes +// we must ensure that this object is served by a recent legacy controller version which understands that it should stop +// reconciliation in case the new gitops.kluctl.io object is present +func (r *KluctlDeploymentReconciler) checkLegacyKluctlDeployment(ctx context.Context, obj *kluctlv1.KluctlDeployment) bool { + log := ctrl.LoggerFrom(ctx) + + obj2 := uo.New() + obj2.SetK8sGVK(schema.GroupVersionKind{ + Group: "flux.kluctl.io", + Version: "v1alpha1", + Kind: "KluctlDeployment", + }) + err := r.Get(ctx, client.ObjectKeyFromObject(obj), obj2.ToUnstructured()) + if err != nil { + err = errors2.Unwrap(err) + if meta2.IsNoMatchError(err) || errors.IsNotFound(err) || discovery.IsGroupDiscoveryFailedError(err) { + // legacy object not present, we're safe to continue + return false + } + log.Error(err, "Failed to retrieve legacy KluctlDeployment. Skipping reconciliation.") + // some unexpected error...we should be on the safe side and bail out reconciliation + return true + } + + readyForMigration, _, err := obj2.GetNestedBool("") + if err != nil { + // some unexpected error...we should be on the safe side and bail out reconciliation + log.Error(err, "Failed to retrieve readyForMigration value. Skipping reconciliation.") + return true + } + if !readyForMigration { + log.V(1).Info("legacy KluctlDeployment does not have the readyForMigration status set. Skipping reconciliation. " + + "Please ensure that you have upgraded to the latest version of the legacy flux-kluctl-controller and that is is still running.") + return true + } + + return false +} + func (r *KluctlDeploymentReconciler) exportDeploymentObjectToProm(obj *kluctlv1.KluctlDeployment) { pruneEnabled := 0.0 deleteEnabled := 0.0 diff --git a/pkg/utils/uo/nested_fields.go b/pkg/utils/uo/nested_fields.go index a30a992a5..da3c05758 100644 --- a/pkg/utils/uo/nested_fields.go +++ b/pkg/utils/uo/nested_fields.go @@ -270,3 +270,23 @@ func (uo *UnstructuredObject) SetNestedFieldDefault(defaultValue interface{}, ke } return uo.SetNestedField(defaultValue, keys...) } + +func (uo *UnstructuredObject) GetNestedBool(keys ...interface{}) (bool, bool, error) { + v, found, err := uo.GetNestedField(keys...) + if err != nil { + return false, false, err + } + if !found { + return false, true, nil + } + if x, ok := v.(bool); ok { + return x, true, nil + } else if x, ok := v.(*bool); ok { + if x == nil { + return false, true, nil + } + return *x, true, nil + } else { + return false, false, fmt.Errorf("field is not a bool") + } +} From 81aae0ef249582c4aedc60f2c4f516381434993b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 25 May 2023 14:31:05 +0200 Subject: [PATCH 1042/2268] fix: Cleanup config dir to use fixed name --- Makefile | 2 +- .../patches/webhook_in_kluctldeployments.yaml | 2 +- config/default/kustomization.yaml | 132 ------------------ config/default/manager_config_patch.yaml | 10 -- config/manager/manager.yaml | 24 ++-- config/prometheus/kustomization.yaml | 2 - config/prometheus/monitor.yaml | 26 ---- config/rbac/kluctldeployment_editor_role.yaml | 2 +- config/rbac/kluctldeployment_viewer_role.yaml | 2 +- config/rbac/leader_election_role.yaml | 5 +- config/rbac/leader_election_role_binding.yaml | 11 +- config/rbac/role.yaml | 2 +- config/rbac/role_binding.yaml | 12 +- config/rbac/service_account.yaml | 8 +- 14 files changed, 36 insertions(+), 204 deletions(-) delete mode 100644 config/default/manager_config_patch.yaml delete mode 100644 config/prometheus/kustomization.yaml delete mode 100644 config/prometheus/monitor.yaml diff --git a/Makefile b/Makefile index 46c182d03..bb1058f15 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(CONTROLLER_GEN) rbac:roleName=kluctl-controller-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. diff --git a/config/crd/patches/webhook_in_kluctldeployments.yaml b/config/crd/patches/webhook_in_kluctldeployments.yaml index fa1689a1d..07d04c73e 100644 --- a/config/crd/patches/webhook_in_kluctldeployments.yaml +++ b/config/crd/patches/webhook_in_kluctldeployments.yaml @@ -9,7 +9,7 @@ spec: webhook: clientConfig: service: - namespace: system + namespace: kluctl-system name: webhook-service path: /convert conversionReviewVersions: diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index ce073d33f..98deb64cd 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -1,137 +1,5 @@ -# Adds namespace to all resources. -namespace: controller-system - -# Value of this field is prepended to the -# names of all resources, e.g. a deployment named -# "wordpress" becomes "alices-wordpress". -# Note that it should also match with the prefix (text before '-') of the namespace -# field above. -namePrefix: kluctl- - -# Labels to add to all resources and selectors. -#labels: -#- includeSelectors: true -# pairs: -# someName: someValue resources: - ../crd - ../rbac - ../manager -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- ../webhook -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager -# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. -#- ../prometheus - - -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- manager_webhook_patch.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -# Uncomment the following replacements to add the cert-manager CA injection annotations -#replacements: -# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.namespace # namespace of the certificate CR -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.name -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - source: # Add cert-manager annotation to the webhook Service -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.name # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - source: -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.namespace # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml deleted file mode 100644 index f6f589169..000000000 --- a/config/default/manager_config_patch.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 0eaa207d9..60bc2cbf5 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -8,33 +8,33 @@ metadata: app.kubernetes.io/component: manager app.kubernetes.io/created-by: controller app.kubernetes.io/part-of: controller - app.kubernetes.io/managed-by: kustomize - name: system + app.kubernetes.io/managed-by: kluctl + name: kluctl-system --- apiVersion: apps/v1 kind: Deployment metadata: - name: controller-manager - namespace: system + name: kluctl-controller + namespace: kluctl-system labels: - control-plane: controller-manager + control-plane: kluctl-controller app.kubernetes.io/name: deployment - app.kubernetes.io/instance: controller-manager + app.kubernetes.io/instance: kluctl-controller app.kubernetes.io/component: manager app.kubernetes.io/created-by: controller app.kubernetes.io/part-of: controller - app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/managed-by: kluctl spec: selector: matchLabels: - control-plane: controller-manager + control-plane: kluctl-controller replicas: 1 template: metadata: annotations: kubectl.kubernetes.io/default-container: manager labels: - control-plane: controller-manager + control-plane: kluctl-controller spec: # TODO(user): Uncomment the following code to configure the nodeAffinity expression # according to the platforms which are supported by your solution. @@ -71,9 +71,9 @@ spec: - controller args: - --leader-elect - image: controller:latest + image: ghcr.io/kluctl/kluctl:latest imagePullPolicy: IfNotPresent - name: manager + name: controller securityContext: allowPrivilegeEscalation: false capabilities: @@ -100,5 +100,5 @@ spec: requests: cpu: 10m memory: 64Mi - serviceAccountName: controller-manager + serviceAccountName: kluctl-controller terminationGracePeriodSeconds: 10 diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml deleted file mode 100644 index ed137168a..000000000 --- a/config/prometheus/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: -- monitor.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml deleted file mode 100644 index 62e8a7ccb..000000000 --- a/config/prometheus/monitor.yaml +++ /dev/null @@ -1,26 +0,0 @@ - -# Prometheus Monitor Service (Metrics) -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - labels: - control-plane: controller-manager - app.kubernetes.io/name: servicemonitor - app.kubernetes.io/instance: controller-manager-metrics-monitor - app.kubernetes.io/component: metrics - app.kubernetes.io/created-by: controller - app.kubernetes.io/part-of: controller - app.kubernetes.io/managed-by: kustomize - name: controller-manager-metrics-monitor - namespace: system -spec: - endpoints: - - path: /metrics - port: https - scheme: https - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token - tlsConfig: - insecureSkipVerify: true - selector: - matchLabels: - control-plane: controller-manager diff --git a/config/rbac/kluctldeployment_editor_role.yaml b/config/rbac/kluctldeployment_editor_role.yaml index 8304fdd0c..bdd62da70 100644 --- a/config/rbac/kluctldeployment_editor_role.yaml +++ b/config/rbac/kluctldeployment_editor_role.yaml @@ -8,7 +8,7 @@ metadata: app.kubernetes.io/component: rbac app.kubernetes.io/created-by: controller app.kubernetes.io/part-of: controller - app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/managed-by: kluctl name: kluctldeployment-editor-role rules: - apiGroups: diff --git a/config/rbac/kluctldeployment_viewer_role.yaml b/config/rbac/kluctldeployment_viewer_role.yaml index 50685c666..bee8b81ea 100644 --- a/config/rbac/kluctldeployment_viewer_role.yaml +++ b/config/rbac/kluctldeployment_viewer_role.yaml @@ -8,7 +8,7 @@ metadata: app.kubernetes.io/component: rbac app.kubernetes.io/created-by: controller app.kubernetes.io/part-of: controller - app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/managed-by: kluctl name: kluctldeployment-viewer-role rules: - apiGroups: diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml index 9bf1a8ac0..4d4c486d0 100644 --- a/config/rbac/leader_election_role.yaml +++ b/config/rbac/leader_election_role.yaml @@ -8,8 +8,9 @@ metadata: app.kubernetes.io/component: rbac app.kubernetes.io/created-by: controller app.kubernetes.io/part-of: controller - app.kubernetes.io/managed-by: kustomize - name: leader-election-role + app.kubernetes.io/managed-by: kluctl + name: kluctl-controller-leader-election-role + namespace: kluctl-system rules: - apiGroups: - "" diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml index d8cfaffd6..1ddbed5d3 100644 --- a/config/rbac/leader_election_role_binding.yaml +++ b/config/rbac/leader_election_role_binding.yaml @@ -7,13 +7,14 @@ metadata: app.kubernetes.io/component: rbac app.kubernetes.io/created-by: controller app.kubernetes.io/part-of: controller - app.kubernetes.io/managed-by: kustomize - name: leader-election-rolebinding + app.kubernetes.io/managed-by: kluctl + name: kluctl-controller-leader-election-rolebinding + namespace: kluctl-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: leader-election-role + name: kluctl-controller-leader-election-role subjects: - kind: ServiceAccount - name: controller-manager - namespace: system + name: kluctl-controller + namespace: kluctl-system diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 14fffd85c..5f7215c78 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: manager-role + name: kluctl-controller-role rules: - apiGroups: - "" diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 5c650c9a8..7381f094e 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -3,17 +3,17 @@ kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/instance: manager-rolebinding + app.kubernetes.io/instance: kluctl-controller-rolebinding app.kubernetes.io/component: rbac app.kubernetes.io/created-by: controller app.kubernetes.io/part-of: controller - app.kubernetes.io/managed-by: kustomize - name: manager-rolebinding + app.kubernetes.io/managed-by: kluctl + name: kluctl-controller-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: manager-role + name: kluctl-controller-role subjects: - kind: ServiceAccount - name: controller-manager - namespace: system + name: kluctl-controller + namespace: kluctl-system diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml index a0e977167..b5f5dd8fc 100644 --- a/config/rbac/service_account.yaml +++ b/config/rbac/service_account.yaml @@ -3,10 +3,10 @@ kind: ServiceAccount metadata: labels: app.kubernetes.io/name: serviceaccount - app.kubernetes.io/instance: controller-manager-sa + app.kubernetes.io/instance: kluctl-controller-sa app.kubernetes.io/component: rbac app.kubernetes.io/created-by: controller app.kubernetes.io/part-of: controller - app.kubernetes.io/managed-by: kustomize - name: controller-manager - namespace: system + app.kubernetes.io/managed-by: kluctl + name: kluctl-controller + namespace: kluctl-system From 1ac87b7a1af0f3db13c39a0fbfb1b23f01a1abc2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 25 May 2023 14:32:21 +0200 Subject: [PATCH 1043/2268] feat: Add embedded controller deployment --- .github/workflows/tests.yml | 9 + Makefile | 5 +- install/controller/.kluctl.yaml | 5 + .../controller/controller-version-patch.yaml | 0 install/controller/controller/crd.yaml | 1168 +++++++++++++++++ .../controller/controller/kustomization.yaml | 13 + install/controller/controller/manager.yaml | 75 ++ install/controller/controller/rbac.yaml | 165 +++ install/controller/deployment.yaml | 3 + install/controller/embed.go | 6 + 10 files changed, 1448 insertions(+), 1 deletion(-) create mode 100644 install/controller/.kluctl.yaml create mode 100644 install/controller/controller/controller-version-patch.yaml create mode 100644 install/controller/controller/crd.yaml create mode 100644 install/controller/controller/kustomization.yaml create mode 100644 install/controller/controller/manager.yaml create mode 100644 install/controller/controller/rbac.yaml create mode 100644 install/controller/deployment.yaml create mode 100644 install/controller/embed.go diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8559911f4..fd0338d69 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -48,6 +48,15 @@ jobs: git diff exit 1 fi + - name: Verify generated manifests are up-to-date + run: | + make manifests + if [ ! -z "$(git status --porcelain)" ]; then + echo "make manifests must be invoked and the result committed" + git status + git diff + exit 1 + fi tests: strategy: diff --git a/Makefile b/Makefile index bb1058f15..c8d2eb5a3 100644 --- a/Makefile +++ b/Makefile @@ -58,8 +58,11 @@ help: ## Display this help. ##@ Development .PHONY: manifests -manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. +manifests: controller-gen kustomize ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. $(CONTROLLER_GEN) rbac:roleName=kluctl-controller-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(KUSTOMIZE) build config/crd > install/controller/controller/crd.yaml + $(KUSTOMIZE) build config/rbac > install/controller/controller/rbac.yaml + $(KUSTOMIZE) build config/manager > install/controller/controller/manager.yaml .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. diff --git a/install/controller/.kluctl.yaml b/install/controller/.kluctl.yaml new file mode 100644 index 000000000..7f7283c68 --- /dev/null +++ b/install/controller/.kluctl.yaml @@ -0,0 +1,5 @@ +discriminator: kluctl.io-controller + +args: + - name: controller_version + default: v0.0.0 \ No newline at end of file diff --git a/install/controller/controller/controller-version-patch.yaml b/install/controller/controller/controller-version-patch.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/install/controller/controller/crd.yaml b/install/controller/controller/crd.yaml new file mode 100644 index 000000000..11208546d --- /dev/null +++ b/install/controller/controller/crd.yaml @@ -0,0 +1,1168 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + name: kluctldeployments.gitops.kluctl.io +spec: + group: gitops.kluctl.io + names: + kind: KluctlDeployment + listKind: KluctlDeploymentList + plural: kluctldeployments + singular: kluctldeployment + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.dryRun + name: DryRun + type: boolean + - jsonPath: .status.lastDeployResult.command.endTime + name: Deployed + type: date + - jsonPath: .status.lastPruneResult.command.endTime + name: Pruned + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: KluctlDeployment is the Schema for the kluctldeployments API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + abortOnError: + default: false + description: ForceReplaceOnError instructs kluctl to abort deployments + immediately when something fails. Equivalent to using '--abort-on-error' + when calling kluctl. + type: boolean + args: + description: Args specifies dynamic target args. + type: object + x-kubernetes-preserve-unknown-fields: true + context: + description: If specified, overrides the context to be used. This + will effectively make kluctl ignore the context specified in the + target. + type: string + decryption: + description: Decrypt Kubernetes secrets before applying them on the + cluster. + properties: + provider: + description: Provider is the name of the decryption engine. + enum: + - sops + type: string + secretRef: + description: The secret name containing the private OpenPGP keys + used for decryption. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccount: + description: ServiceAccount specifies the service account used + to authenticate against cloud providers. This is currently only + usable for AWS KMS keys. The specified service account will + be used to authenticate to AWS by signing a token in an IRSA + compliant way. + type: string + required: + - provider + type: object + delete: + default: false + description: Delete enables deletion of the specified target when + the KluctlDeployment object gets deleted. + type: boolean + deployInterval: + description: DeployInterval specifies the interval at which to deploy + the KluctlDeployment, even in cases the rendered result does not + change. + pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ + type: string + deployMode: + default: full-deploy + description: DeployMode specifies what deploy mode should be used. + The options 'full-deploy' and 'poke-images' are supported. With + the 'poke-images' option, only images are patched into the target + without performing a full deployment. + enum: + - full-deploy + - poke-images + type: string + dryRun: + default: false + description: DryRun instructs kluctl to run everything in dry-run + mode. Equivalent to using '--dry-run' when calling kluctl. + type: boolean + excludeDeploymentDirs: + description: ExcludeDeploymentDirs instructs kluctl to exclude deployments + with the given dir. Equivalent to using '--exclude-deployment-dir' + when calling kluctl. + items: + type: string + type: array + excludeTags: + description: ExcludeTags instructs kluctl to exclude deployments with + given tags. Equivalent to using '--exclude-tag' when calling kluctl. + items: + type: string + type: array + forceApply: + default: false + description: ForceApply instructs kluctl to force-apply in case of + SSA conflicts. Equivalent to using '--force-apply' when calling + kluctl. + type: boolean + forceReplaceOnError: + default: false + description: ForceReplaceOnError instructs kluctl to force-replace + resources in case a normal replace fails. Equivalent to using '--force-replace-on-error' + when calling kluctl. + type: boolean + helmCredentials: + description: HelmCredentials is a list of Helm credentials used when + non pre-pulled Helm Charts are used inside a Kluctl deployment. + items: + properties: + secretRef: + description: 'SecretRef holds the name of a secret that contains + the Helm credentials. The secret must either contain the fields + `credentialsId` which refers to the credentialsId found in + https://kluctl.io/docs/kluctl/reference/deployments/helm/#private-chart-repositories + or an `url` used to match the credentials found in Kluctl + projects helm-chart.yaml files. The secret can either container + basic authentication credentials via `username` and `password` + or TLS authentication via `certFile` and `keyFile`. `caFile` + can be specified to override the CA to use while contacting + the repository. The secret can also contain `insecureSkipTlsVerify: + "true"`, which will disable TLS verification. `passCredentialsAll: + "true"` can be specified to make the controller pass credentials + to all requests, even if the hostname changes in-between.' + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: object + type: array + images: + description: Images contains a list of fixed image overrides. Equivalent + to using '--fixed-images-file' when calling kluctl. + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + includeDeploymentDirs: + description: IncludeDeploymentDirs instructs kluctl to only include + deployments with the given dir. Equivalent to using '--include-deployment-dir' + when calling kluctl. + items: + type: string + type: array + includeTags: + description: IncludeTags instructs kluctl to only include deployments + with given tags. Equivalent to using '--include-tag' when calling + kluctl. + items: + type: string + type: array + interval: + description: The interval at which to reconcile the KluctlDeployment. + Reconciliation means that the deployment is fully rendered and only + deployed when the result changes compared to the last deployment. + To override this behavior, set the DeployInterval value. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: The KubeConfig for deploying to the target cluster. Specifies + the kubeconfig to be used when invoking kluctl. Contexts in this + kubeconfig must match the context found in the kluctl target. As + an alternative, specify the context to be used via 'context' + properties: + secretRef: + description: SecretRef holds the name of a secret that contains + a key with the kubeconfig file as the value. If no key is set, + the key will default to 'value'. The secret must be in the same + namespace as the Kustomization. It is recommended that the kubeconfig + is self-contained, and the secret is regularly updated if credentials + such as a cloud-access-token expire. Cloud specific `cmd-path` + auth helpers will not function without adding binaries and credentials + to the Pod that is responsible for reconciling the KluctlDeployment. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific + default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + type: object + noWait: + default: false + description: NoWait instructs kluctl to not wait for any resources + to become ready, including hooks. Equivalent to using '--no-wait' + when calling kluctl. + type: boolean + prune: + default: false + description: Prune enables pruning after deploying. + type: boolean + replaceOnError: + default: false + description: ReplaceOnError instructs kluctl to replace resources + on error. Equivalent to using '--replace-on-error' when calling + kluctl. + type: boolean + retryInterval: + description: The interval at which to retry a previously failed reconciliation. + When not specified, the controller uses the Interval value to retry + failures. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + serviceAccountName: + description: The name of the Kubernetes service account to use while + deploying. If not specified, the default service account is used. + type: string + source: + description: Specifies the project source location + properties: + path: + description: Path specifies the sub-directory to be used as project + directory + type: string + ref: + description: Ref specifies the branch, tag or commit that should + be used. If omitted, the default branch of the repo is used. + properties: + branch: + description: Branch to filter for. Can also be a regex. + type: string + tag: + description: Branch to filter for. Can also be a regex. + type: string + type: object + secretRef: + description: SecretRef specifies the Secret containing authentication + credentials for the git repository. For HTTPS repositories the + Secret must contain 'username' and 'password' fields. For SSH + repositories the Secret must contain 'identity' and 'known_hosts' + fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + url: + description: Url specifies the Git url where the project source + is located + type: string + required: + - url + type: object + suspend: + description: This flag tells the controller to suspend subsequent + kluctl executions, it does not apply to already started executions. + Defaults to false. + type: boolean + target: + description: Target specifies the kluctl target to deploy. If not + specified, an empty target is used that has no name and no context. + Use 'TargetName' and 'Context' to specify the name and context in + that case. + maxLength: 63 + minLength: 1 + type: string + targetNameOverride: + description: TargetNameOverride sets or overrides the target name. + This is especially useful when deployment without a target. + maxLength: 63 + minLength: 1 + type: string + timeout: + description: Timeout for all operations. Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + validate: + default: true + description: Validate enables validation after deploying + type: boolean + validateInterval: + description: ValidateInterval specifies the interval at which to validate + the KluctlDeployment. Validation is performed the same way as with + 'kluctl validate -t '. Defaults to the same value as specified + in Interval. Validate is also performed whenever a deployment is + performed, independent of the value of ValidateInterval + pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ + type: string + required: + - interval + - source + type: object + status: + description: KluctlDeploymentStatus defines the observed state of KluctlDeployment + properties: + LastHandledDeployAt: + type: string + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastDeployError: + type: string + lastDeployResult: + description: LastDeployResult is the result of the last deploy command + properties: + appliedHookObjects: + type: integer + appliedObjects: + type: integer + changedObjects: + type: integer + clusterInfo: + properties: + clusterId: + type: string + required: + - clusterId + type: object + commandInfo: + properties: + abortOnError: + type: boolean + args: + type: object + x-kubernetes-preserve-unknown-fields: true + command: + type: string + contextOverride: + type: string + dryRun: + type: boolean + endTime: + format: date-time + type: string + excludeDeploymentDirs: + items: + type: string + type: array + excludeTags: + items: + type: string + type: array + forceApply: + type: boolean + forceReplaceOnError: + type: boolean + images: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + includeDeploymentDirs: + items: + type: string + type: array + includeTags: + items: + type: string + type: array + initiator: + type: string + kluctlDeployment: + properties: + gitRef: + type: string + gitUrl: + type: string + name: + type: string + namespace: + type: string + required: + - gitRef + - gitUrl + - name + - namespace + type: object + noWait: + type: boolean + replaceOnError: + type: boolean + startTime: + format: date-time + type: string + target: + type: string + targetNameOverride: + type: string + required: + - endTime + - initiator + - startTime + type: object + deletedObjects: + type: integer + errors: + type: integer + gitInfo: + properties: + commit: + type: string + dirty: + type: boolean + ref: + type: string + subDir: + type: string + url: + type: string + required: + - commit + - dirty + - ref + - subDir + - url + type: object + id: + type: string + newObjects: + type: integer + orphanObjects: + type: integer + projectKey: + properties: + gitRepoKey: + type: string + subDir: + type: string + type: object + remoteObjects: + type: integer + renderedObjects: + type: integer + target: + properties: + args: + type: object + x-kubernetes-preserve-unknown-fields: true + context: + type: string + discriminator: + type: string + images: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + name: + type: string + sealingConfig: + properties: + args: + type: object + x-kubernetes-preserve-unknown-fields: true + certFile: + type: string + secretSets: + items: + type: string + type: array + type: object + required: + - name + type: object + targetKey: + properties: + clusterId: + type: string + discriminator: + type: string + targetName: + type: string + required: + - clusterId + type: object + totalChanges: + type: integer + warnings: + type: integer + required: + - appliedHookObjects + - appliedObjects + - changedObjects + - commandInfo + - deletedObjects + - errors + - id + - newObjects + - orphanObjects + - projectKey + - remoteObjects + - renderedObjects + - target + - targetKey + - totalChanges + - warnings + type: object + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + lastObjectsHash: + type: string + lastPruneError: + type: string + lastPruneResult: + description: LastDeployResult is the result of the last prune command + properties: + appliedHookObjects: + type: integer + appliedObjects: + type: integer + changedObjects: + type: integer + clusterInfo: + properties: + clusterId: + type: string + required: + - clusterId + type: object + commandInfo: + properties: + abortOnError: + type: boolean + args: + type: object + x-kubernetes-preserve-unknown-fields: true + command: + type: string + contextOverride: + type: string + dryRun: + type: boolean + endTime: + format: date-time + type: string + excludeDeploymentDirs: + items: + type: string + type: array + excludeTags: + items: + type: string + type: array + forceApply: + type: boolean + forceReplaceOnError: + type: boolean + images: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + includeDeploymentDirs: + items: + type: string + type: array + includeTags: + items: + type: string + type: array + initiator: + type: string + kluctlDeployment: + properties: + gitRef: + type: string + gitUrl: + type: string + name: + type: string + namespace: + type: string + required: + - gitRef + - gitUrl + - name + - namespace + type: object + noWait: + type: boolean + replaceOnError: + type: boolean + startTime: + format: date-time + type: string + target: + type: string + targetNameOverride: + type: string + required: + - endTime + - initiator + - startTime + type: object + deletedObjects: + type: integer + errors: + type: integer + gitInfo: + properties: + commit: + type: string + dirty: + type: boolean + ref: + type: string + subDir: + type: string + url: + type: string + required: + - commit + - dirty + - ref + - subDir + - url + type: object + id: + type: string + newObjects: + type: integer + orphanObjects: + type: integer + projectKey: + properties: + gitRepoKey: + type: string + subDir: + type: string + type: object + remoteObjects: + type: integer + renderedObjects: + type: integer + target: + properties: + args: + type: object + x-kubernetes-preserve-unknown-fields: true + context: + type: string + discriminator: + type: string + images: + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + resultImage: + type: string + required: + - image + - resultImage + type: object + type: array + name: + type: string + sealingConfig: + properties: + args: + type: object + x-kubernetes-preserve-unknown-fields: true + certFile: + type: string + secretSets: + items: + type: string + type: array + type: object + required: + - name + type: object + targetKey: + properties: + clusterId: + type: string + discriminator: + type: string + targetName: + type: string + required: + - clusterId + type: object + totalChanges: + type: integer + warnings: + type: integer + required: + - appliedHookObjects + - appliedObjects + - changedObjects + - commandInfo + - deletedObjects + - errors + - id + - newObjects + - orphanObjects + - projectKey + - remoteObjects + - renderedObjects + - target + - targetKey + - totalChanges + - warnings + type: object + lastValidateError: + type: string + lastValidateResult: + description: LastValidateResult is the result of the last validate + command + properties: + drift: + items: + properties: + changes: + items: + properties: + jsonPath: + type: string + newValue: + x-kubernetes-preserve-unknown-fields: true + oldValue: + x-kubernetes-preserve-unknown-fields: true + type: + type: string + unifiedDiff: + type: string + required: + - jsonPath + - type + type: object + type: array + ref: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + required: + - ref + type: object + type: array + endTime: + format: date-time + type: string + errors: + items: + properties: + message: + type: string + ref: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + required: + - message + - ref + type: object + type: array + id: + type: string + ready: + type: boolean + results: + items: + properties: + annotation: + type: string + message: + type: string + ref: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + required: + - annotation + - message + - ref + type: object + type: array + startTime: + format: date-time + type: string + warnings: + items: + properties: + message: + type: string + ref: + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - kind + - name + type: object + required: + - message + - ref + type: object + type: array + required: + - endTime + - id + - ready + - startTime + type: object + observedCommit: + description: ObservedCommit is the last commit observed + type: string + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + projectKey: + properties: + gitRepoKey: + type: string + subDir: + type: string + type: object + targetKey: + properties: + clusterId: + type: string + discriminator: + type: string + targetName: + type: string + required: + - clusterId + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml new file mode 100644 index 000000000..4c41784cc --- /dev/null +++ b/install/controller/controller/kustomization.yaml @@ -0,0 +1,13 @@ +resources: + - crd.yaml + - manager.yaml + - rbac.yaml + +patches: + - target: + kind: Deployment + name: kluctl-controller + patch: |- + - op: add + path: /spec/template/spec/containers/0/image + value: ghcr.io/kluctl/kluctl:{{ args.controller_version }} diff --git a/install/controller/controller/manager.yaml b/install/controller/controller/manager.yaml new file mode 100644 index 000000000..02f78bc76 --- /dev/null +++ b/install/controller/controller/manager.yaml @@ -0,0 +1,75 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: controller + app.kubernetes.io/instance: system + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: namespace + app.kubernetes.io/part-of: controller + control-plane: controller-manager + name: kluctl-system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: controller + app.kubernetes.io/instance: kluctl-controller + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: deployment + app.kubernetes.io/part-of: controller + control-plane: kluctl-controller + name: kluctl-controller + namespace: kluctl-system +spec: + replicas: 1 + selector: + matchLabels: + control-plane: kluctl-controller + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + control-plane: kluctl-controller + spec: + containers: + - args: + - --leader-elect + command: + - kluctl + - controller + image: ghcr.io/kluctl/kluctl:latest + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: controller + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + securityContext: + runAsNonRoot: true + serviceAccountName: kluctl-controller + terminationGracePeriodSeconds: 10 diff --git a/install/controller/controller/rbac.yaml b/install/controller/controller/rbac.yaml new file mode 100644 index 000000000..1845b4548 --- /dev/null +++ b/install/controller/controller/rbac.yaml @@ -0,0 +1,165 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/instance: kluctl-controller-sa + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: serviceaccount + app.kubernetes.io/part-of: controller + name: kluctl-controller + namespace: kluctl-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/instance: leader-election-role + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: role + app.kubernetes.io/part-of: controller + name: kluctl-controller-leader-election-role + namespace: kluctl-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kluctl-controller-role +rules: +- apiGroups: + - "" + resources: + - configmaps + - secrets + - serviceaccounts + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - flux.kluctl.io + resources: + - kluctldeployments + verbs: + - get + - list + - watch +- apiGroups: + - flux.kluctl.io + resources: + - kluctldeployments/status + verbs: + - get +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments/status + verbs: + - get + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/instance: leader-election-rolebinding + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: rolebinding + app.kubernetes.io/part-of: controller + name: kluctl-controller-leader-election-rolebinding + namespace: kluctl-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: kluctl-controller-leader-election-role +subjects: +- kind: ServiceAccount + name: kluctl-controller + namespace: kluctl-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/instance: kluctl-controller-rolebinding + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/part-of: controller + name: kluctl-controller-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kluctl-controller-role +subjects: +- kind: ServiceAccount + name: kluctl-controller + namespace: kluctl-system diff --git a/install/controller/deployment.yaml b/install/controller/deployment.yaml new file mode 100644 index 000000000..1ab7a6d3e --- /dev/null +++ b/install/controller/deployment.yaml @@ -0,0 +1,3 @@ + +deployments: + - path: controller diff --git a/install/controller/embed.go b/install/controller/embed.go new file mode 100644 index 000000000..3bf3cd004 --- /dev/null +++ b/install/controller/embed.go @@ -0,0 +1,6 @@ +package controller + +import "embed" + +//go:embed all:* +var Config embed.FS From 201449734ad6189499e3a2e1a5fbf887a3840aa8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 25 May 2023 14:33:51 +0200 Subject: [PATCH 1044/2268] chore: Add prepeare-release.sh script --- hack/prepare-release.sh | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100755 hack/prepare-release.sh diff --git a/hack/prepare-release.sh b/hack/prepare-release.sh new file mode 100755 index 000000000..609c006d5 --- /dev/null +++ b/hack/prepare-release.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +set -e + +VERSION=$1 + +VERSION_REGEX='v([0-9]*)\.([0-9]*)\.([0-9]*)' +VERSION_REGEX_SED='v\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)' + +if [ ! -z "$(git status --porcelain)" ]; then + echo "working directory is dirty!" + exit 1 +fi + +if [ -z "$VERSION" ]; then + echo "No version specified, using 'git sv next-version'" + VERSION=v$(git sv next-version) +fi + +if [[ ! ($VERSION =~ $VERSION_REGEX) ]]; then + echo "version is invalid" + exit 1 +fi + +echo VERSION=$VERSION + +FILES="install/controller/.kluctl.yaml" + +for f in $FILES; do + cat $f | sed "s/$VERSION_REGEX_SED/$VERSION/g" > $f.tmp + mv $f.tmp $f + + git add $f +done + +if [ -z "$(git status --porcelain)" ]; then + echo "nothing has changed!" + exit 1 +fi + +echo "committing" +git commit -o -m "build: Preparing release $VERSION" -- $FILES + +echo "tagging" +git tag -f $VERSION From 47bf5a0b4bcbda41536f8524edfc7e26c2ab32b1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 25 May 2023 14:37:00 +0200 Subject: [PATCH 1045/2268] ci: Increment minor version instead of patch version when building snapshots --- .goreleaser.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 8d657a092..ba0d196f9 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -52,7 +52,7 @@ sboms: checksum: name_template: '{{ .ProjectName }}_v{{ .Version }}_checksums.txt' snapshot: - name_template: "{{ incpatch .Version }}-next" + name_template: "{{ incminor .Version }}-next" changelog: sort: asc filters: From 29e4b21cef922c971c536799610b5b7c08a7fab0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 25 May 2023 14:43:50 +0200 Subject: [PATCH 1046/2268] feat: Move "controller" sub-command to "controller run" --- cmd/kluctl/commands/cmd_controller.go | 217 +------------------- cmd/kluctl/commands/cmd_controller_run.go | 220 +++++++++++++++++++++ cmd/kluctl/commands/cobra_utils.go | 5 +- cmd/kluctl/commands/root.go | 2 +- config/manager/manager.yaml | 1 + e2e/gitops_test.go | 1 + install/controller/controller/manager.yaml | 1 + 7 files changed, 229 insertions(+), 218 deletions(-) create mode 100644 cmd/kluctl/commands/cmd_controller_run.go diff --git a/cmd/kluctl/commands/cmd_controller.go b/cmd/kluctl/commands/cmd_controller.go index 241169b7f..508c75220 100644 --- a/cmd/kluctl/commands/cmd_controller.go +++ b/cmd/kluctl/commands/cmd_controller.go @@ -1,220 +1,5 @@ package commands -import ( - "context" - "fmt" - kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" - "github.com/kluctl/kluctl/v2/cmd/kluctl/args" - "github.com/kluctl/kluctl/v2/pkg/controllers" - ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" - "github.com/kluctl/kluctl/v2/pkg/results" - "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" - log "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "os" - "os/user" - "path/filepath" - "sigs.k8s.io/cli-utils/pkg/flowcontrol" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - crtlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" -) - -var ( - setupLog = ctrl.Log.WithName("setup") -) - -const controllerName = "kluctl-controller" - type controllerCmd struct { - scheme *runtime.Scheme - - Kubeconfig string `group:"controller" help:"Override the kubeconfig to use."` - Context string `group:"controller" help:"Override the context to use."` - - MetricsBindAddress string `group:"controller" help:"The address the metric endpoint binds to." default:":8080"` - HealthProbeBindAddress string `group:"controller" help:"The address the probe endpoint binds to." default:":8081"` - LeaderElect bool `group:"controller" help:"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager."` - - DefaultServiceAccount string `group:"controller" help:"Default service account used for impersonation."` - DryRun bool `group:"controller" help:"Run all deployments in dryRun=true mode."` - - args.CommandResultFlags -} - -func (cmd *controllerCmd) Help() string { - return `This command will run the Kluctl Controller. This is usually meant to be run inside a cluster and not from your local machine. -` -} - -func (cmd *controllerCmd) initScheme() { - cmd.scheme = runtime.NewScheme() - scheme := cmd.scheme - - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - - utilruntime.Must(kluctlv1.AddToScheme(scheme)) - - //+kubebuilder:scaffold:scheme -} - -func (cmd *controllerCmd) Run(ctx context.Context) error { - cmd.initScheme() - - metricsRecorder := metrics.NewRecorder() - crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...) - - opts := zap.Options{ - Development: true, - } - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) - - restConfig, err := cmd.loadConfig(cmd.Kubeconfig, cmd.Context) - if err != nil { - setupLog.Error(err, "unable to load kubeconfig") - os.Exit(1) - } - - enabled, err := flowcontrol.IsEnabled(context.Background(), restConfig) - if err == nil && enabled { - // A negative QPS and Burst indicates that the client should not have a rate limiter. - // Ref: https://github.com/kubernetes/kubernetes/blob/v1.24.0/staging/src/k8s.io/client-go/rest/config.go#L354-L364 - restConfig.QPS = -1 - restConfig.Burst = -1 - } - - mgr, err := ctrl.NewManager(restConfig, ctrl.Options{ - Scheme: cmd.scheme, - MetricsBindAddress: cmd.MetricsBindAddress, - Port: 9443, - HealthProbeBindAddress: cmd.HealthProbeBindAddress, - LeaderElection: cmd.LeaderElect, - LeaderElectionID: "5ab5d0f9.kluctl.io", - // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily - // when the Manager ends. This requires the binary to immediately end when the - // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly - // speeds up voluntary leader transitions as the new leader don't have to wait - // LeaseDuration time first. - // - // In the default scaffold provided, the program ends immediately after - // the manager stops, so would be fine to enable this option. However, - // if you are doing or is intended to do any operation such as perform cleanups - // after the manager stops then its usage might be unsafe. - // LeaderElectionReleaseOnCancel: true, - }) - if err != nil { - setupLog.Error(err, "unable to start manager") - os.Exit(1) - } - - eventRecorder := mgr.GetEventRecorderFor(controllerName) - - sshPool := &ssh_pool.SshPool{} - - r := controllers.KluctlDeploymentReconciler{ - ControllerName: controllerName, - DefaultServiceAccount: cmd.DefaultServiceAccount, - DryRun: cmd.DryRun, - RestConfig: restConfig, - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - EventRecorder: eventRecorder, - MetricsRecorder: metricsRecorder, - SshPool: sshPool, - } - - if cmd.WriteCommandResult { - resultStore, err := results.NewResultStoreSecrets(ctx, mgr.GetClient(), mgr.GetCache(), cmd.CommandResultNamespace, cmd.KeepCommandResultsCount) - if err != nil { - return err - } - r.ResultStore = resultStore - } - - if err = r.SetupWithManager(ctx, mgr, controllers.KluctlDeploymentReconcilerOpts{ - HTTPRetry: 9, - }); err != nil { - setupLog.Error(err, "unable to create controller", "controller", kluctlv1.KluctlDeploymentKind) - os.Exit(1) - } - - //+kubebuilder:scaffold:builder - - if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up health check") - os.Exit(1) - } - if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up ready check") - os.Exit(1) - } - - setupLog.Info("starting manager") - if err := mgr.Start(ctx); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) - } - - return nil -} - -// taken from clientcmd -func (cmd *controllerCmd) loadConfig(kubeconfig string, context string) (config *rest.Config, configErr error) { - // If a flag is specified with the config location, use that - if len(kubeconfig) > 0 { - return cmd.loadConfigWithContext("", &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, context) - } - - // If the recommended kubeconfig env variable is not specified, - // try the in-cluster config. - kubeconfigPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar) - if len(kubeconfigPath) == 0 { - c, err := rest.InClusterConfig() - if err == nil { - return c, nil - } - - defer func() { - if configErr != nil { - log.Error(err, "unable to load in-cluster config") - } - }() - } - - // If the recommended kubeconfig env variable is set, or there - // is no in-cluster config, try the default recommended locations. - // - // NOTE: For default config file locations, upstream only checks - // $HOME for the user's home directory, but we can also try - // os/user.HomeDir when $HOME is unset. - // - // TODO(jlanford): could this be done upstream? - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - if _, ok := os.LookupEnv("HOME"); !ok { - u, err := user.Current() - if err != nil { - return nil, fmt.Errorf("could not get current user: %w", err) - } - loadingRules.Precedence = append(loadingRules.Precedence, filepath.Join(u.HomeDir, clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName)) - } - - return cmd.loadConfigWithContext("", loadingRules, context) -} - -// taken from clientcmd -func (cmd *controllerCmd) loadConfigWithContext(apiServerURL string, loader clientcmd.ClientConfigLoader, context string) (*rest.Config, error) { - return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - loader, - &clientcmd.ConfigOverrides{ - ClusterInfo: clientcmdapi.Cluster{ - Server: apiServerURL, - }, - CurrentContext: context, - }).ClientConfig() + Run_ controllerRunCmd `cmd:"run" help:"Run the Kluctl controller"` } diff --git a/cmd/kluctl/commands/cmd_controller_run.go b/cmd/kluctl/commands/cmd_controller_run.go new file mode 100644 index 000000000..6d9cb40b2 --- /dev/null +++ b/cmd/kluctl/commands/cmd_controller_run.go @@ -0,0 +1,220 @@ +package commands + +import ( + "context" + "fmt" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/pkg/controllers" + ssh_pool "github.com/kluctl/kluctl/v2/pkg/git/ssh-pool" + "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" + log "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "os" + "os/user" + "path/filepath" + "sigs.k8s.io/cli-utils/pkg/flowcontrol" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + crtlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" +) + +var ( + setupLog = ctrl.Log.WithName("setup") +) + +const controllerName = "kluctl-controller" + +type controllerRunCmd struct { + scheme *runtime.Scheme + + Kubeconfig string `group:"controller" help:"Override the kubeconfig to use."` + Context string `group:"controller" help:"Override the context to use."` + + MetricsBindAddress string `group:"controller" help:"The address the metric endpoint binds to." default:":8080"` + HealthProbeBindAddress string `group:"controller" help:"The address the probe endpoint binds to." default:":8081"` + LeaderElect bool `group:"controller" help:"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager."` + + DefaultServiceAccount string `group:"controller" help:"Default service account used for impersonation."` + DryRun bool `group:"controller" help:"Run all deployments in dryRun=true mode."` + + args.CommandResultFlags +} + +func (cmd *controllerRunCmd) Help() string { + return `This command will run the Kluctl Controller. This is usually meant to be run inside a cluster and not from your local machine. +` +} + +func (cmd *controllerRunCmd) initScheme() { + cmd.scheme = runtime.NewScheme() + scheme := cmd.scheme + + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + utilruntime.Must(kluctlv1.AddToScheme(scheme)) + + //+kubebuilder:scaffold:scheme +} + +func (cmd *controllerRunCmd) Run(ctx context.Context) error { + cmd.initScheme() + + metricsRecorder := metrics.NewRecorder() + crtlmetrics.Registry.MustRegister(metricsRecorder.Collectors()...) + + opts := zap.Options{ + Development: true, + } + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + restConfig, err := cmd.loadConfig(cmd.Kubeconfig, cmd.Context) + if err != nil { + setupLog.Error(err, "unable to load kubeconfig") + os.Exit(1) + } + + enabled, err := flowcontrol.IsEnabled(context.Background(), restConfig) + if err == nil && enabled { + // A negative QPS and Burst indicates that the client should not have a rate limiter. + // Ref: https://github.com/kubernetes/kubernetes/blob/v1.24.0/staging/src/k8s.io/client-go/rest/config.go#L354-L364 + restConfig.QPS = -1 + restConfig.Burst = -1 + } + + mgr, err := ctrl.NewManager(restConfig, ctrl.Options{ + Scheme: cmd.scheme, + MetricsBindAddress: cmd.MetricsBindAddress, + Port: 9443, + HealthProbeBindAddress: cmd.HealthProbeBindAddress, + LeaderElection: cmd.LeaderElect, + LeaderElectionID: "5ab5d0f9.kluctl.io", + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + eventRecorder := mgr.GetEventRecorderFor(controllerName) + + sshPool := &ssh_pool.SshPool{} + + r := controllers.KluctlDeploymentReconciler{ + ControllerName: controllerName, + DefaultServiceAccount: cmd.DefaultServiceAccount, + DryRun: cmd.DryRun, + RestConfig: restConfig, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + EventRecorder: eventRecorder, + MetricsRecorder: metricsRecorder, + SshPool: sshPool, + } + + if cmd.WriteCommandResult { + resultStore, err := results.NewResultStoreSecrets(ctx, mgr.GetClient(), mgr.GetCache(), cmd.CommandResultNamespace, cmd.KeepCommandResultsCount) + if err != nil { + return err + } + r.ResultStore = resultStore + } + + if err = r.SetupWithManager(ctx, mgr, controllers.KluctlDeploymentReconcilerOpts{ + HTTPRetry: 9, + }); err != nil { + setupLog.Error(err, "unable to create controller", "controller", kluctlv1.KluctlDeploymentKind) + os.Exit(1) + } + + //+kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctx); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } + + return nil +} + +// taken from clientcmd +func (cmd *controllerRunCmd) loadConfig(kubeconfig string, context string) (config *rest.Config, configErr error) { + // If a flag is specified with the config location, use that + if len(kubeconfig) > 0 { + return cmd.loadConfigWithContext("", &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, context) + } + + // If the recommended kubeconfig env variable is not specified, + // try the in-cluster config. + kubeconfigPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar) + if len(kubeconfigPath) == 0 { + c, err := rest.InClusterConfig() + if err == nil { + return c, nil + } + + defer func() { + if configErr != nil { + log.Error(err, "unable to load in-cluster config") + } + }() + } + + // If the recommended kubeconfig env variable is set, or there + // is no in-cluster config, try the default recommended locations. + // + // NOTE: For default config file locations, upstream only checks + // $HOME for the user's home directory, but we can also try + // os/user.HomeDir when $HOME is unset. + // + // TODO(jlanford): could this be done upstream? + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + if _, ok := os.LookupEnv("HOME"); !ok { + u, err := user.Current() + if err != nil { + return nil, fmt.Errorf("could not get current user: %w", err) + } + loadingRules.Precedence = append(loadingRules.Precedence, filepath.Join(u.HomeDir, clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName)) + } + + return cmd.loadConfigWithContext("", loadingRules, context) +} + +// taken from clientcmd +func (cmd *controllerRunCmd) loadConfigWithContext(apiServerURL string, loader clientcmd.ClientConfigLoader, context string) (*rest.Config, error) { + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + loader, + &clientcmd.ConfigOverrides{ + ClusterInfo: clientcmdapi.Cluster{ + Server: apiServerURL, + }, + CurrentContext: context, + }).ClientConfig() +} diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index d4b4fb013..de630a332 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -106,7 +106,10 @@ func (c *rootCommand) buildCobraSubCommands(cg *commandAndGroups, cmdStruct inte v2 := v.Field(i).Addr().Interface() name := buildCobraName(f.Name) - if _, ok := f.Tag.Lookup("cmd"); ok { + if cmd, ok := f.Tag.Lookup("cmd"); ok { + if cmd != "" { + name = cmd + } // subcommand shortHelp := f.Tag.Get("help") longHelp := "" diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 74559d894..14b15c02e 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -66,7 +66,7 @@ type cli struct { Render renderCmd `cmd:"" help:"Renders all resources and configuration files"` Seal sealCmd `cmd:"" help:"Seal secrets based on target's sealingConfig"` Validate validateCmd `cmd:"" help:"Validates the already deployed deployment"` - Controller controllerCmd `cmd:"" help:"Run the Kluctl controller"` + Controller controllerCmd `cmd:"" help:"Kluctl controller sub-commands"` Version versionCmd `cmd:"" help:"Print kluctl version"` } diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 60bc2cbf5..c1f1d3e68 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -69,6 +69,7 @@ spec: - command: - kluctl - controller + - run args: - --leader-elect image: ghcr.io/kluctl/kluctl:latest diff --git a/e2e/gitops_test.go b/e2e/gitops_test.go index 1df30c7fa..edb46e916 100644 --- a/e2e/gitops_test.go +++ b/e2e/gitops_test.go @@ -96,6 +96,7 @@ func (suite *GitopsTestSuite) startController() { ctx, ctxCancel := context.WithCancel(context.Background()) args := []string{ "controller", + "run", "--kubeconfig", tmpKubeconfig, "--context", diff --git a/install/controller/controller/manager.yaml b/install/controller/controller/manager.yaml index 02f78bc76..7cee4bf75 100644 --- a/install/controller/controller/manager.yaml +++ b/install/controller/controller/manager.yaml @@ -42,6 +42,7 @@ spec: command: - kluctl - controller + - run image: ghcr.io/kluctl/kluctl:latest imagePullPolicy: IfNotPresent livenessProbe: From f6e30a1129cc6a4191eccaa30f0a07cae23d3d9e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 00:16:54 +0200 Subject: [PATCH 1047/2268] feat: Add controller install sub-command --- cmd/kluctl/commands/cmd_controller.go | 3 +- cmd/kluctl/commands/cmd_controller_install.go | 54 +++++++++++++++++++ cmd/kluctl/commands/cmd_deploy.go | 3 ++ cmd/kluctl/commands/cmd_list_targets.go | 2 +- cmd/kluctl/commands/cmd_seal.go | 2 +- cmd/kluctl/commands/completion.go | 2 +- cmd/kluctl/commands/utils.go | 14 +++-- install/controller/embed.go | 2 +- pkg/kluctl_project/project.go | 3 +- pkg/kluctl_project/project_load.go | 10 ++-- 10 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 cmd/kluctl/commands/cmd_controller_install.go diff --git a/cmd/kluctl/commands/cmd_controller.go b/cmd/kluctl/commands/cmd_controller.go index 508c75220..5a49c5187 100644 --- a/cmd/kluctl/commands/cmd_controller.go +++ b/cmd/kluctl/commands/cmd_controller.go @@ -1,5 +1,6 @@ package commands type controllerCmd struct { - Run_ controllerRunCmd `cmd:"run" help:"Run the Kluctl controller"` + Install controllerInstallCmd `cmd:"" help:"Install the Kluctl controller"` + Run_ controllerRunCmd `cmd:"run" help:"Run the Kluctl controller"` } diff --git a/cmd/kluctl/commands/cmd_controller_install.go b/cmd/kluctl/commands/cmd_controller_install.go new file mode 100644 index 000000000..319a0f496 --- /dev/null +++ b/cmd/kluctl/commands/cmd_controller_install.go @@ -0,0 +1,54 @@ +package commands + +import ( + "context" + "fmt" + "github.com/kluctl/go-embed-python/embed_util" + "github.com/kluctl/kluctl/v2/cmd/kluctl/args" + "github.com/kluctl/kluctl/v2/install/controller" + "time" +) + +type controllerInstallCmd struct { + args.YesFlags + args.DryRunFlags + args.CommandResultFlags + + Context string `group:"controller" help:"Override the context to use."` + ControllerVersion string `group:"controller" help:"Specify the controller version to install."` +} + +func (cmd *controllerInstallCmd) Help() string { + return `This command will install the kluctl-controller to the current Kubernetes clusters.` +} + +func (cmd *controllerInstallCmd) Run(ctx context.Context) error { + src, err := embed_util.NewEmbeddedFiles(controller.Project, "kluctl-controller-deployment") + if err != nil { + return err + } + + var deployArgs []string + if cmd.ControllerVersion != "" { + deployArgs = append(deployArgs, fmt.Sprintf("controller_version=%s", cmd.ControllerVersion)) + } + + cmd2 := deployCmd{ + ProjectFlags: args.ProjectFlags{ + ProjectDir: args.ProjectDir{ + ProjectDir: args.ExistingDirType(src.GetExtractedPath()), + }, + Timeout: 10 * time.Minute, + }, + TargetFlags: args.TargetFlags{ + Context: cmd.Context, + }, + ArgsFlags: args.ArgsFlags{ + Arg: deployArgs, + }, + DryRunFlags: cmd.DryRunFlags, + CommandResultFlags: cmd.CommandResultFlags, + internal: true, + } + return cmd2.Run(ctx) +} diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index e292dc27d..e86f1a53d 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -27,6 +27,8 @@ type deployCmd struct { args.CommandResultFlags NoWait bool `group:"misc" help:"Don't wait for objects readiness'"` + + internal bool } func (cmd *deployCmd) Help() string { @@ -47,6 +49,7 @@ func (cmd *deployCmd) Run(ctx context.Context) error { dryRunArgs: &cmd.DryRunFlags, renderOutputDirFlags: cmd.RenderOutputDirFlags, commandResultFlags: &cmd.CommandResultFlags, + internalDeploy: cmd.internal, } return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { return cmd.runCmdDeploy(cmdCtx) diff --git a/cmd/kluctl/commands/cmd_list_targets.go b/cmd/kluctl/commands/cmd_list_targets.go index ce39e8476..8df9d4373 100644 --- a/cmd/kluctl/commands/cmd_list_targets.go +++ b/cmd/kluctl/commands/cmd_list_targets.go @@ -17,7 +17,7 @@ func (cmd *listTargetsCmd) Help() string { } func (cmd *listTargetsCmd) Run(ctx context.Context) error { - return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, nil, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, nil, false, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { var result []*types.Target for _, t := range p.Targets { result = append(result, t) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index 69ecad6ee..c081a61f8 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -155,7 +155,7 @@ func (cmd *sealCmd) loadCert(cmdCtx *commandCtx) (*x509.Certificate, error) { } func (cmd *sealCmd) Run(ctx context.Context) error { - return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, nil, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(ctx, cmd.ProjectFlags, nil, false, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { hadError := false noTargetMatch := true diff --git a/cmd/kluctl/commands/completion.go b/cmd/kluctl/commands/completion.go index 77e7fe978..653b032f7 100644 --- a/cmd/kluctl/commands/completion.go +++ b/cmd/kluctl/commands/completion.go @@ -50,7 +50,7 @@ func RegisterFlagCompletionFuncs(cmdStruct interface{}, ccmd *cobra.Command) err func withProjectForCompletion(ctx context.Context, projectArgs *args.ProjectFlags, argsFlags *args.ArgsFlags, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { // let's not update git caches too often projectArgs.GitCacheUpdateInterval = time.Second * 60 - return withKluctlProjectFromArgs(ctx, *projectArgs, argsFlags, false, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(ctx, *projectArgs, argsFlags, false, false, true, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return cb(ctx, p) }) } diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index 66e6103b0..a03b70b77 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -24,7 +24,7 @@ import ( "strings" ) -func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFlags, argsFlags *args.ArgsFlags, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { +func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFlags, argsFlags *args.ArgsFlags, internalDeploy bool, strictTemplates bool, forCompletion bool, cb func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error) error { tmpDir, err := os.MkdirTemp(utils.GetTmpBaseDir(ctx), "project-") if err != nil { return fmt.Errorf("creating temporary project directory failed: %w", err) @@ -42,9 +42,12 @@ func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFla return err } - repoRoot, err := git.DetectGitRepositoryRoot(projectDir) - if err != nil { - status.Warning(ctx, "Failed to detect git project root. This might cause follow-up errors") + var repoRoot string + if !internalDeploy { + repoRoot, err = git.DetectGitRepositoryRoot(projectDir) + if err != nil { + status.Warning(ctx, "Failed to detect git project root. This might cause follow-up errors") + } } ctx, cancel := context.WithTimeout(ctx, projectFlags.Timeout) @@ -126,6 +129,7 @@ type projectTargetCommandArgs struct { renderOutputDirFlags args.RenderOutputDirFlags commandResultFlags *args.CommandResultFlags + internalDeploy bool forSeal bool forCompletion bool offlineKubernetes bool @@ -140,7 +144,7 @@ type commandCtx struct { } func withProjectCommandContext(ctx context.Context, args projectTargetCommandArgs, cb func(cmdCtx *commandCtx) error) error { - return withKluctlProjectFromArgs(ctx, args.projectFlags, &args.argsFlags, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { + return withKluctlProjectFromArgs(ctx, args.projectFlags, &args.argsFlags, args.internalDeploy, true, false, func(ctx context.Context, p *kluctl_project.LoadedKluctlProject) error { return withProjectTargetCommandContext(ctx, args, p, cb) }) } diff --git a/install/controller/embed.go b/install/controller/embed.go index 3bf3cd004..dc5e78645 100644 --- a/install/controller/embed.go +++ b/install/controller/embed.go @@ -3,4 +3,4 @@ package controller import "embed" //go:embed all:* -var Config embed.FS +var Project embed.FS diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index ad2d230c9..7afc4918f 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -18,8 +18,7 @@ type LoadedKluctlProject struct { TmpDir string - projectRootDir string - ProjectDir string + ProjectDir string sealedSecretsDir string diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 2982be334..5ed7aa53f 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -38,11 +38,13 @@ func (c *LoadedKluctlProject) getConfigPath() string { func (c *LoadedKluctlProject) loadKluctlProject() error { var err error - c.projectRootDir = c.LoadArgs.RepoRoot c.ProjectDir = c.LoadArgs.ProjectDir - err = utils.CheckInDir(c.projectRootDir, c.ProjectDir) - if err != nil { - return err + + if c.LoadArgs.RepoRoot != "" { + err = utils.CheckInDir(c.LoadArgs.RepoRoot, c.ProjectDir) + if err != nil { + return err + } } configPath := c.getConfigPath() From be6d540f683a0a5b7eee85b2a7a272e1b10c485e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 08:27:27 +0200 Subject: [PATCH 1048/2268] docs: Add links to command results arguments --- docs/reference/commands/delete.md | 1 + docs/reference/commands/deploy.md | 1 + docs/reference/commands/poke-images.md | 1 + docs/reference/commands/prune.md | 1 + 4 files changed, 4 insertions(+) diff --git a/docs/reference/commands/delete.md b/docs/reference/commands/delete.md index da4ad7739..058367db7 100644 --- a/docs/reference/commands/delete.md +++ b/docs/reference/commands/delete.md @@ -27,6 +27,7 @@ The following sets of arguments are available: 1. [project arguments](./common-arguments.md#project-arguments) 1. [image arguments](./common-arguments.md#image-arguments) 1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) +1. [command results arguments](./common-arguments.md#command-results-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/deploy.md b/docs/reference/commands/deploy.md index ea4d88e11..344fb448b 100644 --- a/docs/reference/commands/deploy.md +++ b/docs/reference/commands/deploy.md @@ -25,6 +25,7 @@ The following sets of arguments are available: 1. [project arguments](./common-arguments.md#project-arguments) 1. [image arguments](./common-arguments.md#image-arguments) 1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) +1. [command results arguments](./common-arguments.md#command-results-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/poke-images.md b/docs/reference/commands/poke-images.md index 55ce70bbc..6dc316676 100644 --- a/docs/reference/commands/poke-images.md +++ b/docs/reference/commands/poke-images.md @@ -25,6 +25,7 @@ The following sets of arguments are available: 1. [project arguments](./common-arguments.md#project-arguments) 1. [image arguments](./common-arguments.md#image-arguments) 1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) +1. [command results arguments](./common-arguments.md#command-results-arguments) In addition, the following arguments are available: diff --git a/docs/reference/commands/prune.md b/docs/reference/commands/prune.md index 6d012920e..40b7f211f 100644 --- a/docs/reference/commands/prune.md +++ b/docs/reference/commands/prune.md @@ -21,6 +21,7 @@ The following sets of arguments are available: 1. [project arguments](./common-arguments.md#project-arguments) 1. [image arguments](./common-arguments.md#image-arguments) 1. [inclusion/exclusion arguments](./common-arguments.md#inclusionexclusion-arguments) +1. [command results arguments](./common-arguments.md#command-results-arguments) In addition, the following arguments are available: From 9de8794983f23e0c779959b9847d3b9b2ab27cff Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 08:31:55 +0200 Subject: [PATCH 1049/2268] docs: Add docs about new subcommands --- cmd/kluctl/commands/cmd_controller_install.go | 4 +- cmd/kluctl/commands/cmd_controller_run.go | 14 +++---- cmd/kluctl/commands/root.go | 1 - docs/reference/commands/controller-install.md | 37 +++++++++++++++++++ .../{controller.md => controller-run.md} | 14 +++---- internal/replace-commands-help/main.go | 5 ++- 6 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 docs/reference/commands/controller-install.md rename docs/reference/commands/{controller.md => controller-run.md} (81%) diff --git a/cmd/kluctl/commands/cmd_controller_install.go b/cmd/kluctl/commands/cmd_controller_install.go index 319a0f496..d5e3db490 100644 --- a/cmd/kluctl/commands/cmd_controller_install.go +++ b/cmd/kluctl/commands/cmd_controller_install.go @@ -14,8 +14,8 @@ type controllerInstallCmd struct { args.DryRunFlags args.CommandResultFlags - Context string `group:"controller" help:"Override the context to use."` - ControllerVersion string `group:"controller" help:"Specify the controller version to install."` + Context string `group:"misc" help:"Override the context to use."` + ControllerVersion string `group:"misc" help:"Specify the controller version to install."` } func (cmd *controllerInstallCmd) Help() string { diff --git a/cmd/kluctl/commands/cmd_controller_run.go b/cmd/kluctl/commands/cmd_controller_run.go index 6d9cb40b2..bc83b0a5d 100644 --- a/cmd/kluctl/commands/cmd_controller_run.go +++ b/cmd/kluctl/commands/cmd_controller_run.go @@ -35,15 +35,15 @@ const controllerName = "kluctl-controller" type controllerRunCmd struct { scheme *runtime.Scheme - Kubeconfig string `group:"controller" help:"Override the kubeconfig to use."` - Context string `group:"controller" help:"Override the context to use."` + Kubeconfig string `group:"misc" help:"Override the kubeconfig to use."` + Context string `group:"misc" help:"Override the context to use."` - MetricsBindAddress string `group:"controller" help:"The address the metric endpoint binds to." default:":8080"` - HealthProbeBindAddress string `group:"controller" help:"The address the probe endpoint binds to." default:":8081"` - LeaderElect bool `group:"controller" help:"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager."` + MetricsBindAddress string `group:"misc" help:"The address the metric endpoint binds to." default:":8080"` + HealthProbeBindAddress string `group:"misc" help:"The address the probe endpoint binds to." default:":8081"` + LeaderElect bool `group:"misc" help:"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager."` - DefaultServiceAccount string `group:"controller" help:"Default service account used for impersonation."` - DryRun bool `group:"controller" help:"Run all deployments in dryRun=true mode."` + DefaultServiceAccount string `group:"misc" help:"Default service account used for impersonation."` + DryRun bool `group:"misc" help:"Run all deployments in dryRun=true mode."` args.CommandResultFlags } diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 14b15c02e..cc629d689 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -78,7 +78,6 @@ var flagGroups = []groupInfo{ {group: "inclusion", title: "Inclusion/Exclusion arguments:", description: "Control inclusion/exclusion."}, {group: "misc", title: "Misc arguments:", description: "Command specific arguments."}, {group: "results", title: "Command Results:", description: "Configure how command results are stored."}, - {group: "controller", title: "Controller:", description: "Controller arguments."}, } var origStderr = os.Stderr diff --git a/docs/reference/commands/controller-install.md b/docs/reference/commands/controller-install.md new file mode 100644 index 000000000..516e9ee91 --- /dev/null +++ b/docs/reference/commands/controller-install.md @@ -0,0 +1,37 @@ + + +## Command + +Usage: kluctl controller install [flags] + +Install the Kluctl controller +This command will install the kluctl-controller to the current Kubernetes clusters. + + + +## Arguments +The following sets of arguments are available: +1. [command results arguments](./common-arguments.md#command-results-arguments) + +In addition, the following arguments are available: + +``` +Misc arguments: + Command specific arguments. + + --context string Override the context to use. + --controller-version string Specify the controller version to install. + --dry-run Performs all kubernetes API calls in dry-run mode. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + +``` + diff --git a/docs/reference/commands/controller.md b/docs/reference/commands/controller-run.md similarity index 81% rename from docs/reference/commands/controller.md rename to docs/reference/commands/controller-run.md index 588b04f6e..53589381f 100644 --- a/docs/reference/commands/controller.md +++ b/docs/reference/commands/controller-run.md @@ -1,8 +1,8 @@ ## Command - -Usage: kluctl controller [flags] + +Usage: kluctl controller run [flags] Run the Kluctl controller This command will run the Kluctl Controller. This is usually meant to be run inside a cluster and not from your local machine. @@ -21,10 +21,10 @@ This command will run the Kluctl Controller. This is usually meant to be run ins ## Arguments The following arguments are available: - + ``` -Controller: - Controller arguments. +Misc arguments: + Command specific arguments. --context string Override the context to use. --default-service-account string Default service account used for impersonation. diff --git a/internal/replace-commands-help/main.go b/internal/replace-commands-help/main.go index 2f47f1c08..6fbbf7924 100644 --- a/internal/replace-commands-help/main.go +++ b/internal/replace-commands-help/main.go @@ -135,7 +135,10 @@ func getHelpSection(command string, section string) []string { log.Fatal(err) } - helpCmd := exec.Command(exe, command, "--help") + args := strings.Split(command, " ") + args = append(args, "--help") + + helpCmd := exec.Command(exe, args...) helpCmd.Env = os.Environ() helpCmd.Env = append(helpCmd.Env, "CALL_KLUCTL=true") From 211d65eecd10a8011dc7dd9de3b67eac8ae20b90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 08:39:19 +0200 Subject: [PATCH 1050/2268] chore(deps): Bump github.com/ohler55/ojg from 1.18.5 to 1.18.6 (#508) Bumps [github.com/ohler55/ojg](https://github.com/ohler55/ojg) from 1.18.5 to 1.18.6. - [Changelog](https://github.com/ohler55/ojg/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/ojg/compare/v1.18.5...v1.18.6) --- updated-dependencies: - dependency-name: github.com/ohler55/ojg dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0a686c573..03f26806f 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.18.5 + github.com/ohler55/ojg v1.18.6 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.10.0 diff --git a/go.sum b/go.sum index 6afa7e683..8c298eb8c 100644 --- a/go.sum +++ b/go.sum @@ -644,8 +644,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/nelsam/hel/v2 v2.3.3/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/ohler55/ojg v1.18.5 h1:tzn5LJtkSyXowCo8SlGieU0zEc7WF4143Ri9MYlQy7Q= -github.com/ohler55/ojg v1.18.5/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= +github.com/ohler55/ojg v1.18.6 h1:ZY5I/8I+zW8s+/QpX9E/P9QJwECi4lNx67VgdOzTTno= +github.com/ohler55/ojg v1.18.6/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= From 7ecd12bfeb6c7913b7e3e19f21befc51fae2fb5e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 11:56:37 +0200 Subject: [PATCH 1051/2268] fix: Use strict yaml unmarshalling in custom unmarshallers (#510) * chore: Add .gitignore for webui * fix: Use strict yaml unmarshalling in custom unmarshallers --- pkg/deployment/external_args.go | 3 +-- pkg/types/deployment.go | 5 ++--- pkg/types/git_project.go | 5 ++--- pkg/types/git_url.go | 5 +++-- pkg/types/result/compact.go | 2 +- pkg/types/yaml_url.go | 3 ++- pkg/utils/uo/uo.go | 2 +- pkg/webui/ui/.gitignore | 23 +++++++++++++++++++++++ 8 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 pkg/webui/ui/.gitignore diff --git a/pkg/deployment/external_args.go b/pkg/deployment/external_args.go index 808d65dbf..17c003d84 100644 --- a/pkg/deployment/external_args.go +++ b/pkg/deployment/external_args.go @@ -1,7 +1,6 @@ package deployment import ( - "encoding/json" "fmt" "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -61,7 +60,7 @@ func LoadDefaultArgs(args []*types.DeploymentArg, deployArgs *uo.UnstructuredObj for _, a := range args { if a.Default != nil { var v any - err := json.Unmarshal(a.Default.Raw, &v) + err := yaml.ReadYamlBytes(a.Default.Raw, &v) if err != nil { return err } diff --git a/pkg/types/deployment.go b/pkg/types/deployment.go index a4f8ee8ee..843186106 100644 --- a/pkg/types/deployment.go +++ b/pkg/types/deployment.go @@ -1,7 +1,6 @@ package types import ( - "encoding/json" "github.com/go-playground/validator/v10" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/yaml" @@ -70,14 +69,14 @@ type SingleStringOrList []string func (s *SingleStringOrList) UnmarshalJSON(b []byte) error { var single string - if err := json.Unmarshal(b, &single); err == nil { + if err := yaml.ReadYamlBytes(b, &single); err == nil { // it's a single project *s = []string{single} return nil } // try as array var arr []string - if err := json.Unmarshal(b, &arr); err != nil { + if err := yaml.ReadYamlBytes(b, &arr); err != nil { return err } *s = arr diff --git a/pkg/types/git_project.go b/pkg/types/git_project.go index 32a785541..e73cff058 100644 --- a/pkg/types/git_project.go +++ b/pkg/types/git_project.go @@ -1,7 +1,6 @@ package types import ( - "encoding/json" "fmt" "regexp" "strings" @@ -20,12 +19,12 @@ type GitProject struct { } func (gp *GitProject) UnmarshalJSON(b []byte) error { - if err := json.Unmarshal(b, &gp.Url); err == nil { + if err := yaml.ReadYamlBytes(b, &gp.Url); err == nil { // it's a simple string return nil } type raw GitProject - return json.Unmarshal(b, (*raw)(gp)) + return yaml.ReadYamlBytes(b, (*raw)(gp)) } // invalidDirName evaluate directory name against forbidden characters diff --git a/pkg/types/git_url.go b/pkg/types/git_url.go index 4c649bb04..8392967bf 100644 --- a/pkg/types/git_url.go +++ b/pkg/types/git_url.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/kluctl/kluctl/v2/pkg/git/giturls" + "github.com/kluctl/kluctl/v2/pkg/yaml" "net/url" "strings" ) @@ -43,7 +44,7 @@ func (in *GitUrl) DeepCopyInto(out *GitUrl) { func (u *GitUrl) UnmarshalJSON(b []byte) error { var s string - err := json.Unmarshal(b, &s) + err := yaml.ReadYamlBytes(b, &s) if err != nil { return err } @@ -142,7 +143,7 @@ func (u GitRepoKey) String() string { func (u *GitRepoKey) UnmarshalJSON(b []byte) error { var s string - err := json.Unmarshal(b, &s) + err := yaml.ReadYamlBytes(b, &s) if err != nil { return err } diff --git a/pkg/types/result/compact.go b/pkg/types/result/compact.go index ae94847c3..6caa48aaf 100644 --- a/pkg/types/result/compact.go +++ b/pkg/types/result/compact.go @@ -73,7 +73,7 @@ func (l CompactedObjects) MarshalJSON() ([]byte, error) { func (l *CompactedObjects) UnmarshalJSON(b []byte) error { var compactedList []CompactedObject - err := json.Unmarshal(b, &compactedList) + err := yaml.ReadYamlBytes(b, &compactedList) if err != nil { return err } diff --git a/pkg/types/yaml_url.go b/pkg/types/yaml_url.go index f2ffac1df..0f7ffda06 100644 --- a/pkg/types/yaml_url.go +++ b/pkg/types/yaml_url.go @@ -2,6 +2,7 @@ package types import ( "encoding/json" + "github.com/kluctl/kluctl/v2/pkg/yaml" "net/url" ) @@ -11,7 +12,7 @@ type YamlUrl struct { func (u *YamlUrl) UnmarshalJSON(b []byte) error { var s string - err := json.Unmarshal(b, &s) + err := yaml.ReadYamlBytes(b, &s) if err != nil { return err } diff --git a/pkg/utils/uo/uo.go b/pkg/utils/uo/uo.go index 92d2e73d7..c9ab95bf1 100644 --- a/pkg/utils/uo/uo.go +++ b/pkg/utils/uo/uo.go @@ -19,7 +19,7 @@ func (uo *UnstructuredObject) MarshalJSON() ([]byte, error) { } func (uo *UnstructuredObject) UnmarshalJSON(b []byte) error { - return json.Unmarshal(b, &uo.Object) + return yaml.ReadYamlBytes(b, &uo.Object) } func (uo *UnstructuredObject) IsZero() bool { diff --git a/pkg/webui/ui/.gitignore b/pkg/webui/ui/.gitignore new file mode 100644 index 000000000..4d29575de --- /dev/null +++ b/pkg/webui/ui/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* From c2fc47f0f26ed8981e5a8f4c4556d7e47ee42e2d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 09:11:06 +0200 Subject: [PATCH 1052/2268] refactor: Introduce ParseGitRepoKey --- pkg/types/git_url.go | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/pkg/types/git_url.go b/pkg/types/git_url.go index 8392967bf..5ab73fdd0 100644 --- a/pkg/types/git_url.go +++ b/pkg/types/git_url.go @@ -134,6 +134,21 @@ type GitRepoKey struct { Path string `json:"-"` } +func ParseGitRepoKey(s string) (GitRepoKey, error) { + if s == "" { + return GitRepoKey{}, nil + } + + s2 := strings.SplitN(s, "/", 2) + if len(s2) != 2 { + return GitRepoKey{}, fmt.Errorf("invalid git repo key: %s", s) + } + return GitRepoKey{ + Host: s2[0], + Path: s2[1], + }, nil +} + func (u GitRepoKey) String() string { if u.Host == "" && u.Path == "" { return "" @@ -152,12 +167,11 @@ func (u *GitRepoKey) UnmarshalJSON(b []byte) error { u.Path = "" return nil } - s2 := strings.SplitN(s, "/", 2) - if len(s2) != 2 { - return fmt.Errorf("invalid git repo key: %s", s) + x, err := ParseGitRepoKey(s) + if err != nil { + return err } - u.Host = s2[0] - u.Path = s2[1] + *u = x return nil } From 9ec07a6fdc2fa54d777c6757247f81269f093fa0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 13:58:36 +0200 Subject: [PATCH 1053/2268] fix: Fix crash in WatchCommandResultSummaries handlers --- pkg/results/result-store-secrets.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 5d244d538..1389db4ab 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -310,6 +310,9 @@ func (s *ResultStoreSecrets) WatchCommandResultSummaries(options ListCommandResu if err != nil { return } + if summary == nil { + return + } if !s.filterSummary(summary, options.ProjectFilter) { return } From 10fa182d84ebcdac8b23bc317dd4f23086fbb8c6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 13:59:06 +0200 Subject: [PATCH 1054/2268] fix: Use imagePullPolicy: Always for snapshot builds --- install/controller/controller/kustomization.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml index 4c41784cc..d4711bbb7 100644 --- a/install/controller/controller/kustomization.yaml +++ b/install/controller/controller/kustomization.yaml @@ -11,3 +11,12 @@ patches: - op: add path: /spec/template/spec/containers/0/image value: ghcr.io/kluctl/kluctl:{{ args.controller_version }} +{% if args.controller_version.endswith("-next-amd64") or args.controller_version.endswith("-next-arm64") %} + - target: + kind: Deployment + name: kluctl-controller + patch: |- + - op: add + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always +{% endif %} From 00ba739db2996a1509c9dda1ee5af99d97e74d3f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 13:59:18 +0200 Subject: [PATCH 1055/2268] fix: Fix crash in handleCommandResult --- pkg/controllers/kluctl_project.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 7af6cda94..5c9a946bc 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -640,6 +640,11 @@ func (pt *preparedTarget) loadTarget(ctx context.Context, p *kluctl_project.Load func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, cmdResult *result.CommandResult, commandName string) error { log := ctrl.LoggerFrom(ctx) + if cmdErr != nil { + pt.pp.r.event(ctx, pt.pp.obj, true, fmt.Sprintf("%s failed. %s", commandName, cmdErr.Error()), nil) + return cmdErr + } + cmdResult.Command.Initiator = result.CommandInititiator_KluctlDeployment cmdResult.GitInfo.Url = &pt.pp.obj.Spec.Source.URL cmdResult.GitInfo.Ref = pt.pp.obj.Spec.Source.Ref.String() @@ -655,10 +660,6 @@ func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, log.Info(fmt.Sprintf("command finished with err=%v", cmdErr)) defer pt.exportCommandResultMetricsToProm(summary) - if cmdErr != nil { - pt.pp.r.event(ctx, pt.pp.obj, true, fmt.Sprintf("%s failed. %s", commandName, cmdErr.Error()), nil) - return cmdErr - } msg := fmt.Sprintf("%s succeeded.", commandName) if summary.NewObjects != 0 { From 0616517baca3d6f692996a916a9e60c8fb36f96b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 13:59:28 +0200 Subject: [PATCH 1056/2268] fix: Give the controller more cpu+memory --- config/manager/manager.yaml | 6 +++--- install/controller/controller/manager.yaml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index c1f1d3e68..f544bbcee 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -97,9 +97,9 @@ spec: resources: limits: cpu: 500m - memory: 128Mi + memory: 512Mi requests: - cpu: 10m - memory: 64Mi + cpu: 500m + memory: 512Mi serviceAccountName: kluctl-controller terminationGracePeriodSeconds: 10 diff --git a/install/controller/controller/manager.yaml b/install/controller/controller/manager.yaml index 7cee4bf75..ed9e00985 100644 --- a/install/controller/controller/manager.yaml +++ b/install/controller/controller/manager.yaml @@ -61,10 +61,10 @@ spec: resources: limits: cpu: 500m - memory: 128Mi + memory: 512Mi requests: - cpu: 10m - memory: 64Mi + cpu: 500m + memory: 512Mi securityContext: allowPrivilegeEscalation: false capabilities: From 23fe48ab5a4e554717deae92f9e4714f3ecefc03 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 14:35:20 +0200 Subject: [PATCH 1057/2268] fix: Add missing cluster-admin binding for the controller (#512) --- config/rbac/kustomization.yaml | 1 + config/rbac/reconciler_binding.yaml | 12 ++++++++++++ install/controller/controller/rbac.yaml | 13 +++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 config/rbac/reconciler_binding.yaml diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 166fe7986..588dbee4b 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -9,3 +9,4 @@ resources: - role_binding.yaml - leader_election_role.yaml - leader_election_role_binding.yaml +- reconciler_binding.yaml diff --git a/config/rbac/reconciler_binding.yaml b/config/rbac/reconciler_binding.yaml new file mode 100644 index 000000000..f85f09807 --- /dev/null +++ b/config/rbac/reconciler_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kluctl-controller-cluster-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: kluctl-controller + namespace: kluctl-system diff --git a/install/controller/controller/rbac.yaml b/install/controller/controller/rbac.yaml index 1845b4548..aa7d7bccb 100644 --- a/install/controller/controller/rbac.yaml +++ b/install/controller/controller/rbac.yaml @@ -146,6 +146,19 @@ subjects: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding +metadata: + name: kluctl-controller-cluster-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: kluctl-controller + namespace: kluctl-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/component: rbac From b8af67f8213a8ab60d896a6631d2e74077e14936 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 13:31:59 +0200 Subject: [PATCH 1058/2268] chore(deps): Bump github.com/hashicorp/vault/api from 1.9.1 to 1.9.2 (#513) Bumps [github.com/hashicorp/vault/api](https://github.com/hashicorp/vault) from 1.9.1 to 1.9.2. - [Release notes](https://github.com/hashicorp/vault/releases) - [Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/vault/compare/v1.9.1...v1.9.2) --- updated-dependencies: - dependency-name: github.com/hashicorp/vault/api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 03f26806f..f4c9b9273 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-containerregistry v0.15.2 - github.com/hashicorp/vault/api v1.9.1 + github.com/hashicorp/vault/api v1.9.2 github.com/hexops/gotextdiff v1.0.3 github.com/imdario/mergo v0.3.15 github.com/jinzhu/copier v0.3.5 @@ -134,6 +134,7 @@ require ( github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect @@ -237,7 +238,6 @@ require ( 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/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 8c298eb8c..332d6da82 100644 --- a/go.sum +++ b/go.sum @@ -282,6 +282,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-gorp/gorp/v3 v3.0.5/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 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= @@ -478,8 +480,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.9.1 h1:LtY/I16+5jVGU8rufyyAkwopgq/HpUnxFBg+QLOAV38= -github.com/hashicorp/vault/api v1.9.1/go.mod h1:78kktNcQYbBGSrOjQfHjXN32OhhxXnbYl3zxpd2uPUs= +github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as= +github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -871,6 +873,7 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk 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-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1301,8 +1304,6 @@ gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= From c01a2f623b7a999ab6e44264155f493baa08b799 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 13:32:31 +0200 Subject: [PATCH 1059/2268] chore(deps): Bump github.com/imdario/mergo from 0.3.15 to 0.3.16 (#514) Bumps [github.com/imdario/mergo](https://github.com/imdario/mergo) from 0.3.15 to 0.3.16. - [Release notes](https://github.com/imdario/mergo/releases) - [Commits](https://github.com/imdario/mergo/compare/v0.3.15...v0.3.16) --- updated-dependencies: - dependency-name: github.com/imdario/mergo dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f4c9b9273..3fc55f456 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/google/go-containerregistry v0.15.2 github.com/hashicorp/vault/api v1.9.2 github.com/hexops/gotextdiff v1.0.3 - github.com/imdario/mergo v0.3.15 + github.com/imdario/mergo v0.3.16 github.com/jinzhu/copier v0.3.5 github.com/kevinburke/ssh_config v1.2.0 github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 diff --git a/go.sum b/go.sum index 332d6da82..845de9aaa 100644 --- a/go.sum +++ b/go.sum @@ -493,8 +493,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= -github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 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= From 2c1b544f63e160c7ca72e48f21e4e60de52cbee9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 30 May 2023 14:11:23 +0200 Subject: [PATCH 1060/2268] fix: Fix bogus breakpoint (#515) * fix: Fix bogus breakpoint * chore: Add .gitignore entry for public/staticdata --- pkg/k8s/k8s_cluster.go | 5 ----- pkg/webui/ui/.gitignore | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index b7b37d515..50063ca8e 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -7,7 +7,6 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "net/http" - "runtime" "sigs.k8s.io/controller-runtime/pkg/client" "strings" "sync" @@ -457,10 +456,6 @@ func (k *K8sCluster) FixNamespaceInRef(ref k8s.ObjectRef) k8s.ObjectRef { } func (k *K8sCluster) GetSchemaForGVK(gvk schema.GroupVersionKind) (*uo.UnstructuredObject, error) { - if gvk.Kind == "KluctlDeployment" { - runtime.Breakpoint() - } - rms, err := k.clientFactory.Mapper().RESTMappings(gvk.GroupKind(), gvk.Version) if err != nil { return nil, err diff --git a/pkg/webui/ui/.gitignore b/pkg/webui/ui/.gitignore index 4d29575de..b2ba12633 100644 --- a/pkg/webui/ui/.gitignore +++ b/pkg/webui/ui/.gitignore @@ -21,3 +21,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +/public/staticdata From 89b0cfaef2f17cc43e62e3cec6560868806576f9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 30 May 2023 14:17:11 +0200 Subject: [PATCH 1061/2268] fix: Give controller more CPU limits (#516) --- config/manager/manager.yaml | 2 +- install/controller/controller/controller-version-patch.yaml | 0 install/controller/controller/manager.yaml | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 install/controller/controller/controller-version-patch.yaml diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index f544bbcee..83c13b50a 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -96,7 +96,7 @@ spec: # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: limits: - cpu: 500m + cpu: 2000m memory: 512Mi requests: cpu: 500m diff --git a/install/controller/controller/controller-version-patch.yaml b/install/controller/controller/controller-version-patch.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/install/controller/controller/manager.yaml b/install/controller/controller/manager.yaml index ed9e00985..5544b30e2 100644 --- a/install/controller/controller/manager.yaml +++ b/install/controller/controller/manager.yaml @@ -60,7 +60,7 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m + cpu: 2000m memory: 512Mi requests: cpu: 500m From 9985d95aba8f55d5165368d5e9fe27dcae069516 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 30 May 2023 14:34:34 +0200 Subject: [PATCH 1062/2268] fix: Don't write result when ResultStore is not initialized (#517) --- pkg/controllers/kluctl_project.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 5c9a946bc..52dfdc8fe 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -650,10 +650,13 @@ func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, cmdResult.GitInfo.Ref = pt.pp.obj.Spec.Source.Ref.String() cmdResult.ProjectKey.GitRepoKey = pt.pp.obj.Spec.Source.URL.RepoKey() - log.Info(fmt.Sprintf("Writing command result %s", cmdResult.Id)) - err := pt.pp.r.ResultStore.WriteCommandResult(cmdResult) - if err != nil { - log.Error(err, "Writing command result failed") + var err error + if pt.pp.r.ResultStore != nil { + log.Info(fmt.Sprintf("Writing command result %s", cmdResult.Id)) + err = pt.pp.r.ResultStore.WriteCommandResult(cmdResult) + if err != nil { + log.Error(err, "Writing command result failed") + } } summary := cmdResult.BuildSummary() From adea0679900faac66a81f751eb7b5ab7f891f379 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 30 May 2023 14:42:55 +0200 Subject: [PATCH 1063/2268] fix: Don't wait for deletion when performed by the controller (#518) --- cmd/kluctl/commands/cmd_delete.go | 2 +- cmd/kluctl/commands/cmd_prune.go | 2 +- pkg/controllers/kluctl_project.go | 4 ++-- pkg/deployment/commands/delete.go | 6 ++++-- pkg/deployment/commands/prune.go | 6 ++++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cmd/kluctl/commands/cmd_delete.go b/cmd/kluctl/commands/cmd_delete.go index f93e08d14..d60bcc85e 100644 --- a/cmd/kluctl/commands/cmd_delete.go +++ b/cmd/kluctl/commands/cmd_delete.go @@ -46,7 +46,7 @@ func (cmd *deleteCmd) Run(ctx context.Context) error { commandResultFlags: &cmd.CommandResultFlags, } return withProjectCommandContext(ctx, ptArgs, func(cmdCtx *commandCtx) error { - cmd2 := commands.NewDeleteCommand(cmd.Discriminator, cmdCtx.targetCtx, nil) + cmd2 := commands.NewDeleteCommand(cmd.Discriminator, cmdCtx.targetCtx, nil, true) result, err := cmd2.Run(cmdCtx.targetCtx.SharedContext.Ctx, cmdCtx.targetCtx.SharedContext.K, func(refs []k8s2.ObjectRef) error { return confirmDeletion(ctx, refs, cmd.DryRun, cmd.Yes) diff --git a/cmd/kluctl/commands/cmd_prune.go b/cmd/kluctl/commands/cmd_prune.go index 9e3eea413..f6bbd79fe 100644 --- a/cmd/kluctl/commands/cmd_prune.go +++ b/cmd/kluctl/commands/cmd_prune.go @@ -48,7 +48,7 @@ func (cmd *pruneCmd) Run(ctx context.Context) error { } func (cmd *pruneCmd) runCmdPrune(cmdCtx *commandCtx) error { - cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx) + cmd2 := commands.NewPruneCommand(cmdCtx.targetCtx.Target.Discriminator, cmdCtx.targetCtx, true) result, err := cmd2.Run(func(refs []k8s2.ObjectRef) error { return confirmDeletion(cmdCtx.ctx, refs, cmd.DryRun, cmd.Yes) }) diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 52dfdc8fe..715fbf36b 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -731,7 +731,7 @@ func (pt *preparedTarget) kluctlPrune(ctx context.Context, targetContext *kluctl timer := prometheus.NewTimer(internal_metrics.NewKluctlPruneDuration(pt.pp.obj.ObjectMeta.Namespace, pt.pp.obj.ObjectMeta.Name)) defer timer.ObserveDuration() - cmd := commands.NewPruneCommand("", targetContext) + cmd := commands.NewPruneCommand("", targetContext, false) cmdResult, err := cmd.Run(func(refs []k8s.ObjectRef) error { pt.printDeletedRefs(ctx, refs) return nil @@ -767,7 +767,7 @@ func (pt *preparedTarget) kluctlDelete(ctx context.Context, discriminator string inclusion := pt.buildInclusion() - cmd := commands.NewDeleteCommand(discriminator, nil, inclusion) + cmd := commands.NewDeleteCommand(discriminator, nil, inclusion, false) restConfig, err := pt.buildRestConfig(ctx) if err != nil { diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index 11c49e8bb..28794cb9a 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -18,13 +18,15 @@ type DeleteCommand struct { discriminator string targetCtx *kluctl_project.TargetContext inclusion *utils.Inclusion + wait bool } -func NewDeleteCommand(discriminator string, targetCtx *kluctl_project.TargetContext, inclusion *utils.Inclusion) *DeleteCommand { +func NewDeleteCommand(discriminator string, targetCtx *kluctl_project.TargetContext, inclusion *utils.Inclusion, wait bool) *DeleteCommand { return &DeleteCommand{ discriminator: discriminator, targetCtx: targetCtx, inclusion: inclusion, + wait: wait, } } @@ -64,7 +66,7 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb } } - deleted, err := utils2.DeleteObjects(ctx, k, deleteRefs, dew, true) + deleted, err := utils2.DeleteObjects(ctx, k, deleteRefs, dew, cmd.wait) if err != nil { return nil, err } diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index 1c3bed093..e48093d7b 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -14,12 +14,14 @@ import ( type PruneCommand struct { discriminator string targetCtx *kluctl_project.TargetContext + wait bool } -func NewPruneCommand(discriminator string, targetCtx *kluctl_project.TargetContext) *PruneCommand { +func NewPruneCommand(discriminator string, targetCtx *kluctl_project.TargetContext, wait bool) *PruneCommand { return &PruneCommand{ discriminator: discriminator, targetCtx: targetCtx, + wait: wait, } } @@ -52,7 +54,7 @@ func (cmd *PruneCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*resu } } - deleted, err := utils2.DeleteObjects(cmd.targetCtx.SharedContext.Ctx, cmd.targetCtx.SharedContext.K, deleteRefs, dew, true) + deleted, err := utils2.DeleteObjects(cmd.targetCtx.SharedContext.Ctx, cmd.targetCtx.SharedContext.K, deleteRefs, dew, cmd.wait) if err != nil { return nil, err } From a46f4f8a801c7192b5e405641f1621ceadcad5dc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 30 May 2023 23:36:21 +0200 Subject: [PATCH 1064/2268] fix: Create patch object right before the actual modification (#519) --- pkg/controllers/kluctldeployment_controller.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 2e6532208..201c3ab8d 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -74,8 +74,6 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, client.IgnoreNotFound(err) } - patch := client.MergeFrom(obj.DeepCopy()) - var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, r.calcTimeout(obj)) defer cancel() @@ -88,6 +86,7 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req // Add our finalizer if it does not exist if !controllerutil.ContainsFinalizer(obj, kluctlv1.KluctlDeploymentFinalizer) { + patch := client.MergeFrom(obj.DeepCopy()) controllerutil.AddFinalizer(obj, kluctlv1.KluctlDeploymentFinalizer) if err := r.Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { log.Error(err, "unable to register finalizer") @@ -121,6 +120,7 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req // set the reconciliation status to progressing if obj.Status.ObservedGeneration == 0 { + patch := client.MergeFrom(obj.DeepCopy()) setReadiness(obj, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress") if err := r.Status().Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { return ctrl.Result{Requeue: true}, err @@ -129,6 +129,8 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req r.recordReadiness(ctx, obj) } + patch := client.MergeFrom(obj.DeepCopy()) + // record the value of the reconciliation request, if any if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { obj.Status.LastHandledReconcileAt = v From 62b305cb9629733cab50434dec488fc55642ecb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 09:14:36 +0200 Subject: [PATCH 1065/2268] chore(deps): Bump github.com/spf13/viper from 1.15.0 to 1.16.0 (#520) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.15.0 to 1.16.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.15.0...v1.16.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 19 ++++++++++--------- go.sum | 50 +++++++++++++++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 3fc55f456..ad7176a81 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/sirupsen/logrus v1.9.2 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.15.0 + github.com/spf13/viper v1.16.0 github.com/stretchr/testify v1.8.3 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.9.0 @@ -82,7 +82,7 @@ require ( cloud.google.com/go/compute v1.19.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v0.13.0 // indirect - cloud.google.com/go/kms v1.10.0 // indirect + cloud.google.com/go/kms v1.10.1 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.5.1 // indirect @@ -149,9 +149,10 @@ require ( 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/s2a-go v0.1.3 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.1 // indirect + github.com/googleapis/gax-go/v2 v2.8.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect @@ -193,7 +194,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc3 // indirect - github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect @@ -207,8 +208,8 @@ require ( github.com/ryanuber/go-glob v1.0.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/skeema/knownhosts v1.1.1 // indirect - github.com/spf13/afero v1.9.3 // indirect - github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/urfave/cli v1.22.12 // indirect @@ -231,10 +232,10 @@ require ( golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.9.1 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect - google.golang.org/api v0.114.0 // indirect + google.golang.org/api v0.122.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230323212658-478b75c54725 // indirect - google.golang.org/grpc v1.54.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/grpc v1.55.0 // 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 diff --git a/go.sum b/go.sum index 845de9aaa..d229ef45a 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,8 @@ cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/kms v1.10.0 h1:Imrtp8792uqNP9bdfPrjtUkjjqOMBcAJ2bdFaAnLhnk= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= 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= @@ -169,6 +169,7 @@ github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTx github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= 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/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= @@ -183,7 +184,11 @@ github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUK 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/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg= github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc= @@ -243,6 +248,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y 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 v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -257,8 +263,8 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -409,6 +415,8 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE= +github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -419,8 +427,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9 github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= 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/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -665,8 +673,8 @@ github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/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/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= @@ -755,12 +763,13 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.6.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/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= @@ -773,8 +782,8 @@ 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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -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/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= 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= @@ -880,8 +889,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= @@ -1097,6 +1107,7 @@ 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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -1196,8 +1207,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.122.0 h1:zDobeejm3E7pEG1mNHvdxvjs5XJoCMzyNH+CmwL94Es= +google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= 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= @@ -1250,8 +1261,8 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725 h1:VmCWItVXcKboEMCwZaWge+1JLiTCQSngZeINF+wzO+g= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1274,8 +1285,9 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/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.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +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= From 483e6f5c33b4a5706f11f9d0774aeee86ca85aba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 09:44:19 +0200 Subject: [PATCH 1066/2268] chore(deps): Bump github.com/stretchr/testify from 1.8.3 to 1.8.4 (#521) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.3 to 1.8.4. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.8.3...v1.8.4) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index ad7176a81..c7d16d423 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.16.0 - github.com/stretchr/testify v1.8.3 + github.com/stretchr/testify v1.8.4 github.com/xanzy/ssh-agent v0.3.3 golang.org/x/crypto v0.9.0 golang.org/x/net v0.10.0 diff --git a/go.sum b/go.sum index d229ef45a..77124abb6 100644 --- a/go.sum +++ b/go.sum @@ -801,8 +801,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F 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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= From a9f3eac34673c86969ab4b5f2d0fde05c1494f68 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 11:14:40 +0200 Subject: [PATCH 1067/2268] fix: Directly use unstructured.Unstructured when getting legacy KD object (#522) --- pkg/controllers/kluctldeployment_controller.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 201c3ab8d..cd7b4ce47 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -14,10 +14,10 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" - "github.com/kluctl/kluctl/v2/pkg/utils/uo" "k8s.io/apimachinery/pkg/api/errors" meta2 "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/runtime/schema" "k8s.io/client-go/discovery" @@ -524,15 +524,17 @@ func (r *KluctlDeploymentReconciler) doFinalize(ctx context.Context, obj *kluctl func (r *KluctlDeploymentReconciler) checkLegacyKluctlDeployment(ctx context.Context, obj *kluctlv1.KluctlDeployment) bool { log := ctrl.LoggerFrom(ctx) - obj2 := uo.New() - obj2.SetK8sGVK(schema.GroupVersionKind{ + var obj2 unstructured.Unstructured + obj2.SetGroupVersionKind(schema.GroupVersionKind{ Group: "flux.kluctl.io", Version: "v1alpha1", Kind: "KluctlDeployment", }) - err := r.Get(ctx, client.ObjectKeyFromObject(obj), obj2.ToUnstructured()) + err := r.Get(ctx, client.ObjectKeyFromObject(obj), &obj2) if err != nil { - err = errors2.Unwrap(err) + if errors2.Unwrap(err) != nil { + err = errors2.Unwrap(err) + } if meta2.IsNoMatchError(err) || errors.IsNotFound(err) || discovery.IsGroupDiscoveryFailedError(err) { // legacy object not present, we're safe to continue return false @@ -542,7 +544,7 @@ func (r *KluctlDeploymentReconciler) checkLegacyKluctlDeployment(ctx context.Con return true } - readyForMigration, _, err := obj2.GetNestedBool("") + readyForMigration, _, err := unstructured.NestedBool(obj2.Object, "status", "readyForMigration") if err != nil { // some unexpected error...we should be on the safe side and bail out reconciliation log.Error(err, "Failed to retrieve readyForMigration value. Skipping reconciliation.") From 8ef0c58c3452d91ac3094cdb6dc479830445bade Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 13:26:48 +0200 Subject: [PATCH 1068/2268] fix: Update LastHandledReconcileAt much earlier --- pkg/controllers/kluctldeployment_controller.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index cd7b4ce47..2aa95c042 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -118,6 +118,15 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req defer r.MetricsRecorder.RecordDuration(*objRef, reconcileStart) } + // record the value of the reconciliation request, if any + if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { + patch := client.MergeFrom(obj.DeepCopy()) + obj.Status.LastHandledReconcileAt = v + if err := r.Status().Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { + return ctrl.Result{}, err + } + } + // set the reconciliation status to progressing if obj.Status.ObservedGeneration == 0 { patch := client.MergeFrom(obj.DeepCopy()) @@ -130,12 +139,6 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req } patch := client.MergeFrom(obj.DeepCopy()) - - // record the value of the reconciliation request, if any - if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { - obj.Status.LastHandledReconcileAt = v - } - // reconcile kluctlDeployment by applying the latest revision ctrlResult, reconcileErr := r.doReconcile(ctx, obj) if err := r.Status().Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { From af2f67edceaf0402660b9a476f6a12f9cf7e5d1a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 13:27:19 +0200 Subject: [PATCH 1069/2268] feat: Set readiness status while migrating --- api/v1beta1/condition_types.go | 3 ++ .../kluctldeployment_controller.go | 47 ++++++++++++++----- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/api/v1beta1/condition_types.go b/api/v1beta1/condition_types.go index e559651c3..9bd45ed52 100644 --- a/api/v1beta1/condition_types.go +++ b/api/v1beta1/condition_types.go @@ -35,4 +35,7 @@ const ( // ReconciliationSucceededReason represents the fact that // the reconciliation succeeded. ReconciliationSucceededReason string = "ReconciliationSucceeded" + + // WaitingForLegacyMigrationReason means that the controller is waiting for the legacy controller to set `readyForMigration=true` + WaitingForLegacyMigrationReason string = "WaitingForLegacyMigration" ) diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 2aa95c042..cdfa9f903 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -14,6 +14,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" + log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/errors" meta2 "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -62,7 +63,6 @@ type KluctlDeploymentReconcilerOpts struct { // +kubebuilder:rbac:groups="",resources=events,verbs=create;patch func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := ctrl.LoggerFrom(ctx) reconcileStart := time.Now() ctx = status.NewContext(ctx, status.NewSimpleStatusHandler(func(message string) { @@ -99,10 +99,6 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req return r.finalize(ctx, obj) } - if r.checkLegacyKluctlDeployment(ctx, obj) { - return ctrl.Result{}, nil - } - // Return early if the KluctlDeployment is suspended. if obj.Spec.Suspend { log.Info("Reconciliation is suspended for this object") @@ -180,6 +176,8 @@ func (r *KluctlDeploymentReconciler) doReconcile( ctx context.Context, obj *kluctlv1.KluctlDeployment) (*ctrl.Result, error) { + log := ctrl.LoggerFrom(ctx) + r.exportDeploymentObjectToProm(obj) doFail := func(reason string, err error) (*ctrl.Result, error) { @@ -192,6 +190,31 @@ func (r *KluctlDeploymentReconciler) doReconcile( return doFail(kluctlv1.PrepareFailedReason, err) } + legacyKd, err := r.checkLegacyKluctlDeployment(ctx, obj) + if err != nil { + return nil, err + } + if legacyKd { + c := meta2.FindStatusCondition(obj.Status.Conditions, meta.ReadyCondition) + if c != nil && c.Reason == kluctlv1.WaitingForLegacyMigrationReason { + log.Info("legacy KluctlDeployment does not have the readyForMigration status set. Skipping reconciliation. ") + return &ctrl.Result{RequeueAfter: 5 * time.Second}, err + } + log.Info("legacy KluctlDeployment does not have the readyForMigration status set. Skipping reconciliation. " + + "Please ensure that you have upgraded to the latest version of the legacy flux-kluctl-controller and that is is still running.") + setReadiness(obj, metav1.ConditionFalse, kluctlv1.WaitingForLegacyMigrationReason, "waiting for the legacy controller to set readyForMigration=true") + r.recordReadiness(ctx, obj) + return &ctrl.Result{Requeue: true}, nil + } else { + c := meta2.FindStatusCondition(obj.Status.Conditions, meta.ReadyCondition) + if c != nil && c.Reason == kluctlv1.WaitingForLegacyMigrationReason { + log.Info("legacy KluctlDeployment has the readyForMigration status set now") + setReadiness(obj, metav1.ConditionFalse, meta.ProgressingReason, "migration is finished") + r.recordReadiness(ctx, obj) + return &ctrl.Result{Requeue: true}, nil + } + } + oldGeneration := obj.Status.ObservedGeneration obj.Status.ObservedGeneration = obj.GetGeneration() if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; ok { @@ -524,7 +547,7 @@ func (r *KluctlDeploymentReconciler) doFinalize(ctx context.Context, obj *kluctl // checkLegacyKluctlDeployment checks if a legacy KluctlDeployment from the old flux.kluctl.io group is present. If yes // we must ensure that this object is served by a recent legacy controller version which understands that it should stop // reconciliation in case the new gitops.kluctl.io object is present -func (r *KluctlDeploymentReconciler) checkLegacyKluctlDeployment(ctx context.Context, obj *kluctlv1.KluctlDeployment) bool { +func (r *KluctlDeploymentReconciler) checkLegacyKluctlDeployment(ctx context.Context, obj *kluctlv1.KluctlDeployment) (bool, error) { log := ctrl.LoggerFrom(ctx) var obj2 unstructured.Unstructured @@ -540,26 +563,24 @@ func (r *KluctlDeploymentReconciler) checkLegacyKluctlDeployment(ctx context.Con } if meta2.IsNoMatchError(err) || errors.IsNotFound(err) || discovery.IsGroupDiscoveryFailedError(err) { // legacy object not present, we're safe to continue - return false + return false, nil } log.Error(err, "Failed to retrieve legacy KluctlDeployment. Skipping reconciliation.") // some unexpected error...we should be on the safe side and bail out reconciliation - return true + return true, err } readyForMigration, _, err := unstructured.NestedBool(obj2.Object, "status", "readyForMigration") if err != nil { // some unexpected error...we should be on the safe side and bail out reconciliation log.Error(err, "Failed to retrieve readyForMigration value. Skipping reconciliation.") - return true + return true, err } if !readyForMigration { - log.V(1).Info("legacy KluctlDeployment does not have the readyForMigration status set. Skipping reconciliation. " + - "Please ensure that you have upgraded to the latest version of the legacy flux-kluctl-controller and that is is still running.") - return true + return true, nil } - return false + return false, nil } func (r *KluctlDeploymentReconciler) exportDeploymentObjectToProm(obj *kluctlv1.KluctlDeployment) { From 330d9f418c8af9d8caee28efaf199853f77243ac Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 13:54:17 +0200 Subject: [PATCH 1070/2268] fix: Move LastHandledReconcileAt and ObservedGeneration handling into doReconcile --- .../kluctldeployment_controller.go | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index cdfa9f903..2f584b49d 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -114,26 +114,6 @@ func (r *KluctlDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Req defer r.MetricsRecorder.RecordDuration(*objRef, reconcileStart) } - // record the value of the reconciliation request, if any - if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { - patch := client.MergeFrom(obj.DeepCopy()) - obj.Status.LastHandledReconcileAt = v - if err := r.Status().Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { - return ctrl.Result{}, err - } - } - - // set the reconciliation status to progressing - if obj.Status.ObservedGeneration == 0 { - patch := client.MergeFrom(obj.DeepCopy()) - setReadiness(obj, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress") - if err := r.Status().Patch(ctx, obj, patch, client.FieldOwner(r.ControllerName)); err != nil { - return ctrl.Result{Requeue: true}, err - } - - r.recordReadiness(ctx, obj) - } - patch := client.MergeFrom(obj.DeepCopy()) // reconcile kluctlDeployment by applying the latest revision ctrlResult, reconcileErr := r.doReconcile(ctx, obj) @@ -190,6 +170,11 @@ func (r *KluctlDeploymentReconciler) doReconcile( return doFail(kluctlv1.PrepareFailedReason, err) } + // record the value of the reconciliation request, if any + if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { + obj.Status.LastHandledReconcileAt = v + } + legacyKd, err := r.checkLegacyKluctlDeployment(ctx, obj) if err != nil { return nil, err @@ -215,6 +200,12 @@ func (r *KluctlDeploymentReconciler) doReconcile( } } + // set the reconciliation status to progressing + if obj.Status.ObservedGeneration == 0 { + setReadiness(obj, metav1.ConditionUnknown, meta.ProgressingReason, "reconciliation in progress") + r.recordReadiness(ctx, obj) + } + oldGeneration := obj.Status.ObservedGeneration obj.Status.ObservedGeneration = obj.GetGeneration() if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; ok { From bdc40813ea7ab7e7cad471788e6cbe4a3ef8ccdb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 13:27:35 +0200 Subject: [PATCH 1071/2268] tests: Add migration tests --- e2e/default_clusters.go | 4 +- e2e/gitops_migration_test.go | 119 ++++ e2e/seal_test.go | 2 +- .../flux.kluctl.io_kluctldeployments.yaml | 659 ++++++++++++++++++ e2e/test_resources/resources.go | 20 +- 5 files changed, 796 insertions(+), 8 deletions(-) create mode 100644 e2e/gitops_migration_test.go create mode 100644 e2e/test_resources/flux.kluctl.io_kluctldeployments.yaml diff --git a/e2e/default_clusters.go b/e2e/default_clusters.go index 373957a89..964029bcd 100644 --- a/e2e/default_clusters.go +++ b/e2e/default_clusters.go @@ -30,7 +30,7 @@ func init() { if err != nil { panic(err) } - test_resources.ApplyYaml("sealed-secrets.yaml", defaultCluster1) + test_resources.ApplyYaml(nil, "sealed-secrets.yaml", defaultCluster1) }() go func() { defer wg.Done() @@ -41,7 +41,7 @@ func init() { if err != nil { panic(err) } - test_resources.ApplyYaml("sealed-secrets.yaml", defaultCluster2) + test_resources.ApplyYaml(nil, "sealed-secrets.yaml", defaultCluster2) }() wg.Wait() diff --git a/e2e/gitops_migration_test.go b/e2e/gitops_migration_test.go new file mode 100644 index 000000000..283f459aa --- /dev/null +++ b/e2e/gitops_migration_test.go @@ -0,0 +1,119 @@ +package e2e + +import ( + "context" + "fmt" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/e2e/test_resources" + meta2 "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/stretchr/testify/assert" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" + "time" +) +import . "github.com/onsi/gomega" + +var legacyGvk = schema.GroupVersionKind{ + Group: "flux.kluctl.io", + Version: "v1alpha1", + Kind: "KluctlDeployment", +} + +func (suite *GitopsTestSuite) createLegacyKluctlDeployment(p *test_utils.TestProject, target string) client.ObjectKey { + gitopsNs := p.TestSlug() + "-gitops" + createNamespace(suite.T(), suite.k, gitopsNs) + + kluctlDeployment := uo.New() + kluctlDeployment.SetK8sGVK(legacyGvk) + + kluctlDeployment.SetK8sName(p.TestSlug()) + kluctlDeployment.SetK8sNamespace(gitopsNs) + _ = kluctlDeployment.SetNestedField(map[string]any{ + "interval": interval.String(), + "deployInterval": "never", + "timeout": timeout.String(), + "target": target, + "source": map[string]any{ + "url": p.GitUrl(), + }, + }, "spec") + + err := suite.k.Client.Create(context.Background(), kluctlDeployment.ToUnstructured()) + if err != nil { + suite.T().Fatal(err) + } + + key := client.ObjectKeyFromObject(kluctlDeployment.ToUnstructured()) + return key +} + +func (suite *GitopsTestSuite) TestGitOpsLegacyMigration() { + g := NewWithT(suite.T()) + + test_resources.ApplyYaml(suite.T(), "flux.kluctl.io_kluctldeployments.yaml", suite.k) + suite.T().Cleanup(func() { + obj := uo.New() + obj.SetK8sGVK(apiextensionsv1.SchemeGroupVersion.WithKind("CustomResourceDefinition")) + obj.SetK8sName("kluctldeployments.flux.kluctl.io") + _ = suite.k.Client.Delete(context.TODO(), obj.ToUnstructured()) + }) + + p := test_utils.NewTestProject(suite.T()) + createNamespace(suite.T(), suite.k, p.TestSlug()) + + p.UpdateTarget("target1", nil) + p.AddKustomizeDeployment("d1", []test_utils.KustomizeResource{ + {Name: "cm1.yaml", Content: uo.FromStringMust(fmt.Sprintf(`apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 + namespace: "%s" +data: + k1: v1 +`, p.TestSlug()))}, + }, nil) + + key := suite.createLegacyKluctlDeployment(p, "target1") + key2 := suite.createKluctlDeployment(p, "target1", nil) + assert.Equal(suite.T(), key, key2) + + checkMigrationStatus := func() bool { + var kd kluctlv1.KluctlDeployment + _ = suite.k.Client.Get(context.Background(), key, &kd) + c := meta.FindStatusCondition(kd.Status.Conditions, meta2.ReadyCondition) + return c != nil && c.Reason == kluctlv1.WaitingForLegacyMigrationReason + } + + suite.Run("wait for migration to start", func() { + g.Eventually(checkMigrationStatus, timeout, time.Second).Should(BeTrue()) + }) + + suite.Run("stays in migration state", func() { + suite.triggerReconcile(key) + g.Consistently(checkMigrationStatus, 3*time.Second, time.Second).Should(BeTrue()) + }) + + suite.Run("legacy controller marks the deployment with readyForMigration", func() { + var obj unstructured.Unstructured + obj.SetGroupVersionKind(legacyGvk) + err := suite.k.Client.Get(context.TODO(), key, &obj) + g.Expect(err).To(Succeed()) + + _ = unstructured.SetNestedField(obj.Object, true, "status", "readyForMigration") + err = suite.k.Client.Status().Update(context.TODO(), &obj) + g.Expect(err).To(Succeed()) + }) + + suite.Run("wait for migration to end", func() { + g.Eventually(checkMigrationStatus, timeout, time.Second).Should(BeFalse()) + }) + + suite.Run("initial deployment", func() { + suite.waitForCommit(key, getHeadRevision(suite.T(), p)) + }) +} diff --git a/e2e/seal_test.go b/e2e/seal_test.go index e0cfab88d..aa21459fb 100644 --- a/e2e/seal_test.go +++ b/e2e/seal_test.go @@ -216,7 +216,7 @@ func TestSeal_WithBootstrap(t *testing.T) { sealedSecretsDir := p.LocalProjectDir() assert.FileExists(t, filepath.Join(sealedSecretsDir, ".sealed-secrets/secret-deployment/test-target/secret-secret.yml")) - test_resources.ApplyYaml("sealed-secrets.yaml", k) + test_resources.ApplyYaml(t, "sealed-secrets.yaml", k) p.KluctlMust("deploy", "--yes", "-t", "test-target") diff --git a/e2e/test_resources/flux.kluctl.io_kluctldeployments.yaml b/e2e/test_resources/flux.kluctl.io_kluctldeployments.yaml new file mode 100644 index 000000000..fe7d3b8d6 --- /dev/null +++ b/e2e/test_resources/flux.kluctl.io_kluctldeployments.yaml @@ -0,0 +1,659 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: kluctldeployments.flux.kluctl.io +spec: + group: flux.kluctl.io + names: + kind: KluctlDeployment + listKind: KluctlDeploymentList + plural: kluctldeployments + singular: kluctldeployment + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.dryRun + name: DryRun + type: boolean + - jsonPath: .status.lastDeployResult.time + name: Deployed + type: date + - jsonPath: .status.lastPruneResult.time + name: Pruned + type: date + - jsonPath: .status.lastValidateResult.time + name: Validated + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: KluctlDeployment is the Schema for the kluctldeployments API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + abortOnError: + default: false + description: ForceReplaceOnError instructs kluctl to abort deployments + immediately when something fails. Equivalent to using '--abort-on-error' + when calling kluctl. + type: boolean + args: + description: Args specifies dynamic target args. + type: object + x-kubernetes-preserve-unknown-fields: true + context: + description: If specified, overrides the context to be used. This + will effectively make kluctl ignore the context specified in the + target. + type: string + decryption: + description: Decrypt Kubernetes secrets before applying them on the + cluster. + properties: + provider: + description: Provider is the name of the decryption engine. + enum: + - sops + type: string + secretRef: + description: The secret name containing the private OpenPGP keys + used for decryption. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccount: + description: ServiceAccount specifies the service account used + to authenticate against cloud providers. This is currently only + usable for AWS KMS keys. The specified service account will + be used to authenticate to AWS by signing a token in an IRSA + compliant way. + type: string + required: + - provider + type: object + delete: + default: false + description: Delete enables deletion of the specified target when + the KluctlDeployment object gets deleted. + type: boolean + deployInterval: + description: DeployInterval specifies the interval at which to deploy + the KluctlDeployment. It defaults to the Interval value, meaning + that it will re-deploy on every reconciliation. If you set DeployInterval + to a different value, + pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ + type: string + deployMode: + default: full-deploy + description: DeployMode specifies what deploy mode should be used. + The options 'full-deploy' and 'poke-images' are supported. With + 'poke images' option, only the images from the fixed images are + exchanged and no complete deployment is triggered. + enum: + - full-deploy + - poke-images + type: string + deployOnChanges: + default: true + description: DeployOnChanges will cause a re-deployment whenever the + rendered resources change in the deployment. This check is performed + on every reconciliation. This means that a deployment will be triggered + even before the DeployInterval has passed in case something has + changed in the rendered resources. + type: boolean + dryRun: + default: false + description: DryRun instructs kluctl to run everything in dry-run + mode. Equivalent to using '--dry-run' when calling kluctl. + type: boolean + excludeDeploymentDirs: + description: ExcludeDeploymentDirs instructs kluctl to exclude deployments + with the given dir. Equivalent to using '--exclude-deployment-dir' + when calling kluctl. + items: + type: string + type: array + excludeTags: + description: ExcludeTags instructs kluctl to exclude deployments with + given tags. Equivalent to using '--exclude-tag' when calling kluctl. + items: + type: string + type: array + forceApply: + default: false + description: ForceApply instructs kluctl to force-apply in case of + SSA conflicts. Equivalent to using '--force-apply' when calling + kluctl. + type: boolean + forceReplaceOnError: + default: false + description: ForceReplaceOnError instructs kluctl to force-replace + resources in case a normal replace fails. Equivalent to using '--force-replace-on-error' + when calling kluctl. + type: boolean + helmCredentials: + description: HelmCredentials is a list of Helm credentials used when + non pre-pulled Helm Charts are used inside a Kluctl deployment. + items: + properties: + secretRef: + description: 'SecretRef holds the name of a secret that contains + the Helm credentials. The secret must either contain the fields + `credentialsId` which refers to the credentialsId found in + https://kluctl.io/docs/kluctl/reference/deployments/helm/#private-chart-repositories + or an `url` used to match the credentials found in Kluctl + projects helm-chart.yaml files. The secret can either container + basic authentication credentials via `username` and `password` + or TLS authentication via `certFile` and `keyFile`. `caFile` + can be specified to override the CA to use while contacting + the repository. The secret can also contain `insecureSkipTlsVerify: + "true"`, which will disable TLS verification. `passCredentialsAll: + "true"` can be specified to make the controller pass credentials + to all requests, even if the hostname changes in-between.' + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: object + type: array + images: + description: Images contains a list of fixed image overrides. Equivalent + to using '--fixed-images-file' when calling kluctl. + items: + properties: + container: + type: string + deployTags: + items: + type: string + type: array + deployedImage: + type: string + deployment: + type: string + deploymentDir: + type: string + image: + type: string + namespace: + type: string + object: + description: ObjectRef contains the information necessary to + locate a resource within a cluster. + properties: + group: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - namespace + - version + type: object + registryImage: + type: string + resultImage: + type: string + versionFilter: + type: string + required: + - image + - resultImage + type: object + type: array + includeDeploymentDirs: + description: IncludeDeploymentDirs instructs kluctl to only include + deployments with the given dir. Equivalent to using '--include-deployment-dir' + when calling kluctl. + items: + type: string + type: array + includeTags: + description: IncludeTags instructs kluctl to only include deployments + with given tags. Equivalent to using '--include-tag' when calling + kluctl. + items: + type: string + type: array + interval: + description: The interval at which to reconcile the KluctlDeployment. + By default, the controller will re-deploy and validate the deployment + on each reconciliation. To override this behavior, change the DeployInterval + and/or ValidateInterval values. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: The KubeConfig for deploying to the target cluster. Specifies + the kubeconfig to be used when invoking kluctl. Contexts in this + kubeconfig must match the context found in the kluctl target. As + an alternative, specify the context to be used via 'context' + properties: + secretRef: + description: SecretRef holds the name of a secret that contains + a key with the kubeconfig file as the value. If no key is set, + the key will default to 'value'. The secret must be in the same + namespace as the Kustomization. It is recommended that the kubeconfig + is self-contained, and the secret is regularly updated if credentials + such as a cloud-access-token expire. Cloud specific `cmd-path` + auth helpers will not function without adding binaries and credentials + to the Pod that is responsible for reconciling the KluctlDeployment. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific + default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + type: object + noWait: + default: false + description: NoWait instructs kluctl to not wait for any resources + to become ready, including hooks. Equivalent to using '--no-wait' + when calling kluctl. + type: boolean + path: + description: 'Path to the directory containing the .kluctl.yaml file, + or the Defaults to ''None'', which translates to the root path of + the SourceRef. Deprecated: Use source.path instead' + type: string + prune: + default: false + description: Prune enables pruning after deploying. + type: boolean + registrySecrets: + description: DEPRECATED RegistrySecrets is a list of secret references + to be used for image registry authentication. The secrets must either + have ".dockerconfigjson" included or "registry", "username" and + "password". Additionally, "caFile" and "insecure" can be specified. + Kluctl has deprecated querying the registry at deploy time and thus + this field is also deprecated. + items: + description: LocalObjectReference contains enough information to + locate the referenced Kubernetes resource object. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + type: array + renameContexts: + description: RenameContexts specifies a list of context rename operations. + This is useful when the kluctl target's context does not match with + the contexts found in the kubeconfig while deploying. This is the + case when using kubeconfigs generated from service accounts, in + which case the context name is always "default". + items: + description: RenameContext specifies a single rename of a context + properties: + newContext: + description: NewContext is the new name of the context + type: string + oldContext: + description: OldContext is the name of the context to be renamed + type: string + required: + - newContext + - oldContext + type: object + type: array + replaceOnError: + default: false + description: ReplaceOnError instructs kluctl to replace resources + on error. Equivalent to using '--replace-on-error' when calling + kluctl. + type: boolean + retryInterval: + description: The interval at which to retry a previously failed reconciliation. + When not specified, the controller uses the Interval value to retry + failures. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + serviceAccountName: + description: The name of the Kubernetes service account to use while + deploying. If not specified, the default service account is used. + type: string + source: + description: Specifies the project source location + properties: + path: + description: Path specifies the sub-directory to be used as project + directory + type: string + ref: + description: Ref specifies the branch, tag or commit that should + be used. If omitted, the default branch of the repo is used. + properties: + branch: + description: Branch to filter for. Can also be a regex. + type: string + tag: + description: Branch to filter for. Can also be a regex. + type: string + type: object + secretRef: + description: SecretRef specifies the Secret containing authentication + credentials for the git repository. For HTTPS repositories the + Secret must contain 'username' and 'password' fields. For SSH + repositories the Secret must contain 'identity' and 'known_hosts' + fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + url: + description: Url specifies the Git url where the project source + is located + type: string + required: + - url + type: object + sourceRef: + description: 'Reference of the source where the kluctl project is. + The authentication secrets from the source are also used to authenticate + dependent git repositories which are cloned while deploying the + kluctl project. Deprecated: Use source instead' + properties: + apiVersion: + description: API version of the referent, if not specified the + Kubernetes preferred version will be used. + type: string + kind: + description: Kind of the referent. + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - kind + - name + type: object + suspend: + description: This flag tells the controller to suspend subsequent + kluctl executions, it does not apply to already started executions. + Defaults to false. + type: boolean + target: + description: Target specifies the kluctl target to deploy. If not + specified, an empty target is used that has no name and no context. + Use 'TargetName' and 'Context' to specify the name and context in + that case. + maxLength: 63 + minLength: 1 + type: string + targetNameOverride: + description: TargetNameOverride sets or overrides the target name. + This is especially useful when deployment without a target. + maxLength: 63 + minLength: 1 + type: string + timeout: + description: Timeout for all operations. Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + updateImages: + default: false + description: DEPRECATED UpdateImages instructs kluctl to update dynamic + images. Equivalent to using '-u' when calling kluctl. Setting this + field to true is deprecated. + type: boolean + validate: + default: true + description: Validate enables validation after deploying + type: boolean + validateInterval: + description: ValidateInterval specifies the interval at which to validate + the KluctlDeployment. Validation is performed the same way as with + 'kluctl validate -t '. Defaults to the same value as specified + in Interval. Validate is also performed whenever a deployment is + performed, independent of the value of ValidateInterval + pattern: ^(([0-9]+(\.[0-9]+)?(ms|s|m|h))+)|never$ + type: string + required: + - interval + type: object + status: + description: KluctlDeploymentStatus defines the observed state of KluctlDeployment + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: 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. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + 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])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + discriminator: + description: Discriminator is the discriminator found in the target + when the last deployment was done. This is used to perform cleanup/deletion + in case the KluctlDeployment project is deleted + type: string + lastAttemptedRevision: + description: LastAttemptedRevision is the revision of the last reconciliation + attempt. + type: string + lastDeployResult: + description: LastDeployResult is the result of the last deploy command + properties: + error: + type: string + objectsHash: + description: ObjectsHash is the hash of all rendered objects + type: string + rawResult: + type: string + revision: + description: Revision is the source revision. Please note that + kluctl projects have dependent git repositories which are not + considered in the source revision + type: string + target: + type: string + targetNameOverride: + type: string + time: + description: AttemptedAt is the time when the attempt was performed + format: date-time + type: string + required: + - time + type: object + lastHandledDeployAt: + type: string + lastHandledReconcileAt: + description: LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value can + be detected. + type: string + lastPruneResult: + description: LastDeployResult is the result of the last prune command + properties: + error: + type: string + objectsHash: + description: ObjectsHash is the hash of all rendered objects + type: string + rawResult: + type: string + revision: + description: Revision is the source revision. Please note that + kluctl projects have dependent git repositories which are not + considered in the source revision + type: string + target: + type: string + targetNameOverride: + type: string + time: + description: AttemptedAt is the time when the attempt was performed + format: date-time + type: string + required: + - time + type: object + lastValidateResult: + description: LastValidateResult is the result of the last validate + command + properties: + error: + type: string + objectsHash: + description: ObjectsHash is the hash of all rendered objects + type: string + rawResult: + type: string + revision: + description: Revision is the source revision. Please note that + kluctl projects have dependent git repositories which are not + considered in the source revision + type: string + target: + type: string + targetNameOverride: + type: string + time: + description: AttemptedAt is the time when the attempt was performed + format: date-time + type: string + required: + - time + type: object + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + rawTarget: + type: string + readyForMigration: + description: ReadyForMigration is used to signal the new controller + that this object is handled by a legacy controller version that + will honor the existence of KluctlDeployment objects from the gitops.kluctl.io + group. + type: boolean + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/e2e/test_resources/resources.go b/e2e/test_resources/resources.go index 23dad66c9..0d7b050c2 100644 --- a/e2e/test_resources/resources.go +++ b/e2e/test_resources/resources.go @@ -3,6 +3,7 @@ package test_resources import ( "context" "embed" + "fmt" "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/kluctl/kluctl/v2/pkg/validation" @@ -14,6 +15,7 @@ import ( "sort" "strings" "sync" + "testing" "time" ) @@ -79,20 +81,28 @@ func waitReadiness(k *test_utils.EnvTestCluster, x *uo.UnstructuredObject) { } } -func ApplyYaml(name string, k *test_utils.EnvTestCluster) { +func ApplyYaml(t *testing.T, name string, k *test_utils.EnvTestCluster) { tmpFile := GetYamlTmpFile(name) defer os.Remove(tmpFile) + doPanic := func(err error) { + if t != nil { + t.Fatal(err) + } else { + panic(err) + } + } + docs, err := yaml.ReadYamlAllFile(tmpFile) if err != nil { - panic(err) + doPanic(err) } var objects []*uo.UnstructuredObject for _, doc := range docs { m, ok := doc.(map[string]any) if !ok { - panic("not a map!") + doPanic(fmt.Errorf("not a map!")) } x := uo.FromMap(m) objects = append(objects, x) @@ -119,7 +129,7 @@ func ApplyYaml(name string, k *test_utils.EnvTestCluster) { data, err := yaml.WriteYamlBytes(x) if err != nil { - panic(err) + doPanic(err) } gvr := guessGVR(x.GetK8sGVK()) @@ -129,7 +139,7 @@ func ApplyYaml(name string, k *test_utils.EnvTestCluster) { FieldManager: "e2e-tests", }) if err != nil { - panic(err) + doPanic(err) } // wait for CRDs to get accepted From c31d6a201833cb1f615de406566a94647eb7ff70 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 15:37:59 +0200 Subject: [PATCH 1072/2268] fix: Fix inclusion of relative base dirs when the project is not at the repo root (#525) * refactor: Use LoadArgs directly instead of copying the ProjectDir into LoadedKluctlProject * fix: Allow all includes inside the same repo root * tests: Add tests for local includes --- cmd/kluctl/commands/cmd_seal.go | 2 +- e2e/deployment_items_test.go | 69 ++++++++++++++++++++++++++++ e2e/test-utils/project.go | 23 +++++++--- pkg/kluctl_project/project.go | 2 - pkg/kluctl_project/project_load.go | 8 ++-- pkg/kluctl_project/target_context.go | 10 ++-- 6 files changed, 96 insertions(+), 18 deletions(-) diff --git a/cmd/kluctl/commands/cmd_seal.go b/cmd/kluctl/commands/cmd_seal.go index c081a61f8..b272c5966 100644 --- a/cmd/kluctl/commands/cmd_seal.go +++ b/cmd/kluctl/commands/cmd_seal.go @@ -93,7 +93,7 @@ func (cmd *sealCmd) loadCert(cmdCtx *commandCtx) (*x509.Certificate, error) { var certFile string if sealingConfig != nil && sealingConfig.CertFile != nil { - path, err := securejoin.SecureJoin(cmdCtx.targetCtx.KluctlProject.ProjectDir, *sealingConfig.CertFile) + path, err := securejoin.SecureJoin(cmdCtx.targetCtx.KluctlProject.LoadArgs.ProjectDir, *sealingConfig.CertFile) if err != nil { return nil, err } diff --git a/e2e/deployment_items_test.go b/e2e/deployment_items_test.go index 855e20120..9f8dfdd96 100644 --- a/e2e/deployment_items_test.go +++ b/e2e/deployment_items_test.go @@ -5,6 +5,7 @@ import ( "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" + "path/filepath" "testing" ) @@ -190,3 +191,71 @@ func TestTemplateIgnore(t *testing.T) { "k1": `{{ "a" }}`, }, cm3.Object["data"]) } + +func testLocalIncludes(t *testing.T, projectDir string) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t, test_utils.WithBareProject()) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateDeploymentYaml("base", func(o *uo.UnstructuredObject) error { + *o = *uo.FromMap(map[string]interface{}{ + "deployments": []map[string]any{ + {"path": "cm"}, + }, + }) + return nil + }) + p.UpdateYaml("base/cm/cm.yaml", func(o *uo.UnstructuredObject) error { + *o = *createConfigMapObject(map[string]string{ + "d1": "v1", + }, resourceOpts{name: "{{ name }}", namespace: p.TestSlug()}) + return nil + }, "") + + baseDir, _ := filepath.Rel(filepath.Join(p.LocalProjectDir(), projectDir), filepath.Join(p.LocalRepoDir(), "base")) + baseDir = filepath.ToSlash(baseDir) + + p.UpdateDeploymentYaml(projectDir, func(o *uo.UnstructuredObject) error { + *o = *uo.FromMap(map[string]interface{}{ + "deployments": []map[string]any{ + { + "include": baseDir, + "vars": []map[string]any{ + { + "values": map[string]any{ + "name": "cm-inc1", + }, + }, + }, + }, + { + "include": baseDir, + "vars": []map[string]any{ + { + "values": map[string]any{ + "name": "cm-inc2", + }, + }, + }, + }, + }, + }) + return nil + }) + + p.KluctlMust("deploy", "--yes", "--project-dir", filepath.Join(p.LocalProjectDir(), projectDir)) + assertConfigMapExists(t, k, p.TestSlug(), "cm-inc1") + assertConfigMapExists(t, k, p.TestSlug(), "cm-inc2") +} + +func TestIncludeLocalFromRoot(t *testing.T) { + testLocalIncludes(t, ".") +} + +func TestIncludeLocalFromSubdir(t *testing.T) { + testLocalIncludes(t, "foo") +} diff --git a/e2e/test-utils/project.go b/e2e/test-utils/project.go index e8c33d1ec..358fe1dfe 100644 --- a/e2e/test-utils/project.go +++ b/e2e/test-utils/project.go @@ -26,6 +26,7 @@ type TestProject struct { extraEnv []string useProcess bool + bare bool gitServer *git2.TestGitServer gitRepoName string @@ -58,6 +59,12 @@ func WithGitSubDir(subDir string) TestProjectOption { } } +func WithBareProject() TestProjectOption { + return func(p *TestProject) { + p.bare = true + } +} + func NewTestProject(t *testing.T, opts ...TestProjectOption) *TestProject { p := &TestProject{ t: t, @@ -73,13 +80,15 @@ func NewTestProject(t *testing.T, opts ...TestProjectOption) *TestProject { } p.gitServer.GitInit(p.gitRepoName) - p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { - _ = o.SetNestedField(fmt.Sprintf("%s-{{ target.name or 'no-name' }}", rand.String(16)), "discriminator") - return nil - }) - p.UpdateDeploymentYaml(".", func(c *uo.UnstructuredObject) error { - return nil - }) + if !p.bare { + p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { + _ = o.SetNestedField(fmt.Sprintf("%s-{{ target.name or 'no-name' }}", rand.String(16)), "discriminator") + return nil + }) + p.UpdateDeploymentYaml(".", func(c *uo.UnstructuredObject) error { + return nil + }) + } return p } diff --git a/pkg/kluctl_project/project.go b/pkg/kluctl_project/project.go index 7afc4918f..50aea59fc 100644 --- a/pkg/kluctl_project/project.go +++ b/pkg/kluctl_project/project.go @@ -18,8 +18,6 @@ type LoadedKluctlProject struct { TmpDir string - ProjectDir string - sealedSecretsDir string Config types2.KluctlProject diff --git a/pkg/kluctl_project/project_load.go b/pkg/kluctl_project/project_load.go index 5ed7aa53f..04493fb1d 100644 --- a/pkg/kluctl_project/project_load.go +++ b/pkg/kluctl_project/project_load.go @@ -27,7 +27,7 @@ type LoadKluctlProjectArgs struct { func (c *LoadedKluctlProject) getConfigPath() string { configPath := c.LoadArgs.ProjectConfig if configPath == "" { - p := yaml.FixPathExt(filepath.Join(c.ProjectDir, ".kluctl.yml")) + p := yaml.FixPathExt(filepath.Join(c.LoadArgs.ProjectDir, ".kluctl.yml")) if utils.IsFile(p) { configPath = p } @@ -38,10 +38,8 @@ func (c *LoadedKluctlProject) getConfigPath() string { func (c *LoadedKluctlProject) loadKluctlProject() error { var err error - c.ProjectDir = c.LoadArgs.ProjectDir - if c.LoadArgs.RepoRoot != "" { - err = utils.CheckInDir(c.LoadArgs.RepoRoot, c.ProjectDir) + err = utils.CheckInDir(c.LoadArgs.RepoRoot, c.LoadArgs.ProjectDir) if err != nil { return err } @@ -59,7 +57,7 @@ func (c *LoadedKluctlProject) loadKluctlProject() error { s := status.Start(c.ctx, "Loading kluctl project") defer s.Failed() - c.sealedSecretsDir = filepath.Join(c.ProjectDir, ".sealed-secrets") + c.sealedSecretsDir = filepath.Join(c.LoadArgs.ProjectDir, ".sealed-secrets") sealedSecretsUsed := false if c.Config.SecretsConfig != nil { diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 93d92ca94..4761c9a0d 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -44,7 +44,11 @@ type TargetContextParams struct { } func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params TargetContextParams) (*TargetContext, error) { - deploymentDir, err := filepath.Abs(p.ProjectDir) + repoRoot, err := filepath.Abs(p.LoadArgs.RepoRoot) + if err != nil { + return nil, err + } + relProjectDir, err := filepath.Rel(repoRoot, p.LoadArgs.ProjectDir) if err != nil { return nil, err } @@ -129,7 +133,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe DefaultSealedSecretsOutputPattern: target.Name, } - d, err := deployment.NewDeploymentProject(dctx, varsCtx, deployment.NewSource(deploymentDir), ".", nil) + d, err := deployment.NewDeploymentProject(dctx, varsCtx, deployment.NewSource(repoRoot), relProjectDir, nil) if err != nil { return nil, err } @@ -220,7 +224,7 @@ func (p *LoadedKluctlProject) findSecretsEntry(name string) (*types.SecretSet, e } func (p *LoadedKluctlProject) loadSecrets(target *types.Target, varsCtx *vars.VarsCtx, varsLoader *vars.VarsLoader) error { - searchDirs := []string{p.ProjectDir} + searchDirs := []string{p.LoadArgs.ProjectDir} for _, secretSetName := range target.SealingConfig.SecretSets { secretEntry, err := p.findSecretsEntry(secretSetName) From 1e8d63d562b3584294698912f45f33427b9ca036 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 15:48:32 +0200 Subject: [PATCH 1073/2268] build: Preparing release v2.20.0 --- install/controller/.kluctl.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/controller/.kluctl.yaml b/install/controller/.kluctl.yaml index 7f7283c68..481c21a87 100644 --- a/install/controller/.kluctl.yaml +++ b/install/controller/.kluctl.yaml @@ -2,4 +2,4 @@ discriminator: kluctl.io-controller args: - name: controller_version - default: v0.0.0 \ No newline at end of file + default: v2.20.0 \ No newline at end of file From 600160b98a5ddd0c30d2c1524966e24f89dd7f1b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 16:48:12 +0200 Subject: [PATCH 1074/2268] docs: Remove mentions of flux from source --- api/v1beta1/doc.go | 2 +- pkg/controllers/predicates.go | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/api/v1beta1/doc.go b/api/v1beta1/doc.go index 7dae3af55..5d3a94b0e 100644 --- a/api/v1beta1/doc.go +++ b/api/v1beta1/doc.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1beta1 contains API Schema definitions for the flux.kluctl.io v1beta1 API group. +// Package v1beta1 contains API Schema definitions for the gitops.kluctl.io v1beta1 API group. // +kubebuilder:object:generate=true // +groupName=gitops.kluctl.io package v1beta1 diff --git a/pkg/controllers/predicates.go b/pkg/controllers/predicates.go index 1c6f52c30..feec05a2c 100644 --- a/pkg/controllers/predicates.go +++ b/pkg/controllers/predicates.go @@ -1,19 +1,3 @@ -/* -Copyright 2020 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - package controllers import ( From d1c2ef53fe21160bd3f10d52ac481c46dfc9dccd Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 16:48:22 +0200 Subject: [PATCH 1075/2268] chore: Remove unused code --- cmd/kluctl/args/flux.go | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 cmd/kluctl/args/flux.go diff --git a/cmd/kluctl/args/flux.go b/cmd/kluctl/args/flux.go deleted file mode 100644 index a1307a16a..000000000 --- a/cmd/kluctl/args/flux.go +++ /dev/null @@ -1,34 +0,0 @@ -package args - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" -) - -type KluctlDeploymentFlags struct { - KluctlDeployment string `group:"flux" short:"k" help:"Name of the KluctlDeployment to interact with"` - Namespace string `group:"flux" short:"n" help:"Namespace where KluctlDeployment is located"` - WithSource bool `group:"flux" help:"--with-source will annotate Source object as well, triggering pulling"` - NoWait bool `group:"flux" help:"Don't wait for objects readiness'"` -} - -var KluctlDeploymentGVK = schema.GroupVersionKind{ - Group: "flux.kluctl.io", - Version: "v1alpha1", - Kind: "KluctlDeployment", -} - -var GitRepositoryGVK = schema.GroupVersionKind{ - Group: "source.toolkit.fluxcd.io", - Version: "v1beta2", - Kind: "GitRepository", -} - -func (cmd *KluctlDeploymentFlags) VerifyFlags() bool { - if cmd.KluctlDeployment == "" { - return false - } - if cmd.Namespace == "" { - cmd.Namespace = "default" - } - return true -} From 68deddec416e75d0bbcd88d166f876194541bce5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 26 May 2023 16:48:41 +0200 Subject: [PATCH 1076/2268] docs: Update readme to mention that flux-kluctl-controller is deprecated --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 51c58facb..33ed42f81 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,16 @@ Kluctl is centered around "targets", which can be a cluster or a specific enviro or multiple clusters. Targets can be deployed, diffed, pruned, deleted, and so on. The idea is to have the same set of operations for every target, no matter how simple or complex the deployment and/or target is. -Kluctl does not depend on external operators/controllers and allows to use the same deployment wherever you want, +Kluctl does not strictly depend on a controller and allows to use the same deployment wherever you want, as long as access to the kluctl project and clusters is available. This means, that you can use it from your local machine, from your CI/CD pipelines or any automation platform/system that allows to call custom tools. -Flux support is in alpha stadium and available via the [flux-kluctl-controller](https://github.com/kluctl/flux-kluctl-controller). +If you want to follow a pull based GitOps flow, then you can use the Kluctl Controller, which then allows you to use +`KluctlDeployment` custom resources to define your Kluctl deployments. + +Please note: GitOps support was previously implemented via the now deprecated [flux-kluctl-controller](https://github.com/kluctl/flux-kluctl-controller). +Historically, the flux-kluctl-controller depended on the Flux ecosystem (the source-controller to be specific), which +has changed in the meantime, meaning that it runs completely independent and thus is not part of the Flux ecosystem anymore. ## What can I do with Kluctl? From 88a0bf0b67ae351213556d1b71eaca460c56424f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 27 May 2023 23:52:01 +0200 Subject: [PATCH 1077/2268] docs: Add installation instructions for new controller --- docs/installation.md | 38 ++++++++++++++++++++++++++++++++------ hack/prepare-release.sh | 2 +- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index bd42145bf..656d3d93c 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -10,13 +10,17 @@ description: "Installing kluctl." # Installation -## Binaries +Kluctl is available as a CLI and as a GitOps controller. + +## Installing the CLI + +### Binaries The kluctl CLI is available as a binary executable for all major platforms, the binaries can be downloaded form GitHub [releases page](https://github.com/kluctl/kluctl/releases). -## Installation with Homebrew +### Installation with Homebrew With [Homebrew](https://brew.sh) for macOS and Linux: @@ -24,7 +28,7 @@ With [Homebrew](https://brew.sh) for macOS and Linux: brew install kluctl/tap/kluctl ``` -## Installation with Bash +### Installation with Bash With [Bash](https://www.gnu.org/software/bash/) for macOS and Linux: @@ -38,7 +42,7 @@ The install script does the following: * copies the kluctl binary to `/usr/local/bin` * removes the temporary directory -## Build from source +### Build from source Clone the repository: @@ -61,7 +65,7 @@ Run the binary: -## Container images +### Container images A container image with `kluctl` is available on GitHub: * `ghcr.io/kluctl/kluctl:` + +## Installing the GitOps Controller + +The controller can be installed via two available options. + +### Using the "install" sub-command + +The [`kluctl controller install`](./reference/commands/controller-install.md) command can be used to install the +controller. It will use an embedded version of the Controller Kluctl deployment project +found [here](https://github.com/kluctl/kluctl/tree/main/install/controller). + +### Using a Kluctl deployment + +To manage and install the controller via Kluctl, you can use a Git include in your own deployment: + +```yaml +deployments: + - git: + url: https://github.com/kluctl/kluctl.git + subDir: install/controller + ref: v0.0.0 +``` diff --git a/hack/prepare-release.sh b/hack/prepare-release.sh index 609c006d5..496c9b6bd 100755 --- a/hack/prepare-release.sh +++ b/hack/prepare-release.sh @@ -24,7 +24,7 @@ fi echo VERSION=$VERSION -FILES="install/controller/.kluctl.yaml" +FILES="install/controller/.kluctl.yaml docs/installation.md" for f in $FILES; do cat $f | sed "s/$VERSION_REGEX_SED/$VERSION/g" > $f.tmp From 3fffac350b59b01ce710b4147a176e38fd28ad15 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 28 May 2023 00:10:14 +0200 Subject: [PATCH 1078/2268] docs: Add GitOps docs --- docs/reference/README.md | 3 +- docs/reference/gitops/README.md | 107 ++++ docs/reference/gitops/metrics/README.md | 9 + .../gitops/metrics/v1beta1/README.md | 19 + .../v1beta1/kluctldeployment_controller.md | 29 + docs/reference/gitops/migration/README.md | 71 +++ docs/reference/gitops/spec/README.md | 9 + docs/reference/gitops/spec/v1beta1/README.md | 25 + .../gitops/spec/v1beta1/kluctldeployment.md | 551 ++++++++++++++++++ 9 files changed, 822 insertions(+), 1 deletion(-) create mode 100644 docs/reference/gitops/README.md create mode 100644 docs/reference/gitops/metrics/README.md create mode 100644 docs/reference/gitops/metrics/v1beta1/README.md create mode 100644 docs/reference/gitops/metrics/v1beta1/kluctldeployment_controller.md create mode 100644 docs/reference/gitops/migration/README.md create mode 100644 docs/reference/gitops/spec/README.md create mode 100644 docs/reference/gitops/spec/v1beta1/README.md create mode 100644 docs/reference/gitops/spec/v1beta1/kluctldeployment.md diff --git a/docs/reference/README.md b/docs/reference/README.md index 19695290e..729f8fb45 100644 --- a/docs/reference/README.md +++ b/docs/reference/README.md @@ -13,4 +13,5 @@ weight: 110 1. [.kluctl.yaml](./kluctl-project) 2. [Deployments](./deployments) -3. [Kluctl Commands](./commands) +3. [GitOps](./gitops) +4. [Kluctl Commands](./commands) diff --git a/docs/reference/gitops/README.md b/docs/reference/gitops/README.md new file mode 100644 index 000000000..63c6fd4ae --- /dev/null +++ b/docs/reference/gitops/README.md @@ -0,0 +1,107 @@ + + +# GitOps + +GitOps in Kluctl is implemented through the Kluctl Controller, which must be [installed](../../installation.md#installing-the-gitops-controller) +to your target cluster. + +The Kluctl Controller is a Kubernetes operator which implements the [`KluctlDeployment`](./spec/v1beta1/kluctldeployment.md#kluctldeployment) +custom resource. This resource allows to define a Kluctl deployment that should be constantly reconciled (re-deployed) +whenever the deployment changes. + +## Motivation and Philosophy + +Kluctl tries its best to implement all its features via [Kluctl projects](../kluctl-project/README.md), meaning that +the deployments are, at least theoretically, deployable from the CLI at all times. The Kluctl Controller does not +add functionality on top of that and thus does not couple your deployments to a running controller. + +Instead, the `KluctlDeployment` custom resource acts as an interface to the deployment. It tries to offer the same +functionality and options as offered by the CLI, but through a custom resource instead of a CLI invocation. + +As an example, arguments passed via `-a arg=value` can be passed to the custom resource via the `spec.args` field. +The same applies to options like `--dry-run`, which equals to `spec.dryRun: true` in the custom resource. Check the +documentation of [`KluctlDeployment`](./spec/v1beta1/kluctldeployment.md#spec-fields) for more such options. + +## Installation + +Installation instructions can be found [here](../../installation.md#installing-the-gitops-controller) + +## Design + +The reconciliation process consists of multiple steps which are constantly repeated: + +- **clone** the root Kluctl project via Git +- **prepare** the Kluctl deployment by rendering the whole deployment +- **deploy** the specified target via [kluctl deploy](../commands/deploy.md) if the rendered resources changed +- **prune** orphaned objects via [kluctl prune](../commands/prune.md) +- **validate** the deployment status via [kluctl validate](../commands/validate.md) + +Reconciliation is performed on a configurable [interval](./spec/v1beta1/kluctldeployment.md#interval). A single +reconciliation iteration will first clone and prepare the project. Only when the rendered resources indicate a change +(by using a hash internally), the controller will initiate a deployment. After the deployment, the controller will +also perform pruning (only if [prune: true](./spec/v1beta1/kluctldeployment.md#prune) is set). + +When the `KluctlDeployment` is removed from the cluster, the controller cal also delete all resources belonging to +that deployment. This will only happen if [delete: true](./spec/v1beta1/kluctldeployment.md#delete) is set. + +Deletion and pruning is based on the [discriminator](../kluctl-project/README.md#discriminator) of the given target. + +A `KluctlDeployment` can be [suspended](./spec/v1beta1/kluctldeployment.md#suspend). While suspended, the controller +will skip reconciliation, including deletion and pruning. + +The API design of the controller can be found at [kluctldeployment.gitops.kluctl.io/v1beta1](./spec/v1beta1/README.md). + +## Example + +After installing the Kluctl Controller, we can create a `KluctlDeployment` that automatically deploys the +[Microservices Demo](https://kluctl.io/docs/guides/tutorials/microservices-demo/3-templating-and-multi-env/). + +Create a KluctlDeployment that uses the demo project source to deploy the `test` target to the same cluster that the +controller runs on. + +```yaml +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: microservices-demo-test + namespace: kluctl-system +spec: + interval: 10m + source: + url: https://github.com/kluctl/kluctl-examples.git + path: "./microservices-demo/3-templating-and-multi-env/" + timeout: 2m + target: test + context: default + prune: true +``` + +This example will deploy a fully-fledged microservices application with multiple backend services, frontends and +databases, all via one single `KluctlDeployment`. + +To deploy the same Kluctl project to another target (e.g. prod), simply create the following resource. + +```yaml +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: microservices-demo-prod + namespace: kluctl-system +spec: + interval: 10m + source: + url: https://github.com/kluctl/kluctl-examples.git + path: "./microservices-demo/3-templating-and-multi-env/" + timeout: 2m + target: prod + context: default + prune: true +``` diff --git a/docs/reference/gitops/metrics/README.md b/docs/reference/gitops/metrics/README.md new file mode 100644 index 000000000..2ae19f029 --- /dev/null +++ b/docs/reference/gitops/metrics/README.md @@ -0,0 +1,9 @@ + diff --git a/docs/reference/gitops/metrics/v1beta1/README.md b/docs/reference/gitops/metrics/v1beta1/README.md new file mode 100644 index 000000000..d13eb0dc2 --- /dev/null +++ b/docs/reference/gitops/metrics/v1beta1/README.md @@ -0,0 +1,19 @@ + + +# Prometheus Metrics + +The controller exports several metrics in the [OpenMetrics compatible format](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md). +They can be scraped by all sorts of monitoring solutions (e.g. Prometheus) or stored in a database. Because the +controller is based on [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime), all +the [default metrics](https://book.kubebuilder.io/reference/metrics-reference.html) as well as the +following controller-specific custom metrics are exported: + +- [kluctldeployment_controller](kluctldeployment_controller.md) diff --git a/docs/reference/gitops/metrics/v1beta1/kluctldeployment_controller.md b/docs/reference/gitops/metrics/v1beta1/kluctldeployment_controller.md new file mode 100644 index 000000000..9c5163f5f --- /dev/null +++ b/docs/reference/gitops/metrics/v1beta1/kluctldeployment_controller.md @@ -0,0 +1,29 @@ + + +# Exported Metrics References + +| Metrics name | Type | Description | +|-----------------------------|-----------|--------------------------------------------------------------------------------------| +| deployment_duration_seconds | Histogram | How long a single deployment takes in seconds. | +| number_of_changed_objects | Gauge | How many objects have been changed by a single deployment. | +| number_of_deleted_objects | Gauge | How many objects have been deleted by a single deployment. | +| number_of_errors | Gauge | How many errors are related to a single deployment. | +| number_of_images | Gauge | Number of images of a single deployment. | +| number_of_orphan_objects | Gauge | How many orphans are related to a single deployment. | +| number_of_warnings | Gauge | How many warnings are related to a single deployment. | +| prune_duration_seconds | Histogram | How long a single prune takes in seconds. | +| validate_duration_seconds | Histogram | How long a single validate takes in seconds. | +| deployment_interval_seconds | Gauge | The configured deployment interval of a single deployment. | +| dry_run_enabled | Gauge | Is dry-run enabled for a single deployment. | +| last_object_status | Gauge | Last object status of a single deployment. Zero means failure and one means success. | +| prune_enabled | Gauge | Is pruning enabled for a single deployment. | +| delete_enabled | Gauge | Is deletion enabled for a single deployment. | +| source_spec | Gauge | The configured source spec of a single deployment exported via labels. | diff --git a/docs/reference/gitops/migration/README.md b/docs/reference/gitops/migration/README.md new file mode 100644 index 000000000..9fb8d394c --- /dev/null +++ b/docs/reference/gitops/migration/README.md @@ -0,0 +1,71 @@ + + +# Legacy Controller Migration + +Older versions of Kluctl (pre v2.20.0) relied on a legacy version of the Kluctl controller, named +[flux-kluctl-controller](https://github.com/kluctl/flux-kluctl-controller). If you upgraded from such an older +version and were already using `KluctlDeployments` from the `flux.kluctl.io` API group, you must migrate these +deployments to the new `gitops.kluctl.io` group. + +To do this, follow the following steps: + +1. Upgrade the legacy flux-kluctl-controller to at least v0.16.0. This version will introduce a special marker field +into the legacy `KluctlDeployment` status and set it to true. This marker field is used to inform the new Kluctl Controller +that the legacy controller is now aware of the existence of the new controller. +2. If not already done, [install](../../../installation.md#installing-the-gitops-controller) the new Kluctl Controller. +3. To be on the safe side, disable [pruning](https://kluctl.io/docs/flux/spec/v1alpha1/kluctldeployment/#prune) and +[deletion](https://kluctl.io/docs/flux/spec/v1alpha1/kluctldeployment/#delete) for all legacy `KluctlDeployment` objects. +Don't forget to deploy/apply these changes before continuing with the next step. +4. Modify your `KluctlDeployment` manifests to use the `gitops.kluctl.io/v1beta1` as `apiVersion`. It's important +to use the same name and namespace as used in the legacy resources. Also read the [breaking changes](#breaking-changes) +section. +5. Deploy/Apply the modified `KluctlDeployment` resources. +6. At this point, the legacy controller will detect that the `KluctlDeployment` exists twice, once for the legacy +API group/version and once for the new group/version. Based on that knowledge, the legacy controller will stop reconciling +the legacy `KluctlDeployment`. +7. At the same time, the new controller will detect that the legacy `KluctlDeployment` has the marker field set, which +means that the legacy controller is known to honor the new controller's existence. +8. This will lead to the new controller taking over and reconciling the new `KluctlDeployment`. +9. If you disabled deletion/pruning in step 3., you should undo this on the new `KluctlDeployments` now. + +After these steps, the legacy `KluctlDeployment` resources will be excluded from reconciliation by the legacy controller. +This means, you can safely remove/prune the legacy resources. + +# Breaking changes + +There exist some breaking changes between the legacy `flux.kluctl.io/v1alpha1` and `gitops.kluctl.io/v1beta1` custom +resources and controllers. These are: + +### Only deploy when resources change + +The legacy controller did a full deploy on each reconciliation, following the Flux way of reconciliations. This +behaviour was configurable by allowing you to set `spec.deployInterval: never`, which disabled full deployments and +caused the controller to only deploy when the resulting rendered resources actually changed. + +The new controller will behave this way by default, unless you explicitly set `spec.deployInterval` to some interval +value. + +This means, you will have to introduce `spec.deployInterval` in case you expect the controller to behave as before or +remove `spec.deployInterval: never` if you already used the Kluctl specific behavior. + +### renameContexts has been removed + +The `spec.renameContexts` field is not available anymore. Use `spec.context` instead. + +### status will not contain full result anymore + +The legacy controller wrote the full command result (with objects, diffs, ...) into the status field. The new +controller will instead only write a summary of the result. + +# Why no fully automated migration? + +I have decided against a fully automated migration as the move of the API group causes resources to have a different +identity. This can easily lead to unexpected behaviour and does not play well with GitOps. diff --git a/docs/reference/gitops/spec/README.md b/docs/reference/gitops/spec/README.md new file mode 100644 index 000000000..cb95e02f6 --- /dev/null +++ b/docs/reference/gitops/spec/README.md @@ -0,0 +1,9 @@ + diff --git a/docs/reference/gitops/spec/v1beta1/README.md b/docs/reference/gitops/spec/v1beta1/README.md new file mode 100644 index 000000000..19271ca12 --- /dev/null +++ b/docs/reference/gitops/spec/v1beta1/README.md @@ -0,0 +1,25 @@ + + +# gitops.kluctl.io/v1beta1 + +This is the v1beta1 API specification for defining continuous delivery pipelines +of Kluctl Deployments. + +## Specification + +- [KluctlDeployment CRD](kluctldeployment.md) + + [Spec fields](kluctldeployment.md#spec-fields) + + [Reconciliation](kluctldeployment.md#reconciliation) + + [Kubeconfigs and RBAC](kluctldeployment.md#kubeconfigs-and-rbac) + + [Git authentication](kluctldeployment.md#git-authentication) + + [Helm Repository authentication](kluctldeployment.md#helm-repository-authentication) + + [Secrets Decryption](kluctldeployment.md#secrets-decryption) + + [Status](kluctldeployment.md#status) diff --git a/docs/reference/gitops/spec/v1beta1/kluctldeployment.md b/docs/reference/gitops/spec/v1beta1/kluctldeployment.md new file mode 100644 index 000000000..db46f4cf0 --- /dev/null +++ b/docs/reference/gitops/spec/v1beta1/kluctldeployment.md @@ -0,0 +1,551 @@ + + +# KluctlDeployment + +The `KluctlDeployment` API defines a deployment of a [target](../../../kluctl-project/targets) +from a [Kluctl Project](../../../kluctl-project). + +## Example + +```yaml +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: microservices-demo-prod +spec: + interval: 5m + source: + url: https://github.com/kluctl/kluctl-examples.git + path: "./microservices-demo/3-templating-and-multi-env/" + timeout: 2m + target: prod + context: default + prune: true + delete: true +``` + +In the above example a KluctlDeployment is being created that defines the deployment based on the Kluctl project. + +The deployment is performed every 5 minutes. It will deploy the `prod` +[target](../../../kluctl-project/targets) and then prune orphaned objects afterward. + +When the KluctlDeployment gets deleted, `delete: true` will cause the controller to actually delete the target +resources. + +It uses the `default` context provided by the default service account and thus overrides the context specified in the +target definition. + +## Spec fields + +### source + +The KluctlDeployment `spec.source` specifies the source repository to be used. Example: + +```yaml +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: example +spec: + source: + url: https://github.com/kluctl/kluctl-examples.git + path: path/to/project + secretRef: + name: git-credentials + ref: + branch: my-branch + ... +``` + +The `url` specifies the git clone url. It can either be a https or a git/ssh url. Git/Ssh url will require a secret +to be provided with credentials. + +The `path` specifies the sub-directory where the Kluctl project is located. + +The `ref` provides the Git reference to be used. It can either be a branch or a tag. + +See [Git authentication](#git-authentication) for details on authentication. + +### interval +See [Reconciliation](#reconciliation). + +### suspend +See [Reconciliation](#reconciliation). + +### target +`spec.target` specifies the target to be deployed. It must exist in the Kluctl projects +[kluctl.yaml targets](../../../kluctl-project/targets) list. + +This field is optional and can be omitted if the referenced Kluctl project allows deployments without targets. + +### targetNameOverride +`spec.targetNameOverride` will set or override the name of the target. This is equivalent to passing +`--target-name-override` to `kluctl deploy`. + +### context +`spec.context` will override the context used while deploying. This is equivalent to passing `--context` to +`kluctl deploy`. + +### deployMode +By default, the operator will perform a full deployment, which is equivalent to using the `kluctl deploy` command. +As an alternative, the controller can be instructed to only perform a `kluctl poke-images` command. Please +see [poke-images](../../../commands/poke-images.md) for details on the command. To do so, set `spec.deployMode` +field to `poke-images`. + +Example: +``` +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: microservices-demo-prod +spec: + interval: 5m + source: + url: https://github.com/kluctl/kluctl-examples.git + path: "./microservices-demo/3-templating-and-multi-env/" + timeout: 2m + target: prod + context: default + deployMode: poke-images +``` + +### prune + +To enable pruning, set `spec.prune` to `true`. This will cause the controller to run `kluctl prune` after each +successful deployment. + +### delete + +To enable deletion, set `spec.delete` to `true`. This will cause the controller to run `kluctl delete` when the +KluctlDeployment gets deleted. + +### args +`spec.args` is an object representing [arguments](../../../kluctl-project/#args) +passed to the deployment. Example: + +```yaml +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: example +spec: + interval: 5m + source: + url: https://github.com/kluctl/kluctl-examples.git + path: "./microservices-demo/3-templating-and-multi-env/" + timeout: 2m + target: prod + context: default + args: + arg1: value1 + arg2: value2 + arg3: + k1: v1 + k2: v2 +``` + +The above example is equivalent to calling `kluctl deploy -t prod -a arg1=value1 -a arg2=value2`. + +### images +`spec.images` specifies a list of fixed images to be used by +[`image.get_image(...)`](../../../deployments/images#imagesget_image). Example: + +``` +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: example +spec: + interval: 5m + source: + url: https://example.com + timeout: 2m + target: prod + images: + - image: nginx + resultImage: nginx:1.21.6 + namespace: example-namespace + deployment: Deployment/example + - image: registry.gitlab.com/my-org/my-repo/image + resultImage: registry.gitlab.com/my-org/my-repo/image:1.2.3 +``` + +The above example will cause the `images.get_image("nginx")` invocations of the `example` Deployment to return +`nginx:1.21.6`. It will also cause all `images.get_image("registry.gitlab.com/my-org/my-repo/image")` invocations +to return `registry.gitlab.com/my-org/my-repo/image:1.2.3`. + +The fixed images provided here take precedence over the ones provided in the +[target definition](../../../kluctl-project/targets#images). + +`spec.images` is equivalent to calling `kluctl deploy -t prod --fixed-image=nginx:example-namespace:Deployment/example=nginx:1.21.6 ...` +and to `kluctl deploy -t prod --fixed-images-file=fixed-images.yaml` with `fixed-images.yaml` containing: + +```yaml +images: +- image: nginx + resultImage: nginx:1.21.6 + namespace: example-namespace + deployment: Deployment/example +- image: registry.gitlab.com/my-org/my-repo/image + resultImage: registry.gitlab.com/my-org/my-repo/image:1.2.3 +``` + +### dryRun +`spec.dryRun` is a boolean value that turns the deployment into a dry-run deployment. This is equivalent to calling +`kluctl deploy -t prod --dry-run`. + +### noWait +`spec.noWait` is a boolean value that disables all internal waiting (hooks and readiness). This is equivalent to calling +`kluctl deploy -t prod --no-wait`. + +### forceApply +`spec.forceApply` is a boolean value that causes kluctl to solve conflicts via force apply. This is equivalent to calling +`kluctl deploy -t prod --force-apply`. + +### replaceOnError and forceReplaceOnError +`spec.replaceOnError` and `spec.forceReplaceOnError` are both boolean values that cause kluctl to perform a replace +after a failed apply. `forceReplaceOnError` goes a step further and deletes and recreates the object in question. +These are equivalent to calling `kluctl deploy -t prod --replace-on-error` and `kluctl deploy -t prod --force-replace-on-error`. + +### abortOnError +`spec.abortOnError` is a boolean value that causes kluctl to abort as fast as possible in case of errors. This is equivalent to calling +`kluctl deploy -t prod --abort-on-error`. + +### includeTags, excludeTags, includeDeploymentDirs and excludeDeploymentDirs +`spec.includeTags` and `spec.excludeTags` are lists of tags to be used in inclusion/exclusion logic while deploying. +These are equivalent to calling `kluctl deploy -t prod --include-tag ` and `kluctl deploy -t prod --exclude-tag `. + +`spec.includeDeploymentDirs` and `spec.excludeDeploymentDirs` are lists of relative deployment directories to be used in +inclusion/exclusion logic while deploying. These are equivalent to calling `kluctl deploy -t prod --include-tag ` +and `kluctl deploy -t prod --exclude-tag `. + +## Reconciliation + +The KluctlDeployment `spec.interval` tells the controller at which interval to try reconciliations. +The interval time units are `s`, `m` and `h` e.g. `interval: 5m`, the minimum value should be over 60 seconds. + +At each reconciliation run, the controller will check if any rendered objects have been changes since the last +deployment and then perform a new deployment if changes are detected. Changes are tracked via a hash consisting of +all rendered objects. + +To enforce periodic full deployments even if nothing has changed, `spec.deployInterval` can be used to specify an +interval at which forced deployments must be performed by the controller. + +The KluctlDeployment reconciliation can be suspended by setting `spec.suspend` to `true`. + +The controller can be told to reconcile the KluctlDeployment outside of the specified interval +by annotating the KluctlDeployment object with `kluctl.io/request-reconcile`. + +On-demand reconciliation example: + +```bash +kubectl annotate --overwrite kluctldeployment/microservices-demo-prod kluctl.io/request-reconcile="$(date +%s)" +``` + +Similarly, a deployment can be forced even if the source has not changed by using the `kluctl.io/request-deploy` +annotation: + +```bash +kubectl annotate --overwrite kluctldeployment/microservices-demo-prod kluctl.io/request-deploy="$(date +%s)" +``` + + +## Kubeconfigs and RBAC + +As Kluctl is meant to be a CLI-first tool, it expects a kubeconfig to be present while deployments are +performed. The controller will generate such kubeconfigs on-the-fly before performing the actual deployment. + +The kubeconfig can be generated from 3 different sources: +1. The default impersonation service account specified at controller startup (via `--default-service-account`) +2. The service account specified via `spec.serviceAccountName` in the KluctlDeployment +3. The secret specified via `spec.kubeConfig` in the KluctlDeployment. + +The behavior/functionality of 1. and 2. is comparable to how the [kustomize-controller](https://fluxcd.io/docs/components/kustomize/kustomization/#role-based-access-control) +handles impersonation, with the difference that a kubeconfig with a "default" context is created in-between. + +`spec.kubeConfig` will simply load the kubeconfig from `data.value` of the specified secret. + +Kluctl [targets](../../../kluctl-project/targets) specify a context name that is expected to +be present in the kubeconfig while deploying. As the context found in the generated kubeconfig does not necessarily +have the correct name, `spec.context` can be used to while deploying. This is especially useful +when using service account based kubeconfigs, as these always have the same context with the name "default". + +Here is an example of a deployment that uses the service account "prod-service-account" and overrides the context +appropriately (assuming the Kluctl cluster config for the given target expects a "prod" context): + +```yaml +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: example + namespace: kluctl-system +spec: + interval: 10m + source: + url: https://github.com/kluctl/kluctl-examples.git + path: "./microservices-demo/3-templating-and-multi-env/" + target: prod + serviceAccountName: prod-service-account + context: default +``` + +## Git authentication + +The `spec.source` can optionally specify a `spec.source.secretRef` (see [here](#source)) which must point to an existing +secret (in the same namespace) containing Git credentials. + +### Basic access authentication + +To authenticate towards a Git repository over HTTPS using basic access +authentication (in other words: using a username and password), the referenced +Secret is expected to contain `.data.username` and `.data.password` values. + +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: basic-access-auth +type: Opaque +data: + username: + password: +``` + +### HTTPS Certificate Authority + +To provide a Certificate Authority to trust while connecting with a Git +repository over HTTPS, the referenced Secret can contain a `.data.caFile` +value. + +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: https-ca-credentials + namespace: default +type: Opaque +data: + caFile: +``` + +### SSH authentication + +To authenticate towards a Git repository over SSH, the referenced Secret is +expected to contain `identity` and `known_hosts` fields. With the respective +private key of the SSH key pair, and the host keys of the Git repository. + +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: ssh-credentials +type: Opaque +stringData: + identity: | + -----BEGIN OPENSSH PRIVATE KEY----- + ... + -----END OPENSSH PRIVATE KEY----- + known_hosts: | + github.com ecdsa-sha2-nistp256 AAAA... +``` + +## Helm Repository authentication + +Kluctl allows to [integrate Helm Charts](../../../deployments/helm) in two different ways. +One is to [pre-pull charts](../../../commands/helm-pull) and put them into version control, +making it unnecessary to pull them at deploy time. This option also means that you don't have to take any special care +on the controller side. + +The other way is to let Kluctl pull Helm Charts at deploy time. In that case, you have to ensure that the controller +has the necessary access to the Helm repositories. To add credentials for authentication, set the `spec.helmCredentials` +field to a list of secret references: + +### Basic access authentication + +```yaml +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: example + namespace: kluctl-system +spec: + interval: 10m + source: + url: https://github.com/kluctl/kluctl-examples.git + path: "./microservices-demo/3-templating-and-multi-env/" + target: prod + serviceAccountName: prod-service-account + context: default + + helmCredentials: + - secretRef: + name: helm-creds +--- +apiVersion: v1 +kind: Secret +metadata: + name: helm-creds + namespace: kluctl-system +stringData: + url: https://example-repo.com + username: my-user + password: my-password +``` + +### TLS authentication + +For TLS authentication, see the following example secret: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: helm-creds + namespace: kluctl-system +data: + certFile: + keyFile: + # NOTE: Can be supplied without the above values + caFile: +``` + +### Disabling TLS verification + +In case you need to disable TLS verification (not recommended!), add the key `insecureSkipTlsVerify` with the value +`"true"` (make sure it's a string, so surround it with `"`). + +### Pass credentials + +To enable passing of credentials to all requests, add the key `passCredentialsAll` with the value `"true"`. +This will pass the credentials to all requests, even if the hostname changes. + +## Secrets Decryption + +Kluctl offers a [SOPS Integration](../../../deployments/sops) that allows to use encrypted +manifests and variable sources in Kluctl deployments. Decryption by the controller is also supported and currently +mirrors how the [Secrets Decryption configuration](https://fluxcd.io/flux/components/kustomize/kustomization/#secrets-decryption) +of the Flux Kustomize Controller. To configure it in the `KluctlDeployment`, simply set the `decryption` field in the +spec: + +``` +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: example + namespace: kluctl-system +spec: + decryption: + provider: sops + secretRef: + name: sops-keys + ... +``` + +The `sops-keys` Secret has the same format as in the +[Flux Kustomize Controller](https://fluxcd.io/flux/components/kustomize/kustomization/#decryption-secret-reference). + +### AWS KMS with IRSA + +In addition to the [AWS KMS Secret Entry](https://fluxcd.io/flux/components/kustomize/kustomization/#aws-kms-secret-entry) +in the secret and the [global AWS KMS](https://fluxcd.io/flux/components/kustomize/kustomization/#aws-kms) +authentication via the controller's service account, the Kluctl controller also supports using the IRSA role of the +impersonated service account of the `KluctlDeployment` (specified via `serviceAccountName` in the spec or +`--default-service-account`): + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kluctl-deployment + namespace: kluctl-system + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::123456:role/my-irsa-enabled-role +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: kluctl-deployment + namespace: kluctl-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + # watch out, don't use cluster-admin if you don't trust the deployment + name: cluster-admin +subjects: + - kind: ServiceAccount + name: kluctl-deployment + namespace: kluctl-system +--- +apiVersion: gitops.kluctl.io/v1beta1 +kind: KluctlDeployment +metadata: + name: example + namespace: kluctl-system +spec: + serviceAccountName: kluctl-deployment + decryption: + provider: sops + # you can also leave out the secretRef if you don't provide addinional keys + secretRef: + name: sops-keys + ... +``` + +## Status + +When the controller completes a deployments, it reports the result in the `status` sub-resource. + +A successful reconciliation sets the ready condition to `true`. + +```yaml +status: + conditions: + - lastTransitionTime: "2022-07-07T11:48:14Z" + message: "deploy: ok" + reason: ReconciliationSucceeded + status: "True" + type: Ready + lastDeployResult: + ... + lastPruneResult: + ... + lastValidateResult: + ... +``` + +You can wait for the controller to complete a reconciliation with: + +```bash +kubectl wait kluctldeployment/backend --for=condition=ready +``` + +A failed reconciliation sets the ready condition to `false`: + +```yaml +status: + conditions: + - lastTransitionTime: "2022-05-04T10:18:11Z" + message: target invalid-name not found in kluctl project + reason: PrepareFailed + status: "False" + type: Ready + lastDeployResult: + ... + lastPruneResult: + ... + lastValidateResult: + ... +``` + +> **Note** that the lastDeployResult, lastPruneResult and lastValidateResult are only updated on a successful reconciliation. From 28b0ef4b02c6deaba9988bf47bf96b2660702926 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 16:21:56 +0200 Subject: [PATCH 1079/2268] docs: Add api docs generation --- .github/workflows/tests.yml | 9 + Makefile | 10 + .../api/kluctl-controller.front-matter.yaml | 4 + .../reference/gitops/api/kluctl-controller.md | 1465 +++++++++++++++++ hack/api-docs/config.json | 29 + hack/api-docs/template/members.tpl | 46 + hack/api-docs/template/pkg.tpl | 46 + hack/api-docs/template/type.tpl | 60 + 8 files changed, 1669 insertions(+) create mode 100644 docs/reference/gitops/api/kluctl-controller.front-matter.yaml create mode 100644 docs/reference/gitops/api/kluctl-controller.md create mode 100644 hack/api-docs/config.json create mode 100644 hack/api-docs/template/members.tpl create mode 100644 hack/api-docs/template/pkg.tpl create mode 100644 hack/api-docs/template/type.tpl diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fd0338d69..2a4ffa48f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -57,6 +57,15 @@ jobs: git diff exit 1 fi + - name: Verify generated api-docs are up-to-date + run: | + make api-docs + if [ ! -z "$(git status --porcelain)" ]; then + echo "make api-docs must be invoked and the result committed" + git status + git diff + exit 1 + fi tests: strategy: diff --git a/Makefile b/Makefile index c8d2eb5a3..58d9424aa 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,10 @@ manifests: controller-gen kustomize ## Generate WebhookConfiguration, ClusterRol $(KUSTOMIZE) build config/rbac > install/controller/controller/rbac.yaml $(KUSTOMIZE) build config/manager > install/controller/controller/manager.yaml +# Generate API reference documentation +api-docs: gen-crd-api-reference-docs + $(GEN_CRD_API_REFERENCE_DOCS) -v=4 -api-dir=./api/v1beta1 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/reference/controller/api/kluctl-controller.md + .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. go generate ./... @@ -169,6 +173,12 @@ $(CONTROLLER_GEN): $(LOCALBIN) test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) +# Find or download gen-crd-api-reference-docs +GEN_CRD_API_REFERENCE_DOCS = $(LOCALBIN)/gen-crd-api-reference-docs +.PHONY: gen-crd-api-reference-docs +gen-crd-api-reference-docs: + GOBIN=$(LOCALBIN) go install github.com/ahmetb/gen-crd-api-reference-docs@v0.3.0 + .PHONY: envtest envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. $(ENVTEST): $(LOCALBIN) diff --git a/docs/reference/gitops/api/kluctl-controller.front-matter.yaml b/docs/reference/gitops/api/kluctl-controller.front-matter.yaml new file mode 100644 index 000000000..be96cbbcb --- /dev/null +++ b/docs/reference/gitops/api/kluctl-controller.front-matter.yaml @@ -0,0 +1,4 @@ +title: Kluctl Controller API reference +linkTitle: Kluctl Controller API +description: Kluctl Controller API reference +weight: 300 diff --git a/docs/reference/gitops/api/kluctl-controller.md b/docs/reference/gitops/api/kluctl-controller.md new file mode 100644 index 000000000..f1c1d35ee --- /dev/null +++ b/docs/reference/gitops/api/kluctl-controller.md @@ -0,0 +1,1465 @@ +

Kluctl Controller API reference

+

Packages:

+ +

gitops.kluctl.io/v1beta1

+

Package v1beta1 contains API Schema definitions for the gitops.kluctl.io v1beta1 API group.

+Resource Types: +
    +

    Decryption +

    +

    +(Appears on: +KluctlDeploymentSpec) +

    +

    Decryption defines how decryption is handled for Kubernetes manifests.

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +provider
    + +string + +
    +

    Provider is the name of the decryption engine.

    +
    +secretRef
    + + +LocalObjectReference + + +
    +(Optional) +

    The secret name containing the private OpenPGP keys used for decryption.

    +
    +serviceAccount
    + +string + +
    +(Optional) +

    ServiceAccount specifies the service account used to authenticate against cloud providers. +This is currently only usable for AWS KMS keys. The specified service account will be used to authenticate to AWS +by signing a token in an IRSA compliant way.

    +
    +
    +
    +

    GitRef +

    +

    +(Appears on: +ProjectSource) +

    +
    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +branch
    + +string + +
    +(Optional) +

    Branch to filter for. Can also be a regex.

    +
    +tag
    + +string + +
    +(Optional) +

    Branch to filter for. Can also be a regex.

    +
    +
    +
    +

    HelmCredentials +

    +

    +(Appears on: +KluctlDeploymentSpec) +

    +
    +
    + + + + + + + + + + + + + +
    FieldDescription
    +secretRef
    + + +LocalObjectReference + + +
    +

    SecretRef holds the name of a secret that contains the Helm credentials. +The secret must either contain the fields credentialsId which refers to the credentialsId +found in https://kluctl.io/docs/kluctl/reference/deployments/helm/#private-chart-repositories or an url used +to match the credentials found in Kluctl projects helm-chart.yaml files. +The secret can either container basic authentication credentials via username and password or +TLS authentication via certFile and keyFile. caFile can be specified to override the CA to use while +contacting the repository. +The secret can also contain insecureSkipTlsVerify: "true", which will disable TLS verification. +passCredentialsAll: "true" can be specified to make the controller pass credentials to all requests, even if +the hostname changes in-between.

    +
    +
    +
    +

    KluctlDeployment +

    +

    KluctlDeployment is the Schema for the kluctldeployments API

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +metadata
    + + +Kubernetes meta/v1.ObjectMeta + + +
    +Refer to the Kubernetes API documentation for the fields of the +metadata field. +
    +spec
    + + +KluctlDeploymentSpec + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +source
    + + +ProjectSource + + +
    +

    Specifies the project source location

    +
    +decryption
    + + +Decryption + + +
    +(Optional) +

    Decrypt Kubernetes secrets before applying them on the cluster.

    +
    +interval
    + + +Kubernetes meta/v1.Duration + + +
    +

    The interval at which to reconcile the KluctlDeployment. +Reconciliation means that the deployment is fully rendered and only deployed when the result changes compared +to the last deployment. +To override this behavior, set the DeployInterval value.

    +
    +retryInterval
    + + +Kubernetes meta/v1.Duration + + +
    +(Optional) +

    The interval at which to retry a previously failed reconciliation. +When not specified, the controller uses the Interval +value to retry failures.

    +
    +deployInterval
    + + +SafeDuration + + +
    +(Optional) +

    DeployInterval specifies the interval at which to deploy the KluctlDeployment, even in cases the rendered +result does not change.

    +
    +validateInterval
    + + +SafeDuration + + +
    +(Optional) +

    ValidateInterval specifies the interval at which to validate the KluctlDeployment. +Validation is performed the same way as with ‘kluctl validate -t ’. +Defaults to the same value as specified in Interval. +Validate is also performed whenever a deployment is performed, independent of the value of ValidateInterval

    +
    +timeout
    + + +Kubernetes meta/v1.Duration + + +
    +(Optional) +

    Timeout for all operations. +Defaults to ‘Interval’ duration.

    +
    +suspend
    + +bool + +
    +(Optional) +

    This flag tells the controller to suspend subsequent kluctl executions, +it does not apply to already started executions. Defaults to false.

    +
    +helmCredentials
    + + +[]HelmCredentials + + +
    +(Optional) +

    HelmCredentials is a list of Helm credentials used when non pre-pulled Helm Charts are used inside a +Kluctl deployment.

    +
    +serviceAccountName
    + +string + +
    +(Optional) +

    The name of the Kubernetes service account to use while deploying. +If not specified, the default service account is used.

    +
    +kubeConfig
    + + +KubeConfig + + +
    +(Optional) +

    The KubeConfig for deploying to the target cluster. +Specifies the kubeconfig to be used when invoking kluctl. Contexts in this kubeconfig must match +the context found in the kluctl target. As an alternative, specify the context to be used via ‘context’

    +
    +target
    + +string + +
    +(Optional) +

    Target specifies the kluctl target to deploy. If not specified, an empty target is used that has no name and no +context. Use ‘TargetName’ and ‘Context’ to specify the name and context in that case.

    +
    +targetNameOverride
    + +string + +
    +(Optional) +

    TargetNameOverride sets or overrides the target name. This is especially useful when deployment without a target.

    +
    +context
    + +string + +
    +(Optional) +

    If specified, overrides the context to be used. This will effectively make kluctl ignore the context specified +in the target.

    +
    +args
    + +k8s.io/apimachinery/pkg/runtime.RawExtension + +
    +(Optional) +

    Args specifies dynamic target args.

    +
    +images
    + +[]github.com/kluctl/kluctl/v2/pkg/types.FixedImage + +
    +(Optional) +

    Images contains a list of fixed image overrides. +Equivalent to using ‘–fixed-images-file’ when calling kluctl.

    +
    +dryRun
    + +bool + +
    +(Optional) +

    DryRun instructs kluctl to run everything in dry-run mode. +Equivalent to using ‘–dry-run’ when calling kluctl.

    +
    +noWait
    + +bool + +
    +(Optional) +

    NoWait instructs kluctl to not wait for any resources to become ready, including hooks. +Equivalent to using ‘–no-wait’ when calling kluctl.

    +
    +forceApply
    + +bool + +
    +(Optional) +

    ForceApply instructs kluctl to force-apply in case of SSA conflicts. +Equivalent to using ‘–force-apply’ when calling kluctl.

    +
    +replaceOnError
    + +bool + +
    +(Optional) +

    ReplaceOnError instructs kluctl to replace resources on error. +Equivalent to using ‘–replace-on-error’ when calling kluctl.

    +
    +forceReplaceOnError
    + +bool + +
    +(Optional) +

    ForceReplaceOnError instructs kluctl to force-replace resources in case a normal replace fails. +Equivalent to using ‘–force-replace-on-error’ when calling kluctl.

    +
    +abortOnError
    + +bool + +
    +(Optional) +

    ForceReplaceOnError instructs kluctl to abort deployments immediately when something fails. +Equivalent to using ‘–abort-on-error’ when calling kluctl.

    +
    +includeTags
    + +[]string + +
    +(Optional) +

    IncludeTags instructs kluctl to only include deployments with given tags. +Equivalent to using ‘–include-tag’ when calling kluctl.

    +
    +excludeTags
    + +[]string + +
    +(Optional) +

    ExcludeTags instructs kluctl to exclude deployments with given tags. +Equivalent to using ‘–exclude-tag’ when calling kluctl.

    +
    +includeDeploymentDirs
    + +[]string + +
    +(Optional) +

    IncludeDeploymentDirs instructs kluctl to only include deployments with the given dir. +Equivalent to using ‘–include-deployment-dir’ when calling kluctl.

    +
    +excludeDeploymentDirs
    + +[]string + +
    +(Optional) +

    ExcludeDeploymentDirs instructs kluctl to exclude deployments with the given dir. +Equivalent to using ‘–exclude-deployment-dir’ when calling kluctl.

    +
    +deployMode
    + +string + +
    +(Optional) +

    DeployMode specifies what deploy mode should be used. +The options ‘full-deploy’ and ‘poke-images’ are supported. +With the ‘poke-images’ option, only images are patched into the target without performing a full deployment.

    +
    +validate
    + +bool + +
    +(Optional) +

    Validate enables validation after deploying

    +
    +prune
    + +bool + +
    +(Optional) +

    Prune enables pruning after deploying.

    +
    +delete
    + +bool + +
    +(Optional) +

    Delete enables deletion of the specified target when the KluctlDeployment object gets deleted.

    +
    +
    +status
    + + +KluctlDeploymentStatus + + +
    +
    +
    +
    +

    KluctlDeploymentSpec +

    +

    +(Appears on: +KluctlDeployment) +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +source
    + + +ProjectSource + + +
    +

    Specifies the project source location

    +
    +decryption
    + + +Decryption + + +
    +(Optional) +

    Decrypt Kubernetes secrets before applying them on the cluster.

    +
    +interval
    + + +Kubernetes meta/v1.Duration + + +
    +

    The interval at which to reconcile the KluctlDeployment. +Reconciliation means that the deployment is fully rendered and only deployed when the result changes compared +to the last deployment. +To override this behavior, set the DeployInterval value.

    +
    +retryInterval
    + + +Kubernetes meta/v1.Duration + + +
    +(Optional) +

    The interval at which to retry a previously failed reconciliation. +When not specified, the controller uses the Interval +value to retry failures.

    +
    +deployInterval
    + + +SafeDuration + + +
    +(Optional) +

    DeployInterval specifies the interval at which to deploy the KluctlDeployment, even in cases the rendered +result does not change.

    +
    +validateInterval
    + + +SafeDuration + + +
    +(Optional) +

    ValidateInterval specifies the interval at which to validate the KluctlDeployment. +Validation is performed the same way as with ‘kluctl validate -t ’. +Defaults to the same value as specified in Interval. +Validate is also performed whenever a deployment is performed, independent of the value of ValidateInterval

    +
    +timeout
    + + +Kubernetes meta/v1.Duration + + +
    +(Optional) +

    Timeout for all operations. +Defaults to ‘Interval’ duration.

    +
    +suspend
    + +bool + +
    +(Optional) +

    This flag tells the controller to suspend subsequent kluctl executions, +it does not apply to already started executions. Defaults to false.

    +
    +helmCredentials
    + + +[]HelmCredentials + + +
    +(Optional) +

    HelmCredentials is a list of Helm credentials used when non pre-pulled Helm Charts are used inside a +Kluctl deployment.

    +
    +serviceAccountName
    + +string + +
    +(Optional) +

    The name of the Kubernetes service account to use while deploying. +If not specified, the default service account is used.

    +
    +kubeConfig
    + + +KubeConfig + + +
    +(Optional) +

    The KubeConfig for deploying to the target cluster. +Specifies the kubeconfig to be used when invoking kluctl. Contexts in this kubeconfig must match +the context found in the kluctl target. As an alternative, specify the context to be used via ‘context’

    +
    +target
    + +string + +
    +(Optional) +

    Target specifies the kluctl target to deploy. If not specified, an empty target is used that has no name and no +context. Use ‘TargetName’ and ‘Context’ to specify the name and context in that case.

    +
    +targetNameOverride
    + +string + +
    +(Optional) +

    TargetNameOverride sets or overrides the target name. This is especially useful when deployment without a target.

    +
    +context
    + +string + +
    +(Optional) +

    If specified, overrides the context to be used. This will effectively make kluctl ignore the context specified +in the target.

    +
    +args
    + +k8s.io/apimachinery/pkg/runtime.RawExtension + +
    +(Optional) +

    Args specifies dynamic target args.

    +
    +images
    + +[]github.com/kluctl/kluctl/v2/pkg/types.FixedImage + +
    +(Optional) +

    Images contains a list of fixed image overrides. +Equivalent to using ‘–fixed-images-file’ when calling kluctl.

    +
    +dryRun
    + +bool + +
    +(Optional) +

    DryRun instructs kluctl to run everything in dry-run mode. +Equivalent to using ‘–dry-run’ when calling kluctl.

    +
    +noWait
    + +bool + +
    +(Optional) +

    NoWait instructs kluctl to not wait for any resources to become ready, including hooks. +Equivalent to using ‘–no-wait’ when calling kluctl.

    +
    +forceApply
    + +bool + +
    +(Optional) +

    ForceApply instructs kluctl to force-apply in case of SSA conflicts. +Equivalent to using ‘–force-apply’ when calling kluctl.

    +
    +replaceOnError
    + +bool + +
    +(Optional) +

    ReplaceOnError instructs kluctl to replace resources on error. +Equivalent to using ‘–replace-on-error’ when calling kluctl.

    +
    +forceReplaceOnError
    + +bool + +
    +(Optional) +

    ForceReplaceOnError instructs kluctl to force-replace resources in case a normal replace fails. +Equivalent to using ‘–force-replace-on-error’ when calling kluctl.

    +
    +abortOnError
    + +bool + +
    +(Optional) +

    ForceReplaceOnError instructs kluctl to abort deployments immediately when something fails. +Equivalent to using ‘–abort-on-error’ when calling kluctl.

    +
    +includeTags
    + +[]string + +
    +(Optional) +

    IncludeTags instructs kluctl to only include deployments with given tags. +Equivalent to using ‘–include-tag’ when calling kluctl.

    +
    +excludeTags
    + +[]string + +
    +(Optional) +

    ExcludeTags instructs kluctl to exclude deployments with given tags. +Equivalent to using ‘–exclude-tag’ when calling kluctl.

    +
    +includeDeploymentDirs
    + +[]string + +
    +(Optional) +

    IncludeDeploymentDirs instructs kluctl to only include deployments with the given dir. +Equivalent to using ‘–include-deployment-dir’ when calling kluctl.

    +
    +excludeDeploymentDirs
    + +[]string + +
    +(Optional) +

    ExcludeDeploymentDirs instructs kluctl to exclude deployments with the given dir. +Equivalent to using ‘–exclude-deployment-dir’ when calling kluctl.

    +
    +deployMode
    + +string + +
    +(Optional) +

    DeployMode specifies what deploy mode should be used. +The options ‘full-deploy’ and ‘poke-images’ are supported. +With the ‘poke-images’ option, only images are patched into the target without performing a full deployment.

    +
    +validate
    + +bool + +
    +(Optional) +

    Validate enables validation after deploying

    +
    +prune
    + +bool + +
    +(Optional) +

    Prune enables pruning after deploying.

    +
    +delete
    + +bool + +
    +(Optional) +

    Delete enables deletion of the specified target when the KluctlDeployment object gets deleted.

    +
    +
    +
    +

    KluctlDeploymentStatus +

    +

    +(Appears on: +KluctlDeployment) +

    +

    KluctlDeploymentStatus defines the observed state of KluctlDeployment

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +lastHandledReconcileAt
    + +string + +
    +(Optional) +

    LastHandledReconcileAt holds the value of the most recent +reconcile request value, so a change of the annotation value +can be detected.

    +
    +LastHandledDeployAt
    + +string + +
    +(Optional) +
    +observedGeneration
    + +int64 + +
    +(Optional) +

    ObservedGeneration is the last reconciled generation.

    +
    +observedCommit
    + +string + +
    +

    ObservedCommit is the last commit observed

    +
    +conditions
    + + +[]Kubernetes meta/v1.Condition + + +
    +(Optional) +
    +projectKey
    + +github.com/kluctl/kluctl/v2/pkg/types/result.ProjectKey + +
    +(Optional) +
    +targetKey
    + +github.com/kluctl/kluctl/v2/pkg/types/result.TargetKey + +
    +(Optional) +
    +lastObjectsHash
    + +string + +
    +(Optional) +
    +lastDeployError
    + +string + +
    +(Optional) +
    +lastPruneError
    + +string + +
    +(Optional) +
    +lastValidateError
    + +string + +
    +(Optional) +
    +lastDeployResult
    + +github.com/kluctl/kluctl/v2/pkg/types/result.CommandResultSummary + +
    +(Optional) +

    LastDeployResult is the result of the last deploy command

    +
    +lastPruneResult
    + +github.com/kluctl/kluctl/v2/pkg/types/result.CommandResultSummary + +
    +(Optional) +

    LastDeployResult is the result of the last prune command

    +
    +lastValidateResult
    + +github.com/kluctl/kluctl/v2/pkg/types/result.ValidateResult + +
    +(Optional) +

    LastValidateResult is the result of the last validate command

    +
    +
    +
    +

    KubeConfig +

    +

    +(Appears on: +KluctlDeploymentSpec) +

    +

    KubeConfig references a Kubernetes secret that contains a kubeconfig file.

    +
    +
    + + + + + + + + + + + + + +
    FieldDescription
    +secretRef
    + + +SecretKeyReference + + +
    +

    SecretRef holds the name of a secret that contains a key with +the kubeconfig file as the value. If no key is set, the key will default +to ‘value’. The secret must be in the same namespace as +the Kustomization. +It is recommended that the kubeconfig is self-contained, and the secret +is regularly updated if credentials such as a cloud-access-token expire. +Cloud specific cmd-path auth helpers will not function without adding +binaries and credentials to the Pod that is responsible for reconciling +the KluctlDeployment.

    +
    +
    +
    +

    LocalObjectReference +

    +

    +(Appears on: +Decryption, +HelmCredentials, +ProjectSource) +

    +
    +
    + + + + + + + + + + + + + +
    FieldDescription
    +name
    + +string + +
    +

    Name of the referent.

    +
    +
    +
    +

    ProjectSource +

    +

    +(Appears on: +KluctlDeploymentSpec) +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +url
    + +github.com/kluctl/kluctl/v2/pkg/types.GitUrl + +
    +

    Url specifies the Git url where the project source is located

    +
    +ref
    + + +GitRef + + +
    +(Optional) +

    Ref specifies the branch, tag or commit that should be used. If omitted, the default branch of the repo is used.

    +
    +path
    + +string + +
    +(Optional) +

    Path specifies the sub-directory to be used as project directory

    +
    +secretRef
    + + +LocalObjectReference + + +
    +(Optional) +

    SecretRef specifies the Secret containing authentication credentials for +the git repository. +For HTTPS repositories the Secret must contain ‘username’ and ‘password’ +fields. +For SSH repositories the Secret must contain ‘identity’ +and ‘known_hosts’ fields.

    +
    +
    +
    +

    SafeDuration +

    +

    +(Appears on: +KluctlDeploymentSpec) +

    +
    +
    + + + + + + + + + + + + + +
    FieldDescription
    +Duration
    + + +Kubernetes meta/v1.Duration + + +
    +
    +
    +
    +

    SecretKeyReference +

    +

    +(Appears on: +KubeConfig) +

    +

    SecretKeyReference contains enough information to locate the referenced Kubernetes Secret object in the same +namespace. Optionally a key can be specified. +Use this type instead of core/v1 SecretKeySelector when the Key is optional and the Optional field is not +applicable.

    +
    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +name
    + +string + +
    +

    Name of the Secret.

    +
    +key
    + +string + +
    +(Optional) +

    Key in the Secret, when not specified an implementation-specific default key is used.

    +
    +
    +
    +
    +

    This page was automatically generated with gen-crd-api-reference-docs

    +
    diff --git a/hack/api-docs/config.json b/hack/api-docs/config.json new file mode 100644 index 000000000..ae9d840f1 --- /dev/null +++ b/hack/api-docs/config.json @@ -0,0 +1,29 @@ +{ + "hideMemberFields": [ + "TypeMeta" + ], + "hideTypePatterns": [ + "ParseError$", + "List$" + ], + "externalPackages": [ + { + "typeMatchPrefix": "^k8s\\.io/apimachinery/pkg/apis/meta/v1\\.Duration$", + "docsURLTemplate": "https://godoc.org/k8s.io/apimachinery/pkg/apis/meta/v1#Duration" + }, + { + "typeMatchPrefix": "^k8s\\.io/apiextensions-apiserver/pkg/apis/apiextensions/v1\\.JSON$", + "docsURLTemplate": "https://pkg.go.dev/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1?tab=doc#JSON" + }, + { + "typeMatchPrefix": "^k8s\\.io/(api|apimachinery/pkg/apis)/", + "docsURLTemplate": "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#{{lower .TypeIdentifier}}-{{arrIndex .PackageSegments -1}}-{{arrIndex .PackageSegments -2}}" + } + ], + "typeDisplayNamePrefixOverrides": { + "k8s.io/api/": "Kubernetes ", + "k8s.io/apimachinery/pkg/apis/": "Kubernetes ", + "k8s.io/apiextensions-apiserver/": "Kubernetes " + }, + "markdownDisabled": false +} diff --git a/hack/api-docs/template/members.tpl b/hack/api-docs/template/members.tpl new file mode 100644 index 000000000..26e7251e4 --- /dev/null +++ b/hack/api-docs/template/members.tpl @@ -0,0 +1,46 @@ +{{ define "members" }} + {{ range .Members }} + {{ if not (hiddenMember .)}} + + + {{ fieldName . }}
    + + {{ if linkForType .Type }} + + {{ typeDisplayName .Type }} + + {{ else }} + {{ typeDisplayName .Type }} + {{ end }} + + + + {{ if fieldEmbedded . }} +

    + (Members of {{ fieldName . }} are embedded into this type.) +

    + {{ end}} + + {{ if isOptionalMember .}} + (Optional) + {{ end }} + + {{ safe (renderComments .CommentLines) }} + + {{ if and (eq (.Type.Name.Name) "ObjectMeta") }} + Refer to the Kubernetes API documentation for the fields of the + metadata field. + {{ end }} + + {{ if or (eq (fieldName .) "spec") }} +
    +
    + + {{ template "members" .Type }} +
    + {{ end }} + + + {{ end }} + {{ end }} +{{ end }} diff --git a/hack/api-docs/template/pkg.tpl b/hack/api-docs/template/pkg.tpl new file mode 100644 index 000000000..0a4c0224e --- /dev/null +++ b/hack/api-docs/template/pkg.tpl @@ -0,0 +1,46 @@ +{{ define "packages" }} +

    Kluctl Controller API reference

    + + {{ with .packages}} +

    Packages:

    + + {{ end}} + + {{ range .packages }} +

    + {{- packageDisplayName . -}} +

    + + {{ with (index .GoPackages 0 )}} + {{ with .DocComments }} + {{ safe (renderComments .) }} + {{ end }} + {{ end }} + + Resource Types: + +
      + {{- range (visibleTypes (sortedTypes .Types)) -}} + {{ if isExportedType . -}} +
    • + {{ typeDisplayName . }} +
    • + {{- end }} + {{- end -}} +
    + + {{ range (visibleTypes (sortedTypes .Types))}} + {{ template "type" . }} + {{ end }} + {{ end }} + +
    +

    This page was automatically generated with gen-crd-api-reference-docs

    +
    +{{ end }} diff --git a/hack/api-docs/template/type.tpl b/hack/api-docs/template/type.tpl new file mode 100644 index 000000000..cd2fa6981 --- /dev/null +++ b/hack/api-docs/template/type.tpl @@ -0,0 +1,60 @@ +{{ define "type" }} +

    + {{- .Name.Name }} + {{ if eq .Kind "Alias" }}({{.Underlying}} alias){{ end -}} +

    + + {{ with (typeReferences .) }} +

    + (Appears on: + {{- $prev := "" -}} + {{- range . -}} + {{- if $prev -}}, {{ end -}} + {{ $prev = . }} + {{ typeDisplayName . }} + {{- end -}} + ) +

    + {{ end }} + + {{ with .CommentLines }} + {{ safe (renderComments .) }} + {{ end }} + + {{ if .Members }} +
    +
    + + + + + + + + + {{ if isExportedType . }} + + + + + + + + + {{ end }} + {{ template "members" . }} + +
    FieldDescription
    + apiVersion
    + string
    + {{ apiGroup . }} +
    + kind
    + string +
    + {{ .Name.Name }} +
    +
    +
    + {{ end }} +{{ end }} From f79ca8222c187193a19ce2a2f511ba6d911f2e10 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 16:22:35 +0200 Subject: [PATCH 1080/2268] docs: Update Kluctl version in installation.md --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 656d3d93c..31c012d25 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -113,5 +113,5 @@ deployments: - git: url: https://github.com/kluctl/kluctl.git subDir: install/controller - ref: v0.0.0 + ref: v2.20.0 ``` From a143ba523a50be5e3f69eb4256803a8c007cdec9 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 16:32:04 +0200 Subject: [PATCH 1081/2268] docs: Fix links to .md files --- docs/reference/gitops/spec/v1beta1/kluctldeployment.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/gitops/spec/v1beta1/kluctldeployment.md b/docs/reference/gitops/spec/v1beta1/kluctldeployment.md index db46f4cf0..85396a984 100644 --- a/docs/reference/gitops/spec/v1beta1/kluctldeployment.md +++ b/docs/reference/gitops/spec/v1beta1/kluctldeployment.md @@ -362,8 +362,8 @@ stringData: ## Helm Repository authentication -Kluctl allows to [integrate Helm Charts](../../../deployments/helm) in two different ways. -One is to [pre-pull charts](../../../commands/helm-pull) and put them into version control, +Kluctl allows to [integrate Helm Charts](../../../deployments/helm.md) in two different ways. +One is to [pre-pull charts](../../../commands/helm-pull.md) and put them into version control, making it unnecessary to pull them at deploy time. This option also means that you don't have to take any special care on the controller side. @@ -432,7 +432,7 @@ This will pass the credentials to all requests, even if the hostname changes. ## Secrets Decryption -Kluctl offers a [SOPS Integration](../../../deployments/sops) that allows to use encrypted +Kluctl offers a [SOPS Integration](../../../deployments/sops.md) that allows to use encrypted manifests and variable sources in Kluctl deployments. Decryption by the controller is also supported and currently mirrors how the [Secrets Decryption configuration](https://fluxcd.io/flux/components/kustomize/kustomization/#secrets-decryption) of the Flux Kustomize Controller. To configure it in the `KluctlDeployment`, simply set the `decryption` field in the From 442e3f675766929d2c2bbd091b55bf7f09ed1815 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 16:42:04 +0200 Subject: [PATCH 1082/2268] build: Preparing release v2.20.1 --- docs/installation.md | 2 +- install/controller/.kluctl.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 31c012d25..c899bccd5 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -113,5 +113,5 @@ deployments: - git: url: https://github.com/kluctl/kluctl.git subDir: install/controller - ref: v2.20.0 + ref: v2.20.1 ``` diff --git a/install/controller/.kluctl.yaml b/install/controller/.kluctl.yaml index 481c21a87..f74304426 100644 --- a/install/controller/.kluctl.yaml +++ b/install/controller/.kluctl.yaml @@ -2,4 +2,4 @@ discriminator: kluctl.io-controller args: - name: controller_version - default: v2.20.0 \ No newline at end of file + default: v2.20.1 \ No newline at end of file From c61c60d907f49dbc6d6c604f9bcf5fef3913cfe0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 18:08:04 +0200 Subject: [PATCH 1083/2268] fix: Use projectDir as repoRoot in case there is no Git repo involved --- cmd/kluctl/commands/utils.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/kluctl/commands/utils.go b/cmd/kluctl/commands/utils.go index a03b70b77..0f524badc 100644 --- a/cmd/kluctl/commands/utils.go +++ b/cmd/kluctl/commands/utils.go @@ -50,6 +50,10 @@ func withKluctlProjectFromArgs(ctx context.Context, projectFlags args.ProjectFla } } + if repoRoot == "" { + repoRoot = projectDir + } + ctx, cancel := context.WithTimeout(ctx, projectFlags.Timeout) defer cancel() From c3636d2556b28233fc6719789e05a30e83b53230 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 18:13:00 +0200 Subject: [PATCH 1084/2268] docs: Fix link to images.md --- docs/reference/gitops/spec/v1beta1/kluctldeployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/gitops/spec/v1beta1/kluctldeployment.md b/docs/reference/gitops/spec/v1beta1/kluctldeployment.md index 85396a984..391bbd7cb 100644 --- a/docs/reference/gitops/spec/v1beta1/kluctldeployment.md +++ b/docs/reference/gitops/spec/v1beta1/kluctldeployment.md @@ -156,7 +156,7 @@ The above example is equivalent to calling `kluctl deploy -t prod -a arg1=value1 ### images `spec.images` specifies a list of fixed images to be used by -[`image.get_image(...)`](../../../deployments/images#imagesget_image). Example: +[`image.get_image(...)`](../../../deployments/images.md#imagesget_image). Example: ``` apiVersion: gitops.kluctl.io/v1beta1 From 4bff0959cd734a2bc6e80f4ed53e157d6b6ac5c6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 19:49:56 +0200 Subject: [PATCH 1085/2268] fix: Fix pre-pulled helm chart pathes --- pkg/deployment/deployment_item.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/deployment/deployment_item.go b/pkg/deployment/deployment_item.go index d291acbd0..9ad65c80c 100644 --- a/pkg/deployment/deployment_item.go +++ b/pkg/deployment/deployment_item.go @@ -180,7 +180,16 @@ func (di *DeploymentItem) isHelmValuesYaml(p string) bool { func (di *DeploymentItem) newHelmRelease(subDir string) (*helm.Release, error) { configPath := yaml.FixPathExt(filepath.Join(di.RenderedDir, subDir, "helm-chart.yaml")) - helmChartsDir := filepath.Join(di.Project.source.dir, ".helm-charts") + + var helmChartsDir string + if di.Project.source == di.Project.getRootProject().source { + // deployment item is part of the root project repo, so it's allowed to be in a relative dir + helmChartsDir = filepath.Join(di.Project.source.dir, di.Project.getRootProject().relDir, ".helm-charts") + } else { + // deployment item is part of a git-included project, so it must be at the repo root + // TODO this limitation should be lifted in some way (search multiple pathes?) + helmChartsDir = filepath.Join(di.Project.source.dir, ".helm-charts") + } hr, err := helm.NewRelease(di.Project.source.dir, filepath.Join(di.RelToSourceItemDir, subDir), configPath, helmChartsDir, di.ctx.HelmCredentials) if err != nil { From f80248ea7cc2a810db8210612af6095be0410c5c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 19:58:21 +0200 Subject: [PATCH 1086/2268] build: Preparing release v2.20.2 --- docs/installation.md | 2 +- install/controller/.kluctl.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index c899bccd5..7e40fabdb 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -113,5 +113,5 @@ deployments: - git: url: https://github.com/kluctl/kluctl.git subDir: install/controller - ref: v2.20.1 + ref: v2.20.2 ``` diff --git a/install/controller/.kluctl.yaml b/install/controller/.kluctl.yaml index f74304426..166bbf0ae 100644 --- a/install/controller/.kluctl.yaml +++ b/install/controller/.kluctl.yaml @@ -2,4 +2,4 @@ discriminator: kluctl.io-controller args: - name: controller_version - default: v2.20.1 \ No newline at end of file + default: v2.20.2 \ No newline at end of file From cbb7fc433c5c333aa26c027fd76a92be41d2c80e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 31 May 2023 22:48:13 +0200 Subject: [PATCH 1087/2268] build: Multiple fixes to docs generation and checks (#527) * build: Write api-docs result to correct directory * build: Exclude api-docs from markdown-link-check --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 58d9424aa..c7bea97f0 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ manifests: controller-gen kustomize ## Generate WebhookConfiguration, ClusterRol # Generate API reference documentation api-docs: gen-crd-api-reference-docs - $(GEN_CRD_API_REFERENCE_DOCS) -v=4 -api-dir=./api/v1beta1 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/reference/controller/api/kluctl-controller.md + $(GEN_CRD_API_REFERENCE_DOCS) -v=4 -api-dir=./api/v1beta1 -config=./hack/api-docs/config.json -template-dir=./hack/api-docs/template -out-file=./docs/reference/gitops/api/kluctl-controller.md .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. @@ -97,7 +97,11 @@ replace-commands-help: ## Replace commands help in docs MARKDOWN_LINK_CHECK_VERSION=3.11.2 markdown-link-check: ## Check markdown files for dead links - find . -name '*.md' | xargs docker run -v ${PWD}:/tmp:ro --rm -i -w /tmp ghcr.io/tcort/markdown-link-check:$(MARKDOWN_LINK_CHECK_VERSION) + find . -name '*.md' \ + -and -not -path './docs/reference/gitops/api/kluctl-controller.md' \ + -and -not -path './pkg/webui/ui/node_modules/*' \ + -and -not -path './pkg/webui/ui/build/*' | \ + xargs docker run -v ${PWD}:/tmp:ro --rm -i -w /tmp ghcr.io/tcort/markdown-link-check:$(MARKDOWN_LINK_CHECK_VERSION) ##@ Build From cb6b4f62be770e8a6758c86032a5f5063a2216f6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 1 Jun 2023 12:38:14 +0200 Subject: [PATCH 1088/2268] fix: Don't show error when addGitInfo fails (#528) --- pkg/deployment/commands/result_utils.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/deployment/commands/result_utils.go b/pkg/deployment/commands/result_utils.go index 4b987d949..52e210d98 100644 --- a/pkg/deployment/commands/result_utils.go +++ b/pkg/deployment/commands/result_utils.go @@ -90,6 +90,9 @@ func addGitInfo(targetCtx *kluctl_project.TargetContext, r *result.CommandResult g, err := git2.PlainOpen(targetCtx.KluctlProject.LoadArgs.RepoRoot) if err != nil { + if err == git2.ErrRepositoryNotExists { + return nil + } return err } From 932661e4d57505eebb60f90aebaa3cb737c12251 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 1 Jun 2023 13:03:43 +0200 Subject: [PATCH 1089/2268] fix: Allow to specify controller_version via args and via vars --- hack/prepare-release.sh | 2 +- install/controller/controller/kustomization.yaml | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hack/prepare-release.sh b/hack/prepare-release.sh index 496c9b6bd..f63be7543 100755 --- a/hack/prepare-release.sh +++ b/hack/prepare-release.sh @@ -24,7 +24,7 @@ fi echo VERSION=$VERSION -FILES="install/controller/.kluctl.yaml docs/installation.md" +FILES="install/controller/.kluctl.yaml install/controller/controller/kustomization.yaml docs/installation.md" for f in $FILES; do cat $f | sed "s/$VERSION_REGEX_SED/$VERSION/g" > $f.tmp diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml index d4711bbb7..961e9fca4 100644 --- a/install/controller/controller/kustomization.yaml +++ b/install/controller/controller/kustomization.yaml @@ -1,3 +1,5 @@ +{% set controller_version = (args.controller_version if args.controller_version is defined else controller_version) | default("v2.20.2") %} + resources: - crd.yaml - manager.yaml @@ -10,8 +12,8 @@ patches: patch: |- - op: add path: /spec/template/spec/containers/0/image - value: ghcr.io/kluctl/kluctl:{{ args.controller_version }} -{% if args.controller_version.endswith("-next-amd64") or args.controller_version.endswith("-next-arm64") %} + value: ghcr.io/kluctl/kluctl:{{ controller_version }} +{% if controller_version.endswith("-next-amd64") or controller_version.endswith("-next-arm64") %} - target: kind: Deployment name: kluctl-controller From 3ac2dfae67c70b481375794428a044ffb58e9adc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 1 Jun 2023 13:36:07 +0200 Subject: [PATCH 1090/2268] fix: Switch back to debian based base images due to missing arm+musl support --- Dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 77958da58..233c596fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,11 @@ -FROM alpine:3.18.0 +# We must use a glibc based distro due to embedded python not supporting musl libc for aarch64 (only amd64+musl is supported) +# see https://github.com/indygreg/python-build-standalone/issues/87 +FROM debian:bullseye-slim -RUN apk add ca-certificates +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* # We need git for kustomize to support overlays from git -RUN apk add git +RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/* # Ensure helm is not trying to access / ENV HELM_CACHE_HOME=/tmp/helm-cache From 3d6033ceb99d9cc156310e645b2c3f4993a0ece2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 1 Jun 2023 14:07:38 +0200 Subject: [PATCH 1091/2268] fix: Properly pass hostkey callback to ssh auth method The KnownHostsWrapper used in the past is not needed anymore and actually caused host key verification to fail when ~/.ssh/known_hosts was missing, even if known_hosts were explicitely passed. --- pkg/git/auth/list_auth_provider.go | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/pkg/git/auth/list_auth_provider.go b/pkg/git/auth/list_auth_provider.go index b69f0b2df..444e38d4d 100644 --- a/pkg/git/auth/list_auth_provider.go +++ b/pkg/git/auth/list_auth_provider.go @@ -6,7 +6,6 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport/ssh" "github.com/kluctl/kluctl/v2/pkg/git/messages" "github.com/kluctl/kluctl/v2/pkg/types" - ssh2 "golang.org/x/crypto/ssh" "strings" ) @@ -28,28 +27,6 @@ type AuthEntry struct { CABundle []byte } -type KnownHostsWrapper struct { - authMethod ssh.AuthMethod - hostKeyCallback ssh2.HostKeyCallback -} - -func (w *KnownHostsWrapper) String() string { - return w.authMethod.String() -} - -func (w *KnownHostsWrapper) Name() string { - return w.authMethod.Name() -} - -func (w *KnownHostsWrapper) ClientConfig() (*ssh2.ClientConfig, error) { - ccfg, err := w.authMethod.ClientConfig() - if err != nil { - return nil, err - } - ccfg.HostKeyCallback = w.hostKeyCallback - return ccfg, nil -} - func (a *ListAuthProvider) AddEntry(e AuthEntry) { a.entries = append(a.entries, e) } @@ -102,11 +79,9 @@ func (a *ListAuthProvider) BuildAuth(ctx context.Context, gitUrl types.GitUrl) A if err != nil { a.MessageCallbacks.Trace("ListAuthProvider: failed to parse private key: %v", err) } else { + pk.HostKeyCallback = buildVerifyHostCallback(a.MessageCallbacks, e.KnownHosts) return AuthMethodAndCA{ - AuthMethod: &KnownHostsWrapper{ - authMethod: pk, - hostKeyCallback: buildVerifyHostCallback(a.MessageCallbacks, e.KnownHosts), - }, + AuthMethod: pk, Hash: func() ([]byte, error) { return buildHash(pk.Signer) }, From 8a93c5785eaeff25623f5daea0c78fd0ca358121 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 1 Jun 2023 14:29:14 +0200 Subject: [PATCH 1092/2268] build: Preparing release v2.20.3 --- docs/installation.md | 2 +- install/controller/.kluctl.yaml | 2 +- install/controller/controller/kustomization.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 7e40fabdb..ffb8a3055 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -113,5 +113,5 @@ deployments: - git: url: https://github.com/kluctl/kluctl.git subDir: install/controller - ref: v2.20.2 + ref: v2.20.3 ``` diff --git a/install/controller/.kluctl.yaml b/install/controller/.kluctl.yaml index 166bbf0ae..aa23bb3d9 100644 --- a/install/controller/.kluctl.yaml +++ b/install/controller/.kluctl.yaml @@ -2,4 +2,4 @@ discriminator: kluctl.io-controller args: - name: controller_version - default: v2.20.2 \ No newline at end of file + default: v2.20.3 \ No newline at end of file diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml index 961e9fca4..77c7fc8cf 100644 --- a/install/controller/controller/kustomization.yaml +++ b/install/controller/controller/kustomization.yaml @@ -1,4 +1,4 @@ -{% set controller_version = (args.controller_version if args.controller_version is defined else controller_version) | default("v2.20.2") %} +{% set controller_version = (args.controller_version if args.controller_version is defined else controller_version) | default("v2.20.3") %} resources: - crd.yaml From 198f4db40069ad7e7e2f691a0683431429c5fcac Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 10:10:18 +0200 Subject: [PATCH 1093/2268] ci: Add nightly/devel builds for main branch (#530) --- .github/workflows/nightly.yml | 56 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 11 +++---- .goreleaser.yaml | 9 ++++-- 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/nightly.yml diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 000000000..1aa476299 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,56 @@ +name: goreleaser-release + +on: + push: + branches: + - main + +permissions: + contents: write + packages: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Fetch all tags + run: git fetch --force --tags + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.19 + - name: Setup QEMU + uses: docker/setup-qemu-action@v2 + - name: Setup Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v2 + - name: Setup Syft + uses: anchore/sbom-action/download-syft@v0 + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: kluctlbot + password: ${{ secrets.GHCR_TOKEN }} + - uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-goreleaser-nightly-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-goreleaser-nightly- + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v4 + with: + distribution: goreleaser-pro + version: latest + args: release --nightly --clean + env: + GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5289b22a7..56a929fcc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: goreleaser +name: goreleaser-release on: push: @@ -42,15 +42,16 @@ jobs: path: | ~/go/pkg/mod ~/.cache/go-build - key: ${{ runner.os }}-goreleaser-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-goreleaser-release-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-goreleaser- + ${{ runner.os }}-goreleaser-release- - name: Run GoReleaser uses: goreleaser/goreleaser-action@v4 with: - distribution: goreleaser + distribution: goreleaser-pro version: latest - args: release --rm-dist + args: release --clean env: + GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index ba0d196f9..2a6f9fc00 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -52,7 +52,12 @@ sboms: checksum: name_template: '{{ .ProjectName }}_v{{ .Version }}_checksums.txt' snapshot: - name_template: "{{ incminor .Version }}-next" + name_template: "{{ incminor .Version }}-snapshot" +nightly: + name_template: 'devel' + tag_name: devel + publish_release: true + keep_single_release: true changelog: sort: asc filters: @@ -100,7 +105,7 @@ dockers: - "ghcr.io/kluctl/kluctl:v{{ .Version }}-arm64" docker_manifests: - - name_template: ghcr.io/kluctl/kluctl:{{ .Tag }} + - name_template: ghcr.io/kluctl/kluctl:{{ .Version }} image_templates: - "ghcr.io/kluctl/kluctl:v{{ .Version }}-amd64" - "ghcr.io/kluctl/kluctl:v{{ .Version }}-arm64" From 6b8d0cb991d2f6a61009948f762626bc38e3a43a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 10:14:41 +0200 Subject: [PATCH 1094/2268] ci: Fix name of nightly release workflow (#531) --- .github/workflows/nightly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1aa476299..7419b12ca 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,4 +1,4 @@ -name: goreleaser-release +name: goreleaser-nightly on: push: From d3bbc566328a862d3899db86250f5bf481b2462b Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 10:35:14 +0200 Subject: [PATCH 1095/2268] ci: Use next version number in devel version (#532) --- .goreleaser.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 2a6f9fc00..53c42dfc4 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -54,7 +54,7 @@ checksum: snapshot: name_template: "{{ incminor .Version }}-snapshot" nightly: - name_template: 'devel' + name_template: '{{ incminor .Version }}-devel' tag_name: devel publish_release: true keep_single_release: true From 8d37f7842d7c1c1026f9a36db7c7fbda3d8b807d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 11:10:44 +0200 Subject: [PATCH 1096/2268] ci: Fix missing version in nightlies and mark as non-draft (#533) * ci: Use .Version instead of .Tag for release name_template This fixes missing versions in nightlies. * ci: Set draft=false for nightlies --- .github/workflows/nightly.yml | 4 ++++ .goreleaser.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7419b12ca..54fb5e8c3 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -44,6 +44,10 @@ jobs: key: ${{ runner.os }}-goreleaser-nightly-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-goreleaser-nightly- + - name: Set .goreleaser.yaml's release.draft=false + shell: bash + run: | + cat .goreleaser.yaml | sed 's/draft: true/draft: false/g' > .goreleaser.yaml.tmp && mv .goreleaser.yaml.tmp .goreleaser.yaml - name: Run GoReleaser uses: goreleaser/goreleaser-action@v4 with: diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 53c42dfc4..9bb7f9c57 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -72,7 +72,7 @@ changelog: release: draft: true prerelease: auto - name_template: "{{.ProjectName}}-{{.Tag}}" + name_template: "{{ .ProjectName }}-v{{ .Version }}" dockers: - id: linux-amd64 From 8ce7fcbaa3f9abc35e49b0beebaa683fdfe35dbe Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 11:27:26 +0200 Subject: [PATCH 1097/2268] ci: Fix docker tags and add header to nightly releases (#534) * ci: Fix docker tag of releases * ci: Add header to nightly releases --- .goreleaser.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 9bb7f9c57..c8ebae4c9 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -73,6 +73,12 @@ release: draft: true prerelease: auto name_template: "{{ .ProjectName }}-v{{ .Version }}" + header: | + {{- if .IsNightly -}} + ## Development build + This is a development build of the main branch and not meant for production use. + Docker images are also available via: `ghcr.io/kluctl/kluctl:v{{ .Version }}` + {{- end -}} dockers: - id: linux-amd64 @@ -105,7 +111,7 @@ dockers: - "ghcr.io/kluctl/kluctl:v{{ .Version }}-arm64" docker_manifests: - - name_template: ghcr.io/kluctl/kluctl:{{ .Version }} + - name_template: ghcr.io/kluctl/kluctl:v{{ .Version }} image_templates: - "ghcr.io/kluctl/kluctl:v{{ .Version }}-amd64" - "ghcr.io/kluctl/kluctl:v{{ .Version }}-arm64" From 5ecc3aca34fdeef2432001d871c75fd32a169320 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 13:58:43 +0200 Subject: [PATCH 1098/2268] docs: Don't include pre-releases in version badge (#535) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33ed42f81..1347f626b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![tests](https://github.com/kluctl/kluctl/workflows/tests/badge.svg)](https://github.com/kluctl/kluctl/actions) [![license](https://img.shields.io/github/license/kluctl/kluctl.svg)](https://github.com/kluctl/kluctl/blob/main/LICENSE) -[![release](https://img.shields.io/github/release/kluctl/kluctl/all.svg)](https://github.com/kluctl/kluctl/releases) +[![release](https://img.shields.io/github/release/kluctl/kluctl.svg)](https://github.com/kluctl/kluctl/releases) kluctl From 5648bb9294888be9779528af7eb582d7498b1fad Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 15:00:37 +0200 Subject: [PATCH 1099/2268] fix: Use absolute version of --project-dir (#537) --- cmd/kluctl/args/project.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 21468a46b..880426843 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -2,6 +2,7 @@ package args import ( "os" + "path/filepath" "time" ) @@ -11,7 +12,7 @@ type ProjectDir struct { func (a ProjectDir) GetProjectDir() (string, error) { if a.ProjectDir != "" { - return a.ProjectDir.String(), nil + return filepath.Abs(a.ProjectDir.String()) } cwd, err := os.Getwd() if err != nil { From 29e2b9b45cf2b7133b575897822e4a68bde85c26 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 15:01:30 +0200 Subject: [PATCH 1100/2268] feat: Use chainguards wolfi-base image as base (#536) This gives us a very slim alpine-like image with glibc instead of musl. --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 233c596fe..5c2caa191 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ # We must use a glibc based distro due to embedded python not supporting musl libc for aarch64 (only amd64+musl is supported) # see https://github.com/indygreg/python-build-standalone/issues/87 -FROM debian:bullseye-slim +FROM cgr.dev/chainguard/wolfi-base -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* +RUN apk add ca-certificates # We need git for kustomize to support overlays from git -RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/* +RUN apk add git # Ensure helm is not trying to access / ENV HELM_CACHE_HOME=/tmp/helm-cache From 41e5b749cd42cc2a3b0e3a192133b5346aba0c15 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 15:11:52 +0200 Subject: [PATCH 1101/2268] ci: Only allow a single nightly workflow to run (#538) --- .github/workflows/nightly.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 54fb5e8c3..24933597b 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -9,6 +9,8 @@ permissions: contents: write packages: write +concurrency: goreleaser-nightly + jobs: goreleaser: runs-on: ubuntu-latest From 0c4b3a36ab13478aca5e0841a73367f2170ca01e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 16:05:29 +0200 Subject: [PATCH 1102/2268] chore: Remove unneeded ca-certificates installation from Dockerfile (#539) wolfi-base already has this installed. --- Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5c2caa191..91818c999 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,6 @@ # see https://github.com/indygreg/python-build-standalone/issues/87 FROM cgr.dev/chainguard/wolfi-base -RUN apk add ca-certificates - # We need git for kustomize to support overlays from git RUN apk add git From 225cc331eeeafe03a164c87009eced9e53187ef6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 16:33:30 +0200 Subject: [PATCH 1103/2268] fix: Check for proper suffix when deciding on imagePullPolicy: Always --- install/controller/controller/kustomization.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml index 77c7fc8cf..c159e432b 100644 --- a/install/controller/controller/kustomization.yaml +++ b/install/controller/controller/kustomization.yaml @@ -13,7 +13,7 @@ patches: - op: add path: /spec/template/spec/containers/0/image value: ghcr.io/kluctl/kluctl:{{ controller_version }} -{% if controller_version.endswith("-next-amd64") or controller_version.endswith("-next-arm64") %} +{% if "-devel" in controller_version %} - target: kind: Deployment name: kluctl-controller From 03ac5bee4b7c69bdf9fe0cc20fcb1a9b3752f4ce Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 16:58:28 +0200 Subject: [PATCH 1104/2268] refactor: Use get_var to simplify controller_version defaulting --- install/controller/controller/kustomization.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml index c159e432b..4d84df07c 100644 --- a/install/controller/controller/kustomization.yaml +++ b/install/controller/controller/kustomization.yaml @@ -1,4 +1,4 @@ -{% set controller_version = (args.controller_version if args.controller_version is defined else controller_version) | default("v2.20.3") %} +{% set controller_version = get_var("args.controller_version", "v2.20.3") %} resources: - crd.yaml From 53d56edde0f504daf12a3f046ed5ddd0a8a8b431 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 16:59:07 +0200 Subject: [PATCH 1105/2268] feat: Allow to specify controller args and env vars --- config/manager/manager.yaml | 9 +++++---- install/controller/.kluctl.yaml | 6 +++++- install/controller/controller/kustomization.yaml | 15 ++++++++++++++- install/controller/controller/manager.yaml | 1 + 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 83c13b50a..4700ace09 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -66,15 +66,16 @@ spec: # seccompProfile: # type: RuntimeDefault containers: - - command: + - name: controller + image: ghcr.io/kluctl/kluctl:latest + imagePullPolicy: IfNotPresent + command: - kluctl - controller - run args: - --leader-elect - image: ghcr.io/kluctl/kluctl:latest - imagePullPolicy: IfNotPresent - name: controller + env: [] securityContext: allowPrivilegeEscalation: false capabilities: diff --git a/install/controller/.kluctl.yaml b/install/controller/.kluctl.yaml index aa23bb3d9..d45892605 100644 --- a/install/controller/.kluctl.yaml +++ b/install/controller/.kluctl.yaml @@ -2,4 +2,8 @@ discriminator: kluctl.io-controller args: - name: controller_version - default: v2.20.3 \ No newline at end of file + default: v2.20.3 + - name: controller_args + default: [] + - name: controller_envs + default: [] diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml index 4d84df07c..06923f9bd 100644 --- a/install/controller/controller/kustomization.yaml +++ b/install/controller/controller/kustomization.yaml @@ -13,12 +13,25 @@ patches: - op: add path: /spec/template/spec/containers/0/image value: ghcr.io/kluctl/kluctl:{{ controller_version }} -{% if "-devel" in controller_version %} - target: kind: Deployment name: kluctl-controller patch: |- + - op: test + path: /kind + value: Deployment # this is just a dummy test to avoid empty patches +{% if "-devel" in controller_version %} - op: add path: /spec/template/spec/containers/0/imagePullPolicy value: Always {% endif %} +{% for a in get_var("args.controller_args", []) %} + - op: add + path: /spec/template/spec/containers/0/args/- + value: "{{ a }}" +{% endfor %} +{% for a in get_var("args.controller_envs", []) %} + - op: add + path: /spec/template/spec/containers/0/env/- + value: {{ a | to_json }} +{% endfor %} diff --git a/install/controller/controller/manager.yaml b/install/controller/controller/manager.yaml index 5544b30e2..8ef9d3e81 100644 --- a/install/controller/controller/manager.yaml +++ b/install/controller/controller/manager.yaml @@ -43,6 +43,7 @@ spec: - kluctl - controller - run + env: [] image: ghcr.io/kluctl/kluctl:latest imagePullPolicy: IfNotPresent livenessProbe: From bdb1b4a5f647093be2d9196fa1bbdfc0aa3fe5bb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 17:28:16 +0200 Subject: [PATCH 1106/2268] fix: Use runtime.RawExtension for deploy/prune/validate results This removes the need to stay strictly compatible in results --- api/v1beta1/kluctldeployment_types.go | 95 ++- api/v1beta1/zz_generated.deepcopy.go | 6 +- .../gitops.kluctl.io_kluctldeployments.yaml | 673 +----------------- .../reference/gitops/api/kluctl-controller.md | 6 +- e2e/gitops_errors_test.go | 4 +- install/controller/controller/crd.yaml | 673 +----------------- .../kluctldeployment_controller.go | 66 +- 7 files changed, 157 insertions(+), 1366 deletions(-) diff --git a/api/v1beta1/kluctldeployment_types.go b/api/v1beta1/kluctldeployment_types.go index b5021142c..30bfa45cc 100644 --- a/api/v1beta1/kluctldeployment_types.go +++ b/api/v1beta1/kluctldeployment_types.go @@ -19,6 +19,7 @@ package v1beta1 import ( "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/yaml" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "time" @@ -327,15 +328,103 @@ type KluctlDeploymentStatus struct { // LastDeployResult is the result of the last deploy command // +optional - LastDeployResult *result.CommandResultSummary `json:"lastDeployResult,omitempty"` + // +kubebuilder:pruning:PreserveUnknownFields + LastDeployResult *runtime.RawExtension `json:"lastDeployResult,omitempty"` // LastDeployResult is the result of the last prune command // +optional - LastPruneResult *result.CommandResultSummary `json:"lastPruneResult,omitempty"` + LastPruneResult *runtime.RawExtension `json:"lastPruneResult,omitempty"` // LastValidateResult is the result of the last validate command // +optional - LastValidateResult *result.ValidateResult `json:"lastValidateResult,omitempty"` + LastValidateResult *runtime.RawExtension `json:"lastValidateResult,omitempty"` +} + +func (s *KluctlDeploymentStatus) SetLastDeployResult(crs *result.CommandResultSummary, err error) error { + s.LastDeployError = "" + if err != nil { + s.LastDeployError = err.Error() + } + if crs == nil { + s.LastDeployResult = nil + } else { + b, err := yaml.WriteJsonString(crs) + if err != nil { + return err + } + s.LastDeployResult = &runtime.RawExtension{Raw: []byte(b)} + } + return nil +} + +func (s *KluctlDeploymentStatus) SetLastPruneResult(crs *result.CommandResultSummary, err error) error { + s.LastPruneError = "" + if err != nil { + s.LastPruneError = err.Error() + } + if crs == nil { + s.LastPruneResult = nil + } else { + b, err := yaml.WriteJsonString(crs) + if err != nil { + return err + } + s.LastPruneResult = &runtime.RawExtension{Raw: []byte(b)} + } + return nil +} + +func (s *KluctlDeploymentStatus) SetLastValidateResult(crs *result.ValidateResult, err error) error { + s.LastValidateError = "" + if err != nil { + s.LastValidateError = err.Error() + } + if crs == nil { + s.LastValidateResult = nil + } else { + b, err := yaml.WriteJsonString(crs) + if err != nil { + return err + } + s.LastValidateResult = &runtime.RawExtension{Raw: []byte(b)} + } + return nil +} + +func (s *KluctlDeploymentStatus) GetLastDeployResult() (*result.CommandResultSummary, error) { + if s.LastDeployResult == nil { + return nil, nil + } + var ret result.CommandResultSummary + err := yaml.ReadYamlBytes(s.LastDeployResult.Raw, &ret) + if err != nil { + return nil, err + } + return &ret, nil +} + +func (s *KluctlDeploymentStatus) GetLastPruneResult() (*result.CommandResultSummary, error) { + if s.LastPruneResult == nil { + return nil, nil + } + var ret result.CommandResultSummary + err := yaml.ReadYamlBytes(s.LastPruneResult.Raw, &ret) + if err != nil { + return nil, err + } + return &ret, nil +} + +func (s *KluctlDeploymentStatus) GetLastValidateResult() (*result.ValidateResult, error) { + if s.LastValidateResult == nil { + return nil, nil + } + var ret result.ValidateResult + err := yaml.ReadYamlBytes(s.LastValidateResult.Raw, &ret) + if err != nil { + return nil, err + } + return &ret, nil } //+kubebuilder:object:root=true diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 79c1afc75..1483d24ad 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -255,17 +255,17 @@ func (in *KluctlDeploymentStatus) DeepCopyInto(out *KluctlDeploymentStatus) { } if in.LastDeployResult != nil { in, out := &in.LastDeployResult, &out.LastDeployResult - *out = new(result.CommandResultSummary) + *out = new(runtime.RawExtension) (*in).DeepCopyInto(*out) } if in.LastPruneResult != nil { in, out := &in.LastPruneResult, &out.LastPruneResult - *out = new(result.CommandResultSummary) + *out = new(runtime.RawExtension) (*in).DeepCopyInto(*out) } if in.LastValidateResult != nil { in, out := &in.LastValidateResult, &out.LastValidateResult - *out = new(result.ValidateResult) + *out = new(runtime.RawExtension) (*in).DeepCopyInto(*out) } } diff --git a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml index d07ed9349..3254ed420 100644 --- a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml +++ b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml @@ -447,273 +447,8 @@ spec: type: string lastDeployResult: description: LastDeployResult is the result of the last deploy command - properties: - appliedHookObjects: - type: integer - appliedObjects: - type: integer - changedObjects: - type: integer - clusterInfo: - properties: - clusterId: - type: string - required: - - clusterId - type: object - commandInfo: - properties: - abortOnError: - type: boolean - args: - type: object - x-kubernetes-preserve-unknown-fields: true - command: - type: string - contextOverride: - type: string - dryRun: - type: boolean - endTime: - format: date-time - type: string - excludeDeploymentDirs: - items: - type: string - type: array - excludeTags: - items: - type: string - type: array - forceApply: - type: boolean - forceReplaceOnError: - type: boolean - images: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - resultImage: - type: string - required: - - image - - resultImage - type: object - type: array - includeDeploymentDirs: - items: - type: string - type: array - includeTags: - items: - type: string - type: array - initiator: - type: string - kluctlDeployment: - properties: - gitRef: - type: string - gitUrl: - type: string - name: - type: string - namespace: - type: string - required: - - gitRef - - gitUrl - - name - - namespace - type: object - noWait: - type: boolean - replaceOnError: - type: boolean - startTime: - format: date-time - type: string - target: - type: string - targetNameOverride: - type: string - required: - - endTime - - initiator - - startTime - type: object - deletedObjects: - type: integer - errors: - type: integer - gitInfo: - properties: - commit: - type: string - dirty: - type: boolean - ref: - type: string - subDir: - type: string - url: - type: string - required: - - commit - - dirty - - ref - - subDir - - url - type: object - id: - type: string - newObjects: - type: integer - orphanObjects: - type: integer - projectKey: - properties: - gitRepoKey: - type: string - subDir: - type: string - type: object - remoteObjects: - type: integer - renderedObjects: - type: integer - target: - properties: - args: - type: object - x-kubernetes-preserve-unknown-fields: true - context: - type: string - discriminator: - type: string - images: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - resultImage: - type: string - required: - - image - - resultImage - type: object - type: array - name: - type: string - sealingConfig: - properties: - args: - type: object - x-kubernetes-preserve-unknown-fields: true - certFile: - type: string - secretSets: - items: - type: string - type: array - type: object - required: - - name - type: object - targetKey: - properties: - clusterId: - type: string - discriminator: - type: string - targetName: - type: string - required: - - clusterId - type: object - totalChanges: - type: integer - warnings: - type: integer - required: - - appliedHookObjects - - appliedObjects - - changedObjects - - commandInfo - - deletedObjects - - errors - - id - - newObjects - - orphanObjects - - projectKey - - remoteObjects - - renderedObjects - - target - - targetKey - - totalChanges - - warnings type: object + x-kubernetes-preserve-unknown-fields: true lastHandledReconcileAt: description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can @@ -725,417 +460,15 @@ spec: type: string lastPruneResult: description: LastDeployResult is the result of the last prune command - properties: - appliedHookObjects: - type: integer - appliedObjects: - type: integer - changedObjects: - type: integer - clusterInfo: - properties: - clusterId: - type: string - required: - - clusterId - type: object - commandInfo: - properties: - abortOnError: - type: boolean - args: - type: object - x-kubernetes-preserve-unknown-fields: true - command: - type: string - contextOverride: - type: string - dryRun: - type: boolean - endTime: - format: date-time - type: string - excludeDeploymentDirs: - items: - type: string - type: array - excludeTags: - items: - type: string - type: array - forceApply: - type: boolean - forceReplaceOnError: - type: boolean - images: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - resultImage: - type: string - required: - - image - - resultImage - type: object - type: array - includeDeploymentDirs: - items: - type: string - type: array - includeTags: - items: - type: string - type: array - initiator: - type: string - kluctlDeployment: - properties: - gitRef: - type: string - gitUrl: - type: string - name: - type: string - namespace: - type: string - required: - - gitRef - - gitUrl - - name - - namespace - type: object - noWait: - type: boolean - replaceOnError: - type: boolean - startTime: - format: date-time - type: string - target: - type: string - targetNameOverride: - type: string - required: - - endTime - - initiator - - startTime - type: object - deletedObjects: - type: integer - errors: - type: integer - gitInfo: - properties: - commit: - type: string - dirty: - type: boolean - ref: - type: string - subDir: - type: string - url: - type: string - required: - - commit - - dirty - - ref - - subDir - - url - type: object - id: - type: string - newObjects: - type: integer - orphanObjects: - type: integer - projectKey: - properties: - gitRepoKey: - type: string - subDir: - type: string - type: object - remoteObjects: - type: integer - renderedObjects: - type: integer - target: - properties: - args: - type: object - x-kubernetes-preserve-unknown-fields: true - context: - type: string - discriminator: - type: string - images: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - resultImage: - type: string - required: - - image - - resultImage - type: object - type: array - name: - type: string - sealingConfig: - properties: - args: - type: object - x-kubernetes-preserve-unknown-fields: true - certFile: - type: string - secretSets: - items: - type: string - type: array - type: object - required: - - name - type: object - targetKey: - properties: - clusterId: - type: string - discriminator: - type: string - targetName: - type: string - required: - - clusterId - type: object - totalChanges: - type: integer - warnings: - type: integer - required: - - appliedHookObjects - - appliedObjects - - changedObjects - - commandInfo - - deletedObjects - - errors - - id - - newObjects - - orphanObjects - - projectKey - - remoteObjects - - renderedObjects - - target - - targetKey - - totalChanges - - warnings type: object + x-kubernetes-preserve-unknown-fields: true lastValidateError: type: string lastValidateResult: description: LastValidateResult is the result of the last validate command - properties: - drift: - items: - properties: - changes: - items: - properties: - jsonPath: - type: string - newValue: - x-kubernetes-preserve-unknown-fields: true - oldValue: - x-kubernetes-preserve-unknown-fields: true - type: - type: string - unifiedDiff: - type: string - required: - - jsonPath - - type - type: object - type: array - ref: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - required: - - ref - type: object - type: array - endTime: - format: date-time - type: string - errors: - items: - properties: - message: - type: string - ref: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - required: - - message - - ref - type: object - type: array - id: - type: string - ready: - type: boolean - results: - items: - properties: - annotation: - type: string - message: - type: string - ref: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - required: - - annotation - - message - - ref - type: object - type: array - startTime: - format: date-time - type: string - warnings: - items: - properties: - message: - type: string - ref: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - required: - - message - - ref - type: object - type: array - required: - - endTime - - id - - ready - - startTime type: object + x-kubernetes-preserve-unknown-fields: true observedCommit: description: ObservedCommit is the last commit observed type: string diff --git a/docs/reference/gitops/api/kluctl-controller.md b/docs/reference/gitops/api/kluctl-controller.md index f1c1d35ee..7d64b8644 100644 --- a/docs/reference/gitops/api/kluctl-controller.md +++ b/docs/reference/gitops/api/kluctl-controller.md @@ -1194,7 +1194,7 @@ string lastDeployResult
    -github.com/kluctl/kluctl/v2/pkg/types/result.CommandResultSummary +k8s.io/apimachinery/pkg/runtime.RawExtension @@ -1206,7 +1206,7 @@ github.com/kluctl/kluctl/v2/pkg/types/result.CommandResultSummary lastPruneResult
    -github.com/kluctl/kluctl/v2/pkg/types/result.CommandResultSummary +k8s.io/apimachinery/pkg/runtime.RawExtension @@ -1218,7 +1218,7 @@ github.com/kluctl/kluctl/v2/pkg/types/result.CommandResultSummary lastValidateResult
    -github.com/kluctl/kluctl/v2/pkg/types/result.ValidateResult +k8s.io/apimachinery/pkg/runtime.RawExtension diff --git a/e2e/gitops_errors_test.go b/e2e/gitops_errors_test.go index 56249cf9b..d3ddf8a71 100644 --- a/e2e/gitops_errors_test.go +++ b/e2e/gitops_errors_test.go @@ -44,8 +44,10 @@ func (suite *GitopsTestSuite) assertErrors(key client.ObjectKey, rstatus metav1. rs, err := results.NewResultStoreSecrets(context.TODO(), suite.k.Client, nil, "", 0) g.Expect(err).To(Succeed()) + lastDeployResult, err := kd.Status.GetLastDeployResult() + g.Expect(err).To(Succeed()) cr, err := rs.GetCommandResult(results.GetCommandResultOptions{ - Id: kd.Status.LastDeployResult.Id, + Id: lastDeployResult.Id, }) g.Expect(err).To(Succeed()) diff --git a/install/controller/controller/crd.yaml b/install/controller/controller/crd.yaml index 11208546d..0515fbe6e 100644 --- a/install/controller/controller/crd.yaml +++ b/install/controller/controller/crd.yaml @@ -446,273 +446,8 @@ spec: type: string lastDeployResult: description: LastDeployResult is the result of the last deploy command - properties: - appliedHookObjects: - type: integer - appliedObjects: - type: integer - changedObjects: - type: integer - clusterInfo: - properties: - clusterId: - type: string - required: - - clusterId - type: object - commandInfo: - properties: - abortOnError: - type: boolean - args: - type: object - x-kubernetes-preserve-unknown-fields: true - command: - type: string - contextOverride: - type: string - dryRun: - type: boolean - endTime: - format: date-time - type: string - excludeDeploymentDirs: - items: - type: string - type: array - excludeTags: - items: - type: string - type: array - forceApply: - type: boolean - forceReplaceOnError: - type: boolean - images: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - resultImage: - type: string - required: - - image - - resultImage - type: object - type: array - includeDeploymentDirs: - items: - type: string - type: array - includeTags: - items: - type: string - type: array - initiator: - type: string - kluctlDeployment: - properties: - gitRef: - type: string - gitUrl: - type: string - name: - type: string - namespace: - type: string - required: - - gitRef - - gitUrl - - name - - namespace - type: object - noWait: - type: boolean - replaceOnError: - type: boolean - startTime: - format: date-time - type: string - target: - type: string - targetNameOverride: - type: string - required: - - endTime - - initiator - - startTime - type: object - deletedObjects: - type: integer - errors: - type: integer - gitInfo: - properties: - commit: - type: string - dirty: - type: boolean - ref: - type: string - subDir: - type: string - url: - type: string - required: - - commit - - dirty - - ref - - subDir - - url - type: object - id: - type: string - newObjects: - type: integer - orphanObjects: - type: integer - projectKey: - properties: - gitRepoKey: - type: string - subDir: - type: string - type: object - remoteObjects: - type: integer - renderedObjects: - type: integer - target: - properties: - args: - type: object - x-kubernetes-preserve-unknown-fields: true - context: - type: string - discriminator: - type: string - images: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - resultImage: - type: string - required: - - image - - resultImage - type: object - type: array - name: - type: string - sealingConfig: - properties: - args: - type: object - x-kubernetes-preserve-unknown-fields: true - certFile: - type: string - secretSets: - items: - type: string - type: array - type: object - required: - - name - type: object - targetKey: - properties: - clusterId: - type: string - discriminator: - type: string - targetName: - type: string - required: - - clusterId - type: object - totalChanges: - type: integer - warnings: - type: integer - required: - - appliedHookObjects - - appliedObjects - - changedObjects - - commandInfo - - deletedObjects - - errors - - id - - newObjects - - orphanObjects - - projectKey - - remoteObjects - - renderedObjects - - target - - targetKey - - totalChanges - - warnings type: object + x-kubernetes-preserve-unknown-fields: true lastHandledReconcileAt: description: LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change of the annotation value can @@ -724,417 +459,15 @@ spec: type: string lastPruneResult: description: LastDeployResult is the result of the last prune command - properties: - appliedHookObjects: - type: integer - appliedObjects: - type: integer - changedObjects: - type: integer - clusterInfo: - properties: - clusterId: - type: string - required: - - clusterId - type: object - commandInfo: - properties: - abortOnError: - type: boolean - args: - type: object - x-kubernetes-preserve-unknown-fields: true - command: - type: string - contextOverride: - type: string - dryRun: - type: boolean - endTime: - format: date-time - type: string - excludeDeploymentDirs: - items: - type: string - type: array - excludeTags: - items: - type: string - type: array - forceApply: - type: boolean - forceReplaceOnError: - type: boolean - images: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - resultImage: - type: string - required: - - image - - resultImage - type: object - type: array - includeDeploymentDirs: - items: - type: string - type: array - includeTags: - items: - type: string - type: array - initiator: - type: string - kluctlDeployment: - properties: - gitRef: - type: string - gitUrl: - type: string - name: - type: string - namespace: - type: string - required: - - gitRef - - gitUrl - - name - - namespace - type: object - noWait: - type: boolean - replaceOnError: - type: boolean - startTime: - format: date-time - type: string - target: - type: string - targetNameOverride: - type: string - required: - - endTime - - initiator - - startTime - type: object - deletedObjects: - type: integer - errors: - type: integer - gitInfo: - properties: - commit: - type: string - dirty: - type: boolean - ref: - type: string - subDir: - type: string - url: - type: string - required: - - commit - - dirty - - ref - - subDir - - url - type: object - id: - type: string - newObjects: - type: integer - orphanObjects: - type: integer - projectKey: - properties: - gitRepoKey: - type: string - subDir: - type: string - type: object - remoteObjects: - type: integer - renderedObjects: - type: integer - target: - properties: - args: - type: object - x-kubernetes-preserve-unknown-fields: true - context: - type: string - discriminator: - type: string - images: - items: - properties: - container: - type: string - deployTags: - items: - type: string - type: array - deployedImage: - type: string - deployment: - type: string - deploymentDir: - type: string - image: - type: string - namespace: - type: string - object: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - resultImage: - type: string - required: - - image - - resultImage - type: object - type: array - name: - type: string - sealingConfig: - properties: - args: - type: object - x-kubernetes-preserve-unknown-fields: true - certFile: - type: string - secretSets: - items: - type: string - type: array - type: object - required: - - name - type: object - targetKey: - properties: - clusterId: - type: string - discriminator: - type: string - targetName: - type: string - required: - - clusterId - type: object - totalChanges: - type: integer - warnings: - type: integer - required: - - appliedHookObjects - - appliedObjects - - changedObjects - - commandInfo - - deletedObjects - - errors - - id - - newObjects - - orphanObjects - - projectKey - - remoteObjects - - renderedObjects - - target - - targetKey - - totalChanges - - warnings type: object + x-kubernetes-preserve-unknown-fields: true lastValidateError: type: string lastValidateResult: description: LastValidateResult is the result of the last validate command - properties: - drift: - items: - properties: - changes: - items: - properties: - jsonPath: - type: string - newValue: - x-kubernetes-preserve-unknown-fields: true - oldValue: - x-kubernetes-preserve-unknown-fields: true - type: - type: string - unifiedDiff: - type: string - required: - - jsonPath - - type - type: object - type: array - ref: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - required: - - ref - type: object - type: array - endTime: - format: date-time - type: string - errors: - items: - properties: - message: - type: string - ref: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - required: - - message - - ref - type: object - type: array - id: - type: string - ready: - type: boolean - results: - items: - properties: - annotation: - type: string - message: - type: string - ref: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - required: - - annotation - - message - - ref - type: object - type: array - startTime: - format: date-time - type: string - warnings: - items: - properties: - message: - type: string - ref: - properties: - group: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - version: - type: string - required: - - kind - - name - type: object - required: - - message - - ref - type: object - type: array - required: - - endTime - - id - - ready - - startTime type: object + x-kubernetes-preserve-unknown-fields: true observedCommit: description: ObservedCommit is the last commit observed type: string diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 2f584b49d..3a7a4428f 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -14,6 +14,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/meta" "github.com/kluctl/kluctl/v2/pkg/utils/flux_utils/metrics" + "github.com/kluctl/kluctl/v2/pkg/yaml" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/errors" meta2 "k8s.io/apimachinery/pkg/api/meta" @@ -311,29 +312,26 @@ func (r *KluctlDeploymentReconciler) doReconcile( } else { err = fmt.Errorf("deployMode '%s' not supported", obj.Spec.DeployMode) } - obj.Status.LastDeployResult = deployResult.BuildSummary() - obj.Status.LastDeployError = "" + err = obj.Status.SetLastDeployResult(deployResult.BuildSummary(), err) if err != nil { - obj.Status.LastDeployError = err.Error() + log.Error(err, "Failed to write deploy result") } } if needPrune { // run garbage collection for stale objects that do not have pruning disabled pruneResult, err := pt.kluctlPrune(ctx, targetContext) - obj.Status.LastPruneResult = pruneResult.BuildSummary() - obj.Status.LastPruneError = "" + err = obj.Status.SetLastPruneResult(pruneResult.BuildSummary(), err) if err != nil { - obj.Status.LastPruneError = err.Error() + log.Error(err, "Failed to write prune result") } } if needValidate { validateResult, err := pt.kluctlValidate(ctx, targetContext, deployResult) - obj.Status.LastValidateResult = validateResult - obj.Status.LastValidateError = "" + err = obj.Status.SetLastValidateResult(validateResult, err) if err != nil { - obj.Status.LastValidateError = err.Error() + log.Error(err, "Failed to write validate result") } } @@ -344,7 +342,7 @@ func (r *KluctlDeploymentReconciler) doReconcile( ctrlResult.Requeue = true } - finalStatus, reason := r.buildFinalStatus(obj) + finalStatus, reason := r.buildFinalStatus(ctx, obj) if reason != kluctlv1.ReconciliationSucceededReason { setReadiness(obj, metav1.ConditionFalse, reason, finalStatus) internal_metrics.NewKluctlLastObjectStatus(obj.Namespace, obj.Name).Set(0.0) @@ -355,7 +353,9 @@ func (r *KluctlDeploymentReconciler) doReconcile( return &ctrlResult, nil } -func (r *KluctlDeploymentReconciler) buildFinalStatus(obj *kluctlv1.KluctlDeployment) (finalStatus string, reason string) { +func (r *KluctlDeploymentReconciler) buildFinalStatus(ctx context.Context, obj *kluctlv1.KluctlDeployment) (finalStatus string, reason string) { + log := ctrl.LoggerFrom(ctx) + if obj.Status.LastDeployError != "" { finalStatus = obj.Status.LastDeployError reason = kluctlv1.DeployFailedReason @@ -370,9 +370,31 @@ func (r *KluctlDeploymentReconciler) buildFinalStatus(obj *kluctlv1.KluctlDeploy return } - deployOk := obj.Status.LastDeployResult != nil && obj.Status.LastDeployResult.Errors == 0 - pruneOk := obj.Status.LastPruneResult != nil && obj.Status.LastPruneResult.Errors == 0 - validateOk := obj.Status.LastValidateResult != nil && len(obj.Status.LastValidateResult.Errors) == 0 && obj.Status.LastValidateResult.Ready + var lastDeployResult *result.CommandResultSummary + var lastPruneResult *result.CommandResultSummary + var lastValidateResult *result.ValidateResult + if obj.Status.LastDeployResult != nil { + err := yaml.ReadYamlBytes(obj.Status.LastDeployResult.Raw, &lastDeployResult) + if err != nil { + log.Info(fmt.Sprintf("Failed to parse last deploy result: %s", err.Error())) + } + } + if obj.Status.LastPruneResult != nil { + err := yaml.ReadYamlBytes(obj.Status.LastPruneResult.Raw, &lastPruneResult) + if err != nil { + log.Info(fmt.Sprintf("Failed to parse last prune result: %s", err.Error())) + } + } + if obj.Status.LastValidateResult != nil { + err := yaml.ReadYamlBytes(obj.Status.LastValidateResult.Raw, &lastValidateResult) + if err != nil { + log.Info(fmt.Sprintf("Failed to parse last validate result: %s", err.Error())) + } + } + + deployOk := lastDeployResult != nil && lastDeployResult.Errors == 0 + pruneOk := lastPruneResult != nil && lastPruneResult.Errors == 0 + validateOk := lastValidateResult != nil && len(lastValidateResult.Errors) == 0 && lastValidateResult.Ready if !obj.Spec.Prune { pruneOk = true @@ -464,7 +486,13 @@ func (r *KluctlDeploymentReconciler) nextDeployTime(obj *kluctlv1.KluctlDeployme return nil } - t := obj.Status.LastDeployResult.Command.EndTime.Add(obj.Spec.DeployInterval.Duration.Duration) + var lastDeployResult result.CommandResultSummary + err := yaml.ReadYamlBytes(obj.Status.LastDeployResult.Raw, &lastDeployResult) + if err != nil { + return nil + } + + t := lastDeployResult.Command.EndTime.Add(obj.Spec.DeployInterval.Duration.Duration) return &t } @@ -489,7 +517,13 @@ func (r *KluctlDeploymentReconciler) nextValidateTime(obj *kluctlv1.KluctlDeploy d = obj.Spec.ValidateInterval.Duration.Duration } - t := obj.Status.LastValidateResult.EndTime.Add(d) + var lastValidateResult result.ValidateResult + err := yaml.ReadYamlBytes(obj.Status.LastValidateResult.Raw, &lastValidateResult) + if err != nil { + return nil + } + + t := lastValidateResult.EndTime.Add(d) return &t } From 505991b8efb6f8fa08b57bbf97f99fcb106aeef5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 2 Jun 2023 17:30:55 +0200 Subject: [PATCH 1107/2268] fix: Actually write errors and not only error count into status --- e2e/gitops_errors_test.go | 4 ++-- e2e/gitops_test.go | 4 +++- pkg/controllers/kluctl_project.go | 4 ++-- pkg/controllers/kluctldeployment_controller.go | 4 ++-- pkg/types/result/summary.go | 9 +++++---- pkg/types/result/zz_generated.deepcopy.go | 10 ++++++++++ 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/e2e/gitops_errors_test.go b/e2e/gitops_errors_test.go index d3ddf8a71..a243b0edc 100644 --- a/e2e/gitops_errors_test.go +++ b/e2e/gitops_errors_test.go @@ -54,8 +54,8 @@ func (suite *GitopsTestSuite) assertErrors(key client.ObjectKey, rstatus metav1. g.Expect(cr.Errors).To(ConsistOf(expectedErrors)) g.Expect(cr.Warnings).To(ConsistOf(expectedWarnings)) - g.Expect(kd.Status.LastDeployResult.Errors).To(Equal(len(expectedErrors))) - g.Expect(kd.Status.LastDeployResult.Warnings).To(Equal(len(expectedWarnings))) + g.Expect(lastDeployResult.Errors).To(ConsistOf(expectedErrors)) + g.Expect(lastDeployResult.Warnings).To(ConsistOf(expectedWarnings)) } func (suite *GitopsTestSuite) TestGitOpsErrors() { diff --git a/e2e/gitops_test.go b/e2e/gitops_test.go index edb46e916..72d4dc89f 100644 --- a/e2e/gitops_test.go +++ b/e2e/gitops_test.go @@ -567,7 +567,9 @@ data: if obj.Status.LastDeployResult == nil { return false } - return obj.Status.LastDeployResult.GitInfo.Commit == getHeadRevision(suite.T(), p) + ldr, err := obj.Status.GetLastDeployResult() + g.Expect(err).To(Succeed()) + return ldr.GitInfo.Commit == getHeadRevision(suite.T(), p) }, timeout, time.Second).Should(BeTrue()) suite.Run("cm1 and cm2 were not deleted", func() { diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 715fbf36b..10f1e589c 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -820,6 +820,6 @@ func (pt *preparedTarget) exportCommandResultMetricsToProm(summary *result.Comma internal_metrics.NewKluctlNumberOfDeletedObjects(pt.pp.obj.Namespace, pt.pp.obj.Name).Set(float64(summary.DeletedObjects)) internal_metrics.NewKluctlNumberOfChangedObjects(pt.pp.obj.Namespace, pt.pp.obj.Name).Set(float64(summary.ChangedObjects)) internal_metrics.NewKluctlNumberOfOrphanObjects(pt.pp.obj.Namespace, pt.pp.obj.Name).Set(float64(summary.OrphanObjects)) - internal_metrics.NewKluctlNumberOfWarnings(pt.pp.obj.Namespace, pt.pp.obj.Name, summary.Command.Command).Set(float64(summary.Warnings)) - internal_metrics.NewKluctlNumberOfErrors(pt.pp.obj.Namespace, pt.pp.obj.Name, summary.Command.Command).Set(float64(summary.Errors)) + internal_metrics.NewKluctlNumberOfWarnings(pt.pp.obj.Namespace, pt.pp.obj.Name, summary.Command.Command).Set(float64(len(summary.Warnings))) + internal_metrics.NewKluctlNumberOfErrors(pt.pp.obj.Namespace, pt.pp.obj.Name, summary.Command.Command).Set(float64(len(summary.Errors))) } diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 3a7a4428f..9759d04d2 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -392,8 +392,8 @@ func (r *KluctlDeploymentReconciler) buildFinalStatus(ctx context.Context, obj * } } - deployOk := lastDeployResult != nil && lastDeployResult.Errors == 0 - pruneOk := lastPruneResult != nil && lastPruneResult.Errors == 0 + deployOk := lastDeployResult != nil && len(lastDeployResult.Errors) == 0 + pruneOk := lastPruneResult != nil && len(lastPruneResult.Errors) == 0 validateOk := lastValidateResult != nil && len(lastValidateResult.Errors) == 0 && lastValidateResult.Ready if !obj.Spec.Prune { diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index 85327294a..dc4c38b74 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -23,8 +23,9 @@ type CommandResultSummary struct { ChangedObjects int `json:"changedObjects"` OrphanObjects int `json:"orphanObjects"` DeletedObjects int `json:"deletedObjects"` - Errors int `json:"errors"` - Warnings int `json:"warnings"` + + Errors []DeploymentError `json:"errors"` + Warnings []DeploymentError `json:"warnings"` TotalChanges int `json:"totalChanges"` } @@ -73,8 +74,8 @@ func (cr *CommandResult) BuildSummary() *CommandResultSummary { ChangedObjects: count(func(o ResultObject) bool { return len(o.Changes) != 0 }), OrphanObjects: count(func(o ResultObject) bool { return o.Orphan }), DeletedObjects: count(func(o ResultObject) bool { return o.Deleted }), - Errors: len(cr.Errors), - Warnings: len(cr.Warnings), + Errors: cr.Errors, + Warnings: cr.Warnings, } for _, o := range cr.Objects { ret.TotalChanges += len(o.Changes) diff --git a/pkg/types/result/zz_generated.deepcopy.go b/pkg/types/result/zz_generated.deepcopy.go index 40ade0029..a461c0278 100644 --- a/pkg/types/result/zz_generated.deepcopy.go +++ b/pkg/types/result/zz_generated.deepcopy.go @@ -224,6 +224,16 @@ func (in *CommandResultSummary) DeepCopyInto(out *CommandResultSummary) { in.Command.DeepCopyInto(&out.Command) in.GitInfo.DeepCopyInto(&out.GitInfo) out.ClusterInfo = in.ClusterInfo + if in.Errors != nil { + in, out := &in.Errors, &out.Errors + *out = make([]DeploymentError, len(*in)) + copy(*out, *in) + } + if in.Warnings != nil { + in, out := &in.Warnings, &out.Warnings + *out = make([]DeploymentError, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandResultSummary. From 4ba9b138804da6b877e5e8dc0623a4e644c3bc03 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 3 Jun 2023 00:08:33 +0200 Subject: [PATCH 1108/2268] ci: Add .sv4git.yml to filter tags (#543) --- .sv4git.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .sv4git.yml diff --git a/.sv4git.yml b/.sv4git.yml new file mode 100644 index 000000000..92d3bea10 --- /dev/null +++ b/.sv4git.yml @@ -0,0 +1,2 @@ +tag: + filter: 'v[0-9]*' From 7a2096d2e8db253e3ea363bfe7df325a9e9a1599 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 3 Jun 2023 00:09:28 +0200 Subject: [PATCH 1109/2268] build: Preparing release v2.20.4 --- docs/installation.md | 2 +- install/controller/.kluctl.yaml | 2 +- install/controller/controller/kustomization.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index ffb8a3055..d1f2bbc17 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -113,5 +113,5 @@ deployments: - git: url: https://github.com/kluctl/kluctl.git subDir: install/controller - ref: v2.20.3 + ref: v2.20.4 ``` diff --git a/install/controller/.kluctl.yaml b/install/controller/.kluctl.yaml index d45892605..61be49c62 100644 --- a/install/controller/.kluctl.yaml +++ b/install/controller/.kluctl.yaml @@ -2,7 +2,7 @@ discriminator: kluctl.io-controller args: - name: controller_version - default: v2.20.3 + default: v2.20.4 - name: controller_args default: [] - name: controller_envs diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml index 06923f9bd..be62c808a 100644 --- a/install/controller/controller/kustomization.yaml +++ b/install/controller/controller/kustomization.yaml @@ -1,4 +1,4 @@ -{% set controller_version = get_var("args.controller_version", "v2.20.3") %} +{% set controller_version = get_var("args.controller_version", "v2.20.4") %} resources: - crd.yaml From 556a3e951b03534fed8fc5f384a6718a9d280bc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 08:36:22 +0200 Subject: [PATCH 1110/2268] chore(deps): Bump github.com/ohler55/ojg from 1.18.6 to 1.18.7 (#544) Bumps [github.com/ohler55/ojg](https://github.com/ohler55/ojg) from 1.18.6 to 1.18.7. - [Changelog](https://github.com/ohler55/ojg/blob/develop/CHANGELOG.md) - [Commits](https://github.com/ohler55/ojg/compare/v1.18.6...v1.18.7) --- updated-dependencies: - dependency-name: github.com/ohler55/ojg dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c7d16d423..ecbb99ab8 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-runewidth v0.0.14 github.com/mitchellh/reflectwalk v1.0.2 - github.com/ohler55/ojg v1.18.6 + github.com/ohler55/ojg v1.18.7 github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.10.0 diff --git a/go.sum b/go.sum index 77124abb6..45dbf8ec4 100644 --- a/go.sum +++ b/go.sum @@ -654,8 +654,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/nelsam/hel/v2 v2.3.3/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/ohler55/ojg v1.18.6 h1:ZY5I/8I+zW8s+/QpX9E/P9QJwECi4lNx67VgdOzTTno= -github.com/ohler55/ojg v1.18.6/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= +github.com/ohler55/ojg v1.18.7 h1:sC7zy0usEiWa6bvx3NU1yZH4kCA2F3Qzs6iiDX4+xdk= +github.com/ohler55/ojg v1.18.7/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= From 8b09e0fec9718962dc820737ab1bd9829cd3b968 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Jun 2023 18:31:45 +0200 Subject: [PATCH 1111/2268] fix: Properly convert viber bool/int to string args (#548) --- cmd/kluctl/commands/cobra_utils.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/kluctl/commands/cobra_utils.go b/cmd/kluctl/commands/cobra_utils.go index de630a332..8ecf0fbf2 100644 --- a/cmd/kluctl/commands/cobra_utils.go +++ b/cmd/kluctl/commands/cobra_utils.go @@ -251,6 +251,10 @@ func copyViperValuesToCobraFlags(flags *pflag.FlagSet) error { if v != nil { if x, ok := v.(string); ok { a = []string{x} + } else if x, ok := v.(bool); ok { + a = []string{strconv.FormatBool(x)} + } else if x, ok := v.(int); ok { + a = []string{strconv.FormatInt(int64(x), 10)} } else if x, ok := v.([]any); ok { for _, y := range x { s, ok := y.(string) From 189264cbaebdbd0ee7d421e03643255ba441686a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Jun 2023 23:10:25 +0200 Subject: [PATCH 1112/2268] tests: Add origin remote to test git repos --- pkg/git/test_git_server.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/git/test_git_server.go b/pkg/git/test_git_server.go index 26b656b5c..eee519037 100644 --- a/pkg/git/test_git_server.go +++ b/pkg/git/test_git_server.go @@ -3,6 +3,7 @@ package git import ( "context" "fmt" + config2 "github.com/go-git/go-git/v5/config" "log" "net" "net/http" @@ -84,6 +85,15 @@ func (p *TestGitServer) GitInit(repo string) { if err != nil { p.t.Fatal(err) } + + _, err = r.CreateRemote(&config2.RemoteConfig{ + Name: "origin", + URLs: []string{p.GitRepoUrl(repo)}, + }) + if err != nil { + p.t.Fatal(err) + } + config, err := r.Config() if err != nil { p.t.Fatal(err) From d560e337c77a0380ca46836f7df63990d404aee0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Jun 2023 23:11:06 +0200 Subject: [PATCH 1113/2268] tests: Filter for project key in TestWriteResult --- e2e/results_test.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/e2e/results_test.go b/e2e/results_test.go index b2ceea17d..000265647 100644 --- a/e2e/results_test.go +++ b/e2e/results_test.go @@ -4,6 +4,7 @@ import ( "context" test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "github.com/stretchr/testify/assert" @@ -44,7 +45,13 @@ func TestWriteResult(t *testing.T) { rs, err := results.NewResultStoreSecrets(context.Background(), k.Client, nil, "kluctl-results", 0) assert.NoError(t, err) - summaries, err := rs.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + opts := results.ListCommandResultSummariesOptions{ + ProjectFilter: &result.ProjectKey{ + GitRepoKey: types.ParseGitUrlMust(p.GitUrl()).RepoKey(), + }, + } + + summaries, err := rs.ListCommandResultSummaries(opts) assert.NoError(t, err) assert.Len(t, summaries, 1) assertSummary(t, result.CommandResultSummary{ @@ -63,7 +70,7 @@ func TestWriteResult(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test", "--write-command-result") assertConfigMapExists(t, k, p.TestSlug(), "cm2") - summaries, err = rs.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + summaries, err = rs.ListCommandResultSummaries(opts) assert.NoError(t, err) assert.Len(t, summaries, 2) assertSummary(t, result.CommandResultSummary{ @@ -80,7 +87,7 @@ func TestWriteResult(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test", "--write-command-result") assertConfigMapExists(t, k, p.TestSlug(), "cm2") - summaries, err = rs.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + summaries, err = rs.ListCommandResultSummaries(opts) assert.NoError(t, err) assert.Len(t, summaries, 3) assertSummary(t, result.CommandResultSummary{ @@ -91,7 +98,7 @@ func TestWriteResult(t *testing.T) { p.KluctlMust("prune", "--yes", "-t", "test", "--write-command-result") assertConfigMapNotExists(t, k, p.TestSlug(), "cm2") - summaries, err = rs.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + summaries, err = rs.ListCommandResultSummaries(opts) assert.NoError(t, err) assert.Len(t, summaries, 4) assertSummary(t, result.CommandResultSummary{ From fde5c6e065412d196bbe6c6d62e6b2d996da3d5c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Jun 2023 23:12:09 +0200 Subject: [PATCH 1114/2268] feat: Implement --prune flag for deploy command --- cmd/kluctl/commands/cmd_deploy.go | 3 ++ docs/reference/commands/deploy.md | 2 + e2e/prune_test.go | 73 ++++++++++++++++++++++++++++ pkg/deployment/commands/delete.go | 5 +- pkg/deployment/commands/deploy.go | 25 +++++++++- pkg/deployment/commands/prune.go | 5 +- pkg/deployment/utils/delete_utils.go | 4 +- 7 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 e2e/prune_test.go diff --git a/cmd/kluctl/commands/cmd_deploy.go b/cmd/kluctl/commands/cmd_deploy.go index e86f1a53d..fbd88f62c 100644 --- a/cmd/kluctl/commands/cmd_deploy.go +++ b/cmd/kluctl/commands/cmd_deploy.go @@ -27,6 +27,7 @@ type deployCmd struct { args.CommandResultFlags NoWait bool `group:"misc" help:"Don't wait for objects readiness'"` + Prune bool `group:"misc" help:"Prune orphaned objects directly after deploying. See the help for the 'prune' sub-command for details.'"` internal bool } @@ -67,6 +68,8 @@ func (cmd *deployCmd) runCmdDeploy(cmdCtx *commandCtx) error { cmd2.AbortOnError = cmd.AbortOnError cmd2.ReadinessTimeout = cmd.ReadinessTimeout cmd2.NoWait = cmd.NoWait + cmd2.Prune = cmd.Prune + cmd2.WaitPrune = !cmd.NoWait cb := func(diffResult *result.CommandResult) error { return cmd.diffResultCb(cmdCtx, diffResult) diff --git a/docs/reference/commands/deploy.md b/docs/reference/commands/deploy.md index 344fb448b..ff964cf64 100644 --- a/docs/reference/commands/deploy.md +++ b/docs/reference/commands/deploy.md @@ -61,6 +61,8 @@ Misc arguments: 'format=path'. Format can either be 'text' or 'yaml'. Can be specified multiple times. The actual format for yaml is currently not documented and subject to change. + --prune Prune orphaned objects directly after deploying. See the help + for the 'prune' sub-command for details.' --readiness-timeout duration Maximum time to wait for object readiness. The timeout is meant per-object. Timeouts are in the duration format (1s, 1m, 1h, ...). If not specified, a default timeout of 5m is used. diff --git a/e2e/prune_test.go b/e2e/prune_test.go new file mode 100644 index 000000000..d70c55fb6 --- /dev/null +++ b/e2e/prune_test.go @@ -0,0 +1,73 @@ +package e2e + +import ( + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "testing" +) + +func TestPrune(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + addConfigMapDeployment(p, "cm1", map[string]string{}, resourceOpts{ + name: "cm1", + namespace: p.TestSlug(), + }) + addConfigMapDeployment(p, "cm2", map[string]string{}, resourceOpts{ + name: "cm2", + namespace: p.TestSlug(), + }) + + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.TestSlug(), "cm1") + assertConfigMapExists(t, k, p.TestSlug(), "cm2") + + p.DeleteKustomizeDeployment("cm2") + + p.KluctlMust("prune", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.TestSlug(), "cm1") + assertConfigMapNotExists(t, k, p.TestSlug(), "cm2") +} + +func TestDeployWithPrune(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + addConfigMapDeployment(p, "cm1", map[string]string{}, resourceOpts{ + name: "cm1", + namespace: p.TestSlug(), + }) + addConfigMapDeployment(p, "cm2", map[string]string{}, resourceOpts{ + name: "cm2", + namespace: p.TestSlug(), + }) + + p.KluctlMust("deploy", "--yes", "-t", "test") + assertConfigMapExists(t, k, p.TestSlug(), "cm1") + assertConfigMapExists(t, k, p.TestSlug(), "cm2") + + p.DeleteKustomizeDeployment("cm2") + addConfigMapDeployment(p, "cm3", map[string]string{}, resourceOpts{ + name: "cm3", + namespace: p.TestSlug(), + }) + + p.KluctlMust("deploy", "--yes", "-t", "test", "--prune") + assertConfigMapExists(t, k, p.TestSlug(), "cm1") + assertConfigMapNotExists(t, k, p.TestSlug(), "cm2") + assertConfigMapExists(t, k, p.TestSlug(), "cm3") +} diff --git a/pkg/deployment/commands/delete.go b/pkg/deployment/commands/delete.go index 28794cb9a..6edf12e0c 100644 --- a/pkg/deployment/commands/delete.go +++ b/pkg/deployment/commands/delete.go @@ -66,10 +66,7 @@ func (cmd *DeleteCommand) Run(ctx context.Context, k *k8s.K8sCluster, confirmCb } } - deleted, err := utils2.DeleteObjects(ctx, k, deleteRefs, dew, cmd.wait) - if err != nil { - return nil, err - } + deleted := utils2.DeleteObjects(ctx, k, deleteRefs, dew, cmd.wait) var c *deployment.DeploymentCollection if cmd.targetCtx != nil { diff --git a/pkg/deployment/commands/deploy.go b/pkg/deployment/commands/deploy.go index f2fd0a5b7..2d0979c7a 100644 --- a/pkg/deployment/commands/deploy.go +++ b/pkg/deployment/commands/deploy.go @@ -20,6 +20,8 @@ type DeployCommand struct { AbortOnError bool ReadinessTimeout time.Duration NoWait bool + Prune bool + WaitPrune bool } func NewDeployCommand(targetCtx *kluctl_project.TargetContext) *DeployCommand { @@ -92,9 +94,30 @@ func (cmd *DeployCommand) Run(diffResultCb func(diffResult *result.CommandResult if err != nil { return nil, err } + + var deleted []k8s2.ObjectRef + if cmd.Prune && cmd.targetCtx.Target.Discriminator == "" { + dew.AddError(k8s2.ObjectRef{}, fmt.Errorf("pruning without a discriminator is not supported")) + } else if cmd.Prune { + deleted = utils2.DeleteObjects(cmd.targetCtx.SharedContext.Ctx, cmd.targetCtx.SharedContext.K, orphanObjects, dew, cmd.WaitPrune) + + // now clean up the list of orphan objects (remove the ones that got deleted) + ds := map[k8s2.ObjectRef]bool{} + for _, x := range deleted { + ds[x] = true + } + var tmp []k8s2.ObjectRef + for _, x := range orphanObjects { + if _, ok := ds[x]; !ok { + tmp = append(tmp, x) + } + } + orphanObjects = tmp + } + r := &result.CommandResult{ Id: uuid.New().String(), - Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, au, du, orphanObjects, nil), + Objects: collectObjects(cmd.targetCtx.DeploymentCollection, ru, au, du, orphanObjects, deleted), Errors: dew.GetErrorsList(), Warnings: dew.GetWarningsList(), SeenImages: cmd.targetCtx.DeploymentCollection.Images.SeenImages(false), diff --git a/pkg/deployment/commands/prune.go b/pkg/deployment/commands/prune.go index e48093d7b..fa851d0d9 100644 --- a/pkg/deployment/commands/prune.go +++ b/pkg/deployment/commands/prune.go @@ -54,10 +54,7 @@ func (cmd *PruneCommand) Run(confirmCb func(refs []k8s2.ObjectRef) error) (*resu } } - deleted, err := utils2.DeleteObjects(cmd.targetCtx.SharedContext.Ctx, cmd.targetCtx.SharedContext.K, deleteRefs, dew, cmd.wait) - if err != nil { - return nil, err - } + deleted := utils2.DeleteObjects(cmd.targetCtx.SharedContext.Ctx, cmd.targetCtx.SharedContext.K, deleteRefs, dew, cmd.wait) r := &result.CommandResult{ Id: uuid.New().String(), diff --git a/pkg/deployment/utils/delete_utils.go b/pkg/deployment/utils/delete_utils.go index c32f41358..8e02c38a1 100644 --- a/pkg/deployment/utils/delete_utils.go +++ b/pkg/deployment/utils/delete_utils.go @@ -163,7 +163,7 @@ func FindObjectsForDelete(k *k8s.K8sCluster, allClusterObjects []*uo.Unstructure return ret, nil } -func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, dew *DeploymentErrorsAndWarnings, doWait bool) ([]k8s2.ObjectRef, error) { +func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef, dew *DeploymentErrorsAndWarnings, doWait bool) []k8s2.ObjectRef { g := utils.NewGoHelper(ctx, 8) var ret []k8s2.ObjectRef @@ -210,5 +210,5 @@ func DeleteObjects(ctx context.Context, k *k8s.K8sCluster, refs []k8s2.ObjectRef } g.Wait() - return ret, nil + return ret } From 89be6d63900c1809f3d09fe1c0f7a4833b273ff5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Jun 2023 23:23:42 +0200 Subject: [PATCH 1115/2268] feat: Use new prune feature of deploy command in controller --- api/v1beta1/condition_types.go | 4 -- api/v1beta1/kluctldeployment_types.go | 35 --------------- api/v1beta1/zz_generated.deepcopy.go | 5 --- .../gitops.kluctl.io_kluctldeployments.yaml | 6 --- .../reference/gitops/api/kluctl-controller.md | 23 ---------- e2e/gitops_errors_test.go | 6 ++- install/controller/controller/crd.yaml | 6 --- pkg/controllers/kluctl_project.go | 22 +-------- .../kluctldeployment_controller.go | 45 +------------------ 9 files changed, 7 insertions(+), 145 deletions(-) diff --git a/api/v1beta1/condition_types.go b/api/v1beta1/condition_types.go index 9bd45ed52..6a0711228 100644 --- a/api/v1beta1/condition_types.go +++ b/api/v1beta1/condition_types.go @@ -21,10 +21,6 @@ const ( // kluctl deploy command failed. DeployFailedReason string = "DeployFailed" - // PruneFailedReason represents the fact that the - // pruning of the KluctlDeployment failed. - PruneFailedReason string = "PruneFailed" - // ValidateFailedReason represents the fact that the // validate of the KluctlDeployment failed. ValidateFailedReason string = "ValidateFailed" diff --git a/api/v1beta1/kluctldeployment_types.go b/api/v1beta1/kluctldeployment_types.go index 30bfa45cc..3150f4130 100644 --- a/api/v1beta1/kluctldeployment_types.go +++ b/api/v1beta1/kluctldeployment_types.go @@ -322,8 +322,6 @@ type KluctlDeploymentStatus struct { // +optional LastDeployError string `json:"lastDeployError,omitempty"` // +optional - LastPruneError string `json:"lastPruneError,omitempty"` - // +optional LastValidateError string `json:"lastValidateError,omitempty"` // LastDeployResult is the result of the last deploy command @@ -331,10 +329,6 @@ type KluctlDeploymentStatus struct { // +kubebuilder:pruning:PreserveUnknownFields LastDeployResult *runtime.RawExtension `json:"lastDeployResult,omitempty"` - // LastDeployResult is the result of the last prune command - // +optional - LastPruneResult *runtime.RawExtension `json:"lastPruneResult,omitempty"` - // LastValidateResult is the result of the last validate command // +optional LastValidateResult *runtime.RawExtension `json:"lastValidateResult,omitempty"` @@ -357,23 +351,6 @@ func (s *KluctlDeploymentStatus) SetLastDeployResult(crs *result.CommandResultSu return nil } -func (s *KluctlDeploymentStatus) SetLastPruneResult(crs *result.CommandResultSummary, err error) error { - s.LastPruneError = "" - if err != nil { - s.LastPruneError = err.Error() - } - if crs == nil { - s.LastPruneResult = nil - } else { - b, err := yaml.WriteJsonString(crs) - if err != nil { - return err - } - s.LastPruneResult = &runtime.RawExtension{Raw: []byte(b)} - } - return nil -} - func (s *KluctlDeploymentStatus) SetLastValidateResult(crs *result.ValidateResult, err error) error { s.LastValidateError = "" if err != nil { @@ -403,18 +380,6 @@ func (s *KluctlDeploymentStatus) GetLastDeployResult() (*result.CommandResultSum return &ret, nil } -func (s *KluctlDeploymentStatus) GetLastPruneResult() (*result.CommandResultSummary, error) { - if s.LastPruneResult == nil { - return nil, nil - } - var ret result.CommandResultSummary - err := yaml.ReadYamlBytes(s.LastPruneResult.Raw, &ret) - if err != nil { - return nil, err - } - return &ret, nil -} - func (s *KluctlDeploymentStatus) GetLastValidateResult() (*result.ValidateResult, error) { if s.LastValidateResult == nil { return nil, nil diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 1483d24ad..338ac5635 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -258,11 +258,6 @@ func (in *KluctlDeploymentStatus) DeepCopyInto(out *KluctlDeploymentStatus) { *out = new(runtime.RawExtension) (*in).DeepCopyInto(*out) } - if in.LastPruneResult != nil { - in, out := &in.LastPruneResult, &out.LastPruneResult - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } if in.LastValidateResult != nil { in, out := &in.LastValidateResult, &out.LastValidateResult *out = new(runtime.RawExtension) diff --git a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml index 3254ed420..535e56610 100644 --- a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml +++ b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml @@ -456,12 +456,6 @@ spec: type: string lastObjectsHash: type: string - lastPruneError: - type: string - lastPruneResult: - description: LastDeployResult is the result of the last prune command - type: object - x-kubernetes-preserve-unknown-fields: true lastValidateError: type: string lastValidateResult: diff --git a/docs/reference/gitops/api/kluctl-controller.md b/docs/reference/gitops/api/kluctl-controller.md index 7d64b8644..9a87889ce 100644 --- a/docs/reference/gitops/api/kluctl-controller.md +++ b/docs/reference/gitops/api/kluctl-controller.md @@ -1170,17 +1170,6 @@ string -lastPruneError
    - -string - - - -(Optional) - - - - lastValidateError
    string @@ -1204,18 +1193,6 @@ k8s.io/apimachinery/pkg/runtime.RawExtension -lastPruneResult
    - -k8s.io/apimachinery/pkg/runtime.RawExtension - - - -(Optional) -

    LastDeployResult is the result of the last prune command

    - - - - lastValidateResult
    k8s.io/apimachinery/pkg/runtime.RawExtension diff --git a/e2e/gitops_errors_test.go b/e2e/gitops_errors_test.go index a243b0edc..c2df3e467 100644 --- a/e2e/gitops_errors_test.go +++ b/e2e/gitops_errors_test.go @@ -226,13 +226,15 @@ data: return nil }) suite.waitForCommit(key, getHeadRevision(suite.T(), p)) - suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.PruneFailedReason, "pruning without a discriminator is not supported", nil, nil) + suite.assertErrors(key, metav1.ConditionFalse, kluctlv1.DeployFailedReason, "deploy failed with 1 errors", []result.DeploymentError{ + {Message: "pruning without a discriminator is not supported"}, + }, nil) p.UpdateKluctlYaml(func(o *uo.UnstructuredObject) error { _ = o.SetNestedField(backup, "discriminator") return nil }) suite.waitForCommit(key, getHeadRevision(suite.T(), p)) - suite.assertErrors(key, metav1.ConditionTrue, kluctlv1.ReconciliationSucceededReason, "deploy: ok, prune: ok", nil, nil) + suite.assertErrors(key, metav1.ConditionTrue, kluctlv1.ReconciliationSucceededReason, "deploy: ok", nil, nil) suite.updateKluctlDeployment(key, func(kd *kluctlv1.KluctlDeployment) { kd.Spec.Prune = false }) diff --git a/install/controller/controller/crd.yaml b/install/controller/controller/crd.yaml index 0515fbe6e..b8c794f09 100644 --- a/install/controller/controller/crd.yaml +++ b/install/controller/controller/crd.yaml @@ -455,12 +455,6 @@ spec: type: string lastObjectsHash: type: string - lastPruneError: - type: string - lastPruneResult: - description: LastDeployResult is the result of the last prune command - type: object - x-kubernetes-preserve-unknown-fields: true lastValidateError: type: string lastValidateResult: diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 10f1e589c..ccbf7fd04 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -707,6 +707,8 @@ func (pt *preparedTarget) kluctlDeploy(ctx context.Context, targetContext *kluct cmd.AbortOnError = pt.pp.obj.Spec.AbortOnError cmd.ReadinessTimeout = time.Minute * 10 cmd.NoWait = pt.pp.obj.Spec.NoWait + cmd.Prune = pt.pp.obj.Spec.Prune + cmd.WaitPrune = false cmdResult, err := cmd.Run(nil) err = pt.handleCommandResult(ctx, err, cmdResult, "deploy") @@ -723,26 +725,6 @@ func (pt *preparedTarget) kluctlPokeImages(ctx context.Context, targetContext *k return cmdResult, err } -func (pt *preparedTarget) kluctlPrune(ctx context.Context, targetContext *kluctl_project.TargetContext) (*result.CommandResult, error) { - if !pt.pp.obj.Spec.Prune { - return nil, nil - } - - timer := prometheus.NewTimer(internal_metrics.NewKluctlPruneDuration(pt.pp.obj.ObjectMeta.Namespace, pt.pp.obj.ObjectMeta.Name)) - defer timer.ObserveDuration() - - cmd := commands.NewPruneCommand("", targetContext, false) - cmdResult, err := cmd.Run(func(refs []k8s.ObjectRef) error { - pt.printDeletedRefs(ctx, refs) - return nil - }) - if err != nil { - return nil, err - } - err = pt.handleCommandResult(ctx, err, cmdResult, "prune") - return cmdResult, err -} - func (pt *preparedTarget) kluctlValidate(ctx context.Context, targetContext *kluctl_project.TargetContext, cmdResult *result.CommandResult) (*result.ValidateResult, error) { timer := prometheus.NewTimer(internal_metrics.NewKluctlValidateDuration(pt.pp.obj.ObjectMeta.Namespace, pt.pp.obj.ObjectMeta.Name)) defer timer.ObserveDuration() diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index 9759d04d2..bd1aded1d 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -258,7 +258,6 @@ func (r *KluctlDeploymentReconciler) doReconcile( } needDeploy := false - needPrune := false needValidate := false if obj.Status.LastDeployResult == nil || obj.Status.LastObjectsHash != objectsHash { @@ -293,13 +292,6 @@ func (r *KluctlDeploymentReconciler) doReconcile( obj.Status.LastValidateError = "" } - if obj.Spec.Prune { - needPrune = needDeploy - } else { - obj.Status.LastPruneResult = nil - obj.Status.LastPruneError = "" - } - obj.Status.LastObjectsHash = objectsHash var deployResult *result.CommandResult @@ -318,15 +310,6 @@ func (r *KluctlDeploymentReconciler) doReconcile( } } - if needPrune { - // run garbage collection for stale objects that do not have pruning disabled - pruneResult, err := pt.kluctlPrune(ctx, targetContext) - err = obj.Status.SetLastPruneResult(pruneResult.BuildSummary(), err) - if err != nil { - log.Error(err, "Failed to write prune result") - } - } - if needValidate { validateResult, err := pt.kluctlValidate(ctx, targetContext, deployResult) err = obj.Status.SetLastValidateResult(validateResult, err) @@ -360,10 +343,6 @@ func (r *KluctlDeploymentReconciler) buildFinalStatus(ctx context.Context, obj * finalStatus = obj.Status.LastDeployError reason = kluctlv1.DeployFailedReason return - } else if obj.Status.LastPruneError != "" { - finalStatus = obj.Status.LastPruneError - reason = kluctlv1.PruneFailedReason - return } else if obj.Status.LastValidateError != "" { finalStatus = obj.Status.LastValidateError reason = kluctlv1.ValidateFailedReason @@ -371,7 +350,6 @@ func (r *KluctlDeploymentReconciler) buildFinalStatus(ctx context.Context, obj * } var lastDeployResult *result.CommandResultSummary - var lastPruneResult *result.CommandResultSummary var lastValidateResult *result.ValidateResult if obj.Status.LastDeployResult != nil { err := yaml.ReadYamlBytes(obj.Status.LastDeployResult.Raw, &lastDeployResult) @@ -379,12 +357,6 @@ func (r *KluctlDeploymentReconciler) buildFinalStatus(ctx context.Context, obj * log.Info(fmt.Sprintf("Failed to parse last deploy result: %s", err.Error())) } } - if obj.Status.LastPruneResult != nil { - err := yaml.ReadYamlBytes(obj.Status.LastPruneResult.Raw, &lastPruneResult) - if err != nil { - log.Info(fmt.Sprintf("Failed to parse last prune result: %s", err.Error())) - } - } if obj.Status.LastValidateResult != nil { err := yaml.ReadYamlBytes(obj.Status.LastValidateResult.Raw, &lastValidateResult) if err != nil { @@ -393,12 +365,8 @@ func (r *KluctlDeploymentReconciler) buildFinalStatus(ctx context.Context, obj * } deployOk := lastDeployResult != nil && len(lastDeployResult.Errors) == 0 - pruneOk := lastPruneResult != nil && len(lastPruneResult.Errors) == 0 validateOk := lastValidateResult != nil && len(lastValidateResult.Errors) == 0 && lastValidateResult.Ready - if !obj.Spec.Prune { - pruneOk = true - } if !obj.Spec.Validate { validateOk = true } @@ -411,17 +379,6 @@ func (r *KluctlDeploymentReconciler) buildFinalStatus(ctx context.Context, obj * finalStatus += "failed" } } - if obj.Spec.Prune && obj.Status.LastPruneResult != nil { - if finalStatus != "" { - finalStatus += ", " - } - finalStatus += "prune: " - if pruneOk { - finalStatus += "ok" - } else { - finalStatus += "failed" - } - } if obj.Spec.Validate && obj.Status.LastValidateResult != nil { if finalStatus != "" { finalStatus += ", " @@ -434,7 +391,7 @@ func (r *KluctlDeploymentReconciler) buildFinalStatus(ctx context.Context, obj * } } - if deployOk && pruneOk { + if deployOk { if validateOk { reason = kluctlv1.ReconciliationSucceededReason } else { From 5329041e6cf84ac885679e4fe6c7de5fe0cde7e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 21:32:28 +0200 Subject: [PATCH 1116/2268] chore(deps): Bump github.com/go-playground/validator/v10 (#551) Bumps [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) from 10.14.0 to 10.14.1. - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.14.0...v10.14.1) --- updated-dependencies: - dependency-name: github.com/go-playground/validator/v10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ecbb99ab8..0f9fb0fba 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/bitnami-labs/sealed-secrets v0.21.0 github.com/cyphar/filepath-securejoin v0.2.3 github.com/docker/distribution v2.8.2+incompatible - github.com/go-playground/validator/v10 v10.14.0 + github.com/go-playground/validator/v10 v10.14.1 github.com/gobwas/glob v0.2.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-containerregistry v0.15.2 diff --git a/go.sum b/go.sum index 45dbf8ec4..3e300ece1 100644 --- a/go.sum +++ b/go.sum @@ -313,8 +313,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= From fca4d93868dcb6f84a99e1c4c22a72de8c5eb3ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 21:32:42 +0200 Subject: [PATCH 1117/2268] chore(deps): Bump github.com/sirupsen/logrus from 1.9.2 to 1.9.3 (#552) Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.2 to 1.9.3. - [Release notes](https://github.com/sirupsen/logrus/releases) - [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md) - [Commits](https://github.com/sirupsen/logrus/compare/v1.9.2...v1.9.3) --- updated-dependencies: - dependency-name: github.com/sirupsen/logrus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0f9fb0fba..8a1c1250a 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v2 v2.15.1 github.com/rogpeppe/go-internal v1.10.0 - github.com/sirupsen/logrus v1.9.2 + github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.16.0 diff --git a/go.sum b/go.sum index 3e300ece1..92bed86a7 100644 --- a/go.sum +++ b/go.sum @@ -753,8 +753,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= -github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= From 52dcf1cc7d7b1234327db169dc1272393f082bea Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 6 Jun 2023 22:21:15 +0200 Subject: [PATCH 1118/2268] feat: Remove GitUrl/GitRef from KluctlDeploymentInfo This is duplicated info. --- pkg/types/result/command_result.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/types/result/command_result.go b/pkg/types/result/command_result.go index 7bc46c032..683086d98 100644 --- a/pkg/types/result/command_result.go +++ b/pkg/types/result/command_result.go @@ -29,8 +29,6 @@ type DeploymentError struct { type KluctlDeploymentInfo struct { Name string `json:"name"` Namespace string `json:"namespace"` - GitUrl string `json:"gitUrl"` - GitRef string `json:"gitRef"` } type CommandInitiator string From 67e301c3553ab9e9828efbd7688b28d6bf7c5618 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 6 Jun 2023 22:21:43 +0200 Subject: [PATCH 1119/2268] fix: Fill KluctlDeploymentInfo in CommandResult --- pkg/controllers/kluctl_project.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index ccbf7fd04..4b426668b 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -646,6 +646,11 @@ func (pt *preparedTarget) handleCommandResult(ctx context.Context, cmdErr error, } cmdResult.Command.Initiator = result.CommandInititiator_KluctlDeployment + cmdResult.Command.KluctlDeployment = &result.KluctlDeploymentInfo{ + Name: pt.pp.obj.Name, + Namespace: pt.pp.obj.Namespace, + } + cmdResult.GitInfo.Url = &pt.pp.obj.Spec.Source.URL cmdResult.GitInfo.Ref = pt.pp.obj.Spec.Source.Ref.String() cmdResult.ProjectKey.GitRepoKey = pt.pp.obj.Spec.Source.URL.RepoKey() From e21886e3581a6c3b5fa0be870e15c79ad4151d88 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Jun 2023 16:38:49 +0200 Subject: [PATCH 1120/2268] fix: Fix check for request-deploy annotation --- .../kluctldeployment_controller.go | 19 +++-------- .../kluctldeployment_controller_setup.go | 2 +- pkg/controllers/predicates.go | 34 +++---------------- 3 files changed, 10 insertions(+), 45 deletions(-) diff --git a/pkg/controllers/kluctldeployment_controller.go b/pkg/controllers/kluctldeployment_controller.go index bd1aded1d..6612a18fd 100644 --- a/pkg/controllers/kluctldeployment_controller.go +++ b/pkg/controllers/kluctldeployment_controller.go @@ -208,10 +208,10 @@ func (r *KluctlDeploymentReconciler) doReconcile( } oldGeneration := obj.Status.ObservedGeneration + oldDeployRequest := obj.Status.LastHandledDeployAt + curDeployRequest, _ := obj.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation] obj.Status.ObservedGeneration = obj.GetGeneration() - if v, ok := obj.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; ok { - obj.Status.LastHandledDeployAt = v - } + obj.Status.LastHandledDeployAt = curDeployRequest pp, err := prepareProject(ctx, r, obj, true) if err != nil { @@ -263,7 +263,7 @@ func (r *KluctlDeploymentReconciler) doReconcile( if obj.Status.LastDeployResult == nil || obj.Status.LastObjectsHash != objectsHash { // either never deployed or source code changed needDeploy = true - } else if r.checkRequestedDeploy(obj) { + } else if curDeployRequest != "" && oldDeployRequest != curDeployRequest { // explicitly requested a deploy needDeploy = true } else if oldGeneration != obj.GetGeneration() { @@ -453,17 +453,6 @@ func (r *KluctlDeploymentReconciler) nextDeployTime(obj *kluctlv1.KluctlDeployme return &t } -func (r *KluctlDeploymentReconciler) checkRequestedDeploy(obj *kluctlv1.KluctlDeployment) bool { - v, ok := obj.Annotations[kluctlv1.KluctlRequestDeployAnnotation] - if !ok { - return false - } - if v != obj.Status.LastHandledDeployAt { - return true - } - return false -} - func (r *KluctlDeploymentReconciler) nextValidateTime(obj *kluctlv1.KluctlDeployment) *time.Time { if obj.Status.LastValidateResult == nil { // was never validated before. Return early. diff --git a/pkg/controllers/kluctldeployment_controller_setup.go b/pkg/controllers/kluctldeployment_controller_setup.go index c0ca400d8..fd549f1f2 100644 --- a/pkg/controllers/kluctldeployment_controller_setup.go +++ b/pkg/controllers/kluctldeployment_controller_setup.go @@ -23,7 +23,7 @@ func (r *KluctlDeploymentReconciler) SetupWithManager(ctx context.Context, mgr c return ctrl.NewControllerManagedBy(mgr). For(&kluctlv1.KluctlDeployment{}, builder.WithPredicates( - predicate.Or(predicate.GenerationChangedPredicate{}, ReconcileRequestedPredicate{}, DeployRequestedPredicate{}), + predicate.Or(predicate.GenerationChangedPredicate{}, ReconcileRequestedPredicate{}), )). Complete(r) } diff --git a/pkg/controllers/predicates.go b/pkg/controllers/predicates.go index feec05a2c..ec5964016 100644 --- a/pkg/controllers/predicates.go +++ b/pkg/controllers/predicates.go @@ -16,35 +16,11 @@ func (ReconcileRequestedPredicate) Update(e event.UpdateEvent) bool { return false } - if val, ok := e.ObjectNew.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { - if valOld, okOld := e.ObjectOld.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; okOld { - return val != valOld - } - return true - } - return false -} - -type DeployRequestedPredicate struct { - predicate.Funcs -} - -func (DeployRequestedPredicate) Update(e event.UpdateEvent) bool { - if e.ObjectOld == nil || e.ObjectNew == nil { - return false + check := func(aname string) bool { + v1, ok1 := e.ObjectNew.GetAnnotations()[aname] + v2, ok2 := e.ObjectOld.GetAnnotations()[aname] + return ok1 != ok2 || v1 != v2 } - if val, ok := e.ObjectNew.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; ok { - if valOld, okOld := e.ObjectOld.GetAnnotations()[kluctlv1.KluctlRequestReconcileAnnotation]; okOld { - return val != valOld - } - return true - } - if val, ok := e.ObjectNew.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; ok { - if valOld, okOld := e.ObjectOld.GetAnnotations()[kluctlv1.KluctlRequestDeployAnnotation]; okOld { - return val != valOld - } - return true - } - return false + return check(kluctlv1.KluctlRequestReconcileAnnotation) || check(kluctlv1.KluctlRequestDeployAnnotation) } From 118fc5224d5866710db7001e8111153c7e7d024f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 10:25:46 +0200 Subject: [PATCH 1121/2268] chore(deps): Bump github.com/hashicorp/go-retryablehttp (#554) Bumps [github.com/hashicorp/go-retryablehttp](https://github.com/hashicorp/go-retryablehttp) from 0.7.2 to 0.7.4. - [Changelog](https://github.com/hashicorp/go-retryablehttp/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/go-retryablehttp/compare/v0.7.2...v0.7.4) --- updated-dependencies: - dependency-name: github.com/hashicorp/go-retryablehttp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8a1c1250a..958bb7456 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/go-logr/logr v1.2.4 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/go-retryablehttp v0.7.2 + github.com/hashicorp/go-retryablehttp v0.7.4 github.com/huandu/xstrings v1.4.0 github.com/onsi/gomega v1.27.7 github.com/otiai10/copy v1.11.0 diff --git a/go.sum b/go.sum index 92bed86a7..f7ffc0052 100644 --- a/go.sum +++ b/go.sum @@ -462,8 +462,8 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= -github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= From b627f4d752ab0583ae58788914f5a06a19dac08d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 10:26:09 +0200 Subject: [PATCH 1122/2268] chore(deps): Bump github.com/Azure/azure-sdk-for-go/sdk/azcore (#555) Bumps [github.com/Azure/azure-sdk-for-go/sdk/azcore](https://github.com/Azure/azure-sdk-for-go) from 1.6.0 to 1.6.1. - [Release notes](https://github.com/Azure/azure-sdk-for-go/releases) - [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md) - [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.6.0...sdk/azcore/v1.6.1) --- updated-dependencies: - dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azcore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 958bb7456..f0f0aab43 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( require ( filippo.io/age v1.1.1 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 github.com/aws/aws-sdk-go-v2 v1.18.0 github.com/aws/aws-sdk-go-v2/config v1.18.25 diff --git a/go.sum b/go.sum index f7ffc0052..a9b491fba 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 h1:SEy2xmstIphdPwNBUi7uhvjyjhVKISfwjfOJmuy7kg4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= From 949f8c3c6a7265f23b2262d9c61b78b34186062d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Jun 2023 18:30:40 +0200 Subject: [PATCH 1123/2268] fix: Honor kluctl.io/diff-name again --- pkg/deployment/commands/utils.go | 47 +++++++++++++++++++++++------- pkg/deployment/utils/diff_utils.go | 21 +++++++------ 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/pkg/deployment/commands/utils.go b/pkg/deployment/commands/utils.go index 074d3222d..ebb2b08b2 100644 --- a/pkg/deployment/commands/utils.go +++ b/pkg/deployment/commands/utils.go @@ -10,6 +10,8 @@ import ( func collectObjects(c *deployment.DeploymentCollection, ru *utils.RemoteObjectUtils, au *utils.ApplyDeploymentsUtil, du *utils.DiffUtil, orphans []k8s.ObjectRef, deleted []k8s.ObjectRef) []result.ResultObject { m := map[k8s.ObjectRef]*result.ResultObject{} + remoteDiffNames := map[k8s.ObjectRef]k8s.ObjectRef{} + appliedDiffNames := map[k8s.ObjectRef]k8s.ObjectRef{} getOrCreate := func(ref k8s.ObjectRef) *result.ResultObject { x, ok := m[ref] @@ -23,42 +25,67 @@ func collectObjects(c *deployment.DeploymentCollection, ru *utils.RemoteObjectUt if c != nil { for _, x := range c.LocalObjects() { - o := getOrCreate(x.GetK8sRef()) + dn := du.GetDiffRef(x) + o := getOrCreate(dn) o.Rendered = x } } if ru != nil { for _, x := range ru.GetFilteredRemoteObjects(nil) { - o := getOrCreate(x.GetK8sRef()) + dn := du.GetDiffRef(x) + remoteDiffNames[x.GetK8sRef()] = dn + + o := getOrCreate(dn) o.Remote = x } } if au != nil { for _, x := range au.GetAppliedObjects() { - o := getOrCreate(x.GetK8sRef()) + dn := du.GetDiffRef(x) + appliedDiffNames[x.GetK8sRef()] = dn + o := getOrCreate(dn) o.Applied = x } for _, x := range au.GetAppliedHookObjects() { - o := getOrCreate(x.GetK8sRef()) + dn := du.GetDiffRef(x) + appliedDiffNames[x.GetK8sRef()] = dn + o := getOrCreate(dn) o.Hook = true } - for _, x := range au.GetNewObjectRefs() { - o := getOrCreate(x) - o.New = true - } for _, x := range au.GetDeletedObjects() { - o := getOrCreate(x) + dn, ok := remoteDiffNames[x] + if !ok { + dn = x + } + o := getOrCreate(dn) o.Deleted = true } } if du != nil { for _, x := range du.ChangedObjects { - o := getOrCreate(x.Ref) + dn, ok := appliedDiffNames[x.Ref] + if !ok { + dn = x.Ref + } + o := getOrCreate(dn) o.Changes = x.Changes } } + if au != nil { + for _, x := range au.GetNewObjectRefs() { + dn, ok := appliedDiffNames[x] + if !ok { + dn = x + } + o := getOrCreate(dn) + if len(o.Changes) != 0 { + continue + } + o.New = true + } + } for _, x := range orphans { o := getOrCreate(x) o.Orphan = true diff --git a/pkg/deployment/utils/diff_utils.go b/pkg/deployment/utils/diff_utils.go index 57c37b981..074020ace 100644 --- a/pkg/deployment/utils/diff_utils.go +++ b/pkg/deployment/utils/diff_utils.go @@ -127,13 +127,7 @@ func (u *DiffUtil) diffObject(lo *uo.UnstructuredObject, diffRef k8s2.ObjectRef, func (u *DiffUtil) calcRemoteObjectsForDiff() { u.remoteDiffObjects = make(map[k8s2.ObjectRef]*uo.UnstructuredObject) for _, o := range u.ru.remoteObjects { - diffName := o.GetK8sAnnotation("kluctl.io/diff-name") - if diffName == nil { - x := o.GetK8sName() - diffName = &x - } - diffRef := o.GetK8sRef() - diffRef.Name = *diffName + diffRef := u.GetDiffRef(o) oldCreationTime := time.Time{} if old, ok := u.remoteDiffObjects[diffRef]; ok { oldCreationTime = old.GetK8sCreationTime() @@ -145,11 +139,16 @@ func (u *DiffUtil) calcRemoteObjectsForDiff() { } func (u *DiffUtil) getRemoteObjectForDiff(localObject *uo.UnstructuredObject) (k8s2.ObjectRef, *uo.UnstructuredObject) { - ref := localObject.GetK8sRef() - diffName := localObject.GetK8sAnnotation("kluctl.io/diff-name") + ref := u.GetDiffRef(localObject) + o, _ := u.remoteDiffObjects[ref] + return ref, o +} + +func (u *DiffUtil) GetDiffRef(o *uo.UnstructuredObject) k8s2.ObjectRef { + ref := o.GetK8sRef() + diffName := o.GetK8sAnnotation("kluctl.io/diff-name") if diffName != nil { ref.Name = *diffName } - o, _ := u.remoteDiffObjects[ref] - return ref, o + return ref } From 9765e19a0565c149dbb881ec7976476494d8404c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Jun 2023 11:18:39 +0200 Subject: [PATCH 1124/2268] test: Add tests for kluctl.io/diff-name --- e2e/diff_name_test.go | 87 +++++++++++++++++++++++++ e2e/utils_resources.go | 7 +- pkg/deployment/utils/diff_utils_test.go | 46 ++++++++----- 3 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 e2e/diff_name_test.go diff --git a/e2e/diff_name_test.go b/e2e/diff_name_test.go new file mode 100644 index 000000000..aee6ad745 --- /dev/null +++ b/e2e/diff_name_test.go @@ -0,0 +1,87 @@ +package e2e + +import ( + test_utils "github.com/kluctl/kluctl/v2/e2e/test-utils" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "github.com/stretchr/testify/assert" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "sort" + "testing" +) + +func TestDiffName(t *testing.T) { + t.Parallel() + + k := defaultCluster1 + + p := test_utils.NewTestProject(t) + + createNamespace(t, k, p.TestSlug()) + + p.UpdateTarget("test", nil) + + addConfigMapDeployment(p, "cm", map[string]string{ + "d1": "{{ args.v }}", + }, resourceOpts{ + name: "{{ args.cm_name }}", + fname: "cm.yaml", + namespace: p.TestSlug(), + annotations: map[string]string{ + "kluctl.io/diff-name": "cm", + }, + }) + p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "cm_name=cm-1", "-a", "v=a") + assertConfigMapExists(t, k, p.TestSlug(), "cm-1") + + resultStr, _ := p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "cm_name=cm-2", "-a", "v=b", "-oyaml") + assertConfigMapExists(t, k, p.TestSlug(), "cm-2") + + var cr result.CompactedCommandResult + err := yaml.ReadYamlString(resultStr, &cr) + assert.NoError(t, err) + + r := cr.ToNonCompacted() + sort.Slice(r.Objects, func(i, j int) bool { + return r.Objects[i].Ref.String() < r.Objects[j].Ref.String() + }) + + assert.Len(t, r.Objects, 2) + assert.Equal(t, result.BaseObject{ + Ref: k8s.ObjectRef{Version: "v1", Kind: "ConfigMap", Name: "cm", Namespace: p.TestSlug()}, + Changes: []result.Change{ + {Type: "update", JsonPath: "data.d1", OldValue: &v1.JSON{Raw: []byte("\"a\"")}, NewValue: &v1.JSON{Raw: []byte("\"b\"")}, UnifiedDiff: "-a\n+b"}, + {Type: "update", JsonPath: "metadata.name", OldValue: &v1.JSON{Raw: []byte("\"cm-1\"")}, NewValue: &v1.JSON{Raw: []byte("\"cm-2\"")}, UnifiedDiff: "-cm-1\n+cm-2"}}, + }, r.Objects[0].BaseObject) + assert.Equal(t, result.BaseObject{ + Ref: k8s.ObjectRef{Version: "v1", Kind: "ConfigMap", Name: "cm-1", Namespace: "test-diff-name"}, + Orphan: true, + }, r.Objects[1].BaseObject) + + resultStr, _ = p.KluctlMust("deploy", "--yes", "-t", "test", "-a", "cm_name=cm-3", "-a", "v=c", "-oyaml") + assertConfigMapExists(t, k, p.TestSlug(), "cm-2") + err = yaml.ReadYamlString(resultStr, &cr) + assert.NoError(t, err) + + r = cr.ToNonCompacted() + sort.Slice(r.Objects, func(i, j int) bool { + return r.Objects[i].Ref.String() < r.Objects[j].Ref.String() + }) + + assert.Len(t, r.Objects, 3) + assert.Equal(t, result.BaseObject{ + Ref: k8s.ObjectRef{Version: "v1", Kind: "ConfigMap", Name: "cm", Namespace: p.TestSlug()}, + Changes: []result.Change{ + {Type: "update", JsonPath: "data.d1", OldValue: &v1.JSON{Raw: []byte("\"b\"")}, NewValue: &v1.JSON{Raw: []byte("\"c\"")}, UnifiedDiff: "-b\n+c"}, + {Type: "update", JsonPath: "metadata.name", OldValue: &v1.JSON{Raw: []byte("\"cm-2\"")}, NewValue: &v1.JSON{Raw: []byte("\"cm-3\"")}, UnifiedDiff: "-cm-2\n+cm-3"}}, + }, r.Objects[0].BaseObject) + assert.Equal(t, result.BaseObject{ + Ref: k8s.ObjectRef{Version: "v1", Kind: "ConfigMap", Name: "cm-1", Namespace: "test-diff-name"}, + Orphan: true, + }, r.Objects[1].BaseObject) + assert.Equal(t, result.BaseObject{ + Ref: k8s.ObjectRef{Version: "v1", Kind: "ConfigMap", Name: "cm-2", Namespace: "test-diff-name"}, + Orphan: true, + }, r.Objects[2].BaseObject) +} diff --git a/e2e/utils_resources.go b/e2e/utils_resources.go index a78a2d0fd..5595851bb 100644 --- a/e2e/utils_resources.go +++ b/e2e/utils_resources.go @@ -9,6 +9,7 @@ import ( type resourceOpts struct { name string + fname string namespace string tags []string labels map[string]string @@ -56,8 +57,12 @@ func createSecretObject(data map[string]string, opts resourceOpts) *uo.Unstructu func addConfigMapDeployment(p *test_utils.TestProject, dir string, data map[string]string, opts resourceOpts) { o := createConfigMapObject(data, opts) + fname := opts.fname + if fname == "" { + fname = fmt.Sprintf("configmap-%s.yml", opts.name) + } p.AddKustomizeDeployment(dir, []test_utils.KustomizeResource{ - {Name: fmt.Sprintf("configmap-%s.yml", opts.name), Content: o}, + {Name: fname, Content: o}, }, opts.tags) if opts.when != "" { p.UpdateDeploymentItems(filepath.Dir(dir), func(items []*uo.UnstructuredObject) []*uo.UnstructuredObject { diff --git a/pkg/deployment/utils/diff_utils_test.go b/pkg/deployment/utils/diff_utils_test.go index 60e3fa6e9..8f69247d9 100644 --- a/pkg/deployment/utils/diff_utils_test.go +++ b/pkg/deployment/utils/diff_utils_test.go @@ -48,12 +48,13 @@ func (dtc *diffTestConfig) appliedObjectsMap() map[k8s2.ObjectRef]*uo.Unstructur return ret } -func newTestConfigMap(name string, data map[string]interface{}) *uo.UnstructuredObject { +func newTestConfigMap(name string, data map[string]interface{}, annotations map[string]string) *uo.UnstructuredObject { o := uo.New() o.SetK8sGVKs("", "v1", "ConfigMap") o.SetK8sName(name) o.SetK8sNamespace("default") o.SetNestedField(data, "data") + o.SetK8sAnnotations(annotations) return o } @@ -83,9 +84,9 @@ func TestDiff(t *testing.T) { tests := []*diffTestConfig{ { name: "One changed object (changed field)", - ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"})}, - lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v2"})}, - ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v2"})}, + ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"}, nil)}, + lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v2"}, nil)}, + ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v2"}, nil)}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ @@ -95,9 +96,9 @@ func TestDiff(t *testing.T) { }, { name: "One changed object (new field)", - ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"})}, - lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"})}, - ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"})}, + ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"}, nil)}, + lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"}, nil)}, + ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"}, nil)}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ @@ -107,9 +108,9 @@ func TestDiff(t *testing.T) { }, { name: "One changed object (removed field)", - ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"})}, - lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"})}, - ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"})}, + ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"}, nil)}, + lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"}, nil)}, + ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"}, nil)}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ @@ -119,9 +120,9 @@ func TestDiff(t *testing.T) { }, { name: "One changed object (new + removed field)", - ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"})}, - lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d2": "v2"})}, - ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d2": "v2"})}, + ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1"}, nil)}, + lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d2": "v2"}, nil)}, + ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d2": "v2"}, nil)}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ @@ -132,9 +133,9 @@ func TestDiff(t *testing.T) { }, { name: "One changed object (mixed changes)", - ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"})}, - lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v12", "d3": "v3"})}, - ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v12", "d3": "v3"})}, + ro: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v1", "d2": "v2"}, nil)}, + lo: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v12", "d3": "v3"}, nil)}, + ao: []*uo.UnstructuredObject{newTestConfigMap("test", map[string]interface{}{"d1": "v12", "d3": "v3"}, nil)}, a: func(t *testing.T, dtc *diffTestConfig) { assert.Len(t, dtc.du.ChangedObjects, 1) assert.Equal(t, []result.Change{ @@ -144,6 +145,19 @@ func TestDiff(t *testing.T) { }, dtc.du.ChangedObjects[0].Changes) }, }, + { + name: "kluctl.io/diff-name being set", + ro: []*uo.UnstructuredObject{newTestConfigMap("test1", map[string]interface{}{"d1": "v1"}, map[string]string{"kluctl.io/diff-name": "test"})}, + lo: []*uo.UnstructuredObject{newTestConfigMap("test2", map[string]interface{}{"d1": "v2"}, map[string]string{"kluctl.io/diff-name": "test"})}, + ao: []*uo.UnstructuredObject{newTestConfigMap("test2", map[string]interface{}{"d1": "v2"}, map[string]string{"kluctl.io/diff-name": "test"})}, + a: func(t *testing.T, dtc *diffTestConfig) { + assert.Len(t, dtc.du.ChangedObjects, 1) + assert.Equal(t, []result.Change{ + buildChange("update", "data.d1", buildRaw("v1"), buildRaw("v2"), "-v1\n+v2"), + buildChange("update", "metadata.name", buildRaw("test1"), buildRaw("test2"), "-test1\n+test2"), + }, dtc.du.ChangedObjects[0].Changes) + }, + }, } for _, test := range tests { From cdd50760bdf1a657f04adecca09c4b7576417bde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 11:19:05 +0200 Subject: [PATCH 1125/2268] chore(deps): Bump github.com/onsi/gomega from 1.27.7 to 1.27.8 (#557) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.7 to 1.27.8. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.27.7...v1.27.8) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index f0f0aab43..1b51519b1 100644 --- a/go.mod +++ b/go.mod @@ -67,7 +67,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.4 github.com/huandu/xstrings v1.4.0 - github.com/onsi/gomega v1.27.7 + github.com/onsi/gomega v1.27.8 github.com/otiai10/copy v1.11.0 github.com/prometheus/client_golang v1.15.1 github.com/sergi/go-diff v1.3.1 diff --git a/go.sum b/go.sum index a9b491fba..63ad11bf5 100644 --- a/go.sum +++ b/go.sum @@ -658,9 +658,9 @@ github.com/ohler55/ojg v1.18.7 h1:sC7zy0usEiWa6bvx3NU1yZH4kCA2F3Qzs6iiDX4+xdk= github.com/ohler55/ojg v1.18.7/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= -github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= +github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= From abb03226dded6642d229fd719b9868220042ccd5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 10 Apr 2023 23:46:11 +0200 Subject: [PATCH 1126/2268] feat: Add initial implementation of webui --- cmd/kluctl/commands/cmd_webui.go | 112 + cmd/kluctl/commands/root.go | 1 + go.mod | 11 + go.sum | 25 + pkg/results/result-store-secrets.go | 3 + pkg/results/results-collector.go | 14 +- pkg/utils/fs_copy.go | 84 + pkg/webui/clusteraccessor.go | 110 + pkg/webui/embed.go | 19 + pkg/webui/generate-ts/main.go | 32 + pkg/webui/generate.go | 3 + pkg/webui/server.go | 280 + pkg/webui/shortnames.go | 217 + pkg/webui/staticbuilder.go | 144 + pkg/webui/ui/build.js | 5 + pkg/webui/ui/package-lock.json | 18441 ++++++++++++++++ pkg/webui/ui/package.json | 67 + pkg/webui/ui/public/.gitignore | 1 + pkg/webui/ui/public/favicon.ico | Bin 0 -> 15406 bytes pkg/webui/ui/public/index.html | 44 + pkg/webui/ui/public/logo192.png | Bin 0 -> 13703 bytes pkg/webui/ui/public/logo512.png | Bin 0 -> 26403 bytes pkg/webui/ui/public/manifest.json | 25 + pkg/webui/ui/public/robots.txt | 3 + pkg/webui/ui/public/staticbuild.js | 2 + pkg/webui/ui/src/api.tsx | 251 + pkg/webui/ui/src/components/ActionsMenu.tsx | 93 + pkg/webui/ui/src/components/App.css | 38 + pkg/webui/ui/src/components/App.test.tsx | 9 + pkg/webui/ui/src/components/App.tsx | 30 + pkg/webui/ui/src/components/CodeViewer.tsx | 29 + pkg/webui/ui/src/components/ErrorsTable.tsx | 56 + pkg/webui/ui/src/components/Jdenticon.tsx | 17 + pkg/webui/ui/src/components/LeftDrawer.tsx | 170 + pkg/webui/ui/src/components/Loading.tsx | 15 + pkg/webui/ui/src/components/ObjectYaml.tsx | 26 + .../ui/src/components/PropertiesTable.tsx | 31 + pkg/webui/ui/src/components/Router.tsx | 41 + .../components/result-view/ChangesTable.tsx | 105 + .../result-view/CommandResultStatusLine.tsx | 59 + .../result-view/CommandResultTree.tsx | 95 + .../result-view/CommandResultView.tsx | 53 + .../result-view/NodeStatusFilter.tsx | 97 + .../src/components/result-view/SidePanel.tsx | 78 + .../result-view/nodes/CommandResultNode.tsx | 60 + .../DeletedOrOrphanObjectsCollectionNode.tsx | 54 + .../nodes/DeploymentItemIncludeNode.tsx | 78 + .../result-view/nodes/DeploymentItemNode.tsx | 72 + .../result-view/nodes/NodeBuilder.ts | 194 + .../components/result-view/nodes/NodeData.tsx | 188 + .../result-view/nodes/ObjectNode.tsx | 101 + .../nodes/VarsSourceCollectionNode.tsx | 26 + .../result-view/nodes/VarsSourceNode.tsx | 238 + .../CommandResultDetailsDrawer.tsx | 54 + .../targets-view/CommandResultItem.tsx | 91 + .../src/components/targets-view/Projects.tsx | 50 + .../targets-view/TargetDetailsDrawer.tsx | 108 + .../src/components/targets-view/Targets.tsx | 147 + .../components/targets-view/TargetsView.tsx | 96 + pkg/webui/ui/src/icons/GitIcon.tsx | 6 + pkg/webui/ui/src/icons/KluctlLogo.tsx | 6 + pkg/webui/ui/src/icons/git.svg | 1 + pkg/webui/ui/src/icons/kluctl-logo.svg | 1 + pkg/webui/ui/src/index.css | 25 + pkg/webui/ui/src/index.tsx | 27 + pkg/webui/ui/src/loadscript.ts | 35 + pkg/webui/ui/src/logo.svg | 1 + pkg/webui/ui/src/models.ts | 900 + pkg/webui/ui/src/react-app-env.d.ts | 1 + pkg/webui/ui/src/reportWebVitals.ts | 15 + pkg/webui/ui/src/setupTests.ts | 5 + pkg/webui/ui/src/staticbuild.d.ts | 7 + pkg/webui/ui/src/utils/duration.ts | 30 + pkg/webui/ui/src/utils/misc.ts | 14 + pkg/webui/ui/tsconfig.json | 26 + pkg/webui/validator.go | 223 + 76 files changed, 23815 insertions(+), 1 deletion(-) create mode 100644 cmd/kluctl/commands/cmd_webui.go create mode 100644 pkg/utils/fs_copy.go create mode 100644 pkg/webui/clusteraccessor.go create mode 100644 pkg/webui/embed.go create mode 100644 pkg/webui/generate-ts/main.go create mode 100644 pkg/webui/generate.go create mode 100644 pkg/webui/server.go create mode 100644 pkg/webui/shortnames.go create mode 100644 pkg/webui/staticbuilder.go create mode 100644 pkg/webui/ui/build.js create mode 100644 pkg/webui/ui/package-lock.json create mode 100644 pkg/webui/ui/package.json create mode 100644 pkg/webui/ui/public/.gitignore create mode 100644 pkg/webui/ui/public/favicon.ico create mode 100644 pkg/webui/ui/public/index.html create mode 100644 pkg/webui/ui/public/logo192.png create mode 100644 pkg/webui/ui/public/logo512.png create mode 100644 pkg/webui/ui/public/manifest.json create mode 100644 pkg/webui/ui/public/robots.txt create mode 100644 pkg/webui/ui/public/staticbuild.js create mode 100644 pkg/webui/ui/src/api.tsx create mode 100644 pkg/webui/ui/src/components/ActionsMenu.tsx create mode 100644 pkg/webui/ui/src/components/App.css create mode 100644 pkg/webui/ui/src/components/App.test.tsx create mode 100644 pkg/webui/ui/src/components/App.tsx create mode 100644 pkg/webui/ui/src/components/CodeViewer.tsx create mode 100644 pkg/webui/ui/src/components/ErrorsTable.tsx create mode 100644 pkg/webui/ui/src/components/Jdenticon.tsx create mode 100644 pkg/webui/ui/src/components/LeftDrawer.tsx create mode 100644 pkg/webui/ui/src/components/Loading.tsx create mode 100644 pkg/webui/ui/src/components/ObjectYaml.tsx create mode 100644 pkg/webui/ui/src/components/PropertiesTable.tsx create mode 100644 pkg/webui/ui/src/components/Router.tsx create mode 100644 pkg/webui/ui/src/components/result-view/ChangesTable.tsx create mode 100644 pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx create mode 100644 pkg/webui/ui/src/components/result-view/CommandResultTree.tsx create mode 100644 pkg/webui/ui/src/components/result-view/CommandResultView.tsx create mode 100644 pkg/webui/ui/src/components/result-view/NodeStatusFilter.tsx create mode 100644 pkg/webui/ui/src/components/result-view/SidePanel.tsx create mode 100644 pkg/webui/ui/src/components/result-view/nodes/CommandResultNode.tsx create mode 100644 pkg/webui/ui/src/components/result-view/nodes/DeletedOrOrphanObjectsCollectionNode.tsx create mode 100644 pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx create mode 100644 pkg/webui/ui/src/components/result-view/nodes/DeploymentItemNode.tsx create mode 100644 pkg/webui/ui/src/components/result-view/nodes/NodeBuilder.ts create mode 100644 pkg/webui/ui/src/components/result-view/nodes/NodeData.tsx create mode 100644 pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx create mode 100644 pkg/webui/ui/src/components/result-view/nodes/VarsSourceCollectionNode.tsx create mode 100644 pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx create mode 100644 pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx create mode 100644 pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx create mode 100644 pkg/webui/ui/src/components/targets-view/Projects.tsx create mode 100644 pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx create mode 100644 pkg/webui/ui/src/components/targets-view/Targets.tsx create mode 100644 pkg/webui/ui/src/components/targets-view/TargetsView.tsx create mode 100644 pkg/webui/ui/src/icons/GitIcon.tsx create mode 100644 pkg/webui/ui/src/icons/KluctlLogo.tsx create mode 100644 pkg/webui/ui/src/icons/git.svg create mode 100644 pkg/webui/ui/src/icons/kluctl-logo.svg create mode 100644 pkg/webui/ui/src/index.css create mode 100644 pkg/webui/ui/src/index.tsx create mode 100644 pkg/webui/ui/src/loadscript.ts create mode 100644 pkg/webui/ui/src/logo.svg create mode 100644 pkg/webui/ui/src/models.ts create mode 100644 pkg/webui/ui/src/react-app-env.d.ts create mode 100644 pkg/webui/ui/src/reportWebVitals.ts create mode 100644 pkg/webui/ui/src/setupTests.ts create mode 100644 pkg/webui/ui/src/staticbuild.d.ts create mode 100644 pkg/webui/ui/src/utils/duration.ts create mode 100644 pkg/webui/ui/src/utils/misc.ts create mode 100644 pkg/webui/ui/tsconfig.json create mode 100644 pkg/webui/validator.go diff --git a/cmd/kluctl/commands/cmd_webui.go b/cmd/kluctl/commands/cmd_webui.go new file mode 100644 index 000000000..73838107a --- /dev/null +++ b/cmd/kluctl/commands/cmd_webui.go @@ -0,0 +1,112 @@ +package commands + +import ( + "context" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/webui" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type webuiCmd struct { + Port int `group:"misc" help:"Port to serve the api and webui." default:"8080"` + Context []string `group:"misc" help:"List of kubernetes contexts to use."` + AllContexts bool `group:"misc" help:"Use all Kubernetes contexts found in the kubeconfig."` + StaticPath string `group:"misc" help:"Build static webui."` +} + +func (cmd *webuiCmd) Help() string { + return `TODO` +} + +func (cmd *webuiCmd) Run(ctx context.Context) error { + + stores, configs, err := cmd.createResultStores(ctx) + if err != nil { + return err + } + + collector := results.NewResultsCollector(ctx, stores) + collector.Start() + + if cmd.StaticPath != "" { + collector.WaitForInitialSync() + sbw := webui.NewStaticWebuiBuilder(collector) + return sbw.Build(cmd.StaticPath) + } else { + server := webui.NewCommandResultsServer(ctx, collector, configs) + return server.Run(cmd.Port) + } +} + +func (cmd *webuiCmd) createResultStores(ctx context.Context) ([]results.ResultStore, []*rest.Config, error) { + r := clientcmd.NewDefaultClientConfigLoadingRules() + + kcfg, err := r.Load() + if err != nil { + return nil, nil, err + } + + var stores []results.ResultStore + var configs []*rest.Config + + var contexts []string + if cmd.AllContexts { + for name, _ := range kcfg.Contexts { + contexts = append(contexts, name) + } + } else { + if len(cmd.Context) == 0 { + // placeholder for current context + contexts = append(contexts, "") + } + for _, c := range cmd.Context { + found := false + for name, _ := range kcfg.Contexts { + if c == name { + contexts = append(contexts, name) + found = true + break + } + } + if !found { + return nil, nil, fmt.Errorf("context '%s' not found in kubeconfig", c) + } + } + } + + for _, c := range contexts { + configOverrides := &clientcmd.ConfigOverrides{ + CurrentContext: c, + } + config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + r, + configOverrides).ClientConfig() + if err != nil { + return nil, nil, err + } + + client, err := client.New(config, client.Options{}) + if err != nil { + return nil, nil, err + } + + cache, err := cache.New(config, cache.Options{}) + if err != nil { + return nil, nil, err + } + go cache.Start(ctx) + + store, err := results.NewResultStoreSecrets(ctx, client, cache, "", 0) + if err != nil { + return nil, nil, err + } + stores = append(stores, store) + configs = append(configs, config) + } + + return stores, configs, nil +} diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index cc629d689..67dbc8cfe 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -67,6 +67,7 @@ type cli struct { Seal sealCmd `cmd:"" help:"Seal secrets based on target's sealingConfig"` Validate validateCmd `cmd:"" help:"Validates the already deployed deployment"` Controller controllerCmd `cmd:"" help:"Kluctl controller sub-commands"` + Webui webuiCmd `cmd:"" help:"TODO"` Version versionCmd `cmd:"" help:"Print kluctl version"` } diff --git a/go.mod b/go.mod index 1b51519b1..4fa6e6eff 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 github.com/aws/smithy-go v1.13.5 github.com/dimchansky/utfbom v1.1.1 + github.com/gin-gonic/gin v1.9.0 github.com/go-git/go-git/v5 v5.7.0 github.com/go-logr/logr v1.2.4 github.com/google/uuid v1.3.0 @@ -71,6 +72,7 @@ require ( github.com/otiai10/copy v1.11.0 github.com/prometheus/client_golang v1.15.1 github.com/sergi/go-diff v1.3.1 + github.com/tkrajina/typescriptify-golang-structs v0.1.10 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 sigs.k8s.io/cli-utils v0.34.0 sigs.k8s.io/controller-runtime v0.15.0 @@ -108,9 +110,11 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect + github.com/bytedance/sonic v1.8.0 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/containerd/containerd v1.7.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect @@ -130,6 +134,7 @@ require ( github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect @@ -142,6 +147,7 @@ require ( github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/goccy/go-json v0.10.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -171,6 +177,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect @@ -212,6 +219,9 @@ require ( github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect github.com/urfave/cli v1.22.12 // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect @@ -227,6 +237,7 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.24.0 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/go.sum b/go.sum index 63ad11bf5..992fed9f8 100644 --- a/go.sum +++ b/go.sum @@ -164,6 +164,9 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= +github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= @@ -174,6 +177,9 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 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= @@ -272,6 +278,10 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= +github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= @@ -328,6 +338,8 @@ github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XE github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= +github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -536,6 +548,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 h1:K96SwIr8MzBQ3kFFz2H/pA2y+EEk04vZ4fWj/YZghBU= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2/go.mod h1:vzngsPshNKUtq0gxkYQKNJafrcH7Qy7Qt6yGNt7JmQI= github.com/kluctl/go-jinja2 v0.0.0-20230428103343-a832225dc94c h1:qAIvhYamCEU/tY6NaENEIQCynGV5sdON7zgZKnbrhhw= @@ -807,8 +821,16 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 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/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ= +github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= +github.com/tkrajina/typescriptify-golang-structs v0.1.10 h1:W/Ta9Kqo2lV+7bVXuQoUhZ0bDlnjwtPpKsy3A9M1nYg= +github.com/tkrajina/typescriptify-golang-structs v0.1.10/go.mod h1:sjU00nti/PMEOZb07KljFlR+lJ+RotsC0GBQMv9EKls= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= @@ -876,6 +898,8 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 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/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -1369,6 +1393,7 @@ k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt oras.land/oras-go v1.2.3 h1:v8PJl+gEAntI1pJ/LCrDgsuk+1PKVavVEPsYIHFE5uY= oras.land/oras-go v1.2.3/go.mod h1:M/uaPdYklze0Vf3AakfarnpoEckvw0ESbRdN8Z1vdJg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 1389db4ab..9b62f35c0 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -338,6 +338,9 @@ func (s *ResultStoreSecrets) WatchCommandResultSummaries(options ListCommandResu return nil, err } + stopCh := make(chan struct{}) + toolscache.WaitForCacheSync(stopCh, r.HasSynced) + cancel := func() { _ = s.informer.RemoveEventHandler(r) } diff --git a/pkg/results/results-collector.go b/pkg/results/results-collector.go index 053cbbe37..f5376ad28 100644 --- a/pkg/results/results-collector.go +++ b/pkg/results/results-collector.go @@ -17,7 +17,8 @@ type ResultsCollector struct { resultSummaries map[string]summaryEntry handlers []*handlerEntry - mutex sync.Mutex + initialWG sync.WaitGroup + mutex sync.Mutex } type summaryEntry struct { @@ -38,6 +39,8 @@ func NewResultsCollector(ctx context.Context, stores []ResultStore) *ResultsColl resultSummaries: map[string]summaryEntry{}, } + ret.initialWG.Add(len(stores)) + return ret } @@ -45,6 +48,10 @@ func (rc *ResultsCollector) Start() { rc.startWatchResults() } +func (rc *ResultsCollector) WaitForInitialSync() { + rc.initialWG.Wait() +} + func (rc *ResultsCollector) startWatchResults() { for _, store := range rc.stores { go rc.runWatchResults(store) @@ -52,12 +59,17 @@ func (rc *ResultsCollector) startWatchResults() { } func (rc *ResultsCollector) runWatchResults(store ResultStore) { + initial := true for { _, err := store.WatchCommandResultSummaries(ListCommandResultSummariesOptions{}, func(summary *result.CommandResultSummary) { rc.handleUpdate(store, summary) }, func(id string) { rc.handleDelete(store, id) }) + if initial { + rc.initialWG.Done() + initial = false + } if err == nil { break } diff --git a/pkg/utils/fs_copy.go b/pkg/utils/fs_copy.go new file mode 100644 index 000000000..ef5fef165 --- /dev/null +++ b/pkg/utils/fs_copy.go @@ -0,0 +1,84 @@ +package utils + +import ( + "fmt" + "io" + "io/fs" + "os" + "path" + "path/filepath" +) + +// TODO get rid of all this when https://github.com/otiai10/copy/issues/71 gets implemented + +func CopyFile(src string, dst string) error { + if !IsFile(src) { + return fmt.Errorf("%s is not a regular file", src) + } + f, err := os.Open(src) + if err != nil { + return err + } + defer f.Close() + return CopyFileStream(f, dst) +} + +func FsCopyFile(srcFs fs.FS, src, dst string) error { + src = filepath.ToSlash(src) + source, err := srcFs.Open(src) + if err != nil { + return err + } + defer source.Close() + + sourceFileStat, err := source.Stat() + if err != nil { + return err + } + + if !sourceFileStat.Mode().IsRegular() { + return fmt.Errorf("%s is not a regular file", src) + } + + return CopyFileStream(source, dst) +} + +func CopyFileStream(src io.Reader, dst string) error { + destination, err := os.Create(dst) + if err != nil { + return err + } + defer destination.Close() + + _, err = io.Copy(destination, src) + return err +} + +func FsCopyDir(srcFs fs.FS, src string, dst string) error { + var err error + var fds []fs.DirEntry + + src = filepath.ToSlash(src) + + if fds, err = fs.ReadDir(srcFs, src); err != nil { + return err + } + if err = os.MkdirAll(dst, 0o700); err != nil { + return err + } + for _, fd := range fds { + srcfp := path.Join(src, fd.Name()) + dstfp := filepath.Join(dst, fd.Name()) + + if fd.IsDir() { + if err = FsCopyDir(srcFs, srcfp, dstfp); err != nil { + return err + } + } else { + if err = FsCopyFile(srcFs, srcfp, dstfp); err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/webui/clusteraccessor.go b/pkg/webui/clusteraccessor.go new file mode 100644 index 000000000..fe83386f2 --- /dev/null +++ b/pkg/webui/clusteraccessor.go @@ -0,0 +1,110 @@ +package webui + +import ( + "context" + k8s2 "github.com/kluctl/kluctl/v2/pkg/k8s" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sync" + "time" +) + +type clusterAccessorManager struct { + ctx context.Context + accessors []*clusterAccessor +} + +type clusterAccessor struct { + ctx context.Context + config *rest.Config + client client.Client + k *k8s2.K8sCluster + clusterId string + mutex sync.Mutex +} + +func (cam *clusterAccessorManager) add(config *rest.Config) { + cam.accessors = append(cam.accessors, &clusterAccessor{ + ctx: cam.ctx, + config: config, + }) +} + +func (cam *clusterAccessorManager) start() { + for _, ca := range cam.accessors { + ca.start() + } +} + +func (cam *clusterAccessorManager) getForClusterId(clusterId string) *clusterAccessor { + for _, ca := range cam.accessors { + if ca.getClusterId() == clusterId { + return ca + } + } + return nil +} + +func (ca *clusterAccessor) start() { + go ca.initClient() +} + +func (ca *clusterAccessor) initClient() { + for { + err := ca.tryInitClient() + if err == nil { + break + } + time.Sleep(5 * time.Second) + } +} + +func (ca *clusterAccessor) tryInitClient() error { + var err error + c, err := client.New(ca.config, client.Options{}) + if err != nil { + return err + } + var ns corev1.Namespace + err = c.Get(context.Background(), client.ObjectKey{Name: "kube-system"}, &ns) + if err != nil { + return err + } + + cf, err := k8s2.NewClientFactory(context.Background(), ca.config) + if err != nil { + return err + } + + k, err := k8s2.NewK8sCluster(context.Background(), cf, true) + if err != nil { + return err + } + + ca.mutex.Lock() + defer ca.mutex.Unlock() + ca.client = c + ca.k = k + ca.clusterId = string(ns.UID) + + return nil +} + +func (ca *clusterAccessor) getClusterId() string { + ca.mutex.Lock() + defer ca.mutex.Unlock() + return ca.clusterId +} + +func (ca *clusterAccessor) getClient() client.Client { + ca.mutex.Lock() + defer ca.mutex.Unlock() + return ca.client +} + +func (ca *clusterAccessor) getK() *k8s2.K8sCluster { + ca.mutex.Lock() + defer ca.mutex.Unlock() + return ca.k +} diff --git a/pkg/webui/embed.go b/pkg/webui/embed.go new file mode 100644 index 000000000..be8818c85 --- /dev/null +++ b/pkg/webui/embed.go @@ -0,0 +1,19 @@ +package webui + +import ( + "embed" + log "github.com/sirupsen/logrus" + "io/fs" +) + +//go:embed ui/build +var uiBuildFS embed.FS +var uiFS fs.FS + +func init() { + var err error + uiFS, err = fs.Sub(uiBuildFS, "ui/build") + if err != nil { + log.Fatal("failed to get ui fs", err) + } +} diff --git a/pkg/webui/generate-ts/main.go b/pkg/webui/generate-ts/main.go new file mode 100644 index 000000000..23a26e7ee --- /dev/null +++ b/pkg/webui/generate-ts/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "github.com/kluctl/kluctl/v2/pkg/webui" + "github.com/tkrajina/typescriptify-golang-structs/typescriptify" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func main() { + converter := typescriptify.New(). + WithBackupDir(""). + Add(result.CommandResult{}). + Add(result.ProjectSummary{}). + Add(result.CommandResultSummary{}). + Add(webui.ShortName{}). + Add(uo.UnstructuredObject{}). + ManageType(types.GitUrl{}, typescriptify.TypeOptions{TSType: "string"}). + ManageType(types.GitRepoKey{}, typescriptify.TypeOptions{TSType: "string"}). + ManageType(types.YamlUrl{}, typescriptify.TypeOptions{TSType: "string"}). + ManageType(uo.UnstructuredObject{}, typescriptify.TypeOptions{TSType: "any"}). + ManageType(metav1.Time{}, typescriptify.TypeOptions{TSType: "string"}). + ManageType(apiextensionsv1.JSON{}, typescriptify.TypeOptions{TSType: "any"}) + + err := converter.ConvertToFile("ui/src/models.ts") + if err != nil { + panic(err.Error()) + } +} diff --git a/pkg/webui/generate.go b/pkg/webui/generate.go new file mode 100644 index 000000000..f4aa787b8 --- /dev/null +++ b/pkg/webui/generate.go @@ -0,0 +1,3 @@ +package webui + +//go:generate go run github.com/kluctl/kluctl/v2/pkg/webui/generate-ts diff --git a/pkg/webui/server.go b/pkg/webui/server.go new file mode 100644 index 000000000..746f3b0da --- /dev/null +++ b/pkg/webui/server.go @@ -0,0 +1,280 @@ +package webui + +import ( + "context" + "fmt" + "github.com/gin-gonic/gin" + "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/k8s" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "k8s.io/client-go/rest" + "net" + "net/http" +) + +type CommandResultsServer struct { + ctx context.Context + collector *results.ResultsCollector + cam *clusterAccessorManager + vam *validatorManager +} + +func NewCommandResultsServer(ctx context.Context, collector *results.ResultsCollector, configs []*rest.Config) *CommandResultsServer { + ret := &CommandResultsServer{ + ctx: ctx, + collector: collector, + cam: &clusterAccessorManager{ + ctx: ctx, + }, + } + + for _, config := range configs { + ret.cam.add(config) + } + + ret.vam = newValidatorManager(ctx, collector, ret.cam) + + return ret +} + +func (s *CommandResultsServer) Run(port int) error { + _, _ = s.collector.WatchCommandResultSummaries(results.ListCommandResultSummariesOptions{}, func(summary *result.CommandResultSummary) { + status.Info(s.ctx, "Updated result summary for %s", summary.Id) + }, func(id string) { + status.Info(s.ctx, "Deleted result summary for %s", id) + }) + + s.cam.start() + s.vam.start() + + router := gin.Default() + + router.StaticFS("/webui", http.FS(uiFS)) + + api := router.Group("/api") + api.GET("/getShortNames", s.getShortNames) + api.GET("/listProjects", s.listProjects) + api.GET("/listResults", s.listResults) + api.GET("/getResult", s.getResult) + api.GET("/getResultObject", s.getResultObject) + api.POST("/validateNow", s.validateNow) + + address := fmt.Sprintf(":%d", port) + listener, err := net.Listen("tcp", address) + if err != nil { + return err + } + + httpServer := http.Server{ + Addr: address, + BaseContext: func(listener net.Listener) context.Context { + return s.ctx + }, + Handler: router.Handler(), + } + + return httpServer.Serve(listener) +} + +func (s *CommandResultsServer) getShortNames(c *gin.Context) { + c.JSON(http.StatusOK, GetShortNames()) +} + +func (s *CommandResultsServer) listResults(c *gin.Context) { + args := struct { + FilterProject string `form:"filterProject"` + FilterSubDir string `form:"filterSubDir"` + }{} + err := c.BindQuery(&args) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + repoKey, err := types.ParseGitRepoKey(args.FilterProject) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + var filter *result.ProjectKey + if args.FilterProject != "" { + filter = &result.ProjectKey{ + GitRepoKey: repoKey, + SubDir: args.FilterSubDir, + } + } + + summaries, err := s.collector.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{ + ProjectFilter: filter, + }) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + c.JSON(http.StatusOK, summaries) +} + +func (s *CommandResultsServer) listProjects(c *gin.Context) { + summaries, err := s.collector.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + projects := result.BuildProjectSummaries(summaries) + + for _, p := range projects { + for _, t := range p.Targets { + key := projectTargetKey{ + Project: p.Project, + Target: t.Target, + } + vr, err := s.vam.getValidateResult(key) + if err == nil { + t.LastValidateResult = vr + } + } + } + + c.JSON(http.StatusOK, projects) +} + +type resultIdParam struct { + ResultId string `form:"resultId"` +} +type refParam struct { + Group string `form:"group"` + Version string `form:"version"` + Kind string `form:"kind"` + Name string `form:"name"` + Namespace string `form:"namespace"` +} +type objectTypeParams struct { + ObjectType string `form:"objectType"` +} + +func (ref refParam) toK8sRef() k8s.ObjectRef { + return k8s.ObjectRef{ + Group: ref.Group, + Version: ref.Version, + Kind: ref.Kind, + Name: ref.Name, + Namespace: ref.Namespace, + } +} + +func (s *CommandResultsServer) getResult(c *gin.Context) { + var params resultIdParam + + err := c.Bind(¶ms) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + sr, err := s.collector.GetCommandResult(results.GetCommandResultOptions{ + Id: params.ResultId, + Reduced: true, + }) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + if sr == nil { + c.AbortWithStatus(http.StatusNotFound) + return + } + + c.JSON(http.StatusOK, sr) +} + +func (s *CommandResultsServer) getResultObject(c *gin.Context) { + var params resultIdParam + var ref refParam + var objectType objectTypeParams + + err := c.Bind(¶ms) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + err = c.Bind(&ref) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + err = c.Bind(&objectType) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + sr, err := s.collector.GetCommandResult(results.GetCommandResultOptions{ + Id: params.ResultId, + Reduced: false, + }) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + if sr == nil { + c.AbortWithStatus(http.StatusNotFound) + return + } + + ref2 := ref.toK8sRef() + + var found *result.ResultObject + for _, o := range sr.Objects { + if o.Ref == ref2 { + found = &o + break + } + } + if found == nil { + c.AbortWithStatus(http.StatusNotFound) + return + } + + var o2 *uo.UnstructuredObject + switch objectType.ObjectType { + case "rendered": + o2 = found.Rendered + case "remote": + o2 = found.Remote + case "applied": + o2 = found.Applied + } + if o2 == nil { + c.AbortWithStatus(http.StatusNotFound) + return + } + + c.JSON(http.StatusOK, o2) +} + +func (s *CommandResultsServer) validateNow(c *gin.Context) { + var params projectTargetKey + err := c.Bind(¶ms) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + key := projectTargetKey{ + Project: params.Project, + Target: params.Target, + } + + if !s.vam.validateNow(key) { + _ = c.AbortWithError(http.StatusNotFound, err) + return + } + + c.Status(http.StatusOK) +} diff --git a/pkg/webui/shortnames.go b/pkg/webui/shortnames.go new file mode 100644 index 000000000..04ce897cb --- /dev/null +++ b/pkg/webui/shortnames.go @@ -0,0 +1,217 @@ +package webui + +import ( + "strings" +) + +// output of "kubectl api-resources" +const apiResourcesOutput = ` +bindings v1 true Binding +componentstatuses cs v1 false ComponentStatus +configmaps cm v1 true ConfigMap +endpoints ep v1 true Endpoints +events ev v1 true Event +limitranges limits v1 true LimitRange +namespaces ns v1 false Namespace +nodes no v1 false Node +persistentvolumeclaims pvc v1 true PersistentVolumeClaim +persistentvolumes pv v1 false PersistentVolume +pods po v1 true Pod +podtemplates v1 true PodTemplate +replicationcontrollers rc v1 true ReplicationController +resourcequotas quota v1 true ResourceQuota +secrets v1 true Secret +serviceaccounts sa v1 true ServiceAccount +services svc v1 true Service +postgresqls pg acid.zalan.do/v1 true postgresql +challenges acme.cert-manager.io/v1 true Challenge +orders acme.cert-manager.io/v1 true Order +mutatingwebhookconfigurations admissionregistration.k8s.io/v1 false MutatingWebhookConfiguration +validatingwebhookconfigurations admissionregistration.k8s.io/v1 false ValidatingWebhookConfiguration +agents agent agent.k8s.elastic.co/v1alpha1 true Agent +customresourcedefinitions crd,crds apiextensions.k8s.io/v1 false CustomResourceDefinition +apiservices apiregistration.k8s.io/v1 false APIService +apmservers apm apm.k8s.elastic.co/v1 true ApmServer +controllerrevisions apps/v1 true ControllerRevision +daemonsets ds apps/v1 true DaemonSet +deployments deploy apps/v1 true Deployment +replicasets rs apps/v1 true ReplicaSet +statefulsets sts apps/v1 true StatefulSet +tokenreviews authentication.k8s.io/v1 false TokenReview +localsubjectaccessreviews authorization.k8s.io/v1 true LocalSubjectAccessReview +selfsubjectaccessreviews authorization.k8s.io/v1 false SelfSubjectAccessReview +selfsubjectrulesreviews authorization.k8s.io/v1 false SelfSubjectRulesReview +subjectaccessreviews authorization.k8s.io/v1 false SubjectAccessReview +horizontalpodautoscalers hpa autoscaling/v2 true HorizontalPodAutoscaler +elasticsearchautoscalers esa autoscaling.k8s.elastic.co/v1alpha1 true ElasticsearchAutoscaler +cronjobs cj batch/v1 true CronJob +jobs batch/v1 true Job +beats beat beat.k8s.elastic.co/v1beta1 true Beat +sealedsecrets bitnami.com/v1alpha1 true SealedSecret +certificaterequests cr,crs cert-manager.io/v1 true CertificateRequest +certificates cert,certs cert-manager.io/v1 true Certificate +clusterissuers cert-manager.io/v1 false ClusterIssuer +issuers cert-manager.io/v1 true Issuer +certificatesigningrequests csr certificates.k8s.io/v1 false CertificateSigningRequest +ciliumclusterwidenetworkpolicies ccnp cilium.io/v2 false CiliumClusterwideNetworkPolicy +ciliumendpoints cep,ciliumep cilium.io/v2 true CiliumEndpoint +ciliumexternalworkloads cew cilium.io/v2 false CiliumExternalWorkload +ciliumidentities ciliumid cilium.io/v2 false CiliumIdentity +ciliumnetworkpolicies cnp,ciliumnp cilium.io/v2 true CiliumNetworkPolicy +ciliumnodes cn,ciliumn cilium.io/v2 false CiliumNode +configs config.gatekeeper.sh/v1alpha1 true Config +k8sallowedingressclasses constraints.gatekeeper.sh/v1beta1 false K8sAllowedIngressClasses +k8spspallowedusers constraints.gatekeeper.sh/v1beta1 false K8sPSPAllowedUsers +k8spspallowprivilegeescalationcontainer constraints.gatekeeper.sh/v1beta1 false K8sPSPAllowPrivilegeEscalationContainer +k8spspapparmor constraints.gatekeeper.sh/v1beta1 false K8sPSPAppArmor +k8spspcapabilities constraints.gatekeeper.sh/v1beta1 false K8sPSPCapabilities +k8spspflexvolumes constraints.gatekeeper.sh/v1beta1 false K8sPSPFlexVolumes +k8spspforbiddensysctls constraints.gatekeeper.sh/v1beta1 false K8sPSPForbiddenSysctls +k8spspfsgroup constraints.gatekeeper.sh/v1beta1 false K8sPSPFSGroup +k8spsphostfilesystem constraints.gatekeeper.sh/v1beta1 false K8sPSPHostFilesystem +k8spsphostnamespace constraints.gatekeeper.sh/v1beta1 false K8sPSPHostNamespace +k8spsphostnetworkingports constraints.gatekeeper.sh/v1beta1 false K8sPSPHostNetworkingPorts +k8spspprivilegedcontainer constraints.gatekeeper.sh/v1beta1 false K8sPSPPrivilegedContainer +k8spspprocmount constraints.gatekeeper.sh/v1beta1 false K8sPSPProcMount +k8spspreadonlyrootfilesystem constraints.gatekeeper.sh/v1beta1 false K8sPSPReadOnlyRootFilesystem +k8spspseccomp constraints.gatekeeper.sh/v1beta1 false K8sPSPSeccomp +k8spspselinuxv2 constraints.gatekeeper.sh/v1beta1 false K8sPSPSELinuxV2 +k8spspvolumetypes constraints.gatekeeper.sh/v1beta1 false K8sPSPVolumeTypes +leases coordination.k8s.io/v1 true Lease +strimzipodsets sps core.strimzi.io/v1beta2 true StrimziPodSet +eniconfigs crd.k8s.amazonaws.com/v1alpha1 false ENIConfig +endpointslices discovery.k8s.io/v1 true EndpointSlice +elasticsearches es elasticsearch.k8s.elastic.co/v1 true Elasticsearch +ingressclassparams elbv2.k8s.aws/v1beta1 false IngressClassParams +targetgroupbindings elbv2.k8s.aws/v1beta1 true TargetGroupBinding +enterprisesearches ent enterprisesearch.k8s.elastic.co/v1 true EnterpriseSearch +events ev events.k8s.io/v1 true Event +expansiontemplate expansion.gatekeeper.sh/v1alpha1 false ExpansionTemplate +providers externaldata.gatekeeper.sh/v1beta1 false Provider +flowschemas flowcontrol.apiserver.k8s.io/v1beta2 false FlowSchema +prioritylevelconfigurations flowcontrol.apiserver.k8s.io/v1beta2 false PriorityLevelConfiguration +kluctldeployments flux.kluctl.io/v1alpha1 true KluctlDeployment +jaegers jaegertracing.io/v1 true Jaeger +kafkabridges kb kafka.strimzi.io/v1beta2 true KafkaBridge +kafkaconnectors kctr kafka.strimzi.io/v1beta2 true KafkaConnector +kafkaconnects kc kafka.strimzi.io/v1beta2 true KafkaConnect +kafkamirrormaker2s kmm2 kafka.strimzi.io/v1beta2 true KafkaMirrorMaker2 +kafkamirrormakers kmm kafka.strimzi.io/v1beta2 true KafkaMirrorMaker +kafkarebalances kr kafka.strimzi.io/v1beta2 true KafkaRebalance +kafkas k kafka.strimzi.io/v1beta2 true Kafka +kafkatopics kt kafka.strimzi.io/v1beta2 true KafkaTopic +kafkausers ku kafka.strimzi.io/v1beta2 true KafkaUser +kibanas kb kibana.k8s.elastic.co/v1 true Kibana +elasticmapsservers ems maps.k8s.elastic.co/v1alpha1 true ElasticMapsServer +nodes metrics.k8s.io/v1beta1 false NodeMetrics +pods metrics.k8s.io/v1beta1 true PodMetrics +tenants tenant minio.min.io/v2 true Tenant +alertmanagerconfigs amcfg monitoring.coreos.com/v1alpha1 true AlertmanagerConfig +alertmanagers am monitoring.coreos.com/v1 true Alertmanager +podmonitors pmon monitoring.coreos.com/v1 true PodMonitor +probes prb monitoring.coreos.com/v1 true Probe +prometheuses prom monitoring.coreos.com/v1 true Prometheus +prometheusrules promrule monitoring.coreos.com/v1 true PrometheusRule +servicemonitors smon monitoring.coreos.com/v1 true ServiceMonitor +thanosrulers ruler monitoring.coreos.com/v1 true ThanosRuler +assign mutations.gatekeeper.sh/v1 false Assign +assignmetadata mutations.gatekeeper.sh/v1 false AssignMetadata +modifyset mutations.gatekeeper.sh/v1 false ModifySet +ingressclasses networking.k8s.io/v1 false IngressClass +ingresses ing networking.k8s.io/v1 true Ingress +networkpolicies netpol networking.k8s.io/v1 true NetworkPolicy +runtimeclasses node.k8s.io/v1 false RuntimeClass +poddisruptionbudgets pdb policy/v1 true PodDisruptionBudget +podsecuritypolicies psp policy/v1beta1 false PodSecurityPolicy +clusterrolebindings rbac.authorization.k8s.io/v1 false ClusterRoleBinding +clusterroles rbac.authorization.k8s.io/v1 false ClusterRole +rolebindings rbac.authorization.k8s.io/v1 true RoleBinding +roles rbac.authorization.k8s.io/v1 true Role +dbclusterparametergroups rds.services.k8s.aws/v1alpha1 true DBClusterParameterGroup +dbclusters rds.services.k8s.aws/v1alpha1 true DBCluster +dbinstances rds.services.k8s.aws/v1alpha1 true DBInstance +dbparametergroups rds.services.k8s.aws/v1alpha1 true DBParameterGroup +dbproxies rds.services.k8s.aws/v1alpha1 true DBProxy +dbsecuritygroups rds.services.k8s.aws/v1alpha1 true DBSecurityGroup +dbsubnetgroups rds.services.k8s.aws/v1alpha1 true DBSubnetGroup +globalclusters rds.services.k8s.aws/v1alpha1 true GlobalCluster +buckets s3.services.k8s.aws/v1alpha1 true Bucket +priorityclasses pc scheduling.k8s.io/v1 false PriorityClass +basicauths secretgenerator.mittwald.de/v1alpha1 true BasicAuth +sshkeypairs secretgenerator.mittwald.de/v1alpha1 true SSHKeyPair +stringsecrets secretgenerator.mittwald.de/v1alpha1 true StringSecret +adoptedresources services.k8s.aws/v1alpha1 true AdoptedResource +fieldexports services.k8s.aws/v1alpha1 true FieldExport +volumesnapshotclasses vsclass,vsclasses snapshot.storage.k8s.io/v1 false VolumeSnapshotClass +volumesnapshotcontents vsc,vscs snapshot.storage.k8s.io/v1 false VolumeSnapshotContent +volumesnapshots vs snapshot.storage.k8s.io/v1 true VolumeSnapshot +stackconfigpolicies scp stackconfigpolicy.k8s.elastic.co/v1alpha1 true StackConfigPolicy +constraintpodstatuses status.gatekeeper.sh/v1beta1 true ConstraintPodStatus +constrainttemplatepodstatuses status.gatekeeper.sh/v1beta1 true ConstraintTemplatePodStatus +mutatorpodstatuses status.gatekeeper.sh/v1beta1 true MutatorPodStatus +csidrivers storage.k8s.io/v1 false CSIDriver +csinodes storage.k8s.io/v1 false CSINode +csistoragecapacities storage.k8s.io/v1beta1 true CSIStorageCapacity +storageclasses sc storage.k8s.io/v1 false StorageClass +volumeattachments storage.k8s.io/v1 false VolumeAttachment +constrainttemplates templates.gatekeeper.sh/v1 false ConstraintTemplate +githubcomments templates.kluctl.io/v1alpha1 true GithubComment +gitlabcomments templates.kluctl.io/v1alpha1 true GitlabComment +gitprojectors templates.kluctl.io/v1alpha1 true GitProjector +listgithubpullrequests templates.kluctl.io/v1alpha1 true ListGithubPullRequests +listgitlabmergerequests templates.kluctl.io/v1alpha1 true ListGitlabMergeRequests +objecthandlers templates.kluctl.io/v1alpha1 true ObjectHandler +objecttemplates templates.kluctl.io/v1alpha1 true ObjectTemplate +texttemplates templates.kluctl.io/v1alpha1 true TextTemplate +backuprepositories velero.io/v1 true BackupRepository +backups velero.io/v1 true Backup +backupstoragelocations bsl velero.io/v1 true BackupStorageLocation +deletebackuprequests velero.io/v1 true DeleteBackupRequest +downloadrequests velero.io/v1 true DownloadRequest +podvolumebackups velero.io/v1 true PodVolumeBackup +podvolumerestores velero.io/v1 true PodVolumeRestore +resticrepositories velero.io/v1 true ResticRepository +restores velero.io/v1 true Restore +schedules velero.io/v1 true Schedule +serverstatusrequests ssr velero.io/v1 true ServerStatusRequest +volumesnapshotlocations vsl velero.io/v1 true VolumeSnapshotLocation +securitygrouppolicies sgp vpcresources.k8s.aws/v1beta1 true SecurityGroupPolicy +` + +type ShortName struct { + Group string `json:"group,omitempty"` + Kind string `json:"kind"` + ShortName string `json:"shortName"` +} + +var shortNames []ShortName + +func init() { + for _, l := range strings.Split(apiResourcesOutput, "\n") { + if l == "" { + continue + } + s := strings.Fields(l) + if len(s) == 5 { + sns := strings.Split(s[1], ",") + apiVersion := s[2] + kind := s[4] + + group := "" + s2 := strings.Split(apiVersion, "/") + if len(s2) == 2 { + group = s2[0] + } + + shortNames = append(shortNames, ShortName{ + Group: group, + Kind: kind, + ShortName: sns[0], + }) + } + } +} + +func GetShortNames() []ShortName { + return shortNames +} diff --git a/pkg/webui/staticbuilder.go b/pkg/webui/staticbuilder.go new file mode 100644 index 000000000..fd7f95726 --- /dev/null +++ b/pkg/webui/staticbuilder.go @@ -0,0 +1,144 @@ +package webui + +import ( + "bytes" + "context" + "fmt" + "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/utils" + "github.com/kluctl/kluctl/v2/pkg/yaml" + cp "github.com/otiai10/copy" + "os" + "path/filepath" +) + +type StaticWebuiBuilder struct { + store results.ResultStore +} + +func NewStaticWebuiBuilder(store results.ResultStore) *StaticWebuiBuilder { + ret := &StaticWebuiBuilder{ + store: store, + } + return ret +} + +func (swb *StaticWebuiBuilder) Build(path string) error { + tmpDir, err := os.MkdirTemp("", "") + if err != nil { + return err + } + //defer os.RemoveAll(tmpDir) + + summaries, err := swb.store.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + if err != nil { + return err + } + + projects := result.BuildProjectSummaries(summaries) + + err = os.MkdirAll(filepath.Join(tmpDir, "staticdata"), 0o700) + if err != nil { + return err + } + + gh := utils.NewGoHelper(context.Background(), 8) + for _, rs := range summaries { + rs := rs + gh.RunE(func() error { + cr, err := swb.store.GetCommandResult(results.GetCommandResultOptions{ + Id: rs.Id, + }) + + var js string + + if err != nil { + jerr := struct { + Error string `json:"error"` + }{ + Error: err.Error(), + } + j, err := yaml.WriteJsonString(jerr) + if err != nil { + return err + } + js = fmt.Sprintf("staticResults.set(\"%s\", %s)\n", rs.Id, j) + } else { + j, err := yaml.WriteJsonString(cr) + if err != nil { + return err + } + js = fmt.Sprintf("staticResults.set(\"%s\", %s)\n", rs.Id, j) + } + err = os.WriteFile(filepath.Join(tmpDir, fmt.Sprintf("staticdata/result-%s.js", rs.Id)), []byte(js), 0o600) + if err != nil { + return err + } + return nil + }) + } + gh.Wait() + + j, err := yaml.WriteJsonString(summaries) + if err != nil { + return err + } + j = `const staticSummaries=` + j + err = os.WriteFile(filepath.Join(tmpDir, "staticdata/summaries.js"), []byte(j), 0o600) + if err != nil { + return err + } + + j, err = yaml.WriteJsonString(projects) + if err != nil { + return err + } + j = `const staticProjects=` + j + err = os.WriteFile(filepath.Join(tmpDir, "staticdata/projects.js"), []byte(j), 0o600) + if err != nil { + return err + } + + j, err = yaml.WriteJsonString(GetShortNames()) + if err != nil { + return err + } + j = `const staticShortNames=` + j + err = os.WriteFile(filepath.Join(tmpDir, "staticdata/shortnames.js"), []byte(j), 0o600) + if err != nil { + return err + } + + err = utils.FsCopyDir(uiFS, ".", tmpDir) + if err != nil { + return err + } + + indexHtml, err := os.ReadFile(filepath.Join(tmpDir, "index.html")) + if err != nil { + return err + } + indexHtml = bytes.ReplaceAll(indexHtml, []byte("/webui/"), []byte("./")) + err = os.WriteFile(filepath.Join(tmpDir, "index.html"), indexHtml, 0) + if err != nil { + return err + } + + staticbuildJsBytes, err := os.ReadFile(filepath.Join(tmpDir, "staticbuild.js")) + if err != nil { + return err + } + staticbuildJs := string(staticbuildJsBytes) + "\nisStaticBuild = true\n" + err = os.WriteFile(filepath.Join(tmpDir, "staticbuild.js"), []byte(staticbuildJs), 0) + if err != nil { + return err + } + + err = cp.Copy(tmpDir, path) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/webui/ui/build.js b/pkg/webui/ui/build.js new file mode 100644 index 000000000..0386a1a3c --- /dev/null +++ b/pkg/webui/ui/build.js @@ -0,0 +1,5 @@ +const rewire = require('rewire'); +const defaults = rewire('react-scripts/scripts/build.js'); +const config = defaults.__get__('config'); + +config.optimization.minimize = false diff --git a/pkg/webui/ui/package-lock.json b/pkg/webui/ui/package-lock.json new file mode 100644 index 000000000..014cd172d --- /dev/null +++ b/pkg/webui/ui/package-lock.json @@ -0,0 +1,18441 @@ +{ + "name": "react-demo", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "react-demo", + "version": "0.1.0", + "dependencies": { + "@emotion/react": "^11.10.6", + "@emotion/styled": "^11.10.6", + "@fontsource/roboto": "^4.5.8", + "@mui/icons-material": "^5.11.11", + "@mui/lab": "^5.0.0-alpha.124", + "@mui/material": "^5.11.14", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "@types/jest": "^27.5.2", + "@types/node": "^16.18.18", + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "jdenticon": "^3.2.0", + "js-sha256": "^0.9.0", + "js-yaml": "^4.1.0", + "localforage": "^1.10.0", + "lodash-core": "^4.17.19", + "match-sorter": "^6.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router": "^6.10.0", + "react-router-dom": "^6.10.0", + "react-scripts": "5.0.1", + "react-syntax-highlighter": "^15.5.0", + "sort-by": "^1.2.0", + "typescript": "^4.9.5", + "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/lodash": "^4.14.192", + "@types/react-syntax-highlighter": "^15.5.6", + "rewire": "^6.0.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", + "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==" + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", + "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.3", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.3", + "@babel/types": "^7.21.3", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/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/@babel/eslint-parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.3.tgz", + "integrity": "sha512-kfhmPimwo6k4P8zxNs8+T7yR44q1LdpsZdE1NkCsVlfiuTPRfnGgjaF8Qgug9q9Pou17u6wneYF0lDCZJATMFg==", + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/eslint-parser/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/@babel/generator": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", + "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "dependencies": { + "@babel/types": "^7.21.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/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/@babel/helper-create-class-features-plugin": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", + "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", + "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/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/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", + "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", + "dependencies": { + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "dependencies": { + "@babel/types": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "dependencies": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", + "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz", + "integrity": "sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", + "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.21.0.tgz", + "integrity": "sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", + "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", + "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", + "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", + "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/template": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", + "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz", + "integrity": "sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-flow": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", + "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "dependencies": { + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", + "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", + "dependencies": { + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-simple-access": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", + "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz", + "integrity": "sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz", + "integrity": "sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz", + "integrity": "sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/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/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", + "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "dependencies": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/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/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz", + "integrity": "sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-transform-typescript": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "node_modules/@babel/runtime": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", + "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.3", + "@babel/types": "^7.21.3", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", + "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "node_modules/@csstools/normalize.css": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", + "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz", + "integrity": "sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/serialize": "^1.1.1", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.1.3" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.10.5", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz", + "integrity": "sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==", + "dependencies": { + "@emotion/memoize": "^0.8.0", + "@emotion/sheet": "^1.2.1", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "stylis": "4.1.3" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", + "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", + "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "dependencies": { + "@emotion/memoize": "^0.8.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + }, + "node_modules/@emotion/react": { + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz", + "integrity": "sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.10.6", + "@emotion/cache": "^11.10.5", + "@emotion/serialize": "^1.1.1", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==", + "dependencies": { + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/unitless": "^0.8.0", + "@emotion/utils": "^1.2.0", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz", + "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==" + }, + "node_modules/@emotion/styled": { + "version": "11.10.6", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.6.tgz", + "integrity": "sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.10.6", + "@emotion/is-prop-valid": "^1.2.0", + "@emotion/serialize": "^1.1.1", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@emotion/utils": "^1.2.0" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", + "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", + "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", + "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz", + "integrity": "sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA==", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", + "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", + "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", + "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fontsource/roboto": { + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-4.5.8.tgz", + "integrity": "sha512-CnD7zLItIzt86q4Sj3kZUiLcBk1dSk81qcqgMGaZe7SQ1P8hFNxhMl5AZthK1zrDM5m74VVhaOpuMGIL4gagaA==" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/console/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/console/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/core/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/reporters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/source-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/transform/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "node_modules/@mui/base": { + "version": "5.0.0-alpha.122", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.122.tgz", + "integrity": "sha512-IgZEFQyHa39J1+Q3tekVdhPuUm1fr3icddaNLmiAIeYTVXmR7KR5FhBAIL0P+4shlPq0liUPGlXryoTm0iCeFg==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@emotion/is-prop-valid": "^1.2.0", + "@mui/types": "^7.2.3", + "@mui/utils": "^5.11.13", + "@popperjs/core": "^2.11.6", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/base/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.11.14", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.14.tgz", + "integrity": "sha512-rfc08z6+3Fif+Gopx2/qmk+MEQlwYeA+gOcSK048BHkTty/ol/boHuVeL2BNC/cf9OVRjJLYHtVb/DeW791LSQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.11.11", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.11.11.tgz", + "integrity": "sha512-Eell3ADmQVE8HOpt/LZ3zIma8JSvPh3XgnhwZLT0k5HRqZcd6F/QDHc7xsWtgz09t+UEFvOYJXjtrwKmLdwwpw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab": { + "version": "5.0.0-alpha.124", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.124.tgz", + "integrity": "sha512-K/XEv1zYyLi3tS63tyDta1mqWql+at5w7rWp5Yd63Jx1NjPUtgopAvyRZG2SVYPs/yBwfyDPW1iqQXpRw8h9Xw==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@mui/base": "5.0.0-alpha.122", + "@mui/system": "^5.11.14", + "@mui/types": "^7.2.3", + "@mui/utils": "^5.11.13", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@mui/material": { + "version": "5.11.14", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.14.tgz", + "integrity": "sha512-uoiUyybmo+M+nYARBygmbXgX6s/hH0NKD56LCAv9XvmdGVoXhEGjOvxI5/Bng6FS3NNybnA8V+rgZW1Z/9OJtA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@mui/base": "5.0.0-alpha.122", + "@mui/core-downloads-tracker": "^5.11.14", + "@mui/system": "^5.11.14", + "@mui/types": "^7.2.3", + "@mui/utils": "^5.11.13", + "@types/react-transition-group": "^4.4.5", + "clsx": "^1.2.1", + "csstype": "^3.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@mui/private-theming": { + "version": "5.11.13", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.13.tgz", + "integrity": "sha512-PJnYNKzW5LIx3R+Zsp6WZVPs6w5sEKJ7mgLNnUXuYB1zo5aX71FVLtV7geyPXRcaN2tsoRNK7h444ED0t7cIjA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@mui/utils": "^5.11.13", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.11.11", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.11.11.tgz", + "integrity": "sha512-wV0UgW4lN5FkDBXefN8eTYeuE9sjyQdg5h94vtwZCUamGQEzmCOtir4AakgmbWMy0x8OLjdEUESn9wnf5J9MOg==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@emotion/cache": "^11.10.5", + "csstype": "^3.1.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.11.14", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.14.tgz", + "integrity": "sha512-/MBv5dUoijJNEKEGi5tppIszGN0o2uejmeISi5vl0CLcaQsI1cd+uBgK+JYUP1VWvI/MtkWRLVSWtF2FWhu5Nw==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@mui/private-theming": "^5.11.13", + "@mui/styled-engine": "^5.11.11", + "@mui/types": "^7.2.3", + "@mui/utils": "^5.11.13", + "clsx": "^1.2.1", + "csstype": "^3.1.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.3.tgz", + "integrity": "sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==", + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.11.13", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.13.tgz", + "integrity": "sha512-5ltA58MM9euOuUcnvwFJqpLdEugc9XFsRR8Gt4zZNb31XzMfSKJPR4eumulyhsOTK1rWf7K4D63NKFPfX0AxqA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@types/prop-types": "^15.7.5", + "@types/react-is": "^16.7.1 || ^17.0.0", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", + "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "dependencies": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <4.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@remix-run/router": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz", + "integrity": "sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", + "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "dependencies": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "dependencies": { + "@babel/types": "^7.12.6" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "dependencies": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@testing-library/dom": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.0.1.tgz", + "integrity": "sha512-fTOVsMY9QLFCCXRHG3Ese6cMH5qIWwSbgxZsgeF5TNsy81HKaZ4kgehnSF8FsR3OF+numlIV2YcU79MzbnhSig==", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "peer": true + }, + "node_modules/@testing-library/dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", + "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@testing-library/jest-dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/@testing-library/dom": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", + "integrity": "sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/react/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/react/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/react/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@testing-library/react/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==" + }, + "node_modules/@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.3.tgz", + "integrity": "sha512-fa7GkppZVEByMWGbTtE5MbmXWJTVbrjjaS8K6uQj+XtuuUv1fsuPAxhygfqLmsb/Ufb3CV8deFCpiMfAgi00Sw==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" + }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.10", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz", + "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "27.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", + "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", + "dependencies": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "node_modules/@types/lodash": { + "version": "4.14.192", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.192.tgz", + "integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, + "node_modules/@types/node": { + "version": "16.18.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.18.tgz", + "integrity": "sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==" + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "node_modules/@types/prettier": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/react": { + "version": "18.0.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", + "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.0.11", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", + "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-is": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", + "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-syntax-highlighter": { + "version": "15.5.6", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.6.tgz", + "integrity": "sha512-i7wFuLbIAFlabTeD2I1cLjEOrG/xdMa/rpx2zwzAoGHuXJDhSqp9BSfDlMHSh9JSuNfxHk9eEmMX6D55GiyjGg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", + "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "dependencies": { + "@types/jest": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" + }, + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, + "node_modules/@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.56.0.tgz", + "integrity": "sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.56.0", + "@typescript-eslint/type-utils": "5.56.0", + "@typescript-eslint/utils": "5.56.0", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.56.0.tgz", + "integrity": "sha512-sxWuj0eO5nItmKgZmsBbChVt90EhfkuncDCPbLAVeEJ+SCjXMcZN3AhhNbxed7IeGJ4XwsdL3/FMvD4r+FLqqA==", + "dependencies": { + "@typescript-eslint/utils": "5.56.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.56.0.tgz", + "integrity": "sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==", + "dependencies": { + "@typescript-eslint/scope-manager": "5.56.0", + "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/typescript-estree": "5.56.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz", + "integrity": "sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==", + "dependencies": { + "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/visitor-keys": "5.56.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.56.0.tgz", + "integrity": "sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A==", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.56.0", + "@typescript-eslint/utils": "5.56.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz", + "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz", + "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==", + "dependencies": { + "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/visitor-keys": "5.56.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.56.0.tgz", + "integrity": "sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.56.0", + "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/typescript-estree": "5.56.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz", + "integrity": "sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==", + "dependencies": { + "@typescript-eslint/types": "5.56.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz", + "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", + "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "peerDependencies": { + "@babel/core": "^7.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/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/babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "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/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "node_modules/bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "dependencies": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "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/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001469", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz", + "integrity": "sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/canvas-renderer": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/canvas-renderer/-/canvas-renderer-2.2.1.tgz", + "integrity": "sha512-RrBgVL5qCEDIXpJ6NrzyRNoTnXxYarqm/cS/W6ERhUJts5UQtt/XPEosGN3rqUkZ4fjBArlnCbsISJ+KCFnIAg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/check-types": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz", + "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" + }, + "node_modules/clean-css": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-js": { + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", + "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.1.tgz", + "integrity": "sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==", + "dependencies": { + "browserslist": "^4.21.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.29.1.tgz", + "integrity": "sha512-4En6zYVi0i0XlXHVz/bi6l1XDjCqkKRq765NXuX+SnaIatlE96Odt5lMLjdxUiNI1v9OXI5DSLWYPlmTfkTktg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", + "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.19", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + }, + "node_modules/cssdb": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.4.1.tgz", + "integrity": "sha512-0Q8NOMpXJ3iTDDbUv9grcmQAfdDx4qz+fN/+Md2FGbevT+6+bJNQ2LjB2YIUlLbpBTM32idU1Sb+tb/uGt6/XQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "dependencies": { + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "node_modules/deep-equal": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", + "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", + "dependencies": { + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.2", + "get-intrinsic": "^1.1.3", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.335", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.335.tgz", + "integrity": "sha512-l/eowQqTnrq3gu+WSrdfkhfNHnPgYqlKAwxz7MTOj6mom19vpEDHNXl6dxDxyTiYuhemydprKr/HCrHfgk+OfQ==" + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", + "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.1", + "@eslint/js": "8.36.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.5.0", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/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/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", + "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", + "dependencies": { + "@babel/runtime": "^7.20.7", + "aria-query": "^5.1.3", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.6.2", + "axobject-query": "^3.1.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.3", + "language-tags": "=1.0.5", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-jsx-a11y/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/eslint-plugin-react": { + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/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/eslint-plugin-testing-library": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.10.2.tgz", + "integrity": "sha512-f1DmDWcz5SDM+IpCkEX0lbFqrrTs8HRsEElzDEqN/EBI0hpRj8Cns5+IVANXswE8/LeybIJqPAOQIFu2j5Y5sw==", + "dependencies": { + "@typescript-eslint/utils": "^5.43.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", + "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "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-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "node_modules/immer": { + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", + "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "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/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/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/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jdenticon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jdenticon/-/jdenticon-3.2.0.tgz", + "integrity": "sha512-z6Iq3fTODUMSOiR2nNYrqigS6Y0GvdXfyQWrUby7htDHvX7GNEwaWR4hcaL+FmhEgBe08Xkup/BKxXQhDJByPA==", + "dependencies": { + "canvas-renderer": "~2.2.0" + }, + "bin": { + "jdenticon": "bin/jdenticon.js" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-circus/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-config/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-each/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-jasmine2/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-jasmine2/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-jasmine2/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-jasmine2/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-matcher-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-resolve/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-runner/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-runtime/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-snapshot/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-validate/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "dependencies": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { + "version": "17.0.23", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.23.tgz", + "integrity": "sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-watch-typeahead/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-watch-typeahead/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/jest-watch-typeahead/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "dependencies": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-watcher/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-sdsl": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "dependencies": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "dependencies": { + "language-subtag-registry": "~0.3.2" + } + }, + "node_modules/launch-editor": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", + "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dependencies": { + "lie": "3.1.1" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-core": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash-core/-/lodash-core-4.17.19.tgz", + "integrity": "sha512-FpSbOooU9HcASpL7oJ/ZQGxR7oGzeqlVe1M/iAhnUMTk7eMJBkszS5tUPZCnRtNGPWe8ChswdByFTzW58iGQEQ==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "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/make-dir/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/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/match-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", + "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "remove-accents": "0.4.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", + "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.5.tgz", + "integrity": "sha512-9HaR++0mlgom81s95vvNjxkg52n2b5s//3ZTI1EtzFb98awsLSivs2LMsVqnQ3ay0PVhqWcGNyDaTE961FOcjQ==", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "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/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", + "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-path": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.6.0.tgz", + "integrity": "sha512-fxrwsCFi3/p+LeLOAwo/wyRMODZxdGBtUlWRzsEpsUVrisZbEfZ21arxLGfaWfcnqb8oHPNihIb4XPE8CQPN5A==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", + "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "dependencies": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "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/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "engines": { + "node": ">= 6" + } + }, + "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/pkg-dir/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/pkg-dir/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/pkg-dir/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/pkg-dir/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/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/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/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "browserslist": ">=4", + "postcss": ">=8" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "peerDependencies": { + "postcss": "^8.1.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", + "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "dependencies": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "browserslist": ">= 4", + "postcss": ">= 8" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", + "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.1.1", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.1.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.10", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.2.0", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/postcss-svgo/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/postcss-svgo/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/postcss-svgo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "dependencies": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-dev-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/react-dev-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/react-dev-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/react-dev-utils/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/react-dev-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.10.0.tgz", + "integrity": "sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==", + "dependencies": { + "@remix-run/router": "1.5.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.10.0.tgz", + "integrity": "sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==", + "dependencies": { + "@remix-run/router": "1.5.0", + "react-router": "6.10.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "dependencies": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + }, + "bin": { + "react-scripts": "bin/react-scripts.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + }, + "peerDependencies": { + "react": ">= 16", + "typescript": "^3.2.1 || ^4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-syntax-highlighter": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", + "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.27.0", + "refractor": "^3.6.0" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=8.9" + }, + "peerDependencies": { + "rework": "1.0.1", + "rework-visit": "1.0.0" + }, + "peerDependenciesMeta": { + "rework": { + "optional": true + }, + "rework-visit": { + "optional": true + } + } + }, + "node_modules/resolve-url-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rewire": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rewire/-/rewire-6.0.0.tgz", + "integrity": "sha512-7sZdz5dptqBCapJYocw9EcppLU62KMEqDLIILJnNET2iqzXHaQfaVP5SOJ06XvjX+dNIDJbzjw0ZWzrgDhtjYg==", + "dev": true, + "dependencies": { + "eslint": "^7.32.0" + } + }, + "node_modules/rewire/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/rewire/node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/rewire/node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/rewire/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/rewire/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/rewire/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/rewire/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/rewire/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/rewire/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/rewire/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rewire/node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/rewire/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/rewire/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/rewire/node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/rewire/node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/rewire/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/rewire/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/rewire/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rewire/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rewire/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/rewire/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/rewire/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rewire/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-terser/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + }, + "node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz", + "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sort-by": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/sort-by/-/sort-by-1.2.0.tgz", + "integrity": "sha512-aRyW65r3xMnf4nxJRluCg0H/woJpksU1dQxRtXYzau30sNBOmf5HACpDd9MZDhKh7ALQ5FgSOfMPwZEtUmMqcg==", + "dependencies": { + "object-path": "0.6.0" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" + }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.2.tgz", + "integrity": "sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/stylis": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz", + "integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/svgo/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/svgo/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/tailwindcss": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz", + "integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==", + "dependencies": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.6", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.0.9", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "6.0.0", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/tailwindcss/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.6.tgz", + "integrity": "sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==", + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", + "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.5" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/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/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "node_modules/throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-vitals": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", + "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/webpack": { + "version": "5.76.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz", + "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.13.1.tgz", + "integrity": "sha512-5tWg00bnWbYgkN+pd5yISQKDejRBYGEw15RaEEslH+zdbNDxxaZvEAO2WulaSaFKb5n3YG8JXsGaDsut1D0xdA==", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "peerDependencies": { + "webpack": "^4.44.2 || ^5.47.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-background-sync": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", + "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", + "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-build": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", + "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.5.4", + "workbox-broadcast-update": "6.5.4", + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-google-analytics": "6.5.4", + "workbox-navigation-preload": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-range-requests": "6.5.4", + "workbox-recipes": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4", + "workbox-streams": "6.5.4", + "workbox-sw": "6.5.4", + "workbox-window": "6.5.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/workbox-build/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/workbox-build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", + "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-core": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", + "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==" + }, + "node_modules/workbox-expiration": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", + "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-google-analytics": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", + "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", + "dependencies": { + "workbox-background-sync": "6.5.4", + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", + "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-precaching": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", + "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", + "dependencies": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "node_modules/workbox-range-requests": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", + "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-recipes": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", + "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", + "dependencies": { + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "node_modules/workbox-routing": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", + "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-strategies": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", + "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-streams": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", + "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", + "dependencies": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4" + } + }, + "node_modules/workbox-sw": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", + "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==" + }, + "node_modules/workbox-webpack-plugin": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz", + "integrity": "sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==", + "dependencies": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.5.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": "^4.4.0 || ^5.9.0" + } + }, + "node_modules/workbox-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/workbox-window": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", + "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.5.4" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/pkg/webui/ui/package.json b/pkg/webui/ui/package.json new file mode 100644 index 000000000..eea5b6cc0 --- /dev/null +++ b/pkg/webui/ui/package.json @@ -0,0 +1,67 @@ +{ + "name": "react-demo", + "version": "0.1.0", + "homepage": "/", + "private": true, + "proxy": "http://localhost:8080", + "dependencies": { + "@emotion/react": "^11.10.6", + "@emotion/styled": "^11.10.6", + "@fontsource/roboto": "^4.5.8", + "@mui/icons-material": "^5.11.11", + "@mui/lab": "^5.0.0-alpha.124", + "@mui/material": "^5.11.14", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "@types/jest": "^27.5.2", + "@types/node": "^16.18.18", + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "jdenticon": "^3.2.0", + "js-sha256": "^0.9.0", + "js-yaml": "^4.1.0", + "localforage": "^1.10.0", + "lodash-core": "^4.17.19", + "match-sorter": "^6.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router": "^6.10.0", + "react-router-dom": "^6.10.0", + "react-scripts": "5.0.1", + "react-syntax-highlighter": "^15.5.0", + "sort-by": "^1.2.0", + "typescript": "^4.9.5", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "node build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/lodash": "^4.14.192", + "@types/react-syntax-highlighter": "^15.5.6", + "rewire": "^6.0.0" + } +} diff --git a/pkg/webui/ui/public/.gitignore b/pkg/webui/ui/public/.gitignore new file mode 100644 index 000000000..c9e9f1beb --- /dev/null +++ b/pkg/webui/ui/public/.gitignore @@ -0,0 +1 @@ +staticdata \ No newline at end of file diff --git a/pkg/webui/ui/public/favicon.ico b/pkg/webui/ui/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..496bbc69cabfce158be2fbc7e709e8a81a5bc95e GIT binary patch literal 15406 zcmeHO3v^V)8D6vxTWwWZwYJ*gQCn+UYisN2sUD@R)x%@&y$Qy*0*Z(SJr#k_~K1sI8X6j^_Pc$c$$5q)hSqBj@C_1{Q@LcIGU`Zm8@bQIo zzk~u!KkI6i3>TnrqpY`Mdm0cH{5ED!1ok3XvY zW7L#BgJX;QCBv(4O7+QDKo8fdFm~@AkLM(R#N7+HyD*LmAlGiZ?+HY- zc^H?EeNm&YXF+FJYug{k6#Xs)?1m)Yk%WMrMqw<+B;NJnM4Pr5_Tcl>8Z>Y72i#J& zjhWh4JU^xP3;{i}0X$=T_|7`en?|_eNjHn}vj>i2J%7HiG2+>pyTts}6$ge+?Ece4 zE$|~+BX~8)Z}jQ>XKU^}Fnh&|A~b)<2w7h;v&*-^=MFrV$+|$5erZBpT?C?en3ahi zVF*hcS!Y)UqQ?Eiub4X+_{RNqFq!H+5u?-db-fzv>Yv->jk_<%1bZxP^fR6CbjCyf z)ZU*zYCMSkHp6BPKtGj5@#{`^qQy4AhA=*#(N_`I2{xB{)o;l$gA5TTT-Z@K1&1-h z_zKui4=4C+^_Z8{D!t_U=MR>o+X(#Bx6poAg0adXs{m4@{jy8b(8wMcrh^hUe=(gyZ2+0Js#n=gsj!on8Pu)-eA zcOb0w1id3F?LbJ&x6*(QxLLxCs=G(TUh}pD!uowyxb}SeBJMjuyHyWieYlkl+DA(M z$+Eu{=2*UA+ujuYu(sMt3+v0dJUo7jbWnpFa-(pUU z16(C}vK>78NZHLY%sH$Lby^3wCMDtaVQs(5w2iR7FG0fySb6fBw7enB0~k~G1T5kJ zlQ-5)^e6q%8hsJtPOB~pqwYVZ!zS>95$$&>f3E425&dbj#fj?V-Nt97!)co_kDj9L zO&bX5qtjs${1H!#B1~eOw4s1==$mfFNoYR=9n5l~XH$RBrp~H6TNuWvm9IW7%I6Fc zQ|q4-4O{9N8%_&iJqArPR!q_+KILXiw+YY3c&t2f`%c(J6Y!gc$8Xv?{*?iTYF;lE zQ{EUUiYMQ^YFJhNc^ogsK=i5JsBxjk<2p$lL%t8I)4o?&rCkMGt;{A1#$XlW% z+B(g#ArJDh<74G({l?_jg$Fj?*{e#PQkq!!MM%l5ys7;_LE6v>$`-^lZFS z?O|<>FO>hYW95&1%y+Oaco_ZOD`}&DlNr;XTqx^ME`krtV{DYFGvhVgmu+8WC!;@_ z_iOr8JRhd$nR7rJD`R-tkZj{#EJ>#WY_vqyn`M-~Q5#LaWyedc!y526;Bd~R%`dEP zRqD9X&r3MQ^a`C!cCyhMcK=m@O}#>Q=YZZPlBO)jPP-4XX!VTmQowkDkGodFSZS7) zUhz$|+3ocN)}MWzioCN`T6_omZIYfWm#mG8aWfv+>kD;Fvn4NL8wm%ReXR5_cITsg zPYMP)9Y1S;Sg^VhYp45GD?InVD(u2F5mCRsmg6wM3d?WU-%Apne%~I({g8xVIrpuX z#p1Ow5eS?6@ir&}XzLMuBjIXae@1NDzEX^=zJ0w_?-B`iCikTD#Ya=|?wFw`~omgRFE7H>B#5KGoh65fdBIUclgp}}?B7uAN1p4w;k=&ASaX1s5g zzsZHx|2howB;0WH!gDNn5}>#Fc`Mu(aExH5+hDJUkq=qmuhhDE%6pyC1~9`1VPRJq zXmH>4IqYsbXbmUr3~0yPl#Jh($vLK!pJI(3;^dQ6#tGkR48hv@nSw8U)>3b!_6rsM zc)iEDPYr8zHatzK-yW z?wc_FU*LHHbUI|9)d4x@#~@>yyb1dZg9c`w6gXTbN@!6G*dP%7pl5F7usE{1{)u2fU`r|IFlD#mbGdaaQGT zDT?-(X?N!2M6W&d>;qy{?#I4Rb${21o?6d50_JmTJ~OH}$Q&f$=o3Mk zc~8^!;b%XjA5&>Ky>m~EHT#hy{+5J{TBTg$$OrV)oRyqk^zk(uk6iXJIIDgD@Y1&& z#NPU84{66KyP0E8=6?;u+{Qgl^2&PhByXJM@~kgyy`jvM9p@6v-)CA=kz+T8Fuy8u zWRizkQr5P4T%46g^%dZ=0`w7ICv*?lm%+!{<{wCpJc4oUBH%p*zTlnaucUqraK;gMm+7#AP(Fcj^)yCP-GR<6NbXDD$=13qPEuDHYE1!k@=w$RI* zCG%nZbH#OlLmXt-#X?#7VB%!Ngq>|4b98N&OKY0KLxIKH*Fvp zk6HJloYV9f`_a#RfZ5N7dc^8DyHsHG%WSQ{MGQ!0C^%tU_6XwwE}!z2?{$y@JT=qW(7c;hrWO@>Iw< zjzXVm@%=vh40*RhX7ip?9enK=(*|)@34D8z4a;@cJ;0*|yeMOTtcT}N z_S6r4i+d0DiEBnCU&(ww?^>8^Vj|z(e6;dI&&^naI@53Q95xd_&ULP(dV`FuGH~v~mvWE7_#C-e?r9F-k-GnscaDZt z=by*e4D}(UjW8|1`v`ur+8^$bx|8T6N3QkMb)Y8i2k^C*fqpTb)OoSOpX*sUV;M=8 zevUd#!G!gxs*MyBAeS07&-tutCi`*_{31mW{m=CAr7QY6=JO;!Yf0bEJHF6a5HimO zuiYtGT6IcZhhRr9QeKtDAjGI>E0;LPSyk{3unJRDT5vkrN8@4tNz3vuKdmi>9)@GQ z-MQKp?__{hIdCX*TN|2c77G`7$%tjeBLm>9?IpJnN4(XO3G^F6=xRj{ zF$nGIpQ@dv@r5oS{{km{RJ{6_n7M2$`f$re*w>$|`10G_GvY2?D;;e$>twsN3?hdD zTlse@d>j3X=WBb3@B)v(S^7NQ*|*}$Z{7h${M2Yir)vI_$Zt1@QB&{X9oj*e_Qc*$ zkBgJ`m$VCQN3c@6Nb<3k)G6Y%f#`$Xd^UCV!R{u3xU(_S{T1deA-g3XrHj&;4~VV1 z-W4;KzW{lRWr(F5bF`H0=_+>@s}6X_-72^G?TO#e5p&g=-!kv|u56qmcJAFGo~`Yf zF~7=r2OEp2yyLhdK-stKSSOyV>4|ko>!$Lxd&k|Jwz(e8gT&d49eApaYZ%~;aE@`G z6^!NMuB=b&JFrX4Uo{alv{$&}t=>H0czpS5GpX58;66T>Inw2QjS6lY7K@Ev)k3}O7Q z1^g)N9eWg`q$G!Y8Sf}rW#>2i%aKfTSn?(h@&XR>Xa;@wPV{LVWs)$tl*Ki9XM3aF zuje|(`M-@rS4#uGAGvn zzGDpD{;FijRb~|q|Ci7O`bQj=u6yQeQ2&vzn5;E5tX&w~m> k-LAl1ECQML7Eiu)AL7+G?;m^__cBhDe*a&N|4t432jt`J!vFvP literal 0 HcmV?d00001 diff --git a/pkg/webui/ui/public/index.html b/pkg/webui/ui/public/index.html new file mode 100644 index 000000000..42dfc725c --- /dev/null +++ b/pkg/webui/ui/public/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + React App + + + + +
    + + + diff --git a/pkg/webui/ui/public/logo192.png b/pkg/webui/ui/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..11f22322c00fe673d7aa1530ecfd53fbc82b2373 GIT binary patch literal 13703 zcmXY&V|3tLw1!h#Q@c}RYE3(}+o|oTt*LE$YTLGL+qP}@_Ph7~NY2X2TFFkQfeI4yI5rx8rLi^(Sp1sbHxjepRrplYlV8kRo&iEf>^m3#fc#ghbythAIJiJ0* z>$!qgq+BcBVwf31A&eY@uAqnJq`?n}A(sV(yTRNw=zPe@nKFXYR?bm7(X~u8FWMib zG=Z4yoU_+I_inoKbhj9P%0gHeZ+!#W{aBvhn0Zu!F-&Mn4idJIq3x$MWdH_-Vr* z!Ei6Y#99^_k!g1t{Pzr;-3$RGyO^)gYQSyDgR9F7CMq1#>oDS9#iNoG+T=x_3E%$kma_H-6hmO+$xl*)UM* z2hrhMjH05Jr{OmL5C~;rwYj1Z`dQu^B6jJ8($aXmS8F7P#!^e}jgy37vD^s9-0=*I zvVVqyWsLA4je@w^_ij8)dT@BUw^K4^iA#wH;{lNN_3PzO-_?Z3O?vrEjZr1(<%FtAqKfWUifaF~P@;i|3&Key|ro#mw}>!428ftwh~ z!R73g@Wsa{K0H7-GI$PD1qanAs2Gxa)m~tNOn;iE*brZd_j1jDkS8r-J^o`AByX#G zr#iAT(7csubPxntAR5st<2de7{8rHPNp#wcf9k} z`r>u#r@IakGd6wXM!=j74Ar5;MAqVDf7eERF!tn)$7`O# z`8E{^?2~7_fX-=5JT({lTvTBS6Uuu+n?QZfBC$x#7-&*242A5U;M_Lgax^tc-(%QHcgDK;(K**8=E`ZVj8crXt0mXmcN5r`9bjsC^g+$#J!=QoJL<>P>6}t#(i-bW zC!GB0j|k4sUvQ(1FHsKN>Yp`ZqMgiB?y+*{Um;dV(>cG{2h7ED_D4}A7*uaR_^52h zWEk^ZkT1rCe7b;bUOuhM@F05Y2iA7O{soT0)0cs0t9z4mlql_($cORBGX%|FuQ1MP zeIQx2o1pz)abORL0emeB?Mk9~nSW8F&E`SuR6Kn9I~U$(LOMb09rfOR)_rsd!vd@X z)I`uW?TCXQS)GKt)R$J2mPy@WOf&V+Nm9rVOPtu-4qx8 zzmwW;ugCO|>P+if0gl#=I(lZ)R$y90i~p`b2Xr5ZP8VNQt5zwHX5?LxrG&hsmG)2J zamZu}X9s;3U@(DSg1Z^;`?-}?!#{GWAXr37N!5Rhy2&~%5i5)D1%@>>o(p>`jV|C0 zo@2IwdOIo~WhUi8bDlkZkjmLX%K{6;jHZo$8?_t<6KwrMS9oUD>E;@Kz5a9o=|;=U z&q55^D~!xw*o8B>D_uDIsi+Aa9;DxMY)?(&y0W4klQp-XuwB06!K?GAS6I6caA@-b zf*zL+m`31Y#w7R;Ywl~^il+rrF4q!;LhPr3- z_YD4{Kc>uWo*`acNoK3Fk~JHMQ=ooG$N|+9!AV`=tZOY#m=p-#&FF-Y+3!tv2fyG=y9LZnD(>9IJ_#aN+eYtGqFi4nrXe3`c%7BF4P1f+H;UW-;`A0`x1 zUONOTjo|H>2bT{ewv8vD?*#zSe7Z(gmH|Ku55phvZNN%7t~0JcCXOpjPNiZZb7eFq zm{7|8EGPzkkkA{F4l>E)C)PYwfI_WUuoqqgj`qpFysq)G`Q>axSFSC6c4JgRSG z;)RJj>JpqLF8>jZ#H(KgOKtw~)hwzV zEnbr@{b1z)!L_=M`o+GTdtX}hlo;RP4pR@uOdY&cSDaydn8#7-8?iclf(_}7_BdAy zv*Q7L6wCNt-T2(b{JkD_;KknW^7<@9ANqU{RCInH&)tM3)I!b^OE2BKtR-Vw#r@~A z196g8OEw4}Tk`pd?_iCVjep+qBS;0&VK3v!zGe&9Zapiuv9OIOrG{>N(50+g?Z(g(*5 z_-u5|*cqpPva1J-iq+E(W13Puj{xZJ<9?A=iCEyq;;s#EZQ&^~F_$fIXm9LO*$Xh^ zQ^hX0WvkDxjD?Lc5Y{@0+E^>E7VsAfh0?3amahn2?{!$eKsUgJ+@1cqMoJ1{DZ}6b zB%5RB1uJn*0FPEB1%_}x#8=>r2raO*mJT!K&vO!?S8vuBxz6{8AEl7tWNcx^!E&4a zuB$exk;(F~T2ZKb->Mh`pnpN^zRbW63h1*0RtkH*W-iqX1`(wZcCck>$-{*cvcMl^ zIG1E{MnFiY&_fe1cssy;=vz${iH?~9@zM4*ku{AAyImIW(dgONWo zoUZw}91TqVm4fbyzGlNVhTMAq)}5Oe-al;lc1to;*lAadTk{#kWl|;MJ(y^C&?H!| z(CVoQL?=&Kx;+fTlQ{+K3Ry0L&xg{uWd?+QU@mSCCEja)zHrBt!6fN_>oino+datR zc#k#3-d7BEJ*!Q7&r*+Qr5;vQiut$Bs}6WNe9SeJ_J0 zmp|iuzPxaMb~1O$&KJysg^8>wP{eLABH))GV*oL585dcyhTpwM{aCRixtr-3n#>8g zY`0k*N=D&#V3flSST#8A1Q5LFwi7ddi$nqJi zSr|v{WU^^5!A-Y6CuI7Cx!(j;j_--=E@r2eF3CPSbYxuB3ThWhzl)KG07UsFCscW+ zzX7IwVCk9SzQ6sDoMd_@Q=!q|-q_i$9}2nNm)}2Lx!>+1jir!FY5l1G!f_ZT<-4`W zcZQB1qm1p-xDZ{9JB9dfcv&At&$EplDw6Fxki7Cbrsgk+ms{oao>2Z?kFfhWUkk}= z=Ja=xHDHGwvwtXS^ciO3uxUp`F~TZbfzbM);!o)GTsWXtU@XC0`zd415FYpY>zgI& zpDc2OcE(ZuW43t(T5J16%%PQ>c?(`;a(eFY0mYZPq$*9l`EyI5A@{`NsLKoIYtMF+;<6cygab=K${n{PBIQMwTWwE@u=~WeeL(QvY4qGTujk$QpQUy(Ll&VmWlMLcX@nS%Rt;z0 zuh#pqIRf21{de@&DGE*<0QSIKurc~Lg99w4MCie&J>ND(;qAo;%?FDi!enHFwPZnt z6P8cfz&}pb{q5GH=#M1*r`_0am5Va)7#IMtI0_y9`9cXdxh4ZF_X=LK9ufQCLD>rn zLqaQBPk+RC65B}n;kT_b8%Tn#V=`gjZ`W_7jS!q8_S3nCvnj*&V4t8Eu7(ZLk+Ja)9c*gwYbb(LI^&+bi9-7)4gGaoF}k5wFcdE-zV%q`Zg8J!899@yx4Sb!-(oBK^zVc} zX=xi(t(CnCyMM+M4x0$xADO2vfuerIg?i$*>U$|c9C&?VA9!AV78q}#GE`8F+dt7m z1#bOO1%mhbD66EHVq54#)`s6bjD<3^c59++dG;YpU7a7&$B6&cmv8(O-k~5Z1W=k!TTSx7MK#; z2oPHVLXhiVOE6X0E8f&HsZ%Fm5m?BZ9+kfTk=hOKsZe~iq2eCPoI})PHlQ$`sz&oU zoP~Sm@NK+ero`%xeL8c_?-oIAC-MgADoIGWIjv4EJkgwc0UeW{o1gM3OTg7SlvXQA zW|#h*Lk*+6?Rk^TDxFE{93J6k1E-E3e0-mcDy_!{hN*hIV&mv=mjQRVfsK`E%kH!o zp>J*LZ3TpC1$)E7u9J{K(ye6zI=lZUE*cwPpf5UZbC0P!L}NBc%o8eRal$UpS?gOj z16>m-&MUuOAr9{fzZI=d8-*8V^phaWU<$EZbC}(AQfvibdO=&6*6W9qo3m7~twTxB z8WjSzB$bU6sQ}&9a8`nBy*DP^9Iu(Y^QWS&O2 znD}3r(~_=NYOr1sJ=shY7ml}&82F$I+~E3g97w=LWGyFju}f@zu_9QUcDlq_vWY#! z_nvK@+c4N$K2IOj6ceo@+rT2y9Vboos500f^A*Z8S-FN@xFk=Gz!ahVuS&B z;t42WANW9EI>U2!&Nkyq;j=0&Su<5cjyl*IN>&Ig!fwv}A<2?K|0ABq?xawWy^t0r8KNE^^_fIcG-jb~c=hj{y&0 za;0J}%m590D8p7cfImyOWyqi!w#RaGY3JD-7#N~)#c!uGet7Sh3Ne`4oTvNno;4` z=WO7FIT;p4&q~kXx1Qd}WWNBv0w`glOvr8K1`G@y&kiyrDWyQMJ-T;J(CK+Mf{eg- zE6w&aKRC0z7Jy_8m`HTT>B7o!`iWq7<7G1^iK4D{b>SPyM;?Iz%30|t+gX{y8QCIo2ea^ zASx%^I}q=VTSopQ*@_IteU>n#e`V!6&@X(;f0~znTrD~>5ChQR z!S3 wqli$aKQ2SbKZFKg{E9g@WEz#e7APR8O^xSmtxq&tn!vCgCj&k^n$_Wq;zA zAM`Dv;VH~GnZEgN+f{tP6GI?QJ%CAnC)TK!g(l-IRitx%djDsAT3SP8(raAI-V-XJPO#E7uu)q zC5$n<^Wi}vP<#<2(vH+LMoTN9q}D1A)V(1`*&-=i){4-*;cZeF2-e!U<;2#`V;balnGP<`|Sh9?U$#nvTrMq z2ZvdG(bpSzWSX=I^9<1%KE*Fq^dfkU9l%0~;KTQi(2=GY9xApw<-xM+u)7awFaN$5>r(HwEOb^^?O96vocq-^A~aW7ganJsxg>my^}3eei%8 zyRcD;3X-a|zRxQ5k>SmA{5kCO)4khDq6fAfq+9E22pk~A(|!L<{M-Wi!yUC)@1i*f2chw_?%TD5bN zOzu;LoAxIo4*TOPVjnwdRe;6}{8+QQ$A!4o$~Fj;p&j~dsOJ(z)R(`Qm7=T_@|~jk z5`=4&X3i;>%1#Qp9Qsp4m2zOLh$ho9^HGo@wMrANpx9y~Il-2JS7p8+{?^2PEe5;A z*Gp0%DHEub!<9;f9=fP&6#S^}C{$Rh7zQ5_S~(vJe*B(3nne_Z=$at%l2SZv8G6jl zr^Ddx^bPGAek=6{ZhRVX7d&mi;x8Xs7+&(C6ys0NEMd4Tyq*G+BLPXoXj3QRy*}Ge z*=lh=`GAs=5utG-2&LjOb!j7E7hr4Jn~FBgzR|P=iceE;# zWQp4yeu~XCS9UAPWKQDu_zkTa8P;zF#U@4EukzvPioqG3@f`eGnW0`+fEdKqZnW$p zu8zsN#22ntJHfK---fqVE$G70I3CaGvof2*<-SfEcn*nXXvBq@B!?_wPbr4fn}?Or zM2py;i727GNDW{Pe9~!tQu&!FNhIk}4K1W1G!5a8fLXvIHK)953L3E>LudT@@oaZFbR$=kpCmi^sIyRT5P)U2grZ$FJrs(Gq_r<-BImTq<%Y9Do)7J5mJ976Nn{_OJnA z1QRk=jM_Pc>KvnyR+Dv{<1q^kR9AKv{)VuT0e&M(^K@%?rCBu7!eU&hg&VI*M`YsOvIU%mlhOy zPqT>jJ-Tmcu1Jh@N>?pUKxmKU0_!KP(L{!$Gx6d(8dK1l?q2a1N4 zi@i0ryR@muM9prolt>*JqW8-NaIqGPbw?NA`foj$fRP^x43{-qvyH(#N4IQ*ylI(| zdKW07IMWZk;ZzQYU#pONxuF!nrC-*r6DFR@{0Zy;!4HgPC0<`ofx00V;8F;CFqz9K zu4&Rsd=`F^og9|F@^uQzn|Lu^ptrSpHe&EW5m_WI{}%>+tI=!;3KU=aXjXHj_tC#gi7&1t=g#kJ2RBkgZ*@lO4|Q!(^KX;i zw_;3i+eDsvy9P1FV;L_w40Fn6I!<*p{u zDRy4^94Jzbsayb}ai#7xu@7BX!35(dCTM~)*G__?m?rP-3k=j)4Jw?g14vd%wHk-u zt-QPr3A&ELag(@iVHG=#^Ub$#!@~>a|22b9#Rq{!40 z90vwj$rMoEfG&aMNa$&I`*uzRBl`9S!QuFzn#{Eoa{hJMmyMXp2&N<6Qf#L0ElM3q z?MlJ36MFJj9m~w*G3BrN{*xRKX-{6%CcV-5HU!7q?JJpdl|6IhiQwI#;4CP3aVIyZjp`&Z18iJg6N#@t@0jvSjwi;#x$ldZ8n8 zO0H><4Eir>;Y1*%;s-Rn?~$ZQir z?j&`+5jT3B$5lfB5Hg(E+Rn?@{aN}!nM9yM0SBDVEc?_EV+ib7H8ASAAUHm&@7+Qk z1sSk3?n%!_$TKNnt{Oz+o?gjy`5ej8NKq`>iHI11F3QVFrUk)}pJw`wqTKIGN4t`x zP^-M8)<3TC@wPW58x1O^+GXq2D5Z_o zeWIr-+llKMr=^WSe4mTOGOyV6YL{<7H0Q5J9E>}oG`6cIJbk&vB8HI^;g&8~z} zOtyJn@Gzx`=jbAJk58m}i;Ye=IL{~0%a10!Z&)CPi6X=v{4aUEQ20n-I!`|iLtsko zMIkM+aUvF@MER`2Asvn*{u(3jzZ#WK^`so(zWE4y&M7*V$dX0En?U#VQKrge^qU2K*a1K=TB+>@jgv&B00@0uC&s92(-|`t`InVCjikxiAFu* zMIwj<(Q@t1!Q;rPvOTSIK8bx^QgE4)p-160RgxYrP<)(=#op{&abA%n5fCKf$LnqguuA+WF&kHo%0R5ymcI7K{=Md0wyZ|!l==nO*D*h5p1|YO~;X&O-oQJ;IJo7sTFytPlWlRe}#XfKj_;{kzsS^7#NaJ#$r%DPk9M== zte+1BLBz~ut1eVMuIY#ePzXXAH8N=b4_LyBZ7hV2*Re3lS;nY*d30Ii#zQGVYF#B$ zBl&<8S~p#O?`x|4Im<}%jl|Yq8g-pk`!7A}A2_~^t;)P7z}NBI_+tHzrVD1GO*q2;rM zph_6QHE=rQhQ3l@+_^`ROgd943qz{lZ1Sj{RXc1eT|N(12N>9(i+V?x2(MQs8GaGS ze?}vvB`BxHau$bTk^3ir$xezbAuK||WeDZM7N(J_su9#zR-GWEkHfcRD^J_qS`Q__ zLq;g)U;tg0INzJAQMF%fX`K7PIy_KC4kRfFBh>#Ke+ zyE)`Wyr>5%+}I69`>KGLp(A|EJ@Iw1W$RcRs}4dD}M-VL>B?p z61HQJN!tucMRW0_H?afZ*z3rh?6QFm$+B5KBZ>6)zIb>>gzygFgaI}MGtQOub3&(8 z636-JBYB;BABNB6XHVoDTVo0|J+x2GjO1W~$Xzb~>@Y<0BbCr(;6AQCAu5joRw+2# zW<6Jt1e_iZSU3n#iQqe?Z@aB`tpt!77#<1Cf0hE5rA_+BmL_y}Jp7AJosDTlZ|M^DPB zW}0PZ;HO4Ejb+K>8xDb7C+-lH@a#*>L3}LbZQ~1e4D~g|_owcfGQ8(!^m^f%Jxi#-XB-{^lL+Z1#$1@NG$Pa}`qL9ZF=SAS*oWLB zl_9N_L+Dd8d5&LG9A82B9PbhqR+)*e@P-r{42BgxV+J-7K_|D_je=MVGzc&;z{PR0 z4sQ*Rm^Jg0BcmVG+!g`ln2cjw?dy4cy6<)`s)e3LeWb=aO{K0)5?N zmS(%TJ>H&rSwb9sQd<3asByS$DIe||>6B7Geu}_uBvp;!$%uX6+j4l%TKP6Car5>& zzKRR!pQ|4k|wI4Aea7dw*}sny}2&>^XS9D!Mp_jQex z8;li1sLOaEfwQGo4A~1+Jo+IOz7F0}{FAv=HOlh!j7o)%(h9)0ANr4592EZAYa=5c z8Oj-6SE{`KL-Z2MNF$u*^Q;lk_YTKIPFq0_ElSpYZ)pU*FO4h$c2vL{`o_)JzNG2A zF@|(4UbJcS6w@1a!@1Jd2JO$^QpcDen{O;sgsEU=F>Id9HZN}*e8FviiA6~@ZZoty z%BWBhTd8X88`t z>2UgORu$8DMlGOc1d0T;ciodhquxal7;&MUG;pf9O zd`mcy>*y}Psu;?241vZ8_}D^vNl7wWDo;X9*M#BP14v4ev?-jytaK2nn+HVtPT~c* zmPL-IrPfY|mQbojwbKfoeiFSBGd225_obS|R@Kw4AthI;(Y7GW6evv)qv1Hwic_Rf z{iE&i4r6cpIr+<@*<-YVpnT^?uL~88q!}02&hF`Dl%)RiH=|oh=(uUpg&7v#T{#!g zur~{X^gOVI^g$LdNeYI^l9!`!w{;|Z%t2A^=*(RK*XlL>akkT(XcS9$2`J`>jQB-0 zKgUo=`TZmXrPGFIeL{5vVy!~RI|nP-jRC9t6EJHQu=~AS*cv6<N=f{{5!2ag-gR9wzPO=x(ao@jiTUvf_62EYbOO ze)3jqsT5$0>}*iQiKX_Yh%?yZ zvNp)f$7s4R>Y3eMTh+-DE4~U}Y^63daLW-^!2{}DiOG1`!_Zu*Bv-m@S#xHO|2}w-;=v``}5K_lHe`w*Oe1+l?wxwy~)0$FSwpLxk~4f zUzZAynS>cu>h!AE(^x7Ly|=4)$4|I`x2BW!#-j4jR#oyQ+E&6dIk&FDTC)@KkuX?b zk3j!!v&+Z*gzJ|;n#F6R*arf1X>W}{=;~p$Mh0=FH7WfvA59jd06*AgG~mm7Ggtp@ zc7Sj!Dd!i6?L|LqKZG|W)xu&53{iY07LvouWi|J<12mhfLpKM+pLy13`pk)fo zjU)Bg`%G_dIf+-@Y=oiM_@V|Rp=s&mDGqdiU&pdb-Lm=lSE|Q`mE-@Ju1zvJwuus= z5ndqA($gq*Y%&9UiHHYGI_#7v15qVgqlbq3Z)Qe^WRpoV3f0s%2Kr8ZXJrk=;$VUt zJUY?qMY6MBgyWLS;$3(3U6N(INLxoBBxdStAmuJqW`jKFc6cWD6%JtWk6nhi05<=DX}|U0T~9eHM`}(IxS^3J5LC$HEctYWcjdLC=d3c z!XP_zq&e_ZLqM)Z&NM4nfFRw|5@Rn)ID1DZT)lo?!oZ<0emK@NUKGY1fs(7g%w+{) zvwJ$xrv=k6Oyo(F#(XyjOO*77c6hA$Jn3GtDEh>4+=yr#e4drsFz}hIBxrC97jv0C zEyVIh!9x?YYtSIup-B{EkG-bY(g0#tVo-#fUS%umYj)gVbqKoaCdR5ec)vYuRyA6E zpg{7-p<3jU)Q_M$>I=x~oNPgNBlVWNSUHfXk$;QLV`gmiAPDGF6WvW`EYrQG#8HcO z>X3LVMq#TsbHEVEbmYR4@%UMa|p&VRWxv6Jv#guMf0wSm@-qKb>3=2OLLkb zbU$&#TMJ6;bZ_n8@FC^VC{VgSQuRXZV*%u0H7Ss{xltz`z?n0MQdbqVf@0@HSz3X~~CQHd+=<2E>cOim{~vQpeU)fQM{sf8z5x@teQZXx{D=fB%)iWBHeA z%XLr-dD4agmsnIwpF2M!iSk#))OgAR$RxE4JIoQy81bJJ)!YT~!Z9rrDE@Yk!6wc^ zLz$YU1cOC^bUa2F6xJuAGBI3M>L8@2b!+88b`GX8?m|+M8QfXlNdoVhf1c3Bs7dgd znk3pirBjs^@K^H&6qEkiWJ29D)_+A?dwsNnboyT=TdPV`(IN;o4B^K$7uN z4_8|Cyfp`5u0$VGp&v?7oP_lCiD2bdk_7GgvToQ{+#GKP2XuUC4s0*l{6ps)Lh;1S zk_a-t%;eBC*K&ESu$>`NG@`t9ZlEBZXmtjYe5mrVBSi(R_(jtul7|L_&KXn?|EXBw-KfsGVbA3I>Y6B ze)@Pd(X!uhD_Ac}B<@4zV?7p^1m^uE-$8Hb${}xyN-*ICR!jKLW}r2ivn9Jxqa{9a z&9V~~QT=9r^FBjIG}Zv>qGii=F-Z`YB{)G$JL|Ff39s93RxK`k`S14B2FVc1l{ zAZu=v|MJB##q8vFEIC?DL%JkJ3AW~)Ggze8S9MJ|5f8GURs!YYpFj=IK-Ls2?n_@4xVeafvB`EGtJ+6Y^D`h@3#!H}ufpNP!jW>o{SEv{7B8XunL&=)!wi}6O%t{TRw zBy<=GKow?_6~r@nmpDq4qIdUCz+2e7vv2gzi`H)*u`aEC>`3?EN9I7f3rxlFBSq$mlE&h0u}oNX>CVvX<|CNC zRP!PmWdhU8AR1s0uCU(R56`p*n|0CM^CtAA-G8?yZSjCcmZY_jG7!j+djY-wGP|Mm zzQXsEly~kNljV2BX8Qy{bSkFA+ZC$uBxSQ70MVLT5(?0@iFNZ2hybbHx{XpyjVx% z3?L=qgeD{UbJK2w%;w_$gI3=ZS4&0mT<4dI48JNlJNpr~6xA~jK2J#WJ?-B4*ln>y zKlqkP7LT1$q7n~yrWwz<+b4*8j05e#E;yh*xK0qqrr#GnXtTj4&tpoTo*d*W;Iph~ zGQupHh!%JPgR2Dj0Atl_sp%Io(MY$P_Bng)->rm{qS;Q(yPaz$?T&e(%B>^LDb zrBHET=!p!oC6ESh+KxXJz(J=q|3rw${y)1&Jczr&;K&@v;3`aId#>hZDW&2S%Vs&qyqp!r2tp$v(? zn_DEh4-&j1CL~i~q+KERzO^&khghwKqdNt`cPuIkhcgTMmFP*$%F3gqT8**CQe_N` z50a1Pnrp+4B&les3r`0(xxXAHA)RnS+^vCYjgLLBE&O-DqfUvy9h$cl6(#nK{aoE?2*tU&}2< z=l+HlFyHC(7gCHOo>P|{E3=4g4dd9|#B~)veC;m;u&lEg7_HYDjt&nNMgyFnj~Qfs zsU%fNXmoVqqiO)`&qH)cmfR-kdyN{V_-ri%VsQ}sv2sqMfk~ksi;tMgilbhKdE7M= zp{dw4=7acv88O42gR$PP)O~RJqQsmjlfa+x{E5iXHbs$=;94W$_p+vu{WNUO4{nX| z5l#f~9HG@Dd|!U(@lkFwOt|S}c+G(~fqly^; literal 0 HcmV?d00001 diff --git a/pkg/webui/ui/public/logo512.png b/pkg/webui/ui/public/logo512.png new file mode 100644 index 0000000000000000000000000000000000000000..2bc3b0b03aa840a3beb5879dafeaa47c65dd1ce7 GIT binary patch literal 26403 zcmb@tbySpV8#nsQFvt+XP?EwBItWTBEi#0pgft={NOw0gC@~-UG=g*qBB3JP zUDDkR=V9;te&0F&pS4)RTHencSI6%PRa23>PDD=x0Kjzxc^P#8fP#NQ0r)lW*Pdtp z3HS@*qAn)|6m>E#0{{|Gkdf5%G+cX6=*ehy-iFz~at5Z`v?_OuL~udNT{`B}ZzZ>fR!~15M%|Isaeo4hYkDnOKLs54Xux3l<%4D)+XpVYbHMm`#Vy<`U6?O1LIc zEv@UOp2=C4Gj?`p%)7nv?@xR^EC3h21wf|bs0IVDFj!f|ILYi80mY|Y9iq*V8b0;j z@#q_HY@;j;3Gh-N(}94R3OyQU!hb{Q%U7 z6@VbJ?K2W|5tf#hozX?Jf+Z(Se`Pk^W-qY1e^6mKG6)=dv_}!&jK!KKeIf6$=i|ug zqOhH4IDr#=V4qs zsgcZmUk6MuSwBNNm--`4Tg(Vm!JEc|BBIF}h#`P{hsjK>PrXf&4R5;S=<=L)_jdnf z^1{`+cjCu_fcfih3&^`h!cfQnVIZbo?LF-d0fZU$p#3hz+vvVMuj^BpE3WZTw$1Mb z%jcVgSy>;V(6oxe(10+Ishq4$Py|96UHXg}X@WZ4KPbnG%ttw3*_M7#OOv)R95r4_*O#yW<3hoC?*i_o1q=`OL>1rqyGIhZURtw z>9HbfF#U@afU~CpFmw|EFdrg~-gCo9=#eStJZ^2!*EMjAuQ6OM1Njq*+NewdhYZ*3{tU(olg*lgd5Kb%T$ z$6%s((yFMYiJ8MC(^)||3&RxyusX_|TDDqz=I8azwIN$~Etn90gbq@WLR7MpD2WfMu{E`vw~{t_>zlWw8vrPUU8Hoam))UmovdczD{x zjkR#)g@J@C##19Fzk$UHLEY?1RTk>@utKYrg~3__-4hlD-0YzZ=Zhlz%&U7VtN8jt zvOWUAH{n?HB}i}Hn@9u#Lfv&CB>VK|1LBYO=Y%TzN}0WUED`RIz%oDuZA=LYIxP_b z{J`Lw)?)ZPp5x7$b{~dsa#~O(Vb_ImJB~J;{R^9QKbT_3fsgh_$&qF=Wi?a;?71c52R@!AOk_Dj_2WoIeg!HI01#Wkw;inR$!T&!Hs=gI`k-y*!!(Y4#s9u|QE;93SsEYSD_iv^(N+N-tU!}5@#IG64AQHS0e(jh*- zPdnQ%k8gf?v_5)62wEe14G%FK2*7|~C=7;>pWVNl9Io4do~_njb{{f1Uk<*60}yFq zn*Sp?t60RxGc!I_ZZgL>i?L~{sKH3hXGk#PVw1l~eup#ZwH;)>nH z^~+m%r%j2BQQw0OuJ2v^o{t>*VuZU14E;0)pu>9j2vUhoP8c5|2jj#@-K_r3ijWrs zEr#-`M1vD&mPW2WLFPw4LAS*5`s;{9Am&^|df(*AW6Tpe5P~=B_LvJ9Ao*C^hOvmn zoL#*3=(*4QZQkpdBVprxQQB9H7FWVaf<$6Ln`ed_yG|#or1K?xAj@RFg<2a89Ld57 zkXR3h@ijF1iVWmb?@zISVhP?@3&Wx{v^3&fTz~B|S z0F|SK4O3T4`r6UHMc~0^pMa-yPCT~zNYQA9b#yd6m0;||QDDH+w>SAXNkYLF2&c0E zO*7#f+o174Try=gMT5c^h(`}a{(qNZ&F42*XDvdgxIex)(x{kpR+D-oH*&>O7*;LS zG2taN+Ys|BJ=spKNRj__x{Ye$kv;M4iaI7&#v_6NcF>*5OC>%vwS+m+A$9wQFUS&j z>QHaM3w%9^u*SQcuD4*qI{Iju_m^u|?wCElb&TG_ylaqckTA{ngW6z}!ly+xg8JGM z`wFkfVLIM#{l0(bKkJ%?Hr|J`|8E;T>OKe? zP(+`~)R;9FyqX-3`#Jmc0UQRJ6C|?OF~t@-jE@+@q@KS4WdNym8zj645CDLbfCv7z ztbWfIOuPrz0p$v`!4L#I80|N@vHGRHgF|=n1JC16)hw`_~p4^dP4SW0+iQosN zG&+Fo`B%6xO`}nR)4^sGbAjalG5uH8(4#8+C<4D8Um|-i5|M!9MP6AeI2%+OKUTyWa?t)hQ|NXyqoqs6r#Ge$(?aCz2tVz7Jlj~sttq+_|xan3b;0{SWd{5p` zo?iLH-98{5B#t3SFVBzH7!ZyM0JKqrH}zc1A}Ag?9Rl8)*}E7Y5&|nb7`NoyS-i=u zy2lU4%2YD9gvf(DkQs)@U-2LZG&(#?Zbp6K4Vs3b)Zm`}pkf}~g8LvG!FB2U9I59z z=7H2mWEi+7N6Z!;lo}qM)>mW0*K&cUc7A0WU9o6Skn<0}4i=2-$@tzTUtjIP=gS}y z?h&uBf4(q4u-4BCI=PEet+iRP2@m`BF>5!f5${`SL-tHR-?)mSMB~V>A51SA$+*@R zGDz$j6H{jFcpxrP5jkCpk@vDZ1BC5nUe*r@)HUmip(>snT9b)Y6Iv!z$Kit8-o`v?=}6+^aiu8 z%T2fEf;mjs%z0tfg=>qnT61?jH0m5iYf6-O`)33=q5F1)&q7P3sJ7{PGG5uI-gs=Q zWg-Ra_2kxwEVzEi;L5UMUEGX1*6AW0v8`ZNR*`SM4|#rVn94xe`^}48`iAXLiyYp# zFzn#q$z7+4hPw(`y=ys$MnFEfMDz#dfHCduP|hQe18J0maK2HTWmeU$1(f{5H- zL0&K)(4l;KUHCi-sJI+R3nq4`&N``|VRrxZoKOi&0Rk7~-PvFMg+kCW<=PuI^KDVT zhtn^dn;;rl_IB@V-V4|WsbmfH7ot5EzKqqBq&R+Yq`d+8toK>HHuhHMqlV(#hlir!uyPO+OfswwypRCj_(KN=^rTmQY0+<|fFq)sDpBeM92=-#=%$(|sJiZh15 z6iz}lP4KY^LTN}@0s>x*kF`%u5Fw<`(P%hedr3^2DR0nh9xQ_Cwzn3!qyCN@U-c7! z>zF>Er*BK$-Bkv$@s$o}45o-8JD?ZnIH@N*+u43}58wv3nJB8!{Z!YrFXMc$P zR@i?s(bi~jrnm*H>{hcWg)QoGR8{Is z{fcaeo@mPq0%n1hk*7D6e;t>jO`FypIjK9(0U)-+0?Z?0ITojNlbSjD+qxVik<^$gpF z6~Immq@Me{Pj*}@^C=on>s_{Z9i|USAM9C*4QlP_voLy1s=q?adk zQD*lvp}uEJqh1@$-b(KF7jSbaAscM^K4`HE<^NK@p7+Amvg<2$ynQucn``|^`{BGY z??_VUWK!PL93XmF+$SCITO_-5(MTz65AJ8;eb-2vS`4C=nC3-jk-I9L^ngY()%Vz8 zZDSRTa5jF=b7zp$QL%!_*~ds3X4|0KG|{DeJZ{PG$q`J(MkI$d@nPaX( z^SRRtz`v}pviUd^*W?*8FR#5#|K!}4M&H5fKJC=jN)Q$&r;)h1;^2@vd&}2SaH56D z`khDll;>l8A(QQPtS%f2Rya^DhTe@1I@D1dk33c*D>^!|$ud)jPwJa+JRs0USLzhRtOmTcTxkAnE|4`VIw!mv{_*T04) zGI~Pa7jMoqNuR9*N%R$VJzQ(sRM!`(*qQ?>X5p3z$3*1ocRDlcJ|Ka=D-q^N^QEEd z_2ya)hb*oe-%*D)7d>I~4>ofNta?&-sz{xFNuqkTPZ_1|V&w+=D?3ygMP@>e zyJLFWjO182RWEXd{2Uk}v@tt4Sy>1vqV=o5*7B9io|75$O=l(d}wcGUwP;N#M{F z&+6AcR86griIX_mmTT<#V|E}O&QI{6>UMyOsJ~3Rc8H71+1s&A6^+QgbLz?V(I~ju z`B82euywz#>2q5^CK4Yla>i`r7hL3b=1-7hTf^S+M$YX)#}>w>AbW{-MHwCMbseB`>7v;8C7X-f(n3j8s*b9SCc14m40 zze&}bEhWvL%?CZwe&PkzM}Eyk1aOiSlfCRsvQF?~BlnZ8X@=bn)T4y=W#e9(IGq#LTTCyV+Z$%6YVAyj_&H`3hdLJnj2x8z1i z`{?kalp4iJ`w%s)Va_|i*Ad&5+@}MNy;{4xZo72Vq+Zh+5EgPQ`Krm;bL8%zTBq)Y zHJE;Cl55>Gc)dT_)%%TCgvM)Mx0DoDih99FmqET4qsc=Ar^mbA6k4&9wbq8Fna)!R z7na8?>puiZe{e~a90YCcJeJ7TN$?(tFp(Sf-ZkL7RU`! z9+QyK)WP;UEk&Wi5$GnK7>OoDL2DJ!c7Gugu#yD1ho{q9IYT;owN2Y{vxM6imBEvMR7oP!Js#51-eAU^?gy=_w}21?{E$Z82`FnR_Aq}FZr%la#63wdgD@_ z@Z%e8PjyKHCpX6k>lr2V!fsRDrdG7A#{5SF1u9iOFt(+As{YRW>)E}j6DGb21NQIu1X|1C@{Y^uRc~V7?Lqf&HxEAL zwyA8HD00>6#6G$dO|1CGCG=%IWp9rs2Nal1Bv_~0FsZ6!4f`?KkRRX2Cm#Ve<+!${}N1E~#W;acOheW0V}7OY5ECB^qkwxV`*8#$eTAP2AU} zX5qo6P;l+d*{YUOVjOi0abqKVnf zlDyzRPg)Idt}K#~5IJrqp-R5tzap7@**o{aR*TMDwMD^}iU87xGpBMj{oMS{H02rN zf}Exczmae<|0(n8uL(PhfjqI%X$Y6TR%8-bEE%OgpRFxZvO0?H5WhA1_*Vj+Q^u|L zH6`)c>AX8nkfa2TT2nC#)mAaI)?T}Z%}c@xy>D5se@TQqdR7w7y;RqubsgNrQOl4q z7 z{1IhJ>&2RWlEetZW>@$K(kFJ=$C?Z^)CT{Ah365LTl&9}7FgRBwjpz0MYxT6 zyEg6YdI#p1P)Bf;#zloqEG7K#A>EIk)L@6_kXq%8rYM|UkK`k*A=}r7Dc*U7qhl3@ zo>sE-x%T}pTqoaAiq8X(X~Gm*wsAbQB-8WBknU=NwC7$gzEInT(ANS*TSWQ=E8Jjm zTTGs_BtJlxtNX4Izt*+_-#}u+M3rfRiAyWV?-n=YLD{9FypGd6k?MP;=%;;4nK|2U zOmFJc`R)^m*3Pv>4mp$+w+%*Ee`zgtQ9%#9uH__a_wQ9d z00EF6J{@>%JUm<^?8|tw{`4a9#|M|q^0I=qu{Wbfs0UwnZNRcSZADv3TE23{)5JW-U z<(_R>lhlZ%Co(^ZjC87^+}a^<=%sF`9W%fh=CeQ_X1cu0(W@3@-B|bg9Lb6^3QR(D zxtd0md+idnF0D+nwV+iAK*WV`jNRJ{BK8Bx0&rF#fR*t^?XuUg~lM{WTdIY~};l-Pa zvLk`YBqx2LI8Ullelg(Rc1MpLs`$fF^xImEcHMf`FpH^Up$^ZN_APFH+{WQ!N#?hL zC-nn*p5?kXh5>Lon=?h&&bh{uozjAp~vgQ+S%|8YAv>@|sgkQx@o-ik~RbG>8c4 z@=VQpFUTw@^kF+9Z1BT1|MxnwuKtoj{(5}y8&$W{-vJz>HBaYjgCd#P)TjXm4&_$c>}I7jMOLZ9S?bt;_gd!EuB2jG&|@ye0gAC zJ7DdtVRyg+^rzmR(L3}|NMD|`u?lMQz>ka7T*qT!EI@~VHR2bXvx=pEcDl}gOPu~l zJA=jfNG5wjrw<0-Tfu##FZ9%SejJo?XD8<+D^nc0+`-Bh+L-WrD3|^jkSViz{a3N( z<*FxPG%jHpg<<6gUNl|)xv>%w)qW62(}Q83hXLGpM-C@wrV6Gyx(V&W!s`i+ow@!S zce?`UvfO!=`Yz^1AS6QwwZAWP)4*Dk$g_nsET`C`3$BO|0%{L27GRdorzyY6bX>@? zy+hV_;jjlIp*(cHuJd6o=V)f@xh*=le6WBoToFU|v!0_;qTFpRB_s9LK$LkB7tT^% zeO0H>UiLv|I>UYHKlT=cm4-1f-WqA>poA!9y;|_3$hqFvv zvk3ZMc*1I}oEHT)*PBAQ4$Wfv{o_})_hE9sZesZzN5}%5PI$vd%q>nXLM^`B`4t5> zmg%B)HN6xrA*{VIvS>Jf_hQ&@jmL1}=E%LHX={h5^w=z1;QaT{Nw7t0pmkSY4+4-Q zE~?fm=jE-%=b=WdSPp^7!ECp{G{1+eQb2QN)VUUm4`xnzO5ct1jbRo(`n}l=5%Nj& zqMbckX`TW`*wt_@N?SGo+Z14S`AY5Bn08G}Nd}MS=j!IG% zUKBFIgtJRQf9Lb}r|iYu&Tp&99SK?$4U5S>OD?|M-PWL9cm*(?|Rzi1Q zl;RzxVA7483*=V%K^r-1qu%99YG5g?@1opLvDtstRzUlg4ATBSlNk6SFfz#hIhAN- z8U4l-S{3>S!U+cmqS+1jxiRa4cPdWs6#Ar};UREiiJ0pCVM451Euew36#T5G0975X zamtBF3W<9FL(8W>4w<=1#p`~^JQ(6V%UWtbfXxN)N(IreB|wb};g$yI=9M3Tp}Mc& zj#nrFjp)kZ>bLtZ&`uAfDMTKAB@%io(Ar0{S-hXH?No5D0-8Qv@T}c8nB0#qGUAPo z@*QpZiML4?Qsb+8>S=@K+-xd8>V~gb;X%434DLDH2%RaJm+Uk`d=uF|728j^|7z>r z>n>eltQ%{3Bklp6F&lvgY%8c&h^K>p>UKs~btc`J^ z@$1dL%BOuj4yy-rQBZDUnaW?+n2$AYV|n70=>{t2KPL#MX(bs}adqb_d)Xv-F_XV% zvPU60cp%ytyTp_cMko7hwSKHHbTy%y-TlMA5BI~?u1or-P&CrC2R~tdZ+7;|G_z-5 zXF(^lr)xV&I|7($8PuQDyl2C6d|MVM2zkxmR2QzWCy0PB9H47N%*)0*X@6v3~?I&!73-lXz{je?#V0+?M3k*f1jGQB6}{kQHl=kYqA>bD)j9!zU(7NQu)~r>Mw_& z9=dDRU1M2pM*)`K-t%=*sHgr`xr%S{YI2+j723tEEY9msQ* zr|2Yiwj5hUu-Ve2*FI|z0WuCRY-lWcQ33OVHHLd${mHVDAWoQ zDIALp`5oW`YW#F}lpkpQ7!KQzK$P%3 z8gJAD0?NSd=-^sG`!Sktka3Y$6rSkfKI3@1cEGHzk+A! zzO^;ksn1n5PU@{u?2WJl!~K}I4~O1Uwmh0TVKudlD`O7yL}{DI7~NUg#;9Ou?E(RK z^jGuwNw(QBd|GIc5C{d}Vg1+4Mwh(c`Q!*Do5G2Tm32m(2N z@!Upx!rg@pA_LuOZ7g|bBxnWo;JKdB( z^CU8g9zu$&PrjT^)R1Je1kBWPHl^3I?bZ7znKgAi;kMgkDp?Oy*XxaE%XCaNVqXQJ z0r}yw(Yk7JQ%!%086;t!26vXQzsQUQ_IuQe_h2eP=_h_9uMpNqqVLdoE7v5+CJ2bG z7MnK+5Ztw%bq)G!rI=JLXs?{-L;(Rciq*S+Fhchsi$>{_6r8nIio?i`SvdE-m*EUA+li0Qu7F^# z`qXU7FJj2WbiVhqsfv)YrgvJ`2T-1x|K$}@M3UOt>=t(MwSGe~=m+gAe|5u4ue{%J$0S^i;yGH4uDZ~58=q|EAAPucj`bx(NA zQ|MLq2Ps9LgNCBy59-$WrzjP!P1!r!C^6M)9FtvR-{POqubf&Z9HVz%MsyYx?=RSx{ZT3BxvS>^`Cs9b z&gZU@O1NSh_1-dBdkonQJQ9>3N879e-%06_?@BjxzQl?Cg2$N3G~8Qi{Y4(QA@m6> z{qPjTJrNQ4^;nR+cw>lvLd98iHaXdKpA>9=Rtkrfqsq)jEPoByI zE$a_U5M9yLyd+QOyd8~K=_XAyqmI#WuT^*3_hGWe)hpz^vFmfP-^G|3aS)CFgQYRK z9&ms0&v-2={)EQSR~EU0O182yCveKss79ss<#m3xY|&g#9rsL_-PKy4mDyOPn6mrc zoUo(Rww0&`LI?=$d@*KGHZ}w5snl4rdjUBWB?6%1$9!P&VhrIGwrdNr^u@QZaG#)ggk+k~2$tjiBdw2)E_PYPLa*YlYyFfPsMJ#e z4YfuRP9>^Nf)$y$$7D{q1a3#**VAb)3dAk#s;4LxQLQ!dsFrwj)Cgs^_|nVVApy4p zFD6vYkNkQH_f-3ml<+sNIt7YYe-l?yEiSq}C?8*Lxgod>!Xglx7ex6grGHdIEpvE7 z!bFKoxM~NJ1%jV0Z-%jMzY{!_(0B0f&va@qPc~CwVLfwyj}zWb^H{k@6J}0=*=7NE zNHYvpC+sZvO`!cukfZ)kdzYTU@BD-B^G6=GVcRm7lQqpF!$HuBtkqHT4}ZK?0@NPC zb_l!%2P5$Qs#4O)DuY_MLdrpFRErWy?mhFu*P!{pk4hHdINu{%5a@Jazt(B#Fu`Z= z(t%Kw{TiuF@AAC`0Ts71Z_pR6E#$u$VCK#E&c|USLkMOx0Yb0BkVyz;kSA8I2s6+b zF4AT8NFHeqqB5CgHE-a}Uw#@AZ_Iu+;+>&X7lNz7Jw4|Co@q0Q{pod+uY!&|U{ThO z@bp^yQY-ZBV-Q$@P`Ll`o2H+-fz(*gSJTJXO_SAo4y}3YRFw1}wxe6EO-${-cKEEp ztvo!40-RT{qw~R>xtzl2M{9Znkms(_Teo_5*Iwm;Lhl|3?&VOy);p|rjBy#ldJ{qM z!?H)<^yxu9f0;@65->*wL!*HJr2i{UWCHJKk?ldR&T;A-1doz2Gd5` z5*H%x-H9EGFcK{`0FwT!jp~mMXlm*qwRhf$s$UD>AgnW4ZJm628$0|Tn-#TgGO@C2 ziA9Gu!-^+(oLo%e=wayaQLo|Ubp^C2NNLNj1-HPGrniY}Xu&ZH4+8!g9ZGYk zKl}hMYLFC9(8LxEqQmjX?||)>f9jDts@Jv&pbBhT4xrCaeAbhr!vreP&K_$|l4XJy zB{}}Zha(wj+3jA0yxWlNlv5}5rcx||LV+EmxJ-q?WX84<#LMUPpFo6X(_n;jkmoB( z#MTu;1%-b129>T?EkU`>*5VT^hrmUmm1}&u>soDfT_0#LHtf}E+Ne^(&y zkimp|+QAZS6oJM&EIJ*uXwVkoMSsHS7_8B&;;0z(5bnimn)mK^{CDWhC=sCB~Bm^=-7#G&^ z9IbDXi|;|2nQe7IHJziD{gc<>I)+M(8(mSME?$+46gRurbVXXcZ!jTI$i z(?AS^*e~ESt02r#0V!A*SA!T|Lb>@wrxJr_avQ(MJ%b^ke>dO$rkiayn%`hKjJSgO z-Zqf~Sds4c;xmxG$4Wid*jcV%P3U)yx5ywkz{{IF-hEBV!1WC{qU)zJlG44zvXHGgXfpu&}2qKlhyWkA-mTVGQr3-ed}9o$m4lBT8b z>X}dFLBt?drc6)wEdpp#YkJ#!b&x%&I;C9tAit5hj8Et;T$S&(+E+KWn{y{>^zZ`E zq+%uIVF9I^HRh#e0|sgA8M;){LvBl@9nJ&TtNwhymot;BC9T6*tk9&n~9lu_2 zX*y{fcSr|bhD6!~3=P;n4}2pE`Ulub1j1O=a`fZ}uJG)bvlx@>kmxv-OcfB^j{g5) zsaQZw9SDwat@j`+enJ9iT^vhFD5cQgAyU2zzP-8PkzSBZf?ksgPt1S|^MJFykou#w z7er7+5Y48Rp)B9a1F^k4z4h*{&+}{Y+6}SK?!nONVrj)SEC9uzW7@nFa0G+9+ybA( z(Q})9IRq{3<5QD=#BWHjMqtuptF)XfenZf3`&rX(*=jca7pioswCi*<#3hW!&%W9Ya-r(8*fc zug}<(IVn(AxZgkVym@_HCu)t;y&=VsghK)bt&?fqv&Sm2D3Tjb<{D%Z zTvJ#2+AI*ZQZU|^dITZ=jO|8l(FI%qWlBfxHglLu-2o+9v7Bq<*Z(qORY81Bp8El= zcgh!Eh4vZ7gTS?G(A6u0bK}!(5a^>yp3Ot?RB=V-EhF4k(O6(E>&ZteZ-^rrEelmA zx+<*Egy#g{t{+0yB?@HrgF;*T9{DytV28T7JZt!+VarLrPOB~-2npbTyOQ_xg~ulo zB`se@vO=0&RY28LwU7}a@9FF;6ny?mq|hY%LOa&~q{=H|H_Y%H_5u|I!M6nG|f^F}Ml!?i2^SL`TAKFxv=@ z%KH{TK0w)X5Flr0j* z2ZSP6bT6Uf>=(Wag3#d~Ev_d~hXN{E%feg*es1ffq}=;)krv@9EE$x6BBZ~b{tssH z$&o4y$Vut*rd2Iz2-ebk=OnzCuYSiJQ_{ScOK{^6rk* zd1C7|UYSWm3Z(|-HU&L6w0p!PX+G9qRL*jpuisHT zuRohg4YRrM$dZs<=Cc{QF0Z-=I zvmZeu;QSB>rTv5|{xcXsF8T7#^DAm*$*iFy*@@Hs{SX-~15M3yU+d3?PdugJ1Xk{r zzM<4Ym5AifU40AwKyPm`gOw>7YH1uGystAvmZw+a8=v!On&7_$=yV5Yg+2Uu<6!+0 zx5O?*r}|Ll)Bkn{pfiXZ1rM_1p?CKr^m=#GcpxC^c#1M}?x9==3mgRTUj1NE&^y`! zHEj3i@l{)8Ce!dh6t;~_S;|mjF&+c~+64d-4=2gb`#4cC90{VziAQaQ2g`PGc8`=U zJ3s^xSNo;XrtcZ#e0A=yi7P+*2y-xUpJ|_>yN?tsgnu)-Sg%gR7DN8qDhZ4fGczSB zh5QbcEC>~Li%|Z`H@}?KpZ!TNz6fjK8I3hR`%&mJ1O=1eM4TmaWk500e@}hM#%t28 zR%6^?x-?u*MtG}S&d@)`|L8AXld0Q(Gh_>`C0ti;H0m3*xO){A|3kz$mg1oW{hqiI zO3Sg1$~e}=`TR#&rNk!8NplB7kVt%VYT6=C%L63gzpw3rxsUlOiQ4QvExRQ3>0Njo ztS%bAv&Qx02hG$enX(@T&)rW9a&RaVeiq0gA}o)ps30Hc-0|-Hv~L2d%**E zB6Mm%c)LZVLWwoRhvM4`mhV3v7qB=EY*SkGyVtgUXuZE#_hbt5^1~NVt_d>Ubf9st zf#-nt+CTh(;~Mv~pQCGSMfj1M&Q<4IvA6aHPVINzl3>0M>dPK3rW=7M(&{$c(aa;( zA5{O*7m9M+CycfHV(0#O2haEH%#Fs!@^~eFZ`h`*xA5}lwW-dc-wcPS+f=5dnp=b^ zLpE`m@9wSS}K`3`;jG{pO(#RQg{YfavjQoZZl5>%lOALsi*cg|Gjd z=0^$Xvq3+hp4GIw+X*C3e+@>s8nyZn0Ui98N;3Lh5!b+Z%YU9BFB;HJCLR$vvWsS+ zT^V)$aK2Yb+?@tAjx<>GU(JABEep6L{~6T~9z1yD6Z~CFNh19X&G|T&w*6}BZB^M( z8KD2!!YK*Q$u<$4`+xdWOc+o|a6M~mrD6R|_>uuu83t^e9S$(lzW!H}z=@>=A(fA= zd0k37)VcL1QV3lT^kR5p5sN;6SI9 zmPITd4BE(NfA@bBoraG){>CpW{o%m?PN2;b4H{k3EyYWpcZm(%PI=9B{evVp-5EaY zYWNBN;L`%;AX4+H$kLmh_aj$c2w}R73DPZoM>+HUK4$}Dzt>f{V^gfvXxo1?qa2(V z(EhZYdh|!85&iZf#Xl&Q;oDuSBkTkvAg<8|_j8PlQo7}}F|zmr&@efu|7|`Lqo6 zspMZ+LNUdq^9z&qxeR`~DRU;xzk8oJFk6|(0L>M4pQbW`$7V%()jZ_eK{5}d;#7|m zm;w}88@~V?Q!ris2fQ%!j=54+*roMqE4d`9KyM*JW-lUruCYNAwo_sMghVt`wB0v` z@+K8H7!ySkaL?vi-_1Fm)k+Qs)VSjckg=W2u%AD(hn;xoD-tGEOnC!;f6J6~&wmeD z+OxBe7TWVxvK6~|gb=p<%hdl+uv9^k*{>#!TlYv~DyWDYNFI2m+I-{a;2HsPIv&Ut zto_wppZun*Z^xX5FYm@O<+o2P-J3o=KRVUgQyNgRZ9z9_w)k z2q`#8MHmRoofSxFw4b3&T$}TA{=w^knN&71Q!fjR**rQCKoiYmKIf6$tQdZbEAH4b6nJkc?q3~zI_+vRN;3b%Y zAob5Z*>hW>hI=Hn(CHC1xYB$ZqXh{}-aPiB-isoK2RH1$U>U;2Z9_|WX z{|YSE=PwEf-iX!QYb`gSK}T$$me(4-L_R39u&D_fm|v`&CWBqOn&pHnCjzE>u~Mx^ zf{4Of>GS%VEUi$C|3gn7RJ4ihbGgsQk2aZR&Gc-NFfV1~+VVLy+5Vl%;oFIwJyW`= zd=C@;RG~@nJ2#d$EUO7GWDv~#&^whFEN zws{Q%iZMKU@n!yvT|K^P4+G${ezF1`{2vxzqV&K-D=YEG2Fa{~0``7khwoeAh!}ox zcItJ{{o|KR4LuS1}@A$*1^bBgf zkUYH_{&kJo;?VG=P5cRA-TFV7U9`18&tdu(lH&8tLLRWx#+*==FJz;etB8#cTP$_n z5l+t?l_K+U@ou)~kK8vVjCF%;dA)U^LIP*|k4yhxD0&vpy#mTPVS{y~v`}=s`|+RH zl2`AoiVm0Y7Z87XJ3(f=8|>FxkPzvynrT|QNUap6C*A4GiOrG8Q$W6IHA_h>9 z0@nkHJm+Gsxiud+bgrK6gL554gA zspx)!leNxA*@L5kpF^lMAaIDX!}?r|oPp9%MutM!`23t70c89NUXSYQbB_%mGsTUw z08)YLyi}%Z4&bRTNE1zy0O+$x-w@gvI##G|@OlOhWDA_wqH<@&EhaUFRM^|Dj?f2x z0P{FcS(+Rce|!YTr2l&xFBdvK`WwowGH%M)o;LT^;jgxbBc$kK%yi=dy&2UtaMDXt zj+~Znh@x>(rc(h-go}9ogCZMzOJc;tP%fQ|1OU->di8zz3S@MmU$(!-6J;^hY6`U5 zvSx^saNWIzFA2y@HQyiA_jp1xqTgfjisXci7YkU{hvE!iu3yPHF479SJT)r{V%~vp zmVDgSnJb-78n^a(opCRf@3DM0W8poaB;f|VSDMjH!7wHe`*JyWX~eJw82e~^)q8P9 zF4p&|?Le{rhV7+Nf4H)S*U(@@m%{rOO)-51gBS|FhlaW_-yG_cDslnK`-$Xj*Ka;O zZ11GKW73@2iMM?zhfXp5Aa&@ipgAQCgR9TW=! zLlt*O#u|cLf1uuM=4`F4$2?%kj~on$&Ro9JHGI0?=bU+#bel)I!+IhKSO2!|_k2zj zkJES>a1@9WR3mbZLrmQjUepjxc~%tZ5M-?r{5nze{0BF7ygsk|4zs;S70}TJ2{Z%dqpn91k{wo@ol6>4QZ8Kg}NUAv$o*`5cS0n*)Iv z$1L4x98B`S^mnvBPOP|4LBc@bixX2(gorWqe9Zmb(uVFIVO@`t@Jwpium*rfUB=7TE09H5l>MZ(#Hb(8!H;%>0fszu{^s>qtJW#~z@YdhT-JPn{L{eyaa$ z7HvyES}I`Bva(}iOg8!dGAlScb}O#@60*p7ke!Igf%H-c8%LmI4=Z&%Yua{ zsO%LJ8S;V|wRSwh!Xf*LiI3iKD3PZSJtGLkXKkUm1_`C7XW6k}FHVvYoZ~fJ#gc#~ zD6&~k#_q_dPBQoBKnU8v6TS|E%ipM#ExE17+K+o~f-fFMgKdUf1_Ho@i#To4Dyt-8 zWU&0eDLp<)N6dXyYkKD|&Jqi``p&wf@w*Av)})^%lwqsGKA*nY{rIIUcS<-(osYZD zKHn42B@KPR>aVSQ(d9;z&b~s&{SO}#cUMaTdLN}D4nGay=$CFDHEn-|&S!?(=$1x> zs=?bl5D)dkRk>7bi1!n2PE6`^Xcq4OX5J-f<8a3a%)Iet0I*KtNH-W!RmXBJf@g2A zT4*;aJrt=U%DPB2$|+0MOUKWJmyLF8%Qa>tIAPp)Gr4RH#Y_+R=qCEKi^}JaW$vq; z>C%1ha~$Q_g@)2`dxaNzC)R(DU8T_#KTKZr?)&Qry4-|DHekHWS*J?GJ6p)q}M#aqrS^w2n0;boVsKMkjhl!+!Ev+ z-X*KfJw7+H$d>tSS@3~#Wc+X1uR9)9^9<+%O51q-X)F5e&cKzTw|93hsvT2FZE@gy z!N9--Fx1}VTj9Cz4TkYP)p}(v1F(UyFP*&AvDp2l+NIC-O1wG~8FhTa=Hu;O&Zb=6 z@1Ob%J+352XFRiS(TK;aXb&n>dA8<<+iY+cY?&fT+23nn&VClzot}X&M14c{t)##a zSQTSph4YkEnxP)-sWFZ!b~g1{ENzj3?GHL%#XfWOV7{JXMdLg9nzuI@fR2!>235Qg z3TS=(nIB~|&Y0}FVJZ(4*BCi)NcQj)hpV@idgLzBNzP@_%uWnVj3B*HLkwD*Yvy4)08qKwp;u<4cl|DwLED1F-se1>$q2569}~)>vLP zVYl2MjTXLAiXZt|BM@K^@W*q)Di5Aq0q`_I`t+Y*XWfT%%iUEs+7q1mU(8W1uX63EN)rN zjUFYu(tlk)*qCH)B1ABLnLPbP@J-23I0OfqQypVa8D6l{3!$z)r$jmg1`;(t2b0(< zL+;LeN&b^d{vbK;%7o7w)B5M&bowTD6a7R_*q2Wls*k-&$ldMmhV`ui$Zff|;pX>c zNg>Oz{DGv;mO4e+O#-s@lBj(HfO@bPKULsVhbg7za(f<@C2lVR;VX>cu1LZgbZ^@36HbF{XhHSWeqAiRcMe0+eu>U&! z>5*y#yJZ6QyBPm6O#k-Bd?Y}elzp6mzb0tG33vnl>h!LPE2i_r(#$Xriekr9= z8YLo|?v`3-e8;QMnKGp6Cs_`&k0D_oQ}`FLl@OIHKFpOx7CTJQ(038r{*&zGX*#%2 znsXmObvV~vE+W<2^a2>z&F69c+iJP-*&K23xfy9@aR3X-%nd>gX69>5Ff2JhyPZ^hHzH(36|`DO<)t60fowE}CMuS{ zGC(N2;f_`b(>FarFjRnkPL358?swAVcjkW)nkh;4@^Q3M7D!BQ)?e)KG&!Qyo``o` z7Cp686z$R0fpTavMq+G}O8(fhSR+<|K?StLs*9z4@tg(a;QsDIR}t+6g_iZ_>9#QA zf~O_QrmLlXA}Jdh0v-#5|5?1WhoC>1UJ7__l>KIrDVrqw>Wx5v0#Hg^%QN!YUrQ@g zVSHme$Ns9=kEvzie4MU&`imWw`J3qf^W>ny4yYIh+0kK1~t-fkM0Z!eKht8TZ}7KYgx0pKwxwRV$nb zu$V*?G_NIZDE%_ts;h``a7bQ>#IAQ~w)c7!qxQ1t;1iko=qpm50-Dqn;M63-%z3p< z0FwsT6+D*D7gtH@pycYN(^c1^pj6$Pbj_pmBemY98!P2sMd6?Y30m+4&a?o(73>kd z(7|TnYJsOJxxb4cz7x1#I?w!vc_D{M=;qkJSoU))?W^vYD|97}p;MDy9S&UOv0YCe zYkxkz8*LKLBng7n8|x5Y!YynfiE^@L>kQ+qY&d#lk?i$j1;=fVdxZEJAmn~(3Ef#l z8kr|1@MQrFye_JAV0V+vAJKk!?lfN!s-;Fd6ox%@Yz$?SBrr1rUn~IfI@3`6D~rg? zJb(RHxMRhKfp$Lmj`{Z+#Uq0LUqex`_atk)9R71%&^>C^b|mQZwghX<`MciYmdU7< z$v4p{vV@|Fi{>XB^Hl`urGytzJskI@Ur0gi4b2q=j_b1HMWhjwe#1^o#x2yyFW3-L zikDcTf*%HBKD@2l+)|syKvv=7&{!vZ(H~m;UyI#^F?ZXoxrEMIAqFoN3U~-+WX22d zGJPv*QXS>AoSp^e!)C7Xe|50lpOxSL;J3E*^ZZb;=<=6bE-(zV38KGM7i(0-3*UbF zUW3hWSs$2E^l|%%N!YC+o74R7mkSzQzmvO&f11iX@11Q5J6&q5CB#F~ zgPrgIBLKKMV-P5RWkZZswkEqBWqL&c+8@!m15&DInVo!IxQmhyDP<&9>}ZUDRuB2A zle!Lin0~j$$=~m9v__VJ+<)LZML7(s?V4&tY>jkLxTe%ancX8!D6akp6j6_-20807 zT&N&R-BcwZv4g)tNY@}q#5MppTL|urVaa6n3og56?F5J5$2VFV!Q zw?!o!glWy`XBEz4k-cB*`D7{B{U+J4!K}Fau++Y{HnB@stbFP0(aoBV!y8^Q^zWt- ztJzgL&~whxNvjt?UMQ*mO^msXuhmOBDXCJW46=*j~r?byZWN#R5!?Q3yk!?9=Q|r85d?? zxJLMA9hXss^4=3yhelENt-J6~v<4~Ts;NbRkt``rD3!J06fsLc)@)AzGxPP~v9z3! zs65Jd+tKHGN5ILIdeMMeyl7}mYOXb9SmZF&Smg8CG%dsUqdVm{6G{*^MQ+^eUhEEb zHIw(@kki+ccKy~bo+*@%y+F@wHj3_}IHCn(3bP6;<>m_$W*C|HK1%{1)Cz9rgeY&e zrWym<)Qr-znTz4H$s~PcA~AWtJltv&h;fk*!S|ZrH&3MA1l1l56@Ko`5XdU?Im&Fh zwIqMR_}vI#61`Jl(vFIkyIuOJR~V?%bF+~10fhMebqDSzDGdhwmx`!0kJ>x3b~7yY z!nC%{yiZ8}JGZf9K*TY!DjiBkXr)Y9tOP))P%Sum;6~&3PPQO&!GpOtJPZWmT062W z;c7y6{xnQsaSF+BS`LAlXe2!_10oe_}(fixD`#Z~%l5{9#=+ zFn!CIw~4P^pFY+3vSMoZlO?^9G|>>Ch=w-dR&;}}e;W$N&40nrL5xlu>a;|wiZnT_ zb)ufGv?<44AK-OoFgs8rS+?6IN+pCh`~iB99=~vE7P(Ud^s;)+wAwK;HcMJTMj!0K z_i9vn`{(5*9j!2%CTq6MhrGQv)20oNur6sOp2BT3pN3NIJV-LJ{9{(D-k+NiY&$|sWQwaTq+@^?ndQC|NaGezX`c% zD4hAUvV=}vUKau50W3eMzlU-uzG;^3E?KKKmC-^EO0Oa|l#Tc>tLSyqUa#+-+;dj? zf}CHD{~Wc|JTqP&V5)T7nJA8nfV$V}qQikum6UWTpD7su$#kv@ak!8|ql}VahZBwo z1FPKe;cixEIxw>xYWqgZDoEZrh8ZS;<|r7?Qi|$neN6H8;rXP~S|=^Wsaonn^^5FB!EDE& zz#*pCOc{6wy!IuzPYf+BeS;oH!sAetgyDf z8wSWp$o9049m$^8FQ11$gC2DaX7PL*Hh(aLAarB%a7q37ug1H{pK7e(TxZJoeUg6( zq9sv_g(l7ehLPz@a6yge3M%sF-jukLHACe#Z*de~?ySUITDiKQDU~#|n$DN(%#TF> z^lGgSxPEzkW&U_eMdg)VUkBetvr|WBd4IiMWH$yQ`VQbLE=H zd+Y^_WdAN1cf3u&W#X!b&pcSre&ARp@P-`00u$Mh1-d2{Tm7|-YBpqEV z*yIc~CBk4%w>4#acc*zY&6#+kZUgluAYIghin6ZR^zexv1K{Y(z|mt>KlE7uVO|L- zn^w}vEsW(1y^!rWn*jEKf@$Z+TZ4niEN`Uaw(vM-=w_Emgw|iDdsl% zWu|^f0$D|dh+&kDBVG>9FZ&sR%kZPAi6@Od*S@F^*d{s}bL**4OcYg3GLm8#kt=zD zjz%AW9Afj@fKN*^yi9pQUu$fmO<&8ieQkH?t!TE>D{7553QxkJ-b#82k_QBEbS9qi ztZ7e8KWitB-$^Z&S5YdAIC1VpS-+s#ERrOIS|eTINf?CMe+`K7JZg*I09h3QNvp^8 za`ayYIe~^^D@;>5RHM3>Pcm(GvUt1!|`D)Zc&pNyE&a|N3-QY0#P}8W{AS}{pj6yzM1>q zdm|XI(A3YS9bzAjQZqTzr=6d$F<|a{YcVfP2t2(-Edz0m-(ObbxRCmr`o1|%ME!Jr zdE|zx>cWy{%Z!PCdT;ZXqX4;D!r_K}>Se5*EDfhLxJttAdns#HK6fH>Ssoxipu*8j zWdtO5a}3t&f)VLx!eX=;*=39doh6;Q7gRBAAg@KRZ$z8VG1HPc_gnu~z5P=rJmd~C zc-%w`$91(?HHGN;p~Y65aj}Bww(RI$(A4eGZ|?;FLorWhlT6~^aI*TItO%pZJaJ0? zuhd-j7gl+FI~Ig&YFN*1@4b9fRl+f!NLyD&F>6W9{EszQpMMzb{r9p+z&2#S1f&K< zWX4-tFU`1)0-vM6^ozIJiXGy#w`aU11>6mKd)&0s=`gnEIFwh4kERyBIhD!rI7~TL z_sxz7jmQJ(7PIDgD|Q!^t+T!GVdU)}+mFlMYKvK0#pi0ykVt;=B?T0#`kJAWvV$+{ zFtg6ZwT940W(cYNeh^TLJXKp;1;j_z*na5?bv;*-pzyO0Wvd4g_IP3A`ZD`AFe+q|gO)T&!!U3poUfLNj z#4qi1nCQAC1`RY`2!0lSFhJ?qTgPLhbUoE9)y>%UfFTC{=3*DKedWOviKWQ~h|{pX zkq|9sJ=zq$T@y? zkVHTI@$JAeveL@$mRzA}`$yiwo&YE`PvW1U=}(tmplq6ik{#KeT^pc+F8v2JP~#4q zeBo@PAfpE6oXPRBP%tkP`YEk`C&;Egsz4g7A3X3`@WZu_M(O0EY$_}+A6TF)TTv{j zS{ORi&vvczWXO$9BM7V_0_zw2l@a94Xk zR+2wZZyWc*4cGO?>*vlt2RSyHzx+=GWG~exxW$~c7Z?TGY6N#0Qt0|K*e&|5)>&GQqY`4FF>|+Y)1^4peQLAA>mwwF?`%DjxAWmI0#v$`(Zq{(V0D}W z2(!!%c3(I`N;J0CCk_AgP~Y4zn@f-a-g&-ZdnbMu?XA;&L}=9`6ImYbdgFwsG``Vq zIl{n?vq^4~JQ#8w=q<+PiAQm%Bwx~%9hUFOLzPYO8FU!4mS@fMn6Q?akKb*;C1tQd;oPnEM<~&iNmWd?i#QyvYB7(vhfAe$x}Esa7vHe( z)`M2wsrdipF8L$kDx>(_AT7yfpz%oJNc{5Q)*kq$-x6P*9v2I+FgFf{S|-xX7MwK* z^9-Z8jVOW&@=9@h(hB1EAsmT|d17?EL=PSw2KpUcc2#8=MStEr`Iqu4IbYH>BnI}vt(&EG?aR}nT8Wd>1Bh_`Z zxu8$775f&dSyTX5bkg1J57#Ej{d;Hp!wC?LBXNtf@h& z3T)7xu~YSYnwJsT#^Y&;Wkiv!UGu{J-I2GRJjaGX)oGSZ_Kd%xA@R!}^=Q4q785;g z4xT$$kgj{dE%Uf3t(!JQ2HKg%>JYV@$yv(+gvceA`4O))?REbENGX0x)_dbB%pi66<;TVwIA7Z*s=b#EJrWKsZ2qj*_eXk|VYw1Z z3nx0(>#{&At431O@ykt0$N_9#;fnQOC$XQZ^hQTXw+AFtkd-eo*!V63SC918sY%*@ zk-Uu61ILgT$y00e@9aM1H{=?71};Wnjng?iv8Xez{NnCkkK@UJZMWuAi(>QHwz0ff%9C?i5^>k+G?fvHXk!O_`(+N$ew?7keD8UB|G(EYZx37_f<%JrQ`lKHZz5cBbFJ%_n3pzFS10H1Vd&}FOG3}ljfE@PafE` zJNMY}3`WWu-rkf*MQ-Kz{P`+ri~DE8$q^oX=G$toFek zN?zu9wnHF(A{9(fX1{UtXS0BG_fT>Y7}iTTCuKck=3-S+av@A?#Jb3AJ3JtE4712# z-NO-HJmZgLpAvssCh`3@QF(+Rc)@BZM+;1o)f7IvK3_WPA!xRi14*tgmm{OMXqZ5L(DFT3#ktOVJO%XT=?TFexCNb0M}f zpe>f?dw!fO+2=;8C0q;v#eVU=a9w-I;&U{cGPq_TC*|vyA(w{qN^GU;Z zVc&*r?Z*_uDIXvhn)V@2nr40%?11Z6w+}T%R~$ZchmE2fM_IX0mC#Or*RM~FAf+}2 z3C%8rKJw)qkdQ%oIOjwGcA~N_VzsJ%kRDfVcH_DykS-nikd3XwXi&Ul6|%R}nCoU@l2%*%CaYe+jRjeI5J z;7r*#M(a09DrV9FnfMDKN^};F_DMAi)g$rNZvM)R-ijQ1)%E@lj1d341Z+pO%tw5S z#vj})?L9a7LjrklFr~HQ0{dfgjGH#Z8F&=$2EkY;zj<#uO7Fu-jg^s6rIoQ*X)KCN kC)Jgl7IyDt1M(=32|CI*DI7{v>LBo^D2GCp%9sWI59Y;SdH?_b literal 0 HcmV?d00001 diff --git a/pkg/webui/ui/public/manifest.json b/pkg/webui/ui/public/manifest.json new file mode 100644 index 000000000..080d6c77a --- /dev/null +++ b/pkg/webui/ui/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/pkg/webui/ui/public/robots.txt b/pkg/webui/ui/public/robots.txt new file mode 100644 index 000000000..e9e57dc4d --- /dev/null +++ b/pkg/webui/ui/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/pkg/webui/ui/public/staticbuild.js b/pkg/webui/ui/public/staticbuild.js new file mode 100644 index 000000000..58271d740 --- /dev/null +++ b/pkg/webui/ui/public/staticbuild.js @@ -0,0 +1,2 @@ +let isStaticBuild = false; +const staticResults = new Map() diff --git a/pkg/webui/ui/src/api.tsx b/pkg/webui/ui/src/api.tsx new file mode 100644 index 000000000..173f2a42d --- /dev/null +++ b/pkg/webui/ui/src/api.tsx @@ -0,0 +1,251 @@ +import { + CommandResult, + CommandResultSummary, + ObjectRef, + ProjectKey, + ProjectSummary, + ResultObject, + ShortName, + TargetKey +} from "./models"; +import _ from "lodash"; +import React from "react"; +import { Box, Typography } from "@mui/material"; +import Tooltip from "@mui/material/Tooltip"; +import "./staticbuild.d.ts" +import { loadScript } from "./loadscript"; + +const apiUrl = "/api" +const staticPath = "./staticdata" + +export enum ObjectType { + Rendered = "rendered", + Remote = "remote", + Applied = "applied", +} + +export interface Api { + getShortNames(): Promise + listProjects(): Promise + listResults(filterProject?: string, filterSubDir?: string): Promise + getResult(resultId: string): Promise + getResultObject(resultId: string, ref: ObjectRef, objectType: string): Promise + validateNow(project: ProjectKey, target: TargetKey): Promise +} + +class RealApi implements Api { + async getShortNames(): Promise { + let url = `${apiUrl}/getShortNames` + return fetch(url) + .then(handleErrors) + .then((response) => response.json()); + } + + async listProjects(): Promise { + let url = `${apiUrl}/listProjects` + return fetch(url) + .then(handleErrors) + .then((response) => response.json()); + } + + async listResults(filterProject?: string, filterSubDir?: string): Promise { + let url = `${apiUrl}/listResults` + const params = new URLSearchParams() + if (filterProject) { + params.set("filterProject", filterProject) + } + if (filterSubDir) { + params.set("filterSubDir", filterSubDir) + } + url += "?" + params.toString() + return fetch(url) + .then(handleErrors) + .then((response) => response.json()); + } + + async getResult(resultId: string) { + let url = `${apiUrl}/getResult?resultId=${resultId}` + return fetch(url) + .then(handleErrors) + .then(response => response.text()) + .then(json => { + return new CommandResult(json) + }); + } + + async getResultObject(resultId: string, ref: ObjectRef, objectType: string) { + let url = `${apiUrl}/getResultObject?resultId=${resultId}&${buildRefParams(ref)}&objectType=${objectType}` + return fetch(url) + .then(handleErrors) + .then(response => response.json()); + } + + async validateNow(project: ProjectKey, target: TargetKey) { + const key = { + "project": project, + "target": target, + } + + let url = `${apiUrl}/validateNow` + return fetch(url, { + method: "POST", + body: JSON.stringify(key), + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + }).then(handleErrors); + } +} + +class StaticApi implements Api { + async getShortNames(): Promise { + await loadScript(staticPath + "/shortnames.js") + return staticShortNames + } + async listProjects(): Promise { + await loadScript(staticPath + "/projects.js") + return staticProjects + } + async listResults(filterProject?: string, filterSubDir?: string): Promise { + await loadScript(staticPath + "/summaries.js") + return staticSummaries.filter(s => { + if (filterProject && filterProject != s.project.normalizedGitUrl) { + return false + } + if (filterSubDir && filterSubDir != s.project.subDir) { + return false + } + return true + }) + } + async getResult(resultId: string): Promise { + await loadScript(staticPath + `/result-${resultId}.js`) + return staticResults.get(resultId) + } + async getResultObject(resultId: string, ref: ObjectRef, objectType: string): Promise { + const result = await this.getResult(resultId) + const object = result.objects?.find(x => _.isEqual(x.ref, ref)) + if (!object) { + throw new Error("object not found") + } + switch (objectType) { + case ObjectType.Rendered: + return object.rendered + case ObjectType.Remote: + return object.remote + case ObjectType.Applied: + return object.applied + default: + throw new Error("unknown object type " + objectType) + } + } + validateNow(project: ProjectKey, target: TargetKey): Promise { + throw new Error("not implemented") + } +} + +export let api: Api +if (isStaticBuild) { + api = new StaticApi() +} else { + api = new RealApi() +} + +function handleErrors(response: Response) { + if (!response.ok) { + throw Error(response.statusText) + } + return response +} + +function buildRefParams(ref: ObjectRef): string { + const params = new URLSearchParams() + params.set("kind", ref.kind) + params.set("name", ref.name) + if (ref.group) { + params.set("group", ref.group) + } + if (ref.version) { + params.set("version", ref.version) + } + if (ref.namespace) { + params.set("namespace", ref.namespace) + } + return params.toString() +} + +export function buildRefString(ref: ObjectRef): string { + if (ref.namespace) { + return `${ref.namespace}/${ref.kind}/${ref.name}` + } else { + if (ref.name) { + return `${ref.kind}/${ref.name}` + } else { + return ref.kind + } + } +} + +export function buildRefKindElement(ref: ObjectRef, element?: React.ReactElement): React.ReactElement { + const tooltip = + ApiVersion: {[ref.group, ref.version].filter(x => x).join("/")}
    + Kind: {ref.kind} +
    + return + {element ? element : {ref.kind}} + +} + +export function buildObjectRefFromObject(obj: any): ObjectRef { + const apiVersion: string = obj.apiVersion + const s = apiVersion.split("/", 2) + let ref = new ObjectRef() + if (s.length === 1) { + ref.version = s[0] + } else { + ref.group = s[0] + ref.version = s[1] + } + ref.kind = obj.kind + ref.namespace = obj.metadata.namespace + ref.name = obj.metadata.name + return ref +} + +export function findObjectByRef(l: ResultObject[] | undefined, ref: ObjectRef, filter?: (o: ResultObject) => boolean): ResultObject | undefined { + return l?.find(x => { + if (filter && !filter(x)) { + return false + } + return _.isEqual(x.ref, ref) + }) +} + +export function usePromise(p?: Promise): T { + if (p === undefined) { + throw new Promise(() => undefined) + } + + const promise = p as any + if (promise.status === 'fulfilled') { + return promise.value; + } else if (promise.status === 'rejected') { + throw promise.reason; + } else if (promise.status === 'pending') { + throw promise; + } else { + promise.status = 'pending'; + p.then( + result => { + promise.status = 'fulfilled'; + promise.value = result; + }, + reason => { + promise.status = 'rejected'; + promise.reason = reason; + }, + ); + throw promise; + } +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/ActionsMenu.tsx b/pkg/webui/ui/src/components/ActionsMenu.tsx new file mode 100644 index 000000000..8247b7ed8 --- /dev/null +++ b/pkg/webui/ui/src/components/ActionsMenu.tsx @@ -0,0 +1,93 @@ +import React from "react"; +import { MoreVert } from "@mui/icons-material"; +import { IconButton, Menu, MenuItem } from "@mui/material"; +import { Link } from "react-router-dom"; + +export interface ActionMenuItem { + icon: React.ReactNode + text: string + handler?: () => void + to?: string +} + +export const ActionsMenu = (props: { icon?: React.ReactNode, menuItems: ActionMenuItem[] }) => { + const [anchorEl, setAnchorEl] = React.useState(null); + const menuOpen = Boolean(anchorEl); + + const handleMenuClick = (e: React.MouseEvent) => { + e.stopPropagation() + setAnchorEl(e.currentTarget); + }; + const handleMenuClose = () => { + setAnchorEl(null); + }; + + const icon = props.icon || + + return + + {icon} + + {e.stopPropagation(); handleMenuClose()}} + PaperProps={{ + elevation: 0, + sx: { + overflow: 'visible', + filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))', + mt: 1.5, + '& .MuiAvatar-root': { + width: 32, + height: 32, + ml: -0.5, + mr: 1, + }, + '&:before': { + content: '""', + display: 'block', + position: 'absolute', + top: 0, + right: 14, + width: 10, + height: 10, + bgcolor: 'background.paper', + transform: 'translateY(-50%) rotate(45deg)', + zIndex: 0, + }, + }, + }} + transformOrigin={{ horizontal: 'right', vertical: 'top' }} + anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} + > + {props.menuItems.map((item, i) => { + const handler = (e: React.SyntheticEvent) => { + e.stopPropagation() + if (item.handler) { + item.handler() + } + handleMenuClose() + } + if (item.to) { + return + {item.icon} {item.text} + + } else { + return + {item.icon} {item.text} + + } + })} + + + +} diff --git a/pkg/webui/ui/src/components/App.css b/pkg/webui/ui/src/components/App.css new file mode 100644 index 000000000..78b8850cf --- /dev/null +++ b/pkg/webui/ui/src/components/App.css @@ -0,0 +1,38 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/pkg/webui/ui/src/components/App.test.tsx b/pkg/webui/ui/src/components/App.test.tsx new file mode 100644 index 000000000..2a68616d9 --- /dev/null +++ b/pkg/webui/ui/src/components/App.test.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', () => { + render(); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/pkg/webui/ui/src/components/App.tsx b/pkg/webui/ui/src/components/App.tsx new file mode 100644 index 000000000..ce3a71aa9 --- /dev/null +++ b/pkg/webui/ui/src/components/App.tsx @@ -0,0 +1,30 @@ +import React, { useState } from 'react'; + +import '../index.css'; +import { Box } from "@mui/material"; +import { Outlet, useOutletContext } from "react-router-dom"; +import LeftDrawer from "./LeftDrawer"; + +export interface AppOutletContext { + filters?: React.ReactNode + setFilters: (filter: React.ReactNode) => void +} + +export function useAppOutletContext(): AppOutletContext { + return useOutletContext() +} + +const App = () => { + const [filters, setFilters] = useState() + + const context: AppOutletContext = { + filters: filters, + setFilters: setFilters, + } + + return + } context={context}/> + +}; + +export default App; diff --git a/pkg/webui/ui/src/components/CodeViewer.tsx b/pkg/webui/ui/src/components/CodeViewer.tsx new file mode 100644 index 000000000..2e55a82c0 --- /dev/null +++ b/pkg/webui/ui/src/components/CodeViewer.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +import { Light as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs'; + +import yamlLanguage from 'react-syntax-highlighter/dist/esm/languages/hljs/yaml'; +import diffLanguage from 'react-syntax-highlighter/dist/esm/languages/hljs/diff'; + +SyntaxHighlighter.registerLanguage('yaml', yamlLanguage); +SyntaxHighlighter.registerLanguage('diff', diffLanguage); + +export function CodeViewer(props: { code: string, language: string }) { + return + {props.code!} + + + + /*return + + {props.code!} + + */ +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/ErrorsTable.tsx b/pkg/webui/ui/src/components/ErrorsTable.tsx new file mode 100644 index 000000000..f15abf73a --- /dev/null +++ b/pkg/webui/ui/src/components/ErrorsTable.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TableRow from '@mui/material/TableRow'; +import Paper from '@mui/material/Paper'; +import { DeploymentError } from "../models"; +import { Box, Divider, List, ListItem, ListItemText } from "@mui/material"; +import { buildRefKindElement } from "../api"; + +export function ErrorsTable(props: { errors: DeploymentError[] }) { + return <> + + + + + + Ref + Message + + + + {props.errors?.map((e, i) => ( + + + + {buildRefKindElement(e.ref, <> + + + + )} + + + + + {e.ref.namespace && <> + + + + + } + + + + {e.message} + + + ))} + +
    +
    +
    + +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/Jdenticon.tsx b/pkg/webui/ui/src/components/Jdenticon.tsx new file mode 100644 index 000000000..b46d01ffd --- /dev/null +++ b/pkg/webui/ui/src/components/Jdenticon.tsx @@ -0,0 +1,17 @@ +import React, { useEffect, useRef } from 'react'; +import * as jdenticon from 'jdenticon'; +import { Box, SvgIcon } from "@mui/material"; + +export const Jdenticon = (props: { value: string, size: string }) => { + const icon = useRef(null); + useEffect(() => { + if (icon.current === null) { + return + } + jdenticon.update(icon.current, props.value); + }, [props, icon]) + + return + + +} diff --git a/pkg/webui/ui/src/components/LeftDrawer.tsx b/pkg/webui/ui/src/components/LeftDrawer.tsx new file mode 100644 index 000000000..7c23a7be3 --- /dev/null +++ b/pkg/webui/ui/src/components/LeftDrawer.tsx @@ -0,0 +1,170 @@ +import * as React from 'react'; +import { useState } from 'react'; +import { CSSObject, styled, Theme, useTheme } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import MuiDrawer from '@mui/material/Drawer'; +import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import List from '@mui/material/List'; +import Divider from '@mui/material/Divider'; +import IconButton from '@mui/material/IconButton'; +import MenuIcon from '@mui/icons-material/Menu'; +import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; +import ChevronRightIcon from '@mui/icons-material/ChevronRight'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import { Adjust } from "@mui/icons-material"; +import { Link } from "react-router-dom"; +import { AppOutletContext } from "./App"; +import { KluctlLogo } from "../icons/KluctlLogo"; + +const drawerWidth = 240; + +const openedMixin = (theme: Theme): CSSObject => ({ + width: drawerWidth, + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + overflowX: 'hidden', +}); + +const closedMixin = (theme: Theme): CSSObject => ({ + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + overflowX: 'hidden', + width: `calc(${theme.spacing(7)} + 1px)`, + [theme.breakpoints.up('sm')]: { + width: `calc(${theme.spacing(8)} + 1px)`, + }, +}); + +const DrawerHeader = styled('div')(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + padding: theme.spacing(0, 1), + // necessary for content to be below app bar + ...theme.mixins.toolbar, +})); + +interface AppBarProps extends MuiAppBarProps { + open?: boolean; +} + +const AppBar = styled(MuiAppBar, { + shouldForwardProp: (prop) => prop !== 'open', +})(({ theme, open }) => ({ + zIndex: theme.zIndex.drawer + 1, + transition: theme.transitions.create(['width', 'margin'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + ...(open && { + marginLeft: drawerWidth, + width: `calc(100% - ${drawerWidth}px)`, + transition: theme.transitions.create(['width', 'margin'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }), +})); + +const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })( + ({ theme, open }) => ({ + width: drawerWidth, + flexShrink: 0, + whiteSpace: 'nowrap', + boxSizing: 'border-box', + ...(open && { + ...openedMixin(theme), + '& .MuiDrawer-paper': openedMixin(theme), + }), + ...(!open && { + ...closedMixin(theme), + '& .MuiDrawer-paper': closedMixin(theme), + }), + }), +); + +function Item(props: { text: string, open: boolean, icon: React.ReactNode, to: string }) { + return + + + {props.icon} + + + + +} + +export default function LeftDrawer(props: { content: React.ReactNode, context: AppOutletContext }) { + const context = props.context + + const theme = useTheme(); + const [open, setOpen] = useState(true); + + const handleDrawerOpen = () => { + setOpen(true); + }; + + const handleDrawerClose = () => { + setOpen(false); + }; + + return ( + + + + + + + + + + + + + {theme.direction === 'rtl' ? : } + + + + + } to={"targets"}/> + + {context.filters} + + + + + + {props.content} + + + ); +} diff --git a/pkg/webui/ui/src/components/Loading.tsx b/pkg/webui/ui/src/components/Loading.tsx new file mode 100644 index 000000000..9b26bf95e --- /dev/null +++ b/pkg/webui/ui/src/components/Loading.tsx @@ -0,0 +1,15 @@ +import { Box, CircularProgress } from "@mui/material"; + +export const Loading = () => { + return + + + + +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/ObjectYaml.tsx b/pkg/webui/ui/src/components/ObjectYaml.tsx new file mode 100644 index 000000000..11f50f428 --- /dev/null +++ b/pkg/webui/ui/src/components/ObjectYaml.tsx @@ -0,0 +1,26 @@ +import { CommandResultProps } from "./result-view/CommandResultView"; +import { ObjectRef } from "../models"; +import { api, ObjectType, usePromise } from "../api"; +import React, { Suspense, useEffect, useState } from "react"; +import { CodeViewer } from "./CodeViewer"; + +import * as yaml from 'js-yaml'; +import { Loading } from "./Loading"; + +export const ObjectYaml = (props: {treeProps: CommandResultProps, objectRef: ObjectRef, objectType: ObjectType}) => { + const [promise, setPromise] = useState>() + + useEffect(() => { + const p = api.getResultObject(props.treeProps.summary.id, props.objectRef, props.objectType) + .then(yaml.dump) + setPromise(p) + }, [props.treeProps, props.objectRef, props.objectType]) + + const Content = () => { + return + } + + return }> + + +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/PropertiesTable.tsx b/pkg/webui/ui/src/components/PropertiesTable.tsx new file mode 100644 index 000000000..20cdd967e --- /dev/null +++ b/pkg/webui/ui/src/components/PropertiesTable.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TableRow from '@mui/material/TableRow'; +import Paper from '@mui/material/Paper'; + +export function PropertiesTable(props: {properties: {name: string, value: React.ReactNode}[]}) { + return ( + + + + + Name + Value + + + + {props.properties.map((row) => ( + + {row.name} + {row.value} + + ))} + +
    +
    + ); +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/Router.tsx b/pkg/webui/ui/src/components/Router.tsx new file mode 100644 index 000000000..ee3417a1a --- /dev/null +++ b/pkg/webui/ui/src/components/Router.tsx @@ -0,0 +1,41 @@ +import { createBrowserRouter, createHashRouter, useRouteError } from "react-router-dom"; +import React from "react"; +import App from "./App"; +import { projectsLoader, TargetsView } from "./targets-view/TargetsView"; +import { commandResultLoader, CommandResultView } from "./result-view/CommandResultView"; + +function ErrorPage() { + const error = useRouteError() as any; + + return ( +
    +

    Oops!

    +

    Sorry, an unexpected error has occurred.

    +

    + {error.statusText || error.message} +

    +
    + ); +} + +export const Router = createHashRouter([ + { + path: "/", + element: , + errorElement: , + children: [ + { + path: "targets", + element: , + loader: projectsLoader, + errorElement: , + }, + { + path: "results/:id", + element: , + loader: commandResultLoader, + errorElement: , + }, + ], + }, +]); diff --git a/pkg/webui/ui/src/components/result-view/ChangesTable.tsx b/pkg/webui/ui/src/components/result-view/ChangesTable.tsx new file mode 100644 index 000000000..b802201f3 --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/ChangesTable.tsx @@ -0,0 +1,105 @@ +import React from 'react'; +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TableRow from '@mui/material/TableRow'; +import Paper from '@mui/material/Paper'; +import { buildRefKindElement, buildRefString } from "../../api"; +import { Box, Typography } from "@mui/material"; +import { CodeViewer } from "../CodeViewer"; +import { DiffStatus } from "./nodes/NodeData"; +import Divider from "@mui/material/Divider"; +import { ObjectRef } from "../../models"; + +const RefList = (props: { title: string, refs: ObjectRef[] }) => { + return + {props.title} + + + + + + Kind + + + Namespace + + + Name + + + + + {props.refs.map((ref, i) => { + return + + {buildRefKindElement(ref)} + + + {ref.namespace} + + + {ref.name} + + + })} + +
    +
    + +
    +} + +export function ChangesTable(props: { diffStatus: DiffStatus }) { + let changedObjects: React.ReactElement | undefined + + if (props.diffStatus.changedObjects.length) { + changedObjects = + Changed Objects + {props.diffStatus.changedObjects.map((co, i) => ( + + + + + + {buildRefString(co.ref)} + + + + Path + Changes + + + + {co.changes?.map((c, i) => ( + + + + {c.jsonPath} + + + + + + + ))} + +
    +
    + ))} + +
    + } + + return + {props.diffStatus.newObjects.length ? + : <>} + {props.diffStatus.deletedObjects.length ? + : <>} + {props.diffStatus.orphanObjects.length ? + : <>} + {changedObjects} + +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx b/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx new file mode 100644 index 000000000..d5a48a05b --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx @@ -0,0 +1,59 @@ +import { CommandResultSummary, ValidateResult } from "../../models"; +import { Box, Tooltip, Typography } from "@mui/material"; +import { Dangerous, Delete, Difference, LibraryAdd, LinkOff, Warning } from "@mui/icons-material"; +import React from "react"; + +export interface StatusLineProps { + errors?: number + warnings?: number + changedObjects?: number + newObjects?: number + deletedObjects?: number + orphanObjects?: number +} + +export const StatusLine = (props: StatusLineProps) => { + const children: React.ReactElement[] = [] + + const doPush = (n: number | undefined, t: string, icon: React.ReactElement) => { + if (n) { + children.push( + + + {icon} + + {n} + + ) + } + } + + doPush(props.errors, "total errors.", ) + doPush(props.warnings, "total warnings.", ) + doPush(props.newObjects, "new objects.", ) + doPush(props.deletedObjects, "deleted objects.", ) + doPush(props.orphanObjects, "orphan objects.", ) + doPush(props.changedObjects, "changed objects.", ) + + return + {children} + +} + +export const CommandResultStatusLine = (props: { rs: CommandResultSummary }) => { + return +} + + +export const ValidateResultStatusLine = (props: { vr?: ValidateResult }) => { + return +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx new file mode 100644 index 000000000..d1986046a --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx @@ -0,0 +1,95 @@ +import * as React from 'react'; +import { useEffect, useMemo, useState } from 'react'; +import TreeView from '@mui/lab/TreeView'; +import { TreeItem } from "@mui/lab"; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ChevronRightIcon from '@mui/icons-material/ChevronRight'; +import { NodeBuilder } from "./nodes/NodeBuilder"; +import { NodeData } from "./nodes/NodeData"; +import { ActiveFilters, FilterNode } from "./NodeStatusFilter"; +import { CommandResultProps } from "./CommandResultView"; +import { Loading } from "../Loading"; + +export interface CommandResultTreeProps { + commandResultProps?: CommandResultProps + + onSelectNode: (node?: NodeData) => void + activeFilters?: ActiveFilters +} + +const CommandResultTree = (props: CommandResultTreeProps) => { + const [expanded, setExpanded] = useState(["root"]); + const [selectedNodeId, setSelectedNodeId] = useState() + + const [rootNode, nodeMap] = useMemo(() => { + if (!props.commandResultProps) { + return [undefined, undefined] + } + + const builder = new NodeBuilder(props.commandResultProps) + return builder.buildRoot() + }, [props.commandResultProps]) + + const handleToggle = (event: React.SyntheticEvent, nodeIds: string[]) => { + setExpanded(nodeIds); + }; + + const handleDoubleClick = (e: React.SyntheticEvent, node: NodeData) => { + if (expanded.includes(node.id)) { + setExpanded(expanded.filter((item) => item !== node.id)); + } else { + setExpanded([...expanded, node.id]); + } + e.stopPropagation() + }; + + const handleItemClick = (e: React.SyntheticEvent, node: NodeData) => { + setSelectedNodeId(node.id) + e.stopPropagation() + } + + const onSelectNode = props.onSelectNode + useEffect(() => { + if (!nodeMap || !selectedNodeId) { + return + } + const node = nodeMap.get(selectedNodeId) + if (!node) { + setSelectedNodeId(undefined) + } + onSelectNode(node) + }, [nodeMap, selectedNodeId, onSelectNode]) + + const renderTree = (nodes: NodeData) => { + if (!FilterNode(nodes, props.activeFilters)) { + return null + } + return handleItemClick(e, nodes)}>{nodes.buildTreeItem()}} + sx={{ marginBottom: "5px", marginTop: "5px" }} + onDoubleClick={(e: React.SyntheticEvent) => handleDoubleClick(e, nodes)} + > + {Array.isArray(nodes.children) + ? nodes.children.map((node) => renderTree(node)) + : null} + + }; + + if (!rootNode) { + return + } + + return } + defaultExpandIcon={} + sx={{ width: "100%" }} + > + {renderTree(rootNode)} + +} + +export default CommandResultTree; diff --git a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx new file mode 100644 index 000000000..607bc925b --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { Box, Divider } from "@mui/material"; +import { CommandResult, CommandResultSummary, ShortName } from "../../models"; +import { NodeData } from "./nodes/NodeData"; +import { SidePanel } from "./SidePanel"; +import { ActiveFilters, NodeStatusFilter } from "./NodeStatusFilter"; +import CommandResultTree from "./CommandResultTree"; +import { useLoaderData } from "react-router-dom"; +import { api } from "../../api"; +import { useAppOutletContext } from "../App"; + +export interface CommandResultProps { + shortNames: ShortName[] + summary: CommandResultSummary + commandResult: CommandResult +} + +export async function commandResultLoader({ params }: any) { + const result = api.getResult(params.id) + const shortNames = api.getShortNames() + const summaries = api.listResults() + + return { + shortNames: await shortNames, + summary: (await summaries).find(x => x.id === params.id), + commandResult: await result, + } +} + +export const CommandResultView = () => { + const context = useAppOutletContext() + const commandResultProps = useLoaderData() as CommandResultProps + const [activeFilters, setActiveFilters] = useState() + const [sidePanelNode, setSidePanelNode] = useState() + + useEffect(() => { + context.setFilters(<> + + ) + }, []) + + return + + + + + + + + +} diff --git a/pkg/webui/ui/src/components/result-view/NodeStatusFilter.tsx b/pkg/webui/ui/src/components/result-view/NodeStatusFilter.tsx new file mode 100644 index 000000000..3c78613aa --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/NodeStatusFilter.tsx @@ -0,0 +1,97 @@ +import React, { useState } from "react"; +import { Box, Chip } from "@mui/material"; +import { AutoFixHigh, ErrorOutline, Grade } from "@mui/icons-material"; +import { OverridableComponent } from "@mui/material/OverridableComponent"; +import Tooltip from "@mui/material/Tooltip"; +import { NodeData } from "./nodes/NodeData"; + +export interface ActiveFilters { + onlyImportant: boolean + onlyChanged: boolean + onlyWithErrorsOrWarnings: boolean +} + +export function FilterNode(node: NodeData, activeFilters?: ActiveFilters) { + const hasChanges = node.diffStatus?.hasDiffs() + const hasErrorsOrWarnings = node.healthStatus?.errors.length || node.healthStatus?.warnings.length + if (activeFilters?.onlyImportant && !hasChanges && !hasErrorsOrWarnings) { + return false + } + if (activeFilters?.onlyChanged && !hasChanges) { + return false + } + if (activeFilters?.onlyWithErrorsOrWarnings && !hasErrorsOrWarnings) { + return false + } + return true +} + +const FilterButton = (props: { Icon: OverridableComponent, tooltip: string, color: any, active: boolean, handler: (active: boolean) => void }) => { + const handleClick = () => { + props.handler(!props.active) + } + + const Icon = props.Icon + const chipColor = props.active ? props.color : "default"; + return + + } + onClick={handleClick} + sx={{ + "& .MuiChip-label": { + display: "flex", + justifyContent: "center", + alignItems: "center" + } + }} + /> + +} + +export const NodeStatusFilter = (props: { onFilterChange: (f: ActiveFilters) => void }) => { + const [activeFilters, setActiveFilters] = useState({ + onlyImportant: false, + onlyChanged: false, + onlyWithErrorsOrWarnings: false, + }) + + const doSetActiveFilters = (newActiveFilters: ActiveFilters) => { + setActiveFilters(newActiveFilters) + props.onFilterChange(newActiveFilters) + } + + return ( + + Filters + + { + doSetActiveFilters({ ...activeFilters, onlyImportant: active }); + }}/> + { + doSetActiveFilters({ ...activeFilters, onlyChanged: active }); + }}/> + { + doSetActiveFilters({ ...activeFilters, onlyWithErrorsOrWarnings: active }); + }}/> + + + ) +} diff --git a/pkg/webui/ui/src/components/result-view/SidePanel.tsx b/pkg/webui/ui/src/components/result-view/SidePanel.tsx new file mode 100644 index 000000000..da68288f9 --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/SidePanel.tsx @@ -0,0 +1,78 @@ +import { Box, Tab, Typography } from "@mui/material"; +import React, { useEffect, useState } from "react"; +import { TabContext, TabList, TabPanel } from "@mui/lab"; + +export interface SidePanelTab { + label: string + content: React.ReactNode +} + +export interface SidePanelProvider { + buildSidePanelTitle(): React.ReactNode + buildSidePanelTabs(): SidePanelTab[] +} + +export interface SidePanelProps { + provider?: SidePanelProvider; +} + +export const SidePanel = (props: SidePanelProps) => { + let [selectedTab, setSelectedTab] = useState(); + + function handleTabChange(_e: React.SyntheticEvent, value: string) { + setSelectedTab(value); + } + + let tabs = props.provider?.buildSidePanelTabs() + if (!tabs) { + tabs = [] + } + + useEffect(() => { + if (!tabs?.length) { + setSelectedTab("") + return + } + + if (!selectedTab) { + setSelectedTab(tabs[0].label) + return + } + + if (!tabs.find(x => x.label === selectedTab)) { + // reset it after the type of selected item has changed + setSelectedTab(tabs[0].label) + } + // ignore that it wants us to add selectedTab to the deps (it would cause and endless loop) + // eslint-disable-next-line + }, [props.provider]) + + if (!selectedTab || !tabs.find(x => x.label === selectedTab)) { + return <> + } + + if (!props.provider) { + return <> + } + + return + + {props.provider.buildSidePanelTitle()} + + + + + + {tabs.map((tab, i) => { + return + })} + + + {tabs.map((tab, index) => { + return + {tab.content} + + })} + + +} diff --git a/pkg/webui/ui/src/components/result-view/nodes/CommandResultNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/CommandResultNode.tsx new file mode 100644 index 000000000..170b653af --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/nodes/CommandResultNode.tsx @@ -0,0 +1,60 @@ +import React from 'react'; + +import { NodeData } from "./NodeData"; +import { CloudSync } from "@mui/icons-material"; +import { PropertiesTable } from "../../PropertiesTable"; +import { CodeViewer } from "../../CodeViewer"; +import { CommandResultProps } from "../CommandResultView"; + +import * as yaml from 'js-yaml'; +import { SidePanelTab } from "../SidePanel"; + +export class CommandResultNodeData extends NodeData { + dumpedTargetYaml?: string + + constructor(props: CommandResultProps, id: string) { + super(props, id, true, true); + + if (this.props.commandResult.command?.target) { + this.dumpedTargetYaml = yaml.dump(this.props.commandResult.command.target) + } + } + + buildSidePanelTitle(): React.ReactNode { + return "CommandResult"; + } + + buildIcon(): [React.ReactNode, string] { + return [, "result"] + } + + buildSidePanelTabs(): SidePanelTab[] { + const tabs = [ + {label: "Summary", content: this.buildSummaryPage()} + ] + + if (this.dumpedTargetYaml) { + const page = this.buildTargetPage() + tabs.push({label: "Target", content: page}) + } + + this.buildDiffAndHealthPages(tabs) + + return tabs + } + + buildSummaryPage(): React.ReactNode { + const props = [ + {name: "Initiator", value: this.props.commandResult.command?.initiator}, + {name: "Command", value: this.props.commandResult.command?.command}, + ] + + return <> + + + } + + buildTargetPage(): React.ReactNode { + return + } +} diff --git a/pkg/webui/ui/src/components/result-view/nodes/DeletedOrOrphanObjectsCollectionNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/DeletedOrOrphanObjectsCollectionNode.tsx new file mode 100644 index 000000000..5b3cba216 --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/nodes/DeletedOrOrphanObjectsCollectionNode.tsx @@ -0,0 +1,54 @@ +import { NodeData } from "./NodeData"; +import React from "react"; +import { Delete, LinkOff } from "@mui/icons-material"; +import { CommandResultProps } from "../CommandResultView"; +import { PropertiesTable } from "../../PropertiesTable"; +import { SidePanelTab } from "../SidePanel"; + +export class DeletedOrOrphanObjectsCollectionNode extends NodeData { + deleted: boolean + + constructor(props: CommandResultProps, id: string, deleted: boolean) { + super(props, id, false, true); + this.deleted = deleted + } + + buildSidePanelTitle(): React.ReactNode { + if (this.deleted) { + return `${this.diffStatus?.deletedObjects.length} objects deleted` + } else { + return `${this.diffStatus?.orphanObjects.length} objects orphaned` + } + } + + buildIcon(): [React.ReactNode, string] { + if (this.deleted) { + return [, "deleted"] + } else { + return [, "orphans"] + } + } + + buildSidePanelTabs(): SidePanelTab[] { + const tabs = [ + {label: "Summary", content: this.buildSummaryPage()} + ] + + return tabs + } + + buildSummaryPage(): React.ReactNode { + const props = [] + + if (this.diffStatus?.deletedObjects.length) { + props.push({ name: "Deleted", value: this.diffStatus?.deletedObjects.length }) + } + if (this.diffStatus?.orphanObjects.length) { + props.push({ name: "Orphaned", value: this.diffStatus?.orphanObjects.length }) + } + + return <> + + + } +} diff --git a/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx new file mode 100644 index 000000000..8d5ecc36c --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx @@ -0,0 +1,78 @@ +import React from 'react'; + +import { DeploymentItemConfig, DeploymentProjectConfig } from "../../../models"; +import { NodeData } from "./NodeData"; +import { FolderZip } from "@mui/icons-material"; +import { GitIcon } from "../../../icons/GitIcon"; +import { CommandResultProps } from "../CommandResultView"; +import { PropertiesTable } from "../../PropertiesTable"; +import { buildDeploymentItemSummaryProps } from "./DeploymentItemNode"; +import { SidePanelTab } from "../SidePanel"; + + +export class DeploymentItemIncludeNodeData extends NodeData { + deploymentItem: DeploymentItemConfig + includedDeployment: DeploymentProjectConfig + + constructor(props: CommandResultProps, id: string, deploymentItem: DeploymentItemConfig, includedDeployment: DeploymentProjectConfig) { + super(props, id, true, true); + this.deploymentItem = deploymentItem + this.includedDeployment = includedDeployment + } + + buildSidePanelTitle(): React.ReactNode { + if (this.deploymentItem.include) { + return this.deploymentItem.include + } else if (this.deploymentItem.git) { + const s = this.deploymentItem.git!.url.split("/") + const name = s[s.length-1] + return <> + {name} + {this.deploymentItem.git!.subDir && (<>
    {this.deploymentItem.git!.subDir})} + + } else { + return "unknown include" + } + } + + buildIcon(): [React.ReactNode, string] { + if (this.deploymentItem.git) { + return [, "git"] + } + return [, "include"] + } + + buildSidePanelTabs(): SidePanelTab[] { + const tabs = [ + {label: "Summary", content: this.buildSummaryPage()}, + ] + this.buildDiffAndHealthPages(tabs) + return tabs; + } + + buildSummaryPage(): React.ReactNode { + const props = [] + + if (this.deploymentItem.include) { + props.push({ name: "Type", value: "LocalInclude" }) + props.push({ name: "Path", value: this.deploymentItem.include }) + } else if (this.deploymentItem.git) { + props.push({name: "Type", value: "GitInclude"}) + props.push({name: "Url", value: this.deploymentItem.git.url}) + props.push({name: "SubDir", value: this.deploymentItem.git.subDir}) + let ref = "HEAD" + if (this.deploymentItem.git.ref) { + ref = this.deploymentItem.git.ref! + } + props.push({name: "Ref", value: ref}) + } else { + props.push({name: "Type", value: "Unknown"}) + } + + buildDeploymentItemSummaryProps(this.deploymentItem, props) + + return <> + + + } +} diff --git a/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemNode.tsx new file mode 100644 index 000000000..bebab6f96 --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemNode.tsx @@ -0,0 +1,72 @@ +import React from 'react'; + +import { DeploymentItemConfig } from "../../../models"; +import { NodeData } from "./NodeData"; +import { Source } from "@mui/icons-material"; +import { PropertiesTable } from "../../PropertiesTable"; +import { CommandResultProps } from "../CommandResultView"; +import { SidePanelTab } from "../SidePanel"; + + +export class DeploymentItemNodeData extends NodeData { + deploymentItem: DeploymentItemConfig + + constructor(props: CommandResultProps, id: string, deploymentItem: DeploymentItemConfig) { + super(props, id, true, true); + this.deploymentItem = deploymentItem + } + + buildSidePanelTitle(): React.ReactNode { + return this.deploymentItem.path + } + + buildIcon(): [React.ReactNode, string] { + let iconText = "di" + return [, iconText] + } + + buildSidePanelTabs(): SidePanelTab[] { + const tabs = [ + {label: "Summary", content: this.buildSummaryPage()}, + ] + this.buildDiffAndHealthPages(tabs) + return tabs + } + + buildSummaryPage(): React.ReactNode { + const props = [] + + props.push({name: "Path", value: this.deploymentItem.path}) + + buildDeploymentItemSummaryProps(this.deploymentItem, props) + + return <> + + + } +} + +export function buildDeploymentItemSummaryProps(di: DeploymentItemConfig, props: {name: string, value: React.ReactNode}[]) { + if (di.barrier !== undefined) { + props.push({name: "Barrier", value: di.barrier + ""}) + } + if (di.waitReadiness !== undefined) { + props.push({name: "WaitReadiness", value: di.waitReadiness + ""}) + } + if (di.skipDeleteIfTags !== undefined) { + props.push({name: "SkipDeleteIfTags", value: di.skipDeleteIfTags + ""}) + } + if (di.onlyRender !== undefined) { + props.push({name: "OnlyRender", value: di.onlyRender + ""}) + } + if (di.alwaysDeploy !== undefined) { + props.push({name: "AlwaysDeploy", value: di.alwaysDeploy + ""}) + } + if (di.deleteObjects) { + // TODO this is ugly + props.push({name: "DeleteObjects", value: JSON.stringify(di.deleteObjects)}) + } + if (di.when) { + props.push({name: "When", value: di.when}) + } +} diff --git a/pkg/webui/ui/src/components/result-view/nodes/NodeBuilder.ts b/pkg/webui/ui/src/components/result-view/nodes/NodeBuilder.ts new file mode 100644 index 000000000..c3ab9769b --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/nodes/NodeBuilder.ts @@ -0,0 +1,194 @@ +import { + DeploymentError, + DeploymentItemConfig, + DeploymentProjectConfig, + ObjectRef, + ResultObject, + VarsSource +} from "../../../models"; +import { VarsSourceNodeData } from "./VarsSourceNode"; +import { DeploymentItemNodeData } from "./DeploymentItemNode"; +import { ObjectNodeData } from "./ObjectNode"; +import { NodeData } from "./NodeData"; +import { CommandResultNodeData } from "./CommandResultNode"; +import { VarsSourceCollectionNodeData } from "./VarsSourceCollectionNode"; +import { DeploymentItemIncludeNodeData } from "./DeploymentItemIncludeNode"; +import { CommandResultProps } from "../CommandResultView"; +import { DeletedOrOrphanObjectsCollectionNode } from "./DeletedOrOrphanObjectsCollectionNode"; + +export class NodeBuilder { + private props: CommandResultProps + private changedObjectsMap: Map = new Map() + private newObjectsMap: Map = new Map() + private orphanObjectsMap: Map = new Map() + private deletedObjectsMap: Map = new Map() + private errorsMap: Map = new Map() + private warningsMap: Map = new Map() + + constructor(props: CommandResultProps) { + this.props = props + + props.commandResult.objects?.forEach(o => { + const key = buildObjectRefKey(o.ref) + if (o.changes?.length) { + this.changedObjectsMap.set(key, o) + } + if (o.new) { + this.newObjectsMap.set(key, o.ref) + } + if (o.orphan) { + this.orphanObjectsMap.set(key, o.ref) + } + if (o.deleted) { + this.deletedObjectsMap.set(key, o.ref) + } + }) + props.commandResult.errors?.forEach(e => { + const key = buildObjectRefKey(e.ref) + let l = this.errorsMap.get(key) + if (!l) { + l = [e] + this.errorsMap.set(key, l) + } else { + l.push(e) + } + }) + props.commandResult.warnings?.forEach(e => { + const key = buildObjectRefKey(e.ref) + let l = this.warningsMap.get(key) + if (!l) { + l = [e] + this.warningsMap.set(key, l) + } else { + l.push(e) + } + }) + } + + buildRoot(): [CommandResultNodeData, Map] { + const rootNode = new CommandResultNodeData(this.props, "root") + + this.buildDeploymentProjectChildren(rootNode, this.props.commandResult.deployment!) + + if (this.deletedObjectsMap.size) { + this.buildDeletedOrOrphanNode(rootNode, true, Array.from(this.deletedObjectsMap.values())) + } + if (this.orphanObjectsMap.size) { + this.buildDeletedOrOrphanNode(rootNode, false, Array.from(this.orphanObjectsMap.values())) + } + + const nodeMap: Map = new Map() + function collect(n: NodeData) { + nodeMap.set(n.id, n) + n.children.forEach(c => { + collect(c) + }) + } + collect(rootNode) + + return [rootNode, nodeMap] + } + + buildDeploymentProjectChildren(node: NodeData, deploymentProject: DeploymentProjectConfig) { + if (deploymentProject.vars) { + this.buildVarsSourceCollectionNode(node, deploymentProject.vars) + } + deploymentProject.deployments?.forEach((deploymentItem, i) => { + this.buildDeploymentItemNode(node, deploymentItem, i) + }) + } + + buildVarsSourceCollectionNode(parentNode: NodeData, varsSources?: VarsSource[]) { + if (varsSources === undefined) { + return + } + + const newId = `${parentNode.id}/(vars)` + const node = new VarsSourceCollectionNodeData(this.props, newId) + node.varsSources.push(...varsSources) + + varsSources.forEach((vs, i) => { + this.buildVarsSourceNode(node, vs, i) + }) + + parentNode.pushChild(node) + + return node + } + + buildVarsSourceNode(parentNode: NodeData, varsSource: VarsSource, index: number): VarsSourceNodeData { + const newId = `${parentNode.id}/${index}}` + const node = new VarsSourceNodeData(this.props, newId, varsSource) + parentNode.pushChild(node) + return node + } + + buildDeploymentItemNode(parentNode: NodeData, deploymentItem: DeploymentItemConfig, index: number): NodeData | undefined{ + let node: NodeData | undefined + const newId = `${parentNode.id}/(dis)/${index}` + if (deploymentItem.path) { + node = new DeploymentItemNodeData(this.props, newId, deploymentItem) + this.buildVarsSourceCollectionNode(node, deploymentItem.vars) + deploymentItem.renderedObjects?.forEach(renderedObject => { + this.buildObjectNode(node!, renderedObject) + }) + } else if (deploymentItem.include || deploymentItem.git) { + node = new DeploymentItemIncludeNodeData(this.props, newId, deploymentItem, deploymentItem.renderedInclude!) + this.buildVarsSourceCollectionNode(node, deploymentItem.vars) + if (deploymentItem.renderedInclude) { + this.buildDeploymentProjectChildren(node, deploymentItem.renderedInclude) + } + } else { + return node + } + + parentNode.pushChild(node) + + return node + } + + buildObjectNode(parentNode: NodeData, objectRef: ObjectRef): ObjectNodeData { + const newId = `${parentNode.id}/(obj)/${buildObjectRefKey(objectRef)}` + const node = new ObjectNodeData(this.props, newId, objectRef) + + const key = buildObjectRefKey(objectRef) + if (this.changedObjectsMap.has(key)) { + node.diffStatus?.addChangedObject(this.changedObjectsMap.get(key)!) + } + if (this.newObjectsMap.has(key)) { + node.diffStatus?.newObjects.push(objectRef) + } + if (this.deletedObjectsMap.has(key)) { + node.diffStatus?.deletedObjects.push(objectRef) + } + if (this.orphanObjectsMap.has(key)) { + node.diffStatus?.orphanObjects.push(objectRef) + } + + this.errorsMap.get(key)?.forEach(e => { + node.healthStatus?.errors.push(e) + }) + this.warningsMap.get(key)?.forEach(e => { + node.healthStatus?.warnings.push(e) + }) + + parentNode.pushChild(node) + + return node + } + + buildDeletedOrOrphanNode(parentNode: NodeData, deleted: boolean, refs: ObjectRef[]): DeletedOrOrphanObjectsCollectionNode { + const idType = deleted ? "deleted" : "orphaned" + const newId = `${parentNode.id}/(${idType})` + const node = new DeletedOrOrphanObjectsCollectionNode(this.props, newId, deleted) + refs.forEach(ref => { + this.buildObjectNode(node, ref) + }) + parentNode.pushChild(node, true) + return node + } +} + +function buildObjectRefKey(ref: ObjectRef): string { + return [ref.group, ref.version, ref.kind, ref.namespace, ref.name].join("+") +} diff --git a/pkg/webui/ui/src/components/result-view/nodes/NodeData.tsx b/pkg/webui/ui/src/components/result-view/nodes/NodeData.tsx new file mode 100644 index 000000000..55c3d66d4 --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/nodes/NodeData.tsx @@ -0,0 +1,188 @@ +import { ChangedObject, DeploymentError, ObjectRef, ResultObject } from "../../../models"; +import React from "react"; +import { Box, Typography } from "@mui/material"; +import { CommandResultProps } from "../CommandResultView"; +import { ChangesTable } from "../ChangesTable"; +import { ErrorsTable } from "../../ErrorsTable"; +import { ObjectType } from "../../../api"; +import { ObjectYaml } from "../../ObjectYaml"; +import { StatusLine } from "../CommandResultStatusLine"; +import { SidePanelProvider, SidePanelTab } from "../SidePanel"; + +export class DiffStatus { + newObjects: ObjectRef[] = []; + deletedObjects: ObjectRef[] = []; + orphanObjects: ObjectRef[] = []; + changedObjects: ChangedObject[] = []; + + totalInsertions: number = 0; + totalDeletions: number = 0; + totalUpdates: number = 0; + + addChangedObject(co: ResultObject) { + this.changedObjects.push(co) + co.changes?.forEach(x => { + switch (x.type) { + case "insert": + this.totalInsertions++ + break + case "delete": + this.totalDeletions++ + break + case "update": + this.totalUpdates++ + break + } + }) + } + + merge(other: DiffStatus) { + this.newObjects = this.newObjects.concat(other.newObjects) + this.deletedObjects = this.deletedObjects.concat(other.deletedObjects) + this.orphanObjects = this.orphanObjects.concat(other.orphanObjects) + this.changedObjects = this.changedObjects.concat(other.changedObjects) + + this.totalInsertions += other.totalInsertions + this.totalDeletions += other.totalDeletions + this.totalUpdates += other.totalUpdates + } + + hasDiffs() { + if (this.newObjects.length || this.deletedObjects.length || this.orphanObjects.length) { + return true + } + if (this.changedObjects.find(co => co.changes?.length !== 0)) { + return true + } + return false + } +} + +export class HealthStatus { + errors: DeploymentError[] = [] + warnings: DeploymentError[] = [] + + merge(other: HealthStatus) { + this.errors = this.errors.concat(other.errors) + this.warnings = this.warnings.concat(other.warnings) + } +} + +export abstract class NodeData implements SidePanelProvider { + props: CommandResultProps + + id: string + children: NodeData[] = [] + + healthStatus?: HealthStatus; + diffStatus?: DiffStatus; + + protected constructor(props: CommandResultProps, id: string, hasHealthStatus: boolean, hasDiffStatus: boolean) { + this.props = props + this.id = id + if (hasHealthStatus) { + this.healthStatus = new HealthStatus() + } + if (hasDiffStatus) { + this.diffStatus = new DiffStatus() + } + } + + pushChild(child: NodeData, front?: boolean) { + if (front) { + this.children.unshift(child) + } else { + this.children.push(child) + } + this.merge(child) + } + + merge(other: NodeData) { + if (this.diffStatus && other.diffStatus) { + this.diffStatus.merge(other.diffStatus) + } + if (this.healthStatus && other.healthStatus) { + this.healthStatus.merge(other.healthStatus) + } + } + + abstract buildSidePanelTitle(): React.ReactNode + + abstract buildIcon(): [React.ReactNode, string] + + abstract buildSidePanelTabs(): SidePanelTab[] + + buildStatusLine(): React.ReactNode { + return + } + + buildTreeItem(): React.ReactNode { + const [icon, iconText] = this.buildIcon() + + return + + + + {icon} + {iconText} + + + + + {this.buildSidePanelTitle()}
    +
    + + {this.buildStatusLine()} + +
    +
    +
    + } + + buildDiffAndHealthPages(tabs: { label: string, content: React.ReactNode }[]) { + this.buildChangesPage(tabs) + this.buildErrorsPage(tabs) + this.buildWarningsPage(tabs) + } + + buildObjectPage(ref: ObjectRef, objectType: ObjectType): React.ReactNode { + return + } + + buildChangesPage(tabs: { label: string, content: React.ReactNode }[]) { + if (!this.diffStatus?.hasDiffs()) { + return undefined + } + tabs.push({ + label: "Changes", + content: + }) + } + + buildErrorsPage(tabs: { label: string, content: React.ReactNode }[]) { + if (!this.healthStatus?.errors.length) { + return undefined + } + tabs.push({ + label: "Errors", + content: + }) + } + + buildWarningsPage(tabs: { label: string, content: React.ReactNode }[]) { + if (!this.healthStatus?.warnings.length) { + return undefined + } + tabs.push({ + label: "Warnings", + content: + }) + } +} diff --git a/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx new file mode 100644 index 000000000..62a5b133f --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx @@ -0,0 +1,101 @@ +import React from 'react'; + +import { ObjectRef } from "../../../models"; +import { NodeData } from "./NodeData"; +import { + DataObject, + PublishedWithChanges, + Settings, + SettingsEthernet, + SmartToy, + SvgIconComponent +} from "@mui/icons-material"; +import { PropertiesTable } from "../../PropertiesTable"; +import { findObjectByRef, ObjectType } from "../../../api"; +import { CommandResultProps } from "../CommandResultView"; +import { SidePanelTab } from "../SidePanel"; + +const kindMapping: {[key: string]: {icon: SvgIconComponent}} = { + "/ConfigMap": {icon: Settings}, + "apps/Deployment": {icon: PublishedWithChanges}, + "Service": {icon: SettingsEthernet}, + "ServiceAccount": {icon: SmartToy} +} + +export class ObjectNodeData extends NodeData { + objectRef: ObjectRef + + constructor(props: CommandResultProps, id: string, objectRef: ObjectRef) { + super(props, id, true, true); + this.objectRef = objectRef + } + + buildSidePanelTitle(): React.ReactNode { + return this.objectRef.name + } + + buildIcon(): [React.ReactNode, string] { + const sn = this.props.shortNames.find(sn => sn.group === this.objectRef.group && sn.kind === this.objectRef.kind) + const snStr = sn?.shortName || "" + + const m = kindMapping[this.objectRef.group + "/" + this.objectRef.kind] + if (m !== undefined) { + return [React.createElement(m.icon, {fontSize: "large"}), snStr] + } + + return [, snStr] + } + + buildSidePanelTabs(): SidePanelTab[] { + const tabs = [ + {label: "Summary", content: this.buildSummaryPage()} + ] + + this.buildDiffAndHealthPages(tabs) + + if (findObjectByRef(this.props.commandResult.objects, this.objectRef)?.rendered) { + tabs.push({ label: "Rendered", content: this.buildObjectPage(this.objectRef, ObjectType.Rendered) }) + } + + if (findObjectByRef(this.props.commandResult.objects, this.objectRef)?.remote) { + tabs.push({label: "Remote", content: this.buildObjectPage(this.objectRef, ObjectType.Remote)}) + } + if (findObjectByRef(this.props.commandResult.objects, this.objectRef)?.applied) { + tabs.push({label: "Applied", content: this.buildObjectPage(this.objectRef, ObjectType.Applied)}) + } + + return tabs + } + + buildSummaryPage(): React.ReactNode { + const props = [] + + let apiVersion = this.objectRef.version + if (this.objectRef.group) { + apiVersion = this.objectRef.group + "/" + this.objectRef.version + } + props.push({name: "ApiVersion", value: apiVersion}) + props.push({name: "Kind", value: this.objectRef.kind}) + + props.push({name: "Name", value: this.objectRef.name}) + if (this.objectRef.namespace) { + props.push({ name: "Namespace", value: this.objectRef.namespace }) + } + + const o = findObjectByRef(this.props.commandResult.objects, this.objectRef) + const annotations: {[key: string]: string} = o?.rendered?.metadata.annotations + if (annotations) { + Object.keys(annotations).forEach(k => { + if (k.indexOf("kluctl.io/") !== -1) { + props.push({ name: k, value: annotations[k] }) + } + } + ) + } + + return <> + + + } +} + diff --git a/pkg/webui/ui/src/components/result-view/nodes/VarsSourceCollectionNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceCollectionNode.tsx new file mode 100644 index 000000000..20af76274 --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceCollectionNode.tsx @@ -0,0 +1,26 @@ +import { VarsSource } from "../../../models"; +import { NodeData } from "./NodeData"; +import React from "react"; +import { DataArray } from "@mui/icons-material"; +import { CommandResultProps } from "../CommandResultView"; +import { SidePanelTab } from "../SidePanel"; + +export class VarsSourceCollectionNodeData extends NodeData { + varsSources: VarsSource[] = [] + + constructor(props: CommandResultProps, id: string) { + super(props, id, false, false); + } + + buildSidePanelTitle(): React.ReactNode { + return "vars: " + this.varsSources.length + } + + buildIcon(): [React.ReactNode, string] { + return [, "vars"] + } + + buildSidePanelTabs(): SidePanelTab[] { + return []; + } +} diff --git a/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx new file mode 100644 index 000000000..d53838eeb --- /dev/null +++ b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx @@ -0,0 +1,238 @@ +import { VarsSource } from "../../../models"; +import { NodeData } from "./NodeData"; +import React from "react"; +import { Category, Cloud, Dvr, Http, Lock, Settings, Source } from "@mui/icons-material"; +import { GitIcon } from "../../../icons/GitIcon"; +import { PropertiesTable } from "../../PropertiesTable"; +import { CodeViewer } from "../../CodeViewer"; +import { Box } from "@mui/material"; +import { CommandResultProps } from "../CommandResultView"; + +import * as yaml from 'js-yaml'; +import { SidePanelTab } from "../SidePanel"; + +interface VarsSourceHandler { + type: string + label: () => React.ReactNode + icon: () => React.ReactNode + sourceProps: () => {name: string, value: React.ReactNode}[] +} + +export class VarsSourceNodeData extends NodeData { + varsSource: VarsSource + + labelsYaml?: string + renderedVarsYaml: string + + constructor(props: CommandResultProps, id: string, varsSource: VarsSource) { + super(props, id, false, false); + this.varsSource = varsSource + + let labels = this.varsSource.clusterConfigMap?.labels + if (!labels) { + labels = this.varsSource.clusterSecret?.labels + } + if (labels) { + this.labelsYaml = yaml.dump(labels) + } + + this.renderedVarsYaml = yaml.dump(this.varsSource.renderedVars) + } + + getVarsSourceHandler(): VarsSourceHandler { + if (this.varsSource.values) { + return { + type: "values", + label: () => { + if (this.varsSource.renderedVars) { + return "values: " + Object.keys(this.varsSource.renderedVars).length + } else { + return "empty values" + } + }, + icon: () => , + sourceProps: () => [] + } + } else if (this.varsSource.file) { + return { + type: "file", + label: () => { + return this.varsSource.file + }, + icon: () => , + sourceProps: () => [ + {name: "File", value: this.varsSource.file} + ] + } + } else if (this.varsSource.git) { + return { + type: "git", + label: () => { + const s = this.varsSource.git!.url.split("/") + const name = s[s.length-1] + return <> + {name}
    + {this.varsSource.git!.path} + + }, + icon: () => , + sourceProps: () => { + const sourceProps = [] + sourceProps.push({name: "Url", value: this.varsSource.git!.url}) + sourceProps.push({name: "Path", value: this.varsSource.git!.path}) + let ref = "HEAD" + if (this.varsSource.git!.ref) { + ref = this.varsSource.git!.ref! + } + sourceProps.push({name: "Ref", value: ref}) + return sourceProps + } + } + } else if (this.varsSource.clusterConfigMap || this.varsSource.clusterSecret) { + const vs = (this.varsSource.clusterConfigMap ? this.varsSource.clusterConfigMap : this.varsSource.clusterSecret)! + const type = this.varsSource.clusterConfigMap ? "cm" : "secret" + const icon = this.varsSource.clusterConfigMap ? : + return { + type: type, + label: () => { + return this.varsSource.clusterConfigMap?.name! + }, + icon: () => icon, + sourceProps: () => { + const sourceProps = [] + sourceProps.push({name: "Name", value: vs.name}) + sourceProps.push({name: "Namespace", value: vs.namespace}) + sourceProps.push({name: "Key", value: vs.key}) + if (vs.labels) { + sourceProps.push({name: "Labels", value: }) + } + return sourceProps + } + } + } else if (this.varsSource.systemEnvVars) { + return { + type: "systemEnvVar", + label: () => { + return "systemEnvVars" + }, + icon: () => , + sourceProps: () => { + // TODO + return [] + } + } + } else if (this.varsSource.http) { + return { + type: "http", + label: () => { + return this.varsSource.http!.url + }, + icon: () => , + sourceProps: () => { + const sourceProps = [] + sourceProps.push({name: "Url", value: this.varsSource.http!.url}) + sourceProps.push({name: "Method", value: this.varsSource.http!.method || "GET"}) + if (this.varsSource.http!.jsonPath) { + sourceProps.push({name: "JsonPath", value: this.varsSource.http!.jsonPath}) + } + return sourceProps + } + } + } else if (this.varsSource.awsSecretsManager) { + return { + type: "awsSecretsManager", + label: () => { + return this.varsSource.awsSecretsManager!.secretName + }, + icon: () => , + sourceProps: () => { + const sourceProps = [] + sourceProps.push({name: "SecretName", value: this.varsSource.awsSecretsManager!.secretName}) + if (this.varsSource.awsSecretsManager!.region) { + sourceProps.push({ name: "Region", value: this.varsSource.awsSecretsManager!.region }) + } + if (this.varsSource.awsSecretsManager!.profile) { + sourceProps.push({ name: "Profile", value: this.varsSource.awsSecretsManager!.profile }) + } + return sourceProps + } + } + } else if (this.varsSource.vault) { + return { + type: "vault", + label: () => { + return <> + {this.varsSource.vault!.address}
    + {this.varsSource.vault!.path} + + }, + icon: () => , + sourceProps: () => { + const sourceProps = [] + sourceProps.push({name: "Address", value: this.varsSource.vault!.address}) + sourceProps.push({name: "Path", value: this.varsSource.vault!.path}) + return sourceProps + } + } + } else { + return { + type: "unknown", + label: () => { + return "values: " + Object.keys(this.varsSource.renderedVars).length + }, + icon: () => , + sourceProps: () => [] + } + } + } + + buildSidePanelTitle(): React.ReactNode { + return this.getVarsSourceHandler().label() + } + + buildIcon(): [React.ReactNode, string] { + const h = this.getVarsSourceHandler() + return [h.icon(), h.type] + } + + buildSidePanelTabs(): SidePanelTab[] { + const tabs = [ + {label: "Summary", content: this.buildSummaryPage()}, + {label: "Vars", content: this.buildVarsPage()} + ] + this.buildDiffAndHealthPages(tabs) + return tabs + } + + buildSummaryPage(): React.ReactNode { + const h = this.getVarsSourceHandler() + + const props = [ + {name: "Type", value: h.type}, + ...h.sourceProps(), + {name: "IgnoreMissing", value: (!!this.varsSource.ignoreMissing) + ""}, + {name: "NoOverride", value: (!!this.varsSource.noOverride) + ""}, + ] + + if (this.varsSource.when) { + props.push({name: "When", value: this.varsSource.when}) + } + + return <> + + + } + + buildVarsPage(): React.ReactNode { + return +
    +                
    +            
    +
    + } +} diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx new file mode 100644 index 000000000..298fbe66a --- /dev/null +++ b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx @@ -0,0 +1,54 @@ +import { CommandResultSummary } from "../../models"; +import { api, usePromise } from "../../api"; +import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; +import React, { Suspense, useEffect, useState } from "react"; +import { NodeData } from "../result-view/nodes/NodeData"; +import { SidePanel } from "../result-view/SidePanel"; +import { Box, Drawer } from "@mui/material"; +import { Loading } from "../Loading"; + +async function doGetRootNode(rs: CommandResultSummary) { + const shortNames = api.getShortNames() + const r = api.getResult(rs.id) + const builder = new NodeBuilder({ + shortNames: await shortNames, + summary: rs, + commandResult: await r, + }) + const [node, nodeMap] = builder.buildRoot() + return node +} + +export const CommandResultDetailsDrawer = (props: { rs?: CommandResultSummary, onClose: () => void }) => { + const [prevId, setPrevId] = useState() + const [promise, setPromise] = useState>(new Promise(() => undefined)) + + useEffect(() => { + if (props.rs === undefined) { + return + } + if (props.rs.id === prevId) { + return + } + setPrevId(props.rs.id) + setPromise(doGetRootNode(props.rs)) + }, [props.rs]) + + const Content = () => { + const node = usePromise(promise) + return + } + + return props.onClose()} + > + + }> + + + + +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx new file mode 100644 index 000000000..8c23fb84e --- /dev/null +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -0,0 +1,91 @@ +import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; +import { AccountTree, CleaningServices, CloudSync, Delete, Difference } from "@mui/icons-material"; +import React, { useEffect, useMemo, useState } from "react"; +import * as yaml from "js-yaml"; +import { CodeViewer } from "../CodeViewer"; +import Paper from "@mui/material/Paper"; +import { Box, IconButton, Tooltip, Typography } from "@mui/material"; +import { CommandResultStatusLine } from "../result-view/CommandResultStatusLine"; +import { useNavigate } from "react-router"; +import { formatDurationShort } from "../../utils/duration"; + +export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary, rs: CommandResultSummary, onSelectCommandResult: (rs?: CommandResultSummary) => void }) => { + const calcAgo = () => { + const t1 = new Date(props.rs.commandInfo.startTime) + const t2 = new Date() + const d = t2.getTime() - t1.getTime() + return formatDurationShort(d) + } + + const navigate = useNavigate() + const [ago, setAgo] = useState(calcAgo()) + + let Icon = Difference + switch (props.rs.commandInfo?.command) { + case "delete": + Icon = Delete + break + case "deploy": + Icon = CloudSync + break + case "diff": + Icon = Difference + break + case "poke-images": + Icon = CloudSync + break + case "prune": + Icon = CleaningServices + break + } + + const cmdInfoYaml = useMemo(() => { + return yaml.dump(props.rs.commandInfo) + }, [props.rs]) + let iconTooltip = + + useEffect(() => { + const interval = setInterval(() => setAgo(calcAgo()), 5000); + return () => clearInterval(interval); + }, []) + + return props.onSelectCommandResult(props.rs)} + > + + + + + + + + {ago} + + + + + {props.rs.commandInfo?.command} + + + + + + + { + e.stopPropagation(); + navigate(`/results/${props.rs.id}`) + + }}> + + + + + + + + + + +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/targets-view/Projects.tsx b/pkg/webui/ui/src/components/targets-view/Projects.tsx new file mode 100644 index 000000000..390fb9ee1 --- /dev/null +++ b/pkg/webui/ui/src/components/targets-view/Projects.tsx @@ -0,0 +1,50 @@ +import { ProjectSummary } from "../../models"; +import { getLastPathElement } from "../../utils/misc"; +import Paper from "@mui/material/Paper"; +import { Box, Typography } from "@mui/material"; +import React from "react"; +import Tooltip from "@mui/material/Tooltip"; + +export const ProjectItem = (props: { ps: ProjectSummary }) => { + const name = getLastPathElement(props.ps.project.gitRepoKey) + const subDir = props.ps.project.subDir + + const projectInfo = + {props.ps.project.gitRepoKey}
    + {props.ps.project.subDir ? <> + SubDir: {props.ps.project.subDir}
    + : <>} +
    + + return + + + {name ? + + + {name} + + + : <>} + + + {subDir ? + {subDir} + : <>} + + + + + {/**/} + + + + + +} diff --git a/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx new file mode 100644 index 000000000..6c76ebbc4 --- /dev/null +++ b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx @@ -0,0 +1,108 @@ +import { TargetSummary } from "../../models"; +import { Box, Drawer } from "@mui/material"; +import { SidePanel, SidePanelProvider, SidePanelTab } from "../result-view/SidePanel"; +import React from "react"; +import { PropertiesTable } from "../PropertiesTable"; +import { DiffStatus } from "../result-view/nodes/NodeData"; +import { ChangesTable } from "../result-view/ChangesTable"; +import { ErrorsTable } from "../ErrorsTable"; + +class MyProvider implements SidePanelProvider { + private ts?: TargetSummary; + private diffStatus: DiffStatus; + + constructor(ts?: TargetSummary) { + this.ts = ts + this.diffStatus = new DiffStatus() + + this.ts?.lastValidateResult?.drift?.forEach(co => { + this.diffStatus.addChangedObject(co) + }) + } + + buildSidePanelTabs(): SidePanelTab[] { + if (!this.ts) { + return [] + } + + const tabs = [ + {label: "Summary", content: this.buildSummaryTab()} + ] + + if (this.ts.target) + + if (this.diffStatus.changedObjects.length) { + tabs.push({ + label: "Drift", + content: + }) + } + if (this.ts.lastValidateResult?.errors?.length) { + tabs.push({ + label: "Errors", + content: + }) + } + if (this.ts.lastValidateResult?.warnings?.length) { + tabs.push({ + label: "Warnings", + content: + }) + } + + return tabs + } + + buildSummaryTab(): React.ReactNode { + const props = [ + {name: "Target Name", value: this.getTargetName()}, + {name: "Discriminator", value: this.ts?.target.discriminator}, + ] + + if (this.ts?.lastValidateResult) { + props.push({name: "Ready", value: this.ts.lastValidateResult.ready + ""}) + } + if (this.ts?.lastValidateResult?.errors?.length) { + props.push({name: "Errors", value: this.ts.lastValidateResult.errors.length + ""}) + } + if (this.ts?.lastValidateResult?.warnings?.length) { + props.push({name: "Warnings", value: this.ts.lastValidateResult.warnings.length + ""}) + } + if (this.ts?.lastValidateResult?.drift?.length) { + props.push({name: "Drifted Objects", value: this.ts.lastValidateResult.drift.length + ""}) + } + + return <> + + + } + + getTargetName() { + if (!this.ts) { + return "" + } + + let name = "" + if (this.ts.target.targetName) { + name = this.ts.target.targetName + } + return name + } + + buildSidePanelTitle(): React.ReactNode { + return this.getTargetName() + } +} + +export const TargetDetailsDrawer = (props: { ts?: TargetSummary, onClose: () => void }) => { + return props.onClose()} + > + + + + +} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/targets-view/Targets.tsx b/pkg/webui/ui/src/components/targets-view/Targets.tsx new file mode 100644 index 000000000..b7b99849f --- /dev/null +++ b/pkg/webui/ui/src/components/targets-view/Targets.tsx @@ -0,0 +1,147 @@ +import { ProjectSummary, TargetSummary } from "../../models"; +import { ActionMenuItem, ActionsMenu } from "../ActionsMenu"; +import Paper from "@mui/material/Paper"; +import { Box, Typography } from "@mui/material"; +import React from "react"; +import { Jdenticon } from "../Jdenticon"; +import Tooltip from "@mui/material/Tooltip"; +import { Favorite, Fingerprint, HeartBroken, PublishedWithChanges, QuestionMark } from "@mui/icons-material"; +import { api } from "../../api"; + +const StatusIcon = (props: { ps: ProjectSummary, ts: TargetSummary }) => { + let icon: React.ReactElement + + if (props.ts.lastValidateResult === undefined) { + icon = + } else if (props.ts.lastValidateResult.ready && !props.ts.lastValidateResult.errors) { + if (props.ts.lastValidateResult.warnings?.length) { + icon = + } else if (props.ts.lastValidateResult.drift?.length) { + icon = + } else { + icon = + } + } else { + icon = + } + + const tooltip: string[] = [] + if (props.ts.lastValidateResult === undefined) { + tooltip.push("No validation result available.") + } else { + if (props.ts.lastValidateResult.ready && !props.ts.lastValidateResult.errors?.length) { + tooltip.push("Target is ready.") + } else { + tooltip.push("Target is not ready.") + } + if (props.ts.lastValidateResult.errors?.length) { + tooltip.push(`Target has ${props.ts.lastValidateResult.errors.length} validation errors.`) + } + if (props.ts.lastValidateResult.warnings?.length) { + tooltip.push(`Target has ${props.ts.lastValidateResult.warnings.length} validation warnings.`) + } + if (props.ts.lastValidateResult.drift?.length) { + tooltip.push(`Target has ${props.ts.lastValidateResult.drift.length} drifted objects.`) + } + } + + return {t})}> + {icon} + +} + +export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSelectTarget: (ts?: TargetSummary) => void }) => { + const actionMenuItems: ActionMenuItem[] = [] + + actionMenuItems.push({ + icon: , + text: "Validate now", + handler: () => { + api.validateNow(props.ps.project, props.ts.target) + } + }) + + const allContexts: string[] = [] + + const handleContext = (c?: string) => { + if (!c) { + return + } + if (allContexts.find(x => x === c)) { + return + } + allContexts.push(c) + } + + props.ts.commandResults?.forEach(rs => { + handleContext(rs.commandInfo.contextOverride) + handleContext(rs.target.context) + }) + + const contextTooltip = + All known contexts: + {allContexts.map((context, i) => ( + {context} + ))} + + + let targetName = props.ts.target.targetName + if (!targetName) { + targetName = "" + } + + return props.onSelectTarget(props.ts)} + > + + + + {targetName} + + {allContexts.length ? + + + {allContexts[0]} + + + + : <>} + + + + + +
    + +
    +
    + +
    + +
    +
    +
    + + + + +
    +
    +
    +
    +} diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx new file mode 100644 index 000000000..581d8ce82 --- /dev/null +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -0,0 +1,96 @@ +import { useLoaderData } from "react-router-dom"; +import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; +import { Box, Typography } from "@mui/material"; +import React, { useEffect, useState } from "react"; +import { useAppOutletContext } from "../App"; +import { api } from "../../api"; +import { ProjectItem } from "./Projects"; +import { TargetItem } from "./Targets"; +import Divider from "@mui/material/Divider"; +import { CommandResultItem } from "./CommandResultItem"; +import { CommandResultDetailsDrawer } from "./CommandResultDetailsDrawer"; +import { TargetDetailsDrawer } from "./TargetDetailsDrawer"; + +const targetWidth = "220px" +const targetHeight = "110px" + +export async function projectsLoader() { + const projects = await api.listProjects() + return projects +} + +export const TargetsView = () => { + const context = useAppOutletContext() + const [selectedCommandResult, setSelectedCommandResult] = useState() + const [selectedTargetSummary, setSelectedTargetSummary] = useState() + + const projects = useLoaderData() as ProjectSummary[]; + + useEffect(() => { + context.setFilters(undefined) + }) + + const doSetSelectedCommandResult = (rs?: CommandResultSummary) => { + setSelectedCommandResult(rs) + setSelectedTargetSummary(undefined) + } + const doSetSelectedTargetSummary = (ts?: TargetSummary) => { + setSelectedCommandResult(undefined) + setSelectedTargetSummary(ts) + } + + return + setSelectedCommandResult(undefined)}/> + setSelectedTargetSummary(undefined)}/> + + + Projects + + + + Targets + + + + History + + + + {projects.map((ps, i) => { + return + + + + + + + + + + {ps.targets.map((ts, i) => { + return + doSetSelectedTargetSummary(ts)}/> + + })} + + + + + {ps.targets.map((ts, i) => { + return + {ts.commandResults?.map((rs, i) => { + return + doSetSelectedCommandResult(rs)}/> + + })} + + })} + + + + + })} + +} diff --git a/pkg/webui/ui/src/icons/GitIcon.tsx b/pkg/webui/ui/src/icons/GitIcon.tsx new file mode 100644 index 000000000..6948ffe5a --- /dev/null +++ b/pkg/webui/ui/src/icons/GitIcon.tsx @@ -0,0 +1,6 @@ +import { ReactComponent as GitIconSvg } from './git.svg'; +import React from "react"; + +export const GitIcon = () => { + return +} diff --git a/pkg/webui/ui/src/icons/KluctlLogo.tsx b/pkg/webui/ui/src/icons/KluctlLogo.tsx new file mode 100644 index 000000000..b89f71f95 --- /dev/null +++ b/pkg/webui/ui/src/icons/KluctlLogo.tsx @@ -0,0 +1,6 @@ +import { ReactComponent as KluctlLogoSvg } from './kluctl-logo.svg'; +import React from "react"; + +export const KluctlLogo = () => { + return +} diff --git a/pkg/webui/ui/src/icons/git.svg b/pkg/webui/ui/src/icons/git.svg new file mode 100644 index 000000000..6b6a26a2b --- /dev/null +++ b/pkg/webui/ui/src/icons/git.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pkg/webui/ui/src/icons/kluctl-logo.svg b/pkg/webui/ui/src/icons/kluctl-logo.svg new file mode 100644 index 000000000..d61023e05 --- /dev/null +++ b/pkg/webui/ui/src/icons/kluctl-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pkg/webui/ui/src/index.css b/pkg/webui/ui/src/index.css new file mode 100644 index 000000000..63775a2e6 --- /dev/null +++ b/pkg/webui/ui/src/index.css @@ -0,0 +1,25 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} + +* { + box-sizing: border-box; +} + +html, body, #root { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + font-family: sans-serif; +} diff --git a/pkg/webui/ui/src/index.tsx b/pkg/webui/ui/src/index.tsx new file mode 100644 index 000000000..f7e132268 --- /dev/null +++ b/pkg/webui/ui/src/index.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { RouterProvider } from "react-router-dom"; + +import './index.css'; +import reportWebVitals from './reportWebVitals'; + +import '@fontsource/roboto/300.css'; +import '@fontsource/roboto/400.css'; +import '@fontsource/roboto/500.css'; +import '@fontsource/roboto/700.css'; + +import { Router } from "./components/Router"; + +const root = ReactDOM.createRoot( + document.getElementById('root') as HTMLElement +); +root.render( + + + +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/pkg/webui/ui/src/loadscript.ts b/pkg/webui/ui/src/loadscript.ts new file mode 100644 index 000000000..e8db35596 --- /dev/null +++ b/pkg/webui/ui/src/loadscript.ts @@ -0,0 +1,35 @@ +const loadedScripts = new Map() + +export const loadScript = (fileUrl: string, async = true, type = "text/javascript") => { + if (loadedScripts.has(fileUrl)) { + return loadedScripts.get(fileUrl) + } + + const p = new Promise((resolve, reject) => { + try { + const scriptEle = document.createElement("script"); + scriptEle.type = type; + scriptEle.async = async; + scriptEle.src = fileUrl; + + scriptEle.addEventListener("load", (ev) => { + resolve({ status: true }); + }); + + scriptEle.addEventListener("error", (ev) => { + reject({ + status: false, + message: `Failed to load the script ${FILE_URL}` + }); + }); + + document.body.appendChild(scriptEle); + } catch (error) { + reject(error); + } + }); + + loadedScripts.set(fileUrl, p) + + return p +}; diff --git a/pkg/webui/ui/src/logo.svg b/pkg/webui/ui/src/logo.svg new file mode 100644 index 000000000..9dfc1c058 --- /dev/null +++ b/pkg/webui/ui/src/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pkg/webui/ui/src/models.ts b/pkg/webui/ui/src/models.ts new file mode 100644 index 000000000..2aa413ce5 --- /dev/null +++ b/pkg/webui/ui/src/models.ts @@ -0,0 +1,900 @@ +/* Do not change, this code is generated from Golang structs */ + + +export class DeploymentError { + ref: ObjectRef; + message: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.ref = this.convertValues(source["ref"], ObjectRef); + this.message = source["message"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class Change { + type: string; + jsonPath: string; + oldValue?: any; + newValue?: any; + unifiedDiff?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.type = source["type"]; + this.jsonPath = source["jsonPath"]; + this.oldValue = source["oldValue"]; + this.newValue = source["newValue"]; + this.unifiedDiff = source["unifiedDiff"]; + } +} +export class ResultObject { + ref: ObjectRef; + changes?: Change[]; + new?: boolean; + orphan?: boolean; + deleted?: boolean; + hook?: boolean; + rendered?: any; + remote?: any; + applied?: any; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.ref = this.convertValues(source["ref"], ObjectRef); + this.changes = this.convertValues(source["changes"], Change); + this.new = source["new"]; + this.orphan = source["orphan"]; + this.deleted = source["deleted"]; + this.hook = source["hook"]; + this.rendered = source["rendered"]; + this.remote = source["remote"]; + this.applied = source["applied"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class IgnoreForDiffItemConfig { + fieldPath?: string[]; + fieldPathRegex?: string[]; + group?: string; + kind?: string; + name?: string; + namespace?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.fieldPath = source["fieldPath"]; + this.fieldPathRegex = source["fieldPathRegex"]; + this.group = source["group"]; + this.kind = source["kind"]; + this.name = source["name"]; + this.namespace = source["namespace"]; + } +} +export class HelmChartConfig { + repo?: string; + path?: string; + credentialsId?: string; + chartName?: string; + chartVersion?: string; + updateConstraints?: string; + releaseName: string; + namespace?: string; + output?: string; + skipCRDs?: boolean; + skipUpdate?: boolean; + skipPrePull?: boolean; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.repo = source["repo"]; + this.path = source["path"]; + this.credentialsId = source["credentialsId"]; + this.chartName = source["chartName"]; + this.chartVersion = source["chartVersion"]; + this.updateConstraints = source["updateConstraints"]; + this.releaseName = source["releaseName"]; + this.namespace = source["namespace"]; + this.output = source["output"]; + this.skipCRDs = source["skipCRDs"]; + this.skipUpdate = source["skipUpdate"]; + this.skipPrePull = source["skipPrePull"]; + } +} +export class DeleteObjectItemConfig { + group?: string; + kind?: string; + name: string; + namespace?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.group = source["group"]; + this.kind = source["kind"]; + this.name = source["name"]; + this.namespace = source["namespace"]; + } +} +export class GitProject { + url: string; + ref?: string; + subDir?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.url = source["url"]; + this.ref = source["ref"]; + this.subDir = source["subDir"]; + } +} +export class DeploymentItemConfig { + path?: string; + include?: string; + git?: GitProject; + tags?: string[]; + barrier?: boolean; + message?: string; + waitReadiness?: boolean; + vars?: VarsSource[]; + skipDeleteIfTags?: boolean; + onlyRender?: boolean; + alwaysDeploy?: boolean; + deleteObjects?: DeleteObjectItemConfig[]; + when?: string; + renderedHelmChartConfig?: HelmChartConfig; + renderedObjects?: ObjectRef[]; + renderedInclude?: DeploymentProjectConfig; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.path = source["path"]; + this.include = source["include"]; + this.git = this.convertValues(source["git"], GitProject); + this.tags = source["tags"]; + this.barrier = source["barrier"]; + this.message = source["message"]; + this.waitReadiness = source["waitReadiness"]; + this.vars = this.convertValues(source["vars"], VarsSource); + this.skipDeleteIfTags = source["skipDeleteIfTags"]; + this.onlyRender = source["onlyRender"]; + this.alwaysDeploy = source["alwaysDeploy"]; + this.deleteObjects = this.convertValues(source["deleteObjects"], DeleteObjectItemConfig); + this.when = source["when"]; + this.renderedHelmChartConfig = this.convertValues(source["renderedHelmChartConfig"], HelmChartConfig); + this.renderedObjects = this.convertValues(source["renderedObjects"], ObjectRef); + this.renderedInclude = this.convertValues(source["renderedInclude"], DeploymentProjectConfig); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class SealedSecretsConfig { + outputPattern?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.outputPattern = source["outputPattern"]; + } +} +export class VarsSourceVault { + address: string; + path: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.address = source["address"]; + this.path = source["path"]; + } +} +export class VarsSourceAwsSecretsManager { + secretName: string; + region?: string; + profile?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.secretName = source["secretName"]; + this.region = source["region"]; + this.profile = source["profile"]; + } +} +export class VarsSourceHttp { + url?: string; + method?: string; + body?: string; + headers?: {[key: string]: string}; + jsonPath?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.url = source["url"]; + this.method = source["method"]; + this.body = source["body"]; + this.headers = source["headers"]; + this.jsonPath = source["jsonPath"]; + } +} +export class VarsSourceClusterConfigMapOrSecret { + name?: string; + labels?: {[key: string]: string}; + namespace: string; + key: string; + targetPath?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.name = source["name"]; + this.labels = source["labels"]; + this.namespace = source["namespace"]; + this.key = source["key"]; + this.targetPath = source["targetPath"]; + } +} +export class VarsSourceGit { + url: string; + ref?: string; + path: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.url = source["url"]; + this.ref = source["ref"]; + this.path = source["path"]; + } +} +export class VarsSource { + ignoreMissing?: boolean; + noOverride?: boolean; + values?: any; + file?: string; + git?: VarsSourceGit; + clusterConfigMap?: VarsSourceClusterConfigMapOrSecret; + clusterSecret?: VarsSourceClusterConfigMapOrSecret; + systemEnvVars?: any; + http?: VarsSourceHttp; + awsSecretsManager?: VarsSourceAwsSecretsManager; + vault?: VarsSourceVault; + when?: string; + renderedVars?: any; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.ignoreMissing = source["ignoreMissing"]; + this.noOverride = source["noOverride"]; + this.values = source["values"]; + this.file = source["file"]; + this.git = this.convertValues(source["git"], VarsSourceGit); + this.clusterConfigMap = this.convertValues(source["clusterConfigMap"], VarsSourceClusterConfigMapOrSecret); + this.clusterSecret = this.convertValues(source["clusterSecret"], VarsSourceClusterConfigMapOrSecret); + this.systemEnvVars = source["systemEnvVars"]; + this.http = this.convertValues(source["http"], VarsSourceHttp); + this.awsSecretsManager = this.convertValues(source["awsSecretsManager"], VarsSourceAwsSecretsManager); + this.vault = this.convertValues(source["vault"], VarsSourceVault); + this.when = source["when"]; + this.renderedVars = source["renderedVars"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class DeploymentProjectConfig { + vars?: VarsSource[]; + sealedSecrets?: SealedSecretsConfig; + when?: string; + deployments?: DeploymentItemConfig[]; + commonLabels?: {[key: string]: string}; + commonAnnotations?: {[key: string]: string}; + overrideNamespace?: string; + tags?: string[]; + ignoreForDiff?: IgnoreForDiffItemConfig[]; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.vars = this.convertValues(source["vars"], VarsSource); + this.sealedSecrets = this.convertValues(source["sealedSecrets"], SealedSecretsConfig); + this.when = source["when"]; + this.deployments = this.convertValues(source["deployments"], DeploymentItemConfig); + this.commonLabels = source["commonLabels"]; + this.commonAnnotations = source["commonAnnotations"]; + this.overrideNamespace = source["overrideNamespace"]; + this.tags = source["tags"]; + this.ignoreForDiff = this.convertValues(source["ignoreForDiff"], IgnoreForDiffItemConfig); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class ClusterInfo { + clusterId: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.clusterId = source["clusterId"]; + } +} +export class GitInfo { + url?: string; + ref: string; + subDir: string; + commit: string; + dirty: boolean; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.url = source["url"]; + this.ref = source["ref"]; + this.subDir = source["subDir"]; + this.commit = source["commit"]; + this.dirty = source["dirty"]; + } +} +export class KluctlDeploymentInfo { + name: string; + namespace: string; + gitUrl: string; + gitRef: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.name = source["name"]; + this.namespace = source["namespace"]; + this.gitUrl = source["gitUrl"]; + this.gitRef = source["gitRef"]; + } +} +export class CommandInfo { + initiator: string; + startTime: string; + endTime: string; + kluctlDeployment?: KluctlDeploymentInfo; + command?: string; + target?: string; + targetNameOverride?: string; + contextOverride?: string; + args?: any; + images?: FixedImage[]; + dryRun?: boolean; + noWait?: boolean; + forceApply?: boolean; + replaceOnError?: boolean; + forceReplaceOnError?: boolean; + abortOnError?: boolean; + includeTags?: string[]; + excludeTags?: string[]; + includeDeploymentDirs?: string[]; + excludeDeploymentDirs?: string[]; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.initiator = source["initiator"]; + this.startTime = source["startTime"]; + this.endTime = source["endTime"]; + this.kluctlDeployment = this.convertValues(source["kluctlDeployment"], KluctlDeploymentInfo); + this.command = source["command"]; + this.target = source["target"]; + this.targetNameOverride = source["targetNameOverride"]; + this.contextOverride = source["contextOverride"]; + this.args = source["args"]; + this.images = this.convertValues(source["images"], FixedImage); + this.dryRun = source["dryRun"]; + this.noWait = source["noWait"]; + this.forceApply = source["forceApply"]; + this.replaceOnError = source["replaceOnError"]; + this.forceReplaceOnError = source["forceReplaceOnError"]; + this.abortOnError = source["abortOnError"]; + this.includeTags = source["includeTags"]; + this.excludeTags = source["excludeTags"]; + this.includeDeploymentDirs = source["includeDeploymentDirs"]; + this.excludeDeploymentDirs = source["excludeDeploymentDirs"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class ObjectRef { + group?: string; + version?: string; + kind: string; + name: string; + namespace?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.group = source["group"]; + this.version = source["version"]; + this.kind = source["kind"]; + this.name = source["name"]; + this.namespace = source["namespace"]; + } +} +export class FixedImage { + image: string; + resultImage: string; + deployedImage?: string; + namespace?: string; + object?: ObjectRef; + deployment?: string; + container?: string; + deployTags?: string[]; + deploymentDir?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.image = source["image"]; + this.resultImage = source["resultImage"]; + this.deployedImage = source["deployedImage"]; + this.namespace = source["namespace"]; + this.object = this.convertValues(source["object"], ObjectRef); + this.deployment = source["deployment"]; + this.container = source["container"]; + this.deployTags = source["deployTags"]; + this.deploymentDir = source["deploymentDir"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class SealingConfig { + args?: any; + secretSets?: string[]; + certFile?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.args = source["args"]; + this.secretSets = source["secretSets"]; + this.certFile = source["certFile"]; + } +} +export class Target { + name: string; + context?: string; + args?: any; + sealingConfig?: SealingConfig; + images?: FixedImage[]; + discriminator?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.name = source["name"]; + this.context = source["context"]; + this.args = source["args"]; + this.sealingConfig = this.convertValues(source["sealingConfig"], SealingConfig); + this.images = this.convertValues(source["images"], FixedImage); + this.discriminator = source["discriminator"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class TargetKey { + targetName?: string; + clusterId: string; + discriminator?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.targetName = source["targetName"]; + this.clusterId = source["clusterId"]; + this.discriminator = source["discriminator"]; + } +} +export class ProjectKey { + gitRepoKey?: string; + subDir?: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.gitRepoKey = source["gitRepoKey"]; + this.subDir = source["subDir"]; + } +} +export class CommandResult { + id: string; + projectKey: ProjectKey; + targetKey: TargetKey; + target: Target; + command?: CommandInfo; + gitInfo?: GitInfo; + clusterInfo: ClusterInfo; + deployment?: DeploymentProjectConfig; + objects?: ResultObject[]; + errors?: DeploymentError[]; + warnings?: DeploymentError[]; + seenImages?: FixedImage[]; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.id = source["id"]; + this.projectKey = this.convertValues(source["projectKey"], ProjectKey); + this.targetKey = this.convertValues(source["targetKey"], TargetKey); + this.target = this.convertValues(source["target"], Target); + this.command = this.convertValues(source["command"], CommandInfo); + this.gitInfo = this.convertValues(source["gitInfo"], GitInfo); + this.clusterInfo = this.convertValues(source["clusterInfo"], ClusterInfo); + this.deployment = this.convertValues(source["deployment"], DeploymentProjectConfig); + this.objects = this.convertValues(source["objects"], ResultObject); + this.errors = this.convertValues(source["errors"], DeploymentError); + this.warnings = this.convertValues(source["warnings"], DeploymentError); + this.seenImages = this.convertValues(source["seenImages"], FixedImage); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class CommandResultSummary { + id: string; + projectKey: ProjectKey; + targetKey: TargetKey; + target: Target; + commandInfo: CommandInfo; + gitInfo?: GitInfo; + clusterInfo?: ClusterInfo; + renderedObjects: number; + remoteObjects: number; + appliedObjects: number; + appliedHookObjects: number; + newObjects: number; + changedObjects: number; + orphanObjects: number; + deletedObjects: number; + errors: number; + warnings: number; + totalChanges: number; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.id = source["id"]; + this.projectKey = this.convertValues(source["projectKey"], ProjectKey); + this.targetKey = this.convertValues(source["targetKey"], TargetKey); + this.target = this.convertValues(source["target"], Target); + this.commandInfo = this.convertValues(source["commandInfo"], CommandInfo); + this.gitInfo = this.convertValues(source["gitInfo"], GitInfo); + this.clusterInfo = this.convertValues(source["clusterInfo"], ClusterInfo); + this.renderedObjects = source["renderedObjects"]; + this.remoteObjects = source["remoteObjects"]; + this.appliedObjects = source["appliedObjects"]; + this.appliedHookObjects = source["appliedHookObjects"]; + this.newObjects = source["newObjects"]; + this.changedObjects = source["changedObjects"]; + this.orphanObjects = source["orphanObjects"]; + this.deletedObjects = source["deletedObjects"]; + this.errors = source["errors"]; + this.warnings = source["warnings"]; + this.totalChanges = source["totalChanges"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class ChangedObject { + ref: ObjectRef; + changes?: Change[]; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.ref = this.convertValues(source["ref"], ObjectRef); + this.changes = this.convertValues(source["changes"], Change); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class ValidateResultEntry { + ref: ObjectRef; + annotation: string; + message: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.ref = this.convertValues(source["ref"], ObjectRef); + this.annotation = source["annotation"]; + this.message = source["message"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class ValidateResult { + id: string; + startTime: string; + endTime: string; + ready: boolean; + warnings?: DeploymentError[]; + errors?: DeploymentError[]; + results?: ValidateResultEntry[]; + drift?: ChangedObject[]; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.id = source["id"]; + this.startTime = source["startTime"]; + this.endTime = source["endTime"]; + this.ready = source["ready"]; + this.warnings = this.convertValues(source["warnings"], DeploymentError); + this.errors = this.convertValues(source["errors"], DeploymentError); + this.results = this.convertValues(source["results"], ValidateResultEntry); + this.drift = this.convertValues(source["drift"], ChangedObject); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class TargetSummary { + target: TargetKey; + lastValidateResult?: ValidateResult; + commandResults?: CommandResultSummary[]; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.target = this.convertValues(source["target"], TargetKey); + this.lastValidateResult = this.convertValues(source["lastValidateResult"], ValidateResult); + this.commandResults = this.convertValues(source["commandResults"], CommandResultSummary); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +export class ProjectSummary { + project: ProjectKey; + targets: TargetSummary[]; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.project = this.convertValues(source["project"], ProjectKey); + this.targets = this.convertValues(source["targets"], TargetSummary); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} + +export class ShortName { + group?: string; + kind: string; + shortName: string; + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.group = source["group"]; + this.kind = source["kind"]; + this.shortName = source["shortName"]; + } +} +export class UnstructuredObject { + + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + + } +} \ No newline at end of file diff --git a/pkg/webui/ui/src/react-app-env.d.ts b/pkg/webui/ui/src/react-app-env.d.ts new file mode 100644 index 000000000..6431bc5fc --- /dev/null +++ b/pkg/webui/ui/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/pkg/webui/ui/src/reportWebVitals.ts b/pkg/webui/ui/src/reportWebVitals.ts new file mode 100644 index 000000000..ffeb31f00 --- /dev/null +++ b/pkg/webui/ui/src/reportWebVitals.ts @@ -0,0 +1,15 @@ +import { ReportHandler } from 'web-vitals'; + +const reportWebVitals = (onPerfEntry?: ReportHandler) => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({getCLS, getFID, getFCP, getLCP, getTTFB}) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/pkg/webui/ui/src/setupTests.ts b/pkg/webui/ui/src/setupTests.ts new file mode 100644 index 000000000..b28b91057 --- /dev/null +++ b/pkg/webui/ui/src/setupTests.ts @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; \ No newline at end of file diff --git a/pkg/webui/ui/src/staticbuild.d.ts b/pkg/webui/ui/src/staticbuild.d.ts new file mode 100644 index 000000000..511b2c96a --- /dev/null +++ b/pkg/webui/ui/src/staticbuild.d.ts @@ -0,0 +1,7 @@ +declare var isStaticBuild: bool; + +declare const staticResults: Map; + +declare const staticShortNames: any[]; +declare const staticProjects: any[]; +declare const staticSummaries: any[]; diff --git a/pkg/webui/ui/src/utils/duration.ts b/pkg/webui/ui/src/utils/duration.ts new file mode 100644 index 000000000..5dd95c4dc --- /dev/null +++ b/pkg/webui/ui/src/utils/duration.ts @@ -0,0 +1,30 @@ +export function formatDuration(ms: number, withMs?: boolean) { + if (ms < 0) ms = -ms; + const time = { + day: Math.floor(ms / 86400000), + hour: Math.floor(ms / 3600000) % 24, + minute: Math.floor(ms / 60000) % 60, + second: Math.floor(ms / 1000) % 60, + millisecond: withMs ? Math.floor(ms) % 1000 : 0 + }; + return Object.entries(time) + .filter(val => val[1] !== 0) + .map(val => val[1] + ' ' + (val[1] !== 1 ? val[0] + 's' : val[0])) + .join(', '); +} + +export function formatDurationShort(ms: number) { + if (ms < 0) ms = -ms; + const time = { + d: Math.floor(ms / 86400000), + h: Math.floor(ms / 3600000) % 24, + m: Math.floor(ms / 60000) % 60, + s: Math.floor(ms / 1000) % 60, + ms: Math.floor(ms) % 1000 + }; + const f = Object.entries(time).find(val => val[1] > 0) + if (f === undefined) { + return "0s" + } + return f[1] + f[0] +} \ No newline at end of file diff --git a/pkg/webui/ui/src/utils/misc.ts b/pkg/webui/ui/src/utils/misc.ts new file mode 100644 index 000000000..5543c1436 --- /dev/null +++ b/pkg/webui/ui/src/utils/misc.ts @@ -0,0 +1,14 @@ +export function getLastPathElement(url?: string): string | undefined { + if (url === undefined) { + return undefined + } + if (!url) { + return "" + } + const s= url.split("/") + return s[s.length - 1] +} + +export function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/pkg/webui/ui/tsconfig.json b/pkg/webui/ui/tsconfig.json new file mode 100644 index 000000000..a273b0cfc --- /dev/null +++ b/pkg/webui/ui/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src" + ] +} diff --git a/pkg/webui/validator.go b/pkg/webui/validator.go new file mode 100644 index 000000000..78ba366bf --- /dev/null +++ b/pkg/webui/validator.go @@ -0,0 +1,223 @@ +package webui + +import ( + "context" + "github.com/kluctl/kluctl/v2/pkg/deployment/commands" + "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/status" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "sync" + "time" +) + +const shortValidationInterval = time.Second * 15 +const longValidationInterval = time.Minute * 5 + +type validatorManager struct { + ctx context.Context + + store results.ResultStore + cam *clusterAccessorManager + + validators map[projectTargetKey]*validatorEntry + mutex sync.Mutex +} + +type projectTargetKey struct { + Project result.ProjectKey `json:"project"` + Target result.TargetKey `json:"target"` +} + +type validatorEntry struct { + vm *validatorManager + key projectTargetKey + ch chan bool + validateResult *result.ValidateResult + err error + mutex sync.Mutex +} + +func newValidatorManager(ctx context.Context, store results.ResultStore, cam *clusterAccessorManager) *validatorManager { + return &validatorManager{ + ctx: ctx, + store: store, + cam: cam, + validators: map[projectTargetKey]*validatorEntry{}, + } +} + +func (vm *validatorManager) start() { + runOnce := func() { + summaries, err := vm.store.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + if err != nil { + return + } + + projects := result.BuildProjectSummaries(summaries) + + found := map[projectTargetKey]bool{} + for _, project := range projects { + for _, target := range project.Targets { + k := projectTargetKey{ + Project: project.Project, + Target: target.Target, + } + + vm.mutex.Lock() + _, ok := vm.validators[k] + if !ok { + v := &validatorEntry{ + vm: vm, + key: k, + ch: make(chan bool), + } + vm.validators[k] = v + go v.run() + } + vm.mutex.Unlock() + + found[k] = true + } + } + + vm.mutex.Lock() + for k, v := range vm.validators { + if _, ok := found[k]; !ok { + delete(vm.validators, k) + close(v.ch) + } + } + vm.mutex.Unlock() + } + + go func() { + for { + runOnce() + time.Sleep(5 * time.Second) + } + }() +} + +func (vm *validatorManager) getValidateResult(key projectTargetKey) (*result.ValidateResult, error) { + vm.mutex.Lock() + defer vm.mutex.Unlock() + v := vm.validators[key] + if v == nil { + return nil, nil + } + return v.validateResult, v.err +} + +func (vm *validatorManager) validateNow(key projectTargetKey) bool { + vm.mutex.Lock() + defer vm.mutex.Unlock() + v := vm.validators[key] + if v == nil { + return false + } + v.ch <- true + return true +} + +func (v *validatorEntry) run() { + status.Info(v.vm.ctx, "Started validator for: %v", v.key) + defer status.Info(v.vm.ctx, "Stopped validator for: %v", v.key) + + for { + summaries, err := v.vm.store.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{ + ProjectFilter: &v.key.Project, + }) + if err != nil { + return + } + projects := result.BuildProjectSummaries(summaries) + + longWait := false + outer: + for _, p := range projects { + if p.Project == v.key.Project { + for _, t := range p.Targets { + if t.Target == v.key.Target { + longWait = v.runOnce(t) + break outer + } + } + } + } + + waitTime := shortValidationInterval + if longWait { + waitTime = longValidationInterval + } + + select { + case _, ok := <-v.ch: + if !ok { + break + } + continue + case <-time.After(waitTime): + } + } +} + +func (v *validatorEntry) findFullProjectResult(summaries []result.CommandResultSummary) *result.CommandResultSummary { + for _, summary := range summaries { + if len(summary.Command.IncludeTags) != 0 || len(summary.Command.ExcludeTags) != 0 { + continue + } + if summary.Command.Command == "deploy" || summary.Command.Command == "diff" { + return &summary + } + } + return nil +} + +func (v *validatorEntry) runOnce(target *result.TargetSummary) bool { + s := status.Start(v.vm.ctx, "Validating: %v", v.key) + defer s.Failed() + + summary := v.findFullProjectResult(target.CommandResults) + if summary == nil { + s.FailedWithMessage("No result summaries for %v", v.key) + return false + } + + ca := v.vm.cam.getForClusterId(summary.ClusterInfo.ClusterId) + if ca == nil { + s.FailedWithMessage("No cluster accessor for %v", v.key) + return false + } + k := ca.getK() + if k == nil { + return false + } + + cr, err := v.vm.store.GetCommandResult(results.GetCommandResultOptions{ + Id: summary.Id, + Reduced: false, + }) + if err != nil { + s.FailedWithMessage("Failed to get command result for %v: %v", v.key, err) + return false + } + + discriminator := summary.Target.Discriminator + + cmd := commands.NewValidateCommand(v.vm.ctx, discriminator, nil, cr) + vr, err := cmd.Run(v.vm.ctx, k) + + if err != nil { + s.UpdateAndInfoFallback("Finished validation with error: %v", v.key) + } else { + s.UpdateAndInfoFallback("Finished validation: %v", v.key) + } + s.Success() + + v.mutex.Lock() + defer v.mutex.Unlock() + v.validateResult = vr + v.err = err + + return true +} From 5970e0cacb2499e5c89d355b84a234ef31c6116c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 30 May 2023 13:31:25 +0200 Subject: [PATCH 1127/2268] fix: Server ui from / --- pkg/webui/server.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/webui/server.go b/pkg/webui/server.go index 746f3b0da..8d854718b 100644 --- a/pkg/webui/server.go +++ b/pkg/webui/server.go @@ -10,6 +10,7 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" + "io/fs" "k8s.io/client-go/rest" "net" "net/http" @@ -52,7 +53,30 @@ func (s *CommandResultsServer) Run(port int) error { router := gin.Default() - router.StaticFS("/webui", http.FS(uiFS)) + dis, err := fs.ReadDir(uiFS, ".") + if err != nil { + return err + } + + for _, di := range dis { + if di.IsDir() { + x, err := fs.Sub(uiFS, di.Name()) + if err != nil { + return err + } + router.StaticFS("/"+di.Name(), http.FS(x)) + } else { + router.StaticFileFS("/"+di.Name(), di.Name(), http.FS(uiFS)) + } + } + router.GET("/", func(c *gin.Context) { + b, err := fs.ReadFile(uiFS, "index.html") + if err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, err) + return + } + c.Data(http.StatusOK, "text/html; charset=utf-8", b) + }) api := router.Group("/api") api.GET("/getShortNames", s.getShortNames) From 6dd85526abcf77f468445649715487c35cbba107 Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Sat, 27 May 2023 00:12:05 +0600 Subject: [PATCH 1128/2268] WIP Main Page. --- pkg/webui/ui/public/staticbuild.js | 2 +- pkg/webui/ui/src/components/App.tsx | 13 +- pkg/webui/ui/src/components/LeftDrawer.tsx | 115 +++++++++--------- .../components/targets-view/TargetsView.tsx | 2 +- pkg/webui/ui/src/components/theme.ts | 72 +++++++++++ pkg/webui/ui/src/icons/KluctlLogo.tsx | 2 +- pkg/webui/ui/src/icons/KluctlText.tsx | 6 + pkg/webui/ui/src/icons/Targets.tsx | 5 + pkg/webui/ui/src/icons/kluctl-logo.svg | 4 +- pkg/webui/ui/src/icons/kluctl-text.svg | 7 ++ pkg/webui/ui/src/icons/targets.svg | 6 + pkg/webui/ui/src/index.css | 2 + 12 files changed, 172 insertions(+), 64 deletions(-) create mode 100644 pkg/webui/ui/src/components/theme.ts create mode 100644 pkg/webui/ui/src/icons/KluctlText.tsx create mode 100644 pkg/webui/ui/src/icons/Targets.tsx create mode 100644 pkg/webui/ui/src/icons/kluctl-text.svg create mode 100644 pkg/webui/ui/src/icons/targets.svg diff --git a/pkg/webui/ui/public/staticbuild.js b/pkg/webui/ui/public/staticbuild.js index 58271d740..a653e44b6 100644 --- a/pkg/webui/ui/public/staticbuild.js +++ b/pkg/webui/ui/public/staticbuild.js @@ -1,2 +1,2 @@ -let isStaticBuild = false; +let isStaticBuild = true; const staticResults = new Map() diff --git a/pkg/webui/ui/src/components/App.tsx b/pkg/webui/ui/src/components/App.tsx index ce3a71aa9..ba36ddc4a 100644 --- a/pkg/webui/ui/src/components/App.tsx +++ b/pkg/webui/ui/src/components/App.tsx @@ -1,9 +1,10 @@ import React, { useState } from 'react'; import '../index.css'; -import { Box } from "@mui/material"; +import { Box, ThemeProvider } from "@mui/material"; import { Outlet, useOutletContext } from "react-router-dom"; import LeftDrawer from "./LeftDrawer"; +import { light } from './theme'; export interface AppOutletContext { filters?: React.ReactNode @@ -22,9 +23,13 @@ const App = () => { setFilters: setFilters, } - return - } context={context}/> - + return ( + + + } context={context} /> + + + ); }; export default App; diff --git a/pkg/webui/ui/src/components/LeftDrawer.tsx b/pkg/webui/ui/src/components/LeftDrawer.tsx index 7c23a7be3..cd712c4b5 100644 --- a/pkg/webui/ui/src/components/LeftDrawer.tsx +++ b/pkg/webui/ui/src/components/LeftDrawer.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useState } from 'react'; -import { CSSObject, styled, Theme, useTheme } from '@mui/material/styles'; +import { CSSObject, styled, Theme, ThemeProvider, useTheme } from '@mui/material/styles'; import Box from '@mui/material/Box'; import MuiDrawer from '@mui/material/Drawer'; import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar'; @@ -15,15 +15,19 @@ import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; -import { Adjust } from "@mui/icons-material"; import { Link } from "react-router-dom"; import { AppOutletContext } from "./App"; import { KluctlLogo } from "../icons/KluctlLogo"; +import { Targets } from '../icons/Targets'; +import { dark } from './theme'; +import { KluctlText } from '../icons/KluctlText'; +import { Typography } from '@mui/material'; -const drawerWidth = 240; +const drawerWidthOpen = 224; +const drawerWidthClosed = 96; const openedMixin = (theme: Theme): CSSObject => ({ - width: drawerWidth, + width: drawerWidthOpen, transition: theme.transitions.create('width', { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, @@ -37,17 +41,12 @@ const closedMixin = (theme: Theme): CSSObject => ({ duration: theme.transitions.duration.leavingScreen, }), overflowX: 'hidden', - width: `calc(${theme.spacing(7)} + 1px)`, - [theme.breakpoints.up('sm')]: { - width: `calc(${theme.spacing(8)} + 1px)`, - }, + width: drawerWidthClosed, }); const DrawerHeader = styled('div')(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - justifyContent: 'flex-end', - padding: theme.spacing(0, 1), + height: '106px', + padding: '31px 23px 0 23px', // necessary for content to be below app bar ...theme.mixins.toolbar, })); @@ -59,14 +58,22 @@ interface AppBarProps extends MuiAppBarProps { const AppBar = styled(MuiAppBar, { shouldForwardProp: (prop) => prop !== 'open', })(({ theme, open }) => ({ + height: 106, + border: 'none', + boxShadow: 'none', + background: 'transparent', + padding: '40px 40px 0 40px', + marginLeft: drawerWidthClosed, + justifyContent: 'space-between', + width: `calc(100% - ${drawerWidthClosed}px)`, zIndex: theme.zIndex.drawer + 1, transition: theme.transitions.create(['width', 'margin'], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), ...(open && { - marginLeft: drawerWidth, - width: `calc(100% - ${drawerWidth}px)`, + marginLeft: drawerWidthOpen, + width: `calc(100% - ${drawerWidthOpen}px)`, transition: theme.transitions.create(['width', 'margin'], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, @@ -76,17 +83,24 @@ const AppBar = styled(MuiAppBar, { const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })( ({ theme, open }) => ({ - width: drawerWidth, + width: drawerWidthOpen, flexShrink: 0, whiteSpace: 'nowrap', boxSizing: 'border-box', + borderRadius: '0px 20px 20px 0px', ...(open && { ...openedMixin(theme), - '& .MuiDrawer-paper': openedMixin(theme), + '& .MuiDrawer-paper': { + ...openedMixin(theme), + borderRadius: '0px 20px 20px 0px', + } }), ...(!open && { ...closedMixin(theme), - '& .MuiDrawer-paper': closedMixin(theme), + '& .MuiDrawer-paper': { + ...closedMixin(theme), + borderRadius: '0px 20px 20px 0px', + } }), }), ); @@ -97,7 +111,7 @@ function Item(props: { text: string, open: boolean, icon: React.ReactNode, to: s sx={{ minHeight: 48, justifyContent: props.open ? 'initial' : 'center', - px: 2.5, + px: '24px', }} > {props.icon} - + } @@ -117,52 +131,41 @@ function Item(props: { text: string, open: boolean, icon: React.ReactNode, to: s export default function LeftDrawer(props: { content: React.ReactNode, context: AppOutletContext }) { const context = props.context - const theme = useTheme(); const [open, setOpen] = useState(true); - const handleDrawerOpen = () => { - setOpen(true); - }; - - const handleDrawerClose = () => { - setOpen(false); + const toggleDrawer = () => { + setOpen(o => !o); }; return ( - - - - - - + + Dashboard + + - - - - {theme.direction === 'rtl' ? : } - - - - - } to={"targets"}/> - - {context.filters} - - - + + + + + + {open && } + + + + + } to={"targets"} /> + + {context.filters} + + {context.filters && } + + - + {props.content} diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 581d8ce82..3f44afc91 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -39,7 +39,7 @@ export const TargetsView = () => { setSelectedTargetSummary(ts) } - return + return setSelectedCommandResult(undefined)}/> setSelectedTargetSummary(undefined)}/> diff --git a/pkg/webui/ui/src/components/theme.ts b/pkg/webui/ui/src/components/theme.ts new file mode 100644 index 000000000..a552fab79 --- /dev/null +++ b/pkg/webui/ui/src/components/theme.ts @@ -0,0 +1,72 @@ +import { PaletteOptions, createTheme } from '@mui/material/styles'; + +const paletteDark = { + primary: { main: '#DFEBE9' }, + background: { default: '#222222', paper: '#222222' } +} satisfies PaletteOptions; + +const paletteLight = { + primary: { main: '#222222' }, + secondary: { main: '#39403E'} +} satisfies PaletteOptions; + +export const light = createTheme({ + palette: paletteLight, + components: { + MuiDivider: { + styleOverrides: { + root: { + borderColor: 'rgba(0, 0, 0, 0.5)' + } + } + }, + MuiAppBar: { + styleOverrides: { + root: { + color: paletteLight.primary.main + } + } + } + } +}); + +export const dark = createTheme({ + palette: paletteDark, + components: { + MuiListItem: { + styleOverrides: { + root: { + color: paletteDark.primary.main, + background: paletteDark.background.default + } + } + }, + MuiButtonBase: { + styleOverrides: { + root: { + color: paletteDark.primary.main, + background: paletteDark.background.default + } + } + }, + MuiDrawer: { + styleOverrides: { + root: { + border: 'none' + }, + paper: { + border: 'none' + }, + } + }, + MuiDivider: { + styleOverrides: { + root: { + background: paletteDark.primary.main, + opacity: 0.2, + margin: '0 13px' + } + } + } + } +}) \ No newline at end of file diff --git a/pkg/webui/ui/src/icons/KluctlLogo.tsx b/pkg/webui/ui/src/icons/KluctlLogo.tsx index b89f71f95..f7c3d0665 100644 --- a/pkg/webui/ui/src/icons/KluctlLogo.tsx +++ b/pkg/webui/ui/src/icons/KluctlLogo.tsx @@ -2,5 +2,5 @@ import { ReactComponent as KluctlLogoSvg } from './kluctl-logo.svg'; import React from "react"; export const KluctlLogo = () => { - return + return } diff --git a/pkg/webui/ui/src/icons/KluctlText.tsx b/pkg/webui/ui/src/icons/KluctlText.tsx new file mode 100644 index 000000000..6122532ab --- /dev/null +++ b/pkg/webui/ui/src/icons/KluctlText.tsx @@ -0,0 +1,6 @@ +import { ReactComponent as KluctlTextSvg } from './kluctl-text.svg'; +import React from "react"; + +export const KluctlText = () => { + return +} diff --git a/pkg/webui/ui/src/icons/Targets.tsx b/pkg/webui/ui/src/icons/Targets.tsx new file mode 100644 index 000000000..d1ba0a421 --- /dev/null +++ b/pkg/webui/ui/src/icons/Targets.tsx @@ -0,0 +1,5 @@ +import { ReactComponent as TargetsSvg } from './targets.svg'; + +export const Targets = () => { + return +} diff --git a/pkg/webui/ui/src/icons/kluctl-logo.svg b/pkg/webui/ui/src/icons/kluctl-logo.svg index d61023e05..2248dffe2 100644 --- a/pkg/webui/ui/src/icons/kluctl-logo.svg +++ b/pkg/webui/ui/src/icons/kluctl-logo.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/pkg/webui/ui/src/icons/kluctl-text.svg b/pkg/webui/ui/src/icons/kluctl-text.svg new file mode 100644 index 000000000..ec60048e9 --- /dev/null +++ b/pkg/webui/ui/src/icons/kluctl-text.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pkg/webui/ui/src/icons/targets.svg b/pkg/webui/ui/src/icons/targets.svg new file mode 100644 index 000000000..e6a6ad8c9 --- /dev/null +++ b/pkg/webui/ui/src/icons/targets.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pkg/webui/ui/src/index.css b/pkg/webui/ui/src/index.css index 63775a2e6..738f22103 100644 --- a/pkg/webui/ui/src/index.css +++ b/pkg/webui/ui/src/index.css @@ -5,6 +5,8 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + background: linear-gradient(180deg, #59A588 0%, #404846 100%); + background-attachment: fixed; } code { From 5b463de1026cdd4866ab3c38bfc7df8dd2544cab Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Tue, 30 May 2023 01:57:55 +0600 Subject: [PATCH 1129/2268] WIP Main Page. --- pkg/webui/ui/package-lock.json | 10 +- pkg/webui/ui/package.json | 2 +- pkg/webui/ui/src/components/ActionsMenu.tsx | 5 +- pkg/webui/ui/src/components/LeftDrawer.tsx | 64 ++++++--- pkg/webui/ui/src/components/Router.tsx | 2 +- .../result-view/CommandResultStatusLine.tsx | 36 +++-- .../nodes/DeploymentItemIncludeNode.tsx | 2 +- .../result-view/nodes/VarsSourceNode.tsx | 2 +- .../targets-view/CommandResultItem.tsx | 88 +++++++++---- .../src/components/targets-view/Projects.tsx | 54 +++++--- .../src/components/targets-view/Targets.tsx | 123 ++++++++++-------- .../components/targets-view/TargetsView.tsx | 120 +++++++++++------ pkg/webui/ui/src/components/theme.ts | 18 ++- pkg/webui/ui/src/icons/GitIcon.tsx | 6 - pkg/webui/ui/src/icons/Icons.tsx | 64 +++++++++ pkg/webui/ui/src/icons/KluctlLogo.tsx | 6 - pkg/webui/ui/src/icons/KluctlText.tsx | 6 - pkg/webui/ui/src/icons/Targets.tsx | 5 - pkg/webui/ui/src/icons/cpu.svg | 16 +++ pkg/webui/ui/src/icons/finger-scan.svg | 8 ++ pkg/webui/ui/src/icons/project.svg | 5 + pkg/webui/ui/src/icons/relation-hline.svg | 5 + pkg/webui/ui/src/icons/search.svg | 4 + pkg/webui/ui/src/icons/target.svg | 5 + pkg/webui/ui/src/icons/tree-view.svg | 7 + pkg/webui/ui/src/index.css | 22 ++-- pkg/webui/ui/src/index.tsx | 5 +- 27 files changed, 465 insertions(+), 225 deletions(-) delete mode 100644 pkg/webui/ui/src/icons/GitIcon.tsx create mode 100644 pkg/webui/ui/src/icons/Icons.tsx delete mode 100644 pkg/webui/ui/src/icons/KluctlLogo.tsx delete mode 100644 pkg/webui/ui/src/icons/KluctlText.tsx delete mode 100644 pkg/webui/ui/src/icons/Targets.tsx create mode 100644 pkg/webui/ui/src/icons/cpu.svg create mode 100644 pkg/webui/ui/src/icons/finger-scan.svg create mode 100644 pkg/webui/ui/src/icons/project.svg create mode 100644 pkg/webui/ui/src/icons/relation-hline.svg create mode 100644 pkg/webui/ui/src/icons/search.svg create mode 100644 pkg/webui/ui/src/icons/target.svg create mode 100644 pkg/webui/ui/src/icons/tree-view.svg diff --git a/pkg/webui/ui/package-lock.json b/pkg/webui/ui/package-lock.json index 014cd172d..a31048de4 100644 --- a/pkg/webui/ui/package-lock.json +++ b/pkg/webui/ui/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", - "@fontsource/roboto": "^4.5.8", + "@fontsource-variable/nunito": "^5.0.2", "@mui/icons-material": "^5.11.11", "@mui/lab": "^5.0.0-alpha.124", "@mui/material": "^5.11.14", @@ -2386,10 +2386,10 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@fontsource/roboto": { - "version": "4.5.8", - "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-4.5.8.tgz", - "integrity": "sha512-CnD7zLItIzt86q4Sj3kZUiLcBk1dSk81qcqgMGaZe7SQ1P8hFNxhMl5AZthK1zrDM5m74VVhaOpuMGIL4gagaA==" + "node_modules/@fontsource-variable/nunito": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@fontsource-variable/nunito/-/nunito-5.0.2.tgz", + "integrity": "sha512-ztW98DGVZbq77ITkqg5+C5O8CI/SoaiqWOFBfbyE7CHMkU8XaBOXFe2l6H7YREE15mMIKCGA/icztQuoLi21lA==" }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", diff --git a/pkg/webui/ui/package.json b/pkg/webui/ui/package.json index eea5b6cc0..e658c5ed9 100644 --- a/pkg/webui/ui/package.json +++ b/pkg/webui/ui/package.json @@ -7,7 +7,7 @@ "dependencies": { "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", - "@fontsource/roboto": "^4.5.8", + "@fontsource-variable/nunito": "^5.0.2", "@mui/icons-material": "^5.11.11", "@mui/lab": "^5.0.0-alpha.124", "@mui/material": "^5.11.14", diff --git a/pkg/webui/ui/src/components/ActionsMenu.tsx b/pkg/webui/ui/src/components/ActionsMenu.tsx index 8247b7ed8..4b297e5ca 100644 --- a/pkg/webui/ui/src/components/ActionsMenu.tsx +++ b/pkg/webui/ui/src/components/ActionsMenu.tsx @@ -22,7 +22,7 @@ export const ActionsMenu = (props: { icon?: React.ReactNode, menuItems: ActionMe setAnchorEl(null); }; - const icon = props.icon || + const icon = props.icon || return {icon} @@ -39,7 +40,7 @@ export const ActionsMenu = (props: { icon?: React.ReactNode, menuItems: ActionMe id="account-menu" open={menuOpen} onClose={handleMenuClose} - onClick={ e => {e.stopPropagation(); handleMenuClose()}} + onClick={e => { e.stopPropagation(); handleMenuClose() }} PaperProps={{ elevation: 0, sx: { diff --git a/pkg/webui/ui/src/components/LeftDrawer.tsx b/pkg/webui/ui/src/components/LeftDrawer.tsx index cd712c4b5..5c1c61859 100644 --- a/pkg/webui/ui/src/components/LeftDrawer.tsx +++ b/pkg/webui/ui/src/components/LeftDrawer.tsx @@ -4,27 +4,22 @@ import { CSSObject, styled, Theme, ThemeProvider, useTheme } from '@mui/material import Box from '@mui/material/Box'; import MuiDrawer from '@mui/material/Drawer'; import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar'; -import Toolbar from '@mui/material/Toolbar'; import List from '@mui/material/List'; import Divider from '@mui/material/Divider'; import IconButton from '@mui/material/IconButton'; -import MenuIcon from '@mui/icons-material/Menu'; -import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; -import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import { Link } from "react-router-dom"; import { AppOutletContext } from "./App"; -import { KluctlLogo } from "../icons/KluctlLogo"; -import { Targets } from '../icons/Targets'; +import { KluctlLogo, TargetsIcon, KluctlText, SearchIcon } from '../icons/Icons'; import { dark } from './theme'; -import { KluctlText } from '../icons/KluctlText'; import { Typography } from '@mui/material'; const drawerWidthOpen = 224; const drawerWidthClosed = 96; +const appBarHeight = 106; const openedMixin = (theme: Theme): CSSObject => ({ width: drawerWidthOpen, @@ -45,7 +40,7 @@ const closedMixin = (theme: Theme): CSSObject => ({ }); const DrawerHeader = styled('div')(({ theme }) => ({ - height: '106px', + height: appBarHeight, padding: '31px 23px 0 23px', // necessary for content to be below app bar ...theme.mixins.toolbar, @@ -58,7 +53,7 @@ interface AppBarProps extends MuiAppBarProps { const AppBar = styled(MuiAppBar, { shouldForwardProp: (prop) => prop !== 'open', })(({ theme, open }) => ({ - height: 106, + height: appBarHeight, border: 'none', boxShadow: 'none', background: 'transparent', @@ -133,16 +128,47 @@ export default function LeftDrawer(props: { content: React.ReactNode, context: A const [open, setOpen] = useState(true); + const theme = useTheme(); + const toggleDrawer = () => { setOpen(o => !o); }; return ( - - - - Dashboard - + + + + + Dashboard + + + + + + + + @@ -156,17 +182,19 @@ export default function LeftDrawer(props: { content: React.ReactNode, context: A - - } to={"targets"} /> + + } to={"targets"} /> {context.filters} {context.filters && } - + - {props.content} + + {props.content} + ); diff --git a/pkg/webui/ui/src/components/Router.tsx b/pkg/webui/ui/src/components/Router.tsx index ee3417a1a..0c908fdb7 100644 --- a/pkg/webui/ui/src/components/Router.tsx +++ b/pkg/webui/ui/src/components/Router.tsx @@ -1,4 +1,4 @@ -import { createBrowserRouter, createHashRouter, useRouteError } from "react-router-dom"; +import { createHashRouter, useRouteError } from "react-router-dom"; import React from "react"; import App from "./App"; import { projectsLoader, TargetsView } from "./targets-view/TargetsView"; diff --git a/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx b/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx index d5a48a05b..11b9721d6 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx @@ -20,7 +20,15 @@ export const StatusLine = (props: StatusLineProps) => { children.push( - {icon} + + {icon} + {n} @@ -28,12 +36,12 @@ export const StatusLine = (props: StatusLineProps) => { } } - doPush(props.errors, "total errors.", ) - doPush(props.warnings, "total warnings.", ) - doPush(props.newObjects, "new objects.", ) - doPush(props.deletedObjects, "deleted objects.", ) - doPush(props.orphanObjects, "orphan objects.", ) - doPush(props.changedObjects, "changed objects.", ) + doPush(props.errors, "total errors.", ) + doPush(props.warnings, "total warnings.", ) + doPush(props.newObjects, "new objects.", ) + doPush(props.deletedObjects, "deleted objects.", ) + doPush(props.orphanObjects, "orphan objects.", ) + doPush(props.changedObjects, "changed objects.", ) return {children} @@ -42,18 +50,18 @@ export const StatusLine = (props: StatusLineProps) => { export const CommandResultStatusLine = (props: { rs: CommandResultSummary }) => { return } export const ValidateResultStatusLine = (props: { vr?: ValidateResult }) => { return } \ No newline at end of file diff --git a/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx index 8d5ecc36c..1d33bf8b6 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx +++ b/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { DeploymentItemConfig, DeploymentProjectConfig } from "../../../models"; import { NodeData } from "./NodeData"; import { FolderZip } from "@mui/icons-material"; -import { GitIcon } from "../../../icons/GitIcon"; +import { GitIcon } from "../../../icons/Icons"; import { CommandResultProps } from "../CommandResultView"; import { PropertiesTable } from "../../PropertiesTable"; import { buildDeploymentItemSummaryProps } from "./DeploymentItemNode"; diff --git a/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx index d53838eeb..3b5674e5c 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx +++ b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx @@ -2,7 +2,7 @@ import { VarsSource } from "../../../models"; import { NodeData } from "./NodeData"; import React from "react"; import { Category, Cloud, Dvr, Http, Lock, Settings, Source } from "@mui/icons-material"; -import { GitIcon } from "../../../icons/GitIcon"; +import { GitIcon } from "../../../icons/Icons"; import { PropertiesTable } from "../../PropertiesTable"; import { CodeViewer } from "../../CodeViewer"; import { Box } from "@mui/material"; diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index 8c23fb84e..6eb2dec6e 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -1,6 +1,6 @@ import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; -import { AccountTree, CleaningServices, CloudSync, Delete, Difference } from "@mui/icons-material"; -import React, { useEffect, useMemo, useState } from "react"; +import { CleaningServices, CloudSync, Delete, Difference } from "@mui/icons-material"; +import { useEffect, useMemo, useState } from "react"; import * as yaml from "js-yaml"; import { CodeViewer } from "../CodeViewer"; import Paper from "@mui/material/Paper"; @@ -8,6 +8,7 @@ import { Box, IconButton, Tooltip, Typography } from "@mui/material"; import { CommandResultStatusLine } from "../result-view/CommandResultStatusLine"; import { useNavigate } from "react-router"; import { formatDurationShort } from "../../utils/duration"; +import { TreeViewIcon } from "../../icons/Icons"; export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary, rs: CommandResultSummary, onSelectCommandResult: (rs?: CommandResultSummary) => void }) => { const calcAgo = () => { @@ -42,7 +43,7 @@ export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary const cmdInfoYaml = useMemo(() => { return yaml.dump(props.rs.commandInfo) }, [props.rs]) - let iconTooltip = + let iconTooltip = useEffect(() => { const interval = setInterval(() => setAgo(calcAgo()), 5000); @@ -51,39 +52,70 @@ export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary return props.onSelectCommandResult(props.rs)} > - - - + + + - + + + + + {props.rs.commandInfo?.command} + - {ago} + {ago} - - {props.rs.commandInfo?.command} - - - - - - - { - e.stopPropagation(); - navigate(`/results/${props.rs.id}`) - - }}> - - - - - - + + + + + + { + e.stopPropagation(); + navigate(`/results/${props.rs.id}`); + }} + sx={{ + padding: 0, + width: 26, + height: 26 + }} + > + + + + diff --git a/pkg/webui/ui/src/components/targets-view/Projects.tsx b/pkg/webui/ui/src/components/targets-view/Projects.tsx index 390fb9ee1..4756cf046 100644 --- a/pkg/webui/ui/src/components/targets-view/Projects.tsx +++ b/pkg/webui/ui/src/components/targets-view/Projects.tsx @@ -4,41 +4,57 @@ import Paper from "@mui/material/Paper"; import { Box, Typography } from "@mui/material"; import React from "react"; import Tooltip from "@mui/material/Tooltip"; +import { ProjectIcon } from "../../icons/Icons"; export const ProjectItem = (props: { ps: ProjectSummary }) => { const name = getLastPathElement(props.ps.project.gitRepoKey) const subDir = props.ps.project.subDir const projectInfo = - {props.ps.project.gitRepoKey}
    + {props.ps.project.gitRepoKey}
    {props.ps.project.subDir ? <> - SubDir: {props.ps.project.subDir}
    + SubDir: {props.ps.project.subDir}
    : <>}
    - return - - - {name ? - - - {name} - - - : <>} + return + + + {name && ( + <> + + + + {name} + + + + )} {subDir ? {subDir} : <>} - + {/**/} diff --git a/pkg/webui/ui/src/components/targets-view/Targets.tsx b/pkg/webui/ui/src/components/targets-view/Targets.tsx index b7b99849f..729cf98ff 100644 --- a/pkg/webui/ui/src/components/targets-view/Targets.tsx +++ b/pkg/webui/ui/src/components/targets-view/Targets.tsx @@ -1,28 +1,29 @@ import { ProjectSummary, TargetSummary } from "../../models"; import { ActionMenuItem, ActionsMenu } from "../ActionsMenu"; import Paper from "@mui/material/Paper"; -import { Box, Typography } from "@mui/material"; +import { Box, Typography, useTheme } from "@mui/material"; import React from "react"; -import { Jdenticon } from "../Jdenticon"; import Tooltip from "@mui/material/Tooltip"; -import { Favorite, Fingerprint, HeartBroken, PublishedWithChanges, QuestionMark } from "@mui/icons-material"; +import { Favorite, HeartBroken, PublishedWithChanges } from "@mui/icons-material"; import { api } from "../../api"; +import { CpuIcon, FingerScanIcon, MessageQuestionIcon, TargetIcon } from "../../icons/Icons"; const StatusIcon = (props: { ps: ProjectSummary, ts: TargetSummary }) => { let icon: React.ReactElement + const theme = useTheme(); if (props.ts.lastValidateResult === undefined) { - icon = + icon = } else if (props.ts.lastValidateResult.ready && !props.ts.lastValidateResult.errors) { if (props.ts.lastValidateResult.warnings?.length) { - icon = + icon = } else if (props.ts.lastValidateResult.drift?.length) { - icon = + icon = } else { - icon = + icon = } } else { - icon = + icon = } const tooltip: string[] = [] @@ -46,7 +47,7 @@ const StatusIcon = (props: { ps: ProjectSummary, ts: TargetSummary }) => { } return {t})}> - {icon} + {icon} } @@ -54,7 +55,7 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel const actionMenuItems: ActionMenuItem[] = [] actionMenuItems.push({ - icon: , + icon: , text: "Validate now", handler: () => { api.validateNow(props.ps.project, props.ts.target) @@ -79,9 +80,9 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel }) const contextTooltip = - All known contexts: + All known contexts: {allContexts.map((context, i) => ( - {context} + {context} ))} @@ -92,54 +93,62 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel return props.onSelectTarget(props.ts)} > - - - - {targetName} - - {allContexts.length ? - - - {allContexts[0]} - - - - : <>} - - - - - -
    - -
    + + + + + + {targetName} + + {allContexts.length ? + + + {allContexts[0]} + - -
    - -
    -
    -
    - - - - + : <>} +
    +
    + + + + + + + + + + + +
    diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 3f44afc91..991546840 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -1,6 +1,6 @@ import { useLoaderData } from "react-router-dom"; import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; -import { Box, Typography } from "@mui/material"; +import { Box, BoxProps, Typography } from "@mui/material"; import React, { useEffect, useState } from "react"; import { useAppOutletContext } from "../App"; import { api } from "../../api"; @@ -10,15 +10,56 @@ import Divider from "@mui/material/Divider"; import { CommandResultItem } from "./CommandResultItem"; import { CommandResultDetailsDrawer } from "./CommandResultDetailsDrawer"; import { TargetDetailsDrawer } from "./TargetDetailsDrawer"; +import { RelationHLine } from "../../icons/Icons"; -const targetWidth = "220px" -const targetHeight = "110px" +const colWidth = 433; +const cardWidth = 247; +const projectCardHeight = 80; +const cardHeight = 126; export async function projectsLoader() { const projects = await api.listProjects() return projects } +function ColHeader({ children }: { children: React.ReactNode }) { + return + {children} + +} + +function Card({ children, ...rest }: BoxProps) { + return + {children} + +} + +function CardCol({ children, ...rest }: BoxProps) { + return + {children} + +} + +function CardRow({ children, ...rest }: BoxProps) { + return + {children} + +} + export const TargetsView = () => { const context = useAppOutletContext() const [selectedCommandResult, setSelectedCommandResult] = useState() @@ -39,57 +80,58 @@ export const TargetsView = () => { setSelectedTargetSummary(ts) } + useEffect(() => { + if (projects[2].targets.length < 3) { + projects[2].targets.push(projects[2].targets[0]) + } + }) + return - setSelectedCommandResult(undefined)}/> - setSelectedTargetSummary(undefined)}/> - - - Projects - - - - Targets - - - - History - + setSelectedCommandResult(undefined)} /> + setSelectedTargetSummary(undefined)} /> + + Projects + Targets + History - + {projects.map((ps, i) => { return - - - - - - - + + + + + + - + {ps.targets.map((ts, i) => { - return - doSetSelectedTargetSummary(ts)}/> + return + + doSetSelectedTargetSummary(ts)} /> + + + + })} - - + - + {ps.targets.map((ts, i) => { - return + return {ts.commandResults?.map((rs, i) => { - return + return doSetSelectedCommandResult(rs)}/> - + onSelectCommandResult={(rs) => doSetSelectedCommandResult(rs)} /> + })} - + })} - + - + })} diff --git a/pkg/webui/ui/src/components/theme.ts b/pkg/webui/ui/src/components/theme.ts index a552fab79..bc1c18469 100644 --- a/pkg/webui/ui/src/components/theme.ts +++ b/pkg/webui/ui/src/components/theme.ts @@ -7,11 +7,20 @@ const paletteDark = { const paletteLight = { primary: { main: '#222222' }, - secondary: { main: '#39403E'} + secondary: { main: '#39403E' }, + background: { default: '#DFEBE9', paper: '#DFEBE9' }, + text: { primary: '#222222' } } satisfies PaletteOptions; export const light = createTheme({ palette: paletteLight, + typography: { + fontFamily: 'Nunito Variable', + h1: { color: '#222222' }, + h6: { color: '#39403E' }, + subtitle1: { color: '#39403E' }, + subtitle2: { fontSize: '14px', lineHeight: 1.2 } + }, components: { MuiDivider: { styleOverrides: { @@ -32,6 +41,9 @@ export const light = createTheme({ export const dark = createTheme({ palette: paletteDark, + typography: { + fontFamily: 'Nunito Variable' + }, components: { MuiListItem: { styleOverrides: { @@ -53,10 +65,10 @@ export const dark = createTheme({ styleOverrides: { root: { border: 'none' - }, + }, paper: { border: 'none' - }, + }, } }, MuiDivider: { diff --git a/pkg/webui/ui/src/icons/GitIcon.tsx b/pkg/webui/ui/src/icons/GitIcon.tsx deleted file mode 100644 index 6948ffe5a..000000000 --- a/pkg/webui/ui/src/icons/GitIcon.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { ReactComponent as GitIconSvg } from './git.svg'; -import React from "react"; - -export const GitIcon = () => { - return -} diff --git a/pkg/webui/ui/src/icons/Icons.tsx b/pkg/webui/ui/src/icons/Icons.tsx new file mode 100644 index 000000000..18921d8bc --- /dev/null +++ b/pkg/webui/ui/src/icons/Icons.tsx @@ -0,0 +1,64 @@ +import { ReactComponent as KluctlTextSvg } from './kluctl-text.svg'; +import { ReactComponent as GitIconSvg } from './git.svg'; +import { ReactComponent as KluctlLogoSvg } from './kluctl-logo.svg'; +import { ReactComponent as SearchIconSvg } from './search.svg'; +import { ReactComponent as TargetsIconSvg } from './targets.svg'; +import { ReactComponent as TargetIconSvg } from './target.svg'; +import { ReactComponent as RelationHLineSvg } from './relation-hline.svg'; +import { ReactComponent as ProjectIconSvg } from './project.svg'; +import { ReactComponent as CpuIconSvg } from './cpu.svg'; +import { ReactComponent as FingerScanIconSvg } from './finger-scan.svg'; +import { ReactComponent as TreeViewIconSvg } from './tree-view.svg'; + +export const KluctlText = () => { + return +} + +export const GitIcon = () => { + return +} + +export const KluctlLogo = () => { + return +} + +export const SearchIcon = () => { + return +} + +export const TargetsIcon = () => { + return +} + +export const TargetIcon = () => { + return +} +export const RelationHLine = () => { + return +} + +export const ProjectIcon = () => { + return +} + +export const CpuIcon = () => { + return +} + +export const FingerScanIcon = () => { + return +} + +export const MessageQuestionIcon = (props: { color: string }) => { + return + + + +} + +export const TreeViewIcon = () => { + return +} diff --git a/pkg/webui/ui/src/icons/KluctlLogo.tsx b/pkg/webui/ui/src/icons/KluctlLogo.tsx deleted file mode 100644 index f7c3d0665..000000000 --- a/pkg/webui/ui/src/icons/KluctlLogo.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { ReactComponent as KluctlLogoSvg } from './kluctl-logo.svg'; -import React from "react"; - -export const KluctlLogo = () => { - return -} diff --git a/pkg/webui/ui/src/icons/KluctlText.tsx b/pkg/webui/ui/src/icons/KluctlText.tsx deleted file mode 100644 index 6122532ab..000000000 --- a/pkg/webui/ui/src/icons/KluctlText.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { ReactComponent as KluctlTextSvg } from './kluctl-text.svg'; -import React from "react"; - -export const KluctlText = () => { - return -} diff --git a/pkg/webui/ui/src/icons/Targets.tsx b/pkg/webui/ui/src/icons/Targets.tsx deleted file mode 100644 index d1ba0a421..000000000 --- a/pkg/webui/ui/src/icons/Targets.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { ReactComponent as TargetsSvg } from './targets.svg'; - -export const Targets = () => { - return -} diff --git a/pkg/webui/ui/src/icons/cpu.svg b/pkg/webui/ui/src/icons/cpu.svg new file mode 100644 index 000000000..b30baa518 --- /dev/null +++ b/pkg/webui/ui/src/icons/cpu.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/pkg/webui/ui/src/icons/finger-scan.svg b/pkg/webui/ui/src/icons/finger-scan.svg new file mode 100644 index 000000000..8f7fd351f --- /dev/null +++ b/pkg/webui/ui/src/icons/finger-scan.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pkg/webui/ui/src/icons/project.svg b/pkg/webui/ui/src/icons/project.svg new file mode 100644 index 000000000..4a0c505c9 --- /dev/null +++ b/pkg/webui/ui/src/icons/project.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/webui/ui/src/icons/relation-hline.svg b/pkg/webui/ui/src/icons/relation-hline.svg new file mode 100644 index 000000000..0835a6c9f --- /dev/null +++ b/pkg/webui/ui/src/icons/relation-hline.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/webui/ui/src/icons/search.svg b/pkg/webui/ui/src/icons/search.svg new file mode 100644 index 000000000..1a9508cbd --- /dev/null +++ b/pkg/webui/ui/src/icons/search.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/ui/src/icons/target.svg b/pkg/webui/ui/src/icons/target.svg new file mode 100644 index 000000000..eb93030dc --- /dev/null +++ b/pkg/webui/ui/src/icons/target.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/webui/ui/src/icons/tree-view.svg b/pkg/webui/ui/src/icons/tree-view.svg new file mode 100644 index 000000000..225712f63 --- /dev/null +++ b/pkg/webui/ui/src/icons/tree-view.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pkg/webui/ui/src/index.css b/pkg/webui/ui/src/index.css index 738f22103..e1ba40f42 100644 --- a/pkg/webui/ui/src/index.css +++ b/pkg/webui/ui/src/index.css @@ -1,27 +1,31 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; background: linear-gradient(180deg, #59A588 0%, #404846 100%); background-attachment: fixed; } +body, input { + font-family: 'Nunito Variable', 'Segoe UI', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', -apple-system, BlinkMacSystemFont, + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + monospace; } * { box-sizing: border-box; } -html, body, #root { +html, +body, +#root { width: 100%; height: 100%; margin: 0; padding: 0; - font-family: sans-serif; -} +} \ No newline at end of file diff --git a/pkg/webui/ui/src/index.tsx b/pkg/webui/ui/src/index.tsx index f7e132268..391fce39b 100644 --- a/pkg/webui/ui/src/index.tsx +++ b/pkg/webui/ui/src/index.tsx @@ -5,10 +5,7 @@ import { RouterProvider } from "react-router-dom"; import './index.css'; import reportWebVitals from './reportWebVitals'; -import '@fontsource/roboto/300.css'; -import '@fontsource/roboto/400.css'; -import '@fontsource/roboto/500.css'; -import '@fontsource/roboto/700.css'; +import '@fontsource-variable/nunito'; import { Router } from "./components/Router"; From 776729c327d7907227d4131abe0067ab9f6bef4a Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Tue, 30 May 2023 22:47:24 +0600 Subject: [PATCH 1130/2268] Main Page: Added lines from projects to targets. --- .../components/targets-view/TargetsView.tsx | 111 ++++++++++++++++-- 1 file changed, 100 insertions(+), 11 deletions(-) diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 991546840..8a88ca741 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -1,6 +1,6 @@ import { useLoaderData } from "react-router-dom"; import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; -import { Box, BoxProps, Typography } from "@mui/material"; +import { Box, BoxProps, Typography, useTheme } from "@mui/material"; import React, { useEffect, useState } from "react"; import { useAppOutletContext } from "../App"; import { api } from "../../api"; @@ -16,6 +16,7 @@ const colWidth = 433; const cardWidth = 247; const projectCardHeight = 80; const cardHeight = 126; +const cardGap = 20; export async function projectsLoader() { const projects = await api.listProjects() @@ -49,17 +50,102 @@ function Card({ children, ...rest }: BoxProps) { } function CardCol({ children, ...rest }: BoxProps) { - return + return {children} } function CardRow({ children, ...rest }: BoxProps) { - return + return {children} } +const RelationTree = React.memo(({ targetCount }: { targetCount: number }): JSX.Element | null => { + const theme = useTheme(); + const height = targetCount * cardHeight + (targetCount - 1) * cardGap + const width = 169; + const curveRadius = 12; + const circleRadius = 5; + const strokeWidth = 2; + + if (targetCount <= 0) { + return null; + } + + const targetsCenterYs = Array(targetCount).fill(0).map((_, i) => + cardHeight / 2 + i * (cardHeight + cardGap) + ); + const rootCenterY = height / 2; + + return + {targetsCenterYs.map((cy, i) => { + let d: React.SVGAttributes['d']; + if (targetCount % 2 === 1 && i === Math.floor(targetCount / 2)) { + // target is in the middle. + d = ` + M ${circleRadius},${rootCenterY} + h ${width - circleRadius} + `; + } else if (i < targetCount / 2) { + // target is higher than root. + d = ` + M ${circleRadius},${rootCenterY} + h ${width / 2 - curveRadius - circleRadius} + a ${curveRadius} ${curveRadius} 90 0 0 ${curveRadius} -${curveRadius} + v ${cy - rootCenterY + curveRadius * 2} + a ${curveRadius} ${curveRadius} 90 0 1 ${curveRadius} -${curveRadius} + h ${width / 2 - curveRadius - circleRadius} + `; + } else { + // target is lower than root. + d = ` + M ${circleRadius},${rootCenterY} + h ${width / 2 - curveRadius - circleRadius} + a ${curveRadius} ${curveRadius} 90 0 1 ${curveRadius} ${curveRadius} + v ${cy - rootCenterY - curveRadius * 2} + a ${curveRadius} ${curveRadius} 90 0 0 ${curveRadius} ${curveRadius} + h ${width / 2 - curveRadius - circleRadius} + `; + } + + return [ + , + + ] + })} + + +}); + export const TargetsView = () => { const context = useAppOutletContext() const [selectedCommandResult, setSelectedCommandResult] = useState() @@ -80,12 +166,6 @@ export const TargetsView = () => { setSelectedTargetSummary(ts) } - useEffect(() => { - if (projects[2].targets.length < 3) { - projects[2].targets.push(projects[2].targets[0]) - } - }) - return setSelectedCommandResult(undefined)} /> setSelectedTargetSummary(undefined)} /> @@ -98,11 +178,20 @@ export const TargetsView = () => { {projects.map((ps, i) => { return - + - + + + + {ps.targets.map((ts, i) => { From f17ee24b4b8c12e96dca3fba120adf7d93d9cf3e Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Wed, 31 May 2023 01:55:39 +0600 Subject: [PATCH 1131/2268] Main Page: added side panel for target nodes. --- pkg/webui/ui/src/components/LeftDrawer.tsx | 11 +- .../ui/src/components/PropertiesTable.tsx | 2 +- .../src/components/result-view/SidePanel.tsx | 55 +++++--- .../targets-view/CommandResultItem.tsx | 3 - .../src/components/targets-view/Projects.tsx | 3 - .../targets-view/TargetDetailsDrawer.tsx | 58 ++++---- .../src/components/targets-view/Targets.tsx | 3 - .../components/targets-view/TargetsView.tsx | 6 +- pkg/webui/ui/src/components/theme.ts | 126 ++++++++++++++++-- pkg/webui/ui/src/icons/Icons.tsx | 5 + pkg/webui/ui/src/icons/close.svg | 3 + 11 files changed, 200 insertions(+), 75 deletions(-) create mode 100644 pkg/webui/ui/src/icons/close.svg diff --git a/pkg/webui/ui/src/components/LeftDrawer.tsx b/pkg/webui/ui/src/components/LeftDrawer.tsx index 5c1c61859..a5bd4e5fe 100644 --- a/pkg/webui/ui/src/components/LeftDrawer.tsx +++ b/pkg/webui/ui/src/components/LeftDrawer.tsx @@ -19,7 +19,6 @@ import { Typography } from '@mui/material'; const drawerWidthOpen = 224; const drawerWidthClosed = 96; -const appBarHeight = 106; const openedMixin = (theme: Theme): CSSObject => ({ width: drawerWidthOpen, @@ -40,7 +39,7 @@ const closedMixin = (theme: Theme): CSSObject => ({ }); const DrawerHeader = styled('div')(({ theme }) => ({ - height: appBarHeight, + height: theme.consts.appBarHeight, padding: '31px 23px 0 23px', // necessary for content to be below app bar ...theme.mixins.toolbar, @@ -53,7 +52,7 @@ interface AppBarProps extends MuiAppBarProps { const AppBar = styled(MuiAppBar, { shouldForwardProp: (prop) => prop !== 'open', })(({ theme, open }) => ({ - height: appBarHeight, + height: theme.consts.appBarHeight, border: 'none', boxShadow: 'none', background: 'transparent', @@ -138,7 +137,7 @@ export default function LeftDrawer(props: { content: React.ReactNode, context: A - + Dashboard - @@ -192,7 +190,8 @@ export default function LeftDrawer(props: { content: React.ReactNode, context: A - + + {props.content} diff --git a/pkg/webui/ui/src/components/PropertiesTable.tsx b/pkg/webui/ui/src/components/PropertiesTable.tsx index 20cdd967e..61237aefb 100644 --- a/pkg/webui/ui/src/components/PropertiesTable.tsx +++ b/pkg/webui/ui/src/components/PropertiesTable.tsx @@ -9,7 +9,7 @@ import Paper from '@mui/material/Paper'; export function PropertiesTable(props: {properties: {name: string, value: React.ReactNode}[]}) { return ( - + diff --git a/pkg/webui/ui/src/components/result-view/SidePanel.tsx b/pkg/webui/ui/src/components/result-view/SidePanel.tsx index da68288f9..9a2047a7c 100644 --- a/pkg/webui/ui/src/components/result-view/SidePanel.tsx +++ b/pkg/webui/ui/src/components/result-view/SidePanel.tsx @@ -1,6 +1,8 @@ -import { Box, Tab, Typography } from "@mui/material"; +import { Box, Divider, IconButton, Tab, ThemeProvider, Typography, useTheme } from "@mui/material"; import React, { useEffect, useState } from "react"; import { TabContext, TabList, TabPanel } from "@mui/lab"; +import { CloseIcon } from "../../icons/Icons"; +import { light } from "../theme"; export interface SidePanelTab { label: string @@ -14,10 +16,12 @@ export interface SidePanelProvider { export interface SidePanelProps { provider?: SidePanelProvider; + onClose?: () => void; } export const SidePanel = (props: SidePanelProps) => { - let [selectedTab, setSelectedTab] = useState(); + const [selectedTab, setSelectedTab] = useState(); + const theme = useTheme(); function handleTabChange(_e: React.SyntheticEvent, value: string) { setSelectedTab(value); @@ -55,24 +59,39 @@ export const SidePanel = (props: SidePanelProps) => { return <> } - return - - {props.provider.buildSidePanelTitle()} - - + return - - - {tabs.map((tab, i) => { - return - })} - + + + + + {props.provider.buildSidePanelTitle()} + + + + + + + + + + + {tabs.map((tab, i) => { + return + })} + + + + + + {tabs.map((tab, index) => { + return + + {tab.content} + + + })} - {tabs.map((tab, index) => { - return - {tab.content} - - })} } diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index 6eb2dec6e..3c5a1a5d2 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -75,10 +75,7 @@ export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary textAlign='left' textOverflow='ellipsis' overflow='hidden' - lineHeight='27.28px' flexGrow={1} - fontSize='20px' - fontWeight={800} > {props.rs.commandInfo?.command} diff --git a/pkg/webui/ui/src/components/targets-view/Projects.tsx b/pkg/webui/ui/src/components/targets-view/Projects.tsx index 4756cf046..c5ebb34f9 100644 --- a/pkg/webui/ui/src/components/targets-view/Projects.tsx +++ b/pkg/webui/ui/src/components/targets-view/Projects.tsx @@ -38,10 +38,7 @@ export const ProjectItem = (props: { ps: ProjectSummary }) => { textAlign='left' textOverflow='ellipsis' overflow='hidden' - lineHeight={1.2} flexGrow={1} - fontSize='20px' - fontWeight={800} > {name} diff --git a/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx index 6c76ebbc4..874779b74 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx @@ -1,11 +1,12 @@ import { TargetSummary } from "../../models"; -import { Box, Drawer } from "@mui/material"; +import { Box, Drawer, ThemeProvider } from "@mui/material"; import { SidePanel, SidePanelProvider, SidePanelTab } from "../result-view/SidePanel"; import React from "react"; import { PropertiesTable } from "../PropertiesTable"; import { DiffStatus } from "../result-view/nodes/NodeData"; import { ChangesTable } from "../result-view/ChangesTable"; import { ErrorsTable } from "../ErrorsTable"; +import { dark } from "../theme"; class MyProvider implements SidePanelProvider { private ts?: TargetSummary; @@ -26,27 +27,27 @@ class MyProvider implements SidePanelProvider { } const tabs = [ - {label: "Summary", content: this.buildSummaryTab()} + { label: "Summary", content: this.buildSummaryTab() } ] if (this.ts.target) - if (this.diffStatus.changedObjects.length) { - tabs.push({ - label: "Drift", - content: - }) - } + if (this.diffStatus.changedObjects.length) { + tabs.push({ + label: "Drift", + content: + }) + } if (this.ts.lastValidateResult?.errors?.length) { tabs.push({ label: "Errors", - content: + content: }) } if (this.ts.lastValidateResult?.warnings?.length) { tabs.push({ label: "Warnings", - content: + content: }) } @@ -55,25 +56,25 @@ class MyProvider implements SidePanelProvider { buildSummaryTab(): React.ReactNode { const props = [ - {name: "Target Name", value: this.getTargetName()}, - {name: "Discriminator", value: this.ts?.target.discriminator}, + { name: "Target Name", value: this.getTargetName() }, + { name: "Discriminator", value: this.ts?.target.discriminator }, ] if (this.ts?.lastValidateResult) { - props.push({name: "Ready", value: this.ts.lastValidateResult.ready + ""}) + props.push({ name: "Ready", value: this.ts.lastValidateResult.ready + "" }) } if (this.ts?.lastValidateResult?.errors?.length) { - props.push({name: "Errors", value: this.ts.lastValidateResult.errors.length + ""}) + props.push({ name: "Errors", value: this.ts.lastValidateResult.errors.length + "" }) } if (this.ts?.lastValidateResult?.warnings?.length) { - props.push({name: "Warnings", value: this.ts.lastValidateResult.warnings.length + ""}) + props.push({ name: "Warnings", value: this.ts.lastValidateResult.warnings.length + "" }) } if (this.ts?.lastValidateResult?.drift?.length) { - props.push({name: "Drifted Objects", value: this.ts.lastValidateResult.drift.length + ""}) + props.push({ name: "Drifted Objects", value: this.ts.lastValidateResult.drift.length + "" }) } return <> - + } @@ -95,14 +96,17 @@ class MyProvider implements SidePanelProvider { } export const TargetDetailsDrawer = (props: { ts?: TargetSummary, onClose: () => void }) => { - return props.onClose()} - > - - - - + return + + + + + + ; } \ No newline at end of file diff --git a/pkg/webui/ui/src/components/targets-view/Targets.tsx b/pkg/webui/ui/src/components/targets-view/Targets.tsx index 729cf98ff..d6f891c37 100644 --- a/pkg/webui/ui/src/components/targets-view/Targets.tsx +++ b/pkg/webui/ui/src/components/targets-view/Targets.tsx @@ -112,10 +112,7 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel textAlign='left' textOverflow='ellipsis' overflow='hidden' - lineHeight='27.28px' flexGrow={1} - fontSize='20px' - fontWeight={800} > {targetName} diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 8a88ca741..52178ed7f 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -39,7 +39,7 @@ function ColHeader({ children }: { children: React.ReactNode }) { } }} > - {children} + {children} } @@ -79,9 +79,9 @@ const RelationTree = React.memo(({ targetCount }: { targetCount: number }): JSX. const rootCenterY = height / 2; return {targetsCenterYs.map((cy, i) => { diff --git a/pkg/webui/ui/src/components/theme.ts b/pkg/webui/ui/src/components/theme.ts index bc1c18469..c599bf1cd 100644 --- a/pkg/webui/ui/src/components/theme.ts +++ b/pkg/webui/ui/src/components/theme.ts @@ -1,8 +1,9 @@ -import { PaletteOptions, createTheme } from '@mui/material/styles'; +import { PaletteOptions, ThemeOptions, createTheme } from '@mui/material/styles'; const paletteDark = { primary: { main: '#DFEBE9' }, - background: { default: '#222222', paper: '#222222' } + background: { default: '#222222', paper: '#222222' }, + text: { primary: '#DFEBE9' } } satisfies PaletteOptions; const paletteLight = { @@ -12,13 +13,53 @@ const paletteLight = { text: { primary: '#222222' } } satisfies PaletteOptions; -export const light = createTheme({ - palette: paletteLight, +declare module '@mui/material/styles' { + interface Theme { + consts: { + appBarHeight: number; + }; + } + // allow configuration using `createTheme` + interface ThemeOptions { + consts?: { + appBarHeight?: number; + }; + } +} + +export const common = createTheme({ + consts: { + appBarHeight: 106 + }, typography: { fontFamily: 'Nunito Variable', - h1: { color: '#222222' }, - h6: { color: '#39403E' }, - subtitle1: { color: '#39403E' }, + } +}); + +export const light = createTheme(common, { + palette: paletteLight, + typography: { + h1: { + color: paletteLight.text.primary, + fontWeight: 700, + fontSize: '32px', + lineHeight: '44px', + letterSpacing: '1px', + }, + h2: { + color: paletteLight.text.primary, + fontWeight: 700, + fontSize: '20px', + lineHeight: '27px', + letterSpacing: '1px', + }, + h6: { + color: paletteLight.secondary.main, + fontWeight: 800, + fontSize: '20px', + lineHeight: '27px' + }, + subtitle1: { color: paletteLight.secondary.main }, subtitle2: { fontSize: '14px', lineHeight: 1.2 } }, components: { @@ -35,14 +76,51 @@ export const light = createTheme({ color: paletteLight.primary.main } } + }, + MuiTableCell: { + styleOverrides: { + root: { + border: 'none', + borderTop: `0.5px solid ${paletteLight.secondary.main}`, + fontWeight: 400, + fontSize: '16px', + lineHeight: '22px', + letterSpacing: '1px', + position: 'relative', + ':after': { + content: '""', + position: 'absolute', + top: '10px', + right: 0, + bottom: '10px', + display: 'block', + borderRight: '0.5px solid #39403E', + }, + ':last-of-type:after': { + content: 'none' + } + }, + head: { + border: 'none', + } + } } } -}); +} satisfies ThemeOptions); -export const dark = createTheme({ +export const dark = createTheme(common, { palette: paletteDark, typography: { - fontFamily: 'Nunito Variable' + allVariants: { + color: paletteDark.text.primary + }, + h4: { + color: paletteDark.text.primary, + fontWeight: 700, + fontSize: '24px', + lineHeight: '33px', + letterSpacing: '1px', + } }, components: { MuiListItem: { @@ -79,6 +157,32 @@ export const dark = createTheme({ margin: '0 13px' } } + }, + MuiTabs: { + styleOverrides: { + root: { + height: '36px', + minHeight: 0, + textTransform: 'none' + }, + indicator: { + backgroundColor: '#59A588' + } + } + }, + MuiTab: { + styleOverrides: { + root: { + height: '36px', + minHeight: 0, + fontWeight: 400, + fontSize: '16px', + lineHeight: '22px', + letterSpacing: '1px', + textTransform: 'none', + padding: '7px 5px' + } + } } } -}) \ No newline at end of file +} satisfies ThemeOptions) \ No newline at end of file diff --git a/pkg/webui/ui/src/icons/Icons.tsx b/pkg/webui/ui/src/icons/Icons.tsx index 18921d8bc..156c0233f 100644 --- a/pkg/webui/ui/src/icons/Icons.tsx +++ b/pkg/webui/ui/src/icons/Icons.tsx @@ -9,6 +9,7 @@ import { ReactComponent as ProjectIconSvg } from './project.svg'; import { ReactComponent as CpuIconSvg } from './cpu.svg'; import { ReactComponent as FingerScanIconSvg } from './finger-scan.svg'; import { ReactComponent as TreeViewIconSvg } from './tree-view.svg'; +import { ReactComponent as CloseIconSvg } from './close.svg'; export const KluctlText = () => { return @@ -62,3 +63,7 @@ export const MessageQuestionIcon = (props: { color: string }) => { export const TreeViewIcon = () => { return } + +export const CloseIcon = () => { + return +} \ No newline at end of file diff --git a/pkg/webui/ui/src/icons/close.svg b/pkg/webui/ui/src/icons/close.svg new file mode 100644 index 000000000..f3ce4ed70 --- /dev/null +++ b/pkg/webui/ui/src/icons/close.svg @@ -0,0 +1,3 @@ + + + From 4eed4f3b6d5252fea1de39fcaa0599b919d87fd8 Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Thu, 1 Jun 2023 01:27:26 +0600 Subject: [PATCH 1132/2268] Main Page: added side panel for command result nodes. --- pkg/webui/ui/src/components/ErrorsTable.tsx | 5 ++- .../ui/src/components/PropertiesTable.tsx | 3 +- .../components/result-view/ChangesTable.tsx | 36 ++++++++++--------- .../src/components/result-view/SidePanel.tsx | 14 ++++---- .../CommandResultDetailsDrawer.tsx | 34 ++++++++++-------- pkg/webui/ui/src/components/theme.ts | 20 +++++++++-- 6 files changed, 67 insertions(+), 45 deletions(-) diff --git a/pkg/webui/ui/src/components/ErrorsTable.tsx b/pkg/webui/ui/src/components/ErrorsTable.tsx index f15abf73a..a7875b858 100644 --- a/pkg/webui/ui/src/components/ErrorsTable.tsx +++ b/pkg/webui/ui/src/components/ErrorsTable.tsx @@ -5,7 +5,6 @@ import TableCell from '@mui/material/TableCell'; import TableContainer from '@mui/material/TableContainer'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; -import Paper from '@mui/material/Paper'; import { DeploymentError } from "../models"; import { Box, Divider, List, ListItem, ListItemText } from "@mui/material"; import { buildRefKindElement } from "../api"; @@ -13,7 +12,7 @@ import { buildRefKindElement } from "../api"; export function ErrorsTable(props: { errors: DeploymentError[] }) { return <> - +
    @@ -25,7 +24,7 @@ export function ErrorsTable(props: { errors: DeploymentError[] }) { {props.errors?.map((e, i) => ( - + {buildRefKindElement(e.ref, <> diff --git a/pkg/webui/ui/src/components/PropertiesTable.tsx b/pkg/webui/ui/src/components/PropertiesTable.tsx index 61237aefb..0f189fab6 100644 --- a/pkg/webui/ui/src/components/PropertiesTable.tsx +++ b/pkg/webui/ui/src/components/PropertiesTable.tsx @@ -5,11 +5,10 @@ import TableCell from '@mui/material/TableCell'; import TableContainer from '@mui/material/TableContainer'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; -import Paper from '@mui/material/Paper'; export function PropertiesTable(props: {properties: {name: string, value: React.ReactNode}[]}) { return ( - +
    diff --git a/pkg/webui/ui/src/components/result-view/ChangesTable.tsx b/pkg/webui/ui/src/components/result-view/ChangesTable.tsx index b802201f3..0082446be 100644 --- a/pkg/webui/ui/src/components/result-view/ChangesTable.tsx +++ b/pkg/webui/ui/src/components/result-view/ChangesTable.tsx @@ -5,18 +5,16 @@ import TableCell from '@mui/material/TableCell'; import TableContainer from '@mui/material/TableContainer'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; -import Paper from '@mui/material/Paper'; import { buildRefKindElement, buildRefString } from "../../api"; import { Box, Typography } from "@mui/material"; import { CodeViewer } from "../CodeViewer"; import { DiffStatus } from "./nodes/NodeData"; -import Divider from "@mui/material/Divider"; import { ObjectRef } from "../../models"; const RefList = (props: { title: string, refs: ObjectRef[] }) => { - return + return {props.title} - +
    @@ -48,7 +46,6 @@ const RefList = (props: { title: string, refs: ObjectRef[] }) => {
    -
    } @@ -56,15 +53,22 @@ export function ChangesTable(props: { diffStatus: DiffStatus }) { let changedObjects: React.ReactElement | undefined if (props.diffStatus.changedObjects.length) { - changedObjects = + changedObjects = Changed Objects {props.diffStatus.changedObjects.map((co, i) => ( - + - - {buildRefString(co.ref)} + + {buildRefString(co.ref)} @@ -81,25 +85,25 @@ export function ChangesTable(props: { diffStatus: DiffStatus }) { - + ))}
    - ))} - -
    + )) + } +
    } return {props.diffStatus.newObjects.length ? - : <>} + : <>} {props.diffStatus.deletedObjects.length ? - : <>} + : <>} {props.diffStatus.orphanObjects.length ? - : <>} + : <>} {changedObjects} } \ No newline at end of file diff --git a/pkg/webui/ui/src/components/result-view/SidePanel.tsx b/pkg/webui/ui/src/components/result-view/SidePanel.tsx index 9a2047a7c..bfb5c17b0 100644 --- a/pkg/webui/ui/src/components/result-view/SidePanel.tsx +++ b/pkg/webui/ui/src/components/result-view/SidePanel.tsx @@ -1,4 +1,4 @@ -import { Box, Divider, IconButton, Tab, ThemeProvider, Typography, useTheme } from "@mui/material"; +import { Box, Divider, IconButton, Paper, Tab, ThemeProvider, Typography, useTheme } from "@mui/material"; import React, { useEffect, useState } from "react"; import { TabContext, TabList, TabPanel } from "@mui/lab"; import { CloseIcon } from "../../icons/Icons"; @@ -59,7 +59,7 @@ export const SidePanel = (props: SidePanelProps) => { return <> } - return + return @@ -83,11 +83,13 @@ export const SidePanel = (props: SidePanelProps) => { - + {tabs.map((tab, index) => { - return - - {tab.content} + return + + + {tab.content} + })} diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx index 298fbe66a..5820d9dab 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx @@ -4,8 +4,9 @@ import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; import React, { Suspense, useEffect, useState } from "react"; import { NodeData } from "../result-view/nodes/NodeData"; import { SidePanel } from "../result-view/SidePanel"; -import { Box, Drawer } from "@mui/material"; +import { Box, Drawer, ThemeProvider } from "@mui/material"; import { Loading } from "../Loading"; +import { dark } from "../theme"; async function doGetRootNode(rs: CommandResultSummary) { const shortNames = api.getShortNames() @@ -34,21 +35,24 @@ export const CommandResultDetailsDrawer = (props: { rs?: CommandResultSummary, o setPromise(doGetRootNode(props.rs)) }, [props.rs]) - const Content = () => { + const Content = (props: { onClose: () => void }) => { const node = usePromise(promise) - return + return } - return props.onClose()} - > - - }> - - - - + return + + + }> + + + + + } \ No newline at end of file diff --git a/pkg/webui/ui/src/components/theme.ts b/pkg/webui/ui/src/components/theme.ts index c599bf1cd..86d328802 100644 --- a/pkg/webui/ui/src/components/theme.ts +++ b/pkg/webui/ui/src/components/theme.ts @@ -53,6 +53,13 @@ export const light = createTheme(common, { lineHeight: '27px', letterSpacing: '1px', }, + h5: { + color: paletteLight.secondary.main, + fontWeight: 700, + fontSize: '22px', + lineHeight: '30px', + letterSpacing: '1px', + }, h6: { color: paletteLight.secondary.main, fontWeight: 800, @@ -94,10 +101,13 @@ export const light = createTheme(common, { right: 0, bottom: '10px', display: 'block', - borderRight: '0.5px solid #39403E', + borderRight: `0.5px solid ${paletteLight.secondary.main}`, }, ':last-of-type:after': { content: 'none' + }, + ':last-of-type': { + overflowWrap: 'anywhere' } }, head: { @@ -114,7 +124,7 @@ export const dark = createTheme(common, { allVariants: { color: paletteDark.text.primary }, - h4: { + h4: { color: paletteDark.text.primary, fontWeight: 700, fontSize: '24px', @@ -180,7 +190,11 @@ export const dark = createTheme(common, { lineHeight: '22px', letterSpacing: '1px', textTransform: 'none', - padding: '7px 5px' + padding: '7px 5px', + color: '#8A8E91', + '&.Mui-selected': { + color: paletteDark.text.primary + } } } } From 333507ebad348f8aa111e5c7a18432f0d89e9181 Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Thu, 1 Jun 2023 03:25:52 +0600 Subject: [PATCH 1133/2268] Main Page: replaced all icons. --- .../result-view/CommandResultStatusLine.tsx | 14 +++--- .../targets-view/CommandResultItem.tsx | 17 ++++--- pkg/webui/ui/src/icons/Icons.tsx | 47 ++++++++++++++++++- pkg/webui/ui/src/icons/added.svg | 4 ++ pkg/webui/ui/src/icons/changed.svg | 5 ++ pkg/webui/ui/src/icons/deploy.svg | 8 ++++ pkg/webui/ui/src/icons/diff.svg | 8 ++++ pkg/webui/ui/src/icons/error.svg | 4 ++ pkg/webui/ui/src/icons/orphan.svg | 6 +++ pkg/webui/ui/src/icons/prune.svg | 8 ++++ pkg/webui/ui/src/icons/trash.svg | 6 +++ pkg/webui/ui/src/icons/warning.svg | 5 ++ 12 files changed, 115 insertions(+), 17 deletions(-) create mode 100644 pkg/webui/ui/src/icons/added.svg create mode 100644 pkg/webui/ui/src/icons/changed.svg create mode 100644 pkg/webui/ui/src/icons/deploy.svg create mode 100644 pkg/webui/ui/src/icons/diff.svg create mode 100644 pkg/webui/ui/src/icons/error.svg create mode 100644 pkg/webui/ui/src/icons/orphan.svg create mode 100644 pkg/webui/ui/src/icons/prune.svg create mode 100644 pkg/webui/ui/src/icons/trash.svg create mode 100644 pkg/webui/ui/src/icons/warning.svg diff --git a/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx b/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx index 11b9721d6..4794f31ae 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx @@ -1,7 +1,7 @@ import { CommandResultSummary, ValidateResult } from "../../models"; import { Box, Tooltip, Typography } from "@mui/material"; -import { Dangerous, Delete, Difference, LibraryAdd, LinkOff, Warning } from "@mui/icons-material"; import React from "react"; +import { AddedIcon, ChangedIcon, ErrorIcon, OrphanIcon, TrashIcon, WarningIcon } from "../../icons/Icons"; export interface StatusLineProps { errors?: number @@ -36,12 +36,12 @@ export const StatusLine = (props: StatusLineProps) => { } } - doPush(props.errors, "total errors.", ) - doPush(props.warnings, "total warnings.", ) - doPush(props.newObjects, "new objects.", ) - doPush(props.deletedObjects, "deleted objects.", ) - doPush(props.orphanObjects, "orphan objects.", ) - doPush(props.changedObjects, "changed objects.", ) + doPush(props.errors, "total errors.", ) + doPush(props.warnings, "total warnings.", ) + doPush(props.newObjects, "new objects.", ) + doPush(props.deletedObjects, "deleted objects.", ) + doPush(props.orphanObjects, "orphan objects.", ) + doPush(props.changedObjects, "changed objects.", ) return {children} diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index 3c5a1a5d2..3e305c9b2 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -1,5 +1,4 @@ import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; -import { CleaningServices, CloudSync, Delete, Difference } from "@mui/icons-material"; import { useEffect, useMemo, useState } from "react"; import * as yaml from "js-yaml"; import { CodeViewer } from "../CodeViewer"; @@ -8,7 +7,7 @@ import { Box, IconButton, Tooltip, Typography } from "@mui/material"; import { CommandResultStatusLine } from "../result-view/CommandResultStatusLine"; import { useNavigate } from "react-router"; import { formatDurationShort } from "../../utils/duration"; -import { TreeViewIcon } from "../../icons/Icons"; +import { DeployIcon, DiffIcon, PruneIcon, TreeViewIcon } from "../../icons/Icons"; export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary, rs: CommandResultSummary, onSelectCommandResult: (rs?: CommandResultSummary) => void }) => { const calcAgo = () => { @@ -21,22 +20,22 @@ export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary const navigate = useNavigate() const [ago, setAgo] = useState(calcAgo()) - let Icon = Difference + let Icon: () => JSX.Element = DiffIcon switch (props.rs.commandInfo?.command) { case "delete": - Icon = Delete + Icon = PruneIcon break case "deploy": - Icon = CloudSync + Icon = DeployIcon break case "diff": - Icon = Difference + Icon = DiffIcon break case "poke-images": - Icon = CloudSync + Icon = DeployIcon break case "prune": - Icon = CleaningServices + Icon = PruneIcon break } @@ -66,7 +65,7 @@ export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary - + diff --git a/pkg/webui/ui/src/icons/Icons.tsx b/pkg/webui/ui/src/icons/Icons.tsx index 156c0233f..34c9fca56 100644 --- a/pkg/webui/ui/src/icons/Icons.tsx +++ b/pkg/webui/ui/src/icons/Icons.tsx @@ -10,6 +10,15 @@ import { ReactComponent as CpuIconSvg } from './cpu.svg'; import { ReactComponent as FingerScanIconSvg } from './finger-scan.svg'; import { ReactComponent as TreeViewIconSvg } from './tree-view.svg'; import { ReactComponent as CloseIconSvg } from './close.svg'; +import { ReactComponent as DeployIconSvg } from './deploy.svg'; +import { ReactComponent as PruneIconSvg } from './prune.svg'; +import { ReactComponent as DiffIconSvg } from './diff.svg'; +import { ReactComponent as WarningIconSvg } from './warning.svg'; +import { ReactComponent as ErrorIconSvg } from './error.svg'; +import { ReactComponent as TrashIconSvg } from './trash.svg'; +import { ReactComponent as OrphanIconSvg } from './orphan.svg'; +import { ReactComponent as AddedIconSvg } from './added.svg'; +import { ReactComponent as ChangedIconSvg } from './changed.svg'; export const KluctlText = () => { return @@ -42,6 +51,18 @@ export const ProjectIcon = () => { return } +export const DeployIcon = () => { + return +} + +export const PruneIcon = () => { + return +} + +export const DiffIcon = () => { + return +} + export const CpuIcon = () => { return } @@ -66,4 +87,28 @@ export const TreeViewIcon = () => { export const CloseIcon = () => { return -} \ No newline at end of file +} + +export const WarningIcon = () => { + return +} + +export const ErrorIcon = () => { + return +} + +export const TrashIcon = () => { + return +} + +export const OrphanIcon = () => { + return +} + +export const AddedIcon = () => { + return +} + +export const ChangedIcon = () => { + return +} diff --git a/pkg/webui/ui/src/icons/added.svg b/pkg/webui/ui/src/icons/added.svg new file mode 100644 index 000000000..1086507eb --- /dev/null +++ b/pkg/webui/ui/src/icons/added.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/ui/src/icons/changed.svg b/pkg/webui/ui/src/icons/changed.svg new file mode 100644 index 000000000..5ee26bbe6 --- /dev/null +++ b/pkg/webui/ui/src/icons/changed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/webui/ui/src/icons/deploy.svg b/pkg/webui/ui/src/icons/deploy.svg new file mode 100644 index 000000000..a3be467e3 --- /dev/null +++ b/pkg/webui/ui/src/icons/deploy.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pkg/webui/ui/src/icons/diff.svg b/pkg/webui/ui/src/icons/diff.svg new file mode 100644 index 000000000..ad1b92774 --- /dev/null +++ b/pkg/webui/ui/src/icons/diff.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pkg/webui/ui/src/icons/error.svg b/pkg/webui/ui/src/icons/error.svg new file mode 100644 index 000000000..2f9a8cd4b --- /dev/null +++ b/pkg/webui/ui/src/icons/error.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/ui/src/icons/orphan.svg b/pkg/webui/ui/src/icons/orphan.svg new file mode 100644 index 000000000..43e777661 --- /dev/null +++ b/pkg/webui/ui/src/icons/orphan.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/pkg/webui/ui/src/icons/prune.svg b/pkg/webui/ui/src/icons/prune.svg new file mode 100644 index 000000000..7c4784206 --- /dev/null +++ b/pkg/webui/ui/src/icons/prune.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pkg/webui/ui/src/icons/trash.svg b/pkg/webui/ui/src/icons/trash.svg new file mode 100644 index 000000000..426eec953 --- /dev/null +++ b/pkg/webui/ui/src/icons/trash.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/pkg/webui/ui/src/icons/warning.svg b/pkg/webui/ui/src/icons/warning.svg new file mode 100644 index 000000000..788515486 --- /dev/null +++ b/pkg/webui/ui/src/icons/warning.svg @@ -0,0 +1,5 @@ + + + + + From 378b76bdecb62e3477a190ff9aa91fc74da75bdb Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Thu, 1 Jun 2023 21:22:17 +0600 Subject: [PATCH 1134/2268] Updated left drawer styles. --- pkg/webui/ui/src/components/LeftDrawer.tsx | 29 +++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/pkg/webui/ui/src/components/LeftDrawer.tsx b/pkg/webui/ui/src/components/LeftDrawer.tsx index a5bd4e5fe..85df4d530 100644 --- a/pkg/webui/ui/src/components/LeftDrawer.tsx +++ b/pkg/webui/ui/src/components/LeftDrawer.tsx @@ -100,24 +100,36 @@ const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' ); function Item(props: { text: string, open: boolean, icon: React.ReactNode, to: string }) { - return + return {props.icon} - + } @@ -180,9 +192,8 @@ export default function LeftDrawer(props: { content: React.ReactNode, context: A - + } to={"targets"} /> - {context.filters} {context.filters && } @@ -190,7 +201,7 @@ export default function LeftDrawer(props: { content: React.ReactNode, context: A - + {props.content} From ecdf0c49a94fabe31d646661635dfd7c09dee8f2 Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Thu, 1 Jun 2023 22:00:50 +0600 Subject: [PATCH 1135/2268] Fix tooltips for icons of Command result nodes. --- .../ui/src/components/targets-view/CommandResultItem.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index 3e305c9b2..8812d8181 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -63,11 +63,11 @@ export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary > - - + + - - + + Date: Fri, 2 Jun 2023 01:26:42 +0600 Subject: [PATCH 1136/2268] Result tree page: added filters. --- pkg/webui/ui/src/components/App.tsx | 9 +- pkg/webui/ui/src/components/LeftDrawer.tsx | 96 ++++++++------ .../result-view/CommandResultView.tsx | 123 +++++++++++++++--- pkg/webui/ui/src/icons/Icons.tsx | 35 +++++ pkg/webui/ui/src/icons/arrow-left.svg | 4 + pkg/webui/ui/src/icons/changes.svg | 5 + pkg/webui/ui/src/icons/checkbox-checked.svg | 5 + pkg/webui/ui/src/icons/checkbox-disabled.svg | 4 + pkg/webui/ui/src/icons/checkbox.svg | 3 + pkg/webui/ui/src/icons/star.svg | 4 + pkg/webui/ui/src/icons/warning-sign.svg | 5 + 11 files changed, 236 insertions(+), 57 deletions(-) create mode 100644 pkg/webui/ui/src/icons/arrow-left.svg create mode 100644 pkg/webui/ui/src/icons/changes.svg create mode 100644 pkg/webui/ui/src/icons/checkbox-checked.svg create mode 100644 pkg/webui/ui/src/icons/checkbox-disabled.svg create mode 100644 pkg/webui/ui/src/icons/checkbox.svg create mode 100644 pkg/webui/ui/src/icons/star.svg create mode 100644 pkg/webui/ui/src/icons/warning-sign.svg diff --git a/pkg/webui/ui/src/components/App.tsx b/pkg/webui/ui/src/components/App.tsx index ba36ddc4a..7c326111b 100644 --- a/pkg/webui/ui/src/components/App.tsx +++ b/pkg/webui/ui/src/components/App.tsx @@ -1,14 +1,15 @@ -import React, { useState } from 'react'; +import React, { Dispatch, SetStateAction, useState } from 'react'; import '../index.css'; import { Box, ThemeProvider } from "@mui/material"; import { Outlet, useOutletContext } from "react-router-dom"; import LeftDrawer from "./LeftDrawer"; import { light } from './theme'; +import { ActiveFilters } from './result-view/NodeStatusFilter'; export interface AppOutletContext { - filters?: React.ReactNode - setFilters: (filter: React.ReactNode) => void + filters?: ActiveFilters + setFilters: Dispatch> } export function useAppOutletContext(): AppOutletContext { @@ -16,7 +17,7 @@ export function useAppOutletContext(): AppOutletContext { } const App = () => { - const [filters, setFilters] = useState() + const [filters, setFilters] = useState() const context: AppOutletContext = { filters: filters, diff --git a/pkg/webui/ui/src/components/LeftDrawer.tsx b/pkg/webui/ui/src/components/LeftDrawer.tsx index 85df4d530..62cacf4a5 100644 --- a/pkg/webui/ui/src/components/LeftDrawer.tsx +++ b/pkg/webui/ui/src/components/LeftDrawer.tsx @@ -11,9 +11,9 @@ import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; -import { Link } from "react-router-dom"; +import { Link, useLocation, useNavigate } from "react-router-dom"; import { AppOutletContext } from "./App"; -import { KluctlLogo, TargetsIcon, KluctlText, SearchIcon } from '../icons/Icons'; +import { KluctlLogo, TargetsIcon, KluctlText, SearchIcon, ArrowLeftIcon } from '../icons/Icons'; import { dark } from './theme'; import { Typography } from '@mui/material'; @@ -135,50 +135,74 @@ function Item(props: { text: string, open: boolean, icon: React.ReactNode, to: s } export default function LeftDrawer(props: { content: React.ReactNode, context: AppOutletContext }) { - const context = props.context - const [open, setOpen] = useState(true); - + const location = useLocation() + const navigate = useNavigate(); const theme = useTheme(); const toggleDrawer = () => { setOpen(o => !o); }; + const path = location.pathname.split('/')[1]; + let header: JSX.Element | null = null; + switch (path) { + case 'targets': + header = <> + + Dashboard + + + + + + + + + break; + case 'results': + header = + navigate('/targets')}> + + + + Result Tree + + + break; + } + return ( - - Dashboard - - - - - - - + {header} @@ -194,9 +218,7 @@ export default function LeftDrawer(props: { content: React.ReactNode, context: A } to={"targets"} /> - {context.filters} - {context.filters && } diff --git a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx index 607bc925b..a09a061f5 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useEffect, useState } from 'react'; -import { Box, Divider } from "@mui/material"; +import { Box, Checkbox, CheckboxProps, Divider, FormControlLabel, FormLabel, Typography } from "@mui/material"; import { CommandResult, CommandResultSummary, ShortName } from "../../models"; import { NodeData } from "./nodes/NodeData"; import { SidePanel } from "./SidePanel"; @@ -9,6 +9,7 @@ import CommandResultTree from "./CommandResultTree"; import { useLoaderData } from "react-router-dom"; import { api } from "../../api"; import { useAppOutletContext } from "../App"; +import { ChangesIcon, CheckboxCheckedIcon, CheckboxIcon, StarIcon, WarningSignIcon } from '../../icons/Icons'; export interface CommandResultProps { shortNames: ShortName[] @@ -28,26 +29,116 @@ export async function commandResultLoader({ params }: any) { } } +const FilterCheckbox = (props: { + text: string, + checked: boolean, + Icon: () => JSX.Element, + onChange: CheckboxProps['onChange'] +}) => { + const { text, checked, Icon, onChange } = props; + return } + checkedIcon={} + /> + } + slotProps={{ + typography: { + sx: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + gap: '10px', + } + } + }} + label={ + <> + {text} + + + + + } + /> +} + +const defaultFilters = { + onlyImportant: false, + onlyChanged: false, + onlyWithErrorsOrWarnings: false +} + export const CommandResultView = () => { - const context = useAppOutletContext() - const commandResultProps = useLoaderData() as CommandResultProps - const [activeFilters, setActiveFilters] = useState() - const [sidePanelNode, setSidePanelNode] = useState() - - useEffect(() => { - context.setFilters(<> - - ) - }, []) - - return + const context = useAppOutletContext(); + const commandResultProps = useLoaderData() as CommandResultProps; + const [sidePanelNode, setSidePanelNode] = useState(); + + const divider = ; + + const handleFilterChange = (filter: keyof ActiveFilters) => (_: React.ChangeEvent, checked: boolean) => { + context.setFilters(fs => ({ + ...(fs || defaultFilters), + [filter]: checked + })); + } + + return + + + {divider} + + {divider} + + + + activeFilters={context.filters} /> - - + } diff --git a/pkg/webui/ui/src/icons/Icons.tsx b/pkg/webui/ui/src/icons/Icons.tsx index 34c9fca56..19227081e 100644 --- a/pkg/webui/ui/src/icons/Icons.tsx +++ b/pkg/webui/ui/src/icons/Icons.tsx @@ -19,6 +19,13 @@ import { ReactComponent as TrashIconSvg } from './trash.svg'; import { ReactComponent as OrphanIconSvg } from './orphan.svg'; import { ReactComponent as AddedIconSvg } from './added.svg'; import { ReactComponent as ChangedIconSvg } from './changed.svg'; +import { ReactComponent as CheckboxIconSvg } from './checkbox.svg'; +import { ReactComponent as CheckboxCheckedIconSvg } from './checkbox-checked.svg'; +import { ReactComponent as CheckboxDisabledIconSvg } from './checkbox-disabled.svg'; +import { ReactComponent as ArrowLeftIconSvg } from './arrow-left.svg'; +import { ReactComponent as WarningSignIconSvg } from './warning-sign.svg'; +import { ReactComponent as ChangesIconSvg } from './changes.svg'; +import { ReactComponent as StarIconSvg } from './star.svg'; export const KluctlText = () => { return @@ -112,3 +119,31 @@ export const AddedIcon = () => { export const ChangedIcon = () => { return } + +export const CheckboxIcon = () => { + return +} + +export const CheckboxCheckedIcon = () => { + return +} + +export const CheckboxDisabledIcon = () => { + return +} + +export const ArrowLeftIcon = () => { + return +} + +export const WarningSignIcon = () => { + return +} + +export const ChangesIcon = () => { + return +} + +export const StarIcon = () => { + return +} diff --git a/pkg/webui/ui/src/icons/arrow-left.svg b/pkg/webui/ui/src/icons/arrow-left.svg new file mode 100644 index 000000000..5f8ccdd11 --- /dev/null +++ b/pkg/webui/ui/src/icons/arrow-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/ui/src/icons/changes.svg b/pkg/webui/ui/src/icons/changes.svg new file mode 100644 index 000000000..fe25e1e1f --- /dev/null +++ b/pkg/webui/ui/src/icons/changes.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/webui/ui/src/icons/checkbox-checked.svg b/pkg/webui/ui/src/icons/checkbox-checked.svg new file mode 100644 index 000000000..f9e6a1557 --- /dev/null +++ b/pkg/webui/ui/src/icons/checkbox-checked.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/webui/ui/src/icons/checkbox-disabled.svg b/pkg/webui/ui/src/icons/checkbox-disabled.svg new file mode 100644 index 000000000..9922728ac --- /dev/null +++ b/pkg/webui/ui/src/icons/checkbox-disabled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/ui/src/icons/checkbox.svg b/pkg/webui/ui/src/icons/checkbox.svg new file mode 100644 index 000000000..f9f5f7b78 --- /dev/null +++ b/pkg/webui/ui/src/icons/checkbox.svg @@ -0,0 +1,3 @@ + + + diff --git a/pkg/webui/ui/src/icons/star.svg b/pkg/webui/ui/src/icons/star.svg new file mode 100644 index 000000000..0fda929ba --- /dev/null +++ b/pkg/webui/ui/src/icons/star.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/ui/src/icons/warning-sign.svg b/pkg/webui/ui/src/icons/warning-sign.svg new file mode 100644 index 000000000..e38fa71b2 --- /dev/null +++ b/pkg/webui/ui/src/icons/warning-sign.svg @@ -0,0 +1,5 @@ + + + + + From 97975b31430abc2cee5021fe6c21d492ef81b031 Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Fri, 2 Jun 2023 05:08:05 +0600 Subject: [PATCH 1137/2268] WIP Result tree page. --- .../result-view/CommandResultTree.tsx | 70 +++++++++++++++---- .../result-view/CommandResultView.tsx | 29 +++++--- pkg/webui/ui/src/components/theme.ts | 2 +- 3 files changed, 78 insertions(+), 23 deletions(-) diff --git a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx index d1986046a..f9c00216d 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx @@ -9,6 +9,7 @@ import { NodeData } from "./nodes/NodeData"; import { ActiveFilters, FilterNode } from "./NodeStatusFilter"; import { CommandResultProps } from "./CommandResultView"; import { Loading } from "../Loading"; +import { Box, Divider, Paper, useTheme } from '@mui/material'; export interface CommandResultTreeProps { commandResultProps?: CommandResultProps @@ -18,6 +19,7 @@ export interface CommandResultTreeProps { } const CommandResultTree = (props: CommandResultTreeProps) => { + const theme = useTheme(); const [expanded, setExpanded] = useState(["root"]); const [selectedNodeId, setSelectedNodeId] = useState() @@ -67,8 +69,50 @@ const CommandResultTree = (props: CommandResultTreeProps) => { return handleItemClick(e, nodes)}>{nodes.buildTreeItem()}} - sx={{ marginBottom: "5px", marginTop: "5px" }} + label={ + handleItemClick(e, nodes)} + pl='22px' + position='relative' + > + {nodes.children.length !== 0 && + + } + {nodes.buildTreeItem()} + + } + sx={{ + '& .MuiTreeItem-content': { + height: '78px', + borderBottom: `0.5px solid ${theme.palette.secondary.main}`, + padding: 0, + '& .MuiTreeItem-iconContainer': { + width: '50px', + height: '50px', + margin: 0, + padding: 0, + display: nodes.children.length !== 0 ? 'flex' : 'none', + justifyContent: 'center', + alignItems: 'center', + }, + '& .MuiTreeItem-label': { + margin: 0, + padding: 0 + } + }, + '& .MuiTreeItem-group': { + margin: '0 0 0 38px' + }, + }} onDoubleClick={(e: React.SyntheticEvent) => handleDoubleClick(e, nodes)} > {Array.isArray(nodes.children) @@ -78,18 +122,20 @@ const CommandResultTree = (props: CommandResultTreeProps) => { }; if (!rootNode) { - return + return } - return } - defaultExpandIcon={} - sx={{ width: "100%" }} - > - {renderTree(rootNode)} - + return + } + defaultExpandIcon={} + sx={{ width: "100%" }} + > + {renderTree(rootNode)} + + } export default CommandResultTree; diff --git a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx index a09a061f5..b9186b71e 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { useEffect, useState } from 'react'; -import { Box, Checkbox, CheckboxProps, Divider, FormControlLabel, FormLabel, Typography } from "@mui/material"; +import { useState } from 'react'; +import { Box, Checkbox, CheckboxProps, Divider, FormControlLabel, Typography } from "@mui/material"; import { CommandResult, CommandResultSummary, ShortName } from "../../models"; import { NodeData } from "./nodes/NodeData"; import { SidePanel } from "./SidePanel"; -import { ActiveFilters, NodeStatusFilter } from "./NodeStatusFilter"; +import { ActiveFilters } from "./NodeStatusFilter"; import CommandResultTree from "./CommandResultTree"; import { useLoaderData } from "react-router-dom"; import { api } from "../../api"; @@ -109,8 +109,14 @@ export const CommandResultView = () => { })); } - return - + return + { onChange={handleFilterChange('onlyWithErrorsOrWarnings')} /> - - - + + + - + diff --git a/pkg/webui/ui/src/components/theme.ts b/pkg/webui/ui/src/components/theme.ts index 86d328802..14680cd0a 100644 --- a/pkg/webui/ui/src/components/theme.ts +++ b/pkg/webui/ui/src/components/theme.ts @@ -73,7 +73,7 @@ export const light = createTheme(common, { MuiDivider: { styleOverrides: { root: { - borderColor: 'rgba(0, 0, 0, 0.5)' + borderColor: paletteLight.secondary.main, } } }, From 876f49e34c60ae317c62629c5349e24c06bfaa78 Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Sat, 3 Jun 2023 02:19:11 +0600 Subject: [PATCH 1138/2268] Result tree styling. --- .../result-view/CommandResultTree.tsx | 19 ++- .../result-view/nodes/CommandResultNode.tsx | 4 +- .../nodes/DeploymentItemIncludeNode.tsx | 5 +- .../components/result-view/nodes/NodeData.tsx | 108 ++++++++++++++---- .../result-view/nodes/ObjectNode.tsx | 38 +++--- .../nodes/VarsSourceCollectionNode.tsx | 4 +- .../result-view/nodes/VarsSourceNode.tsx | 6 +- pkg/webui/ui/src/icons/Icons.tsx | 35 ++++++ pkg/webui/ui/src/icons/brackets-curly.svg | 3 + pkg/webui/ui/src/icons/brackets-square.svg | 3 + pkg/webui/ui/src/icons/file.svg | 5 + pkg/webui/ui/src/icons/include.svg | 7 ++ pkg/webui/ui/src/icons/result.svg | 8 ++ pkg/webui/ui/src/icons/triangle-down.svg | 4 + pkg/webui/ui/src/icons/triangle-right.svg | 4 + 15 files changed, 194 insertions(+), 59 deletions(-) create mode 100644 pkg/webui/ui/src/icons/brackets-curly.svg create mode 100644 pkg/webui/ui/src/icons/brackets-square.svg create mode 100644 pkg/webui/ui/src/icons/file.svg create mode 100644 pkg/webui/ui/src/icons/include.svg create mode 100644 pkg/webui/ui/src/icons/result.svg create mode 100644 pkg/webui/ui/src/icons/triangle-down.svg create mode 100644 pkg/webui/ui/src/icons/triangle-right.svg diff --git a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx index f9c00216d..56c3174b7 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx @@ -2,14 +2,13 @@ import * as React from 'react'; import { useEffect, useMemo, useState } from 'react'; import TreeView from '@mui/lab/TreeView'; import { TreeItem } from "@mui/lab"; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import { NodeBuilder } from "./nodes/NodeBuilder"; import { NodeData } from "./nodes/NodeData"; import { ActiveFilters, FilterNode } from "./NodeStatusFilter"; import { CommandResultProps } from "./CommandResultView"; import { Loading } from "../Loading"; import { Box, Divider, Paper, useTheme } from '@mui/material'; +import { TriangleDownIcon, TriangleRightIcon } from '../../icons/Icons'; export interface CommandResultTreeProps { commandResultProps?: CommandResultProps @@ -75,6 +74,8 @@ const CommandResultTree = (props: CommandResultTreeProps) => { alignItems='center' onClick={(e: React.SyntheticEvent) => handleItemClick(e, nodes)} pl='22px' + height='100%' + flex='1 1 auto' position='relative' > {nodes.children.length !== 0 && @@ -87,7 +88,7 @@ const CommandResultTree = (props: CommandResultTreeProps) => { }} /> } - {nodes.buildTreeItem()} + {nodes.buildTreeItem(nodes.children.length !== 0)} } sx={{ @@ -95,9 +96,11 @@ const CommandResultTree = (props: CommandResultTreeProps) => { height: '78px', borderBottom: `0.5px solid ${theme.palette.secondary.main}`, padding: 0, + overflow: 'hidden', '& .MuiTreeItem-iconContainer': { width: '50px', height: '50px', + flex: '0 0 auto', margin: 0, padding: 0, display: nodes.children.length !== 0 ? 'flex' : 'none', @@ -105,8 +108,12 @@ const CommandResultTree = (props: CommandResultTreeProps) => { alignItems: 'center', }, '& .MuiTreeItem-label': { + height: '100%', margin: 0, - padding: 0 + padding: 0, + flex: '1 1 auto', + display: 'flex', + alignItems: 'center' } }, '& .MuiTreeItem-group': { @@ -129,8 +136,8 @@ const CommandResultTree = (props: CommandResultTreeProps) => { } - defaultExpandIcon={} + defaultCollapseIcon={} + defaultExpandIcon={} sx={{ width: "100%" }} > {renderTree(rootNode)} diff --git a/pkg/webui/ui/src/components/result-view/nodes/CommandResultNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/CommandResultNode.tsx index 170b653af..9e5e37266 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/CommandResultNode.tsx +++ b/pkg/webui/ui/src/components/result-view/nodes/CommandResultNode.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { NodeData } from "./NodeData"; -import { CloudSync } from "@mui/icons-material"; import { PropertiesTable } from "../../PropertiesTable"; import { CodeViewer } from "../../CodeViewer"; import { CommandResultProps } from "../CommandResultView"; import * as yaml from 'js-yaml'; import { SidePanelTab } from "../SidePanel"; +import { DeployIcon } from '../../../icons/Icons'; export class CommandResultNodeData extends NodeData { dumpedTargetYaml?: string @@ -25,7 +25,7 @@ export class CommandResultNodeData extends NodeData { } buildIcon(): [React.ReactNode, string] { - return [, "result"] + return [, "result"] } buildSidePanelTabs(): SidePanelTab[] { diff --git a/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx index 1d33bf8b6..096917f17 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx +++ b/pkg/webui/ui/src/components/result-view/nodes/DeploymentItemIncludeNode.tsx @@ -2,8 +2,7 @@ import React from 'react'; import { DeploymentItemConfig, DeploymentProjectConfig } from "../../../models"; import { NodeData } from "./NodeData"; -import { FolderZip } from "@mui/icons-material"; -import { GitIcon } from "../../../icons/Icons"; +import { GitIcon, IncludeIcon } from "../../../icons/Icons"; import { CommandResultProps } from "../CommandResultView"; import { PropertiesTable } from "../../PropertiesTable"; import { buildDeploymentItemSummaryProps } from "./DeploymentItemNode"; @@ -39,7 +38,7 @@ export class DeploymentItemIncludeNodeData extends NodeData { if (this.deploymentItem.git) { return [, "git"] } - return [, "include"] + return [, "include"] } buildSidePanelTabs(): SidePanelTab[] { diff --git a/pkg/webui/ui/src/components/result-view/nodes/NodeData.tsx b/pkg/webui/ui/src/components/result-view/nodes/NodeData.tsx index 55c3d66d4..771c5fdce 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/NodeData.tsx +++ b/pkg/webui/ui/src/components/result-view/nodes/NodeData.tsx @@ -1,6 +1,6 @@ import { ChangedObject, DeploymentError, ObjectRef, ResultObject } from "../../../models"; import React from "react"; -import { Box, Typography } from "@mui/material"; +import { Box, Divider, Typography } from "@mui/material"; import { CommandResultProps } from "../CommandResultView"; import { ChangesTable } from "../ChangesTable"; import { ErrorsTable } from "../../ErrorsTable"; @@ -123,26 +123,86 @@ export abstract class NodeData implements SidePanelProvider { /> } - buildTreeItem(): React.ReactNode { - const [icon, iconText] = this.buildIcon() - - return - - - - {icon} - {iconText} - - - - - {this.buildSidePanelTitle()}
    -
    - - {this.buildStatusLine()} - -
    + buildTreeItem(hasChildren?: boolean): React.ReactNode { + const [icon, iconText] = this.buildIcon(); + + const hasStatusLine = [ + this.healthStatus?.errors.length, + this.healthStatus?.warnings.length, + this.diffStatus?.changedObjects.length, + this.diffStatus?.newObjects.length, + this.diffStatus?.deletedObjects.length, + this.diffStatus?.orphanObjects.length, + ].some(x => (x || 0) > 0); + + return + + {icon} + + {iconText} + + + + {this.buildSidePanelTitle()} + + + {hasStatusLine && + + {this.buildStatusLine()} + } } @@ -153,7 +213,7 @@ export abstract class NodeData implements SidePanelProvider { } buildObjectPage(ref: ObjectRef, objectType: ObjectType): React.ReactNode { - return + return } buildChangesPage(tabs: { label: string, content: React.ReactNode }[]) { @@ -162,7 +222,7 @@ export abstract class NodeData implements SidePanelProvider { } tabs.push({ label: "Changes", - content: + content: }) } @@ -172,7 +232,7 @@ export abstract class NodeData implements SidePanelProvider { } tabs.push({ label: "Errors", - content: + content: }) } @@ -182,7 +242,7 @@ export abstract class NodeData implements SidePanelProvider { } tabs.push({ label: "Warnings", - content: + content: }) } } diff --git a/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx index 62a5b133f..ec851d53f 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx +++ b/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx @@ -3,7 +3,6 @@ import React from 'react'; import { ObjectRef } from "../../../models"; import { NodeData } from "./NodeData"; import { - DataObject, PublishedWithChanges, Settings, SettingsEthernet, @@ -14,12 +13,13 @@ import { PropertiesTable } from "../../PropertiesTable"; import { findObjectByRef, ObjectType } from "../../../api"; import { CommandResultProps } from "../CommandResultView"; import { SidePanelTab } from "../SidePanel"; +import { BracketsCurlyIcon } from '../../../icons/Icons'; -const kindMapping: {[key: string]: {icon: SvgIconComponent}} = { - "/ConfigMap": {icon: Settings}, - "apps/Deployment": {icon: PublishedWithChanges}, - "Service": {icon: SettingsEthernet}, - "ServiceAccount": {icon: SmartToy} +const kindMapping: { [key: string]: { icon: SvgIconComponent } } = { + "/ConfigMap": { icon: Settings }, + "apps/Deployment": { icon: PublishedWithChanges }, + "Service": { icon: SettingsEthernet }, + "ServiceAccount": { icon: SmartToy } } export class ObjectNodeData extends NodeData { @@ -40,15 +40,15 @@ export class ObjectNodeData extends NodeData { const m = kindMapping[this.objectRef.group + "/" + this.objectRef.kind] if (m !== undefined) { - return [React.createElement(m.icon, {fontSize: "large"}), snStr] + return [React.createElement(m.icon, { fontSize: "large" }), snStr] } - return [, snStr] + return [, snStr] } buildSidePanelTabs(): SidePanelTab[] { const tabs = [ - {label: "Summary", content: this.buildSummaryPage()} + { label: "Summary", content: this.buildSummaryPage() } ] this.buildDiffAndHealthPages(tabs) @@ -58,10 +58,10 @@ export class ObjectNodeData extends NodeData { } if (findObjectByRef(this.props.commandResult.objects, this.objectRef)?.remote) { - tabs.push({label: "Remote", content: this.buildObjectPage(this.objectRef, ObjectType.Remote)}) + tabs.push({ label: "Remote", content: this.buildObjectPage(this.objectRef, ObjectType.Remote) }) } if (findObjectByRef(this.props.commandResult.objects, this.objectRef)?.applied) { - tabs.push({label: "Applied", content: this.buildObjectPage(this.objectRef, ObjectType.Applied)}) + tabs.push({ label: "Applied", content: this.buildObjectPage(this.objectRef, ObjectType.Applied) }) } return tabs @@ -74,27 +74,27 @@ export class ObjectNodeData extends NodeData { if (this.objectRef.group) { apiVersion = this.objectRef.group + "/" + this.objectRef.version } - props.push({name: "ApiVersion", value: apiVersion}) - props.push({name: "Kind", value: this.objectRef.kind}) + props.push({ name: "ApiVersion", value: apiVersion }) + props.push({ name: "Kind", value: this.objectRef.kind }) - props.push({name: "Name", value: this.objectRef.name}) + props.push({ name: "Name", value: this.objectRef.name }) if (this.objectRef.namespace) { props.push({ name: "Namespace", value: this.objectRef.namespace }) } const o = findObjectByRef(this.props.commandResult.objects, this.objectRef) - const annotations: {[key: string]: string} = o?.rendered?.metadata.annotations + const annotations: { [key: string]: string } = o?.rendered?.metadata.annotations if (annotations) { Object.keys(annotations).forEach(k => { - if (k.indexOf("kluctl.io/") !== -1) { - props.push({ name: k, value: annotations[k] }) - } + if (k.indexOf("kluctl.io/") !== -1) { + props.push({ name: k, value: annotations[k] }) } + } ) } return <> - + } } diff --git a/pkg/webui/ui/src/components/result-view/nodes/VarsSourceCollectionNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceCollectionNode.tsx index 20af76274..dfa9b5e4d 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/VarsSourceCollectionNode.tsx +++ b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceCollectionNode.tsx @@ -1,9 +1,9 @@ import { VarsSource } from "../../../models"; import { NodeData } from "./NodeData"; import React from "react"; -import { DataArray } from "@mui/icons-material"; import { CommandResultProps } from "../CommandResultView"; import { SidePanelTab } from "../SidePanel"; +import { BracketsSquareIcon } from "../../../icons/Icons"; export class VarsSourceCollectionNodeData extends NodeData { varsSources: VarsSource[] = [] @@ -17,7 +17,7 @@ export class VarsSourceCollectionNodeData extends NodeData { } buildIcon(): [React.ReactNode, string] { - return [, "vars"] + return [, "vars"] } buildSidePanelTabs(): SidePanelTab[] { diff --git a/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx index 3b5674e5c..e337cd4fd 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx +++ b/pkg/webui/ui/src/components/result-view/nodes/VarsSourceNode.tsx @@ -1,8 +1,8 @@ import { VarsSource } from "../../../models"; import { NodeData } from "./NodeData"; import React from "react"; -import { Category, Cloud, Dvr, Http, Lock, Settings, Source } from "@mui/icons-material"; -import { GitIcon } from "../../../icons/Icons"; +import { Category, Cloud, Dvr, Http, Lock, Settings } from "@mui/icons-material"; +import { FileIcon, GitIcon } from "../../../icons/Icons"; import { PropertiesTable } from "../../PropertiesTable"; import { CodeViewer } from "../../CodeViewer"; import { Box } from "@mui/material"; @@ -59,7 +59,7 @@ export class VarsSourceNodeData extends NodeData { label: () => { return this.varsSource.file }, - icon: () => , + icon: () => , sourceProps: () => [ {name: "File", value: this.varsSource.file} ] diff --git a/pkg/webui/ui/src/icons/Icons.tsx b/pkg/webui/ui/src/icons/Icons.tsx index 19227081e..7abba98ce 100644 --- a/pkg/webui/ui/src/icons/Icons.tsx +++ b/pkg/webui/ui/src/icons/Icons.tsx @@ -26,6 +26,13 @@ import { ReactComponent as ArrowLeftIconSvg } from './arrow-left.svg'; import { ReactComponent as WarningSignIconSvg } from './warning-sign.svg'; import { ReactComponent as ChangesIconSvg } from './changes.svg'; import { ReactComponent as StarIconSvg } from './star.svg'; +import { ReactComponent as TriangleDownIconSvg } from './triangle-down.svg'; +import { ReactComponent as TriangleRightIconSvg } from './triangle-right.svg'; +import { ReactComponent as BracketsCurlyIconSvg } from './brackets-curly.svg'; +import { ReactComponent as BracketsSquareIconSvg } from './brackets-square.svg'; +import { ReactComponent as FileIconSvg } from './file.svg'; +import { ReactComponent as ResultIconSvg } from './result.svg'; +import { ReactComponent as IncludeIconSvg } from './include.svg'; export const KluctlText = () => { return @@ -147,3 +154,31 @@ export const ChangesIcon = () => { export const StarIcon = () => { return } + +export const TriangleDownIcon = () => { + return +} + +export const TriangleRightIcon = () => { + return +} + +export const BracketsCurlyIcon = () => { + return +} + +export const BracketsSquareIcon = () => { + return +} + +export const FileIcon = () => { + return +} + +export const ResultIcon = () => { + return +} + +export const IncludeIcon = () => { + return +} diff --git a/pkg/webui/ui/src/icons/brackets-curly.svg b/pkg/webui/ui/src/icons/brackets-curly.svg new file mode 100644 index 000000000..c0ead2b85 --- /dev/null +++ b/pkg/webui/ui/src/icons/brackets-curly.svg @@ -0,0 +1,3 @@ + + + diff --git a/pkg/webui/ui/src/icons/brackets-square.svg b/pkg/webui/ui/src/icons/brackets-square.svg new file mode 100644 index 000000000..ab82bf02c --- /dev/null +++ b/pkg/webui/ui/src/icons/brackets-square.svg @@ -0,0 +1,3 @@ + + + diff --git a/pkg/webui/ui/src/icons/file.svg b/pkg/webui/ui/src/icons/file.svg new file mode 100644 index 000000000..698087505 --- /dev/null +++ b/pkg/webui/ui/src/icons/file.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/webui/ui/src/icons/include.svg b/pkg/webui/ui/src/icons/include.svg new file mode 100644 index 000000000..f17e9d604 --- /dev/null +++ b/pkg/webui/ui/src/icons/include.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pkg/webui/ui/src/icons/result.svg b/pkg/webui/ui/src/icons/result.svg new file mode 100644 index 000000000..013fab63e --- /dev/null +++ b/pkg/webui/ui/src/icons/result.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pkg/webui/ui/src/icons/triangle-down.svg b/pkg/webui/ui/src/icons/triangle-down.svg new file mode 100644 index 000000000..d9c2677ca --- /dev/null +++ b/pkg/webui/ui/src/icons/triangle-down.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/ui/src/icons/triangle-right.svg b/pkg/webui/ui/src/icons/triangle-right.svg new file mode 100644 index 000000000..ad10f4ccc --- /dev/null +++ b/pkg/webui/ui/src/icons/triangle-right.svg @@ -0,0 +1,4 @@ + + + + From 653cea0f4f8c214788f07db2d72c99f603103feb Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Sat, 3 Jun 2023 03:17:50 +0600 Subject: [PATCH 1139/2268] Result Tree page: Added right drawer. --- .../result-view/CommandResultTree.tsx | 17 ++--------- .../result-view/CommandResultView.tsx | 28 +++++++++++++++---- .../src/components/result-view/SidePanel.tsx | 2 +- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx index 56c3174b7..58bd404b6 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx @@ -45,22 +45,11 @@ const CommandResultTree = (props: CommandResultTreeProps) => { }; const handleItemClick = (e: React.SyntheticEvent, node: NodeData) => { - setSelectedNodeId(node.id) - e.stopPropagation() + setSelectedNodeId(node.id); + props.onSelectNode(node); + e.stopPropagation(); } - const onSelectNode = props.onSelectNode - useEffect(() => { - if (!nodeMap || !selectedNodeId) { - return - } - const node = nodeMap.get(selectedNodeId) - if (!node) { - setSelectedNodeId(undefined) - } - onSelectNode(node) - }, [nodeMap, selectedNodeId, onSelectNode]) - const renderTree = (nodes: NodeData) => { if (!FilterNode(nodes, props.activeFilters)) { return null diff --git a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx index b9186b71e..0790dfe13 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx @@ -1,15 +1,16 @@ import * as React from 'react'; import { useState } from 'react'; -import { Box, Checkbox, CheckboxProps, Divider, FormControlLabel, Typography } from "@mui/material"; +import { Box, Checkbox, CheckboxProps, Divider, Drawer, FormControlLabel, ThemeProvider, Typography } from "@mui/material"; import { CommandResult, CommandResultSummary, ShortName } from "../../models"; import { NodeData } from "./nodes/NodeData"; -import { SidePanel } from "./SidePanel"; +import { SidePanel, SidePanelProvider } from "./SidePanel"; import { ActiveFilters } from "./NodeStatusFilter"; import CommandResultTree from "./CommandResultTree"; import { useLoaderData } from "react-router-dom"; import { api } from "../../api"; import { useAppOutletContext } from "../App"; import { ChangesIcon, CheckboxCheckedIcon, CheckboxIcon, StarIcon, WarningSignIcon } from '../../icons/Icons'; +import { dark } from '../theme'; export interface CommandResultProps { shortNames: ShortName[] @@ -83,6 +84,22 @@ const FilterCheckbox = (props: { /> } +function DetailsDrawer(props: { nodeData?: NodeData, onClose?: () => void }) { + return + + + + + + ; +} + const defaultFilters = { onlyImportant: false, onlyChanged: false, @@ -116,6 +133,10 @@ export const CommandResultView = () => { flexDirection='column' overflow='hidden' > + setSidePanelNode(undefined)} + /> { activeFilters={context.filters} /> - - -
    } diff --git a/pkg/webui/ui/src/components/result-view/SidePanel.tsx b/pkg/webui/ui/src/components/result-view/SidePanel.tsx index bfb5c17b0..049c9ddf1 100644 --- a/pkg/webui/ui/src/components/result-view/SidePanel.tsx +++ b/pkg/webui/ui/src/components/result-view/SidePanel.tsx @@ -61,7 +61,7 @@ export const SidePanel = (props: SidePanelProps) => { return - + From bdf318a6a795b244cf3735ce4b10d2917ab80ba4 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Jun 2023 21:43:35 +0200 Subject: [PATCH 1140/2268] fix: Fix crash in buildDeploymentProjectChildren --- pkg/webui/ui/src/components/result-view/nodes/NodeBuilder.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/webui/ui/src/components/result-view/nodes/NodeBuilder.ts b/pkg/webui/ui/src/components/result-view/nodes/NodeBuilder.ts index c3ab9769b..b5bbe43e3 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/NodeBuilder.ts +++ b/pkg/webui/ui/src/components/result-view/nodes/NodeBuilder.ts @@ -68,7 +68,9 @@ export class NodeBuilder { buildRoot(): [CommandResultNodeData, Map] { const rootNode = new CommandResultNodeData(this.props, "root") - this.buildDeploymentProjectChildren(rootNode, this.props.commandResult.deployment!) + if (this.props.commandResult.deployment) { + this.buildDeploymentProjectChildren(rootNode, this.props.commandResult.deployment) + } if (this.deletedObjectsMap.size) { this.buildDeletedOrOrphanNode(rootNode, true, Array.from(this.deletedObjectsMap.values())) From c6fd95dd9d9746d8870e4bb9cfff55e9d4d9c6a3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Jun 2023 21:46:05 +0200 Subject: [PATCH 1141/2268] fix: Use ./ as homepage --- pkg/webui/staticbuilder.go | 11 ----------- pkg/webui/ui/package.json | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/pkg/webui/staticbuilder.go b/pkg/webui/staticbuilder.go index fd7f95726..7cb3aad78 100644 --- a/pkg/webui/staticbuilder.go +++ b/pkg/webui/staticbuilder.go @@ -1,7 +1,6 @@ package webui import ( - "bytes" "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/results" @@ -115,16 +114,6 @@ func (swb *StaticWebuiBuilder) Build(path string) error { return err } - indexHtml, err := os.ReadFile(filepath.Join(tmpDir, "index.html")) - if err != nil { - return err - } - indexHtml = bytes.ReplaceAll(indexHtml, []byte("/webui/"), []byte("./")) - err = os.WriteFile(filepath.Join(tmpDir, "index.html"), indexHtml, 0) - if err != nil { - return err - } - staticbuildJsBytes, err := os.ReadFile(filepath.Join(tmpDir, "staticbuild.js")) if err != nil { return err diff --git a/pkg/webui/ui/package.json b/pkg/webui/ui/package.json index e658c5ed9..68d21f982 100644 --- a/pkg/webui/ui/package.json +++ b/pkg/webui/ui/package.json @@ -1,7 +1,7 @@ { "name": "react-demo", "version": "0.1.0", - "homepage": "/", + "homepage": "./", "private": true, "proxy": "http://localhost:8080", "dependencies": { From 3ebb6c0e87780b11f1967ddad42b76f29c33da3e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 5 Jun 2023 23:41:10 +0200 Subject: [PATCH 1142/2268] fix: update models.ts --- .../components/result-view/CommandResultStatusLine.tsx | 4 ++-- pkg/webui/ui/src/models.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx b/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx index 4794f31ae..8187e6f9e 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultStatusLine.tsx @@ -49,8 +49,8 @@ export const StatusLine = (props: StatusLineProps) => { } export const CommandResultStatusLine = (props: { rs: CommandResultSummary }) => { - return Date: Wed, 7 Jun 2023 16:39:43 +0200 Subject: [PATCH 1143/2268] feat: Add support for reconcile/deploy now --- pkg/webui/clusteraccessor.go | 18 +++++- pkg/webui/server.go | 62 +++++++++++++++++++ pkg/webui/ui/src/api.tsx | 42 ++++++++++--- .../src/components/targets-view/Targets.tsx | 34 +++++++++- 4 files changed, 145 insertions(+), 11 deletions(-) diff --git a/pkg/webui/clusteraccessor.go b/pkg/webui/clusteraccessor.go index fe83386f2..1e44dcdbb 100644 --- a/pkg/webui/clusteraccessor.go +++ b/pkg/webui/clusteraccessor.go @@ -2,8 +2,11 @@ package webui import ( "context" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" k8s2 "github.com/kluctl/kluctl/v2/pkg/k8s" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" "sync" @@ -61,8 +64,19 @@ func (ca *clusterAccessor) initClient() { } func (ca *clusterAccessor) tryInitClient() error { - var err error - c, err := client.New(ca.config, client.Options{}) + scheme := runtime.NewScheme() + err := clientgoscheme.AddToScheme(scheme) + if err != nil { + return err + } + err = kluctlv1.AddToScheme(scheme) + if err != nil { + return err + } + + c, err := client.New(ca.config, client.Options{ + Scheme: scheme, + }) if err != nil { return err } diff --git a/pkg/webui/server.go b/pkg/webui/server.go index 8d854718b..90523fba0 100644 --- a/pkg/webui/server.go +++ b/pkg/webui/server.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/gin-gonic/gin" + kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" "github.com/kluctl/kluctl/v2/pkg/results" "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/types" @@ -11,11 +12,17 @@ import ( "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" "io/fs" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" "net" "net/http" + "sigs.k8s.io/controller-runtime/pkg/client" + "time" ) +const webuiManager = "kluctl-webui" + type CommandResultsServer struct { ctx context.Context collector *results.ResultsCollector @@ -85,6 +92,8 @@ func (s *CommandResultsServer) Run(port int) error { api.GET("/getResult", s.getResult) api.GET("/getResultObject", s.getResultObject) api.POST("/validateNow", s.validateNow) + api.POST("/reconcileNow", s.reconcileNow) + api.POST("/deployNow", s.deployNow) address := fmt.Sprintf(":%d", port) listener, err := net.Listen("tcp", address) @@ -302,3 +311,56 @@ func (s *CommandResultsServer) validateNow(c *gin.Context) { c.Status(http.StatusOK) } + +type kluctlDeploymentParam struct { + Cluster string `json:"cluster"` + Name string `json:"name"` + Namespace string `json:"namespace"` +} + +func (s *CommandResultsServer) doSetAnnotation(c *gin.Context, aname string, avalue string) { + var params kluctlDeploymentParam + err := c.Bind(¶ms) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + ca := s.cam.getForClusterId(params.Cluster) + if ca == nil { + _ = c.AbortWithError(http.StatusNotFound, err) + return + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + var kd kluctlv1.KluctlDeployment + err = ca.getClient().Get(ctx, client.ObjectKey{Name: params.Name, Namespace: params.Namespace}, &kd) + if err != nil { + if errors.IsNotFound(err) { + _ = c.AbortWithError(http.StatusNotFound, err) + return + } + _ = c.AbortWithError(http.StatusInternalServerError, err) + return + } + + patch := client.MergeFrom(kd.DeepCopy()) + metav1.SetMetaDataAnnotation(&kd.ObjectMeta, aname, avalue) + err = ca.getClient().Patch(ctx, &kd, patch, client.FieldOwner(webuiManager)) + if err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, err) + return + } + + c.Status(http.StatusOK) +} + +func (s *CommandResultsServer) reconcileNow(c *gin.Context) { + s.doSetAnnotation(c, kluctlv1.KluctlRequestReconcileAnnotation, time.Now().Format(time.RFC3339Nano)) +} + +func (s *CommandResultsServer) deployNow(c *gin.Context) { + s.doSetAnnotation(c, kluctlv1.KluctlRequestDeployAnnotation, time.Now().Format(time.RFC3339Nano)) +} diff --git a/pkg/webui/ui/src/api.tsx b/pkg/webui/ui/src/api.tsx index 173f2a42d..5b4ea4678 100644 --- a/pkg/webui/ui/src/api.tsx +++ b/pkg/webui/ui/src/api.tsx @@ -31,6 +31,8 @@ export interface Api { getResult(resultId: string): Promise getResultObject(resultId: string, ref: ObjectRef, objectType: string): Promise validateNow(project: ProjectKey, target: TargetKey): Promise + reconcileNow(cluster: string, name: string, namespace: string): Promise + deployNow(cluster: string, name: string, namespace: string): Promise } class RealApi implements Api { @@ -80,22 +82,40 @@ class RealApi implements Api { .then(response => response.json()); } - async validateNow(project: ProjectKey, target: TargetKey) { - const key = { - "project": project, - "target": target, - } - - let url = `${apiUrl}/validateNow` + async doPost(f: string, body: any) { + let url = `${apiUrl}/${f}` return fetch(url, { method: "POST", - body: JSON.stringify(key), + body: JSON.stringify(body), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, }).then(handleErrors); } + + async validateNow(project: ProjectKey, target: TargetKey) { + return this.doPost("validateNow", { + "project": project, + "target": target, + }) + } + + async deployNow(cluster: string, name: string, namespace: string): Promise { + return this.doPost("deployNow", { + "cluster": cluster, + "name": name, + "namespace": namespace, + }) + } + + async reconcileNow(cluster: string, name: string, namespace: string): Promise { + return this.doPost("reconcileNow", { + "cluster": cluster, + "name": name, + "namespace": namespace, + }) + } } class StaticApi implements Api { @@ -143,6 +163,12 @@ class StaticApi implements Api { validateNow(project: ProjectKey, target: TargetKey): Promise { throw new Error("not implemented") } + reconcileNow(cluster: string, name: string, namespace: string): Promise { + throw new Error("not implemented") + } + deployNow(cluster: string, name: string, namespace: string): Promise { + throw new Error("not implemented") + } } export let api: Api diff --git a/pkg/webui/ui/src/components/targets-view/Targets.tsx b/pkg/webui/ui/src/components/targets-view/Targets.tsx index d6f891c37..607b1ade6 100644 --- a/pkg/webui/ui/src/components/targets-view/Targets.tsx +++ b/pkg/webui/ui/src/components/targets-view/Targets.tsx @@ -1,4 +1,4 @@ -import { ProjectSummary, TargetSummary } from "../../models"; +import { KluctlDeploymentInfo, ProjectSummary, TargetSummary } from "../../models"; import { ActionMenuItem, ActionsMenu } from "../ActionsMenu"; import Paper from "@mui/material/Paper"; import { Box, Typography, useTheme } from "@mui/material"; @@ -62,6 +62,38 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel } }) + let kd: KluctlDeploymentInfo | undefined + let allKdEqual = true + props.ts.commandResults?.forEach(rs => { + console.log(rs) + if (rs.commandInfo.kluctlDeployment) { + if (!kd) { + kd = rs.commandInfo.kluctlDeployment + } else { + if (kd.name !== rs.commandInfo.kluctlDeployment.name || kd.namespace !== rs.commandInfo.kluctlDeployment.namespace) { + allKdEqual = false + } + } + } + }) + + if (kd && allKdEqual) { + actionMenuItems.push({ + icon: , + text: "Reconcile", + handler: () => { + api.reconcileNow(props.ts.target.clusterId, kd!.name, kd!.namespace) + } + }) + actionMenuItems.push({ + icon: , + text: "Deploy", + handler: () => { + api.deployNow(props.ts.target.clusterId, kd!.name, kd!.namespace) + } + }) + } + const allContexts: string[] = [] const handleContext = (c?: string) => { From a253a129720e0ebfa8405c090acd5c0c9d559557 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Jun 2023 16:39:49 +0200 Subject: [PATCH 1144/2268] fix: Update models.ts --- pkg/webui/ui/src/models.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/webui/ui/src/models.ts b/pkg/webui/ui/src/models.ts index 8e420682d..d0eed1881 100644 --- a/pkg/webui/ui/src/models.ts +++ b/pkg/webui/ui/src/models.ts @@ -410,15 +410,11 @@ export class GitInfo { export class KluctlDeploymentInfo { name: string; namespace: string; - gitUrl: string; - gitRef: string; constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); this.name = source["name"]; this.namespace = source["namespace"]; - this.gitUrl = source["gitUrl"]; - this.gitRef = source["gitRef"]; } } export class CommandInfo { From 29196a8a397449db892b69fc27b581e20dbd5973 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 7 Jun 2023 21:49:50 +0200 Subject: [PATCH 1145/2268] feat: Determine static build by looking for existence of projects.js --- pkg/webui/ui/public/staticbuild.js | 1 - pkg/webui/ui/src/api.tsx | 23 +++++++++++++------ pkg/webui/ui/src/components/ObjectYaml.tsx | 12 ++++++---- .../result-view/CommandResultView.tsx | 3 ++- .../CommandResultDetailsDrawer.tsx | 3 ++- .../src/components/targets-view/Targets.tsx | 12 ++++++---- .../components/targets-view/TargetsView.tsx | 3 ++- pkg/webui/ui/src/staticbuild.d.ts | 2 -- 8 files changed, 37 insertions(+), 22 deletions(-) diff --git a/pkg/webui/ui/public/staticbuild.js b/pkg/webui/ui/public/staticbuild.js index a653e44b6..af1b72ec3 100644 --- a/pkg/webui/ui/public/staticbuild.js +++ b/pkg/webui/ui/public/staticbuild.js @@ -1,2 +1 @@ -let isStaticBuild = true; const staticResults = new Map() diff --git a/pkg/webui/ui/src/api.tsx b/pkg/webui/ui/src/api.tsx index 5b4ea4678..6e29ca97c 100644 --- a/pkg/webui/ui/src/api.tsx +++ b/pkg/webui/ui/src/api.tsx @@ -35,6 +35,22 @@ export interface Api { deployNow(cluster: string, name: string, namespace: string): Promise } +let apiPromise: Promise | undefined = undefined +export async function getApi(): Promise { + if (!apiPromise) { + apiPromise = loadScript(staticPath + "/projects.js") + } + + try { + await apiPromise + return new StaticApi() + } catch (error) { + return new RealApi() + } +} + +export let api = getApi() + class RealApi implements Api { async getShortNames(): Promise { let url = `${apiUrl}/getShortNames` @@ -171,13 +187,6 @@ class StaticApi implements Api { } } -export let api: Api -if (isStaticBuild) { - api = new StaticApi() -} else { - api = new RealApi() -} - function handleErrors(response: Response) { if (!response.ok) { throw Error(response.statusText) diff --git a/pkg/webui/ui/src/components/ObjectYaml.tsx b/pkg/webui/ui/src/components/ObjectYaml.tsx index 11f50f428..2d3fee8fe 100644 --- a/pkg/webui/ui/src/components/ObjectYaml.tsx +++ b/pkg/webui/ui/src/components/ObjectYaml.tsx @@ -1,6 +1,6 @@ import { CommandResultProps } from "./result-view/CommandResultView"; import { ObjectRef } from "../models"; -import { api, ObjectType, usePromise } from "../api"; +import { getApi, ObjectType, usePromise } from "../api"; import React, { Suspense, useEffect, useState } from "react"; import { CodeViewer } from "./CodeViewer"; @@ -10,10 +10,14 @@ import { Loading } from "./Loading"; export const ObjectYaml = (props: {treeProps: CommandResultProps, objectRef: ObjectRef, objectType: ObjectType}) => { const [promise, setPromise] = useState>() + const getData = async () => { + const api = await getApi() + const o = await api.getResultObject(props.treeProps.summary.id, props.objectRef, props.objectType) + return yaml.dump(o) + } + useEffect(() => { - const p = api.getResultObject(props.treeProps.summary.id, props.objectRef, props.objectType) - .then(yaml.dump) - setPromise(p) + setPromise(getData()) }, [props.treeProps, props.objectRef, props.objectType]) const Content = () => { diff --git a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx index 0790dfe13..c1e8ffd96 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx @@ -7,10 +7,10 @@ import { SidePanel, SidePanelProvider } from "./SidePanel"; import { ActiveFilters } from "./NodeStatusFilter"; import CommandResultTree from "./CommandResultTree"; import { useLoaderData } from "react-router-dom"; -import { api } from "../../api"; import { useAppOutletContext } from "../App"; import { ChangesIcon, CheckboxCheckedIcon, CheckboxIcon, StarIcon, WarningSignIcon } from '../../icons/Icons'; import { dark } from '../theme'; +import { getApi } from "../../api"; export interface CommandResultProps { shortNames: ShortName[] @@ -19,6 +19,7 @@ export interface CommandResultProps { } export async function commandResultLoader({ params }: any) { + const api = await getApi() const result = api.getResult(params.id) const shortNames = api.getShortNames() const summaries = api.listResults() diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx index 5820d9dab..a7c7f7705 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx @@ -1,5 +1,5 @@ import { CommandResultSummary } from "../../models"; -import { api, usePromise } from "../../api"; +import { getApi, usePromise } from "../../api"; import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; import React, { Suspense, useEffect, useState } from "react"; import { NodeData } from "../result-view/nodes/NodeData"; @@ -9,6 +9,7 @@ import { Loading } from "../Loading"; import { dark } from "../theme"; async function doGetRootNode(rs: CommandResultSummary) { + const api = await getApi() const shortNames = api.getShortNames() const r = api.getResult(rs.id) const builder = new NodeBuilder({ diff --git a/pkg/webui/ui/src/components/targets-view/Targets.tsx b/pkg/webui/ui/src/components/targets-view/Targets.tsx index 607b1ade6..530874065 100644 --- a/pkg/webui/ui/src/components/targets-view/Targets.tsx +++ b/pkg/webui/ui/src/components/targets-view/Targets.tsx @@ -5,7 +5,7 @@ import { Box, Typography, useTheme } from "@mui/material"; import React from "react"; import Tooltip from "@mui/material/Tooltip"; import { Favorite, HeartBroken, PublishedWithChanges } from "@mui/icons-material"; -import { api } from "../../api"; +import { getApi } from "../../api"; import { CpuIcon, FingerScanIcon, MessageQuestionIcon, TargetIcon } from "../../icons/Icons"; const StatusIcon = (props: { ps: ProjectSummary, ts: TargetSummary }) => { @@ -57,7 +57,8 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel actionMenuItems.push({ icon: , text: "Validate now", - handler: () => { + handler: async () => { + const api = await getApi() api.validateNow(props.ps.project, props.ts.target) } }) @@ -65,7 +66,6 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel let kd: KluctlDeploymentInfo | undefined let allKdEqual = true props.ts.commandResults?.forEach(rs => { - console.log(rs) if (rs.commandInfo.kluctlDeployment) { if (!kd) { kd = rs.commandInfo.kluctlDeployment @@ -81,14 +81,16 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel actionMenuItems.push({ icon: , text: "Reconcile", - handler: () => { + handler: async () => { + const api = await getApi() api.reconcileNow(props.ts.target.clusterId, kd!.name, kd!.namespace) } }) actionMenuItems.push({ icon: , text: "Deploy", - handler: () => { + handler: async () => { + const api = await getApi() api.deployNow(props.ts.target.clusterId, kd!.name, kd!.namespace) } }) diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 52178ed7f..3a71b9fa8 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -3,7 +3,7 @@ import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../model import { Box, BoxProps, Typography, useTheme } from "@mui/material"; import React, { useEffect, useState } from "react"; import { useAppOutletContext } from "../App"; -import { api } from "../../api"; +import { getApi } from "../../api"; import { ProjectItem } from "./Projects"; import { TargetItem } from "./Targets"; import Divider from "@mui/material/Divider"; @@ -19,6 +19,7 @@ const cardHeight = 126; const cardGap = 20; export async function projectsLoader() { + const api = await getApi() const projects = await api.listProjects() return projects } diff --git a/pkg/webui/ui/src/staticbuild.d.ts b/pkg/webui/ui/src/staticbuild.d.ts index 511b2c96a..f8d60218d 100644 --- a/pkg/webui/ui/src/staticbuild.d.ts +++ b/pkg/webui/ui/src/staticbuild.d.ts @@ -1,5 +1,3 @@ -declare var isStaticBuild: bool; - declare const staticResults: Map; declare const staticShortNames: any[]; From 5ce0efa13822f8392ff3e79e2b596d4dc3ddb019 Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Wed, 7 Jun 2023 06:20:57 +0600 Subject: [PATCH 1146/2268] WIP expanding/collapsing nodes. --- .../ui/src/components/targets-view/Card.tsx | 24 +++ .../CommandResultDetailsDrawer.tsx | 47 +++++- .../targets-view/CommandResultItem.tsx | 2 +- .../components/targets-view/TargetsView.tsx | 151 +++++++++++------- 4 files changed, 159 insertions(+), 65 deletions(-) create mode 100644 pkg/webui/ui/src/components/targets-view/Card.tsx diff --git a/pkg/webui/ui/src/components/targets-view/Card.tsx b/pkg/webui/ui/src/components/targets-view/Card.tsx new file mode 100644 index 000000000..5c550fc9e --- /dev/null +++ b/pkg/webui/ui/src/components/targets-view/Card.tsx @@ -0,0 +1,24 @@ +import { Box, BoxProps } from "@mui/material" + +export const cardWidth = 247; +export const projectCardHeight = 80; +export const cardHeight = 126; +export const cardGap = 20; + +export function Card({ children, ...rest }: BoxProps) { + return + {children} + +} + +export function CardCol({ children, ...rest }: BoxProps) { + return + {children} + +} + +export function CardRow({ children, ...rest }: BoxProps) { + return + {children} + +} diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx index a7c7f7705..e5d997038 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx @@ -1,12 +1,16 @@ -import { CommandResultSummary } from "../../models"; +import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; import { getApi, usePromise } from "../../api"; import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; -import React, { Suspense, useEffect, useState } from "react"; +import { Suspense, useEffect, useState } from "react"; import { NodeData } from "../result-view/nodes/NodeData"; import { SidePanel } from "../result-view/SidePanel"; import { Box, Drawer, ThemeProvider } from "@mui/material"; import { Loading } from "../Loading"; -import { dark } from "../theme"; +import { dark, light } from "../theme"; +import { Card, CardCol } from "./Card"; +import { CommandResultItem } from "./CommandResultItem"; + +const sidePanelWidth = 720; async function doGetRootNode(rs: CommandResultSummary) { const api = await getApi() @@ -21,7 +25,7 @@ async function doGetRootNode(rs: CommandResultSummary) { return node } -export const CommandResultDetailsDrawer = (props: { rs?: CommandResultSummary, onClose: () => void }) => { +export const CommandResultDetailsDrawer = (props: { rs?: CommandResultSummary, ts?: TargetSummary, ps?: ProjectSummary, onClose: () => void }) => { const [prevId, setPrevId] = useState() const [promise, setPromise] = useState>(new Promise(() => undefined)) @@ -34,26 +38,55 @@ export const CommandResultDetailsDrawer = (props: { rs?: CommandResultSummary, o } setPrevId(props.rs.id) setPromise(doGetRootNode(props.rs)) - }, [props.rs]) + }, [props.rs, prevId]) const Content = (props: { onClose: () => void }) => { const node = usePromise(promise) return } + const { ps, ts } = props; + return - + }> + + + + {ps && ts?.commandResults?.map((rs, i) => { + return + { }} + /> + + })} + + + } \ No newline at end of file diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index 8812d8181..937768958 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -9,7 +9,7 @@ import { useNavigate } from "react-router"; import { formatDurationShort } from "../../utils/duration"; import { DeployIcon, DiffIcon, PruneIcon, TreeViewIcon } from "../../icons/Icons"; -export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary, rs: CommandResultSummary, onSelectCommandResult: (rs?: CommandResultSummary) => void }) => { +export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary, rs: CommandResultSummary, onSelectCommandResult: (rs: CommandResultSummary) => void }) => { const calcAgo = () => { const t1 = new Date(props.rs.commandInfo.startTime) const t2 = new Date() diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 3a71b9fa8..2d37a3f7a 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -1,6 +1,6 @@ import { useLoaderData } from "react-router-dom"; import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; -import { Box, BoxProps, Typography, useTheme } from "@mui/material"; +import { Box, Typography, useTheme } from "@mui/material"; import React, { useEffect, useState } from "react"; import { useAppOutletContext } from "../App"; import { getApi } from "../../api"; @@ -10,13 +10,12 @@ import Divider from "@mui/material/Divider"; import { CommandResultItem } from "./CommandResultItem"; import { CommandResultDetailsDrawer } from "./CommandResultDetailsDrawer"; import { TargetDetailsDrawer } from "./TargetDetailsDrawer"; -import { RelationHLine } from "../../icons/Icons"; +import { Card, CardCol, CardRow, cardGap, cardHeight, cardWidth, projectCardHeight } from "./Card"; -const colWidth = 433; -const cardWidth = 247; -const projectCardHeight = 80; -const cardHeight = 126; -const cardGap = 20; +const colWidth = 416; +const curveRadius = 12; +const circleRadius = 5; +const strokeWidth = 2; export async function projectsLoader() { const api = await getApi() @@ -44,31 +43,21 @@ function ColHeader({ children }: { children: React.ReactNode }) { } -function Card({ children, ...rest }: BoxProps) { - return - {children} - -} - -function CardCol({ children, ...rest }: BoxProps) { - return - {children} - -} - -function CardRow({ children, ...rest }: BoxProps) { - return - {children} - -} +const Circle = React.memo((props: React.SVGProps) => { + const theme = useTheme(); + return +}) const RelationTree = React.memo(({ targetCount }: { targetCount: number }): JSX.Element | null => { const theme = useTheme(); const height = targetCount * cardHeight + (targetCount - 1) * cardGap - const width = 169; - const curveRadius = 12; - const circleRadius = 5; - const strokeWidth = 2; + const width = 152; if (targetCount <= 0) { return null; @@ -124,33 +113,26 @@ const RelationTree = React.memo(({ targetCount }: { targetCount: number }): JSX. strokeLinecap='round' strokeLinejoin='round' />, - ] })} - }); export const TargetsView = () => { + const theme = useTheme(); const context = useAppOutletContext() - const [selectedCommandResult, setSelectedCommandResult] = useState() - const [selectedTargetSummary, setSelectedTargetSummary] = useState() + const [selectedCommandResult, setSelectedCommandResult] = useState<{rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary} | undefined>(); + const [selectedTargetSummary, setSelectedTargetSummary] = useState(); const projects = useLoaderData() as ProjectSummary[]; @@ -158,18 +140,26 @@ export const TargetsView = () => { context.setFilters(undefined) }) - const doSetSelectedCommandResult = (rs?: CommandResultSummary) => { - setSelectedCommandResult(rs) - setSelectedTargetSummary(undefined) + const doSetSelectedCommandResult = (o?: {rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary}) => { + setSelectedCommandResult(o); + setSelectedTargetSummary(undefined); } const doSetSelectedTargetSummary = (ts?: TargetSummary) => { - setSelectedCommandResult(undefined) - setSelectedTargetSummary(ts) + setSelectedCommandResult(undefined); + setSelectedTargetSummary(ts); } - return - setSelectedCommandResult(undefined)} /> - setSelectedTargetSummary(undefined)} /> + return + doSetSelectedCommandResult(undefined)} + /> + setSelectedTargetSummary(undefined)} + /> Projects Targets @@ -179,7 +169,7 @@ export const TargetsView = () => { {projects.map((ps, i) => { return - + @@ -194,27 +184,74 @@ export const TargetsView = () => { - + {ps.targets.map((ts, i) => { return doSetSelectedTargetSummary(ts)} /> - - + + + + + + + + + + + + })} - + {ps.targets.map((ts, i) => { - return + return {ts.commandResults?.map((rs, i) => { - return - doSetSelectedCommandResult(rs)} /> + return + doSetSelectedCommandResult({rs, ts, ps})} + /> })} From 1c5419fc5405a4a26b421ce5999b24f825becdbe Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Thu, 8 Jun 2023 05:06:24 +0600 Subject: [PATCH 1147/2268] Added card selection when card stack is expanded. --- pkg/webui/ui/src/components/LeftDrawer.tsx | 17 +-- .../CommandResultDetailsDrawer.tsx | 124 ++++++++++-------- .../targets-view/CommandResultItem.tsx | 32 +++-- .../targets-view/TargetDetailsDrawer.tsx | 8 +- .../components/targets-view/TargetsView.tsx | 23 +++- pkg/webui/ui/src/components/theme.ts | 20 ++- 6 files changed, 135 insertions(+), 89 deletions(-) diff --git a/pkg/webui/ui/src/components/LeftDrawer.tsx b/pkg/webui/ui/src/components/LeftDrawer.tsx index 62cacf4a5..5bd0db728 100644 --- a/pkg/webui/ui/src/components/LeftDrawer.tsx +++ b/pkg/webui/ui/src/components/LeftDrawer.tsx @@ -17,11 +17,8 @@ import { KluctlLogo, TargetsIcon, KluctlText, SearchIcon, ArrowLeftIcon } from ' import { dark } from './theme'; import { Typography } from '@mui/material'; -const drawerWidthOpen = 224; -const drawerWidthClosed = 96; - const openedMixin = (theme: Theme): CSSObject => ({ - width: drawerWidthOpen, + width: theme.consts.leftDrawerWidthOpen, transition: theme.transitions.create('width', { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, @@ -35,7 +32,7 @@ const closedMixin = (theme: Theme): CSSObject => ({ duration: theme.transitions.duration.leavingScreen, }), overflowX: 'hidden', - width: drawerWidthClosed, + width: theme.consts.leftDrawerWidthClosed, }); const DrawerHeader = styled('div')(({ theme }) => ({ @@ -57,17 +54,17 @@ const AppBar = styled(MuiAppBar, { boxShadow: 'none', background: 'transparent', padding: '40px 40px 0 40px', - marginLeft: drawerWidthClosed, + marginLeft: theme.consts.leftDrawerWidthClosed, justifyContent: 'space-between', - width: `calc(100% - ${drawerWidthClosed}px)`, + width: `calc(100% - ${theme.consts.leftDrawerWidthClosed}px)`, zIndex: theme.zIndex.drawer + 1, transition: theme.transitions.create(['width', 'margin'], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), ...(open && { - marginLeft: drawerWidthOpen, - width: `calc(100% - ${drawerWidthOpen}px)`, + marginLeft: theme.consts.leftDrawerWidthOpen, + width: `calc(100% - ${theme.consts.leftDrawerWidthOpen}px)`, transition: theme.transitions.create(['width', 'margin'], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, @@ -77,7 +74,7 @@ const AppBar = styled(MuiAppBar, { const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })( ({ theme, open }) => ({ - width: drawerWidthOpen, + width: theme.consts.leftDrawerWidthOpen, flexShrink: 0, whiteSpace: 'nowrap', boxSizing: 'border-box', diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx index e5d997038..37c3e11a9 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx @@ -4,11 +4,12 @@ import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; import { Suspense, useEffect, useState } from "react"; import { NodeData } from "../result-view/nodes/NodeData"; import { SidePanel } from "../result-view/SidePanel"; -import { Box, Drawer, ThemeProvider } from "@mui/material"; +import { Box, Drawer, ThemeProvider, useTheme } from "@mui/material"; import { Loading } from "../Loading"; -import { dark, light } from "../theme"; +import { dark } from "../theme"; import { Card, CardCol } from "./Card"; import { CommandResultItem } from "./CommandResultItem"; +import React from "react"; const sidePanelWidth = 720; @@ -25,68 +26,81 @@ async function doGetRootNode(rs: CommandResultSummary) { return node } -export const CommandResultDetailsDrawer = (props: { rs?: CommandResultSummary, ts?: TargetSummary, ps?: ProjectSummary, onClose: () => void }) => { - const [prevId, setPrevId] = useState() - const [promise, setPromise] = useState>(new Promise(() => undefined)) +export const CommandResultDetailsDrawer = React.memo((props: { + rs?: CommandResultSummary, + ts?: TargetSummary, + ps?: ProjectSummary, + onClose: () => void +}) => { + const { ps, ts } = props; + const theme = useTheme(); + const [promise, setPromise] = useState>(new Promise(() => undefined)); + const [selectedCommandResult, setSelectedCommandResult] = useState(); + const [prevTargetSummary, setPrevTargetSummary] = useState(ts); + + if (prevTargetSummary !== ts) { + setPrevTargetSummary(ts); + setSelectedCommandResult(ts?.commandResults?.[0]); + } useEffect(() => { - if (props.rs === undefined) { - return - } - if (props.rs.id === prevId) { + if (selectedCommandResult === undefined) { return } - setPrevId(props.rs.id) - setPromise(doGetRootNode(props.rs)) - }, [props.rs, prevId]) + setPromise(doGetRootNode(selectedCommandResult)); + }, [selectedCommandResult]) const Content = (props: { onClose: () => void }) => { const node = usePromise(promise) return } - const { ps, ts } = props; - - return - - - }> - - + return <> + {ps && ts && + + e.stopPropagation()} flexGrow={1} justifyContent='center'> + {ts.commandResults?.map((rs, i) => { + return + + + })} + - - - - {ps && ts?.commandResults?.map((rs, i) => { - return - { }} - /> - - })} - + } + + + + }> + + - - - -} \ No newline at end of file + + + +}); diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index 937768958..bf3ff6a52 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -1,3 +1,4 @@ +import React from "react"; import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; import { useEffect, useMemo, useState } from "react"; import * as yaml from "js-yaml"; @@ -9,16 +10,22 @@ import { useNavigate } from "react-router"; import { formatDurationShort } from "../../utils/duration"; import { DeployIcon, DiffIcon, PruneIcon, TreeViewIcon } from "../../icons/Icons"; -export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary, rs: CommandResultSummary, onSelectCommandResult: (rs: CommandResultSummary) => void }) => { - const calcAgo = () => { - const t1 = new Date(props.rs.commandInfo.startTime) - const t2 = new Date() - const d = t2.getTime() - t1.getTime() - return formatDurationShort(d) - } +const calcAgo = (startTime: string) => { + const t1 = new Date(startTime) + const t2 = new Date() + const d = t2.getTime() - t1.getTime() + return formatDurationShort(d) +} +export const CommandResultItem = React.memo((props: { + ps: ProjectSummary, + ts: TargetSummary, + rs: CommandResultSummary, + onSelectCommandResult: (rs: CommandResultSummary) => void, + selected?: boolean; +}) => { const navigate = useNavigate() - const [ago, setAgo] = useState(calcAgo()) + const [ago, setAgo] = useState(calcAgo(props.rs.commandInfo.startTime)) let Icon: () => JSX.Element = DiffIcon switch (props.rs.commandInfo?.command) { @@ -45,9 +52,9 @@ export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary let iconTooltip = useEffect(() => { - const interval = setInterval(() => setAgo(calcAgo()), 5000); + const interval = setInterval(() => setAgo(calcAgo(props.rs.commandInfo.startTime)), 5000); return () => clearInterval(interval); - }, []) + }, [props.rs.commandInfo.startTime]) return props.onSelectCommandResult(props.rs)} > @@ -116,4 +124,4 @@ export const CommandResultItem = (props: { ps: ProjectSummary, ts: TargetSummary
    -} \ No newline at end of file +}); diff --git a/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx index 874779b74..b3d45a96b 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx @@ -95,18 +95,18 @@ class MyProvider implements SidePanelProvider { } } -export const TargetDetailsDrawer = (props: { ts?: TargetSummary, onClose: () => void }) => { +export const TargetDetailsDrawer = React.memo((props: { ts?: TargetSummary, onClose: () => void }) => { return - + ; -} \ No newline at end of file +}); diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 2d37a3f7a..f480f39f9 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -1,7 +1,7 @@ import { useLoaderData } from "react-router-dom"; import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; import { Box, Typography, useTheme } from "@mui/material"; -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useAppOutletContext } from "../App"; import { getApi } from "../../api"; import { ProjectItem } from "./Projects"; @@ -140,25 +140,34 @@ export const TargetsView = () => { context.setFilters(undefined) }) - const doSetSelectedCommandResult = (o?: {rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary}) => { + const doSetSelectedCommandResult = useCallback((o?: {rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary}) => { setSelectedCommandResult(o); setSelectedTargetSummary(undefined); - } - const doSetSelectedTargetSummary = (ts?: TargetSummary) => { + }, []); + + const doSetSelectedTargetSummary = useCallback((ts?: TargetSummary) => { setSelectedCommandResult(undefined); setSelectedTargetSummary(ts); - } + }, []); + + const onCommandResultDetailsDrawerClose = useCallback(() => { + doSetSelectedCommandResult(undefined); + }, [doSetSelectedCommandResult]); + + const onTargetDetailsDrawerClose = useCallback(() => { + setSelectedTargetSummary(undefined); + }, [setSelectedTargetSummary]); return doSetSelectedCommandResult(undefined)} + onClose={onCommandResultDetailsDrawerClose} /> setSelectedTargetSummary(undefined)} + onClose={onTargetDetailsDrawerClose} /> Projects diff --git a/pkg/webui/ui/src/components/theme.ts b/pkg/webui/ui/src/components/theme.ts index 14680cd0a..701d20a15 100644 --- a/pkg/webui/ui/src/components/theme.ts +++ b/pkg/webui/ui/src/components/theme.ts @@ -17,22 +17,40 @@ declare module '@mui/material/styles' { interface Theme { consts: { appBarHeight: number; + leftDrawerWidthOpen: number; + leftDrawerWidthClosed: number; }; } // allow configuration using `createTheme` interface ThemeOptions { consts?: { appBarHeight?: number; + leftDrawerWidthOpen?: number; + leftDrawerWidthClosed?: number; }; } } export const common = createTheme({ consts: { - appBarHeight: 106 + appBarHeight: 106, + leftDrawerWidthOpen: 224, + leftDrawerWidthClosed: 96 }, typography: { fontFamily: 'Nunito Variable', + }, + components: { + MuiBackdrop: { + styleOverrides: { + root: { + backgroundColor: 'rgba(0, 0, 0, 0.65)' + }, + invisible: { + backgroundColor: 'transparent' + } + } + } } }); From 12662156a12448467d0eacacf4c712e571e2200f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Jun 2023 22:20:06 +0200 Subject: [PATCH 1148/2268] refactor: Remove unused TargetSummary/ProjectSummary --- pkg/types/result/summary.go | 55 ----------------------- pkg/types/result/zz_generated.deepcopy.go | 55 ----------------------- 2 files changed, 110 deletions(-) diff --git a/pkg/types/result/summary.go b/pkg/types/result/summary.go index dc4c38b74..3ad691bcd 100644 --- a/pkg/types/result/summary.go +++ b/pkg/types/result/summary.go @@ -2,7 +2,6 @@ package result import ( "github.com/kluctl/kluctl/v2/pkg/types" - "sort" ) type CommandResultSummary struct { @@ -30,19 +29,6 @@ type CommandResultSummary struct { TotalChanges int `json:"totalChanges"` } -type TargetSummary struct { - Target TargetKey `json:"target"` - - LastValidateResult *ValidateResult `json:"lastValidateResult,omitempty"` - CommandResults []CommandResultSummary `json:"commandResults,omitempty"` -} - -type ProjectSummary struct { - Project ProjectKey `json:"project"` - - Targets []*TargetSummary `json:"targets"` -} - func (cr *CommandResult) BuildSummary() *CommandResultSummary { if cr == nil { return nil @@ -82,44 +68,3 @@ func (cr *CommandResult) BuildSummary() *CommandResultSummary { } return ret } - -func BuildProjectSummaries(summaries []CommandResultSummary) []*ProjectSummary { - m := map[ProjectKey]*ProjectSummary{} - for _, rs := range summaries { - p, ok := m[rs.ProjectKey] - if !ok { - p = &ProjectSummary{Project: rs.ProjectKey} - m[rs.ProjectKey] = p - } - - var target *TargetSummary - for i, t := range p.Targets { - if t.Target == rs.TargetKey { - target = p.Targets[i] - break - } - } - if target == nil { - target = &TargetSummary{ - Target: rs.TargetKey, - } - p.Targets = append(p.Targets, target) - } - - target.CommandResults = append(target.CommandResults, rs) - } - - ret := make([]*ProjectSummary, 0, len(m)) - for _, p := range m { - sort.Slice(p.Targets, func(i, j int) bool { - return p.Targets[i].Target.Less(p.Targets[j].Target) - }) - ret = append(ret, p) - } - - sort.Slice(ret, func(i, j int) bool { - return ret[i].Project.Less(ret[j].Project) - }) - - return ret -} diff --git a/pkg/types/result/zz_generated.deepcopy.go b/pkg/types/result/zz_generated.deepcopy.go index a461c0278..3c35c8174 100644 --- a/pkg/types/result/zz_generated.deepcopy.go +++ b/pkg/types/result/zz_generated.deepcopy.go @@ -372,33 +372,6 @@ func (in *ProjectKey) DeepCopy() *ProjectKey { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProjectSummary) DeepCopyInto(out *ProjectSummary) { - *out = *in - out.Project = in.Project - if in.Targets != nil { - in, out := &in.Targets, &out.Targets - *out = make([]*TargetSummary, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(TargetSummary) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSummary. -func (in *ProjectSummary) DeepCopy() *ProjectSummary { - if in == nil { - return nil - } - out := new(ProjectSummary) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResultObject) DeepCopyInto(out *ResultObject) { *out = *in @@ -442,34 +415,6 @@ func (in *TargetKey) DeepCopy() *TargetKey { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TargetSummary) DeepCopyInto(out *TargetSummary) { - *out = *in - out.Target = in.Target - if in.LastValidateResult != nil { - in, out := &in.LastValidateResult, &out.LastValidateResult - *out = new(ValidateResult) - (*in).DeepCopyInto(*out) - } - if in.CommandResults != nil { - in, out := &in.CommandResults, &out.CommandResults - *out = make([]CommandResultSummary, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSummary. -func (in *TargetSummary) DeepCopy() *TargetSummary { - if in == nil { - return nil - } - out := new(TargetSummary) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ValidateResult) DeepCopyInto(out *ValidateResult) { *out = *in From da2c7bc518fb0b443a32a8c08c7d7c6fe4769fb3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 8 Jun 2023 22:21:51 +0200 Subject: [PATCH 1149/2268] fix: Use proper keys --- pkg/webui/ui/src/components/targets-view/TargetsView.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index f480f39f9..f0d2a073c 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -176,8 +176,8 @@ export const TargetsView = () => { {projects.map((ps, i) => { - return - + return + @@ -195,7 +195,7 @@ export const TargetsView = () => { {ps.targets.map((ts, i) => { - return + return doSetSelectedTargetSummary(ts)} /> @@ -245,7 +245,7 @@ export const TargetsView = () => { {ps.targets.map((ts, i) => { - return + return {ts.commandResults?.map((rs, i) => { return Date: Thu, 8 Jun 2023 22:26:54 +0200 Subject: [PATCH 1150/2268] feat: Use websockets to get updates for summaries and validation results --- go.mod | 1 + go.sum | 16 ++ pkg/webui/generate-ts/main.go | 3 +- pkg/webui/server.go | 92 +++------- pkg/webui/staticbuilder.go | 13 -- pkg/webui/ui/src/api.tsx | 160 ++++++++++++++---- pkg/webui/ui/src/components/App.tsx | 85 +++++++++- pkg/webui/ui/src/components/ObjectYaml.tsx | 3 +- pkg/webui/ui/src/components/Router.tsx | 3 +- .../result-view/CommandResultTree.tsx | 2 +- .../result-view/CommandResultView.tsx | 7 +- .../CommandResultDetailsDrawer.tsx | 6 +- .../targets-view/CommandResultItem.tsx | 7 +- .../src/components/targets-view/Projects.tsx | 2 +- .../targets-view/TargetDetailsDrawer.tsx | 2 +- .../src/components/targets-view/Targets.tsx | 14 +- .../components/targets-view/TargetsView.tsx | 24 +-- pkg/webui/ui/src/models.ts | 65 ++----- pkg/webui/ui/src/project-summaries.ts | 77 +++++++++ pkg/webui/validator.go | 118 ++++++++----- pkg/webui/websocket.go | 108 ++++++++++++ 21 files changed, 549 insertions(+), 259 deletions(-) create mode 100644 pkg/webui/ui/src/project-summaries.ts create mode 100644 pkg/webui/websocket.go diff --git a/go.mod b/go.mod index 4fa6e6eff..d79fdb2ba 100644 --- a/go.mod +++ b/go.mod @@ -259,6 +259,7 @@ require ( k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/kubectl v0.27.1 // indirect k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect + nhooyr.io/websocket v1.8.7 // indirect oras.land/oras-go v1.2.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect ) diff --git a/go.sum b/go.sum index 992fed9f8..7e2749e3d 100644 --- a/go.sum +++ b/go.sum @@ -280,6 +280,7 @@ github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= @@ -318,11 +319,15 @@ github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTr github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= @@ -338,6 +343,9 @@ github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XE github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -447,6 +455,7 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= @@ -532,6 +541,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/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= @@ -546,6 +556,7 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= @@ -574,6 +585,7 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -829,6 +841,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= @@ -1390,6 +1404,8 @@ k8s.io/kubectl v0.27.1 h1:9T5c5KdpburYiW8XKQSH0Uly1kMNE90aGSnbYUZNdcA= k8s.io/kubectl v0.27.1/go.mod h1:QsAkSmrRsKTPlAFzF8kODGDl4p35BIwQnc9XFhkcsy8= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= oras.land/oras-go v1.2.3 h1:v8PJl+gEAntI1pJ/LCrDgsuk+1PKVavVEPsYIHFE5uY= oras.land/oras-go v1.2.3/go.mod h1:M/uaPdYklze0Vf3AakfarnpoEckvw0ESbRdN8Z1vdJg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/pkg/webui/generate-ts/main.go b/pkg/webui/generate-ts/main.go index 23a26e7ee..591a4b819 100644 --- a/pkg/webui/generate-ts/main.go +++ b/pkg/webui/generate-ts/main.go @@ -14,10 +14,11 @@ func main() { converter := typescriptify.New(). WithBackupDir(""). Add(result.CommandResult{}). - Add(result.ProjectSummary{}). Add(result.CommandResultSummary{}). + Add(result.ValidateResult{}). Add(webui.ShortName{}). Add(uo.UnstructuredObject{}). + Add(webui.ProjectTargetKey{}). ManageType(types.GitUrl{}, typescriptify.TypeOptions{TSType: "string"}). ManageType(types.GitRepoKey{}, typescriptify.TypeOptions{TSType: "string"}). ManageType(types.YamlUrl{}, typescriptify.TypeOptions{TSType: "string"}). diff --git a/pkg/webui/server.go b/pkg/webui/server.go index 90523fba0..bd49f200d 100644 --- a/pkg/webui/server.go +++ b/pkg/webui/server.go @@ -7,7 +7,6 @@ import ( kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" "github.com/kluctl/kluctl/v2/pkg/results" "github.com/kluctl/kluctl/v2/pkg/status" - "github.com/kluctl/kluctl/v2/pkg/types" "github.com/kluctl/kluctl/v2/pkg/types/k8s" "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils/uo" @@ -87,13 +86,13 @@ func (s *CommandResultsServer) Run(port int) error { api := router.Group("/api") api.GET("/getShortNames", s.getShortNames) - api.GET("/listProjects", s.listProjects) - api.GET("/listResults", s.listResults) api.GET("/getResult", s.getResult) + api.GET("/getResultSummary", s.getResultSummary) api.GET("/getResultObject", s.getResultObject) api.POST("/validateNow", s.validateNow) api.POST("/reconcileNow", s.reconcileNow) api.POST("/deployNow", s.deployNow) + api.Any("/ws", s.ws) address := fmt.Sprintf(":%d", port) listener, err := net.Listen("tcp", address) @@ -116,67 +115,6 @@ func (s *CommandResultsServer) getShortNames(c *gin.Context) { c.JSON(http.StatusOK, GetShortNames()) } -func (s *CommandResultsServer) listResults(c *gin.Context) { - args := struct { - FilterProject string `form:"filterProject"` - FilterSubDir string `form:"filterSubDir"` - }{} - err := c.BindQuery(&args) - if err != nil { - _ = c.AbortWithError(http.StatusBadRequest, err) - return - } - - repoKey, err := types.ParseGitRepoKey(args.FilterProject) - if err != nil { - _ = c.AbortWithError(http.StatusBadRequest, err) - return - } - - var filter *result.ProjectKey - if args.FilterProject != "" { - filter = &result.ProjectKey{ - GitRepoKey: repoKey, - SubDir: args.FilterSubDir, - } - } - - summaries, err := s.collector.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{ - ProjectFilter: filter, - }) - if err != nil { - _ = c.AbortWithError(http.StatusBadRequest, err) - return - } - - c.JSON(http.StatusOK, summaries) -} - -func (s *CommandResultsServer) listProjects(c *gin.Context) { - summaries, err := s.collector.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) - if err != nil { - _ = c.AbortWithError(http.StatusBadRequest, err) - return - } - - projects := result.BuildProjectSummaries(summaries) - - for _, p := range projects { - for _, t := range p.Targets { - key := projectTargetKey{ - Project: p.Project, - Target: t.Target, - } - vr, err := s.vam.getValidateResult(key) - if err == nil { - t.LastValidateResult = vr - } - } - } - - c.JSON(http.StatusOK, projects) -} - type resultIdParam struct { ResultId string `form:"resultId"` } @@ -226,6 +164,28 @@ func (s *CommandResultsServer) getResult(c *gin.Context) { c.JSON(http.StatusOK, sr) } +func (s *CommandResultsServer) getResultSummary(c *gin.Context) { + var params resultIdParam + + err := c.Bind(¶ms) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + sr, err := s.collector.GetCommandResultSummary(params.ResultId) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + if sr == nil { + c.AbortWithStatus(http.StatusNotFound) + return + } + + c.JSON(http.StatusOK, sr) +} + func (s *CommandResultsServer) getResultObject(c *gin.Context) { var params resultIdParam var ref refParam @@ -292,14 +252,14 @@ func (s *CommandResultsServer) getResultObject(c *gin.Context) { } func (s *CommandResultsServer) validateNow(c *gin.Context) { - var params projectTargetKey + var params ProjectTargetKey err := c.Bind(¶ms) if err != nil { _ = c.AbortWithError(http.StatusBadRequest, err) return } - key := projectTargetKey{ + key := ProjectTargetKey{ Project: params.Project, Target: params.Target, } diff --git a/pkg/webui/staticbuilder.go b/pkg/webui/staticbuilder.go index 7cb3aad78..142b5f148 100644 --- a/pkg/webui/staticbuilder.go +++ b/pkg/webui/staticbuilder.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/results" - "github.com/kluctl/kluctl/v2/pkg/types/result" "github.com/kluctl/kluctl/v2/pkg/utils" "github.com/kluctl/kluctl/v2/pkg/yaml" cp "github.com/otiai10/copy" @@ -35,8 +34,6 @@ func (swb *StaticWebuiBuilder) Build(path string) error { return err } - projects := result.BuildProjectSummaries(summaries) - err = os.MkdirAll(filepath.Join(tmpDir, "staticdata"), 0o700) if err != nil { return err @@ -89,16 +86,6 @@ func (swb *StaticWebuiBuilder) Build(path string) error { return err } - j, err = yaml.WriteJsonString(projects) - if err != nil { - return err - } - j = `const staticProjects=` + j - err = os.WriteFile(filepath.Join(tmpDir, "staticdata/projects.js"), []byte(j), 0o600) - if err != nil { - return err - } - j, err = yaml.WriteJsonString(GetShortNames()) if err != nil { return err diff --git a/pkg/webui/ui/src/api.tsx b/pkg/webui/ui/src/api.tsx index 6e29ca97c..150c4223f 100644 --- a/pkg/webui/ui/src/api.tsx +++ b/pkg/webui/ui/src/api.tsx @@ -3,7 +3,6 @@ import { CommandResultSummary, ObjectRef, ProjectKey, - ProjectSummary, ResultObject, ShortName, TargetKey @@ -14,6 +13,7 @@ import { Box, Typography } from "@mui/material"; import Tooltip from "@mui/material/Tooltip"; import "./staticbuild.d.ts" import { loadScript } from "./loadscript"; +import { sleep } from "./utils/misc"; const apiUrl = "/api" const staticPath = "./staticdata" @@ -26,30 +26,66 @@ export enum ObjectType { export interface Api { getShortNames(): Promise - listProjects(): Promise - listResults(filterProject?: string, filterSubDir?: string): Promise + listenUpdates(filterProject: string | undefined, filterSubDir: string | undefined, handle: (msg: any) => void): Promise<() => void> getResult(resultId: string): Promise + getResultSummary(resultId: string): Promise getResultObject(resultId: string, ref: ObjectRef, objectType: string): Promise validateNow(project: ProjectKey, target: TargetKey): Promise reconcileNow(cluster: string, name: string, namespace: string): Promise deployNow(cluster: string, name: string, namespace: string): Promise } -let apiPromise: Promise | undefined = undefined -export async function getApi(): Promise { - if (!apiPromise) { - apiPromise = loadScript(staticPath + "/projects.js") +class RealOrStaticApi implements Api { + api: Promise + + constructor() { + this.api = this.buildApi() + } + + async buildApi(): Promise { + const p = loadScript(staticPath + "/summaries.js") + try { + await p + return new StaticApi() + } catch (error) { + return new RealApi() + } + } + + async deployNow(cluster: string, name: string, namespace: string): Promise { + return (await this.api).deployNow(cluster, name, namespace) + } + + async getResult(resultId: string): Promise { + return (await this.api).getResult(resultId) + } + + async getResultObject(resultId: string, ref: ObjectRef, objectType: string): Promise { + return (await this.api).getResultObject(resultId, ref, objectType) + } + + async getResultSummary(resultId: string): Promise { + return (await this.api).getResultSummary(resultId) } - try { - await apiPromise - return new StaticApi() - } catch (error) { - return new RealApi() + async getShortNames(): Promise { + return (await this.api).getShortNames() + } + + async listenUpdates(filterProject: string | undefined, filterSubDir: string | undefined, handle: (msg: any) => void): Promise<() => void> { + return (await this.api).listenUpdates(filterProject, filterSubDir, handle) + } + + async reconcileNow(cluster: string, name: string, namespace: string): Promise { + return (await this.api).reconcileNow(cluster, name, namespace) + } + + async validateNow(project: ProjectKey, target: TargetKey): Promise { + return (await this.api).validateNow(project, target) } } -export let api = getApi() +export const api = new RealOrStaticApi() class RealApi implements Api { async getShortNames(): Promise { @@ -59,15 +95,12 @@ class RealApi implements Api { .then((response) => response.json()); } - async listProjects(): Promise { - let url = `${apiUrl}/listProjects` - return fetch(url) - .then(handleErrors) - .then((response) => response.json()); - } - - async listResults(filterProject?: string, filterSubDir?: string): Promise { - let url = `${apiUrl}/listResults` + async listenUpdates(filterProject: string | undefined, filterSubDir: string | undefined, handle: (msg: any) => void): Promise<() => void> { + let host = window.location.host + if (process.env.NODE_ENV === 'development') { + host = "localhost:9090" + } + let url = `ws://${host}${apiUrl}/ws` const params = new URLSearchParams() if (filterProject) { params.set("filterProject", filterProject) @@ -76,9 +109,44 @@ class RealApi implements Api { params.set("filterSubDir", filterSubDir) } url += "?" + params.toString() - return fetch(url) - .then(handleErrors) - .then((response) => response.json()); + + let ws: WebSocket | undefined; + let cancelled = false + + const connect = () => { + if (cancelled) { + return + } + + console.log("ws connect: " + url) + ws = new WebSocket(url); + ws.onopen = function () { + console.log("ws connected") + } + ws.onclose = function (event) { + console.log("ws close") + if (!cancelled) { + sleep(5000).then(connect) + } + } + ws.onmessage = function (event: MessageEvent) { + if (cancelled) { + return + } + const msg = JSON.parse(event.data) + handle(msg) + } + } + + connect() + + return () => { + console.log("ws cancel") + cancelled = true + if (ws) { + ws.close() + } + } } async getResult(resultId: string) { @@ -91,6 +159,16 @@ class RealApi implements Api { }); } + async getResultSummary(resultId: string) { + let url = `${apiUrl}/getResultSummary?resultId=${resultId}` + return fetch(url) + .then(handleErrors) + .then(response => response.text()) + .then(json => { + return new CommandResultSummary(json) + }); + } + async getResultObject(resultId: string, ref: ObjectRef, objectType: string) { let url = `${apiUrl}/getResultObject?resultId=${resultId}&${buildRefParams(ref)}&objectType=${objectType}` return fetch(url) @@ -139,26 +217,36 @@ class StaticApi implements Api { await loadScript(staticPath + "/shortnames.js") return staticShortNames } - async listProjects(): Promise { - await loadScript(staticPath + "/projects.js") - return staticProjects - } - async listResults(filterProject?: string, filterSubDir?: string): Promise { + + async listenUpdates(filterProject: string | undefined, filterSubDir: string | undefined, handle: (msg: any) => void): Promise<() => void> { await loadScript(staticPath + "/summaries.js") - return staticSummaries.filter(s => { - if (filterProject && filterProject != s.project.normalizedGitUrl) { - return false + + staticSummaries.forEach(rs => { + if (filterProject && filterProject != rs.project.normalizedGitUrl) { + return } - if (filterSubDir && filterSubDir != s.project.subDir) { - return false + if (filterSubDir && filterSubDir != rs.project.subDir) { + return } - return true + handle({ + "type": "update_summary", + "summary": rs, + }) }) + return () => { + } } + async getResult(resultId: string): Promise { await loadScript(staticPath + `/result-${resultId}.js`) return staticResults.get(resultId) } + async getResultSummary(resultId: string): Promise { + await loadScript(staticPath + "/summaries.js") + return staticSummaries.filter(s => { + return s.id == resultId + }).at(0) + } async getResultObject(resultId: string, ref: ObjectRef, objectType: string): Promise { const result = await this.getResult(resultId) const object = result.objects?.find(x => _.isEqual(x.ref, ref)) diff --git a/pkg/webui/ui/src/components/App.tsx b/pkg/webui/ui/src/components/App.tsx index 7c326111b..3c6fa4a20 100644 --- a/pkg/webui/ui/src/components/App.tsx +++ b/pkg/webui/ui/src/components/App.tsx @@ -1,4 +1,4 @@ -import React, { Dispatch, SetStateAction, useState } from 'react'; +import React, { createContext, Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react'; import '../index.css'; import { Box, ThemeProvider } from "@mui/material"; @@ -6,30 +6,99 @@ import { Outlet, useOutletContext } from "react-router-dom"; import LeftDrawer from "./LeftDrawer"; import { light } from './theme'; import { ActiveFilters } from './result-view/NodeStatusFilter'; +import { CommandResultSummary, ProjectTargetKey, ValidateResult } from "../models"; +import { api } from "../api"; +import { buildProjectSummaries, ProjectSummary } from "../project-summaries"; export interface AppOutletContext { filters?: ActiveFilters setFilters: Dispatch> } - export function useAppOutletContext(): AppOutletContext { return useOutletContext() } +export interface AppContextProps { + summaries: Map + projects: ProjectSummary[] + validateResults: Map +} +export const AppContext = createContext({ + summaries: new Map(), + projects: [], + validateResults: new Map(), +}); + const App = () => { + let [summaries, setSummaries] = useState>(new Map()) + let [validateResults, setValidateResults] = useState>(new Map()) const [filters, setFilters] = useState() - const context: AppOutletContext = { + const updateSummary = (rs: CommandResultSummary) => { + console.log("update_summary", rs.id, rs.commandInfo.startTime) + summaries.set(rs.id, rs) + summaries = new Map(summaries) + setSummaries(summaries) + } + + const deleteSummary = (id: string) => { + console.log("delete_summary", id) + summaries.delete(id) + summaries = new Map(summaries) + setSummaries(summaries) + } + + const updateValidateResult = (key: ProjectTargetKey, vr: ValidateResult) => { + console.log("validate_result", key) + validateResults.set(JSON.stringify(key), vr) + validateResults = new Map(validateResults) + setValidateResults(validateResults) + } + + useEffect(() => { + console.log("starting listenResults") + const cancel = api.listenUpdates(undefined, undefined, msg => { + switch(msg.type) { + case "update_summary": + updateSummary(msg.summary) + break + case "delete_summary": + deleteSummary(msg.id) + break + case "validate_result": + updateValidateResult(msg.key, msg.result) + break + } + }) + return () => { + console.log("cancel listenResults") + cancel.then(c => c()) + } + }, []) + + const projects = useMemo(() => { + return buildProjectSummaries(summaries, validateResults) + }, [summaries, validateResults]) + + const appContext = { + summaries: summaries, + projects: projects, + validateResults: validateResults, + } + + const outletContext: AppOutletContext = { filters: filters, setFilters: setFilters, } return ( - - - } context={context} /> - - + + + + } context={outletContext}/> + + + ); }; diff --git a/pkg/webui/ui/src/components/ObjectYaml.tsx b/pkg/webui/ui/src/components/ObjectYaml.tsx index 2d3fee8fe..cf4c9ebc1 100644 --- a/pkg/webui/ui/src/components/ObjectYaml.tsx +++ b/pkg/webui/ui/src/components/ObjectYaml.tsx @@ -1,6 +1,6 @@ import { CommandResultProps } from "./result-view/CommandResultView"; import { ObjectRef } from "../models"; -import { getApi, ObjectType, usePromise } from "../api"; +import { api, ObjectType, usePromise } from "../api"; import React, { Suspense, useEffect, useState } from "react"; import { CodeViewer } from "./CodeViewer"; @@ -11,7 +11,6 @@ export const ObjectYaml = (props: {treeProps: CommandResultProps, objectRef: Obj const [promise, setPromise] = useState>() const getData = async () => { - const api = await getApi() const o = await api.getResultObject(props.treeProps.summary.id, props.objectRef, props.objectType) return yaml.dump(o) } diff --git a/pkg/webui/ui/src/components/Router.tsx b/pkg/webui/ui/src/components/Router.tsx index 0c908fdb7..a08c8abd5 100644 --- a/pkg/webui/ui/src/components/Router.tsx +++ b/pkg/webui/ui/src/components/Router.tsx @@ -1,7 +1,7 @@ import { createHashRouter, useRouteError } from "react-router-dom"; import React from "react"; import App from "./App"; -import { projectsLoader, TargetsView } from "./targets-view/TargetsView"; +import { TargetsView } from "./targets-view/TargetsView"; import { commandResultLoader, CommandResultView } from "./result-view/CommandResultView"; function ErrorPage() { @@ -27,7 +27,6 @@ export const Router = createHashRouter([ { path: "targets", element: , - loader: projectsLoader, errorElement: , }, { diff --git a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx index 58bd404b6..eead2b7fa 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { useEffect, useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; import TreeView from '@mui/lab/TreeView'; import { TreeItem } from "@mui/lab"; import { NodeBuilder } from "./nodes/NodeBuilder"; diff --git a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx index c1e8ffd96..d5a02d6c7 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx @@ -10,7 +10,7 @@ import { useLoaderData } from "react-router-dom"; import { useAppOutletContext } from "../App"; import { ChangesIcon, CheckboxCheckedIcon, CheckboxIcon, StarIcon, WarningSignIcon } from '../../icons/Icons'; import { dark } from '../theme'; -import { getApi } from "../../api"; +import { api } from "../../api"; export interface CommandResultProps { shortNames: ShortName[] @@ -19,14 +19,13 @@ export interface CommandResultProps { } export async function commandResultLoader({ params }: any) { - const api = await getApi() const result = api.getResult(params.id) const shortNames = api.getShortNames() - const summaries = api.listResults() + const rs = api.getResult(params.id) return { shortNames: await shortNames, - summary: (await summaries).find(x => x.id === params.id), + summary: await rs, commandResult: await result, } } diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx index 37c3e11a9..ee83051a1 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx @@ -1,5 +1,5 @@ -import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; -import { getApi, usePromise } from "../../api"; +import { CommandResultSummary } from "../../models"; +import { api, usePromise } from "../../api"; import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; import { Suspense, useEffect, useState } from "react"; import { NodeData } from "../result-view/nodes/NodeData"; @@ -10,11 +10,11 @@ import { dark } from "../theme"; import { Card, CardCol } from "./Card"; import { CommandResultItem } from "./CommandResultItem"; import React from "react"; +import { ProjectSummary, TargetSummary } from "../../project-summaries"; const sidePanelWidth = 720; async function doGetRootNode(rs: CommandResultSummary) { - const api = await getApi() const shortNames = api.getShortNames() const r = api.getResult(rs.id) const builder = new NodeBuilder({ diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index bf3ff6a52..f4b400e7f 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; +import { CommandResultSummary } from "../../models"; import { useEffect, useMemo, useState } from "react"; import * as yaml from "js-yaml"; import { CodeViewer } from "../CodeViewer"; @@ -9,6 +9,7 @@ import { CommandResultStatusLine } from "../result-view/CommandResultStatusLine" import { useNavigate } from "react-router"; import { formatDurationShort } from "../../utils/duration"; import { DeployIcon, DiffIcon, PruneIcon, TreeViewIcon } from "../../icons/Icons"; +import { ProjectSummary, TargetSummary } from "../../project-summaries"; const calcAgo = (startTime: string) => { const t1 = new Date(startTime) @@ -18,8 +19,8 @@ const calcAgo = (startTime: string) => { } export const CommandResultItem = React.memo((props: { - ps: ProjectSummary, - ts: TargetSummary, + ps: ProjectSummary, + ts: TargetSummary, rs: CommandResultSummary, onSelectCommandResult: (rs: CommandResultSummary) => void, selected?: boolean; diff --git a/pkg/webui/ui/src/components/targets-view/Projects.tsx b/pkg/webui/ui/src/components/targets-view/Projects.tsx index c5ebb34f9..1ead486b3 100644 --- a/pkg/webui/ui/src/components/targets-view/Projects.tsx +++ b/pkg/webui/ui/src/components/targets-view/Projects.tsx @@ -1,10 +1,10 @@ -import { ProjectSummary } from "../../models"; import { getLastPathElement } from "../../utils/misc"; import Paper from "@mui/material/Paper"; import { Box, Typography } from "@mui/material"; import React from "react"; import Tooltip from "@mui/material/Tooltip"; import { ProjectIcon } from "../../icons/Icons"; +import { ProjectSummary } from "../../project-summaries"; export const ProjectItem = (props: { ps: ProjectSummary }) => { const name = getLastPathElement(props.ps.project.gitRepoKey) diff --git a/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx index b3d45a96b..848db14bc 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx @@ -1,4 +1,3 @@ -import { TargetSummary } from "../../models"; import { Box, Drawer, ThemeProvider } from "@mui/material"; import { SidePanel, SidePanelProvider, SidePanelTab } from "../result-view/SidePanel"; import React from "react"; @@ -7,6 +6,7 @@ import { DiffStatus } from "../result-view/nodes/NodeData"; import { ChangesTable } from "../result-view/ChangesTable"; import { ErrorsTable } from "../ErrorsTable"; import { dark } from "../theme"; +import { TargetSummary } from "../../project-summaries"; class MyProvider implements SidePanelProvider { private ts?: TargetSummary; diff --git a/pkg/webui/ui/src/components/targets-view/Targets.tsx b/pkg/webui/ui/src/components/targets-view/Targets.tsx index 530874065..281622fd0 100644 --- a/pkg/webui/ui/src/components/targets-view/Targets.tsx +++ b/pkg/webui/ui/src/components/targets-view/Targets.tsx @@ -1,12 +1,13 @@ -import { KluctlDeploymentInfo, ProjectSummary, TargetSummary } from "../../models"; +import { KluctlDeploymentInfo } from "../../models"; import { ActionMenuItem, ActionsMenu } from "../ActionsMenu"; import Paper from "@mui/material/Paper"; import { Box, Typography, useTheme } from "@mui/material"; import React from "react"; import Tooltip from "@mui/material/Tooltip"; import { Favorite, HeartBroken, PublishedWithChanges } from "@mui/icons-material"; -import { getApi } from "../../api"; +import { api } from "../../api"; import { CpuIcon, FingerScanIcon, MessageQuestionIcon, TargetIcon } from "../../icons/Icons"; +import { ProjectSummary, TargetSummary } from "../../project-summaries"; const StatusIcon = (props: { ps: ProjectSummary, ts: TargetSummary }) => { let icon: React.ReactElement @@ -57,8 +58,7 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel actionMenuItems.push({ icon: , text: "Validate now", - handler: async () => { - const api = await getApi() + handler: () => { api.validateNow(props.ps.project, props.ts.target) } }) @@ -81,16 +81,14 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel actionMenuItems.push({ icon: , text: "Reconcile", - handler: async () => { - const api = await getApi() + handler: () => { api.reconcileNow(props.ts.target.clusterId, kd!.name, kd!.namespace) } }) actionMenuItems.push({ icon: , text: "Deploy", - handler: async () => { - const api = await getApi() + handler: () => { api.deployNow(props.ts.target.clusterId, kd!.name, kd!.namespace) } }) diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index f0d2a073c..5eb732de9 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -1,9 +1,8 @@ -import { useLoaderData } from "react-router-dom"; -import { CommandResultSummary, ProjectSummary, TargetSummary } from "../../models"; +import { CommandResultSummary } from "../../models"; import { Box, Typography, useTheme } from "@mui/material"; -import React, { useCallback, useEffect, useState } from "react"; -import { useAppOutletContext } from "../App"; -import { getApi } from "../../api"; +import React, { useCallback, useContext, useEffect, useState } from "react"; +import { AppContext, useAppOutletContext } from "../App"; +import { api } from "../../api"; import { ProjectItem } from "./Projects"; import { TargetItem } from "./Targets"; import Divider from "@mui/material/Divider"; @@ -11,18 +10,14 @@ import { CommandResultItem } from "./CommandResultItem"; import { CommandResultDetailsDrawer } from "./CommandResultDetailsDrawer"; import { TargetDetailsDrawer } from "./TargetDetailsDrawer"; import { Card, CardCol, CardRow, cardGap, cardHeight, cardWidth, projectCardHeight } from "./Card"; +import { buildProjectSummaries, ProjectSummary, TargetSummary } from "../../project-summaries"; +import { sum } from "lodash"; const colWidth = 416; const curveRadius = 12; const circleRadius = 5; const strokeWidth = 2; -export async function projectsLoader() { - const api = await getApi() - const projects = await api.listProjects() - return projects -} - function ColHeader({ children }: { children: React.ReactNode }) { return { const [selectedCommandResult, setSelectedCommandResult] = useState<{rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary} | undefined>(); const [selectedTargetSummary, setSelectedTargetSummary] = useState(); - const projects = useLoaderData() as ProjectSummary[]; - - useEffect(() => { - context.setFilters(undefined) - }) + const appContext = useContext(AppContext) + const projects = appContext.projects const doSetSelectedCommandResult = useCallback((o?: {rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary}) => { setSelectedCommandResult(o); diff --git a/pkg/webui/ui/src/models.ts b/pkg/webui/ui/src/models.ts index d0eed1881..333a71ee5 100644 --- a/pkg/webui/ui/src/models.ts +++ b/pkg/webui/ui/src/models.ts @@ -815,44 +815,34 @@ export class ValidateResult { return a; } } -export class TargetSummary { - target: TargetKey; - lastValidateResult?: ValidateResult; - commandResults?: CommandResultSummary[]; +export class ShortName { + group?: string; + kind: string; + shortName: string; constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); - this.target = this.convertValues(source["target"], TargetKey); - this.lastValidateResult = this.convertValues(source["lastValidateResult"], ValidateResult); - this.commandResults = this.convertValues(source["commandResults"], CommandResultSummary); + this.group = source["group"]; + this.kind = source["kind"]; + this.shortName = source["shortName"]; } +} +export class UnstructuredObject { - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + + } } -export class ProjectSummary { +export class ProjectTargetKey { project: ProjectKey; - targets: TargetSummary[]; + target: TargetKey; constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); this.project = this.convertValues(source["project"], ProjectKey); - this.targets = this.convertValues(source["targets"], TargetSummary); + this.target = this.convertValues(source["target"], TargetKey); } convertValues(a: any, classs: any, asMap: boolean = false): any { @@ -872,25 +862,4 @@ export class ProjectSummary { } return a; } -} - -export class ShortName { - group?: string; - kind: string; - shortName: string; - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.group = source["group"]; - this.kind = source["kind"]; - this.shortName = source["shortName"]; - } -} -export class UnstructuredObject { - - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } } \ No newline at end of file diff --git a/pkg/webui/ui/src/project-summaries.ts b/pkg/webui/ui/src/project-summaries.ts new file mode 100644 index 000000000..17d4a526c --- /dev/null +++ b/pkg/webui/ui/src/project-summaries.ts @@ -0,0 +1,77 @@ +import { + CommandResultSummary, + ProjectKey, + ProjectTargetKey, TargetKey, ValidateResult, +} from "./models"; +import _ from "lodash"; + +export interface TargetSummary { + target: TargetKey; + lastValidateResult?: ValidateResult; + commandResults: CommandResultSummary[]; +} + +export interface ProjectSummary { + project: ProjectKey; + targets: TargetSummary[]; +} + +export function compareSummaries(a: CommandResultSummary, b: CommandResultSummary) { + return b.commandInfo.startTime.localeCompare(a.commandInfo.startTime) || + b.commandInfo.endTime.localeCompare(b.commandInfo.endTime) || + (b.commandInfo.command || "").localeCompare(a.commandInfo.command || "") +} + +export function buildProjectSummaries(summaries: Map, validateResults: Map) { + const sorted = Array.from(summaries.values()) + sorted.sort(compareSummaries) + + const m = new Map() + sorted.forEach(rs => { + const projectKey = JSON.stringify(rs.projectKey) + + let p = m.get(projectKey) + if (!p) { + p = { + project: rs.projectKey, + targets: [] + } + m.set(projectKey, p) + } + + const ptKey = new ProjectTargetKey() + ptKey.project = rs.projectKey + ptKey.target = rs.targetKey + + const vr = validateResults.get(JSON.stringify(ptKey)) + + let target = p.targets.find(t => _.isEqual(t.target, rs.targetKey)) + if (!target) { + target = { + target: rs.targetKey, + lastValidateResult: vr, + commandResults: [] + } + p.targets.push(target) + } + + target.commandResults.push(rs) + }) + + const ret: ProjectSummary[] = [] + m.forEach(p => { + p.targets.sort((a, b) => { + return (a.target.targetName || "").localeCompare(b.target.targetName || "") || + a.target.clusterId.localeCompare(b.target.clusterId) || + (a.target.discriminator || "")?.localeCompare(b.target.discriminator || "") + }) + ret.push(p) + }) + + ret.sort((a, b) => { + return (a.project.gitRepoKey || "").localeCompare(b.project.gitRepoKey || "") || + (a.project.subDir || "").localeCompare(b.project.subDir || "") + }) + + return ret +} \ No newline at end of file diff --git a/pkg/webui/validator.go b/pkg/webui/validator.go index 78ba366bf..6b524ca76 100644 --- a/pkg/webui/validator.go +++ b/pkg/webui/validator.go @@ -11,7 +11,9 @@ import ( ) const shortValidationInterval = time.Second * 15 -const longValidationInterval = time.Minute * 5 +const longValidationInterval = time.Minute * 1 + +type validateResultHandler func(key ProjectTargetKey, r *result.ValidateResult) type validatorManager struct { ctx context.Context @@ -19,22 +21,23 @@ type validatorManager struct { store results.ResultStore cam *clusterAccessorManager - validators map[projectTargetKey]*validatorEntry + validators map[ProjectTargetKey]*validatorEntry + handlers map[int]validateResultHandler + nextId int mutex sync.Mutex } -type projectTargetKey struct { +type ProjectTargetKey struct { Project result.ProjectKey `json:"project"` Target result.TargetKey `json:"target"` } type validatorEntry struct { vm *validatorManager - key projectTargetKey + key ProjectTargetKey ch chan bool validateResult *result.ValidateResult err error - mutex sync.Mutex } func newValidatorManager(ctx context.Context, store results.ResultStore, cam *clusterAccessorManager) *validatorManager { @@ -42,7 +45,8 @@ func newValidatorManager(ctx context.Context, store results.ResultStore, cam *cl ctx: ctx, store: store, cam: cam, - validators: map[projectTargetKey]*validatorEntry{}, + validators: map[ProjectTargetKey]*validatorEntry{}, + handlers: map[int]validateResultHandler{}, } } @@ -53,31 +57,29 @@ func (vm *validatorManager) start() { return } - projects := result.BuildProjectSummaries(summaries) - - found := map[projectTargetKey]bool{} - for _, project := range projects { - for _, target := range project.Targets { - k := projectTargetKey{ - Project: project.Project, - Target: target.Target, - } - - vm.mutex.Lock() - _, ok := vm.validators[k] - if !ok { - v := &validatorEntry{ - vm: vm, - key: k, - ch: make(chan bool), - } - vm.validators[k] = v - go v.run() + found := map[ProjectTargetKey]bool{} + for _, rs := range summaries { + k := ProjectTargetKey{ + Project: rs.ProjectKey, + Target: rs.TargetKey, + } + if _, ok := found[k]; ok { + continue + } + vm.mutex.Lock() + _, ok := vm.validators[k] + if !ok { + v := &validatorEntry{ + vm: vm, + key: k, + ch: make(chan bool), } - vm.mutex.Unlock() - - found[k] = true + vm.validators[k] = v + go v.run() } + vm.mutex.Unlock() + + found[k] = true } vm.mutex.Lock() @@ -98,7 +100,29 @@ func (vm *validatorManager) start() { }() } -func (vm *validatorManager) getValidateResult(key projectTargetKey) (*result.ValidateResult, error) { +func (vm *validatorManager) addHandler(h validateResultHandler) func() { + vm.mutex.Lock() + defer vm.mutex.Unlock() + + id := vm.nextId + vm.nextId++ + + vm.handlers[id] = h + + for _, v := range vm.validators { + if v.validateResult != nil { + h(v.key, v.validateResult) + } + } + + return func() { + vm.mutex.Lock() + defer vm.mutex.Unlock() + delete(vm.handlers, id) + } +} + +func (vm *validatorManager) getValidateResult(key ProjectTargetKey) (*result.ValidateResult, error) { vm.mutex.Lock() defer vm.mutex.Unlock() v := vm.validators[key] @@ -108,7 +132,7 @@ func (vm *validatorManager) getValidateResult(key projectTargetKey) (*result.Val return v.validateResult, v.err } -func (vm *validatorManager) validateNow(key projectTargetKey) bool { +func (vm *validatorManager) validateNow(key ProjectTargetKey) bool { vm.mutex.Lock() defer vm.mutex.Unlock() v := vm.validators[key] @@ -130,21 +154,19 @@ func (v *validatorEntry) run() { if err != nil { return } - projects := result.BuildProjectSummaries(summaries) - longWait := false - outer: - for _, p := range projects { - if p.Project == v.key.Project { - for _, t := range p.Targets { - if t.Target == v.key.Target { - longWait = v.runOnce(t) - break outer - } - } + var targetSummaries []result.CommandResultSummary + for _, rs := range summaries { + if rs.TargetKey == v.key.Target { + targetSummaries = append(targetSummaries, rs) } } + longWait := false + if len(targetSummaries) != 0 { + longWait = v.runOnce(targetSummaries) + } + waitTime := shortValidationInterval if longWait { waitTime = longValidationInterval @@ -173,11 +195,11 @@ func (v *validatorEntry) findFullProjectResult(summaries []result.CommandResultS return nil } -func (v *validatorEntry) runOnce(target *result.TargetSummary) bool { +func (v *validatorEntry) runOnce(summaries []result.CommandResultSummary) bool { s := status.Start(v.vm.ctx, "Validating: %v", v.key) defer s.Failed() - summary := v.findFullProjectResult(target.CommandResults) + summary := v.findFullProjectResult(summaries) if summary == nil { s.FailedWithMessage("No result summaries for %v", v.key) return false @@ -214,10 +236,14 @@ func (v *validatorEntry) runOnce(target *result.TargetSummary) bool { } s.Success() - v.mutex.Lock() - defer v.mutex.Unlock() + v.vm.mutex.Lock() + defer v.vm.mutex.Unlock() v.validateResult = vr v.err = err + for _, h := range v.vm.handlers { + h(v.key, vr) + } + return true } diff --git a/pkg/webui/websocket.go b/pkg/webui/websocket.go new file mode 100644 index 000000000..978642ffb --- /dev/null +++ b/pkg/webui/websocket.go @@ -0,0 +1,108 @@ +package webui + +import ( + "context" + "github.com/gin-gonic/gin" + "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/types" + "github.com/kluctl/kluctl/v2/pkg/types/result" + "github.com/kluctl/kluctl/v2/pkg/yaml" + "net/http" + "nhooyr.io/websocket" + "time" +) + +func (s *CommandResultsServer) ws(c *gin.Context) { + args := struct { + FilterProject string `form:"filterProject"` + FilterSubDir string `form:"filterSubDir"` + }{} + err := c.BindQuery(&args) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + conn, err := websocket.Accept(c.Writer, c.Request, &websocket.AcceptOptions{ + InsecureSkipVerify: true, + }) + if err != nil { + //s.logf("%v", err) + return + } + defer conn.Close(websocket.StatusInternalError, "the sky is falling") + + repoKey, err := types.ParseGitRepoKey(args.FilterProject) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + return + } + + var filter *result.ProjectKey + if args.FilterProject != "" { + filter = &result.ProjectKey{ + GitRepoKey: repoKey, + SubDir: args.FilterSubDir, + } + } + + sendMessage := func(msg string) error { + ctx, cancel := context.WithTimeout(s.ctx, 500*time.Millisecond) + defer cancel() + w, err := conn.Writer(ctx, websocket.MessageText) + if err != nil { + return err + } + _, err = w.Write([]byte(msg)) + if err != nil { + _ = w.Close() + return err + } + err = w.Close() + if err != nil { + ctx = nil + } + return err + } + + cancel, err := s.collector.WatchCommandResultSummaries(results.ListCommandResultSummariesOptions{ProjectFilter: filter}, + func(summary *result.CommandResultSummary) { + x := yaml.WriteJsonStringMust(map[string]any{ + "type": "update_summary", + "summary": summary, + }) + _ = sendMessage(x) + }, func(id string) { + x := yaml.WriteJsonStringMust(map[string]any{ + "type": "delete_summary", + "id": id, + }) + _ = sendMessage(x) + }, + ) + if err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, err) + return + } + defer cancel() + + h := s.vam.addHandler(func(key ProjectTargetKey, r *result.ValidateResult) { + x := yaml.WriteJsonStringMust(map[string]any{ + "type": "validate_result", + "key": key, + "result": r, + }) + _ = sendMessage(x) + }) + defer h() + + _, _, err = conn.Read(s.ctx) + if err != nil { + cs := websocket.CloseStatus(err) + if cs == websocket.StatusNormalClosure || cs == websocket.StatusGoingAway { + return + } + _ = c.AbortWithError(http.StatusInternalServerError, err) + return + } +} From 64758af361ce666712744198fb9bf93e5db233a5 Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Fri, 9 Jun 2023 06:51:52 +0600 Subject: [PATCH 1151/2268] Added animations. --- .../result-view/CommandResultView.tsx | 2 +- .../CommandResultDetailsDrawer.tsx | 117 ++++++++++++++---- .../targets-view/CommandResultItem.tsx | 33 ++--- .../components/targets-view/TargetsView.tsx | 29 +++-- 4 files changed, 132 insertions(+), 49 deletions(-) diff --git a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx index d5a02d6c7..79fa85254 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { Box, Checkbox, CheckboxProps, Divider, Drawer, FormControlLabel, ThemeProvider, Typography } from "@mui/material"; import { CommandResult, CommandResultSummary, ShortName } from "../../models"; import { NodeData } from "./nodes/NodeData"; -import { SidePanel, SidePanelProvider } from "./SidePanel"; +import { SidePanel } from "./SidePanel"; import { ActiveFilters } from "./NodeStatusFilter"; import CommandResultTree from "./CommandResultTree"; import { useLoaderData } from "react-router-dom"; diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx index ee83051a1..be88c7ecf 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx @@ -1,16 +1,17 @@ import { CommandResultSummary } from "../../models"; import { api, usePromise } from "../../api"; import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; -import { Suspense, useEffect, useState } from "react"; +import { Suspense, useEffect, useMemo, useRef, useState } from "react"; import { NodeData } from "../result-view/nodes/NodeData"; import { SidePanel } from "../result-view/SidePanel"; import { Box, Drawer, ThemeProvider, useTheme } from "@mui/material"; import { Loading } from "../Loading"; import { dark } from "../theme"; -import { Card, CardCol } from "./Card"; +import { Card, cardGap, cardHeight } from "./Card"; import { CommandResultItem } from "./CommandResultItem"; import React from "react"; import { ProjectSummary, TargetSummary } from "../../project-summaries"; +import { cardWidth } from "./Card"; const sidePanelWidth = 720; @@ -22,7 +23,7 @@ async function doGetRootNode(rs: CommandResultSummary) { summary: rs, commandResult: await r, }) - const [node, nodeMap] = builder.buildRoot() + const [node] = builder.buildRoot() return node } @@ -30,13 +31,16 @@ export const CommandResultDetailsDrawer = React.memo((props: { rs?: CommandResultSummary, ts?: TargetSummary, ps?: ProjectSummary, - onClose: () => void + onClose: () => void, + selectedCardRect?: DOMRect }) => { - const { ps, ts } = props; + const { ps, ts, selectedCardRect } = props; const theme = useTheme(); const [promise, setPromise] = useState>(new Promise(() => undefined)); const [selectedCommandResult, setSelectedCommandResult] = useState(); const [prevTargetSummary, setPrevTargetSummary] = useState(ts); + const [cardsCoords, setCardsCoords] = useState<{ left: number, top: number }[]>([]); + const [transitionRunning, setTransitionRunning] = useState(false); if (prevTargetSummary !== ts) { setPrevTargetSummary(ts); @@ -55,8 +59,81 @@ export const CommandResultDetailsDrawer = React.memo((props: { return } + const cardsContainerElem = useRef(); + + useEffect(() => { + const rect = cardsContainerElem.current?.getBoundingClientRect(); + if (!rect || !selectedCardRect || !ts?.commandResults) { + setCardsCoords([]); + return; + } + + const initialCoords = ts.commandResults.map(() => ({ + left: selectedCardRect.left - rect.left, + top: selectedCardRect.top - rect.top + })); + + setCardsCoords(initialCoords); + setTransitionRunning(true); + }, [selectedCardRect, ts?.commandResults]); + + useEffect(() => { + if (cardsCoords.length > 0) { + const targetCoords = cardsCoords.map((_, i) => ({ + left: 0, + top: i * (cardHeight + cardGap) + })); + + if (cardsCoords.length === targetCoords.length + && cardsCoords.every(({ left, top }, i) => + targetCoords[i].left === left && targetCoords[i].top === top + ) + ) { + return; + } + + setTimeout(() => { + setCardsCoords(targetCoords); + setTimeout(() => { + setTransitionRunning(false); + }, theme.transitions.duration.enteringScreen) + }, 10); + } + }, [cardsCoords, theme.transitions.duration.enteringScreen]) + + const zIndex = theme.zIndex.modal + 1; + + const cards = useMemo(() => { + if (!(ps && ts && ts.commandResults && ts.commandResults.length > 0 && cardsCoords.length > 0)) { + return null; + } + + return ts.commandResults.map((rs, i) => { + return + + + }) + }, [ps, ts, cardsCoords, zIndex, theme.transitions, selectedCommandResult]) + return <> - {ps && ts && + {ps && ts && ts.commandResults && ts.commandResults.length > 0 && - e.stopPropagation()} flexGrow={1} justifyContent='center'> - {ts.commandResults?.map((rs, i) => { - return - - - })} - + e.stopPropagation()} + position='relative' + width={cardWidth} + height={cardHeight * ts.commandResults.length + cardGap * (ts.commandResults.length - 1)} + ref={cardsContainerElem} + > + {cards} + } diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index f4b400e7f..0dee155bd 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -18,18 +18,19 @@ const calcAgo = (startTime: string) => { return formatDurationShort(d) } -export const CommandResultItem = React.memo((props: { +export const CommandResultItem = React.memo((props: { ps: ProjectSummary, ts: TargetSummary, - rs: CommandResultSummary, + rs: CommandResultSummary, onSelectCommandResult: (rs: CommandResultSummary) => void, selected?: boolean; }) => { + const { rs, onSelectCommandResult, selected } = props; const navigate = useNavigate() - const [ago, setAgo] = useState(calcAgo(props.rs.commandInfo.startTime)) + const [ago, setAgo] = useState(calcAgo(rs.commandInfo.startTime)) let Icon: () => JSX.Element = DiffIcon - switch (props.rs.commandInfo?.command) { + switch (rs.commandInfo?.command) { case "delete": Icon = PruneIcon break @@ -47,15 +48,15 @@ export const CommandResultItem = React.memo((props: { break } - const cmdInfoYaml = useMemo(() => { - return yaml.dump(props.rs.commandInfo) - }, [props.rs]) - let iconTooltip = + const iconTooltip = useMemo(() => { + const cmdInfoYaml = yaml.dump(rs.commandInfo); + return + }, [rs.commandInfo]); useEffect(() => { - const interval = setInterval(() => setAgo(calcAgo(props.rs.commandInfo.startTime)), 5000); + const interval = setInterval(() => setAgo(calcAgo(rs.commandInfo.startTime)), 5000); return () => clearInterval(interval); - }, [props.rs.commandInfo.startTime]) + }, [rs.commandInfo.startTime]); return props.onSelectCommandResult(props.rs)} + onClick={() => onSelectCommandResult(rs)} > @@ -85,9 +86,9 @@ export const CommandResultItem = React.memo((props: { overflow='hidden' flexGrow={1} > - {props.rs.commandInfo?.command} + {rs.commandInfo?.command} - + - + { e.stopPropagation(); - navigate(`/results/${props.rs.id}`); + navigate(`/results/${rs.id}`); }} sx={{ padding: 0, diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 5eb732de9..2ed8c3982 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -128,27 +128,29 @@ export const TargetsView = () => { const context = useAppOutletContext() const [selectedCommandResult, setSelectedCommandResult] = useState<{rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary} | undefined>(); const [selectedTargetSummary, setSelectedTargetSummary] = useState(); + const [selectedCardRect, setSelectedCardRect] = useState(); const appContext = useContext(AppContext) const projects = appContext.projects - const doSetSelectedCommandResult = useCallback((o?: {rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary}) => { - setSelectedCommandResult(o); + const onTargetDetailsDrawerClose = useCallback(() => { setSelectedTargetSummary(undefined); }, []); - const doSetSelectedTargetSummary = useCallback((ts?: TargetSummary) => { + const onCommandResultDetailsDrawerClose = useCallback(() => { setSelectedCommandResult(undefined); - setSelectedTargetSummary(ts); + setSelectedCardRect(undefined); }, []); - const onCommandResultDetailsDrawerClose = useCallback(() => { - doSetSelectedCommandResult(undefined); - }, [doSetSelectedCommandResult]); - - const onTargetDetailsDrawerClose = useCallback(() => { - setSelectedTargetSummary(undefined); - }, [setSelectedTargetSummary]); + const doSetSelectedCommandResult = useCallback((o?: {rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary}) => { + onTargetDetailsDrawerClose(); + setSelectedCommandResult(o); + }, [onTargetDetailsDrawerClose]); + + const doSetSelectedTargetSummary = useCallback((ts?: TargetSummary) => { + onCommandResultDetailsDrawerClose(); + setSelectedTargetSummary(ts); + }, [onCommandResultDetailsDrawerClose]); return { ts={selectedCommandResult?.ts} ps={selectedCommandResult?.ps} onClose={onCommandResultDetailsDrawerClose} + selectedCardRect={selectedCardRect} /> { zIndex: -i, display: i < 4 ? 'flex' : 'none' }} + onClick={(e) => { + const rect = e.currentTarget.getBoundingClientRect(); + setSelectedCardRect(rect); + }} > Date: Fri, 9 Jun 2023 10:08:59 +0200 Subject: [PATCH 1152/2268] fix: Enlarge TargetDetailsDrawer --- .../ui/src/components/targets-view/TargetDetailsDrawer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx index 848db14bc..7b60afdea 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetDetailsDrawer.tsx @@ -104,7 +104,7 @@ export const TargetDetailsDrawer = React.memo((props: { ts?: TargetSummary, onCl onClose={props.onClose} ModalProps={{ BackdropProps: { invisible: true } }} > - + From 5574285f7f57d3c30b8b75c064b21a93ab712149 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Jun 2023 11:33:22 +0200 Subject: [PATCH 1153/2268] chore: Optimize imports --- pkg/webui/ui/src/components/App.tsx | 2 +- pkg/webui/ui/src/components/LeftDrawer.tsx | 2 +- .../src/components/result-view/CommandResultView.tsx | 11 ++++++++++- .../src/components/result-view/nodes/ObjectNode.tsx | 8 +------- .../targets-view/CommandResultDetailsDrawer.tsx | 6 ++---- .../src/components/targets-view/CommandResultItem.tsx | 3 +-- .../ui/src/components/targets-view/TargetsView.tsx | 8 +++----- pkg/webui/ui/src/components/theme.ts | 2 +- pkg/webui/ui/src/project-summaries.ts | 6 +----- 9 files changed, 21 insertions(+), 27 deletions(-) diff --git a/pkg/webui/ui/src/components/App.tsx b/pkg/webui/ui/src/components/App.tsx index 3c6fa4a20..e41af9bd3 100644 --- a/pkg/webui/ui/src/components/App.tsx +++ b/pkg/webui/ui/src/components/App.tsx @@ -1,4 +1,4 @@ -import React, { createContext, Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { createContext, Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react'; import '../index.css'; import { Box, ThemeProvider } from "@mui/material"; diff --git a/pkg/webui/ui/src/components/LeftDrawer.tsx b/pkg/webui/ui/src/components/LeftDrawer.tsx index 5bd0db728..75e4d18bb 100644 --- a/pkg/webui/ui/src/components/LeftDrawer.tsx +++ b/pkg/webui/ui/src/components/LeftDrawer.tsx @@ -13,7 +13,7 @@ import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import { Link, useLocation, useNavigate } from "react-router-dom"; import { AppOutletContext } from "./App"; -import { KluctlLogo, TargetsIcon, KluctlText, SearchIcon, ArrowLeftIcon } from '../icons/Icons'; +import { ArrowLeftIcon, KluctlLogo, KluctlText, SearchIcon, TargetsIcon } from '../icons/Icons'; import { dark } from './theme'; import { Typography } from '@mui/material'; diff --git a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx index 79fa85254..70c16cb4b 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx @@ -1,6 +1,15 @@ import * as React from 'react'; import { useState } from 'react'; -import { Box, Checkbox, CheckboxProps, Divider, Drawer, FormControlLabel, ThemeProvider, Typography } from "@mui/material"; +import { + Box, + Checkbox, + CheckboxProps, + Divider, + Drawer, + FormControlLabel, + ThemeProvider, + Typography +} from "@mui/material"; import { CommandResult, CommandResultSummary, ShortName } from "../../models"; import { NodeData } from "./nodes/NodeData"; import { SidePanel } from "./SidePanel"; diff --git a/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx b/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx index ec851d53f..24b14c524 100644 --- a/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx +++ b/pkg/webui/ui/src/components/result-view/nodes/ObjectNode.tsx @@ -2,13 +2,7 @@ import React from 'react'; import { ObjectRef } from "../../../models"; import { NodeData } from "./NodeData"; -import { - PublishedWithChanges, - Settings, - SettingsEthernet, - SmartToy, - SvgIconComponent -} from "@mui/icons-material"; +import { PublishedWithChanges, Settings, SettingsEthernet, SmartToy, SvgIconComponent } from "@mui/icons-material"; import { PropertiesTable } from "../../PropertiesTable"; import { findObjectByRef, ObjectType } from "../../../api"; import { CommandResultProps } from "../CommandResultView"; diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx index be88c7ecf..d4c7aee8c 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx @@ -1,17 +1,15 @@ import { CommandResultSummary } from "../../models"; import { api, usePromise } from "../../api"; import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; -import { Suspense, useEffect, useMemo, useRef, useState } from "react"; +import React, { Suspense, useEffect, useMemo, useRef, useState } from "react"; import { NodeData } from "../result-view/nodes/NodeData"; import { SidePanel } from "../result-view/SidePanel"; import { Box, Drawer, ThemeProvider, useTheme } from "@mui/material"; import { Loading } from "../Loading"; import { dark } from "../theme"; -import { Card, cardGap, cardHeight } from "./Card"; +import { Card, cardGap, cardHeight, cardWidth } from "./Card"; import { CommandResultItem } from "./CommandResultItem"; -import React from "react"; import { ProjectSummary, TargetSummary } from "../../project-summaries"; -import { cardWidth } from "./Card"; const sidePanelWidth = 720; diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index 0dee155bd..76c86de0c 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -1,6 +1,5 @@ -import React from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { CommandResultSummary } from "../../models"; -import { useEffect, useMemo, useState } from "react"; import * as yaml from "js-yaml"; import { CodeViewer } from "../CodeViewer"; import Paper from "@mui/material/Paper"; diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 2ed8c3982..ef04d8c9c 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -1,17 +1,15 @@ import { CommandResultSummary } from "../../models"; import { Box, Typography, useTheme } from "@mui/material"; -import React, { useCallback, useContext, useEffect, useState } from "react"; +import React, { useCallback, useContext, useState } from "react"; import { AppContext, useAppOutletContext } from "../App"; -import { api } from "../../api"; import { ProjectItem } from "./Projects"; import { TargetItem } from "./Targets"; import Divider from "@mui/material/Divider"; import { CommandResultItem } from "./CommandResultItem"; import { CommandResultDetailsDrawer } from "./CommandResultDetailsDrawer"; import { TargetDetailsDrawer } from "./TargetDetailsDrawer"; -import { Card, CardCol, CardRow, cardGap, cardHeight, cardWidth, projectCardHeight } from "./Card"; -import { buildProjectSummaries, ProjectSummary, TargetSummary } from "../../project-summaries"; -import { sum } from "lodash"; +import { Card, CardCol, cardGap, cardHeight, CardRow, cardWidth, projectCardHeight } from "./Card"; +import { ProjectSummary, TargetSummary } from "../../project-summaries"; const colWidth = 416; const curveRadius = 12; diff --git a/pkg/webui/ui/src/components/theme.ts b/pkg/webui/ui/src/components/theme.ts index 701d20a15..85c0250e6 100644 --- a/pkg/webui/ui/src/components/theme.ts +++ b/pkg/webui/ui/src/components/theme.ts @@ -1,4 +1,4 @@ -import { PaletteOptions, ThemeOptions, createTheme } from '@mui/material/styles'; +import { createTheme, PaletteOptions, ThemeOptions } from '@mui/material/styles'; const paletteDark = { primary: { main: '#DFEBE9' }, diff --git a/pkg/webui/ui/src/project-summaries.ts b/pkg/webui/ui/src/project-summaries.ts index 17d4a526c..62a6a5011 100644 --- a/pkg/webui/ui/src/project-summaries.ts +++ b/pkg/webui/ui/src/project-summaries.ts @@ -1,8 +1,4 @@ -import { - CommandResultSummary, - ProjectKey, - ProjectTargetKey, TargetKey, ValidateResult, -} from "./models"; +import { CommandResultSummary, ProjectKey, ProjectTargetKey, TargetKey, ValidateResult, } from "./models"; import _ from "lodash"; export interface TargetSummary { From 9fd6b036b40bb511c9d868e9d754b5ee1fb28d64 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Jun 2023 12:42:10 +0200 Subject: [PATCH 1154/2268] fix: Rename react app to kluctl-webui --- pkg/webui/ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/webui/ui/package.json b/pkg/webui/ui/package.json index 68d21f982..2efb3d9b1 100644 --- a/pkg/webui/ui/package.json +++ b/pkg/webui/ui/package.json @@ -1,5 +1,5 @@ { - "name": "react-demo", + "name": "kluctl-webui", "version": "0.1.0", "homepage": "./", "private": true, From 5faeb119a8807c535c5eb8fecf3ab66ebab14ae0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Jun 2023 12:43:25 +0200 Subject: [PATCH 1155/2268] feat: Introduce fix-assets --- pkg/webui/ui/fix-assets/main.go | 119 ++++++++++++++++++++++++++++++++ pkg/webui/ui/package.json | 5 +- 2 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 pkg/webui/ui/fix-assets/main.go diff --git a/pkg/webui/ui/fix-assets/main.go b/pkg/webui/ui/fix-assets/main.go new file mode 100644 index 000000000..76132b6a9 --- /dev/null +++ b/pkg/webui/ui/fix-assets/main.go @@ -0,0 +1,119 @@ +package main + +import ( + "encoding/json" + "io/fs" + "k8s.io/apimachinery/pkg/util/rand" + "os" + "path" + "path/filepath" + "regexp" + "strings" +) + +type assetManifest struct { + Files map[string]string `json:"files"` + Entrypoints []string `json:"entrypoints"` +} + +func main() { + doError := func(err error) { + if err == nil { + return + } + os.Stderr.WriteString(err.Error() + "\n") + os.Exit(1) + } + + err := os.Chdir("build") + doError(err) + + s, err := os.ReadFile("asset-manifest.json") + doError(err) + + var manifest assetManifest + err = json.Unmarshal(s, &manifest) + doError(err) + + hashRegex := regexp.MustCompile(`([a-z-0-9-_]*)(\.[a-f0-9]*)(\.chunk)?\.(js|css)(\.map)?`) + + newEntries := map[string]string{} + for p1, p2 := range manifest.Files { + oldName := path.Base(p2) + newName := path.Base(p1) + oldPath := path.Join(path.Dir(p2), oldName) + newPath := path.Join(path.Dir(p2), newName) + + m := hashRegex.FindSubmatch([]byte(newName)) + if m != nil { + s := strings.Split(newName, ".") + // get rid of hash + s = append(s[0:1], s[2:]...) + newName = strings.Join(s, ".") + newPath = path.Join(path.Dir(p2), newName) + + delete(manifest.Files, p1) + newEntries[newName] = newPath + } else { + manifest.Files[p1] = newPath + } + + // we assume that .map files are implicitly replaced by the non-.map versions + if !strings.HasSuffix(oldName, ".map") { + err = replaceInFiles(".", oldName, newName+"?f="+oldName) + doError(err) + } + + err = os.Rename(oldPath, newPath) + doError(err) + + for i, e := range manifest.Entrypoints { + if path.Base(e) == oldName { + manifest.Entrypoints[i] = path.Join(path.Dir(e), newName) + } + } + } + for k, v := range newEntries { + manifest.Files[k] = v + } + + s, err = json.MarshalIndent(&manifest, "", " ") + doError(err) + err = os.WriteFile("asset-manifest.json", s, 0o600) + doError(err) +} + +func replaceInFiles(dir string, s string, r string) error { + return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if d.IsDir() { + return nil + } + f, err := os.ReadFile(path) + if err != nil { + return err + } + + dummy1 := rand.String(32) + dummy2 := rand.String(32) + + f2 := string(f) + + f2 = strings.ReplaceAll(f2, "f="+s, dummy1) + f2 = strings.ReplaceAll(f2, s+".map", dummy2) + + f2 = strings.ReplaceAll(f2, s, r) + + f2 = strings.ReplaceAll(f2, dummy1, "f="+s) + f2 = strings.ReplaceAll(f2, dummy2, s+".map") + + if strings.Compare(string(f), f2) == 0 { + return nil + } + + err = os.WriteFile(path, []byte(f2), 0o600) + if err != nil { + return err + } + return nil + }) +} diff --git a/pkg/webui/ui/package.json b/pkg/webui/ui/package.json index 2efb3d9b1..610d3ae1a 100644 --- a/pkg/webui/ui/package.json +++ b/pkg/webui/ui/package.json @@ -36,9 +36,10 @@ }, "scripts": { "start": "react-scripts start", - "build": "node build", + "build": "node build && npm run fix-assets", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "fix-assets": "go run ./fix-assets" }, "eslintConfig": { "extends": [ From 7c6388774d4f6d745bbbf3cd9fad7a27b94d94b8 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Jun 2023 13:58:05 +0200 Subject: [PATCH 1156/2268] ci: Check for up-to-date webui build --- .github/workflows/tests.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2a4ffa48f..cb0239ea7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,6 +19,11 @@ jobs: - uses: actions/setup-go@v4 with: go-version: '1.19' + - uses: actions/setup-node@v3 + with: + node-version: 20 + cache: 'npm' + cache-dependency-path: pkg/webui/ui/package-lock.json - uses: actions/cache@v3 with: path: | @@ -66,6 +71,17 @@ jobs: git diff exit 1 fi + - name: Verify webui build is up-to-date + run: | + cd pkg/webui/ui + npm install + npm run build + if [ ! -z "$(git status --porcelain)" ]; then + echo "npm run build must be invoked and the result committed" + git status + git diff + exit 1 + fi tests: strategy: From 52b6f6e84948d94861beb0258ce5934554d780db Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Jun 2023 14:25:17 +0200 Subject: [PATCH 1157/2268] chore: Update all npm packages --- pkg/webui/ui/package-lock.json | 5917 ++++++++++++-------------------- pkg/webui/ui/package.json | 23 +- 2 files changed, 2152 insertions(+), 3788 deletions(-) diff --git a/pkg/webui/ui/package-lock.json b/pkg/webui/ui/package-lock.json index a31048de4..fa6d08424 100644 --- a/pkg/webui/ui/package-lock.json +++ b/pkg/webui/ui/package-lock.json @@ -1,26 +1,27 @@ { - "name": "react-demo", + "name": "kluctl-webui", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "react-demo", + "name": "kluctl-webui", "version": "0.1.0", "dependencies": { - "@emotion/react": "^11.10.6", - "@emotion/styled": "^11.10.6", - "@fontsource-variable/nunito": "^5.0.2", - "@mui/icons-material": "^5.11.11", + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@fontsource-variable/nunito": "^5.0.3", + "@mui/icons-material": "^5.11.16", "@mui/lab": "^5.0.0-alpha.124", - "@mui/material": "^5.11.14", + "@mui/material": "^5.13.4", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", - "@types/node": "^16.18.18", - "@types/react": "^18.0.28", - "@types/react-dom": "^18.0.11", + "@types/node": "^16.18.34", + "@types/react": "^18.2.9", + "@types/react-dom": "^18.2.4", "jdenticon": "^3.2.0", "js-sha256": "^0.9.0", "js-yaml": "^4.1.0", @@ -30,7 +31,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-router": "^6.10.0", - "react-router-dom": "^6.10.0", + "react-router-dom": "^6.12.1", "react-scripts": "5.0.1", "react-syntax-highlighter": "^15.5.0", "sort-by": "^1.2.0", @@ -39,8 +40,8 @@ }, "devDependencies": { "@types/js-yaml": "^4.0.5", - "@types/lodash": "^4.14.192", - "@types/react-syntax-highlighter": "^15.5.6", + "@types/lodash": "^4.14.195", + "@types/react-syntax-highlighter": "^15.5.7", "rewire": "^6.0.0" } }, @@ -49,12 +50,23 @@ "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==" }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -62,39 +74,39 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", + "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", - "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", + "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.3", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.3", - "@babel/types": "^7.21.3", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helpers": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -118,9 +130,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.3.tgz", - "integrity": "sha512-kfhmPimwo6k4P8zxNs8+T7yR44q1LdpsZdE1NkCsVlfiuTPRfnGgjaF8Qgug9q9Pou17u6wneYF0lDCZJATMFg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.5.tgz", + "integrity": "sha512-C69RWYNYtrgIRE5CmTd77ZiLDXqgBipahJc/jHP3sLcAGj6AJzxNIuKNpVnICqbyK7X3pFUfEvL++rvtbQpZkQ==", "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", @@ -151,11 +163,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", - "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", + "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", "dependencies": { - "@babel/types": "^7.21.3", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -164,49 +176,35 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", - "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", + "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@babel/compat-data": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", "semver": "^6.3.0" @@ -227,18 +225,19 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", - "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz", + "integrity": "sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -247,13 +246,22 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-class-features-plugin/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/@babel/helper-create-regexp-features-plugin": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", - "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.5.tgz", + "integrity": "sha512-1VpEFOIbMRaXyDeUwUfmTIxExLwQ+zkW+Bh5zXpApA3oQedBx9v/updixWxnx/bZpKw7u8VxWjb/qWpIcmPq8A==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.3.1" + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -262,10 +270,18 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-regexp-features-plugin/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/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz", + "integrity": "sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg==", "dependencies": { "@babel/helper-compilation-targets": "^7.17.7", "@babel/helper-plugin-utils": "^7.16.7", @@ -287,115 +303,104 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", - "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", - "dependencies": { - "@babel/types": "^7.18.6" - }, + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", - "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", "dependencies": { - "@babel/types": "^7.21.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", + "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -405,111 +410,111 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", - "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.20.7", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", + "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", "dependencies": { - "@babel/types": "^7.20.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", + "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", + "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", + "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -517,150 +522,118 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", - "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", - "bin": { - "parser": "bin/babel-parser.js" + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=4" } }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=4" } }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", - "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" + "color-name": "1.1.3" } }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=0.8.0" } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=4" } }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" + "node": ">=4" } }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz", - "integrity": "sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.21.0" + "node_modules/@babel/parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", + "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-proposal-json-strings": { + "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -669,13 +642,16 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.5.tgz", + "integrity": "sha512-h8hlezQ4dl6ixodgXkH8lUfcD7x+WAuIqPUjwGoItynrXOAv4a4Tci1zA/qjzQjjcl0v3QpLdc2LM6ZACQuY7A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/plugin-syntax-decorators": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -714,39 +690,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-proposal-optional-chaining": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", @@ -779,9 +722,9 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", - "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-create-class-features-plugin": "^7.21.0", @@ -858,11 +801,11 @@ } }, "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.21.0.tgz", - "integrity": "sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.5.tgz", + "integrity": "sha512-avpUOBS7IU6al8MmF1XpAyj9QYeLPuSDJI5D4pVMSMdL7xQokKqJPYQC67RCT0aCTashUXPiGwMJ0DEXXCEmMA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -894,11 +837,11 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", - "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz", + "integrity": "sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -908,11 +851,25 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -944,11 +901,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1052,11 +1009,11 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1065,28 +1022,27 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", - "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", - "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1095,12 +1051,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.5.tgz", + "integrity": "sha512-gGOEvFzm3fWoyD5uZq7vVTD57pPJ3PczPUD/xCFGjzBpUosnklmXyKnGQbbbGs1NPNPskFex0j93yKbHt0cHyg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { "node": ">=6.9.0" @@ -1109,12 +1068,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", - "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1123,20 +1084,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", - "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "globals": "^11.1.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1145,13 +1098,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", - "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", + "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/template": "^7.20.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1160,12 +1112,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", - "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1174,28 +1127,110 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", + "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.12.0" } }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz", + "integrity": "sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", + "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", + "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, "engines": { "node": ">=6.9.0" }, @@ -1204,12 +1239,27 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", + "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1219,12 +1269,12 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz", - "integrity": "sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz", + "integrity": "sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-flow": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-flow": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1234,11 +1284,11 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", - "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1248,13 +1298,28 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", + "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1264,11 +1329,26 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", + "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { "node": ">=6.9.0" @@ -1278,11 +1358,11 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1292,12 +1372,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1307,13 +1387,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", - "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", "dependencies": { - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-simple-access": "^7.20.2" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1323,14 +1403,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", - "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-identifier": "^7.19.1" + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1340,12 +1420,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1355,12 +1435,12 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", - "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1370,11 +1450,59 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", + "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", + "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", + "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "dependencies": { + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1384,12 +1512,43 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", + "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.5.tgz", + "integrity": "sha512-AconbMKOMkyG+xCng2JogMCDcqW8wedQAqpVIL4cOSescZ7+iW8utC6YDZLMCSUIReEA733gzRSaOSXMAt/4WQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1399,11 +1558,43 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", - "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", + "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { "node": ">=6.9.0" @@ -1413,11 +1604,11 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1427,11 +1618,11 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz", - "integrity": "sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz", + "integrity": "sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1441,11 +1632,11 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", + "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1455,15 +1646,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz", - "integrity": "sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz", + "integrity": "sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.21.0" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1473,11 +1664,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.18.6" + "@babel/plugin-transform-react-jsx": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1487,12 +1678,12 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", + "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1502,11 +1693,11 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", - "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", + "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-plugin-utils": "^7.22.5", "regenerator-transform": "^0.15.1" }, "engines": { @@ -1517,11 +1708,11 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1531,15 +1722,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz", - "integrity": "sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg==", - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.5.tgz", + "integrity": "sha512-bg4Wxd1FWeFx3daHFTWk1pkSWK/AyQuiyAoeZAOkAOUBjnZPH6KT7eMxouV47tQ6hl6ax2zyAWBdWZXbrvXlaw==", + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.3", + "babel-plugin-polyfill-corejs3": "^0.8.1", + "babel-plugin-polyfill-regenerator": "^0.5.0", "semver": "^6.3.0" }, "engines": { @@ -1558,11 +1749,11 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1572,12 +1763,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1587,11 +1778,11 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1601,11 +1792,11 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1615,11 +1806,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1629,14 +1820,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", - "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz", + "integrity": "sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1646,11 +1837,26 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", - "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", + "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1660,12 +1866,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1674,38 +1880,41 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", "dependencies": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.5.tgz", + "integrity": "sha512-fj06hw89dpiZzGZtxn+QybifF07nNiZjZ7sazs2aVDcysAZVGjW7+7iFYxg6GLNM47R/thYfLdrXc+2f11Vi9A==", + "dependencies": { + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -1715,44 +1924,61 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.5", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.5", + "@babel/plugin-transform-classes": "^7.22.5", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", + "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", + "@babel/plugin-transform-numeric-separator": "^7.22.5", + "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5", + "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", + "@babel/types": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.3", + "babel-plugin-polyfill-corejs3": "^0.8.1", + "babel-plugin-polyfill-regenerator": "^0.5.0", + "core-js-compat": "^3.30.2", "semver": "^6.3.0" }, "engines": { @@ -1762,6 +1988,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -1786,16 +2023,16 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", - "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.5.tgz", + "integrity": "sha512-M+Is3WikOpEJHgR385HbuCITPTaPRaNkibTEa9oiofmJvIsrceb4yp9RL9Kb+TE8LznmeyZqpP+Lopwcx59xPQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-transform-react-display-name": "^7.22.5", + "@babel/plugin-transform-react-jsx": "^7.22.5", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1805,13 +2042,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz", - "integrity": "sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz", + "integrity": "sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-transform-typescript": "^7.21.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-typescript": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1826,9 +2065,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", + "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", "dependencies": { "regenerator-runtime": "^0.13.11" }, @@ -1837,31 +2076,31 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", - "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.3", - "@babel/types": "^7.21.3", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", + "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1870,12 +2109,12 @@ } }, "node_modules/@babel/types": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", - "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2158,84 +2397,65 @@ } }, "node_modules/@emotion/babel-plugin": { - "version": "11.10.6", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz", - "integrity": "sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/serialize": "^1.1.1", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", - "stylis": "4.1.3" - } - }, - "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@emotion/babel-plugin/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" + "stylis": "4.2.0" } }, "node_modules/@emotion/cache": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz", - "integrity": "sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", "dependencies": { - "@emotion/memoize": "^0.8.0", - "@emotion/sheet": "^1.2.1", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "stylis": "4.1.3" + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" } }, "node_modules/@emotion/hash": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", - "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" }, "node_modules/@emotion/is-prop-valid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", - "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", "dependencies": { - "@emotion/memoize": "^0.8.0" + "@emotion/memoize": "^0.8.1" } }, "node_modules/@emotion/memoize": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, "node_modules/@emotion/react": { - "version": "11.10.6", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz", - "integrity": "sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==", + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.6", - "@emotion/cache": "^11.10.5", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { @@ -2248,33 +2468,33 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", - "integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", + "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", "dependencies": { - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/unitless": "^0.8.0", - "@emotion/utils": "^1.2.0", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", "csstype": "^3.0.2" } }, "node_modules/@emotion/sheet": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz", - "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" }, "node_modules/@emotion/styled": { - "version": "11.10.6", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.6.tgz", - "integrity": "sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", + "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.6", - "@emotion/is-prop-valid": "^1.2.0", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0" + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" }, "peerDependencies": { "@emotion/react": "^11.0.0-rc.0", @@ -2287,32 +2507,32 @@ } }, "node_modules/@emotion/unitless": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", - "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", "peerDependencies": { "react": ">=16.8.0" } }, "node_modules/@emotion/utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", - "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" }, "node_modules/@emotion/weak-memoize": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", - "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz", - "integrity": "sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -2324,21 +2544,21 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", - "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", - "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -2379,22 +2599,22 @@ } }, "node_modules/@eslint/js": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", - "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@fontsource-variable/nunito": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@fontsource-variable/nunito/-/nunito-5.0.2.tgz", - "integrity": "sha512-ztW98DGVZbq77ITkqg5+C5O8CI/SoaiqWOFBfbyE7CHMkU8XaBOXFe2l6H7YREE15mMIKCGA/icztQuoLi21lA==" + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@fontsource-variable/nunito/-/nunito-5.0.3.tgz", + "integrity": "sha512-yxQ+IPeqA43DXNigCdU4rAziyeeKqm7VkAaJ9/o/h/qBA6w/dKsAH/YWmNB04PFJ6n84+3IAS7AKiOFs+4yZsw==" }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -2512,6 +2732,14 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -2536,70 +2764,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/console/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@jest/console/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/core": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", @@ -2646,70 +2810,6 @@ } } }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/core/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@jest/core/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/environment": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", @@ -2796,59 +2896,6 @@ } } }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@jest/reporters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/reporters/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -2857,17 +2904,6 @@ "node": ">=0.10.0" } }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/schemas": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", @@ -2953,59 +2989,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/transform/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@jest/transform/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/transform/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3014,17 +2997,6 @@ "node": ">=0.10.0" } }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/types": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", @@ -3040,77 +3012,14 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -3133,56 +3042,48 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, "node_modules/@mui/base": { - "version": "5.0.0-alpha.122", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.122.tgz", - "integrity": "sha512-IgZEFQyHa39J1+Q3tekVdhPuUm1fr3icddaNLmiAIeYTVXmR7KR5FhBAIL0P+4shlPq0liUPGlXryoTm0iCeFg==", + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.4.tgz", + "integrity": "sha512-ejhtqYJpjDgHGEljjMBQWZ22yEK0OzIXNa7toJmmXsP4TT3W7xVy8bTJ0TniPDf+JNjrsgfgiFTDGdlEhV1E+g==", "dependencies": { "@babel/runtime": "^7.21.0", - "@emotion/is-prop-valid": "^1.2.0", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.13", - "@popperjs/core": "^2.11.6", + "@emotion/is-prop-valid": "^1.2.1", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.13.1", + "@popperjs/core": "^2.11.8", "clsx": "^1.2.1", "prop-types": "^15.8.1", "react-is": "^18.2.0" @@ -3205,24 +3106,19 @@ } } }, - "node_modules/@mui/base/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.11.14", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.14.tgz", - "integrity": "sha512-rfc08z6+3Fif+Gopx2/qmk+MEQlwYeA+gOcSK048BHkTty/ol/boHuVeL2BNC/cf9OVRjJLYHtVb/DeW791LSQ==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.4.tgz", + "integrity": "sha512-yFrMWcrlI0TqRN5jpb6Ma9iI7sGTHpytdzzL33oskFHNQ8UgrtPas33Y1K7sWAMwCrr1qbWDrOHLAQG4tAzuSw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui" } }, "node_modules/@mui/icons-material": { - "version": "5.11.11", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.11.11.tgz", - "integrity": "sha512-Eell3ADmQVE8HOpt/LZ3zIma8JSvPh3XgnhwZLT0k5HRqZcd6F/QDHc7xsWtgz09t+UEFvOYJXjtrwKmLdwwpw==", + "version": "5.11.16", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.11.16.tgz", + "integrity": "sha512-oKkx9z9Kwg40NtcIajF9uOXhxiyTZrrm9nmIJ4UjkU2IdHpd4QVLbCc/5hZN/y0C6qzi2Zlxyr9TGddQx2vx2A==", "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -3245,15 +3141,15 @@ } }, "node_modules/@mui/lab": { - "version": "5.0.0-alpha.124", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.124.tgz", - "integrity": "sha512-K/XEv1zYyLi3tS63tyDta1mqWql+at5w7rWp5Yd63Jx1NjPUtgopAvyRZG2SVYPs/yBwfyDPW1iqQXpRw8h9Xw==", + "version": "5.0.0-alpha.133", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.133.tgz", + "integrity": "sha512-pPL5/f6si8eCXlsnOZrO+/zg5Yv6qKa9OpI6nGP77Mpn7iYHm9qrcsWFIBs6YjgxqJf6dA2IqtHaSNOSndrXDw==", "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/base": "5.0.0-alpha.122", - "@mui/system": "^5.11.14", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.13", + "@mui/base": "5.0.0-beta.4", + "@mui/system": "^5.13.2", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.13.1", "clsx": "^1.2.1", "prop-types": "^15.8.1", "react-is": "^18.2.0" @@ -3285,25 +3181,20 @@ } } }, - "node_modules/@mui/lab/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/@mui/material": { - "version": "5.11.14", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.14.tgz", - "integrity": "sha512-uoiUyybmo+M+nYARBygmbXgX6s/hH0NKD56LCAv9XvmdGVoXhEGjOvxI5/Bng6FS3NNybnA8V+rgZW1Z/9OJtA==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.13.4.tgz", + "integrity": "sha512-Yq+4f1KLPa/Szd3xqra2hbOAf2Usl8GbubncArM6LIp40mBLtXIdPE29MNtHsbtuzz4g+eidrETgoi3wdbEYfQ==", "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/base": "5.0.0-alpha.122", - "@mui/core-downloads-tracker": "^5.11.14", - "@mui/system": "^5.11.14", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.13", - "@types/react-transition-group": "^4.4.5", + "@mui/base": "5.0.0-beta.4", + "@mui/core-downloads-tracker": "^5.13.4", + "@mui/system": "^5.13.2", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.13.1", + "@types/react-transition-group": "^4.4.6", "clsx": "^1.2.1", - "csstype": "^3.1.1", + "csstype": "^3.1.2", "prop-types": "^15.8.1", "react-is": "^18.2.0", "react-transition-group": "^4.4.5" @@ -3334,18 +3225,13 @@ } } }, - "node_modules/@mui/material/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/@mui/private-theming": { - "version": "5.11.13", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.13.tgz", - "integrity": "sha512-PJnYNKzW5LIx3R+Zsp6WZVPs6w5sEKJ7mgLNnUXuYB1zo5aX71FVLtV7geyPXRcaN2tsoRNK7h444ED0t7cIjA==", + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.13.1.tgz", + "integrity": "sha512-HW4npLUD9BAkVppOUZHeO1FOKUJWAwbpy0VQoGe3McUYTlck1HezGHQCfBQ5S/Nszi7EViqiimECVl9xi+/WjQ==", "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.11.13", + "@mui/utils": "^5.13.1", "prop-types": "^15.8.1" }, "engines": { @@ -3366,13 +3252,13 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.11.11", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.11.11.tgz", - "integrity": "sha512-wV0UgW4lN5FkDBXefN8eTYeuE9sjyQdg5h94vtwZCUamGQEzmCOtir4AakgmbWMy0x8OLjdEUESn9wnf5J9MOg==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.13.2.tgz", + "integrity": "sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw==", "dependencies": { "@babel/runtime": "^7.21.0", - "@emotion/cache": "^11.10.5", - "csstype": "^3.1.1", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.2", "prop-types": "^15.8.1" }, "engines": { @@ -3397,17 +3283,17 @@ } }, "node_modules/@mui/system": { - "version": "5.11.14", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.14.tgz", - "integrity": "sha512-/MBv5dUoijJNEKEGi5tppIszGN0o2uejmeISi5vl0CLcaQsI1cd+uBgK+JYUP1VWvI/MtkWRLVSWtF2FWhu5Nw==", + "version": "5.13.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.2.tgz", + "integrity": "sha512-TPyWmRJPt0JPVxacZISI4o070xEJ7ftxpVtu6LWuYVOUOINlhoGOclam4iV8PDT3EMQEHuUrwU49po34UdWLlw==", "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/private-theming": "^5.11.13", - "@mui/styled-engine": "^5.11.11", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.13", + "@mui/private-theming": "^5.13.1", + "@mui/styled-engine": "^5.13.2", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.13.1", "clsx": "^1.2.1", - "csstype": "^3.1.1", + "csstype": "^3.1.2", "prop-types": "^15.8.1" }, "engines": { @@ -3436,9 +3322,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.3.tgz", - "integrity": "sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==", + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", + "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==", "peerDependencies": { "@types/react": "*" }, @@ -3449,13 +3335,13 @@ } }, "node_modules/@mui/utils": { - "version": "5.11.13", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.13.tgz", - "integrity": "sha512-5ltA58MM9euOuUcnvwFJqpLdEugc9XFsRR8Gt4zZNb31XzMfSKJPR4eumulyhsOTK1rWf7K4D63NKFPfX0AxqA==", + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz", + "integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==", "dependencies": { "@babel/runtime": "^7.21.0", "@types/prop-types": "^15.7.5", - "@types/react-is": "^16.7.1 || ^17.0.0", + "@types/react-is": "^18.2.0", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, @@ -3470,11 +3356,6 @@ "react": "^17.0.0 || ^18.0.0" } }, - "node_modules/@mui/utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -3584,19 +3465,27 @@ } } }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, "node_modules/@remix-run/router": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz", - "integrity": "sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.3.tgz", + "integrity": "sha512-EXJysQ7J3veRECd0kZFQwYYd5sJMcq2O/m60zu1W2l3oVQ9xtub8jTOtYRE0+M2iomyG/W3Ps7+vp2kna0C27Q==", "engines": { "node": ">=14" } @@ -3676,9 +3565,9 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" }, "node_modules/@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.1.tgz", + "integrity": "sha512-RkmuBcqiNioeeBKbgzMlOdreUkJfYaSjwgx9XDgGGpjvWgyaxWvDmZVSN9CS6LjEASadhgPv2BcFp+SeouWXXA==" }, "node_modules/@sinclair/typebox": { "version": "0.24.51", @@ -3920,9 +3809,9 @@ } }, "node_modules/@testing-library/dom": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.0.1.tgz", - "integrity": "sha512-fTOVsMY9QLFCCXRHG3Ese6cMH5qIWwSbgxZsgeF5TNsy81HKaZ4kgehnSF8FsR3OF+numlIV2YcU79MzbnhSig==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.0.tgz", + "integrity": "sha512-Dffe68pGwI6WlLRYR2I0piIkyole9cSBH5jGQKCGMRpHW5RHCqAUaqc2Kv0tUyd4dU4DLPKhJIjyKOnjv4tuUw==", "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", @@ -3938,76 +3827,6 @@ "node": ">=14" } }, - "node_modules/@testing-library/dom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@testing-library/dom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@testing-library/dom/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@testing-library/dom/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true - }, - "node_modules/@testing-library/dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@testing-library/jest-dom": { "version": "5.16.5", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", @@ -4029,20 +3848,6 @@ "yarn": ">=1" } }, - "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@testing-library/jest-dom/node_modules/chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -4055,41 +3860,6 @@ "node": ">=8" } }, - "node_modules/@testing-library/jest-dom/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@testing-library/jest-dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@testing-library/react": { "version": "13.4.0", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", @@ -4125,99 +3895,35 @@ "node": ">=12" } }, - "node_modules/@testing-library/react/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", "dependencies": { - "color-convert": "^2.0.1" + "@babel/runtime": "^7.12.5" }, "engines": { - "node": ">=8" + "node": ">=10", + "npm": ">=6" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" } }, - "node_modules/@testing-library/react/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 6" } }, - "node_modules/@testing-library/react/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@testing-library/react/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@testing-library/react/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/react/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/user-event": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", - "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" + "node": ">=10.13.0" } }, "node_modules/@types/aria-query": { @@ -4226,9 +3932,9 @@ "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==" }, "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -4255,11 +3961,11 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/body-parser": { @@ -4288,18 +3994,18 @@ } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" } }, "node_modules/@types/eslint": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.3.tgz", - "integrity": "sha512-fa7GkppZVEByMWGbTtE5MbmXWJTVbrjjaS8K6uQj+XtuuUv1fsuPAxhygfqLmsb/Ufb3CV8deFCpiMfAgi00Sw==", + "version": "8.40.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.1.tgz", + "integrity": "sha512-vRb792M4mF1FBT+eoLecmkpLXwxsBHvWWRGJjzbYANBM6DtiJc6yETyv4rqDA6QNjF1pkj1U7LMA6dGb3VYlHw==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4315,9 +4021,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", - "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" }, "node_modules/@types/express": { "version": "4.17.17", @@ -4331,13 +4037,14 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.33", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", - "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", "dependencies": { "@types/node": "*", "@types/qs": "*", - "@types/range-parser": "*" + "@types/range-parser": "*", + "@types/send": "*" } }, "node_modules/@types/graceful-fs": { @@ -4362,9 +4069,9 @@ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" }, "node_modules/@types/http-proxy": { - "version": "1.17.10", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz", - "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==", + "version": "1.17.11", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", + "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", "dependencies": { "@types/node": "*" } @@ -4406,9 +4113,9 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -4416,20 +4123,20 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, "node_modules/@types/lodash": { - "version": "4.14.192", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.192.tgz", - "integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==", + "version": "4.14.195", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz", + "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", "dev": true }, "node_modules/@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/node": { - "version": "16.18.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.18.tgz", - "integrity": "sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==" + "version": "16.18.34", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.34.tgz", + "integrity": "sha512-VmVm7gXwhkUimRfBwVI1CHhwp86jDWR04B5FGebMMyxV90SlCmFujwUHrxTD4oO+SOYU86SoxvhgeRQJY7iXFg==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -4437,9 +4144,9 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==" + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" }, "node_modules/@types/prop-types": { "version": "15.7.5", @@ -4462,9 +4169,9 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/react": { - "version": "18.0.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", - "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.9.tgz", + "integrity": "sha512-pL3JAesUkF7PEQGxh5XOwdXGV907te6m1/Qe1ERJLgomojS6Ne790QiA7GUl434JEkFA2aAaB6qJ5z4e1zJn/w==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4472,34 +4179,34 @@ } }, "node_modules/@types/react-dom": { - "version": "18.0.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", - "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", + "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-is": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", - "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==", "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-syntax-highlighter": { - "version": "15.5.6", - "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.6.tgz", - "integrity": "sha512-i7wFuLbIAFlabTeD2I1cLjEOrG/xdMa/rpx2zwzAoGHuXJDhSqp9BSfDlMHSh9JSuNfxHk9eEmMX6D55GiyjGg==", + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.7.tgz", + "integrity": "sha512-bo5fEO5toQeyCp0zVHBeggclqf5SQ/Z5blfFmjwO5dkMVGPgmiwZsJh9nu/Bo5L7IHTuGWrja6LxJVE2uB5ZrQ==", "dev": true, "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", + "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", "dependencies": { "@types/react": "*" } @@ -4518,14 +4225,23 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==" + }, + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } }, "node_modules/@types/serve-index": { "version": "1.9.1", @@ -4558,9 +4274,9 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, "node_modules/@types/testing-library__jest-dom": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", - "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "version": "5.14.6", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz", + "integrity": "sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA==", "dependencies": { "@types/jest": "*" } @@ -4576,9 +4292,9 @@ "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" }, "node_modules/@types/ws": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", - "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", + "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", "dependencies": { "@types/node": "*" } @@ -4597,14 +4313,14 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.56.0.tgz", - "integrity": "sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==", + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz", + "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==", "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.56.0", - "@typescript-eslint/type-utils": "5.56.0", - "@typescript-eslint/utils": "5.56.0", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/type-utils": "5.59.9", + "@typescript-eslint/utils": "5.59.9", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -4630,11 +4346,11 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.56.0.tgz", - "integrity": "sha512-sxWuj0eO5nItmKgZmsBbChVt90EhfkuncDCPbLAVeEJ+SCjXMcZN3AhhNbxed7IeGJ4XwsdL3/FMvD4r+FLqqA==", + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.59.9.tgz", + "integrity": "sha512-eZTK/Ci0QAqNc/q2MqMwI2+QI5ZI9HM12FcfGwbEvKif5ev/CIIYLmrlckvgPrC8XSbl39HtErR5NJiQkRkvWg==", "dependencies": { - "@typescript-eslint/utils": "5.56.0" + "@typescript-eslint/utils": "5.59.9" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4648,13 +4364,13 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.56.0.tgz", - "integrity": "sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg==", + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz", + "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==", "dependencies": { - "@typescript-eslint/scope-manager": "5.56.0", - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/typescript-estree": "5.56.0", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", "debug": "^4.3.4" }, "engines": { @@ -4674,12 +4390,12 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz", - "integrity": "sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==", + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz", + "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==", "dependencies": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0" + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4690,12 +4406,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.56.0.tgz", - "integrity": "sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A==", + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz", + "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==", "dependencies": { - "@typescript-eslint/typescript-estree": "5.56.0", - "@typescript-eslint/utils": "5.56.0", + "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/utils": "5.59.9", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -4716,9 +4432,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz", - "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==", + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz", + "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -4728,12 +4444,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz", - "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==", + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz", + "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==", "dependencies": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4754,16 +4470,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.56.0.tgz", - "integrity": "sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==", + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz", + "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.56.0", - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/typescript-estree": "5.56.0", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -4799,11 +4515,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz", - "integrity": "sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==", + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz", + "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==", "dependencies": { - "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/types": "5.59.9", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -4815,133 +4531,133 @@ } }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, @@ -5004,9 +4720,9 @@ } }, "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "peerDependencies": { "acorn": "^8" } @@ -5019,27 +4735,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/acorn-node/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/acorn-walk": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", @@ -5181,16 +4876,24 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -5409,9 +5112,9 @@ } }, "node_modules/axe-core": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz", - "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz", + "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==", "engines": { "node": ">=4" } @@ -5445,70 +5148,6 @@ "@babel/core": "^7.8.0" } }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/babel-loader": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", @@ -5596,12 +5235,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz", + "integrity": "sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw==", "dependencies": { "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", + "@babel/helper-define-polyfill-provider": "^0.4.0", "semver": "^6.1.1" }, "peerDependencies": { @@ -5617,23 +5256,23 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz", + "integrity": "sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" + "@babel/helper-define-polyfill-provider": "^0.4.0", + "core-js-compat": "^3.30.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz", + "integrity": "sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" + "@babel/helper-define-polyfill-provider": "^0.4.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -5846,9 +5485,9 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz", + "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==", "funding": [ { "type": "opencollective", @@ -5857,13 +5496,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001489", + "electron-to-chromium": "^1.4.411", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -5964,9 +5607,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001469", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz", - "integrity": "sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g==", + "version": "1.0.30001497", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001497.tgz", + "integrity": "sha512-I4/duVK4wL6rAK/aKZl3HXB4g+lIZvaT4VLAn2rCgJ38jVLb0lv2Xug6QuqmxXFVRJMF74SPPWPJ/1Sdm3vCzw==", "funding": [ { "type": "opencollective", @@ -5975,6 +5618,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -5995,16 +5642,18 @@ } }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/char-regex": { @@ -6170,12 +5819,31 @@ "node": ">= 4.0" } }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + "node_modules/coa/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/color-convert": { + "node_modules/coa/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", @@ -6183,20 +5851,68 @@ "color-name": "1.1.3" } }, - "node_modules/color-name": { + "node_modules/coa/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/coa/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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/coa/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" }, "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, "node_modules/combined-stream": { "version": "1.0.8", @@ -6346,9 +6062,9 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/core-js": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", - "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==", + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.2.tgz", + "integrity": "sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -6356,9 +6072,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.1.tgz", - "integrity": "sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==", + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", + "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==", "dependencies": { "browserslist": "^4.21.5" }, @@ -6368,9 +6084,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.29.1.tgz", - "integrity": "sha512-4En6zYVi0i0XlXHVz/bi6l1XDjCqkKRq765NXuX+SnaIatlE96Odt5lMLjdxUiNI1v9OXI5DSLWYPlmTfkTktg==", + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.30.2.tgz", + "integrity": "sha512-p/npFUJXXBkCCTIlEGBdghofn00jWG6ZOtdoIXSJmAu2QBvN0IqpZXWweOytcwE6cfx8ZvVUy1vw8zxhe4Y2vg==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -6436,9 +6152,9 @@ } }, "node_modules/css-declaration-sorter": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", - "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", + "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==", "engines": { "node": "^10 || ^12 || >=14" }, @@ -6464,14 +6180,14 @@ } }, "node_modules/css-loader": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", - "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", + "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.19", + "postcss": "^8.4.21", "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-local-by-default": "^4.0.3", "postcss-modules-scope": "^3.0.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", @@ -6557,14 +6273,14 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.1.0.tgz", + "integrity": "sha512-Jw+GZVbP5IggB2WAn6UHI02LBwGmsIeYN/lNbSMZyDziQ7jmtAUrqKqDja+W89YHVs+KL/3IkIMltAklqB1vAw==", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 12.13.0" @@ -6653,13 +6369,19 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" }, "node_modules/cssdb": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.4.1.tgz", - "integrity": "sha512-0Q8NOMpXJ3iTDDbUv9grcmQAfdDx4qz+fN/+Md2FGbevT+6+bJNQ2LjB2YIUlLbpBTM32idU1Sb+tb/uGt6/XQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.6.0.tgz", + "integrity": "sha512-Nna7rph8V0jC6+JBY4Vk4ndErUmfJfV6NJCaZdurL0omggabiy+QB2HCQtu5c/ACLZ0I7REv7A4QyPIoYzZx0w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + } + ] }, "node_modules/cssesc": { "version": "3.0.0", @@ -6803,9 +6525,9 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -6852,15 +6574,16 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" }, "node_modules/deep-equal": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", - "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz", + "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==", "dependencies": { + "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.2", - "get-intrinsic": "^1.1.3", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.0", "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.1", + "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", @@ -6868,7 +6591,7 @@ "object-is": "^1.1.5", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.0", "side-channel": "^1.0.4", "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", @@ -6925,14 +6648,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/defined": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", - "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -7000,22 +6715,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", - "dependencies": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" - }, - "bin": { - "detective": "bin/detective.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -7051,9 +6750,9 @@ "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" }, "node_modules/dns-packet": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", - "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", + "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" }, @@ -7211,9 +6910,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.335", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.335.tgz", - "integrity": "sha512-l/eowQqTnrq3gu+WSrdfkhfNHnPgYqlKAwxz7MTOj6mom19vpEDHNXl6dxDxyTiYuhemydprKr/HCrHfgk+OfQ==" + "version": "1.4.425", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.425.tgz", + "integrity": "sha512-wv1NufHxu11zfDbY4fglYQApMswleE9FL/DSeyOyauVXDZ+Kco96JK/tPfBUaDqfRarYp2WH2hJ/5UnVywp9Jg==" }, "node_modules/emittery": { "version": "0.8.1", @@ -7248,9 +6947,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", + "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -7367,9 +7066,9 @@ } }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", + "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==" }, "node_modules/es-set-tostringtag": { "version": "2.0.1", @@ -7422,11 +7121,14 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/escodegen": { @@ -7507,15 +7209,15 @@ } }, "node_modules/eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", - "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.1", - "@eslint/js": "8.36.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -7524,9 +7226,9 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.5.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -7534,13 +7236,12 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -7608,9 +7309,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dependencies": { "debug": "^3.2.7" }, @@ -7838,11 +7539,11 @@ } }, "node_modules/eslint-plugin-testing-library": { - "version": "5.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.10.2.tgz", - "integrity": "sha512-f1DmDWcz5SDM+IpCkEX0lbFqrrTs8HRsEElzDEqN/EBI0hpRj8Cns5+IVANXswE8/LeybIJqPAOQIFu2j5Y5sw==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.0.tgz", + "integrity": "sha512-ELY7Gefo+61OfXKlQeXNIDVVLPcvKTeiQOoMZG9TeuWa7Ln4dUNRv8JdRWBQI9Mbb427XGlVB1aa1QPZxBJM8Q==", "dependencies": { - "@typescript-eslint/utils": "^5.43.0" + "@typescript-eslint/utils": "^5.58.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0", @@ -7853,15 +7554,18 @@ } }, "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-utils": { @@ -7889,11 +7593,14 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-webpack-plugin": { @@ -7945,14 +7652,6 @@ "ajv": "^8.8.2" } }, - "node_modules/eslint-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", @@ -7972,14 +7671,14 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.1.0.tgz", + "integrity": "sha512-Jw+GZVbP5IggB2WAn6UHI02LBwGmsIeYN/lNbSMZyDziQ7jmtAUrqKqDja+W89YHVs+KL/3IkIMltAklqB1vAw==", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 12.13.0" @@ -8003,62 +7702,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -8073,25 +7716,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -8104,13 +7728,13 @@ } }, "node_modules/espree": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", - "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8602,51 +8226,6 @@ } } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", @@ -8676,14 +8255,6 @@ "node": ">=10" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", @@ -8701,17 +8272,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -8783,9 +8343,9 @@ } }, "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", + "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==" }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -8858,12 +8418,13 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" }, "funding": { @@ -9041,6 +8602,11 @@ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -9085,11 +8651,11 @@ } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-property-descriptors": { @@ -9256,9 +8822,9 @@ } }, "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.5.tgz", + "integrity": "sha512-72TJlcMkYsEJASa/3HnX7VT59htM7iSHbH59NSZbtc+22Ap0Txnlx91sfeB+/A7wNZg7UxtZdhAW4y+/jimrdg==" }, "node_modules/html-escaper": { "version": "2.0.2", @@ -9286,9 +8852,9 @@ } }, "node_modules/html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.1.tgz", + "integrity": "sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA==", "dependencies": { "@types/html-minifier-terser": "^6.0.0", "html-minifier-terser": "^6.0.2", @@ -9471,9 +9037,9 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, "node_modules/immer": { - "version": "9.0.19", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", - "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==", + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -9494,14 +9060,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -9569,9 +9127,9 @@ } }, "node_modules/ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", "engines": { "node": ">= 10" } @@ -9680,9 +9238,9 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dependencies": { "has": "^1.0.3" }, @@ -10057,25 +9615,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", @@ -10110,14 +9649,14 @@ } }, "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, "bin": { "jake": "bin/cli.js" @@ -10126,79 +9665,15 @@ "node": ">=10" } }, - "node_modules/jake/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jdenticon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jdenticon/-/jdenticon-3.2.0.tgz", + "integrity": "sha512-z6Iq3fTODUMSOiR2nNYrqigS6Y0GvdXfyQWrUby7htDHvX7GNEwaWR4hcaL+FmhEgBe08Xkup/BKxXQhDJByPA==", "dependencies": { - "color-convert": "^2.0.1" + "canvas-renderer": "~2.2.0" }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jake/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jake/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jake/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jake/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jdenticon": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jdenticon/-/jdenticon-3.2.0.tgz", - "integrity": "sha512-z6Iq3fTODUMSOiR2nNYrqigS6Y0GvdXfyQWrUby7htDHvX7GNEwaWR4hcaL+FmhEgBe08Xkup/BKxXQhDJByPA==", - "dependencies": { - "canvas-renderer": "~2.2.0" - }, - "bin": { - "jdenticon": "bin/jdenticon.js" + "bin": { + "jdenticon": "bin/jdenticon.js" }, "engines": { "node": ">=6.4.0" @@ -10270,70 +9745,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-cli": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", @@ -10367,70 +9778,6 @@ } } }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-config": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", @@ -10473,259 +9820,67 @@ } } }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", "dependencies": { - "color-convert": "^2.0.1" + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "detect-newline": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-config/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", "dependencies": { - "color-name": "~1.1.4" + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", "dependencies": { - "has-flag": "^4.0.0" + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-diff": { + "node_modules/jest-environment-node": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-each/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", "dependencies": { "@jest/environment": "^27.5.1", "@jest/fake-timers": "^27.5.1", @@ -10798,70 +9953,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-jasmine2/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-jasmine2/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-jasmine2/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-jasmine2/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-leak-detector": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", @@ -10882,157 +9973,29 @@ "chalk": "^4.0.0", "jest-diff": "^27.5.1", "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-matcher-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "pretty-format": "^27.5.1" + }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", "dependencies": { - "has-flag": "^4.0.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, "node_modules/jest-mock": { @@ -11104,70 +10067,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-resolve/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-runner": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", @@ -11199,70 +10098,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-runner/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-runtime": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", @@ -11295,70 +10130,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-runtime/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-serializer": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", @@ -11397,74 +10168,10 @@ "jest-util": "^27.5.1", "natural-compare": "^1.4.0", "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-snapshot/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" + "semver": "^7.3.2" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, "node_modules/jest-util": { @@ -11483,70 +10190,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-validate": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", @@ -11563,70 +10206,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-validate/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-watch-typeahead": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", @@ -11702,58 +10281,24 @@ } }, "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.23.tgz", - "integrity": "sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/jest-watch-typeahead/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-watch-typeahead/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "node_modules/jest-watch-typeahead/node_modules/emittery": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", @@ -11765,14 +10310,6 @@ "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "node_modules/jest-watch-typeahead/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", @@ -11879,22 +10416,6 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/jest-watch-typeahead/node_modules/slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -11930,9 +10451,9 @@ } }, "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -11943,107 +10464,32 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "dependencies": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-watcher/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/jest-watcher/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", "dependencies": { - "has-flag": "^4.0.0" + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, "node_modules/jest-worker": { @@ -12059,14 +10505,6 @@ "node": ">= 10.13.0" } }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -12081,13 +10519,12 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" + "node_modules/jiti": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", + "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "bin": { + "jiti": "bin/jiti.js" } }, "node_modules/js-sha256": { @@ -12509,11 +10946,11 @@ } }, "node_modules/memfs": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", - "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dependencies": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" }, "engines": { "node": ">= 4.0.0" @@ -12604,9 +11041,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.5.tgz", - "integrity": "sha512-9HaR++0mlgom81s95vvNjxkg52n2b5s//3ZTI1EtzFb98awsLSivs2LMsVqnQ3ay0PVhqWcGNyDaTE961FOcjQ==", + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", + "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", "dependencies": { "schema-utils": "^4.0.0" }, @@ -12653,14 +11090,14 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.1.0.tgz", + "integrity": "sha512-Jw+GZVbP5IggB2WAn6UHI02LBwGmsIeYN/lNbSMZyDziQ7jmtAUrqKqDja+W89YHVs+KL/3IkIMltAklqB1vAw==", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 12.13.0" @@ -12722,10 +11159,26 @@ "multicast-dns": "cli.js" } }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -12779,9 +11232,9 @@ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==" + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -12833,9 +11286,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==" + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", + "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==" }, "node_modules/object-assign": { "version": "4.1.1", @@ -12939,14 +11392,15 @@ } }, "node_modules/object.getownpropertydescriptors": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", - "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz", + "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==", "dependencies": { "array.prototype.reduce": "^1.0.5", "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "safe-array-concat": "^1.0.0" }, "engines": { "node": ">= 0.8" @@ -13391,9 +11845,9 @@ } }, "node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", "funding": [ { "type": "opencollective", @@ -13402,10 +11856,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -13782,16 +12240,16 @@ } }, "node_modules/postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "engines": { - "node": ">=10.0.0" + "node": ">=14.0.0" }, "peerDependencies": { "postcss": "^8.0.0" @@ -13843,15 +12301,15 @@ } }, "node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", "dependencies": { "lilconfig": "^2.0.5", - "yaml": "^1.10.2" + "yaml": "^2.1.1" }, "engines": { - "node": ">= 10" + "node": ">= 14" }, "funding": { "type": "opencollective", @@ -13870,6 +12328,14 @@ } } }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "engines": { + "node": ">= 14" + } + }, "node_modules/postcss-loader": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", @@ -14017,9 +12483,9 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", @@ -14061,11 +12527,11 @@ } }, "node_modules/postcss-nested": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", - "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", "dependencies": { - "postcss-selector-parser": "^6.0.10" + "postcss-selector-parser": "^6.0.11" }, "engines": { "node": ">=12.0" @@ -14459,9 +12925,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", - "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -14609,6 +13075,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, "node_modules/prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", @@ -14758,17 +13229,6 @@ } ] }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -14887,70 +13347,6 @@ "node": ">=14" } }, - "node_modules/react-dev-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/react-dev-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/react-dev-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/react-dev-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/react-dev-utils/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/react-dev-utils/node_modules/loader-utils": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", @@ -14959,17 +13355,6 @@ "node": ">= 12.13.0" } }, - "node_modules/react-dev-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -14988,9 +13373,9 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/react-refresh": { "version": "0.11.0", @@ -15001,11 +13386,11 @@ } }, "node_modules/react-router": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.10.0.tgz", - "integrity": "sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.12.1.tgz", + "integrity": "sha512-evd/GrKJOeOypD0JB9e1r7pQh2gWCsTbUfq059Wm1AFT/K2MNZuDo19lFtAgIhlBrp0MmpgpqtvZC7LPAs7vSw==", "dependencies": { - "@remix-run/router": "1.5.0" + "@remix-run/router": "1.6.3" }, "engines": { "node": ">=14" @@ -15015,12 +13400,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.10.0.tgz", - "integrity": "sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.12.1.tgz", + "integrity": "sha512-POIZN9UDKWwEDga054LvYr2KnK8V+0HR4Ny4Bwv8V7/FZCPxJgsCjYxXGxqxzHs7VBxMKZfgvtKhafuJkJSPGA==", "dependencies": { - "@remix-run/router": "1.5.0", - "react-router": "6.10.0" + "@remix-run/router": "1.6.3", + "react-router": "6.12.1" }, "engines": { "node": ">=14" @@ -15244,13 +13629,13 @@ "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -15353,11 +13738,11 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -15379,7 +13764,7 @@ "node": ">=8" } }, - "node_modules/resolve-from": { + "node_modules/resolve-cwd/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", @@ -15387,6 +13772,14 @@ "node": ">=8" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, "node_modules/resolve-url-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", @@ -15532,21 +13925,6 @@ "node": ">=0.4.0" } }, - "node_modules/rewire/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/rewire/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -15556,52 +13934,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/rewire/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/rewire/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/rewire/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/rewire/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/rewire/node_modules/eslint": { "version": "7.32.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", @@ -15740,15 +14072,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rewire/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/rewire/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -15764,23 +14087,11 @@ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/rewire/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">=8" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, "node_modules/rewire/node_modules/type-fest": { @@ -15838,14 +14149,6 @@ "rollup": "^2.0.0" } }, - "node_modules/rollup-plugin-terser/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/rollup-plugin-terser/node_modules/jest-worker": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", @@ -15867,17 +14170,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/rollup-plugin-terser/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -15900,6 +14192,23 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -16004,9 +14313,9 @@ } }, "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.2.0.tgz", + "integrity": "sha512-0zTyLGyDJYd/MBxG1AhJkKa6fpEBds4OQO2ut0w7OYG+ZGhGea09lijvzsqegYSik88zc7cUtIlnnO+/BvD6gQ==", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -16037,9 +14346,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -16224,9 +14533,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz", - "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -16279,39 +14588,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -16336,11 +14612,11 @@ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" }, "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, "node_modules/source-map-js": { @@ -16659,9 +14935,9 @@ } }, "node_modules/style-loader": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.2.tgz", - "integrity": "sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", + "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==", "engines": { "node": ">= 12.13.0" }, @@ -16689,42 +14965,59 @@ } }, "node_modules/stylis": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz", - "integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/sucrase": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", + "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", "dependencies": { - "has-flag": "^3.0.0" + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/sucrase/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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": ">=8" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { + "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -16735,6 +15028,18 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -16778,6 +15083,17 @@ "node": ">=4.0.0" } }, + "node_modules/svgo/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/svgo/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -16786,6 +15102,32 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/svgo/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/svgo/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, "node_modules/svgo/node_modules/css-select": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", @@ -16831,6 +15173,22 @@ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" }, + "node_modules/svgo/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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svgo/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, "node_modules/svgo/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -16851,6 +15209,17 @@ "boolbase": "~1.0.0" } }, + "node_modules/svgo/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -16895,50 +15264,42 @@ "dev": true }, "node_modules/tailwindcss": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.7.tgz", - "integrity": "sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", + "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", "dependencies": { + "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", - "color-name": "^1.1.4", - "detective": "^5.2.1", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.12", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "lilconfig": "^2.0.6", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.0.9", - "postcss-import": "^14.1.0", - "postcss-js": "^4.0.0", - "postcss-load-config": "^3.1.4", - "postcss-nested": "6.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0", - "quick-lru": "^5.1.1", - "resolve": "^1.22.1" + "resolve": "^1.22.2", + "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" }, "engines": { - "node": ">=12.13.0" - }, - "peerDependencies": { - "postcss": "^8.0.9" + "node": ">=14.0.0" } }, - "node_modules/tailwindcss/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -16999,12 +15360,12 @@ } }, "node_modules/terser": { - "version": "5.16.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.6.tgz", - "integrity": "sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==", + "version": "5.17.7", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz", + "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==", "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -17016,15 +15377,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", - "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", - "terser": "^5.16.5" + "terser": "^5.16.8" }, "engines": { "node": ">= 10.13.0" @@ -17071,6 +15432,25 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/throat": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", @@ -17114,9 +15494,9 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -17151,6 +15531,11 @@ "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -17182,9 +15567,9 @@ } }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -17372,9 +15757,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "funding": [ { "type": "opencollective", @@ -17383,6 +15768,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -17390,7 +15779,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -17472,6 +15861,14 @@ "node": ">=10.12.0" } }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -17542,21 +15939,21 @@ } }, "node_modules/webpack": { - "version": "5.76.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz", - "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==", + "version": "5.86.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.86.0.tgz", + "integrity": "sha512-3BOvworZ8SO/D4GVP+GoRC3fVeg5MO4vzmq8TJJEkdmopxyazGDxN8ClqN12uzrZW9Tv8EED8v5VSb6Sqyi0pg==", "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.14.1", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -17565,9 +15962,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.1.2", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -17641,14 +16038,14 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.1.0.tgz", + "integrity": "sha512-Jw+GZVbP5IggB2WAn6UHI02LBwGmsIeYN/lNbSMZyDziQ7jmtAUrqKqDja+W89YHVs+KL/3IkIMltAklqB1vAw==", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 12.13.0" @@ -17659,9 +16056,9 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.13.1.tgz", - "integrity": "sha512-5tWg00bnWbYgkN+pd5yISQKDejRBYGEw15RaEEslH+zdbNDxxaZvEAO2WulaSaFKb5n3YG8JXsGaDsut1D0xdA==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz", + "integrity": "sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ==", "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -17748,14 +16145,14 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.1.0.tgz", + "integrity": "sha512-Jw+GZVbP5IggB2WAn6UHI02LBwGmsIeYN/lNbSMZyDziQ7jmtAUrqKqDja+W89YHVs+KL/3IkIMltAklqB1vAw==", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 12.13.0" @@ -17828,11 +16225,6 @@ "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" - }, "node_modules/webpack/node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -17987,26 +16379,26 @@ } }, "node_modules/workbox-background-sync": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", - "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.6.0.tgz", + "integrity": "sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==", "dependencies": { "idb": "^7.0.1", - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-broadcast-update": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", - "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.6.0.tgz", + "integrity": "sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-build": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", - "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.6.0.tgz", + "integrity": "sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==", "dependencies": { "@apideck/better-ajv-errors": "^0.3.1", "@babel/core": "^7.11.1", @@ -18030,21 +16422,21 @@ "strip-comments": "^2.0.1", "tempy": "^0.6.0", "upath": "^1.2.0", - "workbox-background-sync": "6.5.4", - "workbox-broadcast-update": "6.5.4", - "workbox-cacheable-response": "6.5.4", - "workbox-core": "6.5.4", - "workbox-expiration": "6.5.4", - "workbox-google-analytics": "6.5.4", - "workbox-navigation-preload": "6.5.4", - "workbox-precaching": "6.5.4", - "workbox-range-requests": "6.5.4", - "workbox-recipes": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4", - "workbox-streams": "6.5.4", - "workbox-sw": "6.5.4", - "workbox-window": "6.5.4" + "workbox-background-sync": "6.6.0", + "workbox-broadcast-update": "6.6.0", + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-google-analytics": "6.6.0", + "workbox-navigation-preload": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-range-requests": "6.6.0", + "workbox-recipes": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0", + "workbox-streams": "6.6.0", + "workbox-sw": "6.6.0", + "workbox-window": "6.6.0" }, "engines": { "node": ">=10.0.0" @@ -18135,117 +16527,118 @@ } }, "node_modules/workbox-cacheable-response": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", - "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.6.0.tgz", + "integrity": "sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==", + "deprecated": "workbox-background-sync@6.6.0", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-core": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", - "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==" + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.6.0.tgz", + "integrity": "sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==" }, "node_modules/workbox-expiration": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", - "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.6.0.tgz", + "integrity": "sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==", "dependencies": { "idb": "^7.0.1", - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-google-analytics": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", - "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.6.0.tgz", + "integrity": "sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==", "dependencies": { - "workbox-background-sync": "6.5.4", - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" + "workbox-background-sync": "6.6.0", + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" } }, "node_modules/workbox-navigation-preload": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", - "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.6.0.tgz", + "integrity": "sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-precaching": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", - "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.6.0.tgz", + "integrity": "sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==", "dependencies": { - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" } }, "node_modules/workbox-range-requests": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", - "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.6.0.tgz", + "integrity": "sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-recipes": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", - "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.6.0.tgz", + "integrity": "sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==", "dependencies": { - "workbox-cacheable-response": "6.5.4", - "workbox-core": "6.5.4", - "workbox-expiration": "6.5.4", - "workbox-precaching": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" } }, "node_modules/workbox-routing": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", - "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.6.0.tgz", + "integrity": "sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-strategies": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", - "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.6.0.tgz", + "integrity": "sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==", "dependencies": { - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/workbox-streams": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", - "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.6.0.tgz", + "integrity": "sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==", "dependencies": { - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4" + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0" } }, "node_modules/workbox-sw": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", - "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==" + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.6.0.tgz", + "integrity": "sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==" }, "node_modules/workbox-webpack-plugin": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz", - "integrity": "sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.0.tgz", + "integrity": "sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==", "dependencies": { "fast-json-stable-stringify": "^2.1.0", "pretty-bytes": "^5.4.1", "upath": "^1.2.0", "webpack-sources": "^1.4.3", - "workbox-build": "6.5.4" + "workbox-build": "6.6.0" }, "engines": { "node": ">=10.0.0" @@ -18272,12 +16665,12 @@ } }, "node_modules/workbox-window": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", - "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.6.0.tgz", + "integrity": "sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==", "dependencies": { "@types/trusted-types": "^2.0.2", - "workbox-core": "6.5.4" + "workbox-core": "6.6.0" } }, "node_modules/wrap-ansi": { @@ -18296,36 +16689,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/pkg/webui/ui/package.json b/pkg/webui/ui/package.json index 610d3ae1a..22e326feb 100644 --- a/pkg/webui/ui/package.json +++ b/pkg/webui/ui/package.json @@ -5,19 +5,20 @@ "private": true, "proxy": "http://localhost:8080", "dependencies": { - "@emotion/react": "^11.10.6", - "@emotion/styled": "^11.10.6", - "@fontsource-variable/nunito": "^5.0.2", - "@mui/icons-material": "^5.11.11", + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@fontsource-variable/nunito": "^5.0.3", + "@mui/icons-material": "^5.11.16", "@mui/lab": "^5.0.0-alpha.124", - "@mui/material": "^5.11.14", + "@mui/material": "^5.13.4", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", - "@types/node": "^16.18.18", - "@types/react": "^18.0.28", - "@types/react-dom": "^18.0.11", + "@types/node": "^16.18.34", + "@types/react": "^18.2.9", + "@types/react-dom": "^18.2.4", "jdenticon": "^3.2.0", "js-sha256": "^0.9.0", "js-yaml": "^4.1.0", @@ -27,7 +28,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-router": "^6.10.0", - "react-router-dom": "^6.10.0", + "react-router-dom": "^6.12.1", "react-scripts": "5.0.1", "react-syntax-highlighter": "^15.5.0", "sort-by": "^1.2.0", @@ -61,8 +62,8 @@ }, "devDependencies": { "@types/js-yaml": "^4.0.5", - "@types/lodash": "^4.14.192", - "@types/react-syntax-highlighter": "^15.5.6", + "@types/lodash": "^4.14.195", + "@types/react-syntax-highlighter": "^15.5.7", "rewire": "^6.0.0" } } From 46911515d786f7a27b409e9c50d224bbde59805e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Jun 2023 14:28:22 +0200 Subject: [PATCH 1158/2268] fix: Fix all build warnings --- pkg/webui/ui/src/api.tsx | 6 +-- pkg/webui/ui/src/components/App.tsx | 46 +++++++++---------- pkg/webui/ui/src/components/ObjectYaml.tsx | 9 ++-- .../result-view/CommandResultTree.tsx | 4 +- .../components/targets-view/TargetsView.tsx | 3 +- 5 files changed, 30 insertions(+), 38 deletions(-) diff --git a/pkg/webui/ui/src/api.tsx b/pkg/webui/ui/src/api.tsx index 150c4223f..4145b0f5f 100644 --- a/pkg/webui/ui/src/api.tsx +++ b/pkg/webui/ui/src/api.tsx @@ -222,10 +222,10 @@ class StaticApi implements Api { await loadScript(staticPath + "/summaries.js") staticSummaries.forEach(rs => { - if (filterProject && filterProject != rs.project.normalizedGitUrl) { + if (filterProject && filterProject !== rs.project.normalizedGitUrl) { return } - if (filterSubDir && filterSubDir != rs.project.subDir) { + if (filterSubDir && filterSubDir !== rs.project.subDir) { return } handle({ @@ -244,7 +244,7 @@ class StaticApi implements Api { async getResultSummary(resultId: string): Promise { await loadScript(staticPath + "/summaries.js") return staticSummaries.filter(s => { - return s.id == resultId + return s.id === resultId }).at(0) } async getResultObject(resultId: string, ref: ObjectRef, objectType: string): Promise { diff --git a/pkg/webui/ui/src/components/App.tsx b/pkg/webui/ui/src/components/App.tsx index e41af9bd3..e4f6fa347 100644 --- a/pkg/webui/ui/src/components/App.tsx +++ b/pkg/webui/ui/src/components/App.tsx @@ -1,4 +1,4 @@ -import React, { createContext, Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react'; +import React, { createContext, Dispatch, SetStateAction, useEffect, useMemo, useRef, useState } from 'react'; import '../index.css'; import { Box, ThemeProvider } from "@mui/material"; @@ -30,32 +30,27 @@ export const AppContext = createContext({ }); const App = () => { - let [summaries, setSummaries] = useState>(new Map()) - let [validateResults, setValidateResults] = useState>(new Map()) const [filters, setFilters] = useState() - const updateSummary = (rs: CommandResultSummary) => { - console.log("update_summary", rs.id, rs.commandInfo.startTime) - summaries.set(rs.id, rs) - summaries = new Map(summaries) - setSummaries(summaries) - } + const summaries = useRef>(new Map()) + const validateResults = useRef>(new Map()) - const deleteSummary = (id: string) => { - console.log("delete_summary", id) - summaries.delete(id) - summaries = new Map(summaries) - setSummaries(summaries) - } + useEffect(() => { + const updateSummary = (rs: CommandResultSummary) => { + console.log("update_summary", rs.id, rs.commandInfo.startTime) + summaries.current.set(rs.id, rs) + } - const updateValidateResult = (key: ProjectTargetKey, vr: ValidateResult) => { - console.log("validate_result", key) - validateResults.set(JSON.stringify(key), vr) - validateResults = new Map(validateResults) - setValidateResults(validateResults) - } + const deleteSummary = (id: string) => { + console.log("delete_summary", id) + summaries.current.delete(id) + } + + const updateValidateResult = (key: ProjectTargetKey, vr: ValidateResult) => { + console.log("validate_result", key) + validateResults.current.set(JSON.stringify(key), vr) + } - useEffect(() => { console.log("starting listenResults") const cancel = api.listenUpdates(undefined, undefined, msg => { switch(msg.type) { @@ -77,13 +72,14 @@ const App = () => { }, []) const projects = useMemo(() => { - return buildProjectSummaries(summaries, validateResults) + console.log("buildProjectSummaries") + return buildProjectSummaries(summaries.current, validateResults.current) }, [summaries, validateResults]) const appContext = { - summaries: summaries, + summaries: summaries.current, projects: projects, - validateResults: validateResults, + validateResults: validateResults.current, } const outletContext: AppOutletContext = { diff --git a/pkg/webui/ui/src/components/ObjectYaml.tsx b/pkg/webui/ui/src/components/ObjectYaml.tsx index cf4c9ebc1..80a71a2b8 100644 --- a/pkg/webui/ui/src/components/ObjectYaml.tsx +++ b/pkg/webui/ui/src/components/ObjectYaml.tsx @@ -10,12 +10,11 @@ import { Loading } from "./Loading"; export const ObjectYaml = (props: {treeProps: CommandResultProps, objectRef: ObjectRef, objectType: ObjectType}) => { const [promise, setPromise] = useState>() - const getData = async () => { - const o = await api.getResultObject(props.treeProps.summary.id, props.objectRef, props.objectType) - return yaml.dump(o) - } - useEffect(() => { + const getData = async () => { + const o = await api.getResultObject(props.treeProps.summary.id, props.objectRef, props.objectType) + return yaml.dump(o) + } setPromise(getData()) }, [props.treeProps, props.objectRef, props.objectType]) diff --git a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx index eead2b7fa..396cd16fa 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultTree.tsx @@ -20,9 +20,8 @@ export interface CommandResultTreeProps { const CommandResultTree = (props: CommandResultTreeProps) => { const theme = useTheme(); const [expanded, setExpanded] = useState(["root"]); - const [selectedNodeId, setSelectedNodeId] = useState() - const [rootNode, nodeMap] = useMemo(() => { + const [rootNode] = useMemo(() => { if (!props.commandResultProps) { return [undefined, undefined] } @@ -45,7 +44,6 @@ const CommandResultTree = (props: CommandResultTreeProps) => { }; const handleItemClick = (e: React.SyntheticEvent, node: NodeData) => { - setSelectedNodeId(node.id); props.onSelectNode(node); e.stopPropagation(); } diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index ef04d8c9c..7cb5a8fd9 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -1,7 +1,7 @@ import { CommandResultSummary } from "../../models"; import { Box, Typography, useTheme } from "@mui/material"; import React, { useCallback, useContext, useState } from "react"; -import { AppContext, useAppOutletContext } from "../App"; +import { AppContext } from "../App"; import { ProjectItem } from "./Projects"; import { TargetItem } from "./Targets"; import Divider from "@mui/material/Divider"; @@ -123,7 +123,6 @@ const RelationTree = React.memo(({ targetCount }: { targetCount: number }): JSX. export const TargetsView = () => { const theme = useTheme(); - const context = useAppOutletContext() const [selectedCommandResult, setSelectedCommandResult] = useState<{rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary} | undefined>(); const [selectedTargetSummary, setSelectedTargetSummary] = useState(); const [selectedCardRect, setSelectedCardRect] = useState(); From 4a493e2026ab33abee5f697821f7e9c0815437fb Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 9 Jun 2023 11:37:39 +0200 Subject: [PATCH 1159/2268] chore: Add current/initial version of built webui --- pkg/webui/ui/.gitignore | 6 +- pkg/webui/ui/build.js | 2 + pkg/webui/ui/build/.gitignore | 1 + pkg/webui/ui/build/asset-manifest.json | 55 + pkg/webui/ui/build/favicon.ico | Bin 0 -> 15406 bytes pkg/webui/ui/build/index.html | 1 + pkg/webui/ui/build/logo192.png | Bin 0 -> 13703 bytes pkg/webui/ui/build/logo512.png | Bin 0 -> 26403 bytes pkg/webui/ui/build/manifest.json | 25 + pkg/webui/ui/build/robots.txt | 3 + pkg/webui/ui/build/static/css/main.css | 87 + pkg/webui/ui/build/static/js/787.chunk.js | 239 + pkg/webui/ui/build/static/js/main.js | 60971 ++++++++++++++++ pkg/webui/ui/build/static/media/added.svg | 4 + .../ui/build/static/media/arrow-left.svg | 4 + .../ui/build/static/media/brackets-curly.svg | 3 + .../ui/build/static/media/brackets-square.svg | 3 + pkg/webui/ui/build/static/media/changed.svg | 5 + pkg/webui/ui/build/static/media/changes.svg | 5 + .../build/static/media/checkbox-checked.svg | 5 + .../build/static/media/checkbox-disabled.svg | 4 + pkg/webui/ui/build/static/media/checkbox.svg | 3 + pkg/webui/ui/build/static/media/close.svg | 3 + pkg/webui/ui/build/static/media/cpu.svg | 16 + pkg/webui/ui/build/static/media/deploy.svg | 8 + pkg/webui/ui/build/static/media/diff.svg | 8 + pkg/webui/ui/build/static/media/error.svg | 4 + pkg/webui/ui/build/static/media/file.svg | 5 + .../ui/build/static/media/finger-scan.svg | 8 + pkg/webui/ui/build/static/media/git.svg | 1 + pkg/webui/ui/build/static/media/include.svg | 7 + .../ui/build/static/media/kluctl-logo.svg | 3 + .../ui/build/static/media/kluctl-text.svg | 7 + .../nunito-cyrillic-ext-wght-normal.woff2 | Bin 0 -> 28960 bytes .../media/nunito-cyrillic-wght-normal.woff2 | Bin 0 -> 20824 bytes .../media/nunito-latin-ext-wght-normal.woff2 | Bin 0 -> 32720 bytes .../media/nunito-latin-wght-normal.woff2 | Bin 0 -> 35904 bytes .../media/nunito-vietnamese-wght-normal.woff2 | Bin 0 -> 10632 bytes pkg/webui/ui/build/static/media/orphan.svg | 6 + pkg/webui/ui/build/static/media/project.svg | 5 + pkg/webui/ui/build/static/media/prune.svg | 8 + .../ui/build/static/media/relation-hline.svg | 5 + pkg/webui/ui/build/static/media/result.svg | 8 + pkg/webui/ui/build/static/media/search.svg | 4 + pkg/webui/ui/build/static/media/star.svg | 4 + pkg/webui/ui/build/static/media/target.svg | 5 + pkg/webui/ui/build/static/media/targets.svg | 6 + pkg/webui/ui/build/static/media/trash.svg | 6 + pkg/webui/ui/build/static/media/tree-view.svg | 7 + .../ui/build/static/media/triangle-down.svg | 4 + .../ui/build/static/media/triangle-right.svg | 4 + .../ui/build/static/media/warning-sign.svg | 5 + pkg/webui/ui/build/static/media/warning.svg | 5 + pkg/webui/ui/build/staticbuild.js | 1 + 54 files changed, 61576 insertions(+), 3 deletions(-) create mode 100644 pkg/webui/ui/build/.gitignore create mode 100644 pkg/webui/ui/build/asset-manifest.json create mode 100644 pkg/webui/ui/build/favicon.ico create mode 100644 pkg/webui/ui/build/index.html create mode 100644 pkg/webui/ui/build/logo192.png create mode 100644 pkg/webui/ui/build/logo512.png create mode 100644 pkg/webui/ui/build/manifest.json create mode 100644 pkg/webui/ui/build/robots.txt create mode 100644 pkg/webui/ui/build/static/css/main.css create mode 100644 pkg/webui/ui/build/static/js/787.chunk.js create mode 100644 pkg/webui/ui/build/static/js/main.js create mode 100644 pkg/webui/ui/build/static/media/added.svg create mode 100644 pkg/webui/ui/build/static/media/arrow-left.svg create mode 100644 pkg/webui/ui/build/static/media/brackets-curly.svg create mode 100644 pkg/webui/ui/build/static/media/brackets-square.svg create mode 100644 pkg/webui/ui/build/static/media/changed.svg create mode 100644 pkg/webui/ui/build/static/media/changes.svg create mode 100644 pkg/webui/ui/build/static/media/checkbox-checked.svg create mode 100644 pkg/webui/ui/build/static/media/checkbox-disabled.svg create mode 100644 pkg/webui/ui/build/static/media/checkbox.svg create mode 100644 pkg/webui/ui/build/static/media/close.svg create mode 100644 pkg/webui/ui/build/static/media/cpu.svg create mode 100644 pkg/webui/ui/build/static/media/deploy.svg create mode 100644 pkg/webui/ui/build/static/media/diff.svg create mode 100644 pkg/webui/ui/build/static/media/error.svg create mode 100644 pkg/webui/ui/build/static/media/file.svg create mode 100644 pkg/webui/ui/build/static/media/finger-scan.svg create mode 100644 pkg/webui/ui/build/static/media/git.svg create mode 100644 pkg/webui/ui/build/static/media/include.svg create mode 100644 pkg/webui/ui/build/static/media/kluctl-logo.svg create mode 100644 pkg/webui/ui/build/static/media/kluctl-text.svg create mode 100644 pkg/webui/ui/build/static/media/nunito-cyrillic-ext-wght-normal.woff2 create mode 100644 pkg/webui/ui/build/static/media/nunito-cyrillic-wght-normal.woff2 create mode 100644 pkg/webui/ui/build/static/media/nunito-latin-ext-wght-normal.woff2 create mode 100644 pkg/webui/ui/build/static/media/nunito-latin-wght-normal.woff2 create mode 100644 pkg/webui/ui/build/static/media/nunito-vietnamese-wght-normal.woff2 create mode 100644 pkg/webui/ui/build/static/media/orphan.svg create mode 100644 pkg/webui/ui/build/static/media/project.svg create mode 100644 pkg/webui/ui/build/static/media/prune.svg create mode 100644 pkg/webui/ui/build/static/media/relation-hline.svg create mode 100644 pkg/webui/ui/build/static/media/result.svg create mode 100644 pkg/webui/ui/build/static/media/search.svg create mode 100644 pkg/webui/ui/build/static/media/star.svg create mode 100644 pkg/webui/ui/build/static/media/target.svg create mode 100644 pkg/webui/ui/build/static/media/targets.svg create mode 100644 pkg/webui/ui/build/static/media/trash.svg create mode 100644 pkg/webui/ui/build/static/media/tree-view.svg create mode 100644 pkg/webui/ui/build/static/media/triangle-down.svg create mode 100644 pkg/webui/ui/build/static/media/triangle-right.svg create mode 100644 pkg/webui/ui/build/static/media/warning-sign.svg create mode 100644 pkg/webui/ui/build/static/media/warning.svg create mode 100644 pkg/webui/ui/build/staticbuild.js diff --git a/pkg/webui/ui/.gitignore b/pkg/webui/ui/.gitignore index b2ba12633..f471c1a59 100644 --- a/pkg/webui/ui/.gitignore +++ b/pkg/webui/ui/.gitignore @@ -8,9 +8,6 @@ # testing /coverage -# production -/build - # misc .DS_Store .env.local @@ -22,4 +19,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +*.css.map +*.js.map + /public/staticdata diff --git a/pkg/webui/ui/build.js b/pkg/webui/ui/build.js index 0386a1a3c..17db0e576 100644 --- a/pkg/webui/ui/build.js +++ b/pkg/webui/ui/build.js @@ -2,4 +2,6 @@ const rewire = require('rewire'); const defaults = rewire('react-scripts/scripts/build.js'); const config = defaults.__get__('config'); +// we disable minimize as we're actually committing the build folder into git, so that people who don't want to deal +// with the UI while working on Go code don't have to use npm and friends config.optimization.minimize = false diff --git a/pkg/webui/ui/build/.gitignore b/pkg/webui/ui/build/.gitignore new file mode 100644 index 000000000..c9e9f1beb --- /dev/null +++ b/pkg/webui/ui/build/.gitignore @@ -0,0 +1 @@ +staticdata \ No newline at end of file diff --git a/pkg/webui/ui/build/asset-manifest.json b/pkg/webui/ui/build/asset-manifest.json new file mode 100644 index 000000000..351d6bcf7 --- /dev/null +++ b/pkg/webui/ui/build/asset-manifest.json @@ -0,0 +1,55 @@ +{ + "files": { + "787.chunk.js": "static/js/787.chunk.js", + "787.chunk.js.map": "static/js/787.chunk.js.map", + "index.html": "index.html", + "main.css": "static/css/main.css", + "main.css.map": "static/css/main.css.map", + "main.js": "static/js/main.js", + "main.js.map": "static/js/main.js.map", + "static/media/added.svg": "static/media/added.svg", + "static/media/arrow-left.svg": "static/media/arrow-left.svg", + "static/media/brackets-curly.svg": "static/media/brackets-curly.svg", + "static/media/brackets-square.svg": "static/media/brackets-square.svg", + "static/media/changed.svg": "static/media/changed.svg", + "static/media/changes.svg": "static/media/changes.svg", + "static/media/checkbox-checked.svg": "static/media/checkbox-checked.svg", + "static/media/checkbox-disabled.svg": "static/media/checkbox-disabled.svg", + "static/media/checkbox.svg": "static/media/checkbox.svg", + "static/media/close.svg": "static/media/close.svg", + "static/media/cpu.svg": "static/media/cpu.svg", + "static/media/deploy.svg": "static/media/deploy.svg", + "static/media/diff.svg": "static/media/diff.svg", + "static/media/error.svg": "static/media/error.svg", + "static/media/file.svg": "static/media/file.svg", + "static/media/finger-scan.svg": "static/media/finger-scan.svg", + "static/media/git.svg": "static/media/git.svg", + "static/media/include.svg": "static/media/include.svg", + "static/media/kluctl-logo.svg": "static/media/kluctl-logo.svg", + "static/media/kluctl-text.svg": "static/media/kluctl-text.svg", + "static/media/nunito-cyrillic-ext-wght-normal.woff2": "static/media/nunito-cyrillic-ext-wght-normal.woff2", + "static/media/nunito-cyrillic-wght-normal.woff2": "static/media/nunito-cyrillic-wght-normal.woff2", + "static/media/nunito-latin-ext-wght-normal.woff2": "static/media/nunito-latin-ext-wght-normal.woff2", + "static/media/nunito-latin-wght-normal.woff2": "static/media/nunito-latin-wght-normal.woff2", + "static/media/nunito-vietnamese-wght-normal.woff2": "static/media/nunito-vietnamese-wght-normal.woff2", + "static/media/orphan.svg": "static/media/orphan.svg", + "static/media/project.svg": "static/media/project.svg", + "static/media/prune.svg": "static/media/prune.svg", + "static/media/relation-hline.svg": "static/media/relation-hline.svg", + "static/media/result.svg": "static/media/result.svg", + "static/media/search.svg": "static/media/search.svg", + "static/media/star.svg": "static/media/star.svg", + "static/media/target.svg": "static/media/target.svg", + "static/media/targets.svg": "static/media/targets.svg", + "static/media/trash.svg": "static/media/trash.svg", + "static/media/tree-view.svg": "static/media/tree-view.svg", + "static/media/triangle-down.svg": "static/media/triangle-down.svg", + "static/media/triangle-right.svg": "static/media/triangle-right.svg", + "static/media/warning-sign.svg": "static/media/warning-sign.svg", + "static/media/warning.svg": "static/media/warning.svg" + }, + "entrypoints": [ + "static/css/main.css", + "static/js/main.js" + ] +} \ No newline at end of file diff --git a/pkg/webui/ui/build/favicon.ico b/pkg/webui/ui/build/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..496bbc69cabfce158be2fbc7e709e8a81a5bc95e GIT binary patch literal 15406 zcmeHO3v^V)8D6vxTWwWZwYJ*gQCn+UYisN2sUD@R)x%@&y$Qy*0*Z(SJr#k_~K1sI8X6j^_Pc$c$$5q)hSqBj@C_1{Q@LcIGU`Zm8@bQIo zzk~u!KkI6i3>TnrqpY`Mdm0cH{5ED!1ok3XvY zW7L#BgJX;QCBv(4O7+QDKo8fdFm~@AkLM(R#N7+HyD*LmAlGiZ?+HY- zc^H?EeNm&YXF+FJYug{k6#Xs)?1m)Yk%WMrMqw<+B;NJnM4Pr5_Tcl>8Z>Y72i#J& zjhWh4JU^xP3;{i}0X$=T_|7`en?|_eNjHn}vj>i2J%7HiG2+>pyTts}6$ge+?Ece4 zE$|~+BX~8)Z}jQ>XKU^}Fnh&|A~b)<2w7h;v&*-^=MFrV$+|$5erZBpT?C?en3ahi zVF*hcS!Y)UqQ?Eiub4X+_{RNqFq!H+5u?-db-fzv>Yv->jk_<%1bZxP^fR6CbjCyf z)ZU*zYCMSkHp6BPKtGj5@#{`^qQy4AhA=*#(N_`I2{xB{)o;l$gA5TTT-Z@K1&1-h z_zKui4=4C+^_Z8{D!t_U=MR>o+X(#Bx6poAg0adXs{m4@{jy8b(8wMcrh^hUe=(gyZ2+0Js#n=gsj!on8Pu)-eA zcOb0w1id3F?LbJ&x6*(QxLLxCs=G(TUh}pD!uowyxb}SeBJMjuyHyWieYlkl+DA(M z$+Eu{=2*UA+ujuYu(sMt3+v0dJUo7jbWnpFa-(pUU z16(C}vK>78NZHLY%sH$Lby^3wCMDtaVQs(5w2iR7FG0fySb6fBw7enB0~k~G1T5kJ zlQ-5)^e6q%8hsJtPOB~pqwYVZ!zS>95$$&>f3E425&dbj#fj?V-Nt97!)co_kDj9L zO&bX5qtjs${1H!#B1~eOw4s1==$mfFNoYR=9n5l~XH$RBrp~H6TNuWvm9IW7%I6Fc zQ|q4-4O{9N8%_&iJqArPR!q_+KILXiw+YY3c&t2f`%c(J6Y!gc$8Xv?{*?iTYF;lE zQ{EUUiYMQ^YFJhNc^ogsK=i5JsBxjk<2p$lL%t8I)4o?&rCkMGt;{A1#$XlW% z+B(g#ArJDh<74G({l?_jg$Fj?*{e#PQkq!!MM%l5ys7;_LE6v>$`-^lZFS z?O|<>FO>hYW95&1%y+Oaco_ZOD`}&DlNr;XTqx^ME`krtV{DYFGvhVgmu+8WC!;@_ z_iOr8JRhd$nR7rJD`R-tkZj{#EJ>#WY_vqyn`M-~Q5#LaWyedc!y526;Bd~R%`dEP zRqD9X&r3MQ^a`C!cCyhMcK=m@O}#>Q=YZZPlBO)jPP-4XX!VTmQowkDkGodFSZS7) zUhz$|+3ocN)}MWzioCN`T6_omZIYfWm#mG8aWfv+>kD;Fvn4NL8wm%ReXR5_cITsg zPYMP)9Y1S;Sg^VhYp45GD?InVD(u2F5mCRsmg6wM3d?WU-%Apne%~I({g8xVIrpuX z#p1Ow5eS?6@ir&}XzLMuBjIXae@1NDzEX^=zJ0w_?-B`iCikTD#Ya=|?wFw`~omgRFE7H>B#5KGoh65fdBIUclgp}}?B7uAN1p4w;k=&ASaX1s5g zzsZHx|2howB;0WH!gDNn5}>#Fc`Mu(aExH5+hDJUkq=qmuhhDE%6pyC1~9`1VPRJq zXmH>4IqYsbXbmUr3~0yPl#Jh($vLK!pJI(3;^dQ6#tGkR48hv@nSw8U)>3b!_6rsM zc)iEDPYr8zHatzK-yW z?wc_FU*LHHbUI|9)d4x@#~@>yyb1dZg9c`w6gXTbN@!6G*dP%7pl5F7usE{1{)u2fU`r|IFlD#mbGdaaQGT zDT?-(X?N!2M6W&d>;qy{?#I4Rb${21o?6d50_JmTJ~OH}$Q&f$=o3Mk zc~8^!;b%XjA5&>Ky>m~EHT#hy{+5J{TBTg$$OrV)oRyqk^zk(uk6iXJIIDgD@Y1&& z#NPU84{66KyP0E8=6?;u+{Qgl^2&PhByXJM@~kgyy`jvM9p@6v-)CA=kz+T8Fuy8u zWRizkQr5P4T%46g^%dZ=0`w7ICv*?lm%+!{<{wCpJc4oUBH%p*zTlnaucUqraK;gMm+7#AP(Fcj^)yCP-GR<6NbXDD$=13qPEuDHYE1!k@=w$RI* zCG%nZbH#OlLmXt-#X?#7VB%!Ngq>|4b98N&OKY0KLxIKH*Fvp zk6HJloYV9f`_a#RfZ5N7dc^8DyHsHG%WSQ{MGQ!0C^%tU_6XwwE}!z2?{$y@JT=qW(7c;hrWO@>Iw< zjzXVm@%=vh40*RhX7ip?9enK=(*|)@34D8z4a;@cJ;0*|yeMOTtcT}N z_S6r4i+d0DiEBnCU&(ww?^>8^Vj|z(e6;dI&&^naI@53Q95xd_&ULP(dV`FuGH~v~mvWE7_#C-e?r9F-k-GnscaDZt z=by*e4D}(UjW8|1`v`ur+8^$bx|8T6N3QkMb)Y8i2k^C*fqpTb)OoSOpX*sUV;M=8 zevUd#!G!gxs*MyBAeS07&-tutCi`*_{31mW{m=CAr7QY6=JO;!Yf0bEJHF6a5HimO zuiYtGT6IcZhhRr9QeKtDAjGI>E0;LPSyk{3unJRDT5vkrN8@4tNz3vuKdmi>9)@GQ z-MQKp?__{hIdCX*TN|2c77G`7$%tjeBLm>9?IpJnN4(XO3G^F6=xRj{ zF$nGIpQ@dv@r5oS{{km{RJ{6_n7M2$`f$re*w>$|`10G_GvY2?D;;e$>twsN3?hdD zTlse@d>j3X=WBb3@B)v(S^7NQ*|*}$Z{7h${M2Yir)vI_$Zt1@QB&{X9oj*e_Qc*$ zkBgJ`m$VCQN3c@6Nb<3k)G6Y%f#`$Xd^UCV!R{u3xU(_S{T1deA-g3XrHj&;4~VV1 z-W4;KzW{lRWr(F5bF`H0=_+>@s}6X_-72^G?TO#e5p&g=-!kv|u56qmcJAFGo~`Yf zF~7=r2OEp2yyLhdK-stKSSOyV>4|ko>!$Lxd&k|Jwz(e8gT&d49eApaYZ%~;aE@`G z6^!NMuB=b&JFrX4Uo{alv{$&}t=>H0czpS5GpX58;66T>Inw2QjS6lY7K@Ev)k3}O7Q z1^g)N9eWg`q$G!Y8Sf}rW#>2i%aKfTSn?(h@&XR>Xa;@wPV{LVWs)$tl*Ki9XM3aF zuje|(`M-@rS4#uGAGvn zzGDpD{;FijRb~|q|Ci7O`bQj=u6yQeQ2&vzn5;E5tX&w~m> k-LAl1ECQML7Eiu)AL7+G?;m^__cBhDe*a&N|4t432jt`J!vFvP literal 0 HcmV?d00001 diff --git a/pkg/webui/ui/build/index.html b/pkg/webui/ui/build/index.html new file mode 100644 index 000000000..56667fb71 --- /dev/null +++ b/pkg/webui/ui/build/index.html @@ -0,0 +1 @@ +React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/logo192.png b/pkg/webui/ui/build/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..11f22322c00fe673d7aa1530ecfd53fbc82b2373 GIT binary patch literal 13703 zcmXY&V|3tLw1!h#Q@c}RYE3(}+o|oTt*LE$YTLGL+qP}@_Ph7~NY2X2TFFkQfeI4yI5rx8rLi^(Sp1sbHxjepRrplYlV8kRo&iEf>^m3#fc#ghbythAIJiJ0* z>$!qgq+BcBVwf31A&eY@uAqnJq`?n}A(sV(yTRNw=zPe@nKFXYR?bm7(X~u8FWMib zG=Z4yoU_+I_inoKbhj9P%0gHeZ+!#W{aBvhn0Zu!F-&Mn4idJIq3x$MWdH_-Vr* z!Ei6Y#99^_k!g1t{Pzr;-3$RGyO^)gYQSyDgR9F7CMq1#>oDS9#iNoG+T=x_3E%$kma_H-6hmO+$xl*)UM* z2hrhMjH05Jr{OmL5C~;rwYj1Z`dQu^B6jJ8($aXmS8F7P#!^e}jgy37vD^s9-0=*I zvVVqyWsLA4je@w^_ij8)dT@BUw^K4^iA#wH;{lNN_3PzO-_?Z3O?vrEjZr1(<%FtAqKfWUifaF~P@;i|3&Key|ro#mw}>!428ftwh~ z!R73g@Wsa{K0H7-GI$PD1qanAs2Gxa)m~tNOn;iE*brZd_j1jDkS8r-J^o`AByX#G zr#iAT(7csubPxntAR5st<2de7{8rHPNp#wcf9k} z`r>u#r@IakGd6wXM!=j74Ar5;MAqVDf7eERF!tn)$7`O# z`8E{^?2~7_fX-=5JT({lTvTBS6Uuu+n?QZfBC$x#7-&*242A5U;M_Lgax^tc-(%QHcgDK;(K**8=E`ZVj8crXt0mXmcN5r`9bjsC^g+$#J!=QoJL<>P>6}t#(i-bW zC!GB0j|k4sUvQ(1FHsKN>Yp`ZqMgiB?y+*{Um;dV(>cG{2h7ED_D4}A7*uaR_^52h zWEk^ZkT1rCe7b;bUOuhM@F05Y2iA7O{soT0)0cs0t9z4mlql_($cORBGX%|FuQ1MP zeIQx2o1pz)abORL0emeB?Mk9~nSW8F&E`SuR6Kn9I~U$(LOMb09rfOR)_rsd!vd@X z)I`uW?TCXQS)GKt)R$J2mPy@WOf&V+Nm9rVOPtu-4qx8 zzmwW;ugCO|>P+if0gl#=I(lZ)R$y90i~p`b2Xr5ZP8VNQt5zwHX5?LxrG&hsmG)2J zamZu}X9s;3U@(DSg1Z^;`?-}?!#{GWAXr37N!5Rhy2&~%5i5)D1%@>>o(p>`jV|C0 zo@2IwdOIo~WhUi8bDlkZkjmLX%K{6;jHZo$8?_t<6KwrMS9oUD>E;@Kz5a9o=|;=U z&q55^D~!xw*o8B>D_uDIsi+Aa9;DxMY)?(&y0W4klQp-XuwB06!K?GAS6I6caA@-b zf*zL+m`31Y#w7R;Ywl~^il+rrF4q!;LhPr3- z_YD4{Kc>uWo*`acNoK3Fk~JHMQ=ooG$N|+9!AV`=tZOY#m=p-#&FF-Y+3!tv2fyG=y9LZnD(>9IJ_#aN+eYtGqFi4nrXe3`c%7BF4P1f+H;UW-;`A0`x1 zUONOTjo|H>2bT{ewv8vD?*#zSe7Z(gmH|Ku55phvZNN%7t~0JcCXOpjPNiZZb7eFq zm{7|8EGPzkkkA{F4l>E)C)PYwfI_WUuoqqgj`qpFysq)G`Q>axSFSC6c4JgRSG z;)RJj>JpqLF8>jZ#H(KgOKtw~)hwzV zEnbr@{b1z)!L_=M`o+GTdtX}hlo;RP4pR@uOdY&cSDaydn8#7-8?iclf(_}7_BdAy zv*Q7L6wCNt-T2(b{JkD_;KknW^7<@9ANqU{RCInH&)tM3)I!b^OE2BKtR-Vw#r@~A z196g8OEw4}Tk`pd?_iCVjep+qBS;0&VK3v!zGe&9Zapiuv9OIOrG{>N(50+g?Z(g(*5 z_-u5|*cqpPva1J-iq+E(W13Puj{xZJ<9?A=iCEyq;;s#EZQ&^~F_$fIXm9LO*$Xh^ zQ^hX0WvkDxjD?Lc5Y{@0+E^>E7VsAfh0?3amahn2?{!$eKsUgJ+@1cqMoJ1{DZ}6b zB%5RB1uJn*0FPEB1%_}x#8=>r2raO*mJT!K&vO!?S8vuBxz6{8AEl7tWNcx^!E&4a zuB$exk;(F~T2ZKb->Mh`pnpN^zRbW63h1*0RtkH*W-iqX1`(wZcCck>$-{*cvcMl^ zIG1E{MnFiY&_fe1cssy;=vz${iH?~9@zM4*ku{AAyImIW(dgONWo zoUZw}91TqVm4fbyzGlNVhTMAq)}5Oe-al;lc1to;*lAadTk{#kWl|;MJ(y^C&?H!| z(CVoQL?=&Kx;+fTlQ{+K3Ry0L&xg{uWd?+QU@mSCCEja)zHrBt!6fN_>oino+datR zc#k#3-d7BEJ*!Q7&r*+Qr5;vQiut$Bs}6WNe9SeJ_J0 zmp|iuzPxaMb~1O$&KJysg^8>wP{eLABH))GV*oL585dcyhTpwM{aCRixtr-3n#>8g zY`0k*N=D&#V3flSST#8A1Q5LFwi7ddi$nqJi zSr|v{WU^^5!A-Y6CuI7Cx!(j;j_--=E@r2eF3CPSbYxuB3ThWhzl)KG07UsFCscW+ zzX7IwVCk9SzQ6sDoMd_@Q=!q|-q_i$9}2nNm)}2Lx!>+1jir!FY5l1G!f_ZT<-4`W zcZQB1qm1p-xDZ{9JB9dfcv&At&$EplDw6Fxki7Cbrsgk+ms{oao>2Z?kFfhWUkk}= z=Ja=xHDHGwvwtXS^ciO3uxUp`F~TZbfzbM);!o)GTsWXtU@XC0`zd415FYpY>zgI& zpDc2OcE(ZuW43t(T5J16%%PQ>c?(`;a(eFY0mYZPq$*9l`EyI5A@{`NsLKoIYtMF+;<6cygab=K${n{PBIQMwTWwE@u=~WeeL(QvY4qGTujk$QpQUy(Ll&VmWlMLcX@nS%Rt;z0 zuh#pqIRf21{de@&DGE*<0QSIKurc~Lg99w4MCie&J>ND(;qAo;%?FDi!enHFwPZnt z6P8cfz&}pb{q5GH=#M1*r`_0am5Va)7#IMtI0_y9`9cXdxh4ZF_X=LK9ufQCLD>rn zLqaQBPk+RC65B}n;kT_b8%Tn#V=`gjZ`W_7jS!q8_S3nCvnj*&V4t8Eu7(ZLk+Ja)9c*gwYbb(LI^&+bi9-7)4gGaoF}k5wFcdE-zV%q`Zg8J!899@yx4Sb!-(oBK^zVc} zX=xi(t(CnCyMM+M4x0$xADO2vfuerIg?i$*>U$|c9C&?VA9!AV78q}#GE`8F+dt7m z1#bOO1%mhbD66EHVq54#)`s6bjD<3^c59++dG;YpU7a7&$B6&cmv8(O-k~5Z1W=k!TTSx7MK#; z2oPHVLXhiVOE6X0E8f&HsZ%Fm5m?BZ9+kfTk=hOKsZe~iq2eCPoI})PHlQ$`sz&oU zoP~Sm@NK+ero`%xeL8c_?-oIAC-MgADoIGWIjv4EJkgwc0UeW{o1gM3OTg7SlvXQA zW|#h*Lk*+6?Rk^TDxFE{93J6k1E-E3e0-mcDy_!{hN*hIV&mv=mjQRVfsK`E%kH!o zp>J*LZ3TpC1$)E7u9J{K(ye6zI=lZUE*cwPpf5UZbC0P!L}NBc%o8eRal$UpS?gOj z16>m-&MUuOAr9{fzZI=d8-*8V^phaWU<$EZbC}(AQfvibdO=&6*6W9qo3m7~twTxB z8WjSzB$bU6sQ}&9a8`nBy*DP^9Iu(Y^QWS&O2 znD}3r(~_=NYOr1sJ=shY7ml}&82F$I+~E3g97w=LWGyFju}f@zu_9QUcDlq_vWY#! z_nvK@+c4N$K2IOj6ceo@+rT2y9Vboos500f^A*Z8S-FN@xFk=Gz!ahVuS&B z;t42WANW9EI>U2!&Nkyq;j=0&Su<5cjyl*IN>&Ig!fwv}A<2?K|0ABq?xawWy^t0r8KNE^^_fIcG-jb~c=hj{y&0 za;0J}%m590D8p7cfImyOWyqi!w#RaGY3JD-7#N~)#c!uGet7Sh3Ne`4oTvNno;4` z=WO7FIT;p4&q~kXx1Qd}WWNBv0w`glOvr8K1`G@y&kiyrDWyQMJ-T;J(CK+Mf{eg- zE6w&aKRC0z7Jy_8m`HTT>B7o!`iWq7<7G1^iK4D{b>SPyM;?Iz%30|t+gX{y8QCIo2ea^ zASx%^I}q=VTSopQ*@_IteU>n#e`V!6&@X(;f0~znTrD~>5ChQR z!S3 wqli$aKQ2SbKZFKg{E9g@WEz#e7APR8O^xSmtxq&tn!vCgCj&k^n$_Wq;zA zAM`Dv;VH~GnZEgN+f{tP6GI?QJ%CAnC)TK!g(l-IRitx%djDsAT3SP8(raAI-V-XJPO#E7uu)q zC5$n<^Wi}vP<#<2(vH+LMoTN9q}D1A)V(1`*&-=i){4-*;cZeF2-e!U<;2#`V;balnGP<`|Sh9?U$#nvTrMq z2ZvdG(bpSzWSX=I^9<1%KE*Fq^dfkU9l%0~;KTQi(2=GY9xApw<-xM+u)7awFaN$5>r(HwEOb^^?O96vocq-^A~aW7ganJsxg>my^}3eei%8 zyRcD;3X-a|zRxQ5k>SmA{5kCO)4khDq6fAfq+9E22pk~A(|!L<{M-Wi!yUC)@1i*f2chw_?%TD5bN zOzu;LoAxIo4*TOPVjnwdRe;6}{8+QQ$A!4o$~Fj;p&j~dsOJ(z)R(`Qm7=T_@|~jk z5`=4&X3i;>%1#Qp9Qsp4m2zOLh$ho9^HGo@wMrANpx9y~Il-2JS7p8+{?^2PEe5;A z*Gp0%DHEub!<9;f9=fP&6#S^}C{$Rh7zQ5_S~(vJe*B(3nne_Z=$at%l2SZv8G6jl zr^Ddx^bPGAek=6{ZhRVX7d&mi;x8Xs7+&(C6ys0NEMd4Tyq*G+BLPXoXj3QRy*}Ge z*=lh=`GAs=5utG-2&LjOb!j7E7hr4Jn~FBgzR|P=iceE;# zWQp4yeu~XCS9UAPWKQDu_zkTa8P;zF#U@4EukzvPioqG3@f`eGnW0`+fEdKqZnW$p zu8zsN#22ntJHfK---fqVE$G70I3CaGvof2*<-SfEcn*nXXvBq@B!?_wPbr4fn}?Or zM2py;i727GNDW{Pe9~!tQu&!FNhIk}4K1W1G!5a8fLXvIHK)953L3E>LudT@@oaZFbR$=kpCmi^sIyRT5P)U2grZ$FJrs(Gq_r<-BImTq<%Y9Do)7J5mJ976Nn{_OJnA z1QRk=jM_Pc>KvnyR+Dv{<1q^kR9AKv{)VuT0e&M(^K@%?rCBu7!eU&hg&VI*M`YsOvIU%mlhOy zPqT>jJ-Tmcu1Jh@N>?pUKxmKU0_!KP(L{!$Gx6d(8dK1l?q2a1N4 zi@i0ryR@muM9prolt>*JqW8-NaIqGPbw?NA`foj$fRP^x43{-qvyH(#N4IQ*ylI(| zdKW07IMWZk;ZzQYU#pONxuF!nrC-*r6DFR@{0Zy;!4HgPC0<`ofx00V;8F;CFqz9K zu4&Rsd=`F^og9|F@^uQzn|Lu^ptrSpHe&EW5m_WI{}%>+tI=!;3KU=aXjXHj_tC#gi7&1t=g#kJ2RBkgZ*@lO4|Q!(^KX;i zw_;3i+eDsvy9P1FV;L_w40Fn6I!<*p{u zDRy4^94Jzbsayb}ai#7xu@7BX!35(dCTM~)*G__?m?rP-3k=j)4Jw?g14vd%wHk-u zt-QPr3A&ELag(@iVHG=#^Ub$#!@~>a|22b9#Rq{!40 z90vwj$rMoEfG&aMNa$&I`*uzRBl`9S!QuFzn#{Eoa{hJMmyMXp2&N<6Qf#L0ElM3q z?MlJ36MFJj9m~w*G3BrN{*xRKX-{6%CcV-5HU!7q?JJpdl|6IhiQwI#;4CP3aVIyZjp`&Z18iJg6N#@t@0jvSjwi;#x$ldZ8n8 zO0H><4Eir>;Y1*%;s-Rn?~$ZQir z?j&`+5jT3B$5lfB5Hg(E+Rn?@{aN}!nM9yM0SBDVEc?_EV+ib7H8ASAAUHm&@7+Qk z1sSk3?n%!_$TKNnt{Oz+o?gjy`5ej8NKq`>iHI11F3QVFrUk)}pJw`wqTKIGN4t`x zP^-M8)<3TC@wPW58x1O^+GXq2D5Z_o zeWIr-+llKMr=^WSe4mTOGOyV6YL{<7H0Q5J9E>}oG`6cIJbk&vB8HI^;g&8~z} zOtyJn@Gzx`=jbAJk58m}i;Ye=IL{~0%a10!Z&)CPi6X=v{4aUEQ20n-I!`|iLtsko zMIkM+aUvF@MER`2Asvn*{u(3jzZ#WK^`so(zWE4y&M7*V$dX0En?U#VQKrge^qU2K*a1K=TB+>@jgv&B00@0uC&s92(-|`t`InVCjikxiAFu* zMIwj<(Q@t1!Q;rPvOTSIK8bx^QgE4)p-160RgxYrP<)(=#op{&abA%n5fCKf$LnqguuA+WF&kHo%0R5ymcI7K{=Md0wyZ|!l==nO*D*h5p1|YO~;X&O-oQJ;IJo7sTFytPlWlRe}#XfKj_;{kzsS^7#NaJ#$r%DPk9M== zte+1BLBz~ut1eVMuIY#ePzXXAH8N=b4_LyBZ7hV2*Re3lS;nY*d30Ii#zQGVYF#B$ zBl&<8S~p#O?`x|4Im<}%jl|Yq8g-pk`!7A}A2_~^t;)P7z}NBI_+tHzrVD1GO*q2;rM zph_6QHE=rQhQ3l@+_^`ROgd943qz{lZ1Sj{RXc1eT|N(12N>9(i+V?x2(MQs8GaGS ze?}vvB`BxHau$bTk^3ir$xezbAuK||WeDZM7N(J_su9#zR-GWEkHfcRD^J_qS`Q__ zLq;g)U;tg0INzJAQMF%fX`K7PIy_KC4kRfFBh>#Ke+ zyE)`Wyr>5%+}I69`>KGLp(A|EJ@Iw1W$RcRs}4dD}M-VL>B?p z61HQJN!tucMRW0_H?afZ*z3rh?6QFm$+B5KBZ>6)zIb>>gzygFgaI}MGtQOub3&(8 z636-JBYB;BABNB6XHVoDTVo0|J+x2GjO1W~$Xzb~>@Y<0BbCr(;6AQCAu5joRw+2# zW<6Jt1e_iZSU3n#iQqe?Z@aB`tpt!77#<1Cf0hE5rA_+BmL_y}Jp7AJosDTlZ|M^DPB zW}0PZ;HO4Ejb+K>8xDb7C+-lH@a#*>L3}LbZQ~1e4D~g|_owcfGQ8(!^m^f%Jxi#-XB-{^lL+Z1#$1@NG$Pa}`qL9ZF=SAS*oWLB zl_9N_L+Dd8d5&LG9A82B9PbhqR+)*e@P-r{42BgxV+J-7K_|D_je=MVGzc&;z{PR0 z4sQ*Rm^Jg0BcmVG+!g`ln2cjw?dy4cy6<)`s)e3LeWb=aO{K0)5?N zmS(%TJ>H&rSwb9sQd<3asByS$DIe||>6B7Geu}_uBvp;!$%uX6+j4l%TKP6Car5>& zzKRR!pQ|4k|wI4Aea7dw*}sny}2&>^XS9D!Mp_jQex z8;li1sLOaEfwQGo4A~1+Jo+IOz7F0}{FAv=HOlh!j7o)%(h9)0ANr4592EZAYa=5c z8Oj-6SE{`KL-Z2MNF$u*^Q;lk_YTKIPFq0_ElSpYZ)pU*FO4h$c2vL{`o_)JzNG2A zF@|(4UbJcS6w@1a!@1Jd2JO$^QpcDen{O;sgsEU=F>Id9HZN}*e8FviiA6~@ZZoty z%BWBhTd8X88`t z>2UgORu$8DMlGOc1d0T;ciodhquxal7;&MUG;pf9O zd`mcy>*y}Psu;?241vZ8_}D^vNl7wWDo;X9*M#BP14v4ev?-jytaK2nn+HVtPT~c* zmPL-IrPfY|mQbojwbKfoeiFSBGd225_obS|R@Kw4AthI;(Y7GW6evv)qv1Hwic_Rf z{iE&i4r6cpIr+<@*<-YVpnT^?uL~88q!}02&hF`Dl%)RiH=|oh=(uUpg&7v#T{#!g zur~{X^gOVI^g$LdNeYI^l9!`!w{;|Z%t2A^=*(RK*XlL>akkT(XcS9$2`J`>jQB-0 zKgUo=`TZmXrPGFIeL{5vVy!~RI|nP-jRC9t6EJHQu=~AS*cv6<N=f{{5!2ag-gR9wzPO=x(ao@jiTUvf_62EYbOO ze)3jqsT5$0>}*iQiKX_Yh%?yZ zvNp)f$7s4R>Y3eMTh+-DE4~U}Y^63daLW-^!2{}DiOG1`!_Zu*Bv-m@S#xHO|2}w-;=v``}5K_lHe`w*Oe1+l?wxwy~)0$FSwpLxk~4f zUzZAynS>cu>h!AE(^x7Ly|=4)$4|I`x2BW!#-j4jR#oyQ+E&6dIk&FDTC)@KkuX?b zk3j!!v&+Z*gzJ|;n#F6R*arf1X>W}{=;~p$Mh0=FH7WfvA59jd06*AgG~mm7Ggtp@ zc7Sj!Dd!i6?L|LqKZG|W)xu&53{iY07LvouWi|J<12mhfLpKM+pLy13`pk)fo zjU)Bg`%G_dIf+-@Y=oiM_@V|Rp=s&mDGqdiU&pdb-Lm=lSE|Q`mE-@Ju1zvJwuus= z5ndqA($gq*Y%&9UiHHYGI_#7v15qVgqlbq3Z)Qe^WRpoV3f0s%2Kr8ZXJrk=;$VUt zJUY?qMY6MBgyWLS;$3(3U6N(INLxoBBxdStAmuJqW`jKFc6cWD6%JtWk6nhi05<=DX}|U0T~9eHM`}(IxS^3J5LC$HEctYWcjdLC=d3c z!XP_zq&e_ZLqM)Z&NM4nfFRw|5@Rn)ID1DZT)lo?!oZ<0emK@NUKGY1fs(7g%w+{) zvwJ$xrv=k6Oyo(F#(XyjOO*77c6hA$Jn3GtDEh>4+=yr#e4drsFz}hIBxrC97jv0C zEyVIh!9x?YYtSIup-B{EkG-bY(g0#tVo-#fUS%umYj)gVbqKoaCdR5ec)vYuRyA6E zpg{7-p<3jU)Q_M$>I=x~oNPgNBlVWNSUHfXk$;QLV`gmiAPDGF6WvW`EYrQG#8HcO z>X3LVMq#TsbHEVEbmYR4@%UMa|p&VRWxv6Jv#guMf0wSm@-qKb>3=2OLLkb zbU$&#TMJ6;bZ_n8@FC^VC{VgSQuRXZV*%u0H7Ss{xltz`z?n0MQdbqVf@0@HSz3X~~CQHd+=<2E>cOim{~vQpeU)fQM{sf8z5x@teQZXx{D=fB%)iWBHeA z%XLr-dD4agmsnIwpF2M!iSk#))OgAR$RxE4JIoQy81bJJ)!YT~!Z9rrDE@Yk!6wc^ zLz$YU1cOC^bUa2F6xJuAGBI3M>L8@2b!+88b`GX8?m|+M8QfXlNdoVhf1c3Bs7dgd znk3pirBjs^@K^H&6qEkiWJ29D)_+A?dwsNnboyT=TdPV`(IN;o4B^K$7uN z4_8|Cyfp`5u0$VGp&v?7oP_lCiD2bdk_7GgvToQ{+#GKP2XuUC4s0*l{6ps)Lh;1S zk_a-t%;eBC*K&ESu$>`NG@`t9ZlEBZXmtjYe5mrVBSi(R_(jtul7|L_&KXn?|EXBw-KfsGVbA3I>Y6B ze)@Pd(X!uhD_Ac}B<@4zV?7p^1m^uE-$8Hb${}xyN-*ICR!jKLW}r2ivn9Jxqa{9a z&9V~~QT=9r^FBjIG}Zv>qGii=F-Z`YB{)G$JL|Ff39s93RxK`k`S14B2FVc1l{ zAZu=v|MJB##q8vFEIC?DL%JkJ3AW~)Ggze8S9MJ|5f8GURs!YYpFj=IK-Ls2?n_@4xVeafvB`EGtJ+6Y^D`h@3#!H}ufpNP!jW>o{SEv{7B8XunL&=)!wi}6O%t{TRw zBy<=GKow?_6~r@nmpDq4qIdUCz+2e7vv2gzi`H)*u`aEC>`3?EN9I7f3rxlFBSq$mlE&h0u}oNX>CVvX<|CNC zRP!PmWdhU8AR1s0uCU(R56`p*n|0CM^CtAA-G8?yZSjCcmZY_jG7!j+djY-wGP|Mm zzQXsEly~kNljV2BX8Qy{bSkFA+ZC$uBxSQ70MVLT5(?0@iFNZ2hybbHx{XpyjVx% z3?L=qgeD{UbJK2w%;w_$gI3=ZS4&0mT<4dI48JNlJNpr~6xA~jK2J#WJ?-B4*ln>y zKlqkP7LT1$q7n~yrWwz<+b4*8j05e#E;yh*xK0qqrr#GnXtTj4&tpoTo*d*W;Iph~ zGQupHh!%JPgR2Dj0Atl_sp%Io(MY$P_Bng)->rm{qS;Q(yPaz$?T&e(%B>^LDb zrBHET=!p!oC6ESh+KxXJz(J=q|3rw${y)1&Jczr&;K&@v;3`aId#>hZDW&2S%Vs&qyqp!r2tp$v(? zn_DEh4-&j1CL~i~q+KERzO^&khghwKqdNt`cPuIkhcgTMmFP*$%F3gqT8**CQe_N` z50a1Pnrp+4B&les3r`0(xxXAHA)RnS+^vCYjgLLBE&O-DqfUvy9h$cl6(#nK{aoE?2*tU&}2< z=l+HlFyHC(7gCHOo>P|{E3=4g4dd9|#B~)veC;m;u&lEg7_HYDjt&nNMgyFnj~Qfs zsU%fNXmoVqqiO)`&qH)cmfR-kdyN{V_-ri%VsQ}sv2sqMfk~ksi;tMgilbhKdE7M= zp{dw4=7acv88O42gR$PP)O~RJqQsmjlfa+x{E5iXHbs$=;94W$_p+vu{WNUO4{nX| z5l#f~9HG@Dd|!U(@lkFwOt|S}c+G(~fqly^; literal 0 HcmV?d00001 diff --git a/pkg/webui/ui/build/logo512.png b/pkg/webui/ui/build/logo512.png new file mode 100644 index 0000000000000000000000000000000000000000..2bc3b0b03aa840a3beb5879dafeaa47c65dd1ce7 GIT binary patch literal 26403 zcmb@tbySpV8#nsQFvt+XP?EwBItWTBEi#0pgft={NOw0gC@~-UG=g*qBB3JP zUDDkR=V9;te&0F&pS4)RTHencSI6%PRa23>PDD=x0Kjzxc^P#8fP#NQ0r)lW*Pdtp z3HS@*qAn)|6m>E#0{{|Gkdf5%G+cX6=*ehy-iFz~at5Z`v?_OuL~udNT{`B}ZzZ>fR!~15M%|Isaeo4hYkDnOKLs54Xux3l<%4D)+XpVYbHMm`#Vy<`U6?O1LIc zEv@UOp2=C4Gj?`p%)7nv?@xR^EC3h21wf|bs0IVDFj!f|ILYi80mY|Y9iq*V8b0;j z@#q_HY@;j;3Gh-N(}94R3OyQU!hb{Q%U7 z6@VbJ?K2W|5tf#hozX?Jf+Z(Se`Pk^W-qY1e^6mKG6)=dv_}!&jK!KKeIf6$=i|ug zqOhH4IDr#=V4qs zsgcZmUk6MuSwBNNm--`4Tg(Vm!JEc|BBIF}h#`P{hsjK>PrXf&4R5;S=<=L)_jdnf z^1{`+cjCu_fcfih3&^`h!cfQnVIZbo?LF-d0fZU$p#3hz+vvVMuj^BpE3WZTw$1Mb z%jcVgSy>;V(6oxe(10+Ishq4$Py|96UHXg}X@WZ4KPbnG%ttw3*_M7#OOv)R95r4_*O#yW<3hoC?*i_o1q=`OL>1rqyGIhZURtw z>9HbfF#U@afU~CpFmw|EFdrg~-gCo9=#eStJZ^2!*EMjAuQ6OM1Njq*+NewdhYZ*3{tU(olg*lgd5Kb%T$ z$6%s((yFMYiJ8MC(^)||3&RxyusX_|TDDqz=I8azwIN$~Etn90gbq@WLR7MpD2WfMu{E`vw~{t_>zlWw8vrPUU8Hoam))UmovdczD{x zjkR#)g@J@C##19Fzk$UHLEY?1RTk>@utKYrg~3__-4hlD-0YzZ=Zhlz%&U7VtN8jt zvOWUAH{n?HB}i}Hn@9u#Lfv&CB>VK|1LBYO=Y%TzN}0WUED`RIz%oDuZA=LYIxP_b z{J`Lw)?)ZPp5x7$b{~dsa#~O(Vb_ImJB~J;{R^9QKbT_3fsgh_$&qF=Wi?a;?71c52R@!AOk_Dj_2WoIeg!HI01#Wkw;inR$!T&!Hs=gI`k-y*!!(Y4#s9u|QE;93SsEYSD_iv^(N+N-tU!}5@#IG64AQHS0e(jh*- zPdnQ%k8gf?v_5)62wEe14G%FK2*7|~C=7;>pWVNl9Io4do~_njb{{f1Uk<*60}yFq zn*Sp?t60RxGc!I_ZZgL>i?L~{sKH3hXGk#PVw1l~eup#ZwH;)>nH z^~+m%r%j2BQQw0OuJ2v^o{t>*VuZU14E;0)pu>9j2vUhoP8c5|2jj#@-K_r3ijWrs zEr#-`M1vD&mPW2WLFPw4LAS*5`s;{9Am&^|df(*AW6Tpe5P~=B_LvJ9Ao*C^hOvmn zoL#*3=(*4QZQkpdBVprxQQB9H7FWVaf<$6Ln`ed_yG|#or1K?xAj@RFg<2a89Ld57 zkXR3h@ijF1iVWmb?@zISVhP?@3&Wx{v^3&fTz~B|S z0F|SK4O3T4`r6UHMc~0^pMa-yPCT~zNYQA9b#yd6m0;||QDDH+w>SAXNkYLF2&c0E zO*7#f+o174Try=gMT5c^h(`}a{(qNZ&F42*XDvdgxIex)(x{kpR+D-oH*&>O7*;LS zG2taN+Ys|BJ=spKNRj__x{Ye$kv;M4iaI7&#v_6NcF>*5OC>%vwS+m+A$9wQFUS&j z>QHaM3w%9^u*SQcuD4*qI{Iju_m^u|?wCElb&TG_ylaqckTA{ngW6z}!ly+xg8JGM z`wFkfVLIM#{l0(bKkJ%?Hr|J`|8E;T>OKe? zP(+`~)R;9FyqX-3`#Jmc0UQRJ6C|?OF~t@-jE@+@q@KS4WdNym8zj645CDLbfCv7z ztbWfIOuPrz0p$v`!4L#I80|N@vHGRHgF|=n1JC16)hw`_~p4^dP4SW0+iQosN zG&+Fo`B%6xO`}nR)4^sGbAjalG5uH8(4#8+C<4D8Um|-i5|M!9MP6AeI2%+OKUTyWa?t)hQ|NXyqoqs6r#Ge$(?aCz2tVz7Jlj~sttq+_|xan3b;0{SWd{5p` zo?iLH-98{5B#t3SFVBzH7!ZyM0JKqrH}zc1A}Ag?9Rl8)*}E7Y5&|nb7`NoyS-i=u zy2lU4%2YD9gvf(DkQs)@U-2LZG&(#?Zbp6K4Vs3b)Zm`}pkf}~g8LvG!FB2U9I59z z=7H2mWEi+7N6Z!;lo}qM)>mW0*K&cUc7A0WU9o6Skn<0}4i=2-$@tzTUtjIP=gS}y z?h&uBf4(q4u-4BCI=PEet+iRP2@m`BF>5!f5${`SL-tHR-?)mSMB~V>A51SA$+*@R zGDz$j6H{jFcpxrP5jkCpk@vDZ1BC5nUe*r@)HUmip(>snT9b)Y6Iv!z$Kit8-o`v?=}6+^aiu8 z%T2fEf;mjs%z0tfg=>qnT61?jH0m5iYf6-O`)33=q5F1)&q7P3sJ7{PGG5uI-gs=Q zWg-Ra_2kxwEVzEi;L5UMUEGX1*6AW0v8`ZNR*`SM4|#rVn94xe`^}48`iAXLiyYp# zFzn#q$z7+4hPw(`y=ys$MnFEfMDz#dfHCduP|hQe18J0maK2HTWmeU$1(f{5H- zL0&K)(4l;KUHCi-sJI+R3nq4`&N``|VRrxZoKOi&0Rk7~-PvFMg+kCW<=PuI^KDVT zhtn^dn;;rl_IB@V-V4|WsbmfH7ot5EzKqqBq&R+Yq`d+8toK>HHuhHMqlV(#hlir!uyPO+OfswwypRCj_(KN=^rTmQY0+<|fFq)sDpBeM92=-#=%$(|sJiZh15 z6iz}lP4KY^LTN}@0s>x*kF`%u5Fw<`(P%hedr3^2DR0nh9xQ_Cwzn3!qyCN@U-c7! z>zF>Er*BK$-Bkv$@s$o}45o-8JD?ZnIH@N*+u43}58wv3nJB8!{Z!YrFXMc$P zR@i?s(bi~jrnm*H>{hcWg)QoGR8{Is z{fcaeo@mPq0%n1hk*7D6e;t>jO`FypIjK9(0U)-+0?Z?0ITojNlbSjD+qxVik<^$gpF z6~Immq@Me{Pj*}@^C=on>s_{Z9i|USAM9C*4QlP_voLy1s=q?adk zQD*lvp}uEJqh1@$-b(KF7jSbaAscM^K4`HE<^NK@p7+Amvg<2$ynQucn``|^`{BGY z??_VUWK!PL93XmF+$SCITO_-5(MTz65AJ8;eb-2vS`4C=nC3-jk-I9L^ngY()%Vz8 zZDSRTa5jF=b7zp$QL%!_*~ds3X4|0KG|{DeJZ{PG$q`J(MkI$d@nPaX( z^SRRtz`v}pviUd^*W?*8FR#5#|K!}4M&H5fKJC=jN)Q$&r;)h1;^2@vd&}2SaH56D z`khDll;>l8A(QQPtS%f2Rya^DhTe@1I@D1dk33c*D>^!|$ud)jPwJa+JRs0USLzhRtOmTcTxkAnE|4`VIw!mv{_*T04) zGI~Pa7jMoqNuR9*N%R$VJzQ(sRM!`(*qQ?>X5p3z$3*1ocRDlcJ|Ka=D-q^N^QEEd z_2ya)hb*oe-%*D)7d>I~4>ofNta?&-sz{xFNuqkTPZ_1|V&w+=D?3ygMP@>e zyJLFWjO182RWEXd{2Uk}v@tt4Sy>1vqV=o5*7B9io|75$O=l(d}wcGUwP;N#M{F z&+6AcR86griIX_mmTT<#V|E}O&QI{6>UMyOsJ~3Rc8H71+1s&A6^+QgbLz?V(I~ju z`B82euywz#>2q5^CK4Yla>i`r7hL3b=1-7hTf^S+M$YX)#}>w>AbW{-MHwCMbseB`>7v;8C7X-f(n3j8s*b9SCc14m40 zze&}bEhWvL%?CZwe&PkzM}Eyk1aOiSlfCRsvQF?~BlnZ8X@=bn)T4y=W#e9(IGq#LTTCyV+Z$%6YVAyj_&H`3hdLJnj2x8z1i z`{?kalp4iJ`w%s)Va_|i*Ad&5+@}MNy;{4xZo72Vq+Zh+5EgPQ`Krm;bL8%zTBq)Y zHJE;Cl55>Gc)dT_)%%TCgvM)Mx0DoDih99FmqET4qsc=Ar^mbA6k4&9wbq8Fna)!R z7na8?>puiZe{e~a90YCcJeJ7TN$?(tFp(Sf-ZkL7RU`! z9+QyK)WP;UEk&Wi5$GnK7>OoDL2DJ!c7Gugu#yD1ho{q9IYT;owN2Y{vxM6imBEvMR7oP!Js#51-eAU^?gy=_w}21?{E$Z82`FnR_Aq}FZr%la#63wdgD@_ z@Z%e8PjyKHCpX6k>lr2V!fsRDrdG7A#{5SF1u9iOFt(+As{YRW>)E}j6DGb21NQIu1X|1C@{Y^uRc~V7?Lqf&HxEAL zwyA8HD00>6#6G$dO|1CGCG=%IWp9rs2Nal1Bv_~0FsZ6!4f`?KkRRX2Cm#Ve<+!${}N1E~#W;acOheW0V}7OY5ECB^qkwxV`*8#$eTAP2AU} zX5qo6P;l+d*{YUOVjOi0abqKVnf zlDyzRPg)Idt}K#~5IJrqp-R5tzap7@**o{aR*TMDwMD^}iU87xGpBMj{oMS{H02rN zf}Exczmae<|0(n8uL(PhfjqI%X$Y6TR%8-bEE%OgpRFxZvO0?H5WhA1_*Vj+Q^u|L zH6`)c>AX8nkfa2TT2nC#)mAaI)?T}Z%}c@xy>D5se@TQqdR7w7y;RqubsgNrQOl4q z7 z{1IhJ>&2RWlEetZW>@$K(kFJ=$C?Z^)CT{Ah365LTl&9}7FgRBwjpz0MYxT6 zyEg6YdI#p1P)Bf;#zloqEG7K#A>EIk)L@6_kXq%8rYM|UkK`k*A=}r7Dc*U7qhl3@ zo>sE-x%T}pTqoaAiq8X(X~Gm*wsAbQB-8WBknU=NwC7$gzEInT(ANS*TSWQ=E8Jjm zTTGs_BtJlxtNX4Izt*+_-#}u+M3rfRiAyWV?-n=YLD{9FypGd6k?MP;=%;;4nK|2U zOmFJc`R)^m*3Pv>4mp$+w+%*Ee`zgtQ9%#9uH__a_wQ9d z00EF6J{@>%JUm<^?8|tw{`4a9#|M|q^0I=qu{Wbfs0UwnZNRcSZADv3TE23{)5JW-U z<(_R>lhlZ%Co(^ZjC87^+}a^<=%sF`9W%fh=CeQ_X1cu0(W@3@-B|bg9Lb6^3QR(D zxtd0md+idnF0D+nwV+iAK*WV`jNRJ{BK8Bx0&rF#fR*t^?XuUg~lM{WTdIY~};l-Pa zvLk`YBqx2LI8Ullelg(Rc1MpLs`$fF^xImEcHMf`FpH^Up$^ZN_APFH+{WQ!N#?hL zC-nn*p5?kXh5>Lon=?h&&bh{uozjAp~vgQ+S%|8YAv>@|sgkQx@o-ik~RbG>8c4 z@=VQpFUTw@^kF+9Z1BT1|MxnwuKtoj{(5}y8&$W{-vJz>HBaYjgCd#P)TjXm4&_$c>}I7jMOLZ9S?bt;_gd!EuB2jG&|@ye0gAC zJ7DdtVRyg+^rzmR(L3}|NMD|`u?lMQz>ka7T*qT!EI@~VHR2bXvx=pEcDl}gOPu~l zJA=jfNG5wjrw<0-Tfu##FZ9%SejJo?XD8<+D^nc0+`-Bh+L-WrD3|^jkSViz{a3N( z<*FxPG%jHpg<<6gUNl|)xv>%w)qW62(}Q83hXLGpM-C@wrV6Gyx(V&W!s`i+ow@!S zce?`UvfO!=`Yz^1AS6QwwZAWP)4*Dk$g_nsET`C`3$BO|0%{L27GRdorzyY6bX>@? zy+hV_;jjlIp*(cHuJd6o=V)f@xh*=le6WBoToFU|v!0_;qTFpRB_s9LK$LkB7tT^% zeO0H>UiLv|I>UYHKlT=cm4-1f-WqA>poA!9y;|_3$hqFvv zvk3ZMc*1I}oEHT)*PBAQ4$Wfv{o_})_hE9sZesZzN5}%5PI$vd%q>nXLM^`B`4t5> zmg%B)HN6xrA*{VIvS>Jf_hQ&@jmL1}=E%LHX={h5^w=z1;QaT{Nw7t0pmkSY4+4-Q zE~?fm=jE-%=b=WdSPp^7!ECp{G{1+eQb2QN)VUUm4`xnzO5ct1jbRo(`n}l=5%Nj& zqMbckX`TW`*wt_@N?SGo+Z14S`AY5Bn08G}Nd}MS=j!IG% zUKBFIgtJRQf9Lb}r|iYu&Tp&99SK?$4U5S>OD?|M-PWL9cm*(?|Rzi1Q zl;RzxVA7483*=V%K^r-1qu%99YG5g?@1opLvDtstRzUlg4ATBSlNk6SFfz#hIhAN- z8U4l-S{3>S!U+cmqS+1jxiRa4cPdWs6#Ar};UREiiJ0pCVM451Euew36#T5G0975X zamtBF3W<9FL(8W>4w<=1#p`~^JQ(6V%UWtbfXxN)N(IreB|wb};g$yI=9M3Tp}Mc& zj#nrFjp)kZ>bLtZ&`uAfDMTKAB@%io(Ar0{S-hXH?No5D0-8Qv@T}c8nB0#qGUAPo z@*QpZiML4?Qsb+8>S=@K+-xd8>V~gb;X%434DLDH2%RaJm+Uk`d=uF|728j^|7z>r z>n>eltQ%{3Bklp6F&lvgY%8c&h^K>p>UKs~btc`J^ z@$1dL%BOuj4yy-rQBZDUnaW?+n2$AYV|n70=>{t2KPL#MX(bs}adqb_d)Xv-F_XV% zvPU60cp%ytyTp_cMko7hwSKHHbTy%y-TlMA5BI~?u1or-P&CrC2R~tdZ+7;|G_z-5 zXF(^lr)xV&I|7($8PuQDyl2C6d|MVM2zkxmR2QzWCy0PB9H47N%*)0*X@6v3~?I&!73-lXz{je?#V0+?M3k*f1jGQB6}{kQHl=kYqA>bD)j9!zU(7NQu)~r>Mw_& z9=dDRU1M2pM*)`K-t%=*sHgr`xr%S{YI2+j723tEEY9msQ* zr|2Yiwj5hUu-Ve2*FI|z0WuCRY-lWcQ33OVHHLd${mHVDAWoQ zDIALp`5oW`YW#F}lpkpQ7!KQzK$P%3 z8gJAD0?NSd=-^sG`!Sktka3Y$6rSkfKI3@1cEGHzk+A! zzO^;ksn1n5PU@{u?2WJl!~K}I4~O1Uwmh0TVKudlD`O7yL}{DI7~NUg#;9Ou?E(RK z^jGuwNw(QBd|GIc5C{d}Vg1+4Mwh(c`Q!*Do5G2Tm32m(2N z@!Upx!rg@pA_LuOZ7g|bBxnWo;JKdB( z^CU8g9zu$&PrjT^)R1Je1kBWPHl^3I?bZ7znKgAi;kMgkDp?Oy*XxaE%XCaNVqXQJ z0r}yw(Yk7JQ%!%086;t!26vXQzsQUQ_IuQe_h2eP=_h_9uMpNqqVLdoE7v5+CJ2bG z7MnK+5Ztw%bq)G!rI=JLXs?{-L;(Rciq*S+Fhchsi$>{_6r8nIio?i`SvdE-m*EUA+li0Qu7F^# z`qXU7FJj2WbiVhqsfv)YrgvJ`2T-1x|K$}@M3UOt>=t(MwSGe~=m+gAe|5u4ue{%J$0S^i;yGH4uDZ~58=q|EAAPucj`bx(NA zQ|MLq2Ps9LgNCBy59-$WrzjP!P1!r!C^6M)9FtvR-{POqubf&Z9HVz%MsyYx?=RSx{ZT3BxvS>^`Cs9b z&gZU@O1NSh_1-dBdkonQJQ9>3N879e-%06_?@BjxzQl?Cg2$N3G~8Qi{Y4(QA@m6> z{qPjTJrNQ4^;nR+cw>lvLd98iHaXdKpA>9=Rtkrfqsq)jEPoByI zE$a_U5M9yLyd+QOyd8~K=_XAyqmI#WuT^*3_hGWe)hpz^vFmfP-^G|3aS)CFgQYRK z9&ms0&v-2={)EQSR~EU0O182yCveKss79ss<#m3xY|&g#9rsL_-PKy4mDyOPn6mrc zoUo(Rww0&`LI?=$d@*KGHZ}w5snl4rdjUBWB?6%1$9!P&VhrIGwrdNr^u@QZaG#)ggk+k~2$tjiBdw2)E_PYPLa*YlYyFfPsMJ#e z4YfuRP9>^Nf)$y$$7D{q1a3#**VAb)3dAk#s;4LxQLQ!dsFrwj)Cgs^_|nVVApy4p zFD6vYkNkQH_f-3ml<+sNIt7YYe-l?yEiSq}C?8*Lxgod>!Xglx7ex6grGHdIEpvE7 z!bFKoxM~NJ1%jV0Z-%jMzY{!_(0B0f&va@qPc~CwVLfwyj}zWb^H{k@6J}0=*=7NE zNHYvpC+sZvO`!cukfZ)kdzYTU@BD-B^G6=GVcRm7lQqpF!$HuBtkqHT4}ZK?0@NPC zb_l!%2P5$Qs#4O)DuY_MLdrpFRErWy?mhFu*P!{pk4hHdINu{%5a@Jazt(B#Fu`Z= z(t%Kw{TiuF@AAC`0Ts71Z_pR6E#$u$VCK#E&c|USLkMOx0Yb0BkVyz;kSA8I2s6+b zF4AT8NFHeqqB5CgHE-a}Uw#@AZ_Iu+;+>&X7lNz7Jw4|Co@q0Q{pod+uY!&|U{ThO z@bp^yQY-ZBV-Q$@P`Ll`o2H+-fz(*gSJTJXO_SAo4y}3YRFw1}wxe6EO-${-cKEEp ztvo!40-RT{qw~R>xtzl2M{9Znkms(_Teo_5*Iwm;Lhl|3?&VOy);p|rjBy#ldJ{qM z!?H)<^yxu9f0;@65->*wL!*HJr2i{UWCHJKk?ldR&T;A-1doz2Gd5` z5*H%x-H9EGFcK{`0FwT!jp~mMXlm*qwRhf$s$UD>AgnW4ZJm628$0|Tn-#TgGO@C2 ziA9Gu!-^+(oLo%e=wayaQLo|Ubp^C2NNLNj1-HPGrniY}Xu&ZH4+8!g9ZGYk zKl}hMYLFC9(8LxEqQmjX?||)>f9jDts@Jv&pbBhT4xrCaeAbhr!vreP&K_$|l4XJy zB{}}Zha(wj+3jA0yxWlNlv5}5rcx||LV+EmxJ-q?WX84<#LMUPpFo6X(_n;jkmoB( z#MTu;1%-b129>T?EkU`>*5VT^hrmUmm1}&u>soDfT_0#LHtf}E+Ne^(&y zkimp|+QAZS6oJM&EIJ*uXwVkoMSsHS7_8B&;;0z(5bnimn)mK^{CDWhC=sCB~Bm^=-7#G&^ z9IbDXi|;|2nQe7IHJziD{gc<>I)+M(8(mSME?$+46gRurbVXXcZ!jTI$i z(?AS^*e~ESt02r#0V!A*SA!T|Lb>@wrxJr_avQ(MJ%b^ke>dO$rkiayn%`hKjJSgO z-Zqf~Sds4c;xmxG$4Wid*jcV%P3U)yx5ywkz{{IF-hEBV!1WC{qU)zJlG44zvXHGgXfpu&}2qKlhyWkA-mTVGQr3-ed}9o$m4lBT8b z>X}dFLBt?drc6)wEdpp#YkJ#!b&x%&I;C9tAit5hj8Et;T$S&(+E+KWn{y{>^zZ`E zq+%uIVF9I^HRh#e0|sgA8M;){LvBl@9nJ&TtNwhymot;BC9T6*tk9&n~9lu_2 zX*y{fcSr|bhD6!~3=P;n4}2pE`Ulub1j1O=a`fZ}uJG)bvlx@>kmxv-OcfB^j{g5) zsaQZw9SDwat@j`+enJ9iT^vhFD5cQgAyU2zzP-8PkzSBZf?ksgPt1S|^MJFykou#w z7er7+5Y48Rp)B9a1F^k4z4h*{&+}{Y+6}SK?!nONVrj)SEC9uzW7@nFa0G+9+ybA( z(Q})9IRq{3<5QD=#BWHjMqtuptF)XfenZf3`&rX(*=jca7pioswCi*<#3hW!&%W9Ya-r(8*fc zug}<(IVn(AxZgkVym@_HCu)t;y&=VsghK)bt&?fqv&Sm2D3Tjb<{D%Z zTvJ#2+AI*ZQZU|^dITZ=jO|8l(FI%qWlBfxHglLu-2o+9v7Bq<*Z(qORY81Bp8El= zcgh!Eh4vZ7gTS?G(A6u0bK}!(5a^>yp3Ot?RB=V-EhF4k(O6(E>&ZteZ-^rrEelmA zx+<*Egy#g{t{+0yB?@HrgF;*T9{DytV28T7JZt!+VarLrPOB~-2npbTyOQ_xg~ulo zB`se@vO=0&RY28LwU7}a@9FF;6ny?mq|hY%LOa&~q{=H|H_Y%H_5u|I!M6nG|f^F}Ml!?i2^SL`TAKFxv=@ z%KH{TK0w)X5Flr0j* z2ZSP6bT6Uf>=(Wag3#d~Ev_d~hXN{E%feg*es1ffq}=;)krv@9EE$x6BBZ~b{tssH z$&o4y$Vut*rd2Iz2-ebk=OnzCuYSiJQ_{ScOK{^6rk* zd1C7|UYSWm3Z(|-HU&L6w0p!PX+G9qRL*jpuisHT zuRohg4YRrM$dZs<=Cc{QF0Z-=I zvmZeu;QSB>rTv5|{xcXsF8T7#^DAm*$*iFy*@@Hs{SX-~15M3yU+d3?PdugJ1Xk{r zzM<4Ym5AifU40AwKyPm`gOw>7YH1uGystAvmZw+a8=v!On&7_$=yV5Yg+2Uu<6!+0 zx5O?*r}|Ll)Bkn{pfiXZ1rM_1p?CKr^m=#GcpxC^c#1M}?x9==3mgRTUj1NE&^y`! zHEj3i@l{)8Ce!dh6t;~_S;|mjF&+c~+64d-4=2gb`#4cC90{VziAQaQ2g`PGc8`=U zJ3s^xSNo;XrtcZ#e0A=yi7P+*2y-xUpJ|_>yN?tsgnu)-Sg%gR7DN8qDhZ4fGczSB zh5QbcEC>~Li%|Z`H@}?KpZ!TNz6fjK8I3hR`%&mJ1O=1eM4TmaWk500e@}hM#%t28 zR%6^?x-?u*MtG}S&d@)`|L8AXld0Q(Gh_>`C0ti;H0m3*xO){A|3kz$mg1oW{hqiI zO3Sg1$~e}=`TR#&rNk!8NplB7kVt%VYT6=C%L63gzpw3rxsUlOiQ4QvExRQ3>0Njo ztS%bAv&Qx02hG$enX(@T&)rW9a&RaVeiq0gA}o)ps30Hc-0|-Hv~L2d%**E zB6Mm%c)LZVLWwoRhvM4`mhV3v7qB=EY*SkGyVtgUXuZE#_hbt5^1~NVt_d>Ubf9st zf#-nt+CTh(;~Mv~pQCGSMfj1M&Q<4IvA6aHPVINzl3>0M>dPK3rW=7M(&{$c(aa;( zA5{O*7m9M+CycfHV(0#O2haEH%#Fs!@^~eFZ`h`*xA5}lwW-dc-wcPS+f=5dnp=b^ zLpE`m@9wSS}K`3`;jG{pO(#RQg{YfavjQoZZl5>%lOALsi*cg|Gjd z=0^$Xvq3+hp4GIw+X*C3e+@>s8nyZn0Ui98N;3Lh5!b+Z%YU9BFB;HJCLR$vvWsS+ zT^V)$aK2Yb+?@tAjx<>GU(JABEep6L{~6T~9z1yD6Z~CFNh19X&G|T&w*6}BZB^M( z8KD2!!YK*Q$u<$4`+xdWOc+o|a6M~mrD6R|_>uuu83t^e9S$(lzW!H}z=@>=A(fA= zd0k37)VcL1QV3lT^kR5p5sN;6SI9 zmPITd4BE(NfA@bBoraG){>CpW{o%m?PN2;b4H{k3EyYWpcZm(%PI=9B{evVp-5EaY zYWNBN;L`%;AX4+H$kLmh_aj$c2w}R73DPZoM>+HUK4$}Dzt>f{V^gfvXxo1?qa2(V z(EhZYdh|!85&iZf#Xl&Q;oDuSBkTkvAg<8|_j8PlQo7}}F|zmr&@efu|7|`Lqo6 zspMZ+LNUdq^9z&qxeR`~DRU;xzk8oJFk6|(0L>M4pQbW`$7V%()jZ_eK{5}d;#7|m zm;w}88@~V?Q!ris2fQ%!j=54+*roMqE4d`9KyM*JW-lUruCYNAwo_sMghVt`wB0v` z@+K8H7!ySkaL?vi-_1Fm)k+Qs)VSjckg=W2u%AD(hn;xoD-tGEOnC!;f6J6~&wmeD z+OxBe7TWVxvK6~|gb=p<%hdl+uv9^k*{>#!TlYv~DyWDYNFI2m+I-{a;2HsPIv&Ut zto_wppZun*Z^xX5FYm@O<+o2P-J3o=KRVUgQyNgRZ9z9_w)k z2q`#8MHmRoofSxFw4b3&T$}TA{=w^knN&71Q!fjR**rQCKoiYmKIf6$tQdZbEAH4b6nJkc?q3~zI_+vRN;3b%Y zAob5Z*>hW>hI=Hn(CHC1xYB$ZqXh{}-aPiB-isoK2RH1$U>U;2Z9_|WX z{|YSE=PwEf-iX!QYb`gSK}T$$me(4-L_R39u&D_fm|v`&CWBqOn&pHnCjzE>u~Mx^ zf{4Of>GS%VEUi$C|3gn7RJ4ihbGgsQk2aZR&Gc-NFfV1~+VVLy+5Vl%;oFIwJyW`= zd=C@;RG~@nJ2#d$EUO7GWDv~#&^whFEN zws{Q%iZMKU@n!yvT|K^P4+G${ezF1`{2vxzqV&K-D=YEG2Fa{~0``7khwoeAh!}ox zcItJ{{o|KR4LuS1}@A$*1^bBgf zkUYH_{&kJo;?VG=P5cRA-TFV7U9`18&tdu(lH&8tLLRWx#+*==FJz;etB8#cTP$_n z5l+t?l_K+U@ou)~kK8vVjCF%;dA)U^LIP*|k4yhxD0&vpy#mTPVS{y~v`}=s`|+RH zl2`AoiVm0Y7Z87XJ3(f=8|>FxkPzvynrT|QNUap6C*A4GiOrG8Q$W6IHA_h>9 z0@nkHJm+Gsxiud+bgrK6gL554gA zspx)!leNxA*@L5kpF^lMAaIDX!}?r|oPp9%MutM!`23t70c89NUXSYQbB_%mGsTUw z08)YLyi}%Z4&bRTNE1zy0O+$x-w@gvI##G|@OlOhWDA_wqH<@&EhaUFRM^|Dj?f2x z0P{FcS(+Rce|!YTr2l&xFBdvK`WwowGH%M)o;LT^;jgxbBc$kK%yi=dy&2UtaMDXt zj+~Znh@x>(rc(h-go}9ogCZMzOJc;tP%fQ|1OU->di8zz3S@MmU$(!-6J;^hY6`U5 zvSx^saNWIzFA2y@HQyiA_jp1xqTgfjisXci7YkU{hvE!iu3yPHF479SJT)r{V%~vp zmVDgSnJb-78n^a(opCRf@3DM0W8poaB;f|VSDMjH!7wHe`*JyWX~eJw82e~^)q8P9 zF4p&|?Le{rhV7+Nf4H)S*U(@@m%{rOO)-51gBS|FhlaW_-yG_cDslnK`-$Xj*Ka;O zZ11GKW73@2iMM?zhfXp5Aa&@ipgAQCgR9TW=! zLlt*O#u|cLf1uuM=4`F4$2?%kj~on$&Ro9JHGI0?=bU+#bel)I!+IhKSO2!|_k2zj zkJES>a1@9WR3mbZLrmQjUepjxc~%tZ5M-?r{5nze{0BF7ygsk|4zs;S70}TJ2{Z%dqpn91k{wo@ol6>4QZ8Kg}NUAv$o*`5cS0n*)Iv z$1L4x98B`S^mnvBPOP|4LBc@bixX2(gorWqe9Zmb(uVFIVO@`t@Jwpium*rfUB=7TE09H5l>MZ(#Hb(8!H;%>0fszu{^s>qtJW#~z@YdhT-JPn{L{eyaa$ z7HvyES}I`Bva(}iOg8!dGAlScb}O#@60*p7ke!Igf%H-c8%LmI4=Z&%Yua{ zsO%LJ8S;V|wRSwh!Xf*LiI3iKD3PZSJtGLkXKkUm1_`C7XW6k}FHVvYoZ~fJ#gc#~ zD6&~k#_q_dPBQoBKnU8v6TS|E%ipM#ExE17+K+o~f-fFMgKdUf1_Ho@i#To4Dyt-8 zWU&0eDLp<)N6dXyYkKD|&Jqi``p&wf@w*Av)})^%lwqsGKA*nY{rIIUcS<-(osYZD zKHn42B@KPR>aVSQ(d9;z&b~s&{SO}#cUMaTdLN}D4nGay=$CFDHEn-|&S!?(=$1x> zs=?bl5D)dkRk>7bi1!n2PE6`^Xcq4OX5J-f<8a3a%)Iet0I*KtNH-W!RmXBJf@g2A zT4*;aJrt=U%DPB2$|+0MOUKWJmyLF8%Qa>tIAPp)Gr4RH#Y_+R=qCEKi^}JaW$vq; z>C%1ha~$Q_g@)2`dxaNzC)R(DU8T_#KTKZr?)&Qry4-|DHekHWS*J?GJ6p)q}M#aqrS^w2n0;boVsKMkjhl!+!Ev+ z-X*KfJw7+H$d>tSS@3~#Wc+X1uR9)9^9<+%O51q-X)F5e&cKzTw|93hsvT2FZE@gy z!N9--Fx1}VTj9Cz4TkYP)p}(v1F(UyFP*&AvDp2l+NIC-O1wG~8FhTa=Hu;O&Zb=6 z@1Ob%J+352XFRiS(TK;aXb&n>dA8<<+iY+cY?&fT+23nn&VClzot}X&M14c{t)##a zSQTSph4YkEnxP)-sWFZ!b~g1{ENzj3?GHL%#XfWOV7{JXMdLg9nzuI@fR2!>235Qg z3TS=(nIB~|&Y0}FVJZ(4*BCi)NcQj)hpV@idgLzBNzP@_%uWnVj3B*HLkwD*Yvy4)08qKwp;u<4cl|DwLED1F-se1>$q2569}~)>vLP zVYl2MjTXLAiXZt|BM@K^@W*q)Di5Aq0q`_I`t+Y*XWfT%%iUEs+7q1mU(8W1uX63EN)rN zjUFYu(tlk)*qCH)B1ABLnLPbP@J-23I0OfqQypVa8D6l{3!$z)r$jmg1`;(t2b0(< zL+;LeN&b^d{vbK;%7o7w)B5M&bowTD6a7R_*q2Wls*k-&$ldMmhV`ui$Zff|;pX>c zNg>Oz{DGv;mO4e+O#-s@lBj(HfO@bPKULsVhbg7za(f<@C2lVR;VX>cu1LZgbZ^@36HbF{XhHSWeqAiRcMe0+eu>U&! z>5*y#yJZ6QyBPm6O#k-Bd?Y}elzp6mzb0tG33vnl>h!LPE2i_r(#$Xriekr9= z8YLo|?v`3-e8;QMnKGp6Cs_`&k0D_oQ}`FLl@OIHKFpOx7CTJQ(038r{*&zGX*#%2 znsXmObvV~vE+W<2^a2>z&F69c+iJP-*&K23xfy9@aR3X-%nd>gX69>5Ff2JhyPZ^hHzH(36|`DO<)t60fowE}CMuS{ zGC(N2;f_`b(>FarFjRnkPL358?swAVcjkW)nkh;4@^Q3M7D!BQ)?e)KG&!Qyo``o` z7Cp686z$R0fpTavMq+G}O8(fhSR+<|K?StLs*9z4@tg(a;QsDIR}t+6g_iZ_>9#QA zf~O_QrmLlXA}Jdh0v-#5|5?1WhoC>1UJ7__l>KIrDVrqw>Wx5v0#Hg^%QN!YUrQ@g zVSHme$Ns9=kEvzie4MU&`imWw`J3qf^W>ny4yYIh+0kK1~t-fkM0Z!eKht8TZ}7KYgx0pKwxwRV$nb zu$V*?G_NIZDE%_ts;h``a7bQ>#IAQ~w)c7!qxQ1t;1iko=qpm50-Dqn;M63-%z3p< z0FwsT6+D*D7gtH@pycYN(^c1^pj6$Pbj_pmBemY98!P2sMd6?Y30m+4&a?o(73>kd z(7|TnYJsOJxxb4cz7x1#I?w!vc_D{M=;qkJSoU))?W^vYD|97}p;MDy9S&UOv0YCe zYkxkz8*LKLBng7n8|x5Y!YynfiE^@L>kQ+qY&d#lk?i$j1;=fVdxZEJAmn~(3Ef#l z8kr|1@MQrFye_JAV0V+vAJKk!?lfN!s-;Fd6ox%@Yz$?SBrr1rUn~IfI@3`6D~rg? zJb(RHxMRhKfp$Lmj`{Z+#Uq0LUqex`_atk)9R71%&^>C^b|mQZwghX<`MciYmdU7< z$v4p{vV@|Fi{>XB^Hl`urGytzJskI@Ur0gi4b2q=j_b1HMWhjwe#1^o#x2yyFW3-L zikDcTf*%HBKD@2l+)|syKvv=7&{!vZ(H~m;UyI#^F?ZXoxrEMIAqFoN3U~-+WX22d zGJPv*QXS>AoSp^e!)C7Xe|50lpOxSL;J3E*^ZZb;=<=6bE-(zV38KGM7i(0-3*UbF zUW3hWSs$2E^l|%%N!YC+o74R7mkSzQzmvO&f11iX@11Q5J6&q5CB#F~ zgPrgIBLKKMV-P5RWkZZswkEqBWqL&c+8@!m15&DInVo!IxQmhyDP<&9>}ZUDRuB2A zle!Lin0~j$$=~m9v__VJ+<)LZML7(s?V4&tY>jkLxTe%ancX8!D6akp6j6_-20807 zT&N&R-BcwZv4g)tNY@}q#5MppTL|urVaa6n3og56?F5J5$2VFV!Q zw?!o!glWy`XBEz4k-cB*`D7{B{U+J4!K}Fau++Y{HnB@stbFP0(aoBV!y8^Q^zWt- ztJzgL&~whxNvjt?UMQ*mO^msXuhmOBDXCJW46=*j~r?byZWN#R5!?Q3yk!?9=Q|r85d?? zxJLMA9hXss^4=3yhelENt-J6~v<4~Ts;NbRkt``rD3!J06fsLc)@)AzGxPP~v9z3! zs65Jd+tKHGN5ILIdeMMeyl7}mYOXb9SmZF&Smg8CG%dsUqdVm{6G{*^MQ+^eUhEEb zHIw(@kki+ccKy~bo+*@%y+F@wHj3_}IHCn(3bP6;<>m_$W*C|HK1%{1)Cz9rgeY&e zrWym<)Qr-znTz4H$s~PcA~AWtJltv&h;fk*!S|ZrH&3MA1l1l56@Ko`5XdU?Im&Fh zwIqMR_}vI#61`Jl(vFIkyIuOJR~V?%bF+~10fhMebqDSzDGdhwmx`!0kJ>x3b~7yY z!nC%{yiZ8}JGZf9K*TY!DjiBkXr)Y9tOP))P%Sum;6~&3PPQO&!GpOtJPZWmT062W z;c7y6{xnQsaSF+BS`LAlXe2!_10oe_}(fixD`#Z~%l5{9#=+ zFn!CIw~4P^pFY+3vSMoZlO?^9G|>>Ch=w-dR&;}}e;W$N&40nrL5xlu>a;|wiZnT_ zb)ufGv?<44AK-OoFgs8rS+?6IN+pCh`~iB99=~vE7P(Ud^s;)+wAwK;HcMJTMj!0K z_i9vn`{(5*9j!2%CTq6MhrGQv)20oNur6sOp2BT3pN3NIJV-LJ{9{(D-k+NiY&$|sWQwaTq+@^?ndQC|NaGezX`c% zD4hAUvV=}vUKau50W3eMzlU-uzG;^3E?KKKmC-^EO0Oa|l#Tc>tLSyqUa#+-+;dj? zf}CHD{~Wc|JTqP&V5)T7nJA8nfV$V}qQikum6UWTpD7su$#kv@ak!8|ql}VahZBwo z1FPKe;cixEIxw>xYWqgZDoEZrh8ZS;<|r7?Qi|$neN6H8;rXP~S|=^Wsaonn^^5FB!EDE& zz#*pCOc{6wy!IuzPYf+BeS;oH!sAetgyDf z8wSWp$o9049m$^8FQ11$gC2DaX7PL*Hh(aLAarB%a7q37ug1H{pK7e(TxZJoeUg6( zq9sv_g(l7ehLPz@a6yge3M%sF-jukLHACe#Z*de~?ySUITDiKQDU~#|n$DN(%#TF> z^lGgSxPEzkW&U_eMdg)VUkBetvr|WBd4IiMWH$yQ`VQbLE=H zd+Y^_WdAN1cf3u&W#X!b&pcSre&ARp@P-`00u$Mh1-d2{Tm7|-YBpqEV z*yIc~CBk4%w>4#acc*zY&6#+kZUgluAYIghin6ZR^zexv1K{Y(z|mt>KlE7uVO|L- zn^w}vEsW(1y^!rWn*jEKf@$Z+TZ4niEN`Uaw(vM-=w_Emgw|iDdsl% zWu|^f0$D|dh+&kDBVG>9FZ&sR%kZPAi6@Od*S@F^*d{s}bL**4OcYg3GLm8#kt=zD zjz%AW9Afj@fKN*^yi9pQUu$fmO<&8ieQkH?t!TE>D{7553QxkJ-b#82k_QBEbS9qi ztZ7e8KWitB-$^Z&S5YdAIC1VpS-+s#ERrOIS|eTINf?CMe+`K7JZg*I09h3QNvp^8 za`ayYIe~^^D@;>5RHM3>Pcm(GvUt1!|`D)Zc&pNyE&a|N3-QY0#P}8W{AS}{pj6yzM1>q zdm|XI(A3YS9bzAjQZqTzr=6d$F<|a{YcVfP2t2(-Edz0m-(ObbxRCmr`o1|%ME!Jr zdE|zx>cWy{%Z!PCdT;ZXqX4;D!r_K}>Se5*EDfhLxJttAdns#HK6fH>Ssoxipu*8j zWdtO5a}3t&f)VLx!eX=;*=39doh6;Q7gRBAAg@KRZ$z8VG1HPc_gnu~z5P=rJmd~C zc-%w`$91(?HHGN;p~Y65aj}Bww(RI$(A4eGZ|?;FLorWhlT6~^aI*TItO%pZJaJ0? zuhd-j7gl+FI~Ig&YFN*1@4b9fRl+f!NLyD&F>6W9{EszQpMMzb{r9p+z&2#S1f&K< zWX4-tFU`1)0-vM6^ozIJiXGy#w`aU11>6mKd)&0s=`gnEIFwh4kERyBIhD!rI7~TL z_sxz7jmQJ(7PIDgD|Q!^t+T!GVdU)}+mFlMYKvK0#pi0ykVt;=B?T0#`kJAWvV$+{ zFtg6ZwT940W(cYNeh^TLJXKp;1;j_z*na5?bv;*-pzyO0Wvd4g_IP3A`ZD`AFe+q|gO)T&!!U3poUfLNj z#4qi1nCQAC1`RY`2!0lSFhJ?qTgPLhbUoE9)y>%UfFTC{=3*DKedWOviKWQ~h|{pX zkq|9sJ=zq$T@y? zkVHTI@$JAeveL@$mRzA}`$yiwo&YE`PvW1U=}(tmplq6ik{#KeT^pc+F8v2JP~#4q zeBo@PAfpE6oXPRBP%tkP`YEk`C&;Egsz4g7A3X3`@WZu_M(O0EY$_}+A6TF)TTv{j zS{ORi&vvczWXO$9BM7V_0_zw2l@a94Xk zR+2wZZyWc*4cGO?>*vlt2RSyHzx+=GWG~exxW$~c7Z?TGY6N#0Qt0|K*e&|5)>&GQqY`4FF>|+Y)1^4peQLAA>mwwF?`%DjxAWmI0#v$`(Zq{(V0D}W z2(!!%c3(I`N;J0CCk_AgP~Y4zn@f-a-g&-ZdnbMu?XA;&L}=9`6ImYbdgFwsG``Vq zIl{n?vq^4~JQ#8w=q<+PiAQm%Bwx~%9hUFOLzPYO8FU!4mS@fMn6Q?akKb*;C1tQd;oPnEM<~&iNmWd?i#QyvYB7(vhfAe$x}Esa7vHe( z)`M2wsrdipF8L$kDx>(_AT7yfpz%oJNc{5Q)*kq$-x6P*9v2I+FgFf{S|-xX7MwK* z^9-Z8jVOW&@=9@h(hB1EAsmT|d17?EL=PSw2KpUcc2#8=MStEr`Iqu4IbYH>BnI}vt(&EG?aR}nT8Wd>1Bh_`Z zxu8$775f&dSyTX5bkg1J57#Ej{d;Hp!wC?LBXNtf@h& z3T)7xu~YSYnwJsT#^Y&;Wkiv!UGu{J-I2GRJjaGX)oGSZ_Kd%xA@R!}^=Q4q785;g z4xT$$kgj{dE%Uf3t(!JQ2HKg%>JYV@$yv(+gvceA`4O))?REbENGX0x)_dbB%pi66<;TVwIA7Z*s=b#EJrWKsZ2qj*_eXk|VYw1Z z3nx0(>#{&At431O@ykt0$N_9#;fnQOC$XQZ^hQTXw+AFtkd-eo*!V63SC918sY%*@ zk-Uu61ILgT$y00e@9aM1H{=?71};Wnjng?iv8Xez{NnCkkK@UJZMWuAi(>QHwz0ff%9C?i5^>k+G?fvHXk!O_`(+N$ew?7keD8UB|G(EYZx37_f<%JrQ`lKHZz5cBbFJ%_n3pzFS10H1Vd&}FOG3}ljfE@PafE` zJNMY}3`WWu-rkf*MQ-Kz{P`+ri~DE8$q^oX=G$toFek zN?zu9wnHF(A{9(fX1{UtXS0BG_fT>Y7}iTTCuKck=3-S+av@A?#Jb3AJ3JtE4712# z-NO-HJmZgLpAvssCh`3@QF(+Rc)@BZM+;1o)f7IvK3_WPA!xRi14*tgmm{OMXqZ5L(DFT3#ktOVJO%XT=?TFexCNb0M}f zpe>f?dw!fO+2=;8C0q;v#eVU=a9w-I;&U{cGPq_TC*|vyA(w{qN^GU;Z zVc&*r?Z*_uDIXvhn)V@2nr40%?11Z6w+}T%R~$ZchmE2fM_IX0mC#Or*RM~FAf+}2 z3C%8rKJw)qkdQ%oIOjwGcA~N_VzsJ%kRDfVcH_DykS-nikd3XwXi&Ul6|%R}nCoU@l2%*%CaYe+jRjeI5J z;7r*#M(a09DrV9FnfMDKN^};F_DMAi)g$rNZvM)R-ijQ1)%E@lj1d341Z+pO%tw5S z#vj})?L9a7LjrklFr~HQ0{dfgjGH#Z8F&=$2EkY;zj<#uO7Fu-jg^s6rIoQ*X)KCN kC)Jgl7IyDt1M(=32|CI*DI7{v>LBo^D2GCp%9sWI59Y;SdH?_b literal 0 HcmV?d00001 diff --git a/pkg/webui/ui/build/manifest.json b/pkg/webui/ui/build/manifest.json new file mode 100644 index 000000000..080d6c77a --- /dev/null +++ b/pkg/webui/ui/build/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/pkg/webui/ui/build/robots.txt b/pkg/webui/ui/build/robots.txt new file mode 100644 index 000000000..e9e57dc4d --- /dev/null +++ b/pkg/webui/ui/build/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/pkg/webui/ui/build/static/css/main.css b/pkg/webui/ui/build/static/css/main.css new file mode 100644 index 000000000..a8874843f --- /dev/null +++ b/pkg/webui/ui/build/static/css/main.css @@ -0,0 +1,87 @@ +body { + margin: 0; + background: linear-gradient(180deg, #59A588 0%, #404846 100%); + background-attachment: fixed; +} + +body, input { + font-family: 'Nunito Variable', 'Segoe UI', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', -apple-system, BlinkMacSystemFont, + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} + +* { + box-sizing: border-box; +} + +html, +body, +#root { + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} +/* nunito-cyrillic-ext-wght-normal */ +@font-face { + font-family: 'Nunito Variable'; + font-style: normal; + font-display: swap; + font-display: var(--fontsource-display, swap); + font-weight: 200 1000; + src: url(../../static/media/nunito-cyrillic-ext-wght-normal.woff2?f=nunito-cyrillic-ext-wght-normal.c18a3502d6473272bdc3.woff2) format('woff2-variations'); + unicode-range: U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F; +} + +/* nunito-cyrillic-wght-normal */ +@font-face { + font-family: 'Nunito Variable'; + font-style: normal; + font-display: swap; + font-display: var(--fontsource-display, swap); + font-weight: 200 1000; + src: url(../../static/media/nunito-cyrillic-wght-normal.woff2?f=nunito-cyrillic-wght-normal.ab3faa41df1758ee5b88.woff2) format('woff2-variations'); + unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; +} + +/* nunito-vietnamese-wght-normal */ +@font-face { + font-family: 'Nunito Variable'; + font-style: normal; + font-display: swap; + font-display: var(--fontsource-display, swap); + font-weight: 200 1000; + src: url(../../static/media/nunito-vietnamese-wght-normal.woff2?f=nunito-vietnamese-wght-normal.864aa311404227008fae.woff2) format('woff2-variations'); + unicode-range: U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB; +} + +/* nunito-latin-ext-wght-normal */ +@font-face { + font-family: 'Nunito Variable'; + font-style: normal; + font-display: swap; + font-display: var(--fontsource-display, swap); + font-weight: 200 1000; + src: url(../../static/media/nunito-latin-ext-wght-normal.woff2?f=nunito-latin-ext-wght-normal.b2120f41c4a82a277229.woff2) format('woff2-variations'); + unicode-range: U+0100-02AF,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF; +} + +/* nunito-latin-wght-normal */ +@font-face { + font-family: 'Nunito Variable'; + font-style: normal; + font-display: swap; + font-display: var(--fontsource-display, swap); + font-weight: 200 1000; + src: url(../../static/media/nunito-latin-wght-normal.woff2?f=nunito-latin-wght-normal.07adf0b2f5cb89c62ba7.woff2) format('woff2-variations'); + unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; +} + +/*# sourceMappingURL=main.9c2b54a7.css.map*/ \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/787.chunk.js b/pkg/webui/ui/build/static/js/787.chunk.js new file mode 100644 index 000000000..748939666 --- /dev/null +++ b/pkg/webui/ui/build/static/js/787.chunk.js @@ -0,0 +1,239 @@ +"use strict"; +(self["webpackChunkkluctl_webui"] = self["webpackChunkkluctl_webui"] || []).push([[787],{ + +/***/ 787: +/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ getCLS: function() { return /* binding */ h; }, +/* harmony export */ getFCP: function() { return /* binding */ d; }, +/* harmony export */ getFID: function() { return /* binding */ L; }, +/* harmony export */ getLCP: function() { return /* binding */ F; }, +/* harmony export */ getTTFB: function() { return /* binding */ P; } +/* harmony export */ }); +var e, + t, + n, + i, + r = function r(e, t) { + return { + name: e, + value: void 0 === t ? -1 : t, + delta: 0, + entries: [], + id: "v2-".concat(Date.now(), "-").concat(Math.floor(8999999999999 * Math.random()) + 1e12) + }; + }, + a = function a(e, t) { + try { + if (PerformanceObserver.supportedEntryTypes.includes(e)) { + if ("first-input" === e && !("PerformanceEventTiming" in self)) return; + var n = new PerformanceObserver(function (e) { + return e.getEntries().map(t); + }); + return n.observe({ + type: e, + buffered: !0 + }), n; + } + } catch (e) {} + }, + o = function o(e, t) { + var n = function n(i) { + "pagehide" !== i.type && "hidden" !== document.visibilityState || (e(i), t && (removeEventListener("visibilitychange", n, !0), removeEventListener("pagehide", n, !0))); + }; + addEventListener("visibilitychange", n, !0), addEventListener("pagehide", n, !0); + }, + u = function u(e) { + addEventListener("pageshow", function (t) { + t.persisted && e(t); + }, !0); + }, + c = function c(e, t, n) { + var i; + return function (r) { + t.value >= 0 && (r || n) && (t.delta = t.value - (i || 0), (t.delta || void 0 === i) && (i = t.value, e(t))); + }; + }, + f = -1, + s = function s() { + return "hidden" === document.visibilityState ? 0 : 1 / 0; + }, + m = function m() { + o(function (e) { + var t = e.timeStamp; + f = t; + }, !0); + }, + v = function v() { + return f < 0 && (f = s(), m(), u(function () { + setTimeout(function () { + f = s(), m(); + }, 0); + })), { + get firstHiddenTime() { + return f; + } + }; + }, + d = function d(e, t) { + var n, + i = v(), + o = r("FCP"), + f = function f(e) { + "first-contentful-paint" === e.name && (m && m.disconnect(), e.startTime < i.firstHiddenTime && (o.value = e.startTime, o.entries.push(e), n(!0))); + }, + s = window.performance && performance.getEntriesByName && performance.getEntriesByName("first-contentful-paint")[0], + m = s ? null : a("paint", f); + (s || m) && (n = c(e, o, t), s && f(s), u(function (i) { + o = r("FCP"), n = c(e, o, t), requestAnimationFrame(function () { + requestAnimationFrame(function () { + o.value = performance.now() - i.timeStamp, n(!0); + }); + }); + })); + }, + p = !1, + l = -1, + h = function h(e, t) { + p || (d(function (e) { + l = e.value; + }), p = !0); + var n, + i = function i(t) { + l > -1 && e(t); + }, + f = r("CLS", 0), + s = 0, + m = [], + v = function v(e) { + if (!e.hadRecentInput) { + var t = m[0], + i = m[m.length - 1]; + s && e.startTime - i.startTime < 1e3 && e.startTime - t.startTime < 5e3 ? (s += e.value, m.push(e)) : (s = e.value, m = [e]), s > f.value && (f.value = s, f.entries = m, n()); + } + }, + h = a("layout-shift", v); + h && (n = c(i, f, t), o(function () { + h.takeRecords().map(v), n(!0); + }), u(function () { + s = 0, l = -1, f = r("CLS", 0), n = c(i, f, t); + })); + }, + T = { + passive: !0, + capture: !0 + }, + y = new Date(), + g = function g(i, r) { + e || (e = r, t = i, n = new Date(), w(removeEventListener), E()); + }, + E = function E() { + if (t >= 0 && t < n - y) { + var r = { + entryType: "first-input", + name: e.type, + target: e.target, + cancelable: e.cancelable, + startTime: e.timeStamp, + processingStart: e.timeStamp + t + }; + i.forEach(function (e) { + e(r); + }), i = []; + } + }, + S = function S(e) { + if (e.cancelable) { + var t = (e.timeStamp > 1e12 ? new Date() : performance.now()) - e.timeStamp; + "pointerdown" == e.type ? function (e, t) { + var n = function n() { + g(e, t), r(); + }, + i = function i() { + r(); + }, + r = function r() { + removeEventListener("pointerup", n, T), removeEventListener("pointercancel", i, T); + }; + addEventListener("pointerup", n, T), addEventListener("pointercancel", i, T); + }(t, e) : g(t, e); + } + }, + w = function w(e) { + ["mousedown", "keydown", "touchstart", "pointerdown"].forEach(function (t) { + return e(t, S, T); + }); + }, + L = function L(n, f) { + var s, + m = v(), + d = r("FID"), + p = function p(e) { + e.startTime < m.firstHiddenTime && (d.value = e.processingStart - e.startTime, d.entries.push(e), s(!0)); + }, + l = a("first-input", p); + s = c(n, d, f), l && o(function () { + l.takeRecords().map(p), l.disconnect(); + }, !0), l && u(function () { + var a; + d = r("FID"), s = c(n, d, f), i = [], t = -1, e = null, w(addEventListener), a = p, i.push(a), E(); + }); + }, + b = {}, + F = function F(e, t) { + var n, + i = v(), + f = r("LCP"), + s = function s(e) { + var t = e.startTime; + t < i.firstHiddenTime && (f.value = t, f.entries.push(e), n()); + }, + m = a("largest-contentful-paint", s); + if (m) { + n = c(e, f, t); + var d = function d() { + b[f.id] || (m.takeRecords().map(s), m.disconnect(), b[f.id] = !0, n(!0)); + }; + ["keydown", "click"].forEach(function (e) { + addEventListener(e, d, { + once: !0, + capture: !0 + }); + }), o(d, !0), u(function (i) { + f = r("LCP"), n = c(e, f, t), requestAnimationFrame(function () { + requestAnimationFrame(function () { + f.value = performance.now() - i.timeStamp, b[f.id] = !0, n(!0); + }); + }); + }); + } + }, + P = function P(e) { + var t, + n = r("TTFB"); + t = function t() { + try { + var t = performance.getEntriesByType("navigation")[0] || function () { + var e = performance.timing, + t = { + entryType: "navigation", + startTime: 0 + }; + for (var n in e) "navigationStart" !== n && "toJSON" !== n && (t[n] = Math.max(e[n] - e.navigationStart, 0)); + return t; + }(); + if (n.value = n.delta = t.responseStart, n.value < 0 || n.value > performance.now()) return; + n.entries = [t], e(n); + } catch (e) {} + }, "complete" === document.readyState ? setTimeout(t, 0) : addEventListener("load", function () { + return setTimeout(t, 0); + }); + }; + + +/***/ }) + +}]); +//# sourceMappingURL=787.98a1313e.chunk.js.map \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js new file mode 100644 index 000000000..d5043f933 --- /dev/null +++ b/pkg/webui/ui/build/static/js/main.js @@ -0,0 +1,60971 @@ +/******/ (function() { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ 867: +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + +"use strict"; + + +var formatter = __webpack_require__(707); +var fault = create(Error); +module.exports = fault; +fault.eval = create(EvalError); +fault.range = create(RangeError); +fault.reference = create(ReferenceError); +fault.syntax = create(SyntaxError); +fault.type = create(TypeError); +fault.uri = create(URIError); +fault.create = create; + +// Create a new `EConstructor`, with the formatted `format` as a first argument. +function create(EConstructor) { + FormattedError.displayName = EConstructor.displayName || EConstructor.name; + return FormattedError; + function FormattedError(format) { + if (format) { + format = formatter.apply(null, arguments); + } + return new EConstructor(format); + } +} + +/***/ }), + +/***/ 707: +/***/ (function(module) { + +// +// format - printf-like string formatting for JavaScript +// github.com/samsonjs/format +// @_sjs +// +// Copyright 2010 - 2013 Sami Samhuri +// +// MIT License +// http://sjs.mit-license.org +// + +; +(function () { + //// Export the API + var namespace; + + // CommonJS / Node module + if (true) { + namespace = module.exports = format; + } + + // Browsers and other environments + else {} + namespace.format = format; + namespace.vsprintf = vsprintf; + if (typeof console !== 'undefined' && typeof console.log === 'function') { + namespace.printf = printf; + } + function printf( /* ... */ + ) { + console.log(format.apply(null, arguments)); + } + function vsprintf(fmt, replacements) { + return format.apply(null, [fmt].concat(replacements)); + } + function format(fmt) { + var argIndex = 1 // skip initial format argument + , + args = [].slice.call(arguments), + i = 0, + n = fmt.length, + result = '', + c, + escaped = false, + arg, + tmp, + leadingZero = false, + precision, + nextArg = function nextArg() { + return args[argIndex++]; + }, + slurpNumber = function slurpNumber() { + var digits = ''; + while (/\d/.test(fmt[i])) { + digits += fmt[i++]; + c = fmt[i]; + } + return digits.length > 0 ? parseInt(digits) : null; + }; + for (; i < n; ++i) { + c = fmt[i]; + if (escaped) { + escaped = false; + if (c == '.') { + leadingZero = false; + c = fmt[++i]; + } else if (c == '0' && fmt[i + 1] == '.') { + leadingZero = true; + i += 2; + c = fmt[i]; + } else { + leadingZero = true; + } + precision = slurpNumber(); + switch (c) { + case 'b': + // number in binary + result += parseInt(nextArg(), 10).toString(2); + break; + case 'c': + // character + arg = nextArg(); + if (typeof arg === 'string' || arg instanceof String) result += arg;else result += String.fromCharCode(parseInt(arg, 10)); + break; + case 'd': + // number in decimal + result += parseInt(nextArg(), 10); + break; + case 'f': + // floating point number + tmp = String(parseFloat(nextArg()).toFixed(precision || 6)); + result += leadingZero ? tmp : tmp.replace(/^0/, ''); + break; + case 'j': + // JSON + result += JSON.stringify(nextArg()); + break; + case 'o': + // number in octal + result += '0' + parseInt(nextArg(), 10).toString(8); + break; + case 's': + // string + result += nextArg(); + break; + case 'x': + // lowercase hexadecimal + result += '0x' + parseInt(nextArg(), 10).toString(16); + break; + case 'X': + // uppercase hexadecimal + result += '0x' + parseInt(nextArg(), 10).toString(16).toUpperCase(); + break; + default: + result += c; + break; + } + } else if (c === '%') { + escaped = true; + } else { + result += c; + } + } + return result; + } +})(); + +/***/ }), + +/***/ 478: +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + +var _slicedToArray = (__webpack_require__(424)["default"]); +var _toConsumableArray = (__webpack_require__(861)["default"]); +var _inherits = (__webpack_require__(655)["default"]); +var _createSuper = (__webpack_require__(389)["default"]); +var _classCallCheck = (__webpack_require__(690)["default"]); +var _createClass = (__webpack_require__(728)["default"]); +function deepFreeze(obj) { + if (obj instanceof Map) { + obj.clear = obj.delete = obj.set = function () { + throw new Error('map is read-only'); + }; + } else if (obj instanceof Set) { + obj.add = obj.clear = obj.delete = function () { + throw new Error('set is read-only'); + }; + } + + // Freeze self + Object.freeze(obj); + Object.getOwnPropertyNames(obj).forEach(function (name) { + var prop = obj[name]; + + // Freeze prop if it is an object + if (typeof prop == 'object' && !Object.isFrozen(prop)) { + deepFreeze(prop); + } + }); + return obj; +} +var deepFreezeEs6 = deepFreeze; +var _default = deepFreeze; +deepFreezeEs6.default = _default; + +/** @implements CallbackResponse */ +var Response = /*#__PURE__*/function () { + "use strict"; + + /** + * @param {CompiledMode} mode + */ + function Response(mode) { + _classCallCheck(this, Response); + // eslint-disable-next-line no-undefined + if (mode.data === undefined) mode.data = {}; + this.data = mode.data; + this.isMatchIgnored = false; + } + _createClass(Response, [{ + key: "ignoreMatch", + value: function ignoreMatch() { + this.isMatchIgnored = true; + } + }]); + return Response; +}(); +/** + * @param {string} value + * @returns {string} + */ +function escapeHTML(value) { + return value.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); +} + +/** + * performs a shallow merge of multiple objects into one + * + * @template T + * @param {T} original + * @param {Record[]} objects + * @returns {T} a single new object + */ +function inherit(original) { + /** @type Record */ + var result = Object.create(null); + for (var key in original) { + result[key] = original[key]; + } + for (var _len = arguments.length, objects = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + objects[_key - 1] = arguments[_key]; + } + objects.forEach(function (obj) { + for (var _key2 in obj) { + result[_key2] = obj[_key2]; + } + }); + return (/** @type {T} */result + ); +} + +/** + * @typedef {object} Renderer + * @property {(text: string) => void} addText + * @property {(node: Node) => void} openNode + * @property {(node: Node) => void} closeNode + * @property {() => string} value + */ + +/** @typedef {{kind?: string, sublanguage?: boolean}} Node */ +/** @typedef {{walk: (r: Renderer) => void}} Tree */ +/** */ + +var SPAN_CLOSE = ''; + +/** + * Determines if a node needs to be wrapped in + * + * @param {Node} node */ +var emitsWrappingTags = function emitsWrappingTags(node) { + return !!node.kind; +}; + +/** @type {Renderer} */ +var HTMLRenderer = /*#__PURE__*/function () { + "use strict"; + + /** + * Creates a new HTMLRenderer + * + * @param {Tree} parseTree - the parse tree (must support `walk` API) + * @param {{classPrefix: string}} options + */ + function HTMLRenderer(parseTree, options) { + _classCallCheck(this, HTMLRenderer); + this.buffer = ""; + this.classPrefix = options.classPrefix; + parseTree.walk(this); + } + + /** + * Adds texts to the output stream + * + * @param {string} text */ + _createClass(HTMLRenderer, [{ + key: "addText", + value: function addText(text) { + this.buffer += escapeHTML(text); + } + + /** + * Adds a node open to the output stream (if needed) + * + * @param {Node} node */ + }, { + key: "openNode", + value: function openNode(node) { + if (!emitsWrappingTags(node)) return; + var className = node.kind; + if (!node.sublanguage) { + className = "".concat(this.classPrefix).concat(className); + } + this.span(className); + } + + /** + * Adds a node close to the output stream (if needed) + * + * @param {Node} node */ + }, { + key: "closeNode", + value: function closeNode(node) { + if (!emitsWrappingTags(node)) return; + this.buffer += SPAN_CLOSE; + } + + /** + * returns the accumulated buffer + */ + }, { + key: "value", + value: function value() { + return this.buffer; + } + + // helpers + + /** + * Builds a span element + * + * @param {string} className */ + }, { + key: "span", + value: function span(className) { + this.buffer += ""); + } + }]); + return HTMLRenderer; +}(); +/** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} | string} Node */ +/** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} } DataNode */ +/** */ +var TokenTree = /*#__PURE__*/function () { + "use strict"; + + function TokenTree() { + _classCallCheck(this, TokenTree); + /** @type DataNode */ + this.rootNode = { + children: [] + }; + this.stack = [this.rootNode]; + } + _createClass(TokenTree, [{ + key: "top", + get: function get() { + return this.stack[this.stack.length - 1]; + } + }, { + key: "root", + get: function get() { + return this.rootNode; + } + + /** @param {Node} node */ + }, { + key: "add", + value: function add(node) { + this.top.children.push(node); + } + + /** @param {string} kind */ + }, { + key: "openNode", + value: function openNode(kind) { + /** @type Node */ + var node = { + kind: kind, + children: [] + }; + this.add(node); + this.stack.push(node); + } + }, { + key: "closeNode", + value: function closeNode() { + if (this.stack.length > 1) { + return this.stack.pop(); + } + // eslint-disable-next-line no-undefined + return undefined; + } + }, { + key: "closeAllNodes", + value: function closeAllNodes() { + while (this.closeNode()); + } + }, { + key: "toJSON", + value: function toJSON() { + return JSON.stringify(this.rootNode, null, 4); + } + + /** + * @typedef { import("./html_renderer").Renderer } Renderer + * @param {Renderer} builder + */ + }, { + key: "walk", + value: function walk(builder) { + // this does not + return this.constructor._walk(builder, this.rootNode); + // this works + // return TokenTree._walk(builder, this.rootNode); + } + + /** + * @param {Renderer} builder + * @param {Node} node + */ + }], [{ + key: "_walk", + value: function _walk(builder, node) { + var _this = this; + if (typeof node === "string") { + builder.addText(node); + } else if (node.children) { + builder.openNode(node); + node.children.forEach(function (child) { + return _this._walk(builder, child); + }); + builder.closeNode(node); + } + return builder; + } + + /** + * @param {Node} node + */ + }, { + key: "_collapse", + value: function _collapse(node) { + if (typeof node === "string") return; + if (!node.children) return; + if (node.children.every(function (el) { + return typeof el === "string"; + })) { + // node.text = node.children.join(""); + // delete node.children; + node.children = [node.children.join("")]; + } else { + node.children.forEach(function (child) { + TokenTree._collapse(child); + }); + } + } + }]); + return TokenTree; +}(); +/** + Currently this is all private API, but this is the minimal API necessary + that an Emitter must implement to fully support the parser. + + Minimal interface: + + - addKeyword(text, kind) + - addText(text) + - addSublanguage(emitter, subLanguageName) + - finalize() + - openNode(kind) + - closeNode() + - closeAllNodes() + - toHTML() + +*/ +/** + * @implements {Emitter} + */ +var TokenTreeEmitter = /*#__PURE__*/function (_TokenTree) { + "use strict"; + + _inherits(TokenTreeEmitter, _TokenTree); + var _super = _createSuper(TokenTreeEmitter); + /** + * @param {*} options + */ + function TokenTreeEmitter(options) { + var _this2; + _classCallCheck(this, TokenTreeEmitter); + _this2 = _super.call(this); + _this2.options = options; + return _this2; + } + + /** + * @param {string} text + * @param {string} kind + */ + _createClass(TokenTreeEmitter, [{ + key: "addKeyword", + value: function addKeyword(text, kind) { + if (text === "") { + return; + } + this.openNode(kind); + this.addText(text); + this.closeNode(); + } + + /** + * @param {string} text + */ + }, { + key: "addText", + value: function addText(text) { + if (text === "") { + return; + } + this.add(text); + } + + /** + * @param {Emitter & {root: DataNode}} emitter + * @param {string} name + */ + }, { + key: "addSublanguage", + value: function addSublanguage(emitter, name) { + /** @type DataNode */ + var node = emitter.root; + node.kind = name; + node.sublanguage = true; + this.add(node); + } + }, { + key: "toHTML", + value: function toHTML() { + var renderer = new HTMLRenderer(this, this.options); + return renderer.value(); + } + }, { + key: "finalize", + value: function finalize() { + return true; + } + }]); + return TokenTreeEmitter; +}(TokenTree); +/** + * @param {string} value + * @returns {RegExp} + * */ +function escape(value) { + return new RegExp(value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm'); +} + +/** + * @param {RegExp | string } re + * @returns {string} + */ +function source(re) { + if (!re) return null; + if (typeof re === "string") return re; + return re.source; +} + +/** + * @param {...(RegExp | string) } args + * @returns {string} + */ +function concat() { + for (var _len2 = arguments.length, args = new Array(_len2), _key3 = 0; _key3 < _len2; _key3++) { + args[_key3] = arguments[_key3]; + } + var joined = args.map(function (x) { + return source(x); + }).join(""); + return joined; +} + +/** + * Any of the passed expresssions may match + * + * Creates a huge this | this | that | that match + * @param {(RegExp | string)[] } args + * @returns {string} + */ +function either() { + for (var _len3 = arguments.length, args = new Array(_len3), _key4 = 0; _key4 < _len3; _key4++) { + args[_key4] = arguments[_key4]; + } + var joined = '(' + args.map(function (x) { + return source(x); + }).join("|") + ")"; + return joined; +} + +/** + * @param {RegExp} re + * @returns {number} + */ +function countMatchGroups(re) { + return new RegExp(re.toString() + '|').exec('').length - 1; +} + +/** + * Does lexeme start with a regular expression match at the beginning + * @param {RegExp} re + * @param {string} lexeme + */ +function startsWith(re, lexeme) { + var match = re && re.exec(lexeme); + return match && match.index === 0; +} + +// BACKREF_RE matches an open parenthesis or backreference. To avoid +// an incorrect parse, it additionally matches the following: +// - [...] elements, where the meaning of parentheses and escapes change +// - other escape sequences, so we do not misparse escape sequences as +// interesting elements +// - non-matching or lookahead parentheses, which do not capture. These +// follow the '(' with a '?'. +var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./; + +// join logically computes regexps.join(separator), but fixes the +// backreferences so they continue to match. +// it also places each individual regular expression into it's own +// match group, keeping track of the sequencing of those match groups +// is currently an exercise for the caller. :-) +/** + * @param {(string | RegExp)[]} regexps + * @param {string} separator + * @returns {string} + */ +function join(regexps) { + var separator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "|"; + var numCaptures = 0; + return regexps.map(function (regex) { + numCaptures += 1; + var offset = numCaptures; + var re = source(regex); + var out = ''; + while (re.length > 0) { + var match = BACKREF_RE.exec(re); + if (!match) { + out += re; + break; + } + out += re.substring(0, match.index); + re = re.substring(match.index + match[0].length); + if (match[0][0] === '\\' && match[1]) { + // Adjust the backreference. + out += '\\' + String(Number(match[1]) + offset); + } else { + out += match[0]; + if (match[0] === '(') { + numCaptures++; + } + } + } + return out; + }).map(function (re) { + return "(".concat(re, ")"); + }).join(separator); +} + +// Common regexps +var MATCH_NOTHING_RE = /\b\B/; +var IDENT_RE = '[a-zA-Z]\\w*'; +var UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*'; +var NUMBER_RE = '\\b\\d+(\\.\\d+)?'; +var C_NUMBER_RE = '(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float +var BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b... +var RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~'; + +/** +* @param { Partial & {binary?: string | RegExp} } opts +*/ +var SHEBANG = function SHEBANG() { + var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var beginShebang = /^#![ ]*\//; + if (opts.binary) { + opts.begin = concat(beginShebang, /.*\b/, opts.binary, /\b.*/); + } + return inherit({ + className: 'meta', + begin: beginShebang, + end: /$/, + relevance: 0, + /** @type {ModeCallback} */ + "on:begin": function onBegin(m, resp) { + if (m.index !== 0) resp.ignoreMatch(); + } + }, opts); +}; + +// Common modes +var BACKSLASH_ESCAPE = { + begin: '\\\\[\\s\\S]', + relevance: 0 +}; +var APOS_STRING_MODE = { + className: 'string', + begin: '\'', + end: '\'', + illegal: '\\n', + contains: [BACKSLASH_ESCAPE] +}; +var QUOTE_STRING_MODE = { + className: 'string', + begin: '"', + end: '"', + illegal: '\\n', + contains: [BACKSLASH_ESCAPE] +}; +var PHRASAL_WORDS_MODE = { + begin: /\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +}; +/** + * Creates a comment mode + * + * @param {string | RegExp} begin + * @param {string | RegExp} end + * @param {Mode | {}} [modeOptions] + * @returns {Partial} + */ +var COMMENT = function COMMENT(begin, end) { + var modeOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + var mode = inherit({ + className: 'comment', + begin: begin, + end: end, + contains: [] + }, modeOptions); + mode.contains.push(PHRASAL_WORDS_MODE); + mode.contains.push({ + className: 'doctag', + begin: '(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):', + relevance: 0 + }); + return mode; +}; +var C_LINE_COMMENT_MODE = COMMENT('//', '$'); +var C_BLOCK_COMMENT_MODE = COMMENT('/\\*', '\\*/'); +var HASH_COMMENT_MODE = COMMENT('#', '$'); +var NUMBER_MODE = { + className: 'number', + begin: NUMBER_RE, + relevance: 0 +}; +var C_NUMBER_MODE = { + className: 'number', + begin: C_NUMBER_RE, + relevance: 0 +}; +var BINARY_NUMBER_MODE = { + className: 'number', + begin: BINARY_NUMBER_RE, + relevance: 0 +}; +var CSS_NUMBER_MODE = { + className: 'number', + begin: NUMBER_RE + '(' + '%|em|ex|ch|rem' + '|vw|vh|vmin|vmax' + '|cm|mm|in|pt|pc|px' + '|deg|grad|rad|turn' + '|s|ms' + '|Hz|kHz' + '|dpi|dpcm|dppx' + ')?', + relevance: 0 +}; +var REGEXP_MODE = { + // this outer rule makes sure we actually have a WHOLE regex and not simply + // an expression such as: + // + // 3 / something + // + // (which will then blow up when regex's `illegal` sees the newline) + begin: /(?=\/[^/\n]*\/)/, + contains: [{ + className: 'regexp', + begin: /\//, + end: /\/[gimuy]*/, + illegal: /\n/, + contains: [BACKSLASH_ESCAPE, { + begin: /\[/, + end: /\]/, + relevance: 0, + contains: [BACKSLASH_ESCAPE] + }] + }] +}; +var TITLE_MODE = { + className: 'title', + begin: IDENT_RE, + relevance: 0 +}; +var UNDERSCORE_TITLE_MODE = { + className: 'title', + begin: UNDERSCORE_IDENT_RE, + relevance: 0 +}; +var METHOD_GUARD = { + // excludes method names from keyword processing + begin: '\\.\\s*' + UNDERSCORE_IDENT_RE, + relevance: 0 +}; + +/** + * Adds end same as begin mechanics to a mode + * + * Your mode must include at least a single () match group as that first match + * group is what is used for comparison + * @param {Partial} mode + */ +var END_SAME_AS_BEGIN = function END_SAME_AS_BEGIN(mode) { + return Object.assign(mode, { + /** @type {ModeCallback} */ + 'on:begin': function onBegin(m, resp) { + resp.data._beginMatch = m[1]; + }, + /** @type {ModeCallback} */ + 'on:end': function onEnd(m, resp) { + if (resp.data._beginMatch !== m[1]) resp.ignoreMatch(); + } + }); +}; +var MODES = /*#__PURE__*/Object.freeze({ + __proto__: null, + MATCH_NOTHING_RE: MATCH_NOTHING_RE, + IDENT_RE: IDENT_RE, + UNDERSCORE_IDENT_RE: UNDERSCORE_IDENT_RE, + NUMBER_RE: NUMBER_RE, + C_NUMBER_RE: C_NUMBER_RE, + BINARY_NUMBER_RE: BINARY_NUMBER_RE, + RE_STARTERS_RE: RE_STARTERS_RE, + SHEBANG: SHEBANG, + BACKSLASH_ESCAPE: BACKSLASH_ESCAPE, + APOS_STRING_MODE: APOS_STRING_MODE, + QUOTE_STRING_MODE: QUOTE_STRING_MODE, + PHRASAL_WORDS_MODE: PHRASAL_WORDS_MODE, + COMMENT: COMMENT, + C_LINE_COMMENT_MODE: C_LINE_COMMENT_MODE, + C_BLOCK_COMMENT_MODE: C_BLOCK_COMMENT_MODE, + HASH_COMMENT_MODE: HASH_COMMENT_MODE, + NUMBER_MODE: NUMBER_MODE, + C_NUMBER_MODE: C_NUMBER_MODE, + BINARY_NUMBER_MODE: BINARY_NUMBER_MODE, + CSS_NUMBER_MODE: CSS_NUMBER_MODE, + REGEXP_MODE: REGEXP_MODE, + TITLE_MODE: TITLE_MODE, + UNDERSCORE_TITLE_MODE: UNDERSCORE_TITLE_MODE, + METHOD_GUARD: METHOD_GUARD, + END_SAME_AS_BEGIN: END_SAME_AS_BEGIN +}); + +// Grammar extensions / plugins +// See: https://github.com/highlightjs/highlight.js/issues/2833 + +// Grammar extensions allow "syntactic sugar" to be added to the grammar modes +// without requiring any underlying changes to the compiler internals. + +// `compileMatch` being the perfect small example of now allowing a grammar +// author to write `match` when they desire to match a single expression rather +// than being forced to use `begin`. The extension then just moves `match` into +// `begin` when it runs. Ie, no features have been added, but we've just made +// the experience of writing (and reading grammars) a little bit nicer. + +// ------ + +// TODO: We need negative look-behind support to do this properly +/** + * Skip a match if it has a preceding dot + * + * This is used for `beginKeywords` to prevent matching expressions such as + * `bob.keyword.do()`. The mode compiler automatically wires this up as a + * special _internal_ 'on:begin' callback for modes with `beginKeywords` + * @param {RegExpMatchArray} match + * @param {CallbackResponse} response + */ +function skipIfhasPrecedingDot(match, response) { + var before = match.input[match.index - 1]; + if (before === ".") { + response.ignoreMatch(); + } +} + +/** + * `beginKeywords` syntactic sugar + * @type {CompilerExt} + */ +function beginKeywords(mode, parent) { + if (!parent) return; + if (!mode.beginKeywords) return; + + // for languages with keywords that include non-word characters checking for + // a word boundary is not sufficient, so instead we check for a word boundary + // or whitespace - this does no harm in any case since our keyword engine + // doesn't allow spaces in keywords anyways and we still check for the boundary + // first + mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?!\\.)(?=\\b|\\s)'; + mode.__beforeBegin = skipIfhasPrecedingDot; + mode.keywords = mode.keywords || mode.beginKeywords; + delete mode.beginKeywords; + + // prevents double relevance, the keywords themselves provide + // relevance, the mode doesn't need to double it + // eslint-disable-next-line no-undefined + if (mode.relevance === undefined) mode.relevance = 0; +} + +/** + * Allow `illegal` to contain an array of illegal values + * @type {CompilerExt} + */ +function compileIllegal(mode, _parent) { + if (!Array.isArray(mode.illegal)) return; + mode.illegal = either.apply(void 0, _toConsumableArray(mode.illegal)); +} + +/** + * `match` to match a single expression for readability + * @type {CompilerExt} + */ +function compileMatch(mode, _parent) { + if (!mode.match) return; + if (mode.begin || mode.end) throw new Error("begin & end are not supported with match"); + mode.begin = mode.match; + delete mode.match; +} + +/** + * provides the default 1 relevance to all modes + * @type {CompilerExt} + */ +function compileRelevance(mode, _parent) { + // eslint-disable-next-line no-undefined + if (mode.relevance === undefined) mode.relevance = 1; +} + +// keywords that should have no default relevance value +var COMMON_KEYWORDS = ['of', 'and', 'for', 'in', 'not', 'or', 'if', 'then', 'parent', +// common variable name +'list', +// common variable name +'value' // common variable name +]; + +var DEFAULT_KEYWORD_CLASSNAME = "keyword"; + +/** + * Given raw keywords from a language definition, compile them. + * + * @param {string | Record | Array} rawKeywords + * @param {boolean} caseInsensitive + */ +function compileKeywords(rawKeywords, caseInsensitive) { + var className = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_KEYWORD_CLASSNAME; + /** @type KeywordDict */ + var compiledKeywords = {}; + + // input can be a string of keywords, an array of keywords, or a object with + // named keys representing className (which can then point to a string or array) + if (typeof rawKeywords === 'string') { + compileList(className, rawKeywords.split(" ")); + } else if (Array.isArray(rawKeywords)) { + compileList(className, rawKeywords); + } else { + Object.keys(rawKeywords).forEach(function (className) { + // collapse all our objects back into the parent object + Object.assign(compiledKeywords, compileKeywords(rawKeywords[className], caseInsensitive, className)); + }); + } + return compiledKeywords; + + // --- + + /** + * Compiles an individual list of keywords + * + * Ex: "for if when while|5" + * + * @param {string} className + * @param {Array} keywordList + */ + function compileList(className, keywordList) { + if (caseInsensitive) { + keywordList = keywordList.map(function (x) { + return x.toLowerCase(); + }); + } + keywordList.forEach(function (keyword) { + var pair = keyword.split('|'); + compiledKeywords[pair[0]] = [className, scoreForKeyword(pair[0], pair[1])]; + }); + } +} + +/** + * Returns the proper score for a given keyword + * + * Also takes into account comment keywords, which will be scored 0 UNLESS + * another score has been manually assigned. + * @param {string} keyword + * @param {string} [providedScore] + */ +function scoreForKeyword(keyword, providedScore) { + // manual scores always win over common keywords + // so you can force a score of 1 if you really insist + if (providedScore) { + return Number(providedScore); + } + return commonKeyword(keyword) ? 0 : 1; +} + +/** + * Determines if a given keyword is common or not + * + * @param {string} keyword */ +function commonKeyword(keyword) { + return COMMON_KEYWORDS.includes(keyword.toLowerCase()); +} + +// compilation + +/** + * Compiles a language definition result + * + * Given the raw result of a language definition (Language), compiles this so + * that it is ready for highlighting code. + * @param {Language} language + * @param {{plugins: HLJSPlugin[]}} opts + * @returns {CompiledLanguage} + */ +function compileLanguage(language, _ref) { + var plugins = _ref.plugins; + /** + * Builds a regex with the case sensativility of the current language + * + * @param {RegExp | string} value + * @param {boolean} [global] + */ + function langRe(value, global) { + return new RegExp(source(value), 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '')); + } + + /** + Stores multiple regular expressions and allows you to quickly search for + them all in a string simultaneously - returning the first match. It does + this by creating a huge (a|b|c) regex - each individual item wrapped with () + and joined by `|` - using match groups to track position. When a match is + found checking which position in the array has content allows us to figure + out which of the original regexes / match groups triggered the match. + The match object itself (the result of `Regex.exec`) is returned but also + enhanced by merging in any meta-data that was registered with the regex. + This is how we keep track of which mode matched, and what type of rule + (`illegal`, `begin`, end, etc). + */ + var MultiRegex = /*#__PURE__*/function () { + "use strict"; + + function MultiRegex() { + _classCallCheck(this, MultiRegex); + this.matchIndexes = {}; + // @ts-ignore + this.regexes = []; + this.matchAt = 1; + this.position = 0; + } + + // @ts-ignore + _createClass(MultiRegex, [{ + key: "addRule", + value: function addRule(re, opts) { + opts.position = this.position++; + // @ts-ignore + this.matchIndexes[this.matchAt] = opts; + this.regexes.push([opts, re]); + this.matchAt += countMatchGroups(re) + 1; + } + }, { + key: "compile", + value: function compile() { + if (this.regexes.length === 0) { + // avoids the need to check length every time exec is called + // @ts-ignore + this.exec = function () { + return null; + }; + } + var terminators = this.regexes.map(function (el) { + return el[1]; + }); + this.matcherRe = langRe(join(terminators), true); + this.lastIndex = 0; + } + + /** @param {string} s */ + }, { + key: "exec", + value: function exec(s) { + this.matcherRe.lastIndex = this.lastIndex; + var match = this.matcherRe.exec(s); + if (!match) { + return null; + } + + // eslint-disable-next-line no-undefined + var i = match.findIndex(function (el, i) { + return i > 0 && el !== undefined; + }); + // @ts-ignore + var matchData = this.matchIndexes[i]; + // trim off any earlier non-relevant match groups (ie, the other regex + // match groups that make up the multi-matcher) + match.splice(0, i); + return Object.assign(match, matchData); + } + }]); + return MultiRegex; + }(); + /* + Created to solve the key deficiently with MultiRegex - there is no way to + test for multiple matches at a single location. Why would we need to do + that? In the future a more dynamic engine will allow certain matches to be + ignored. An example: if we matched say the 3rd regex in a large group but + decided to ignore it - we'd need to started testing again at the 4th + regex... but MultiRegex itself gives us no real way to do that. + So what this class creates MultiRegexs on the fly for whatever search + position they are needed. + NOTE: These additional MultiRegex objects are created dynamically. For most + grammars most of the time we will never actually need anything more than the + first MultiRegex - so this shouldn't have too much overhead. + Say this is our search group, and we match regex3, but wish to ignore it. + regex1 | regex2 | regex3 | regex4 | regex5 ' ie, startAt = 0 + What we need is a new MultiRegex that only includes the remaining + possibilities: + regex4 | regex5 ' ie, startAt = 3 + This class wraps all that complexity up in a simple API... `startAt` decides + where in the array of expressions to start doing the matching. It + auto-increments, so if a match is found at position 2, then startAt will be + set to 3. If the end is reached startAt will return to 0. + MOST of the time the parser will be setting startAt manually to 0. + */ + var ResumableMultiRegex = /*#__PURE__*/function () { + "use strict"; + + function ResumableMultiRegex() { + _classCallCheck(this, ResumableMultiRegex); + // @ts-ignore + this.rules = []; + // @ts-ignore + this.multiRegexes = []; + this.count = 0; + this.lastIndex = 0; + this.regexIndex = 0; + } + + // @ts-ignore + _createClass(ResumableMultiRegex, [{ + key: "getMatcher", + value: function getMatcher(index) { + if (this.multiRegexes[index]) return this.multiRegexes[index]; + var matcher = new MultiRegex(); + this.rules.slice(index).forEach(function (_ref2) { + var _ref3 = _slicedToArray(_ref2, 2), + re = _ref3[0], + opts = _ref3[1]; + return matcher.addRule(re, opts); + }); + matcher.compile(); + this.multiRegexes[index] = matcher; + return matcher; + } + }, { + key: "resumingScanAtSamePosition", + value: function resumingScanAtSamePosition() { + return this.regexIndex !== 0; + } + }, { + key: "considerAll", + value: function considerAll() { + this.regexIndex = 0; + } + + // @ts-ignore + }, { + key: "addRule", + value: function addRule(re, opts) { + this.rules.push([re, opts]); + if (opts.type === "begin") this.count++; + } + + /** @param {string} s */ + }, { + key: "exec", + value: function exec(s) { + var m = this.getMatcher(this.regexIndex); + m.lastIndex = this.lastIndex; + var result = m.exec(s); + + // The following is because we have no easy way to say "resume scanning at the + // existing position but also skip the current rule ONLY". What happens is + // all prior rules are also skipped which can result in matching the wrong + // thing. Example of matching "booger": + + // our matcher is [string, "booger", number] + // + // ....booger.... + + // if "booger" is ignored then we'd really need a regex to scan from the + // SAME position for only: [string, number] but ignoring "booger" (if it + // was the first match), a simple resume would scan ahead who knows how + // far looking only for "number", ignoring potential string matches (or + // future "booger" matches that might be valid.) + + // So what we do: We execute two matchers, one resuming at the same + // position, but the second full matcher starting at the position after: + + // /--- resume first regex match here (for [number]) + // |/---- full match here for [string, "booger", number] + // vv + // ....booger.... + + // Which ever results in a match first is then used. So this 3-4 step + // process essentially allows us to say "match at this position, excluding + // a prior rule that was ignored". + // + // 1. Match "booger" first, ignore. Also proves that [string] does non match. + // 2. Resume matching for [number] + // 3. Match at index + 1 for [string, "booger", number] + // 4. If #2 and #3 result in matches, which came first? + if (this.resumingScanAtSamePosition()) { + if (result && result.index === this.lastIndex) ;else { + // use the second matcher result + var m2 = this.getMatcher(0); + m2.lastIndex = this.lastIndex + 1; + result = m2.exec(s); + } + } + if (result) { + this.regexIndex += result.position + 1; + if (this.regexIndex === this.count) { + // wrap-around to considering all matches again + this.considerAll(); + } + } + return result; + } + }]); + return ResumableMultiRegex; + }(); + /** + * Given a mode, builds a huge ResumableMultiRegex that can be used to walk + * the content and find matches. + * + * @param {CompiledMode} mode + * @returns {ResumableMultiRegex} + */ + function buildModeRegex(mode) { + var mm = new ResumableMultiRegex(); + mode.contains.forEach(function (term) { + return mm.addRule(term.begin, { + rule: term, + type: "begin" + }); + }); + if (mode.terminatorEnd) { + mm.addRule(mode.terminatorEnd, { + type: "end" + }); + } + if (mode.illegal) { + mm.addRule(mode.illegal, { + type: "illegal" + }); + } + return mm; + } + + /** skip vs abort vs ignore + * + * @skip - The mode is still entered and exited normally (and contains rules apply), + * but all content is held and added to the parent buffer rather than being + * output when the mode ends. Mostly used with `sublanguage` to build up + * a single large buffer than can be parsed by sublanguage. + * + * - The mode begin ands ends normally. + * - Content matched is added to the parent mode buffer. + * - The parser cursor is moved forward normally. + * + * @abort - A hack placeholder until we have ignore. Aborts the mode (as if it + * never matched) but DOES NOT continue to match subsequent `contains` + * modes. Abort is bad/suboptimal because it can result in modes + * farther down not getting applied because an earlier rule eats the + * content but then aborts. + * + * - The mode does not begin. + * - Content matched by `begin` is added to the mode buffer. + * - The parser cursor is moved forward accordingly. + * + * @ignore - Ignores the mode (as if it never matched) and continues to match any + * subsequent `contains` modes. Ignore isn't technically possible with + * the current parser implementation. + * + * - The mode does not begin. + * - Content matched by `begin` is ignored. + * - The parser cursor is not moved forward. + */ + + /** + * Compiles an individual mode + * + * This can raise an error if the mode contains certain detectable known logic + * issues. + * @param {Mode} mode + * @param {CompiledMode | null} [parent] + * @returns {CompiledMode | never} + */ + function compileMode(mode, parent) { + var _ref4; + var cmode = /** @type CompiledMode */mode; + if (mode.isCompiled) return cmode; + [ + // do this early so compiler extensions generally don't have to worry about + // the distinction between match/begin + compileMatch].forEach(function (ext) { + return ext(mode, parent); + }); + language.compilerExtensions.forEach(function (ext) { + return ext(mode, parent); + }); + + // __beforeBegin is considered private API, internal use only + mode.__beforeBegin = null; + [beginKeywords, + // do this later so compiler extensions that come earlier have access to the + // raw array if they wanted to perhaps manipulate it, etc. + compileIllegal, + // default to 1 relevance if not specified + compileRelevance].forEach(function (ext) { + return ext(mode, parent); + }); + mode.isCompiled = true; + var keywordPattern = null; + if (typeof mode.keywords === "object") { + keywordPattern = mode.keywords.$pattern; + delete mode.keywords.$pattern; + } + if (mode.keywords) { + mode.keywords = compileKeywords(mode.keywords, language.case_insensitive); + } + + // both are not allowed + if (mode.lexemes && keywordPattern) { + throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) "); + } + + // `mode.lexemes` was the old standard before we added and now recommend + // using `keywords.$pattern` to pass the keyword pattern + keywordPattern = keywordPattern || mode.lexemes || /\w+/; + cmode.keywordPatternRe = langRe(keywordPattern, true); + if (parent) { + if (!mode.begin) mode.begin = /\B|\b/; + cmode.beginRe = langRe(mode.begin); + if (mode.endSameAsBegin) mode.end = mode.begin; + if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/; + if (mode.end) cmode.endRe = langRe(mode.end); + cmode.terminatorEnd = source(mode.end) || ''; + if (mode.endsWithParent && parent.terminatorEnd) { + cmode.terminatorEnd += (mode.end ? '|' : '') + parent.terminatorEnd; + } + } + if (mode.illegal) cmode.illegalRe = langRe( /** @type {RegExp | string} */mode.illegal); + if (!mode.contains) mode.contains = []; + mode.contains = (_ref4 = []).concat.apply(_ref4, _toConsumableArray(mode.contains.map(function (c) { + return expandOrCloneMode(c === 'self' ? mode : c); + }))); + mode.contains.forEach(function (c) { + compileMode( /** @type Mode */c, cmode); + }); + if (mode.starts) { + compileMode(mode.starts, parent); + } + cmode.matcher = buildModeRegex(cmode); + return cmode; + } + if (!language.compilerExtensions) language.compilerExtensions = []; + + // self is not valid at the top-level + if (language.contains && language.contains.includes('self')) { + throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation."); + } + + // we need a null object, which inherit will guarantee + language.classNameAliases = inherit(language.classNameAliases || {}); + return compileMode( /** @type Mode */language); +} + +/** + * Determines if a mode has a dependency on it's parent or not + * + * If a mode does have a parent dependency then often we need to clone it if + * it's used in multiple places so that each copy points to the correct parent, + * where-as modes without a parent can often safely be re-used at the bottom of + * a mode chain. + * + * @param {Mode | null} mode + * @returns {boolean} - is there a dependency on the parent? + * */ +function dependencyOnParent(mode) { + if (!mode) return false; + return mode.endsWithParent || dependencyOnParent(mode.starts); +} + +/** + * Expands a mode or clones it if necessary + * + * This is necessary for modes with parental dependenceis (see notes on + * `dependencyOnParent`) and for nodes that have `variants` - which must then be + * exploded into their own individual modes at compile time. + * + * @param {Mode} mode + * @returns {Mode | Mode[]} + * */ +function expandOrCloneMode(mode) { + if (mode.variants && !mode.cachedVariants) { + mode.cachedVariants = mode.variants.map(function (variant) { + return inherit(mode, { + variants: null + }, variant); + }); + } + + // EXPAND + // if we have variants then essentially "replace" the mode with the variants + // this happens in compileMode, where this function is called from + if (mode.cachedVariants) { + return mode.cachedVariants; + } + + // CLONE + // if we have dependencies on parents then we need a unique + // instance of ourselves, so we can be reused with many + // different parents without issue + if (dependencyOnParent(mode)) { + return inherit(mode, { + starts: mode.starts ? inherit(mode.starts) : null + }); + } + if (Object.isFrozen(mode)) { + return inherit(mode); + } + + // no special dependency issues, just return ourselves + return mode; +} +var version = "10.7.3"; + +// @ts-nocheck + +function hasValueOrEmptyAttribute(value) { + return Boolean(value || value === ""); +} +function BuildVuePlugin(hljs) { + var Component = { + props: ["language", "code", "autodetect"], + data: function data() { + return { + detectedLanguage: "", + unknownLanguage: false + }; + }, + computed: { + className: function className() { + if (this.unknownLanguage) return ""; + return "hljs " + this.detectedLanguage; + }, + highlighted: function highlighted() { + // no idea what language to use, return raw code + if (!this.autoDetect && !hljs.getLanguage(this.language)) { + console.warn("The language \"".concat(this.language, "\" you specified could not be found.")); + this.unknownLanguage = true; + return escapeHTML(this.code); + } + var result = {}; + if (this.autoDetect) { + result = hljs.highlightAuto(this.code); + this.detectedLanguage = result.language; + } else { + result = hljs.highlight(this.language, this.code, this.ignoreIllegals); + this.detectedLanguage = this.language; + } + return result.value; + }, + autoDetect: function autoDetect() { + return !this.language || hasValueOrEmptyAttribute(this.autodetect); + }, + ignoreIllegals: function ignoreIllegals() { + return true; + } + }, + // this avoids needing to use a whole Vue compilation pipeline just + // to build Highlight.js + render: function render(createElement) { + return createElement("pre", {}, [createElement("code", { + class: this.className, + domProps: { + innerHTML: this.highlighted + } + })]); + } // template: `
    ` + }; + var VuePlugin = { + install: function install(Vue) { + Vue.component('highlightjs', Component); + } + }; + return { + Component: Component, + VuePlugin: VuePlugin + }; +} + +/* plugin itself */ + +/** @type {HLJSPlugin} */ +var mergeHTMLPlugin = { + "after:highlightElement": function afterHighlightElement(_ref5) { + var el = _ref5.el, + result = _ref5.result, + text = _ref5.text; + var originalStream = nodeStream(el); + if (!originalStream.length) return; + var resultNode = document.createElement('div'); + resultNode.innerHTML = result.value; + result.value = mergeStreams(originalStream, nodeStream(resultNode), text); + } +}; + +/* Stream merging support functions */ + +/** + * @typedef Event + * @property {'start'|'stop'} event + * @property {number} offset + * @property {Node} node + */ + +/** + * @param {Node} node + */ +function tag(node) { + return node.nodeName.toLowerCase(); +} + +/** + * @param {Node} node + */ +function nodeStream(node) { + /** @type Event[] */ + var result = []; + (function _nodeStream(node, offset) { + for (var child = node.firstChild; child; child = child.nextSibling) { + if (child.nodeType === 3) { + offset += child.nodeValue.length; + } else if (child.nodeType === 1) { + result.push({ + event: 'start', + offset: offset, + node: child + }); + offset = _nodeStream(child, offset); + // Prevent void elements from having an end tag that would actually + // double them in the output. There are more void elements in HTML + // but we list only those realistically expected in code display. + if (!tag(child).match(/br|hr|img|input/)) { + result.push({ + event: 'stop', + offset: offset, + node: child + }); + } + } + } + return offset; + })(node, 0); + return result; +} + +/** + * @param {any} original - the original stream + * @param {any} highlighted - stream of the highlighted source + * @param {string} value - the original source itself + */ +function mergeStreams(original, highlighted, value) { + var processed = 0; + var result = ''; + var nodeStack = []; + function selectStream() { + if (!original.length || !highlighted.length) { + return original.length ? original : highlighted; + } + if (original[0].offset !== highlighted[0].offset) { + return original[0].offset < highlighted[0].offset ? original : highlighted; + } + + /* + To avoid starting the stream just before it should stop the order is + ensured that original always starts first and closes last: + if (event1 == 'start' && event2 == 'start') + return original; + if (event1 == 'start' && event2 == 'stop') + return highlighted; + if (event1 == 'stop' && event2 == 'start') + return original; + if (event1 == 'stop' && event2 == 'stop') + return highlighted; + ... which is collapsed to: + */ + return highlighted[0].event === 'start' ? original : highlighted; + } + + /** + * @param {Node} node + */ + function open(node) { + /** @param {Attr} attr */ + function attributeString(attr) { + return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"'; + } + // @ts-ignore + result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('') + '>'; + } + + /** + * @param {Node} node + */ + function close(node) { + result += ''; + } + + /** + * @param {Event} event + */ + function render(event) { + (event.event === 'start' ? open : close)(event.node); + } + while (original.length || highlighted.length) { + var stream = selectStream(); + result += escapeHTML(value.substring(processed, stream[0].offset)); + processed = stream[0].offset; + if (stream === original) { + /* + On any opening or closing tag of the original markup we first close + the entire highlighted node stack, then render the original tag along + with all the following original tags at the same offset and then + reopen all the tags on the highlighted stack. + */ + nodeStack.reverse().forEach(close); + do { + render(stream.splice(0, 1)[0]); + stream = selectStream(); + } while (stream === original && stream.length && stream[0].offset === processed); + nodeStack.reverse().forEach(open); + } else { + if (stream[0].event === 'start') { + nodeStack.push(stream[0].node); + } else { + nodeStack.pop(); + } + render(stream.splice(0, 1)[0]); + } + } + return result + escapeHTML(value.substr(processed)); +} + +/* + +For the reasoning behind this please see: +https://github.com/highlightjs/highlight.js/issues/2880#issuecomment-747275419 + +*/ + +/** + * @type {Record} + */ +var seenDeprecations = {}; + +/** + * @param {string} message + */ +var error = function error(message) { + console.error(message); +}; + +/** + * @param {string} message + * @param {any} args + */ +var warn = function warn(message) { + var _console; + for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key5 = 1; _key5 < _len4; _key5++) { + args[_key5 - 1] = arguments[_key5]; + } + (_console = console).log.apply(_console, ["WARN: ".concat(message)].concat(args)); +}; + +/** + * @param {string} version + * @param {string} message + */ +var deprecated = function deprecated(version, message) { + if (seenDeprecations["".concat(version, "/").concat(message)]) return; + console.log("Deprecated as of ".concat(version, ". ").concat(message)); + seenDeprecations["".concat(version, "/").concat(message)] = true; +}; + +/* +Syntax highlighting with language autodetection. +https://highlightjs.org/ +*/ + +var escape$1 = escapeHTML; +var inherit$1 = inherit; +var NO_MATCH = Symbol("nomatch"); + +/** + * @param {any} hljs - object that is extended (legacy) + * @returns {HLJSApi} + */ +var HLJS = function HLJS(hljs) { + // Global internal variables used within the highlight.js library. + /** @type {Record} */ + var languages = Object.create(null); + /** @type {Record} */ + var aliases = Object.create(null); + /** @type {HLJSPlugin[]} */ + var plugins = []; + + // safe/production mode - swallows more errors, tries to keep running + // even if a single syntax or parse hits a fatal error + var SAFE_MODE = true; + var fixMarkupRe = /(^(<[^>]+>|\t|)+|\n)/gm; + var LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?"; + /** @type {Language} */ + var PLAINTEXT_LANGUAGE = { + disableAutodetect: true, + name: 'Plain text', + contains: [] + }; + + // Global options used when within external APIs. This is modified when + // calling the `hljs.configure` function. + /** @type HLJSOptions */ + var options = { + noHighlightRe: /^(no-?highlight)$/i, + languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i, + classPrefix: 'hljs-', + tabReplace: null, + useBR: false, + languages: null, + // beta configuration options, subject to change, welcome to discuss + // https://github.com/highlightjs/highlight.js/issues/1086 + __emitter: TokenTreeEmitter + }; + + /* Utility functions */ + + /** + * Tests a language name to see if highlighting should be skipped + * @param {string} languageName + */ + function shouldNotHighlight(languageName) { + return options.noHighlightRe.test(languageName); + } + + /** + * @param {HighlightedHTMLElement} block - the HTML element to determine language for + */ + function blockLanguage(block) { + var classes = block.className + ' '; + classes += block.parentNode ? block.parentNode.className : ''; + + // language-* takes precedence over non-prefixed class names. + var match = options.languageDetectRe.exec(classes); + if (match) { + var language = getLanguage(match[1]); + if (!language) { + warn(LANGUAGE_NOT_FOUND.replace("{}", match[1])); + warn("Falling back to no-highlight mode for this block.", block); + } + return language ? match[1] : 'no-highlight'; + } + return classes.split(/\s+/).find(function (_class) { + return shouldNotHighlight(_class) || getLanguage(_class); + }); + } + + /** + * Core highlighting function. + * + * OLD API + * highlight(lang, code, ignoreIllegals, continuation) + * + * NEW API + * highlight(code, {lang, ignoreIllegals}) + * + * @param {string} codeOrlanguageName - the language to use for highlighting + * @param {string | HighlightOptions} optionsOrCode - the code to highlight + * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail + * @param {CompiledMode} [continuation] - current continuation mode, if any + * + * @returns {HighlightResult} Result - an object that represents the result + * @property {string} language - the language name + * @property {number} relevance - the relevance score + * @property {string} value - the highlighted HTML code + * @property {string} code - the original raw code + * @property {CompiledMode} top - top of the current mode stack + * @property {boolean} illegal - indicates whether any illegal matches were found + */ + function highlight(codeOrlanguageName, optionsOrCode, ignoreIllegals, continuation) { + var code = ""; + var languageName = ""; + if (typeof optionsOrCode === "object") { + code = codeOrlanguageName; + ignoreIllegals = optionsOrCode.ignoreIllegals; + languageName = optionsOrCode.language; + // continuation not supported at all via the new API + // eslint-disable-next-line no-undefined + continuation = undefined; + } else { + // old API + deprecated("10.7.0", "highlight(lang, code, ...args) has been deprecated."); + deprecated("10.7.0", "Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"); + languageName = codeOrlanguageName; + code = optionsOrCode; + } + + /** @type {BeforeHighlightContext} */ + var context = { + code: code, + language: languageName + }; + // the plugin can change the desired language or the code to be highlighted + // just be changing the object it was passed + fire("before:highlight", context); + + // a before plugin can usurp the result completely by providing it's own + // in which case we don't even need to call highlight + var result = context.result ? context.result : _highlight(context.language, context.code, ignoreIllegals, continuation); + result.code = context.code; + // the plugin can change anything in result to suite it + fire("after:highlight", result); + return result; + } + + /** + * private highlight that's used internally and does not fire callbacks + * + * @param {string} languageName - the language to use for highlighting + * @param {string} codeToHighlight - the code to highlight + * @param {boolean?} [ignoreIllegals] - whether to ignore illegal matches, default is to bail + * @param {CompiledMode?} [continuation] - current continuation mode, if any + * @returns {HighlightResult} - result of the highlight operation + */ + function _highlight(languageName, codeToHighlight, ignoreIllegals, continuation) { + /** + * Return keyword data if a match is a keyword + * @param {CompiledMode} mode - current mode + * @param {RegExpMatchArray} match - regexp match data + * @returns {KeywordData | false} + */ + function keywordData(mode, match) { + var matchText = language.case_insensitive ? match[0].toLowerCase() : match[0]; + return Object.prototype.hasOwnProperty.call(mode.keywords, matchText) && mode.keywords[matchText]; + } + function processKeywords() { + if (!top.keywords) { + emitter.addText(modeBuffer); + return; + } + var lastIndex = 0; + top.keywordPatternRe.lastIndex = 0; + var match = top.keywordPatternRe.exec(modeBuffer); + var buf = ""; + while (match) { + buf += modeBuffer.substring(lastIndex, match.index); + var data = keywordData(top, match); + if (data) { + var _data = _slicedToArray(data, 2), + kind = _data[0], + keywordRelevance = _data[1]; + emitter.addText(buf); + buf = ""; + relevance += keywordRelevance; + if (kind.startsWith("_")) { + // _ implied for relevance only, do not highlight + // by applying a class name + buf += match[0]; + } else { + var cssClass = language.classNameAliases[kind] || kind; + emitter.addKeyword(match[0], cssClass); + } + } else { + buf += match[0]; + } + lastIndex = top.keywordPatternRe.lastIndex; + match = top.keywordPatternRe.exec(modeBuffer); + } + buf += modeBuffer.substr(lastIndex); + emitter.addText(buf); + } + function processSubLanguage() { + if (modeBuffer === "") return; + /** @type HighlightResult */ + var result = null; + if (typeof top.subLanguage === 'string') { + if (!languages[top.subLanguage]) { + emitter.addText(modeBuffer); + return; + } + result = _highlight(top.subLanguage, modeBuffer, true, continuations[top.subLanguage]); + continuations[top.subLanguage] = /** @type {CompiledMode} */result.top; + } else { + result = highlightAuto(modeBuffer, top.subLanguage.length ? top.subLanguage : null); + } + + // Counting embedded language score towards the host language may be disabled + // with zeroing the containing mode relevance. Use case in point is Markdown that + // allows XML everywhere and makes every XML snippet to have a much larger Markdown + // score. + if (top.relevance > 0) { + relevance += result.relevance; + } + emitter.addSublanguage(result.emitter, result.language); + } + function processBuffer() { + if (top.subLanguage != null) { + processSubLanguage(); + } else { + processKeywords(); + } + modeBuffer = ''; + } + + /** + * @param {Mode} mode - new mode to start + */ + function startNewMode(mode) { + if (mode.className) { + emitter.openNode(language.classNameAliases[mode.className] || mode.className); + } + top = Object.create(mode, { + parent: { + value: top + } + }); + return top; + } + + /** + * @param {CompiledMode } mode - the mode to potentially end + * @param {RegExpMatchArray} match - the latest match + * @param {string} matchPlusRemainder - match plus remainder of content + * @returns {CompiledMode | void} - the next mode, or if void continue on in current mode + */ + function endOfMode(mode, match, matchPlusRemainder) { + var matched = startsWith(mode.endRe, matchPlusRemainder); + if (matched) { + if (mode["on:end"]) { + var resp = new Response(mode); + mode["on:end"](match, resp); + if (resp.isMatchIgnored) matched = false; + } + if (matched) { + while (mode.endsParent && mode.parent) { + mode = mode.parent; + } + return mode; + } + } + // even if on:end fires an `ignore` it's still possible + // that we might trigger the end node because of a parent mode + if (mode.endsWithParent) { + return endOfMode(mode.parent, match, matchPlusRemainder); + } + } + + /** + * Handle matching but then ignoring a sequence of text + * + * @param {string} lexeme - string containing full match text + */ + function doIgnore(lexeme) { + if (top.matcher.regexIndex === 0) { + // no more regexs to potentially match here, so we move the cursor forward one + // space + modeBuffer += lexeme[0]; + return 1; + } else { + // no need to move the cursor, we still have additional regexes to try and + // match at this very spot + resumeScanAtSamePosition = true; + return 0; + } + } + + /** + * Handle the start of a new potential mode match + * + * @param {EnhancedMatch} match - the current match + * @returns {number} how far to advance the parse cursor + */ + function doBeginMatch(match) { + var lexeme = match[0]; + var newMode = match.rule; + var resp = new Response(newMode); + // first internal before callbacks, then the public ones + var beforeCallbacks = [newMode.__beforeBegin, newMode["on:begin"]]; + for (var _i = 0, _beforeCallbacks = beforeCallbacks; _i < _beforeCallbacks.length; _i++) { + var cb = _beforeCallbacks[_i]; + if (!cb) continue; + cb(match, resp); + if (resp.isMatchIgnored) return doIgnore(lexeme); + } + if (newMode && newMode.endSameAsBegin) { + newMode.endRe = escape(lexeme); + } + if (newMode.skip) { + modeBuffer += lexeme; + } else { + if (newMode.excludeBegin) { + modeBuffer += lexeme; + } + processBuffer(); + if (!newMode.returnBegin && !newMode.excludeBegin) { + modeBuffer = lexeme; + } + } + startNewMode(newMode); + // if (mode["after:begin"]) { + // let resp = new Response(mode); + // mode["after:begin"](match, resp); + // } + return newMode.returnBegin ? 0 : lexeme.length; + } + + /** + * Handle the potential end of mode + * + * @param {RegExpMatchArray} match - the current match + */ + function doEndMatch(match) { + var lexeme = match[0]; + var matchPlusRemainder = codeToHighlight.substr(match.index); + var endMode = endOfMode(top, match, matchPlusRemainder); + if (!endMode) { + return NO_MATCH; + } + var origin = top; + if (origin.skip) { + modeBuffer += lexeme; + } else { + if (!(origin.returnEnd || origin.excludeEnd)) { + modeBuffer += lexeme; + } + processBuffer(); + if (origin.excludeEnd) { + modeBuffer = lexeme; + } + } + do { + if (top.className) { + emitter.closeNode(); + } + if (!top.skip && !top.subLanguage) { + relevance += top.relevance; + } + top = top.parent; + } while (top !== endMode.parent); + if (endMode.starts) { + if (endMode.endSameAsBegin) { + endMode.starts.endRe = endMode.endRe; + } + startNewMode(endMode.starts); + } + return origin.returnEnd ? 0 : lexeme.length; + } + function processContinuations() { + var list = []; + for (var current = top; current !== language; current = current.parent) { + if (current.className) { + list.unshift(current.className); + } + } + list.forEach(function (item) { + return emitter.openNode(item); + }); + } + + /** @type {{type?: MatchType, index?: number, rule?: Mode}}} */ + var lastMatch = {}; + + /** + * Process an individual match + * + * @param {string} textBeforeMatch - text preceeding the match (since the last match) + * @param {EnhancedMatch} [match] - the match itself + */ + function processLexeme(textBeforeMatch, match) { + var lexeme = match && match[0]; + + // add non-matched text to the current mode buffer + modeBuffer += textBeforeMatch; + if (lexeme == null) { + processBuffer(); + return 0; + } + + // we've found a 0 width match and we're stuck, so we need to advance + // this happens when we have badly behaved rules that have optional matchers to the degree that + // sometimes they can end up matching nothing at all + // Ref: https://github.com/highlightjs/highlight.js/issues/2140 + if (lastMatch.type === "begin" && match.type === "end" && lastMatch.index === match.index && lexeme === "") { + // spit the "skipped" character that our regex choked on back into the output sequence + modeBuffer += codeToHighlight.slice(match.index, match.index + 1); + if (!SAFE_MODE) { + /** @type {AnnotatedError} */ + var err = new Error('0 width match regex'); + err.languageName = languageName; + err.badRule = lastMatch.rule; + throw err; + } + return 1; + } + lastMatch = match; + if (match.type === "begin") { + return doBeginMatch(match); + } else if (match.type === "illegal" && !ignoreIllegals) { + // illegal match, we do not continue processing + /** @type {AnnotatedError} */ + var _err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '') + '"'); + _err.mode = top; + throw _err; + } else if (match.type === "end") { + var processed = doEndMatch(match); + if (processed !== NO_MATCH) { + return processed; + } + } + + // edge case for when illegal matches $ (end of line) which is technically + // a 0 width match but not a begin/end match so it's not caught by the + // first handler (when ignoreIllegals is true) + if (match.type === "illegal" && lexeme === "") { + // advance so we aren't stuck in an infinite loop + return 1; + } + + // infinite loops are BAD, this is a last ditch catch all. if we have a + // decent number of iterations yet our index (cursor position in our + // parsing) still 3x behind our index then something is very wrong + // so we bail + if (iterations > 100000 && iterations > match.index * 3) { + var _err2 = new Error('potential infinite loop, way more iterations than matches'); + throw _err2; + } + + /* + Why might be find ourselves here? Only one occasion now. An end match that was + triggered but could not be completed. When might this happen? When an `endSameasBegin` + rule sets the end rule to a specific match. Since the overall mode termination rule that's + being used to scan the text isn't recompiled that means that any match that LOOKS like + the end (but is not, because it is not an exact match to the beginning) will + end up here. A definite end match, but when `doEndMatch` tries to "reapply" + the end rule and fails to match, we wind up here, and just silently ignore the end. + This causes no real harm other than stopping a few times too many. + */ + + modeBuffer += lexeme; + return lexeme.length; + } + var language = getLanguage(languageName); + if (!language) { + error(LANGUAGE_NOT_FOUND.replace("{}", languageName)); + throw new Error('Unknown language: "' + languageName + '"'); + } + var md = compileLanguage(language, { + plugins: plugins + }); + var result = ''; + /** @type {CompiledMode} */ + var top = continuation || md; + /** @type Record */ + var continuations = {}; // keep continuations for sub-languages + var emitter = new options.__emitter(options); + processContinuations(); + var modeBuffer = ''; + var relevance = 0; + var index = 0; + var iterations = 0; + var resumeScanAtSamePosition = false; + try { + top.matcher.considerAll(); + for (;;) { + iterations++; + if (resumeScanAtSamePosition) { + // only regexes not matched previously will now be + // considered for a potential match + resumeScanAtSamePosition = false; + } else { + top.matcher.considerAll(); + } + top.matcher.lastIndex = index; + var match = top.matcher.exec(codeToHighlight); + // console.log("match", match[0], match.rule && match.rule.begin) + + if (!match) break; + var beforeMatch = codeToHighlight.substring(index, match.index); + var processedCount = processLexeme(beforeMatch, match); + index = match.index + processedCount; + } + processLexeme(codeToHighlight.substr(index)); + emitter.closeAllNodes(); + emitter.finalize(); + result = emitter.toHTML(); + return { + // avoid possible breakage with v10 clients expecting + // this to always be an integer + relevance: Math.floor(relevance), + value: result, + language: languageName, + illegal: false, + emitter: emitter, + top: top + }; + } catch (err) { + if (err.message && err.message.includes('Illegal')) { + return { + illegal: true, + illegalBy: { + msg: err.message, + context: codeToHighlight.slice(index - 100, index + 100), + mode: err.mode + }, + sofar: result, + relevance: 0, + value: escape$1(codeToHighlight), + emitter: emitter + }; + } else if (SAFE_MODE) { + return { + illegal: false, + relevance: 0, + value: escape$1(codeToHighlight), + emitter: emitter, + language: languageName, + top: top, + errorRaised: err + }; + } else { + throw err; + } + } + } + + /** + * returns a valid highlight result, without actually doing any actual work, + * auto highlight starts with this and it's possible for small snippets that + * auto-detection may not find a better match + * @param {string} code + * @returns {HighlightResult} + */ + function justTextHighlightResult(code) { + var result = { + relevance: 0, + emitter: new options.__emitter(options), + value: escape$1(code), + illegal: false, + top: PLAINTEXT_LANGUAGE + }; + result.emitter.addText(code); + return result; + } + + /** + Highlighting with language detection. Accepts a string with the code to + highlight. Returns an object with the following properties: + - language (detected language) + - relevance (int) + - value (an HTML string with highlighting markup) + - second_best (object with the same structure for second-best heuristically + detected language, may be absent) + @param {string} code + @param {Array} [languageSubset] + @returns {AutoHighlightResult} + */ + function highlightAuto(code, languageSubset) { + languageSubset = languageSubset || options.languages || Object.keys(languages); + var plaintext = justTextHighlightResult(code); + var results = languageSubset.filter(getLanguage).filter(autoDetection).map(function (name) { + return _highlight(name, code, false); + }); + results.unshift(plaintext); // plaintext is always an option + + var sorted = results.sort(function (a, b) { + // sort base on relevance + if (a.relevance !== b.relevance) return b.relevance - a.relevance; + + // always award the tie to the base language + // ie if C++ and Arduino are tied, it's more likely to be C++ + if (a.language && b.language) { + if (getLanguage(a.language).supersetOf === b.language) { + return 1; + } else if (getLanguage(b.language).supersetOf === a.language) { + return -1; + } + } + + // otherwise say they are equal, which has the effect of sorting on + // relevance while preserving the original ordering - which is how ties + // have historically been settled, ie the language that comes first always + // wins in the case of a tie + return 0; + }); + var _sorted = _slicedToArray(sorted, 2), + best = _sorted[0], + secondBest = _sorted[1]; + + /** @type {AutoHighlightResult} */ + var result = best; + result.second_best = secondBest; + return result; + } + + /** + Post-processing of the highlighted markup: + - replace TABs with something more useful + - replace real line-breaks with '
    ' for non-pre containers + @param {string} html + @returns {string} + */ + function fixMarkup(html) { + if (!(options.tabReplace || options.useBR)) { + return html; + } + return html.replace(fixMarkupRe, function (match) { + if (match === '\n') { + return options.useBR ? '
    ' : match; + } else if (options.tabReplace) { + return match.replace(/\t/g, options.tabReplace); + } + return match; + }); + } + + /** + * Builds new class name for block given the language name + * + * @param {HTMLElement} element + * @param {string} [currentLang] + * @param {string} [resultLang] + */ + function updateClassName(element, currentLang, resultLang) { + var language = currentLang ? aliases[currentLang] : resultLang; + element.classList.add("hljs"); + if (language) element.classList.add(language); + } + + /** @type {HLJSPlugin} */ + var brPlugin = { + "before:highlightElement": function beforeHighlightElement(_ref6) { + var el = _ref6.el; + if (options.useBR) { + el.innerHTML = el.innerHTML.replace(/\n/g, '').replace(//g, '\n'); + } + }, + "after:highlightElement": function afterHighlightElement(_ref7) { + var result = _ref7.result; + if (options.useBR) { + result.value = result.value.replace(/\n/g, "
    "); + } + } + }; + var TAB_REPLACE_RE = /^(<[^>]+>|\t)+/gm; + /** @type {HLJSPlugin} */ + var tabReplacePlugin = { + "after:highlightElement": function afterHighlightElement(_ref8) { + var result = _ref8.result; + if (options.tabReplace) { + result.value = result.value.replace(TAB_REPLACE_RE, function (m) { + return m.replace(/\t/g, options.tabReplace); + }); + } + } + }; + + /** + * Applies highlighting to a DOM node containing code. Accepts a DOM node and + * two optional parameters for fixMarkup. + * + * @param {HighlightedHTMLElement} element - the HTML element to highlight + */ + function highlightElement(element) { + /** @type HTMLElement */ + var node = null; + var language = blockLanguage(element); + if (shouldNotHighlight(language)) return; + + // support for v10 API + fire("before:highlightElement", { + el: element, + language: language + }); + node = element; + var text = node.textContent; + var result = language ? highlight(text, { + language: language, + ignoreIllegals: true + }) : highlightAuto(text); + + // support for v10 API + fire("after:highlightElement", { + el: element, + result: result, + text: text + }); + element.innerHTML = result.value; + updateClassName(element, language, result.language); + element.result = { + language: result.language, + // TODO: remove with version 11.0 + re: result.relevance, + relavance: result.relevance + }; + if (result.second_best) { + element.second_best = { + language: result.second_best.language, + // TODO: remove with version 11.0 + re: result.second_best.relevance, + relavance: result.second_best.relevance + }; + } + } + + /** + * Updates highlight.js global options with the passed options + * + * @param {Partial} userOptions + */ + function configure(userOptions) { + if (userOptions.useBR) { + deprecated("10.3.0", "'useBR' will be removed entirely in v11.0"); + deprecated("10.3.0", "Please see https://github.com/highlightjs/highlight.js/issues/2559"); + } + options = inherit$1(options, userOptions); + } + + /** + * Highlights to all
     blocks on a page
    +   *
    +   * @type {Function & {called?: boolean}}
    +   */
    +  // TODO: remove v12, deprecated
    +  var initHighlighting = function initHighlighting() {
    +    if (initHighlighting.called) return;
    +    initHighlighting.called = true;
    +    deprecated("10.6.0", "initHighlighting() is deprecated.  Use highlightAll() instead.");
    +    var blocks = document.querySelectorAll('pre code');
    +    blocks.forEach(highlightElement);
    +  };
    +
    +  // Higlights all when DOMContentLoaded fires
    +  // TODO: remove v12, deprecated
    +  function initHighlightingOnLoad() {
    +    deprecated("10.6.0", "initHighlightingOnLoad() is deprecated.  Use highlightAll() instead.");
    +    wantsHighlight = true;
    +  }
    +  var wantsHighlight = false;
    +
    +  /**
    +   * auto-highlights all pre>code elements on the page
    +   */
    +  function highlightAll() {
    +    // if we are called too early in the loading process
    +    if (document.readyState === "loading") {
    +      wantsHighlight = true;
    +      return;
    +    }
    +    var blocks = document.querySelectorAll('pre code');
    +    blocks.forEach(highlightElement);
    +  }
    +  function boot() {
    +    // if a highlight was requested before DOM was loaded, do now
    +    if (wantsHighlight) highlightAll();
    +  }
    +
    +  // make sure we are in the browser environment
    +  if (typeof window !== 'undefined' && window.addEventListener) {
    +    window.addEventListener('DOMContentLoaded', boot, false);
    +  }
    +
    +  /**
    +   * Register a language grammar module
    +   *
    +   * @param {string} languageName
    +   * @param {LanguageFn} languageDefinition
    +   */
    +  function registerLanguage(languageName, languageDefinition) {
    +    var lang = null;
    +    try {
    +      lang = languageDefinition(hljs);
    +    } catch (error$1) {
    +      error("Language definition for '{}' could not be registered.".replace("{}", languageName));
    +      // hard or soft error
    +      if (!SAFE_MODE) {
    +        throw error$1;
    +      } else {
    +        error(error$1);
    +      }
    +      // languages that have serious errors are replaced with essentially a
    +      // "plaintext" stand-in so that the code blocks will still get normal
    +      // css classes applied to them - and one bad language won't break the
    +      // entire highlighter
    +      lang = PLAINTEXT_LANGUAGE;
    +    }
    +    // give it a temporary name if it doesn't have one in the meta-data
    +    if (!lang.name) lang.name = languageName;
    +    languages[languageName] = lang;
    +    lang.rawDefinition = languageDefinition.bind(null, hljs);
    +    if (lang.aliases) {
    +      registerAliases(lang.aliases, {
    +        languageName: languageName
    +      });
    +    }
    +  }
    +
    +  /**
    +   * Remove a language grammar module
    +   *
    +   * @param {string} languageName
    +   */
    +  function unregisterLanguage(languageName) {
    +    delete languages[languageName];
    +    for (var _i2 = 0, _Object$keys = Object.keys(aliases); _i2 < _Object$keys.length; _i2++) {
    +      var alias = _Object$keys[_i2];
    +      if (aliases[alias] === languageName) {
    +        delete aliases[alias];
    +      }
    +    }
    +  }
    +
    +  /**
    +   * @returns {string[]} List of language internal names
    +   */
    +  function listLanguages() {
    +    return Object.keys(languages);
    +  }
    +
    +  /**
    +    intended usage: When one language truly requires another
    +     Unlike `getLanguage`, this will throw when the requested language
    +    is not available.
    +     @param {string} name - name of the language to fetch/require
    +    @returns {Language | never}
    +  */
    +  function requireLanguage(name) {
    +    deprecated("10.4.0", "requireLanguage will be removed entirely in v11.");
    +    deprecated("10.4.0", "Please see https://github.com/highlightjs/highlight.js/pull/2844");
    +    var lang = getLanguage(name);
    +    if (lang) {
    +      return lang;
    +    }
    +    var err = new Error('The \'{}\' language is required, but not loaded.'.replace('{}', name));
    +    throw err;
    +  }
    +
    +  /**
    +   * @param {string} name - name of the language to retrieve
    +   * @returns {Language | undefined}
    +   */
    +  function getLanguage(name) {
    +    name = (name || '').toLowerCase();
    +    return languages[name] || languages[aliases[name]];
    +  }
    +
    +  /**
    +   *
    +   * @param {string|string[]} aliasList - single alias or list of aliases
    +   * @param {{languageName: string}} opts
    +   */
    +  function registerAliases(aliasList, _ref9) {
    +    var languageName = _ref9.languageName;
    +    if (typeof aliasList === 'string') {
    +      aliasList = [aliasList];
    +    }
    +    aliasList.forEach(function (alias) {
    +      aliases[alias.toLowerCase()] = languageName;
    +    });
    +  }
    +
    +  /**
    +   * Determines if a given language has auto-detection enabled
    +   * @param {string} name - name of the language
    +   */
    +  function autoDetection(name) {
    +    var lang = getLanguage(name);
    +    return lang && !lang.disableAutodetect;
    +  }
    +
    +  /**
    +   * Upgrades the old highlightBlock plugins to the new
    +   * highlightElement API
    +   * @param {HLJSPlugin} plugin
    +   */
    +  function upgradePluginAPI(plugin) {
    +    // TODO: remove with v12
    +    if (plugin["before:highlightBlock"] && !plugin["before:highlightElement"]) {
    +      plugin["before:highlightElement"] = function (data) {
    +        plugin["before:highlightBlock"](Object.assign({
    +          block: data.el
    +        }, data));
    +      };
    +    }
    +    if (plugin["after:highlightBlock"] && !plugin["after:highlightElement"]) {
    +      plugin["after:highlightElement"] = function (data) {
    +        plugin["after:highlightBlock"](Object.assign({
    +          block: data.el
    +        }, data));
    +      };
    +    }
    +  }
    +
    +  /**
    +   * @param {HLJSPlugin} plugin
    +   */
    +  function addPlugin(plugin) {
    +    upgradePluginAPI(plugin);
    +    plugins.push(plugin);
    +  }
    +
    +  /**
    +   *
    +   * @param {PluginEvent} event
    +   * @param {any} args
    +   */
    +  function fire(event, args) {
    +    var cb = event;
    +    plugins.forEach(function (plugin) {
    +      if (plugin[cb]) {
    +        plugin[cb](args);
    +      }
    +    });
    +  }
    +
    +  /**
    +  Note: fixMarkup is deprecated and will be removed entirely in v11
    +   @param {string} arg
    +  @returns {string}
    +  */
    +  function deprecateFixMarkup(arg) {
    +    deprecated("10.2.0", "fixMarkup will be removed entirely in v11.0");
    +    deprecated("10.2.0", "Please see https://github.com/highlightjs/highlight.js/issues/2534");
    +    return fixMarkup(arg);
    +  }
    +
    +  /**
    +   *
    +   * @param {HighlightedHTMLElement} el
    +   */
    +  function deprecateHighlightBlock(el) {
    +    deprecated("10.7.0", "highlightBlock will be removed entirely in v12.0");
    +    deprecated("10.7.0", "Please use highlightElement now.");
    +    return highlightElement(el);
    +  }
    +
    +  /* Interface definition */
    +  Object.assign(hljs, {
    +    highlight: highlight,
    +    highlightAuto: highlightAuto,
    +    highlightAll: highlightAll,
    +    fixMarkup: deprecateFixMarkup,
    +    highlightElement: highlightElement,
    +    // TODO: Remove with v12 API
    +    highlightBlock: deprecateHighlightBlock,
    +    configure: configure,
    +    initHighlighting: initHighlighting,
    +    initHighlightingOnLoad: initHighlightingOnLoad,
    +    registerLanguage: registerLanguage,
    +    unregisterLanguage: unregisterLanguage,
    +    listLanguages: listLanguages,
    +    getLanguage: getLanguage,
    +    registerAliases: registerAliases,
    +    requireLanguage: requireLanguage,
    +    autoDetection: autoDetection,
    +    inherit: inherit$1,
    +    addPlugin: addPlugin,
    +    // plugins for frameworks
    +    vuePlugin: BuildVuePlugin(hljs).VuePlugin
    +  });
    +  hljs.debugMode = function () {
    +    SAFE_MODE = false;
    +  };
    +  hljs.safeMode = function () {
    +    SAFE_MODE = true;
    +  };
    +  hljs.versionString = version;
    +  for (var key in MODES) {
    +    // @ts-ignore
    +    if (typeof MODES[key] === "object") {
    +      // @ts-ignore
    +      deepFreezeEs6(MODES[key]);
    +    }
    +  }
    +
    +  // merge all the modes/regexs into our main object
    +  Object.assign(hljs, MODES);
    +
    +  // built-in plugins, likely to be moved out of core in the future
    +  hljs.addPlugin(brPlugin); // slated to be removed in v11
    +  hljs.addPlugin(mergeHTMLPlugin);
    +  hljs.addPlugin(tabReplacePlugin);
    +  return hljs;
    +};
    +
    +// export an "instance" of the highlighter
    +var highlight = HLJS({});
    +module.exports = highlight;
    +
    +/***/ }),
    +
    +/***/ 682:
    +/***/ (function(module) {
    +
    +/*
    +Language: Diff
    +Description: Unified and context diff
    +Author: Vasily Polovnyov 
    +Website: https://www.gnu.org/software/diffutils/
    +Category: common
    +*/
    +
    +/** @type LanguageFn */
    +function diff(hljs) {
    +  return {
    +    name: 'Diff',
    +    aliases: ['patch'],
    +    contains: [{
    +      className: 'meta',
    +      relevance: 10,
    +      variants: [{
    +        begin: /^@@ +-\d+,\d+ +\+\d+,\d+ +@@/
    +      }, {
    +        begin: /^\*\*\* +\d+,\d+ +\*\*\*\*$/
    +      }, {
    +        begin: /^--- +\d+,\d+ +----$/
    +      }]
    +    }, {
    +      className: 'comment',
    +      variants: [{
    +        begin: /Index: /,
    +        end: /$/
    +      }, {
    +        begin: /^index/,
    +        end: /$/
    +      }, {
    +        begin: /={3,}/,
    +        end: /$/
    +      }, {
    +        begin: /^-{3}/,
    +        end: /$/
    +      }, {
    +        begin: /^\*{3} /,
    +        end: /$/
    +      }, {
    +        begin: /^\+{3}/,
    +        end: /$/
    +      }, {
    +        begin: /^\*{15}$/
    +      }, {
    +        begin: /^diff --git/,
    +        end: /$/
    +      }]
    +    }, {
    +      className: 'addition',
    +      begin: /^\+/,
    +      end: /$/
    +    }, {
    +      className: 'deletion',
    +      begin: /^-/,
    +      end: /$/
    +    }, {
    +      className: 'addition',
    +      begin: /^!/,
    +      end: /$/
    +    }]
    +  };
    +}
    +module.exports = diff;
    +
    +/***/ }),
    +
    +/***/ 712:
    +/***/ (function(module) {
    +
    +/*
    +Language: YAML
    +Description: Yet Another Markdown Language
    +Author: Stefan Wienert 
    +Contributors: Carl Baxter 
    +Requires: ruby.js
    +Website: https://yaml.org
    +Category: common, config
    +*/
    +function yaml(hljs) {
    +  var LITERALS = 'true false yes no null';
    +
    +  // YAML spec allows non-reserved URI characters in tags.
    +  var URI_CHARACTERS = '[\\w#;/?:@&=+$,.~*\'()[\\]]+';
    +
    +  // Define keys as starting with a word character
    +  // ...containing word chars, spaces, colons, forward-slashes, hyphens and periods
    +  // ...and ending with a colon followed immediately by a space, tab or newline.
    +  // The YAML spec allows for much more than this, but this covers most use-cases.
    +  var KEY = {
    +    className: 'attr',
    +    variants: [{
    +      begin: '\\w[\\w :\\/.-]*:(?=[ \t]|$)'
    +    }, {
    +      begin: '"\\w[\\w :\\/.-]*":(?=[ \t]|$)'
    +    },
    +    // double quoted keys
    +    {
    +      begin: '\'\\w[\\w :\\/.-]*\':(?=[ \t]|$)'
    +    } // single quoted keys
    +    ]
    +  };
    +
    +  var TEMPLATE_VARIABLES = {
    +    className: 'template-variable',
    +    variants: [{
    +      begin: /\{\{/,
    +      end: /\}\}/
    +    },
    +    // jinja templates Ansible
    +    {
    +      begin: /%\{/,
    +      end: /\}/
    +    } // Ruby i18n
    +    ]
    +  };
    +
    +  var STRING = {
    +    className: 'string',
    +    relevance: 0,
    +    variants: [{
    +      begin: /'/,
    +      end: /'/
    +    }, {
    +      begin: /"/,
    +      end: /"/
    +    }, {
    +      begin: /\S+/
    +    }],
    +    contains: [hljs.BACKSLASH_ESCAPE, TEMPLATE_VARIABLES]
    +  };
    +
    +  // Strings inside of value containers (objects) can't contain braces,
    +  // brackets, or commas
    +  var CONTAINER_STRING = hljs.inherit(STRING, {
    +    variants: [{
    +      begin: /'/,
    +      end: /'/
    +    }, {
    +      begin: /"/,
    +      end: /"/
    +    }, {
    +      begin: /[^\s,{}[\]]+/
    +    }]
    +  });
    +  var DATE_RE = '[0-9]{4}(-[0-9][0-9]){0,2}';
    +  var TIME_RE = '([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?';
    +  var FRACTION_RE = '(\\.[0-9]*)?';
    +  var ZONE_RE = '([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?';
    +  var TIMESTAMP = {
    +    className: 'number',
    +    begin: '\\b' + DATE_RE + TIME_RE + FRACTION_RE + ZONE_RE + '\\b'
    +  };
    +  var VALUE_CONTAINER = {
    +    end: ',',
    +    endsWithParent: true,
    +    excludeEnd: true,
    +    keywords: LITERALS,
    +    relevance: 0
    +  };
    +  var OBJECT = {
    +    begin: /\{/,
    +    end: /\}/,
    +    contains: [VALUE_CONTAINER],
    +    illegal: '\\n',
    +    relevance: 0
    +  };
    +  var ARRAY = {
    +    begin: '\\[',
    +    end: '\\]',
    +    contains: [VALUE_CONTAINER],
    +    illegal: '\\n',
    +    relevance: 0
    +  };
    +  var MODES = [KEY, {
    +    className: 'meta',
    +    begin: '^---\\s*$',
    +    relevance: 10
    +  }, {
    +    // multi line string
    +    // Blocks start with a | or > followed by a newline
    +    //
    +    // Indentation of subsequent lines must be the same to
    +    // be considered part of the block
    +    className: 'string',
    +    begin: '[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*'
    +  }, {
    +    // Ruby/Rails erb
    +    begin: '<%[%=-]?',
    +    end: '[%-]?%>',
    +    subLanguage: 'ruby',
    +    excludeBegin: true,
    +    excludeEnd: true,
    +    relevance: 0
    +  }, {
    +    // named tags
    +    className: 'type',
    +    begin: '!\\w+!' + URI_CHARACTERS
    +  },
    +  // https://yaml.org/spec/1.2/spec.html#id2784064
    +  {
    +    // verbatim tags
    +    className: 'type',
    +    begin: '!<' + URI_CHARACTERS + ">"
    +  }, {
    +    // primary tags
    +    className: 'type',
    +    begin: '!' + URI_CHARACTERS
    +  }, {
    +    // secondary tags
    +    className: 'type',
    +    begin: '!!' + URI_CHARACTERS
    +  }, {
    +    // fragment id &ref
    +    className: 'meta',
    +    begin: '&' + hljs.UNDERSCORE_IDENT_RE + '$'
    +  }, {
    +    // fragment reference *ref
    +    className: 'meta',
    +    begin: '\\*' + hljs.UNDERSCORE_IDENT_RE + '$'
    +  }, {
    +    // array listing
    +    className: 'bullet',
    +    // TODO: remove |$ hack when we have proper look-ahead support
    +    begin: '-(?=[ ]|$)',
    +    relevance: 0
    +  }, hljs.HASH_COMMENT_MODE, {
    +    beginKeywords: LITERALS,
    +    keywords: {
    +      literal: LITERALS
    +    }
    +  }, TIMESTAMP,
    +  // numbers are any valid C-style number that
    +  // sit isolated from other words
    +  {
    +    className: 'number',
    +    begin: hljs.C_NUMBER_RE + '\\b',
    +    relevance: 0
    +  }, OBJECT, ARRAY, STRING];
    +  var VALUE_MODES = [].concat(MODES);
    +  VALUE_MODES.pop();
    +  VALUE_MODES.push(CONTAINER_STRING);
    +  VALUE_CONTAINER.contains = VALUE_MODES;
    +  return {
    +    name: 'YAML',
    +    case_insensitive: true,
    +    aliases: ['yml'],
    +    contains: MODES
    +  };
    +}
    +module.exports = yaml;
    +
    +/***/ }),
    +
    +/***/ 110:
    +/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
    +
    +"use strict";
    +
    +
    +var reactIs = __webpack_require__(309);
    +
    +/**
    + * Copyright 2015, Yahoo! Inc.
    + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
    + */
    +var REACT_STATICS = {
    +  childContextTypes: true,
    +  contextType: true,
    +  contextTypes: true,
    +  defaultProps: true,
    +  displayName: true,
    +  getDefaultProps: true,
    +  getDerivedStateFromError: true,
    +  getDerivedStateFromProps: true,
    +  mixins: true,
    +  propTypes: true,
    +  type: true
    +};
    +var KNOWN_STATICS = {
    +  name: true,
    +  length: true,
    +  prototype: true,
    +  caller: true,
    +  callee: true,
    +  arguments: true,
    +  arity: true
    +};
    +var FORWARD_REF_STATICS = {
    +  '$$typeof': true,
    +  render: true,
    +  defaultProps: true,
    +  displayName: true,
    +  propTypes: true
    +};
    +var MEMO_STATICS = {
    +  '$$typeof': true,
    +  compare: true,
    +  defaultProps: true,
    +  displayName: true,
    +  propTypes: true,
    +  type: true
    +};
    +var TYPE_STATICS = {};
    +TYPE_STATICS[reactIs.ForwardRef] = FORWARD_REF_STATICS;
    +TYPE_STATICS[reactIs.Memo] = MEMO_STATICS;
    +function getStatics(component) {
    +  // React v16.11 and below
    +  if (reactIs.isMemo(component)) {
    +    return MEMO_STATICS;
    +  } // React v16.12 and above
    +
    +  return TYPE_STATICS[component['$$typeof']] || REACT_STATICS;
    +}
    +var defineProperty = Object.defineProperty;
    +var getOwnPropertyNames = Object.getOwnPropertyNames;
    +var getOwnPropertySymbols = Object.getOwnPropertySymbols;
    +var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
    +var getPrototypeOf = Object.getPrototypeOf;
    +var objectPrototype = Object.prototype;
    +function hoistNonReactStatics(targetComponent, sourceComponent, blacklist) {
    +  if (typeof sourceComponent !== 'string') {
    +    // don't hoist over string (html) components
    +    if (objectPrototype) {
    +      var inheritedComponent = getPrototypeOf(sourceComponent);
    +      if (inheritedComponent && inheritedComponent !== objectPrototype) {
    +        hoistNonReactStatics(targetComponent, inheritedComponent, blacklist);
    +      }
    +    }
    +    var keys = getOwnPropertyNames(sourceComponent);
    +    if (getOwnPropertySymbols) {
    +      keys = keys.concat(getOwnPropertySymbols(sourceComponent));
    +    }
    +    var targetStatics = getStatics(targetComponent);
    +    var sourceStatics = getStatics(sourceComponent);
    +    for (var i = 0; i < keys.length; ++i) {
    +      var key = keys[i];
    +      if (!KNOWN_STATICS[key] && !(blacklist && blacklist[key]) && !(sourceStatics && sourceStatics[key]) && !(targetStatics && targetStatics[key])) {
    +        var descriptor = getOwnPropertyDescriptor(sourceComponent, key);
    +        try {
    +          // Avoid failures from read-only properties
    +          defineProperty(targetComponent, key, descriptor);
    +        } catch (e) {}
    +      }
    +    }
    +  }
    +  return targetComponent;
    +}
    +module.exports = hoistNonReactStatics;
    +
    +/***/ }),
    +
    +/***/ 746:
    +/***/ (function(__unused_webpack_module, exports) {
    +
    +"use strict";
    +/** @license React v16.13.1
    + * react-is.production.min.js
    + *
    + * Copyright (c) Facebook, Inc. and its affiliates.
    + *
    + * This source code is licensed under the MIT license found in the
    + * LICENSE file in the root directory of this source tree.
    + */
    +
    +
    +
    +var b = "function" === typeof Symbol && Symbol.for,
    +  c = b ? Symbol.for("react.element") : 60103,
    +  d = b ? Symbol.for("react.portal") : 60106,
    +  e = b ? Symbol.for("react.fragment") : 60107,
    +  f = b ? Symbol.for("react.strict_mode") : 60108,
    +  g = b ? Symbol.for("react.profiler") : 60114,
    +  h = b ? Symbol.for("react.provider") : 60109,
    +  k = b ? Symbol.for("react.context") : 60110,
    +  l = b ? Symbol.for("react.async_mode") : 60111,
    +  m = b ? Symbol.for("react.concurrent_mode") : 60111,
    +  n = b ? Symbol.for("react.forward_ref") : 60112,
    +  p = b ? Symbol.for("react.suspense") : 60113,
    +  q = b ? Symbol.for("react.suspense_list") : 60120,
    +  r = b ? Symbol.for("react.memo") : 60115,
    +  t = b ? Symbol.for("react.lazy") : 60116,
    +  v = b ? Symbol.for("react.block") : 60121,
    +  w = b ? Symbol.for("react.fundamental") : 60117,
    +  x = b ? Symbol.for("react.responder") : 60118,
    +  y = b ? Symbol.for("react.scope") : 60119;
    +function z(a) {
    +  if ("object" === typeof a && null !== a) {
    +    var u = a.$$typeof;
    +    switch (u) {
    +      case c:
    +        switch (a = a.type, a) {
    +          case l:
    +          case m:
    +          case e:
    +          case g:
    +          case f:
    +          case p:
    +            return a;
    +          default:
    +            switch (a = a && a.$$typeof, a) {
    +              case k:
    +              case n:
    +              case t:
    +              case r:
    +              case h:
    +                return a;
    +              default:
    +                return u;
    +            }
    +        }
    +      case d:
    +        return u;
    +    }
    +  }
    +}
    +function A(a) {
    +  return z(a) === m;
    +}
    +exports.AsyncMode = l;
    +exports.ConcurrentMode = m;
    +exports.ContextConsumer = k;
    +exports.ContextProvider = h;
    +exports.Element = c;
    +exports.ForwardRef = n;
    +exports.Fragment = e;
    +exports.Lazy = t;
    +exports.Memo = r;
    +exports.Portal = d;
    +exports.Profiler = g;
    +exports.StrictMode = f;
    +exports.Suspense = p;
    +exports.isAsyncMode = function (a) {
    +  return A(a) || z(a) === l;
    +};
    +exports.isConcurrentMode = A;
    +exports.isContextConsumer = function (a) {
    +  return z(a) === k;
    +};
    +exports.isContextProvider = function (a) {
    +  return z(a) === h;
    +};
    +exports.isElement = function (a) {
    +  return "object" === typeof a && null !== a && a.$$typeof === c;
    +};
    +exports.isForwardRef = function (a) {
    +  return z(a) === n;
    +};
    +exports.isFragment = function (a) {
    +  return z(a) === e;
    +};
    +exports.isLazy = function (a) {
    +  return z(a) === t;
    +};
    +exports.isMemo = function (a) {
    +  return z(a) === r;
    +};
    +exports.isPortal = function (a) {
    +  return z(a) === d;
    +};
    +exports.isProfiler = function (a) {
    +  return z(a) === g;
    +};
    +exports.isStrictMode = function (a) {
    +  return z(a) === f;
    +};
    +exports.isSuspense = function (a) {
    +  return z(a) === p;
    +};
    +exports.isValidElementType = function (a) {
    +  return "string" === typeof a || "function" === typeof a || a === e || a === m || a === g || a === f || a === p || a === q || "object" === typeof a && null !== a && (a.$$typeof === t || a.$$typeof === r || a.$$typeof === h || a.$$typeof === k || a.$$typeof === n || a.$$typeof === w || a.$$typeof === x || a.$$typeof === y || a.$$typeof === v);
    +};
    +exports.typeOf = z;
    +
    +/***/ }),
    +
    +/***/ 309:
    +/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
    +
    +"use strict";
    +
    +
    +if (true) {
    +  module.exports = __webpack_require__(746);
    +} else {}
    +
    +/***/ }),
    +
    +/***/ 763:
    +/***/ (function(module, exports, __webpack_require__) {
    +
    +/* module decorator */ module = __webpack_require__.nmd(module);
    +var __WEBPACK_AMD_DEFINE_RESULT__;/**
    + * @license
    + * Lodash 
    + * Copyright OpenJS Foundation and other contributors 
    + * Released under MIT license 
    + * Based on Underscore.js 1.8.3 
    + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
    + */
    +;
    +(function () {
    +  /** Used as a safe reference for `undefined` in pre-ES5 environments. */
    +  var undefined;
    +
    +  /** Used as the semantic version number. */
    +  var VERSION = '4.17.21';
    +
    +  /** Used as the size to enable large array optimizations. */
    +  var LARGE_ARRAY_SIZE = 200;
    +
    +  /** Error message constants. */
    +  var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',
    +    FUNC_ERROR_TEXT = 'Expected a function',
    +    INVALID_TEMPL_VAR_ERROR_TEXT = 'Invalid `variable` option passed into `_.template`';
    +
    +  /** Used to stand-in for `undefined` hash values. */
    +  var HASH_UNDEFINED = '__lodash_hash_undefined__';
    +
    +  /** Used as the maximum memoize cache size. */
    +  var MAX_MEMOIZE_SIZE = 500;
    +
    +  /** Used as the internal argument placeholder. */
    +  var PLACEHOLDER = '__lodash_placeholder__';
    +
    +  /** Used to compose bitmasks for cloning. */
    +  var CLONE_DEEP_FLAG = 1,
    +    CLONE_FLAT_FLAG = 2,
    +    CLONE_SYMBOLS_FLAG = 4;
    +
    +  /** Used to compose bitmasks for value comparisons. */
    +  var COMPARE_PARTIAL_FLAG = 1,
    +    COMPARE_UNORDERED_FLAG = 2;
    +
    +  /** Used to compose bitmasks for function metadata. */
    +  var WRAP_BIND_FLAG = 1,
    +    WRAP_BIND_KEY_FLAG = 2,
    +    WRAP_CURRY_BOUND_FLAG = 4,
    +    WRAP_CURRY_FLAG = 8,
    +    WRAP_CURRY_RIGHT_FLAG = 16,
    +    WRAP_PARTIAL_FLAG = 32,
    +    WRAP_PARTIAL_RIGHT_FLAG = 64,
    +    WRAP_ARY_FLAG = 128,
    +    WRAP_REARG_FLAG = 256,
    +    WRAP_FLIP_FLAG = 512;
    +
    +  /** Used as default options for `_.truncate`. */
    +  var DEFAULT_TRUNC_LENGTH = 30,
    +    DEFAULT_TRUNC_OMISSION = '...';
    +
    +  /** Used to detect hot functions by number of calls within a span of milliseconds. */
    +  var HOT_COUNT = 800,
    +    HOT_SPAN = 16;
    +
    +  /** Used to indicate the type of lazy iteratees. */
    +  var LAZY_FILTER_FLAG = 1,
    +    LAZY_MAP_FLAG = 2,
    +    LAZY_WHILE_FLAG = 3;
    +
    +  /** Used as references for various `Number` constants. */
    +  var INFINITY = 1 / 0,
    +    MAX_SAFE_INTEGER = 9007199254740991,
    +    MAX_INTEGER = 1.7976931348623157e+308,
    +    NAN = 0 / 0;
    +
    +  /** Used as references for the maximum length and index of an array. */
    +  var MAX_ARRAY_LENGTH = 4294967295,
    +    MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
    +    HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
    +
    +  /** Used to associate wrap methods with their bit flags. */
    +  var wrapFlags = [['ary', WRAP_ARY_FLAG], ['bind', WRAP_BIND_FLAG], ['bindKey', WRAP_BIND_KEY_FLAG], ['curry', WRAP_CURRY_FLAG], ['curryRight', WRAP_CURRY_RIGHT_FLAG], ['flip', WRAP_FLIP_FLAG], ['partial', WRAP_PARTIAL_FLAG], ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], ['rearg', WRAP_REARG_FLAG]];
    +
    +  /** `Object#toString` result references. */
    +  var argsTag = '[object Arguments]',
    +    arrayTag = '[object Array]',
    +    asyncTag = '[object AsyncFunction]',
    +    boolTag = '[object Boolean]',
    +    dateTag = '[object Date]',
    +    domExcTag = '[object DOMException]',
    +    errorTag = '[object Error]',
    +    funcTag = '[object Function]',
    +    genTag = '[object GeneratorFunction]',
    +    mapTag = '[object Map]',
    +    numberTag = '[object Number]',
    +    nullTag = '[object Null]',
    +    objectTag = '[object Object]',
    +    promiseTag = '[object Promise]',
    +    proxyTag = '[object Proxy]',
    +    regexpTag = '[object RegExp]',
    +    setTag = '[object Set]',
    +    stringTag = '[object String]',
    +    symbolTag = '[object Symbol]',
    +    undefinedTag = '[object Undefined]',
    +    weakMapTag = '[object WeakMap]',
    +    weakSetTag = '[object WeakSet]';
    +  var arrayBufferTag = '[object ArrayBuffer]',
    +    dataViewTag = '[object DataView]',
    +    float32Tag = '[object Float32Array]',
    +    float64Tag = '[object Float64Array]',
    +    int8Tag = '[object Int8Array]',
    +    int16Tag = '[object Int16Array]',
    +    int32Tag = '[object Int32Array]',
    +    uint8Tag = '[object Uint8Array]',
    +    uint8ClampedTag = '[object Uint8ClampedArray]',
    +    uint16Tag = '[object Uint16Array]',
    +    uint32Tag = '[object Uint32Array]';
    +
    +  /** Used to match empty string literals in compiled template source. */
    +  var reEmptyStringLeading = /\b__p \+= '';/g,
    +    reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
    +    reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
    +
    +  /** Used to match HTML entities and HTML characters. */
    +  var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,
    +    reUnescapedHtml = /[&<>"']/g,
    +    reHasEscapedHtml = RegExp(reEscapedHtml.source),
    +    reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
    +
    +  /** Used to match template delimiters. */
    +  var reEscape = /<%-([\s\S]+?)%>/g,
    +    reEvaluate = /<%([\s\S]+?)%>/g,
    +    reInterpolate = /<%=([\s\S]+?)%>/g;
    +
    +  /** Used to match property names within property paths. */
    +  var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
    +    reIsPlainProp = /^\w*$/,
    +    rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
    +
    +  /**
    +   * Used to match `RegExp`
    +   * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
    +   */
    +  var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
    +    reHasRegExpChar = RegExp(reRegExpChar.source);
    +
    +  /** Used to match leading whitespace. */
    +  var reTrimStart = /^\s+/;
    +
    +  /** Used to match a single whitespace character. */
    +  var reWhitespace = /\s/;
    +
    +  /** Used to match wrap detail comments. */
    +  var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,
    +    reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/,
    +    reSplitDetails = /,? & /;
    +
    +  /** Used to match words composed of alphanumeric characters. */
    +  var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
    +
    +  /**
    +   * Used to validate the `validate` option in `_.template` variable.
    +   *
    +   * Forbids characters which could potentially change the meaning of the function argument definition:
    +   * - "()," (modification of function parameters)
    +   * - "=" (default value)
    +   * - "[]{}" (destructuring of function parameters)
    +   * - "/" (beginning of a comment)
    +   * - whitespace
    +   */
    +  var reForbiddenIdentifierChars = /[()=,{}\[\]\/\s]/;
    +
    +  /** Used to match backslashes in property paths. */
    +  var reEscapeChar = /\\(\\)?/g;
    +
    +  /**
    +   * Used to match
    +   * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components).
    +   */
    +  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
    +
    +  /** Used to match `RegExp` flags from their coerced string values. */
    +  var reFlags = /\w*$/;
    +
    +  /** Used to detect bad signed hexadecimal string values. */
    +  var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
    +
    +  /** Used to detect binary string values. */
    +  var reIsBinary = /^0b[01]+$/i;
    +
    +  /** Used to detect host constructors (Safari). */
    +  var reIsHostCtor = /^\[object .+?Constructor\]$/;
    +
    +  /** Used to detect octal string values. */
    +  var reIsOctal = /^0o[0-7]+$/i;
    +
    +  /** Used to detect unsigned integer values. */
    +  var reIsUint = /^(?:0|[1-9]\d*)$/;
    +
    +  /** Used to match Latin Unicode letters (excluding mathematical operators). */
    +  var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;
    +
    +  /** Used to ensure capturing order of template delimiters. */
    +  var reNoMatch = /($^)/;
    +
    +  /** Used to match unescaped characters in compiled string literals. */
    +  var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
    +
    +  /** Used to compose unicode character classes. */
    +  var rsAstralRange = "\\ud800-\\udfff",
    +    rsComboMarksRange = "\\u0300-\\u036f",
    +    reComboHalfMarksRange = "\\ufe20-\\ufe2f",
    +    rsComboSymbolsRange = "\\u20d0-\\u20ff",
    +    rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
    +    rsDingbatRange = "\\u2700-\\u27bf",
    +    rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff',
    +    rsMathOpRange = '\\xac\\xb1\\xd7\\xf7',
    +    rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf',
    +    rsPunctuationRange = "\\u2000-\\u206f",
    +    rsSpaceRange = " \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",
    +    rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde',
    +    rsVarRange = "\\ufe0e\\ufe0f",
    +    rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;
    +
    +  /** Used to compose unicode capture groups. */
    +  var rsApos = "['\u2019]",
    +    rsAstral = '[' + rsAstralRange + ']',
    +    rsBreak = '[' + rsBreakRange + ']',
    +    rsCombo = '[' + rsComboRange + ']',
    +    rsDigits = '\\d+',
    +    rsDingbat = '[' + rsDingbatRange + ']',
    +    rsLower = '[' + rsLowerRange + ']',
    +    rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',
    +    rsFitz = "\\ud83c[\\udffb-\\udfff]",
    +    rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
    +    rsNonAstral = '[^' + rsAstralRange + ']',
    +    rsRegional = "(?:\\ud83c[\\udde6-\\uddff]){2}",
    +    rsSurrPair = "[\\ud800-\\udbff][\\udc00-\\udfff]",
    +    rsUpper = '[' + rsUpperRange + ']',
    +    rsZWJ = "\\u200d";
    +
    +  /** Used to compose unicode regexes. */
    +  var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')',
    +    rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')',
    +    rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',
    +    rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',
    +    reOptMod = rsModifier + '?',
    +    rsOptVar = '[' + rsVarRange + ']?',
    +    rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
    +    rsOrdLower = '\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])',
    +    rsOrdUpper = '\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])',
    +    rsSeq = rsOptVar + reOptMod + rsOptJoin,
    +    rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,
    +    rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
    +
    +  /** Used to match apostrophes. */
    +  var reApos = RegExp(rsApos, 'g');
    +
    +  /**
    +   * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
    +   * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
    +   */
    +  var reComboMark = RegExp(rsCombo, 'g');
    +
    +  /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
    +  var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
    +
    +  /** Used to match complex or compound words. */
    +  var reUnicodeWord = RegExp([rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, rsUpper + '+' + rsOptContrUpper, rsOrdUpper, rsOrdLower, rsDigits, rsEmoji].join('|'), 'g');
    +
    +  /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
    +  var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');
    +
    +  /** Used to detect strings that need a more robust regexp to match words. */
    +  var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;
    +
    +  /** Used to assign default `context` object properties. */
    +  var contextProps = ['Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'];
    +
    +  /** Used to make template sourceURLs easier to identify. */
    +  var templateCounter = -1;
    +
    +  /** Used to identify `toStringTag` values of typed arrays. */
    +  var typedArrayTags = {};
    +  typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true;
    +  typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
    +
    +  /** Used to identify `toStringTag` values supported by `_.clone`. */
    +  var cloneableTags = {};
    +  cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[mapTag] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[setTag] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
    +  cloneableTags[errorTag] = cloneableTags[funcTag] = cloneableTags[weakMapTag] = false;
    +
    +  /** Used to map Latin Unicode letters to basic Latin letters. */
    +  var deburredLetters = {
    +    // Latin-1 Supplement block.
    +    '\xc0': 'A',
    +    '\xc1': 'A',
    +    '\xc2': 'A',
    +    '\xc3': 'A',
    +    '\xc4': 'A',
    +    '\xc5': 'A',
    +    '\xe0': 'a',
    +    '\xe1': 'a',
    +    '\xe2': 'a',
    +    '\xe3': 'a',
    +    '\xe4': 'a',
    +    '\xe5': 'a',
    +    '\xc7': 'C',
    +    '\xe7': 'c',
    +    '\xd0': 'D',
    +    '\xf0': 'd',
    +    '\xc8': 'E',
    +    '\xc9': 'E',
    +    '\xca': 'E',
    +    '\xcb': 'E',
    +    '\xe8': 'e',
    +    '\xe9': 'e',
    +    '\xea': 'e',
    +    '\xeb': 'e',
    +    '\xcc': 'I',
    +    '\xcd': 'I',
    +    '\xce': 'I',
    +    '\xcf': 'I',
    +    '\xec': 'i',
    +    '\xed': 'i',
    +    '\xee': 'i',
    +    '\xef': 'i',
    +    '\xd1': 'N',
    +    '\xf1': 'n',
    +    '\xd2': 'O',
    +    '\xd3': 'O',
    +    '\xd4': 'O',
    +    '\xd5': 'O',
    +    '\xd6': 'O',
    +    '\xd8': 'O',
    +    '\xf2': 'o',
    +    '\xf3': 'o',
    +    '\xf4': 'o',
    +    '\xf5': 'o',
    +    '\xf6': 'o',
    +    '\xf8': 'o',
    +    '\xd9': 'U',
    +    '\xda': 'U',
    +    '\xdb': 'U',
    +    '\xdc': 'U',
    +    '\xf9': 'u',
    +    '\xfa': 'u',
    +    '\xfb': 'u',
    +    '\xfc': 'u',
    +    '\xdd': 'Y',
    +    '\xfd': 'y',
    +    '\xff': 'y',
    +    '\xc6': 'Ae',
    +    '\xe6': 'ae',
    +    '\xde': 'Th',
    +    '\xfe': 'th',
    +    '\xdf': 'ss',
    +    // Latin Extended-A block.
    +    "\u0100": 'A',
    +    "\u0102": 'A',
    +    "\u0104": 'A',
    +    "\u0101": 'a',
    +    "\u0103": 'a',
    +    "\u0105": 'a',
    +    "\u0106": 'C',
    +    "\u0108": 'C',
    +    "\u010A": 'C',
    +    "\u010C": 'C',
    +    "\u0107": 'c',
    +    "\u0109": 'c',
    +    "\u010B": 'c',
    +    "\u010D": 'c',
    +    "\u010E": 'D',
    +    "\u0110": 'D',
    +    "\u010F": 'd',
    +    "\u0111": 'd',
    +    "\u0112": 'E',
    +    "\u0114": 'E',
    +    "\u0116": 'E',
    +    "\u0118": 'E',
    +    "\u011A": 'E',
    +    "\u0113": 'e',
    +    "\u0115": 'e',
    +    "\u0117": 'e',
    +    "\u0119": 'e',
    +    "\u011B": 'e',
    +    "\u011C": 'G',
    +    "\u011E": 'G',
    +    "\u0120": 'G',
    +    "\u0122": 'G',
    +    "\u011D": 'g',
    +    "\u011F": 'g',
    +    "\u0121": 'g',
    +    "\u0123": 'g',
    +    "\u0124": 'H',
    +    "\u0126": 'H',
    +    "\u0125": 'h',
    +    "\u0127": 'h',
    +    "\u0128": 'I',
    +    "\u012A": 'I',
    +    "\u012C": 'I',
    +    "\u012E": 'I',
    +    "\u0130": 'I',
    +    "\u0129": 'i',
    +    "\u012B": 'i',
    +    "\u012D": 'i',
    +    "\u012F": 'i',
    +    "\u0131": 'i',
    +    "\u0134": 'J',
    +    "\u0135": 'j',
    +    "\u0136": 'K',
    +    "\u0137": 'k',
    +    "\u0138": 'k',
    +    "\u0139": 'L',
    +    "\u013B": 'L',
    +    "\u013D": 'L',
    +    "\u013F": 'L',
    +    "\u0141": 'L',
    +    "\u013A": 'l',
    +    "\u013C": 'l',
    +    "\u013E": 'l',
    +    "\u0140": 'l',
    +    "\u0142": 'l',
    +    "\u0143": 'N',
    +    "\u0145": 'N',
    +    "\u0147": 'N',
    +    "\u014A": 'N',
    +    "\u0144": 'n',
    +    "\u0146": 'n',
    +    "\u0148": 'n',
    +    "\u014B": 'n',
    +    "\u014C": 'O',
    +    "\u014E": 'O',
    +    "\u0150": 'O',
    +    "\u014D": 'o',
    +    "\u014F": 'o',
    +    "\u0151": 'o',
    +    "\u0154": 'R',
    +    "\u0156": 'R',
    +    "\u0158": 'R',
    +    "\u0155": 'r',
    +    "\u0157": 'r',
    +    "\u0159": 'r',
    +    "\u015A": 'S',
    +    "\u015C": 'S',
    +    "\u015E": 'S',
    +    "\u0160": 'S',
    +    "\u015B": 's',
    +    "\u015D": 's',
    +    "\u015F": 's',
    +    "\u0161": 's',
    +    "\u0162": 'T',
    +    "\u0164": 'T',
    +    "\u0166": 'T',
    +    "\u0163": 't',
    +    "\u0165": 't',
    +    "\u0167": 't',
    +    "\u0168": 'U',
    +    "\u016A": 'U',
    +    "\u016C": 'U',
    +    "\u016E": 'U',
    +    "\u0170": 'U',
    +    "\u0172": 'U',
    +    "\u0169": 'u',
    +    "\u016B": 'u',
    +    "\u016D": 'u',
    +    "\u016F": 'u',
    +    "\u0171": 'u',
    +    "\u0173": 'u',
    +    "\u0174": 'W',
    +    "\u0175": 'w',
    +    "\u0176": 'Y',
    +    "\u0177": 'y',
    +    "\u0178": 'Y',
    +    "\u0179": 'Z',
    +    "\u017B": 'Z',
    +    "\u017D": 'Z',
    +    "\u017A": 'z',
    +    "\u017C": 'z',
    +    "\u017E": 'z',
    +    "\u0132": 'IJ',
    +    "\u0133": 'ij',
    +    "\u0152": 'Oe',
    +    "\u0153": 'oe',
    +    "\u0149": "'n",
    +    "\u017F": 's'
    +  };
    +
    +  /** Used to map characters to HTML entities. */
    +  var htmlEscapes = {
    +    '&': '&',
    +    '<': '<',
    +    '>': '>',
    +    '"': '"',
    +    "'": '''
    +  };
    +
    +  /** Used to map HTML entities to characters. */
    +  var htmlUnescapes = {
    +    '&': '&',
    +    '<': '<',
    +    '>': '>',
    +    '"': '"',
    +    ''': "'"
    +  };
    +
    +  /** Used to escape characters for inclusion in compiled string literals. */
    +  var stringEscapes = {
    +    '\\': '\\',
    +    "'": "'",
    +    '\n': 'n',
    +    '\r': 'r',
    +    "\u2028": 'u2028',
    +    "\u2029": 'u2029'
    +  };
    +
    +  /** Built-in method references without a dependency on `root`. */
    +  var freeParseFloat = parseFloat,
    +    freeParseInt = parseInt;
    +
    +  /** Detect free variable `global` from Node.js. */
    +  var freeGlobal = typeof __webpack_require__.g == 'object' && __webpack_require__.g && __webpack_require__.g.Object === Object && __webpack_require__.g;
    +
    +  /** Detect free variable `self`. */
    +  var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
    +
    +  /** Used as a reference to the global object. */
    +  var root = freeGlobal || freeSelf || Function('return this')();
    +
    +  /** Detect free variable `exports`. */
    +  var freeExports =  true && exports && !exports.nodeType && exports;
    +
    +  /** Detect free variable `module`. */
    +  var freeModule = freeExports && "object" == 'object' && module && !module.nodeType && module;
    +
    +  /** Detect the popular CommonJS extension `module.exports`. */
    +  var moduleExports = freeModule && freeModule.exports === freeExports;
    +
    +  /** Detect free variable `process` from Node.js. */
    +  var freeProcess = moduleExports && freeGlobal.process;
    +
    +  /** Used to access faster Node.js helpers. */
    +  var nodeUtil = function () {
    +    try {
    +      // Use `util.types` for Node.js 10+.
    +      var types = freeModule && freeModule.require && freeModule.require('util').types;
    +      if (types) {
    +        return types;
    +      }
    +
    +      // Legacy `process.binding('util')` for Node.js < 10.
    +      return freeProcess && freeProcess.binding && freeProcess.binding('util');
    +    } catch (e) {}
    +  }();
    +
    +  /* Node.js helper references. */
    +  var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer,
    +    nodeIsDate = nodeUtil && nodeUtil.isDate,
    +    nodeIsMap = nodeUtil && nodeUtil.isMap,
    +    nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,
    +    nodeIsSet = nodeUtil && nodeUtil.isSet,
    +    nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
    +
    +  /*--------------------------------------------------------------------------*/
    +
    +  /**
    +   * A faster alternative to `Function#apply`, this function invokes `func`
    +   * with the `this` binding of `thisArg` and the arguments of `args`.
    +   *
    +   * @private
    +   * @param {Function} func The function to invoke.
    +   * @param {*} thisArg The `this` binding of `func`.
    +   * @param {Array} args The arguments to invoke `func` with.
    +   * @returns {*} Returns the result of `func`.
    +   */
    +  function apply(func, thisArg, args) {
    +    switch (args.length) {
    +      case 0:
    +        return func.call(thisArg);
    +      case 1:
    +        return func.call(thisArg, args[0]);
    +      case 2:
    +        return func.call(thisArg, args[0], args[1]);
    +      case 3:
    +        return func.call(thisArg, args[0], args[1], args[2]);
    +    }
    +    return func.apply(thisArg, args);
    +  }
    +
    +  /**
    +   * A specialized version of `baseAggregator` for arrays.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to iterate over.
    +   * @param {Function} setter The function to set `accumulator` values.
    +   * @param {Function} iteratee The iteratee to transform keys.
    +   * @param {Object} accumulator The initial aggregated object.
    +   * @returns {Function} Returns `accumulator`.
    +   */
    +  function arrayAggregator(array, setter, iteratee, accumulator) {
    +    var index = -1,
    +      length = array == null ? 0 : array.length;
    +    while (++index < length) {
    +      var value = array[index];
    +      setter(accumulator, value, iteratee(value), array);
    +    }
    +    return accumulator;
    +  }
    +
    +  /**
    +   * A specialized version of `_.forEach` for arrays without support for
    +   * iteratee shorthands.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to iterate over.
    +   * @param {Function} iteratee The function invoked per iteration.
    +   * @returns {Array} Returns `array`.
    +   */
    +  function arrayEach(array, iteratee) {
    +    var index = -1,
    +      length = array == null ? 0 : array.length;
    +    while (++index < length) {
    +      if (iteratee(array[index], index, array) === false) {
    +        break;
    +      }
    +    }
    +    return array;
    +  }
    +
    +  /**
    +   * A specialized version of `_.forEachRight` for arrays without support for
    +   * iteratee shorthands.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to iterate over.
    +   * @param {Function} iteratee The function invoked per iteration.
    +   * @returns {Array} Returns `array`.
    +   */
    +  function arrayEachRight(array, iteratee) {
    +    var length = array == null ? 0 : array.length;
    +    while (length--) {
    +      if (iteratee(array[length], length, array) === false) {
    +        break;
    +      }
    +    }
    +    return array;
    +  }
    +
    +  /**
    +   * A specialized version of `_.every` for arrays without support for
    +   * iteratee shorthands.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to iterate over.
    +   * @param {Function} predicate The function invoked per iteration.
    +   * @returns {boolean} Returns `true` if all elements pass the predicate check,
    +   *  else `false`.
    +   */
    +  function arrayEvery(array, predicate) {
    +    var index = -1,
    +      length = array == null ? 0 : array.length;
    +    while (++index < length) {
    +      if (!predicate(array[index], index, array)) {
    +        return false;
    +      }
    +    }
    +    return true;
    +  }
    +
    +  /**
    +   * A specialized version of `_.filter` for arrays without support for
    +   * iteratee shorthands.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to iterate over.
    +   * @param {Function} predicate The function invoked per iteration.
    +   * @returns {Array} Returns the new filtered array.
    +   */
    +  function arrayFilter(array, predicate) {
    +    var index = -1,
    +      length = array == null ? 0 : array.length,
    +      resIndex = 0,
    +      result = [];
    +    while (++index < length) {
    +      var value = array[index];
    +      if (predicate(value, index, array)) {
    +        result[resIndex++] = value;
    +      }
    +    }
    +    return result;
    +  }
    +
    +  /**
    +   * A specialized version of `_.includes` for arrays without support for
    +   * specifying an index to search from.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to inspect.
    +   * @param {*} target The value to search for.
    +   * @returns {boolean} Returns `true` if `target` is found, else `false`.
    +   */
    +  function arrayIncludes(array, value) {
    +    var length = array == null ? 0 : array.length;
    +    return !!length && baseIndexOf(array, value, 0) > -1;
    +  }
    +
    +  /**
    +   * This function is like `arrayIncludes` except that it accepts a comparator.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to inspect.
    +   * @param {*} target The value to search for.
    +   * @param {Function} comparator The comparator invoked per element.
    +   * @returns {boolean} Returns `true` if `target` is found, else `false`.
    +   */
    +  function arrayIncludesWith(array, value, comparator) {
    +    var index = -1,
    +      length = array == null ? 0 : array.length;
    +    while (++index < length) {
    +      if (comparator(value, array[index])) {
    +        return true;
    +      }
    +    }
    +    return false;
    +  }
    +
    +  /**
    +   * A specialized version of `_.map` for arrays without support for iteratee
    +   * shorthands.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to iterate over.
    +   * @param {Function} iteratee The function invoked per iteration.
    +   * @returns {Array} Returns the new mapped array.
    +   */
    +  function arrayMap(array, iteratee) {
    +    var index = -1,
    +      length = array == null ? 0 : array.length,
    +      result = Array(length);
    +    while (++index < length) {
    +      result[index] = iteratee(array[index], index, array);
    +    }
    +    return result;
    +  }
    +
    +  /**
    +   * Appends the elements of `values` to `array`.
    +   *
    +   * @private
    +   * @param {Array} array The array to modify.
    +   * @param {Array} values The values to append.
    +   * @returns {Array} Returns `array`.
    +   */
    +  function arrayPush(array, values) {
    +    var index = -1,
    +      length = values.length,
    +      offset = array.length;
    +    while (++index < length) {
    +      array[offset + index] = values[index];
    +    }
    +    return array;
    +  }
    +
    +  /**
    +   * A specialized version of `_.reduce` for arrays without support for
    +   * iteratee shorthands.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to iterate over.
    +   * @param {Function} iteratee The function invoked per iteration.
    +   * @param {*} [accumulator] The initial value.
    +   * @param {boolean} [initAccum] Specify using the first element of `array` as
    +   *  the initial value.
    +   * @returns {*} Returns the accumulated value.
    +   */
    +  function arrayReduce(array, iteratee, accumulator, initAccum) {
    +    var index = -1,
    +      length = array == null ? 0 : array.length;
    +    if (initAccum && length) {
    +      accumulator = array[++index];
    +    }
    +    while (++index < length) {
    +      accumulator = iteratee(accumulator, array[index], index, array);
    +    }
    +    return accumulator;
    +  }
    +
    +  /**
    +   * A specialized version of `_.reduceRight` for arrays without support for
    +   * iteratee shorthands.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to iterate over.
    +   * @param {Function} iteratee The function invoked per iteration.
    +   * @param {*} [accumulator] The initial value.
    +   * @param {boolean} [initAccum] Specify using the last element of `array` as
    +   *  the initial value.
    +   * @returns {*} Returns the accumulated value.
    +   */
    +  function arrayReduceRight(array, iteratee, accumulator, initAccum) {
    +    var length = array == null ? 0 : array.length;
    +    if (initAccum && length) {
    +      accumulator = array[--length];
    +    }
    +    while (length--) {
    +      accumulator = iteratee(accumulator, array[length], length, array);
    +    }
    +    return accumulator;
    +  }
    +
    +  /**
    +   * A specialized version of `_.some` for arrays without support for iteratee
    +   * shorthands.
    +   *
    +   * @private
    +   * @param {Array} [array] The array to iterate over.
    +   * @param {Function} predicate The function invoked per iteration.
    +   * @returns {boolean} Returns `true` if any element passes the predicate check,
    +   *  else `false`.
    +   */
    +  function arraySome(array, predicate) {
    +    var index = -1,
    +      length = array == null ? 0 : array.length;
    +    while (++index < length) {
    +      if (predicate(array[index], index, array)) {
    +        return true;
    +      }
    +    }
    +    return false;
    +  }
    +
    +  /**
    +   * Gets the size of an ASCII `string`.
    +   *
    +   * @private
    +   * @param {string} string The string inspect.
    +   * @returns {number} Returns the string size.
    +   */
    +  var asciiSize = baseProperty('length');
    +
    +  /**
    +   * Converts an ASCII `string` to an array.
    +   *
    +   * @private
    +   * @param {string} string The string to convert.
    +   * @returns {Array} Returns the converted array.
    +   */
    +  function asciiToArray(string) {
    +    return string.split('');
    +  }
    +
    +  /**
    +   * Splits an ASCII `string` into an array of its words.
    +   *
    +   * @private
    +   * @param {string} The string to inspect.
    +   * @returns {Array} Returns the words of `string`.
    +   */
    +  function asciiWords(string) {
    +    return string.match(reAsciiWord) || [];
    +  }
    +
    +  /**
    +   * The base implementation of methods like `_.findKey` and `_.findLastKey`,
    +   * without support for iteratee shorthands, which iterates over `collection`
    +   * using `eachFunc`.
    +   *
    +   * @private
    +   * @param {Array|Object} collection The collection to inspect.
    +   * @param {Function} predicate The function invoked per iteration.
    +   * @param {Function} eachFunc The function to iterate over `collection`.
    +   * @returns {*} Returns the found element or its key, else `undefined`.
    +   */
    +  function baseFindKey(collection, predicate, eachFunc) {
    +    var result;
    +    eachFunc(collection, function (value, key, collection) {
    +      if (predicate(value, key, collection)) {
    +        result = key;
    +        return false;
    +      }
    +    });
    +    return result;
    +  }
    +
    +  /**
    +   * The base implementation of `_.findIndex` and `_.findLastIndex` without
    +   * support for iteratee shorthands.
    +   *
    +   * @private
    +   * @param {Array} array The array to inspect.
    +   * @param {Function} predicate The function invoked per iteration.
    +   * @param {number} fromIndex The index to search from.
    +   * @param {boolean} [fromRight] Specify iterating from right to left.
    +   * @returns {number} Returns the index of the matched value, else `-1`.
    +   */
    +  function baseFindIndex(array, predicate, fromIndex, fromRight) {
    +    var length = array.length,
    +      index = fromIndex + (fromRight ? 1 : -1);
    +    while (fromRight ? index-- : ++index < length) {
    +      if (predicate(array[index], index, array)) {
    +        return index;
    +      }
    +    }
    +    return -1;
    +  }
    +
    +  /**
    +   * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
    +   *
    +   * @private
    +   * @param {Array} array The array to inspect.
    +   * @param {*} value The value to search for.
    +   * @param {number} fromIndex The index to search from.
    +   * @returns {number} Returns the index of the matched value, else `-1`.
    +   */
    +  function baseIndexOf(array, value, fromIndex) {
    +    return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex);
    +  }
    +
    +  /**
    +   * This function is like `baseIndexOf` except that it accepts a comparator.
    +   *
    +   * @private
    +   * @param {Array} array The array to inspect.
    +   * @param {*} value The value to search for.
    +   * @param {number} fromIndex The index to search from.
    +   * @param {Function} comparator The comparator invoked per element.
    +   * @returns {number} Returns the index of the matched value, else `-1`.
    +   */
    +  function baseIndexOfWith(array, value, fromIndex, comparator) {
    +    var index = fromIndex - 1,
    +      length = array.length;
    +    while (++index < length) {
    +      if (comparator(array[index], value)) {
    +        return index;
    +      }
    +    }
    +    return -1;
    +  }
    +
    +  /**
    +   * The base implementation of `_.isNaN` without support for number objects.
    +   *
    +   * @private
    +   * @param {*} value The value to check.
    +   * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
    +   */
    +  function baseIsNaN(value) {
    +    return value !== value;
    +  }
    +
    +  /**
    +   * The base implementation of `_.mean` and `_.meanBy` without support for
    +   * iteratee shorthands.
    +   *
    +   * @private
    +   * @param {Array} array The array to iterate over.
    +   * @param {Function} iteratee The function invoked per iteration.
    +   * @returns {number} Returns the mean.
    +   */
    +  function baseMean(array, iteratee) {
    +    var length = array == null ? 0 : array.length;
    +    return length ? baseSum(array, iteratee) / length : NAN;
    +  }
    +
    +  /**
    +   * The base implementation of `_.property` without support for deep paths.
    +   *
    +   * @private
    +   * @param {string} key The key of the property to get.
    +   * @returns {Function} Returns the new accessor function.
    +   */
    +  function baseProperty(key) {
    +    return function (object) {
    +      return object == null ? undefined : object[key];
    +    };
    +  }
    +
    +  /**
    +   * The base implementation of `_.propertyOf` without support for deep paths.
    +   *
    +   * @private
    +   * @param {Object} object The object to query.
    +   * @returns {Function} Returns the new accessor function.
    +   */
    +  function basePropertyOf(object) {
    +    return function (key) {
    +      return object == null ? undefined : object[key];
    +    };
    +  }
    +
    +  /**
    +   * The base implementation of `_.reduce` and `_.reduceRight`, without support
    +   * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
    +   *
    +   * @private
    +   * @param {Array|Object} collection The collection to iterate over.
    +   * @param {Function} iteratee The function invoked per iteration.
    +   * @param {*} accumulator The initial value.
    +   * @param {boolean} initAccum Specify using the first or last element of
    +   *  `collection` as the initial value.
    +   * @param {Function} eachFunc The function to iterate over `collection`.
    +   * @returns {*} Returns the accumulated value.
    +   */
    +  function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
    +    eachFunc(collection, function (value, index, collection) {
    +      accumulator = initAccum ? (initAccum = false, value) : iteratee(accumulator, value, index, collection);
    +    });
    +    return accumulator;
    +  }
    +
    +  /**
    +   * The base implementation of `_.sortBy` which uses `comparer` to define the
    +   * sort order of `array` and replaces criteria objects with their corresponding
    +   * values.
    +   *
    +   * @private
    +   * @param {Array} array The array to sort.
    +   * @param {Function} comparer The function to define sort order.
    +   * @returns {Array} Returns `array`.
    +   */
    +  function baseSortBy(array, comparer) {
    +    var length = array.length;
    +    array.sort(comparer);
    +    while (length--) {
    +      array[length] = array[length].value;
    +    }
    +    return array;
    +  }
    +
    +  /**
    +   * The base implementation of `_.sum` and `_.sumBy` without support for
    +   * iteratee shorthands.
    +   *
    +   * @private
    +   * @param {Array} array The array to iterate over.
    +   * @param {Function} iteratee The function invoked per iteration.
    +   * @returns {number} Returns the sum.
    +   */
    +  function baseSum(array, iteratee) {
    +    var result,
    +      index = -1,
    +      length = array.length;
    +    while (++index < length) {
    +      var current = iteratee(array[index]);
    +      if (current !== undefined) {
    +        result = result === undefined ? current : result + current;
    +      }
    +    }
    +    return result;
    +  }
    +
    +  /**
    +   * The base implementation of `_.times` without support for iteratee shorthands
    +   * or max array length checks.
    +   *
    +   * @private
    +   * @param {number} n The number of times to invoke `iteratee`.
    +   * @param {Function} iteratee The function invoked per iteration.
    +   * @returns {Array} Returns the array of results.
    +   */
    +  function baseTimes(n, iteratee) {
    +    var index = -1,
    +      result = Array(n);
    +    while (++index < n) {
    +      result[index] = iteratee(index);
    +    }
    +    return result;
    +  }
    +
    +  /**
    +   * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
    +   * of key-value pairs for `object` corresponding to the property names of `props`.
    +   *
    +   * @private
    +   * @param {Object} object The object to query.
    +   * @param {Array} props The property names to get values for.
    +   * @returns {Object} Returns the key-value pairs.
    +   */
    +  function baseToPairs(object, props) {
    +    return arrayMap(props, function (key) {
    +      return [key, object[key]];
    +    });
    +  }
    +
    +  /**
    +   * The base implementation of `_.trim`.
    +   *
    +   * @private
    +   * @param {string} string The string to trim.
    +   * @returns {string} Returns the trimmed string.
    +   */
    +  function baseTrim(string) {
    +    return string ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') : string;
    +  }
    +
    +  /**
    +   * The base implementation of `_.unary` without support for storing metadata.
    +   *
    +   * @private
    +   * @param {Function} func The function to cap arguments for.
    +   * @returns {Function} Returns the new capped function.
    +   */
    +  function baseUnary(func) {
    +    return function (value) {
    +      return func(value);
    +    };
    +  }
    +
    +  /**
    +   * The base implementation of `_.values` and `_.valuesIn` which creates an
    +   * array of `object` property values corresponding to the property names
    +   * of `props`.
    +   *
    +   * @private
    +   * @param {Object} object The object to query.
    +   * @param {Array} props The property names to get values for.
    +   * @returns {Object} Returns the array of property values.
    +   */
    +  function baseValues(object, props) {
    +    return arrayMap(props, function (key) {
    +      return object[key];
    +    });
    +  }
    +
    +  /**
    +   * Checks if a `cache` value for `key` exists.
    +   *
    +   * @private
    +   * @param {Object} cache The cache to query.
    +   * @param {string} key The key of the entry to check.
    +   * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    +   */
    +  function cacheHas(cache, key) {
    +    return cache.has(key);
    +  }
    +
    +  /**
    +   * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
    +   * that is not found in the character symbols.
    +   *
    +   * @private
    +   * @param {Array} strSymbols The string symbols to inspect.
    +   * @param {Array} chrSymbols The character symbols to find.
    +   * @returns {number} Returns the index of the first unmatched string symbol.
    +   */
    +  function charsStartIndex(strSymbols, chrSymbols) {
    +    var index = -1,
    +      length = strSymbols.length;
    +    while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
    +    return index;
    +  }
    +
    +  /**
    +   * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
    +   * that is not found in the character symbols.
    +   *
    +   * @private
    +   * @param {Array} strSymbols The string symbols to inspect.
    +   * @param {Array} chrSymbols The character symbols to find.
    +   * @returns {number} Returns the index of the last unmatched string symbol.
    +   */
    +  function charsEndIndex(strSymbols, chrSymbols) {
    +    var index = strSymbols.length;
    +    while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
    +    return index;
    +  }
    +
    +  /**
    +   * Gets the number of `placeholder` occurrences in `array`.
    +   *
    +   * @private
    +   * @param {Array} array The array to inspect.
    +   * @param {*} placeholder The placeholder to search for.
    +   * @returns {number} Returns the placeholder count.
    +   */
    +  function countHolders(array, placeholder) {
    +    var length = array.length,
    +      result = 0;
    +    while (length--) {
    +      if (array[length] === placeholder) {
    +        ++result;
    +      }
    +    }
    +    return result;
    +  }
    +
    +  /**
    +   * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A
    +   * letters to basic Latin letters.
    +   *
    +   * @private
    +   * @param {string} letter The matched letter to deburr.
    +   * @returns {string} Returns the deburred letter.
    +   */
    +  var deburrLetter = basePropertyOf(deburredLetters);
    +
    +  /**
    +   * Used by `_.escape` to convert characters to HTML entities.
    +   *
    +   * @private
    +   * @param {string} chr The matched character to escape.
    +   * @returns {string} Returns the escaped character.
    +   */
    +  var escapeHtmlChar = basePropertyOf(htmlEscapes);
    +
    +  /**
    +   * Used by `_.template` to escape characters for inclusion in compiled string literals.
    +   *
    +   * @private
    +   * @param {string} chr The matched character to escape.
    +   * @returns {string} Returns the escaped character.
    +   */
    +  function escapeStringChar(chr) {
    +    return '\\' + stringEscapes[chr];
    +  }
    +
    +  /**
    +   * Gets the value at `key` of `object`.
    +   *
    +   * @private
    +   * @param {Object} [object] The object to query.
    +   * @param {string} key The key of the property to get.
    +   * @returns {*} Returns the property value.
    +   */
    +  function getValue(object, key) {
    +    return object == null ? undefined : object[key];
    +  }
    +
    +  /**
    +   * Checks if `string` contains Unicode symbols.
    +   *
    +   * @private
    +   * @param {string} string The string to inspect.
    +   * @returns {boolean} Returns `true` if a symbol is found, else `false`.
    +   */
    +  function hasUnicode(string) {
    +    return reHasUnicode.test(string);
    +  }
    +
    +  /**
    +   * Checks if `string` contains a word composed of Unicode symbols.
    +   *
    +   * @private
    +   * @param {string} string The string to inspect.
    +   * @returns {boolean} Returns `true` if a word is found, else `false`.
    +   */
    +  function hasUnicodeWord(string) {
    +    return reHasUnicodeWord.test(string);
    +  }
    +
    +  /**
    +   * Converts `iterator` to an array.
    +   *
    +   * @private
    +   * @param {Object} iterator The iterator to convert.
    +   * @returns {Array} Returns the converted array.
    +   */
    +  function iteratorToArray(iterator) {
    +    var data,
    +      result = [];
    +    while (!(data = iterator.next()).done) {
    +      result.push(data.value);
    +    }
    +    return result;
    +  }
    +
    +  /**
    +   * Converts `map` to its key-value pairs.
    +   *
    +   * @private
    +   * @param {Object} map The map to convert.
    +   * @returns {Array} Returns the key-value pairs.
    +   */
    +  function mapToArray(map) {
    +    var index = -1,
    +      result = Array(map.size);
    +    map.forEach(function (value, key) {
    +      result[++index] = [key, value];
    +    });
    +    return result;
    +  }
    +
    +  /**
    +   * Creates a unary function that invokes `func` with its argument transformed.
    +   *
    +   * @private
    +   * @param {Function} func The function to wrap.
    +   * @param {Function} transform The argument transform.
    +   * @returns {Function} Returns the new function.
    +   */
    +  function overArg(func, transform) {
    +    return function (arg) {
    +      return func(transform(arg));
    +    };
    +  }
    +
    +  /**
    +   * Replaces all `placeholder` elements in `array` with an internal placeholder
    +   * and returns an array of their indexes.
    +   *
    +   * @private
    +   * @param {Array} array The array to modify.
    +   * @param {*} placeholder The placeholder to replace.
    +   * @returns {Array} Returns the new array of placeholder indexes.
    +   */
    +  function replaceHolders(array, placeholder) {
    +    var index = -1,
    +      length = array.length,
    +      resIndex = 0,
    +      result = [];
    +    while (++index < length) {
    +      var value = array[index];
    +      if (value === placeholder || value === PLACEHOLDER) {
    +        array[index] = PLACEHOLDER;
    +        result[resIndex++] = index;
    +      }
    +    }
    +    return result;
    +  }
    +
    +  /**
    +   * Converts `set` to an array of its values.
    +   *
    +   * @private
    +   * @param {Object} set The set to convert.
    +   * @returns {Array} Returns the values.
    +   */
    +  function setToArray(set) {
    +    var index = -1,
    +      result = Array(set.size);
    +    set.forEach(function (value) {
    +      result[++index] = value;
    +    });
    +    return result;
    +  }
    +
    +  /**
    +   * Converts `set` to its value-value pairs.
    +   *
    +   * @private
    +   * @param {Object} set The set to convert.
    +   * @returns {Array} Returns the value-value pairs.
    +   */
    +  function setToPairs(set) {
    +    var index = -1,
    +      result = Array(set.size);
    +    set.forEach(function (value) {
    +      result[++index] = [value, value];
    +    });
    +    return result;
    +  }
    +
    +  /**
    +   * A specialized version of `_.indexOf` which performs strict equality
    +   * comparisons of values, i.e. `===`.
    +   *
    +   * @private
    +   * @param {Array} array The array to inspect.
    +   * @param {*} value The value to search for.
    +   * @param {number} fromIndex The index to search from.
    +   * @returns {number} Returns the index of the matched value, else `-1`.
    +   */
    +  function strictIndexOf(array, value, fromIndex) {
    +    var index = fromIndex - 1,
    +      length = array.length;
    +    while (++index < length) {
    +      if (array[index] === value) {
    +        return index;
    +      }
    +    }
    +    return -1;
    +  }
    +
    +  /**
    +   * A specialized version of `_.lastIndexOf` which performs strict equality
    +   * comparisons of values, i.e. `===`.
    +   *
    +   * @private
    +   * @param {Array} array The array to inspect.
    +   * @param {*} value The value to search for.
    +   * @param {number} fromIndex The index to search from.
    +   * @returns {number} Returns the index of the matched value, else `-1`.
    +   */
    +  function strictLastIndexOf(array, value, fromIndex) {
    +    var index = fromIndex + 1;
    +    while (index--) {
    +      if (array[index] === value) {
    +        return index;
    +      }
    +    }
    +    return index;
    +  }
    +
    +  /**
    +   * Gets the number of symbols in `string`.
    +   *
    +   * @private
    +   * @param {string} string The string to inspect.
    +   * @returns {number} Returns the string size.
    +   */
    +  function stringSize(string) {
    +    return hasUnicode(string) ? unicodeSize(string) : asciiSize(string);
    +  }
    +
    +  /**
    +   * Converts `string` to an array.
    +   *
    +   * @private
    +   * @param {string} string The string to convert.
    +   * @returns {Array} Returns the converted array.
    +   */
    +  function stringToArray(string) {
    +    return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string);
    +  }
    +
    +  /**
    +   * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
    +   * character of `string`.
    +   *
    +   * @private
    +   * @param {string} string The string to inspect.
    +   * @returns {number} Returns the index of the last non-whitespace character.
    +   */
    +  function trimmedEndIndex(string) {
    +    var index = string.length;
    +    while (index-- && reWhitespace.test(string.charAt(index))) {}
    +    return index;
    +  }
    +
    +  /**
    +   * Used by `_.unescape` to convert HTML entities to characters.
    +   *
    +   * @private
    +   * @param {string} chr The matched character to unescape.
    +   * @returns {string} Returns the unescaped character.
    +   */
    +  var unescapeHtmlChar = basePropertyOf(htmlUnescapes);
    +
    +  /**
    +   * Gets the size of a Unicode `string`.
    +   *
    +   * @private
    +   * @param {string} string The string inspect.
    +   * @returns {number} Returns the string size.
    +   */
    +  function unicodeSize(string) {
    +    var result = reUnicode.lastIndex = 0;
    +    while (reUnicode.test(string)) {
    +      ++result;
    +    }
    +    return result;
    +  }
    +
    +  /**
    +   * Converts a Unicode `string` to an array.
    +   *
    +   * @private
    +   * @param {string} string The string to convert.
    +   * @returns {Array} Returns the converted array.
    +   */
    +  function unicodeToArray(string) {
    +    return string.match(reUnicode) || [];
    +  }
    +
    +  /**
    +   * Splits a Unicode `string` into an array of its words.
    +   *
    +   * @private
    +   * @param {string} The string to inspect.
    +   * @returns {Array} Returns the words of `string`.
    +   */
    +  function unicodeWords(string) {
    +    return string.match(reUnicodeWord) || [];
    +  }
    +
    +  /*--------------------------------------------------------------------------*/
    +
    +  /**
    +   * Create a new pristine `lodash` function using the `context` object.
    +   *
    +   * @static
    +   * @memberOf _
    +   * @since 1.1.0
    +   * @category Util
    +   * @param {Object} [context=root] The context object.
    +   * @returns {Function} Returns a new `lodash` function.
    +   * @example
    +   *
    +   * _.mixin({ 'foo': _.constant('foo') });
    +   *
    +   * var lodash = _.runInContext();
    +   * lodash.mixin({ 'bar': lodash.constant('bar') });
    +   *
    +   * _.isFunction(_.foo);
    +   * // => true
    +   * _.isFunction(_.bar);
    +   * // => false
    +   *
    +   * lodash.isFunction(lodash.foo);
    +   * // => false
    +   * lodash.isFunction(lodash.bar);
    +   * // => true
    +   *
    +   * // Create a suped-up `defer` in Node.js.
    +   * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;
    +   */
    +  var runInContext = function runInContext(context) {
    +    context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps));
    +
    +    /** Built-in constructor references. */
    +    var Array = context.Array,
    +      Date = context.Date,
    +      Error = context.Error,
    +      Function = context.Function,
    +      Math = context.Math,
    +      Object = context.Object,
    +      RegExp = context.RegExp,
    +      String = context.String,
    +      TypeError = context.TypeError;
    +
    +    /** Used for built-in method references. */
    +    var arrayProto = Array.prototype,
    +      funcProto = Function.prototype,
    +      objectProto = Object.prototype;
    +
    +    /** Used to detect overreaching core-js shims. */
    +    var coreJsData = context['__core-js_shared__'];
    +
    +    /** Used to resolve the decompiled source of functions. */
    +    var funcToString = funcProto.toString;
    +
    +    /** Used to check objects for own properties. */
    +    var hasOwnProperty = objectProto.hasOwnProperty;
    +
    +    /** Used to generate unique IDs. */
    +    var idCounter = 0;
    +
    +    /** Used to detect methods masquerading as native. */
    +    var maskSrcKey = function () {
    +      var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
    +      return uid ? 'Symbol(src)_1.' + uid : '';
    +    }();
    +
    +    /**
    +     * Used to resolve the
    +     * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
    +     * of values.
    +     */
    +    var nativeObjectToString = objectProto.toString;
    +
    +    /** Used to infer the `Object` constructor. */
    +    var objectCtorString = funcToString.call(Object);
    +
    +    /** Used to restore the original `_` reference in `_.noConflict`. */
    +    var oldDash = root._;
    +
    +    /** Used to detect if a method is native. */
    +    var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&').replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$');
    +
    +    /** Built-in value references. */
    +    var Buffer = moduleExports ? context.Buffer : undefined,
    +      Symbol = context.Symbol,
    +      Uint8Array = context.Uint8Array,
    +      allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined,
    +      getPrototype = overArg(Object.getPrototypeOf, Object),
    +      objectCreate = Object.create,
    +      propertyIsEnumerable = objectProto.propertyIsEnumerable,
    +      splice = arrayProto.splice,
    +      spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined,
    +      symIterator = Symbol ? Symbol.iterator : undefined,
    +      symToStringTag = Symbol ? Symbol.toStringTag : undefined;
    +    var defineProperty = function () {
    +      try {
    +        var func = getNative(Object, 'defineProperty');
    +        func({}, '', {});
    +        return func;
    +      } catch (e) {}
    +    }();
    +
    +    /** Mocked built-ins. */
    +    var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout,
    +      ctxNow = Date && Date.now !== root.Date.now && Date.now,
    +      ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout;
    +
    +    /* Built-in method references for those with the same name as other `lodash` methods. */
    +    var nativeCeil = Math.ceil,
    +      nativeFloor = Math.floor,
    +      nativeGetSymbols = Object.getOwnPropertySymbols,
    +      nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,
    +      nativeIsFinite = context.isFinite,
    +      nativeJoin = arrayProto.join,
    +      nativeKeys = overArg(Object.keys, Object),
    +      nativeMax = Math.max,
    +      nativeMin = Math.min,
    +      nativeNow = Date.now,
    +      nativeParseInt = context.parseInt,
    +      nativeRandom = Math.random,
    +      nativeReverse = arrayProto.reverse;
    +
    +    /* Built-in method references that are verified to be native. */
    +    var DataView = getNative(context, 'DataView'),
    +      Map = getNative(context, 'Map'),
    +      Promise = getNative(context, 'Promise'),
    +      Set = getNative(context, 'Set'),
    +      WeakMap = getNative(context, 'WeakMap'),
    +      nativeCreate = getNative(Object, 'create');
    +
    +    /** Used to store function metadata. */
    +    var metaMap = WeakMap && new WeakMap();
    +
    +    /** Used to lookup unminified function names. */
    +    var realNames = {};
    +
    +    /** Used to detect maps, sets, and weakmaps. */
    +    var dataViewCtorString = toSource(DataView),
    +      mapCtorString = toSource(Map),
    +      promiseCtorString = toSource(Promise),
    +      setCtorString = toSource(Set),
    +      weakMapCtorString = toSource(WeakMap);
    +
    +    /** Used to convert symbols to primitives and strings. */
    +    var symbolProto = Symbol ? Symbol.prototype : undefined,
    +      symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,
    +      symbolToString = symbolProto ? symbolProto.toString : undefined;
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates a `lodash` object which wraps `value` to enable implicit method
    +     * chain sequences. Methods that operate on and return arrays, collections,
    +     * and functions can be chained together. Methods that retrieve a single value
    +     * or may return a primitive value will automatically end the chain sequence
    +     * and return the unwrapped value. Otherwise, the value must be unwrapped
    +     * with `_#value`.
    +     *
    +     * Explicit chain sequences, which must be unwrapped with `_#value`, may be
    +     * enabled using `_.chain`.
    +     *
    +     * The execution of chained methods is lazy, that is, it's deferred until
    +     * `_#value` is implicitly or explicitly called.
    +     *
    +     * Lazy evaluation allows several methods to support shortcut fusion.
    +     * Shortcut fusion is an optimization to merge iteratee calls; this avoids
    +     * the creation of intermediate arrays and can greatly reduce the number of
    +     * iteratee executions. Sections of a chain sequence qualify for shortcut
    +     * fusion if the section is applied to an array and iteratees accept only
    +     * one argument. The heuristic for whether a section qualifies for shortcut
    +     * fusion is subject to change.
    +     *
    +     * Chaining is supported in custom builds as long as the `_#value` method is
    +     * directly or indirectly included in the build.
    +     *
    +     * In addition to lodash methods, wrappers have `Array` and `String` methods.
    +     *
    +     * The wrapper `Array` methods are:
    +     * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
    +     *
    +     * The wrapper `String` methods are:
    +     * `replace` and `split`
    +     *
    +     * The wrapper methods that support shortcut fusion are:
    +     * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
    +     * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
    +     * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
    +     *
    +     * The chainable wrapper methods are:
    +     * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
    +     * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
    +     * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
    +     * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
    +     * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
    +     * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
    +     * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
    +     * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
    +     * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
    +     * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
    +     * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
    +     * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
    +     * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
    +     * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
    +     * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
    +     * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
    +     * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
    +     * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
    +     * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
    +     * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
    +     * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
    +     * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
    +     * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
    +     * `zipObject`, `zipObjectDeep`, and `zipWith`
    +     *
    +     * The wrapper methods that are **not** chainable by default are:
    +     * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
    +     * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,
    +     * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,
    +     * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,
    +     * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,
    +     * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,
    +     * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,
    +     * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,
    +     * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,
    +     * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,
    +     * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,
    +     * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,
    +     * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,
    +     * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,
    +     * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,
    +     * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,
    +     * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,
    +     * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,
    +     * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,
    +     * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,
    +     * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,
    +     * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,
    +     * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,
    +     * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,
    +     * `upperFirst`, `value`, and `words`
    +     *
    +     * @name _
    +     * @constructor
    +     * @category Seq
    +     * @param {*} value The value to wrap in a `lodash` instance.
    +     * @returns {Object} Returns the new `lodash` wrapper instance.
    +     * @example
    +     *
    +     * function square(n) {
    +     *   return n * n;
    +     * }
    +     *
    +     * var wrapped = _([1, 2, 3]);
    +     *
    +     * // Returns an unwrapped value.
    +     * wrapped.reduce(_.add);
    +     * // => 6
    +     *
    +     * // Returns a wrapped value.
    +     * var squares = wrapped.map(square);
    +     *
    +     * _.isArray(squares);
    +     * // => false
    +     *
    +     * _.isArray(squares.value());
    +     * // => true
    +     */
    +    function lodash(value) {
    +      if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
    +        if (value instanceof LodashWrapper) {
    +          return value;
    +        }
    +        if (hasOwnProperty.call(value, '__wrapped__')) {
    +          return wrapperClone(value);
    +        }
    +      }
    +      return new LodashWrapper(value);
    +    }
    +
    +    /**
    +     * The base implementation of `_.create` without support for assigning
    +     * properties to the created object.
    +     *
    +     * @private
    +     * @param {Object} proto The object to inherit from.
    +     * @returns {Object} Returns the new object.
    +     */
    +    var baseCreate = function () {
    +      function object() {}
    +      return function (proto) {
    +        if (!isObject(proto)) {
    +          return {};
    +        }
    +        if (objectCreate) {
    +          return objectCreate(proto);
    +        }
    +        object.prototype = proto;
    +        var result = new object();
    +        object.prototype = undefined;
    +        return result;
    +      };
    +    }();
    +
    +    /**
    +     * The function whose prototype chain sequence wrappers inherit from.
    +     *
    +     * @private
    +     */
    +    function baseLodash() {
    +      // No operation performed.
    +    }
    +
    +    /**
    +     * The base constructor for creating `lodash` wrapper objects.
    +     *
    +     * @private
    +     * @param {*} value The value to wrap.
    +     * @param {boolean} [chainAll] Enable explicit method chain sequences.
    +     */
    +    function LodashWrapper(value, chainAll) {
    +      this.__wrapped__ = value;
    +      this.__actions__ = [];
    +      this.__chain__ = !!chainAll;
    +      this.__index__ = 0;
    +      this.__values__ = undefined;
    +    }
    +
    +    /**
    +     * By default, the template delimiters used by lodash are like those in
    +     * embedded Ruby (ERB) as well as ES2015 template strings. Change the
    +     * following template settings to use alternative delimiters.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @type {Object}
    +     */
    +    lodash.templateSettings = {
    +      /**
    +       * Used to detect `data` property values to be HTML-escaped.
    +       *
    +       * @memberOf _.templateSettings
    +       * @type {RegExp}
    +       */
    +      'escape': reEscape,
    +      /**
    +       * Used to detect code to be evaluated.
    +       *
    +       * @memberOf _.templateSettings
    +       * @type {RegExp}
    +       */
    +      'evaluate': reEvaluate,
    +      /**
    +       * Used to detect `data` property values to inject.
    +       *
    +       * @memberOf _.templateSettings
    +       * @type {RegExp}
    +       */
    +      'interpolate': reInterpolate,
    +      /**
    +       * Used to reference the data object in the template text.
    +       *
    +       * @memberOf _.templateSettings
    +       * @type {string}
    +       */
    +      'variable': '',
    +      /**
    +       * Used to import variables into the compiled template.
    +       *
    +       * @memberOf _.templateSettings
    +       * @type {Object}
    +       */
    +      'imports': {
    +        /**
    +         * A reference to the `lodash` function.
    +         *
    +         * @memberOf _.templateSettings.imports
    +         * @type {Function}
    +         */
    +        '_': lodash
    +      }
    +    };
    +
    +    // Ensure wrappers are instances of `baseLodash`.
    +    lodash.prototype = baseLodash.prototype;
    +    lodash.prototype.constructor = lodash;
    +    LodashWrapper.prototype = baseCreate(baseLodash.prototype);
    +    LodashWrapper.prototype.constructor = LodashWrapper;
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
    +     *
    +     * @private
    +     * @constructor
    +     * @param {*} value The value to wrap.
    +     */
    +    function LazyWrapper(value) {
    +      this.__wrapped__ = value;
    +      this.__actions__ = [];
    +      this.__dir__ = 1;
    +      this.__filtered__ = false;
    +      this.__iteratees__ = [];
    +      this.__takeCount__ = MAX_ARRAY_LENGTH;
    +      this.__views__ = [];
    +    }
    +
    +    /**
    +     * Creates a clone of the lazy wrapper object.
    +     *
    +     * @private
    +     * @name clone
    +     * @memberOf LazyWrapper
    +     * @returns {Object} Returns the cloned `LazyWrapper` object.
    +     */
    +    function lazyClone() {
    +      var result = new LazyWrapper(this.__wrapped__);
    +      result.__actions__ = copyArray(this.__actions__);
    +      result.__dir__ = this.__dir__;
    +      result.__filtered__ = this.__filtered__;
    +      result.__iteratees__ = copyArray(this.__iteratees__);
    +      result.__takeCount__ = this.__takeCount__;
    +      result.__views__ = copyArray(this.__views__);
    +      return result;
    +    }
    +
    +    /**
    +     * Reverses the direction of lazy iteration.
    +     *
    +     * @private
    +     * @name reverse
    +     * @memberOf LazyWrapper
    +     * @returns {Object} Returns the new reversed `LazyWrapper` object.
    +     */
    +    function lazyReverse() {
    +      if (this.__filtered__) {
    +        var result = new LazyWrapper(this);
    +        result.__dir__ = -1;
    +        result.__filtered__ = true;
    +      } else {
    +        result = this.clone();
    +        result.__dir__ *= -1;
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Extracts the unwrapped value from its lazy wrapper.
    +     *
    +     * @private
    +     * @name value
    +     * @memberOf LazyWrapper
    +     * @returns {*} Returns the unwrapped value.
    +     */
    +    function lazyValue() {
    +      var array = this.__wrapped__.value(),
    +        dir = this.__dir__,
    +        isArr = isArray(array),
    +        isRight = dir < 0,
    +        arrLength = isArr ? array.length : 0,
    +        view = getView(0, arrLength, this.__views__),
    +        start = view.start,
    +        end = view.end,
    +        length = end - start,
    +        index = isRight ? end : start - 1,
    +        iteratees = this.__iteratees__,
    +        iterLength = iteratees.length,
    +        resIndex = 0,
    +        takeCount = nativeMin(length, this.__takeCount__);
    +      if (!isArr || !isRight && arrLength == length && takeCount == length) {
    +        return baseWrapperValue(array, this.__actions__);
    +      }
    +      var result = [];
    +      outer: while (length-- && resIndex < takeCount) {
    +        index += dir;
    +        var iterIndex = -1,
    +          value = array[index];
    +        while (++iterIndex < iterLength) {
    +          var data = iteratees[iterIndex],
    +            iteratee = data.iteratee,
    +            type = data.type,
    +            computed = iteratee(value);
    +          if (type == LAZY_MAP_FLAG) {
    +            value = computed;
    +          } else if (!computed) {
    +            if (type == LAZY_FILTER_FLAG) {
    +              continue outer;
    +            } else {
    +              break outer;
    +            }
    +          }
    +        }
    +        result[resIndex++] = value;
    +      }
    +      return result;
    +    }
    +
    +    // Ensure `LazyWrapper` is an instance of `baseLodash`.
    +    LazyWrapper.prototype = baseCreate(baseLodash.prototype);
    +    LazyWrapper.prototype.constructor = LazyWrapper;
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates a hash object.
    +     *
    +     * @private
    +     * @constructor
    +     * @param {Array} [entries] The key-value pairs to cache.
    +     */
    +    function Hash(entries) {
    +      var index = -1,
    +        length = entries == null ? 0 : entries.length;
    +      this.clear();
    +      while (++index < length) {
    +        var entry = entries[index];
    +        this.set(entry[0], entry[1]);
    +      }
    +    }
    +
    +    /**
    +     * Removes all key-value entries from the hash.
    +     *
    +     * @private
    +     * @name clear
    +     * @memberOf Hash
    +     */
    +    function hashClear() {
    +      this.__data__ = nativeCreate ? nativeCreate(null) : {};
    +      this.size = 0;
    +    }
    +
    +    /**
    +     * Removes `key` and its value from the hash.
    +     *
    +     * @private
    +     * @name delete
    +     * @memberOf Hash
    +     * @param {Object} hash The hash to modify.
    +     * @param {string} key The key of the value to remove.
    +     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
    +     */
    +    function hashDelete(key) {
    +      var result = this.has(key) && delete this.__data__[key];
    +      this.size -= result ? 1 : 0;
    +      return result;
    +    }
    +
    +    /**
    +     * Gets the hash value for `key`.
    +     *
    +     * @private
    +     * @name get
    +     * @memberOf Hash
    +     * @param {string} key The key of the value to get.
    +     * @returns {*} Returns the entry value.
    +     */
    +    function hashGet(key) {
    +      var data = this.__data__;
    +      if (nativeCreate) {
    +        var result = data[key];
    +        return result === HASH_UNDEFINED ? undefined : result;
    +      }
    +      return hasOwnProperty.call(data, key) ? data[key] : undefined;
    +    }
    +
    +    /**
    +     * Checks if a hash value for `key` exists.
    +     *
    +     * @private
    +     * @name has
    +     * @memberOf Hash
    +     * @param {string} key The key of the entry to check.
    +     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    +     */
    +    function hashHas(key) {
    +      var data = this.__data__;
    +      return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
    +    }
    +
    +    /**
    +     * Sets the hash `key` to `value`.
    +     *
    +     * @private
    +     * @name set
    +     * @memberOf Hash
    +     * @param {string} key The key of the value to set.
    +     * @param {*} value The value to set.
    +     * @returns {Object} Returns the hash instance.
    +     */
    +    function hashSet(key, value) {
    +      var data = this.__data__;
    +      this.size += this.has(key) ? 0 : 1;
    +      data[key] = nativeCreate && value === undefined ? HASH_UNDEFINED : value;
    +      return this;
    +    }
    +
    +    // Add methods to `Hash`.
    +    Hash.prototype.clear = hashClear;
    +    Hash.prototype['delete'] = hashDelete;
    +    Hash.prototype.get = hashGet;
    +    Hash.prototype.has = hashHas;
    +    Hash.prototype.set = hashSet;
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates an list cache object.
    +     *
    +     * @private
    +     * @constructor
    +     * @param {Array} [entries] The key-value pairs to cache.
    +     */
    +    function ListCache(entries) {
    +      var index = -1,
    +        length = entries == null ? 0 : entries.length;
    +      this.clear();
    +      while (++index < length) {
    +        var entry = entries[index];
    +        this.set(entry[0], entry[1]);
    +      }
    +    }
    +
    +    /**
    +     * Removes all key-value entries from the list cache.
    +     *
    +     * @private
    +     * @name clear
    +     * @memberOf ListCache
    +     */
    +    function listCacheClear() {
    +      this.__data__ = [];
    +      this.size = 0;
    +    }
    +
    +    /**
    +     * Removes `key` and its value from the list cache.
    +     *
    +     * @private
    +     * @name delete
    +     * @memberOf ListCache
    +     * @param {string} key The key of the value to remove.
    +     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
    +     */
    +    function listCacheDelete(key) {
    +      var data = this.__data__,
    +        index = assocIndexOf(data, key);
    +      if (index < 0) {
    +        return false;
    +      }
    +      var lastIndex = data.length - 1;
    +      if (index == lastIndex) {
    +        data.pop();
    +      } else {
    +        splice.call(data, index, 1);
    +      }
    +      --this.size;
    +      return true;
    +    }
    +
    +    /**
    +     * Gets the list cache value for `key`.
    +     *
    +     * @private
    +     * @name get
    +     * @memberOf ListCache
    +     * @param {string} key The key of the value to get.
    +     * @returns {*} Returns the entry value.
    +     */
    +    function listCacheGet(key) {
    +      var data = this.__data__,
    +        index = assocIndexOf(data, key);
    +      return index < 0 ? undefined : data[index][1];
    +    }
    +
    +    /**
    +     * Checks if a list cache value for `key` exists.
    +     *
    +     * @private
    +     * @name has
    +     * @memberOf ListCache
    +     * @param {string} key The key of the entry to check.
    +     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    +     */
    +    function listCacheHas(key) {
    +      return assocIndexOf(this.__data__, key) > -1;
    +    }
    +
    +    /**
    +     * Sets the list cache `key` to `value`.
    +     *
    +     * @private
    +     * @name set
    +     * @memberOf ListCache
    +     * @param {string} key The key of the value to set.
    +     * @param {*} value The value to set.
    +     * @returns {Object} Returns the list cache instance.
    +     */
    +    function listCacheSet(key, value) {
    +      var data = this.__data__,
    +        index = assocIndexOf(data, key);
    +      if (index < 0) {
    +        ++this.size;
    +        data.push([key, value]);
    +      } else {
    +        data[index][1] = value;
    +      }
    +      return this;
    +    }
    +
    +    // Add methods to `ListCache`.
    +    ListCache.prototype.clear = listCacheClear;
    +    ListCache.prototype['delete'] = listCacheDelete;
    +    ListCache.prototype.get = listCacheGet;
    +    ListCache.prototype.has = listCacheHas;
    +    ListCache.prototype.set = listCacheSet;
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates a map cache object to store key-value pairs.
    +     *
    +     * @private
    +     * @constructor
    +     * @param {Array} [entries] The key-value pairs to cache.
    +     */
    +    function MapCache(entries) {
    +      var index = -1,
    +        length = entries == null ? 0 : entries.length;
    +      this.clear();
    +      while (++index < length) {
    +        var entry = entries[index];
    +        this.set(entry[0], entry[1]);
    +      }
    +    }
    +
    +    /**
    +     * Removes all key-value entries from the map.
    +     *
    +     * @private
    +     * @name clear
    +     * @memberOf MapCache
    +     */
    +    function mapCacheClear() {
    +      this.size = 0;
    +      this.__data__ = {
    +        'hash': new Hash(),
    +        'map': new (Map || ListCache)(),
    +        'string': new Hash()
    +      };
    +    }
    +
    +    /**
    +     * Removes `key` and its value from the map.
    +     *
    +     * @private
    +     * @name delete
    +     * @memberOf MapCache
    +     * @param {string} key The key of the value to remove.
    +     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
    +     */
    +    function mapCacheDelete(key) {
    +      var result = getMapData(this, key)['delete'](key);
    +      this.size -= result ? 1 : 0;
    +      return result;
    +    }
    +
    +    /**
    +     * Gets the map value for `key`.
    +     *
    +     * @private
    +     * @name get
    +     * @memberOf MapCache
    +     * @param {string} key The key of the value to get.
    +     * @returns {*} Returns the entry value.
    +     */
    +    function mapCacheGet(key) {
    +      return getMapData(this, key).get(key);
    +    }
    +
    +    /**
    +     * Checks if a map value for `key` exists.
    +     *
    +     * @private
    +     * @name has
    +     * @memberOf MapCache
    +     * @param {string} key The key of the entry to check.
    +     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    +     */
    +    function mapCacheHas(key) {
    +      return getMapData(this, key).has(key);
    +    }
    +
    +    /**
    +     * Sets the map `key` to `value`.
    +     *
    +     * @private
    +     * @name set
    +     * @memberOf MapCache
    +     * @param {string} key The key of the value to set.
    +     * @param {*} value The value to set.
    +     * @returns {Object} Returns the map cache instance.
    +     */
    +    function mapCacheSet(key, value) {
    +      var data = getMapData(this, key),
    +        size = data.size;
    +      data.set(key, value);
    +      this.size += data.size == size ? 0 : 1;
    +      return this;
    +    }
    +
    +    // Add methods to `MapCache`.
    +    MapCache.prototype.clear = mapCacheClear;
    +    MapCache.prototype['delete'] = mapCacheDelete;
    +    MapCache.prototype.get = mapCacheGet;
    +    MapCache.prototype.has = mapCacheHas;
    +    MapCache.prototype.set = mapCacheSet;
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     *
    +     * Creates an array cache object to store unique values.
    +     *
    +     * @private
    +     * @constructor
    +     * @param {Array} [values] The values to cache.
    +     */
    +    function SetCache(values) {
    +      var index = -1,
    +        length = values == null ? 0 : values.length;
    +      this.__data__ = new MapCache();
    +      while (++index < length) {
    +        this.add(values[index]);
    +      }
    +    }
    +
    +    /**
    +     * Adds `value` to the array cache.
    +     *
    +     * @private
    +     * @name add
    +     * @memberOf SetCache
    +     * @alias push
    +     * @param {*} value The value to cache.
    +     * @returns {Object} Returns the cache instance.
    +     */
    +    function setCacheAdd(value) {
    +      this.__data__.set(value, HASH_UNDEFINED);
    +      return this;
    +    }
    +
    +    /**
    +     * Checks if `value` is in the array cache.
    +     *
    +     * @private
    +     * @name has
    +     * @memberOf SetCache
    +     * @param {*} value The value to search for.
    +     * @returns {number} Returns `true` if `value` is found, else `false`.
    +     */
    +    function setCacheHas(value) {
    +      return this.__data__.has(value);
    +    }
    +
    +    // Add methods to `SetCache`.
    +    SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
    +    SetCache.prototype.has = setCacheHas;
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates a stack cache object to store key-value pairs.
    +     *
    +     * @private
    +     * @constructor
    +     * @param {Array} [entries] The key-value pairs to cache.
    +     */
    +    function Stack(entries) {
    +      var data = this.__data__ = new ListCache(entries);
    +      this.size = data.size;
    +    }
    +
    +    /**
    +     * Removes all key-value entries from the stack.
    +     *
    +     * @private
    +     * @name clear
    +     * @memberOf Stack
    +     */
    +    function stackClear() {
    +      this.__data__ = new ListCache();
    +      this.size = 0;
    +    }
    +
    +    /**
    +     * Removes `key` and its value from the stack.
    +     *
    +     * @private
    +     * @name delete
    +     * @memberOf Stack
    +     * @param {string} key The key of the value to remove.
    +     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
    +     */
    +    function stackDelete(key) {
    +      var data = this.__data__,
    +        result = data['delete'](key);
    +      this.size = data.size;
    +      return result;
    +    }
    +
    +    /**
    +     * Gets the stack value for `key`.
    +     *
    +     * @private
    +     * @name get
    +     * @memberOf Stack
    +     * @param {string} key The key of the value to get.
    +     * @returns {*} Returns the entry value.
    +     */
    +    function stackGet(key) {
    +      return this.__data__.get(key);
    +    }
    +
    +    /**
    +     * Checks if a stack value for `key` exists.
    +     *
    +     * @private
    +     * @name has
    +     * @memberOf Stack
    +     * @param {string} key The key of the entry to check.
    +     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    +     */
    +    function stackHas(key) {
    +      return this.__data__.has(key);
    +    }
    +
    +    /**
    +     * Sets the stack `key` to `value`.
    +     *
    +     * @private
    +     * @name set
    +     * @memberOf Stack
    +     * @param {string} key The key of the value to set.
    +     * @param {*} value The value to set.
    +     * @returns {Object} Returns the stack cache instance.
    +     */
    +    function stackSet(key, value) {
    +      var data = this.__data__;
    +      if (data instanceof ListCache) {
    +        var pairs = data.__data__;
    +        if (!Map || pairs.length < LARGE_ARRAY_SIZE - 1) {
    +          pairs.push([key, value]);
    +          this.size = ++data.size;
    +          return this;
    +        }
    +        data = this.__data__ = new MapCache(pairs);
    +      }
    +      data.set(key, value);
    +      this.size = data.size;
    +      return this;
    +    }
    +
    +    // Add methods to `Stack`.
    +    Stack.prototype.clear = stackClear;
    +    Stack.prototype['delete'] = stackDelete;
    +    Stack.prototype.get = stackGet;
    +    Stack.prototype.has = stackHas;
    +    Stack.prototype.set = stackSet;
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates an array of the enumerable property names of the array-like `value`.
    +     *
    +     * @private
    +     * @param {*} value The value to query.
    +     * @param {boolean} inherited Specify returning inherited property names.
    +     * @returns {Array} Returns the array of property names.
    +     */
    +    function arrayLikeKeys(value, inherited) {
    +      var isArr = isArray(value),
    +        isArg = !isArr && isArguments(value),
    +        isBuff = !isArr && !isArg && isBuffer(value),
    +        isType = !isArr && !isArg && !isBuff && isTypedArray(value),
    +        skipIndexes = isArr || isArg || isBuff || isType,
    +        result = skipIndexes ? baseTimes(value.length, String) : [],
    +        length = result.length;
    +      for (var key in value) {
    +        if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (
    +        // Safari 9 has enumerable `arguments.length` in strict mode.
    +        key == 'length' ||
    +        // Node.js 0.10 has enumerable non-index properties on buffers.
    +        isBuff && (key == 'offset' || key == 'parent') ||
    +        // PhantomJS 2 has enumerable non-index properties on typed arrays.
    +        isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset') ||
    +        // Skip index properties.
    +        isIndex(key, length)))) {
    +          result.push(key);
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * A specialized version of `_.sample` for arrays.
    +     *
    +     * @private
    +     * @param {Array} array The array to sample.
    +     * @returns {*} Returns the random element.
    +     */
    +    function arraySample(array) {
    +      var length = array.length;
    +      return length ? array[baseRandom(0, length - 1)] : undefined;
    +    }
    +
    +    /**
    +     * A specialized version of `_.sampleSize` for arrays.
    +     *
    +     * @private
    +     * @param {Array} array The array to sample.
    +     * @param {number} n The number of elements to sample.
    +     * @returns {Array} Returns the random elements.
    +     */
    +    function arraySampleSize(array, n) {
    +      return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length));
    +    }
    +
    +    /**
    +     * A specialized version of `_.shuffle` for arrays.
    +     *
    +     * @private
    +     * @param {Array} array The array to shuffle.
    +     * @returns {Array} Returns the new shuffled array.
    +     */
    +    function arrayShuffle(array) {
    +      return shuffleSelf(copyArray(array));
    +    }
    +
    +    /**
    +     * This function is like `assignValue` except that it doesn't assign
    +     * `undefined` values.
    +     *
    +     * @private
    +     * @param {Object} object The object to modify.
    +     * @param {string} key The key of the property to assign.
    +     * @param {*} value The value to assign.
    +     */
    +    function assignMergeValue(object, key, value) {
    +      if (value !== undefined && !eq(object[key], value) || value === undefined && !(key in object)) {
    +        baseAssignValue(object, key, value);
    +      }
    +    }
    +
    +    /**
    +     * Assigns `value` to `key` of `object` if the existing value is not equivalent
    +     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    +     * for equality comparisons.
    +     *
    +     * @private
    +     * @param {Object} object The object to modify.
    +     * @param {string} key The key of the property to assign.
    +     * @param {*} value The value to assign.
    +     */
    +    function assignValue(object, key, value) {
    +      var objValue = object[key];
    +      if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || value === undefined && !(key in object)) {
    +        baseAssignValue(object, key, value);
    +      }
    +    }
    +
    +    /**
    +     * Gets the index at which the `key` is found in `array` of key-value pairs.
    +     *
    +     * @private
    +     * @param {Array} array The array to inspect.
    +     * @param {*} key The key to search for.
    +     * @returns {number} Returns the index of the matched value, else `-1`.
    +     */
    +    function assocIndexOf(array, key) {
    +      var length = array.length;
    +      while (length--) {
    +        if (eq(array[length][0], key)) {
    +          return length;
    +        }
    +      }
    +      return -1;
    +    }
    +
    +    /**
    +     * Aggregates elements of `collection` on `accumulator` with keys transformed
    +     * by `iteratee` and values set by `setter`.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} setter The function to set `accumulator` values.
    +     * @param {Function} iteratee The iteratee to transform keys.
    +     * @param {Object} accumulator The initial aggregated object.
    +     * @returns {Function} Returns `accumulator`.
    +     */
    +    function baseAggregator(collection, setter, iteratee, accumulator) {
    +      baseEach(collection, function (value, key, collection) {
    +        setter(accumulator, value, iteratee(value), collection);
    +      });
    +      return accumulator;
    +    }
    +
    +    /**
    +     * The base implementation of `_.assign` without support for multiple sources
    +     * or `customizer` functions.
    +     *
    +     * @private
    +     * @param {Object} object The destination object.
    +     * @param {Object} source The source object.
    +     * @returns {Object} Returns `object`.
    +     */
    +    function baseAssign(object, source) {
    +      return object && copyObject(source, keys(source), object);
    +    }
    +
    +    /**
    +     * The base implementation of `_.assignIn` without support for multiple sources
    +     * or `customizer` functions.
    +     *
    +     * @private
    +     * @param {Object} object The destination object.
    +     * @param {Object} source The source object.
    +     * @returns {Object} Returns `object`.
    +     */
    +    function baseAssignIn(object, source) {
    +      return object && copyObject(source, keysIn(source), object);
    +    }
    +
    +    /**
    +     * The base implementation of `assignValue` and `assignMergeValue` without
    +     * value checks.
    +     *
    +     * @private
    +     * @param {Object} object The object to modify.
    +     * @param {string} key The key of the property to assign.
    +     * @param {*} value The value to assign.
    +     */
    +    function baseAssignValue(object, key, value) {
    +      if (key == '__proto__' && defineProperty) {
    +        defineProperty(object, key, {
    +          'configurable': true,
    +          'enumerable': true,
    +          'value': value,
    +          'writable': true
    +        });
    +      } else {
    +        object[key] = value;
    +      }
    +    }
    +
    +    /**
    +     * The base implementation of `_.at` without support for individual paths.
    +     *
    +     * @private
    +     * @param {Object} object The object to iterate over.
    +     * @param {string[]} paths The property paths to pick.
    +     * @returns {Array} Returns the picked elements.
    +     */
    +    function baseAt(object, paths) {
    +      var index = -1,
    +        length = paths.length,
    +        result = Array(length),
    +        skip = object == null;
    +      while (++index < length) {
    +        result[index] = skip ? undefined : get(object, paths[index]);
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.clamp` which doesn't coerce arguments.
    +     *
    +     * @private
    +     * @param {number} number The number to clamp.
    +     * @param {number} [lower] The lower bound.
    +     * @param {number} upper The upper bound.
    +     * @returns {number} Returns the clamped number.
    +     */
    +    function baseClamp(number, lower, upper) {
    +      if (number === number) {
    +        if (upper !== undefined) {
    +          number = number <= upper ? number : upper;
    +        }
    +        if (lower !== undefined) {
    +          number = number >= lower ? number : lower;
    +        }
    +      }
    +      return number;
    +    }
    +
    +    /**
    +     * The base implementation of `_.clone` and `_.cloneDeep` which tracks
    +     * traversed objects.
    +     *
    +     * @private
    +     * @param {*} value The value to clone.
    +     * @param {boolean} bitmask The bitmask flags.
    +     *  1 - Deep clone
    +     *  2 - Flatten inherited properties
    +     *  4 - Clone symbols
    +     * @param {Function} [customizer] The function to customize cloning.
    +     * @param {string} [key] The key of `value`.
    +     * @param {Object} [object] The parent object of `value`.
    +     * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
    +     * @returns {*} Returns the cloned value.
    +     */
    +    function baseClone(value, bitmask, customizer, key, object, stack) {
    +      var result,
    +        isDeep = bitmask & CLONE_DEEP_FLAG,
    +        isFlat = bitmask & CLONE_FLAT_FLAG,
    +        isFull = bitmask & CLONE_SYMBOLS_FLAG;
    +      if (customizer) {
    +        result = object ? customizer(value, key, object, stack) : customizer(value);
    +      }
    +      if (result !== undefined) {
    +        return result;
    +      }
    +      if (!isObject(value)) {
    +        return value;
    +      }
    +      var isArr = isArray(value);
    +      if (isArr) {
    +        result = initCloneArray(value);
    +        if (!isDeep) {
    +          return copyArray(value, result);
    +        }
    +      } else {
    +        var tag = getTag(value),
    +          isFunc = tag == funcTag || tag == genTag;
    +        if (isBuffer(value)) {
    +          return cloneBuffer(value, isDeep);
    +        }
    +        if (tag == objectTag || tag == argsTag || isFunc && !object) {
    +          result = isFlat || isFunc ? {} : initCloneObject(value);
    +          if (!isDeep) {
    +            return isFlat ? copySymbolsIn(value, baseAssignIn(result, value)) : copySymbols(value, baseAssign(result, value));
    +          }
    +        } else {
    +          if (!cloneableTags[tag]) {
    +            return object ? value : {};
    +          }
    +          result = initCloneByTag(value, tag, isDeep);
    +        }
    +      }
    +      // Check for circular references and return its corresponding clone.
    +      stack || (stack = new Stack());
    +      var stacked = stack.get(value);
    +      if (stacked) {
    +        return stacked;
    +      }
    +      stack.set(value, result);
    +      if (isSet(value)) {
    +        value.forEach(function (subValue) {
    +          result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
    +        });
    +      } else if (isMap(value)) {
    +        value.forEach(function (subValue, key) {
    +          result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
    +        });
    +      }
    +      var keysFunc = isFull ? isFlat ? getAllKeysIn : getAllKeys : isFlat ? keysIn : keys;
    +      var props = isArr ? undefined : keysFunc(value);
    +      arrayEach(props || value, function (subValue, key) {
    +        if (props) {
    +          key = subValue;
    +          subValue = value[key];
    +        }
    +        // Recursively populate clone (susceptible to call stack limits).
    +        assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
    +      });
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.conforms` which doesn't clone `source`.
    +     *
    +     * @private
    +     * @param {Object} source The object of property predicates to conform to.
    +     * @returns {Function} Returns the new spec function.
    +     */
    +    function baseConforms(source) {
    +      var props = keys(source);
    +      return function (object) {
    +        return baseConformsTo(object, source, props);
    +      };
    +    }
    +
    +    /**
    +     * The base implementation of `_.conformsTo` which accepts `props` to check.
    +     *
    +     * @private
    +     * @param {Object} object The object to inspect.
    +     * @param {Object} source The object of property predicates to conform to.
    +     * @returns {boolean} Returns `true` if `object` conforms, else `false`.
    +     */
    +    function baseConformsTo(object, source, props) {
    +      var length = props.length;
    +      if (object == null) {
    +        return !length;
    +      }
    +      object = Object(object);
    +      while (length--) {
    +        var key = props[length],
    +          predicate = source[key],
    +          value = object[key];
    +        if (value === undefined && !(key in object) || !predicate(value)) {
    +          return false;
    +        }
    +      }
    +      return true;
    +    }
    +
    +    /**
    +     * The base implementation of `_.delay` and `_.defer` which accepts `args`
    +     * to provide to `func`.
    +     *
    +     * @private
    +     * @param {Function} func The function to delay.
    +     * @param {number} wait The number of milliseconds to delay invocation.
    +     * @param {Array} args The arguments to provide to `func`.
    +     * @returns {number|Object} Returns the timer id or timeout object.
    +     */
    +    function baseDelay(func, wait, args) {
    +      if (typeof func != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      return setTimeout(function () {
    +        func.apply(undefined, args);
    +      }, wait);
    +    }
    +
    +    /**
    +     * The base implementation of methods like `_.difference` without support
    +     * for excluding multiple arrays or iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array} array The array to inspect.
    +     * @param {Array} values The values to exclude.
    +     * @param {Function} [iteratee] The iteratee invoked per element.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns the new array of filtered values.
    +     */
    +    function baseDifference(array, values, iteratee, comparator) {
    +      var index = -1,
    +        includes = arrayIncludes,
    +        isCommon = true,
    +        length = array.length,
    +        result = [],
    +        valuesLength = values.length;
    +      if (!length) {
    +        return result;
    +      }
    +      if (iteratee) {
    +        values = arrayMap(values, baseUnary(iteratee));
    +      }
    +      if (comparator) {
    +        includes = arrayIncludesWith;
    +        isCommon = false;
    +      } else if (values.length >= LARGE_ARRAY_SIZE) {
    +        includes = cacheHas;
    +        isCommon = false;
    +        values = new SetCache(values);
    +      }
    +      outer: while (++index < length) {
    +        var value = array[index],
    +          computed = iteratee == null ? value : iteratee(value);
    +        value = comparator || value !== 0 ? value : 0;
    +        if (isCommon && computed === computed) {
    +          var valuesIndex = valuesLength;
    +          while (valuesIndex--) {
    +            if (values[valuesIndex] === computed) {
    +              continue outer;
    +            }
    +          }
    +          result.push(value);
    +        } else if (!includes(values, computed, comparator)) {
    +          result.push(value);
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.forEach` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} iteratee The function invoked per iteration.
    +     * @returns {Array|Object} Returns `collection`.
    +     */
    +    var baseEach = createBaseEach(baseForOwn);
    +
    +    /**
    +     * The base implementation of `_.forEachRight` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} iteratee The function invoked per iteration.
    +     * @returns {Array|Object} Returns `collection`.
    +     */
    +    var baseEachRight = createBaseEach(baseForOwnRight, true);
    +
    +    /**
    +     * The base implementation of `_.every` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} predicate The function invoked per iteration.
    +     * @returns {boolean} Returns `true` if all elements pass the predicate check,
    +     *  else `false`
    +     */
    +    function baseEvery(collection, predicate) {
    +      var result = true;
    +      baseEach(collection, function (value, index, collection) {
    +        result = !!predicate(value, index, collection);
    +        return result;
    +      });
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of methods like `_.max` and `_.min` which accepts a
    +     * `comparator` to determine the extremum value.
    +     *
    +     * @private
    +     * @param {Array} array The array to iterate over.
    +     * @param {Function} iteratee The iteratee invoked per iteration.
    +     * @param {Function} comparator The comparator used to compare values.
    +     * @returns {*} Returns the extremum value.
    +     */
    +    function baseExtremum(array, iteratee, comparator) {
    +      var index = -1,
    +        length = array.length;
    +      while (++index < length) {
    +        var value = array[index],
    +          current = iteratee(value);
    +        if (current != null && (computed === undefined ? current === current && !isSymbol(current) : comparator(current, computed))) {
    +          var computed = current,
    +            result = value;
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.fill` without an iteratee call guard.
    +     *
    +     * @private
    +     * @param {Array} array The array to fill.
    +     * @param {*} value The value to fill `array` with.
    +     * @param {number} [start=0] The start position.
    +     * @param {number} [end=array.length] The end position.
    +     * @returns {Array} Returns `array`.
    +     */
    +    function baseFill(array, value, start, end) {
    +      var length = array.length;
    +      start = toInteger(start);
    +      if (start < 0) {
    +        start = -start > length ? 0 : length + start;
    +      }
    +      end = end === undefined || end > length ? length : toInteger(end);
    +      if (end < 0) {
    +        end += length;
    +      }
    +      end = start > end ? 0 : toLength(end);
    +      while (start < end) {
    +        array[start++] = value;
    +      }
    +      return array;
    +    }
    +
    +    /**
    +     * The base implementation of `_.filter` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} predicate The function invoked per iteration.
    +     * @returns {Array} Returns the new filtered array.
    +     */
    +    function baseFilter(collection, predicate) {
    +      var result = [];
    +      baseEach(collection, function (value, index, collection) {
    +        if (predicate(value, index, collection)) {
    +          result.push(value);
    +        }
    +      });
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.flatten` with support for restricting flattening.
    +     *
    +     * @private
    +     * @param {Array} array The array to flatten.
    +     * @param {number} depth The maximum recursion depth.
    +     * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
    +     * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
    +     * @param {Array} [result=[]] The initial result value.
    +     * @returns {Array} Returns the new flattened array.
    +     */
    +    function baseFlatten(array, depth, predicate, isStrict, result) {
    +      var index = -1,
    +        length = array.length;
    +      predicate || (predicate = isFlattenable);
    +      result || (result = []);
    +      while (++index < length) {
    +        var value = array[index];
    +        if (depth > 0 && predicate(value)) {
    +          if (depth > 1) {
    +            // Recursively flatten arrays (susceptible to call stack limits).
    +            baseFlatten(value, depth - 1, predicate, isStrict, result);
    +          } else {
    +            arrayPush(result, value);
    +          }
    +        } else if (!isStrict) {
    +          result[result.length] = value;
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `baseForOwn` which iterates over `object`
    +     * properties returned by `keysFunc` and invokes `iteratee` for each property.
    +     * Iteratee functions may exit iteration early by explicitly returning `false`.
    +     *
    +     * @private
    +     * @param {Object} object The object to iterate over.
    +     * @param {Function} iteratee The function invoked per iteration.
    +     * @param {Function} keysFunc The function to get the keys of `object`.
    +     * @returns {Object} Returns `object`.
    +     */
    +    var baseFor = createBaseFor();
    +
    +    /**
    +     * This function is like `baseFor` except that it iterates over properties
    +     * in the opposite order.
    +     *
    +     * @private
    +     * @param {Object} object The object to iterate over.
    +     * @param {Function} iteratee The function invoked per iteration.
    +     * @param {Function} keysFunc The function to get the keys of `object`.
    +     * @returns {Object} Returns `object`.
    +     */
    +    var baseForRight = createBaseFor(true);
    +
    +    /**
    +     * The base implementation of `_.forOwn` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Object} object The object to iterate over.
    +     * @param {Function} iteratee The function invoked per iteration.
    +     * @returns {Object} Returns `object`.
    +     */
    +    function baseForOwn(object, iteratee) {
    +      return object && baseFor(object, iteratee, keys);
    +    }
    +
    +    /**
    +     * The base implementation of `_.forOwnRight` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Object} object The object to iterate over.
    +     * @param {Function} iteratee The function invoked per iteration.
    +     * @returns {Object} Returns `object`.
    +     */
    +    function baseForOwnRight(object, iteratee) {
    +      return object && baseForRight(object, iteratee, keys);
    +    }
    +
    +    /**
    +     * The base implementation of `_.functions` which creates an array of
    +     * `object` function property names filtered from `props`.
    +     *
    +     * @private
    +     * @param {Object} object The object to inspect.
    +     * @param {Array} props The property names to filter.
    +     * @returns {Array} Returns the function names.
    +     */
    +    function baseFunctions(object, props) {
    +      return arrayFilter(props, function (key) {
    +        return isFunction(object[key]);
    +      });
    +    }
    +
    +    /**
    +     * The base implementation of `_.get` without support for default values.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @param {Array|string} path The path of the property to get.
    +     * @returns {*} Returns the resolved value.
    +     */
    +    function baseGet(object, path) {
    +      path = castPath(path, object);
    +      var index = 0,
    +        length = path.length;
    +      while (object != null && index < length) {
    +        object = object[toKey(path[index++])];
    +      }
    +      return index && index == length ? object : undefined;
    +    }
    +
    +    /**
    +     * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
    +     * `keysFunc` and `symbolsFunc` to get the enumerable property names and
    +     * symbols of `object`.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @param {Function} keysFunc The function to get the keys of `object`.
    +     * @param {Function} symbolsFunc The function to get the symbols of `object`.
    +     * @returns {Array} Returns the array of property names and symbols.
    +     */
    +    function baseGetAllKeys(object, keysFunc, symbolsFunc) {
    +      var result = keysFunc(object);
    +      return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
    +    }
    +
    +    /**
    +     * The base implementation of `getTag` without fallbacks for buggy environments.
    +     *
    +     * @private
    +     * @param {*} value The value to query.
    +     * @returns {string} Returns the `toStringTag`.
    +     */
    +    function baseGetTag(value) {
    +      if (value == null) {
    +        return value === undefined ? undefinedTag : nullTag;
    +      }
    +      return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);
    +    }
    +
    +    /**
    +     * The base implementation of `_.gt` which doesn't coerce arguments.
    +     *
    +     * @private
    +     * @param {*} value The value to compare.
    +     * @param {*} other The other value to compare.
    +     * @returns {boolean} Returns `true` if `value` is greater than `other`,
    +     *  else `false`.
    +     */
    +    function baseGt(value, other) {
    +      return value > other;
    +    }
    +
    +    /**
    +     * The base implementation of `_.has` without support for deep paths.
    +     *
    +     * @private
    +     * @param {Object} [object] The object to query.
    +     * @param {Array|string} key The key to check.
    +     * @returns {boolean} Returns `true` if `key` exists, else `false`.
    +     */
    +    function baseHas(object, key) {
    +      return object != null && hasOwnProperty.call(object, key);
    +    }
    +
    +    /**
    +     * The base implementation of `_.hasIn` without support for deep paths.
    +     *
    +     * @private
    +     * @param {Object} [object] The object to query.
    +     * @param {Array|string} key The key to check.
    +     * @returns {boolean} Returns `true` if `key` exists, else `false`.
    +     */
    +    function baseHasIn(object, key) {
    +      return object != null && key in Object(object);
    +    }
    +
    +    /**
    +     * The base implementation of `_.inRange` which doesn't coerce arguments.
    +     *
    +     * @private
    +     * @param {number} number The number to check.
    +     * @param {number} start The start of the range.
    +     * @param {number} end The end of the range.
    +     * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
    +     */
    +    function baseInRange(number, start, end) {
    +      return number >= nativeMin(start, end) && number < nativeMax(start, end);
    +    }
    +
    +    /**
    +     * The base implementation of methods like `_.intersection`, without support
    +     * for iteratee shorthands, that accepts an array of arrays to inspect.
    +     *
    +     * @private
    +     * @param {Array} arrays The arrays to inspect.
    +     * @param {Function} [iteratee] The iteratee invoked per element.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns the new array of shared values.
    +     */
    +    function baseIntersection(arrays, iteratee, comparator) {
    +      var includes = comparator ? arrayIncludesWith : arrayIncludes,
    +        length = arrays[0].length,
    +        othLength = arrays.length,
    +        othIndex = othLength,
    +        caches = Array(othLength),
    +        maxLength = Infinity,
    +        result = [];
    +      while (othIndex--) {
    +        var array = arrays[othIndex];
    +        if (othIndex && iteratee) {
    +          array = arrayMap(array, baseUnary(iteratee));
    +        }
    +        maxLength = nativeMin(array.length, maxLength);
    +        caches[othIndex] = !comparator && (iteratee || length >= 120 && array.length >= 120) ? new SetCache(othIndex && array) : undefined;
    +      }
    +      array = arrays[0];
    +      var index = -1,
    +        seen = caches[0];
    +      outer: while (++index < length && result.length < maxLength) {
    +        var value = array[index],
    +          computed = iteratee ? iteratee(value) : value;
    +        value = comparator || value !== 0 ? value : 0;
    +        if (!(seen ? cacheHas(seen, computed) : includes(result, computed, comparator))) {
    +          othIndex = othLength;
    +          while (--othIndex) {
    +            var cache = caches[othIndex];
    +            if (!(cache ? cacheHas(cache, computed) : includes(arrays[othIndex], computed, comparator))) {
    +              continue outer;
    +            }
    +          }
    +          if (seen) {
    +            seen.push(computed);
    +          }
    +          result.push(value);
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.invert` and `_.invertBy` which inverts
    +     * `object` with values transformed by `iteratee` and set by `setter`.
    +     *
    +     * @private
    +     * @param {Object} object The object to iterate over.
    +     * @param {Function} setter The function to set `accumulator` values.
    +     * @param {Function} iteratee The iteratee to transform values.
    +     * @param {Object} accumulator The initial inverted object.
    +     * @returns {Function} Returns `accumulator`.
    +     */
    +    function baseInverter(object, setter, iteratee, accumulator) {
    +      baseForOwn(object, function (value, key, object) {
    +        setter(accumulator, iteratee(value), key, object);
    +      });
    +      return accumulator;
    +    }
    +
    +    /**
    +     * The base implementation of `_.invoke` without support for individual
    +     * method arguments.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @param {Array|string} path The path of the method to invoke.
    +     * @param {Array} args The arguments to invoke the method with.
    +     * @returns {*} Returns the result of the invoked method.
    +     */
    +    function baseInvoke(object, path, args) {
    +      path = castPath(path, object);
    +      object = parent(object, path);
    +      var func = object == null ? object : object[toKey(last(path))];
    +      return func == null ? undefined : apply(func, object, args);
    +    }
    +
    +    /**
    +     * The base implementation of `_.isArguments`.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is an `arguments` object,
    +     */
    +    function baseIsArguments(value) {
    +      return isObjectLike(value) && baseGetTag(value) == argsTag;
    +    }
    +
    +    /**
    +     * The base implementation of `_.isArrayBuffer` without Node.js optimizations.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
    +     */
    +    function baseIsArrayBuffer(value) {
    +      return isObjectLike(value) && baseGetTag(value) == arrayBufferTag;
    +    }
    +
    +    /**
    +     * The base implementation of `_.isDate` without Node.js optimizations.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
    +     */
    +    function baseIsDate(value) {
    +      return isObjectLike(value) && baseGetTag(value) == dateTag;
    +    }
    +
    +    /**
    +     * The base implementation of `_.isEqual` which supports partial comparisons
    +     * and tracks traversed objects.
    +     *
    +     * @private
    +     * @param {*} value The value to compare.
    +     * @param {*} other The other value to compare.
    +     * @param {boolean} bitmask The bitmask flags.
    +     *  1 - Unordered comparison
    +     *  2 - Partial comparison
    +     * @param {Function} [customizer] The function to customize comparisons.
    +     * @param {Object} [stack] Tracks traversed `value` and `other` objects.
    +     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
    +     */
    +    function baseIsEqual(value, other, bitmask, customizer, stack) {
    +      if (value === other) {
    +        return true;
    +      }
    +      if (value == null || other == null || !isObjectLike(value) && !isObjectLike(other)) {
    +        return value !== value && other !== other;
    +      }
    +      return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
    +    }
    +
    +    /**
    +     * A specialized version of `baseIsEqual` for arrays and objects which performs
    +     * deep comparisons and tracks traversed objects enabling objects with circular
    +     * references to be compared.
    +     *
    +     * @private
    +     * @param {Object} object The object to compare.
    +     * @param {Object} other The other object to compare.
    +     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
    +     * @param {Function} customizer The function to customize comparisons.
    +     * @param {Function} equalFunc The function to determine equivalents of values.
    +     * @param {Object} [stack] Tracks traversed `object` and `other` objects.
    +     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
    +     */
    +    function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
    +      var objIsArr = isArray(object),
    +        othIsArr = isArray(other),
    +        objTag = objIsArr ? arrayTag : getTag(object),
    +        othTag = othIsArr ? arrayTag : getTag(other);
    +      objTag = objTag == argsTag ? objectTag : objTag;
    +      othTag = othTag == argsTag ? objectTag : othTag;
    +      var objIsObj = objTag == objectTag,
    +        othIsObj = othTag == objectTag,
    +        isSameTag = objTag == othTag;
    +      if (isSameTag && isBuffer(object)) {
    +        if (!isBuffer(other)) {
    +          return false;
    +        }
    +        objIsArr = true;
    +        objIsObj = false;
    +      }
    +      if (isSameTag && !objIsObj) {
    +        stack || (stack = new Stack());
    +        return objIsArr || isTypedArray(object) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
    +      }
    +      if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
    +        var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
    +          othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
    +        if (objIsWrapped || othIsWrapped) {
    +          var objUnwrapped = objIsWrapped ? object.value() : object,
    +            othUnwrapped = othIsWrapped ? other.value() : other;
    +          stack || (stack = new Stack());
    +          return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
    +        }
    +      }
    +      if (!isSameTag) {
    +        return false;
    +      }
    +      stack || (stack = new Stack());
    +      return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
    +    }
    +
    +    /**
    +     * The base implementation of `_.isMap` without Node.js optimizations.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is a map, else `false`.
    +     */
    +    function baseIsMap(value) {
    +      return isObjectLike(value) && getTag(value) == mapTag;
    +    }
    +
    +    /**
    +     * The base implementation of `_.isMatch` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Object} object The object to inspect.
    +     * @param {Object} source The object of property values to match.
    +     * @param {Array} matchData The property names, values, and compare flags to match.
    +     * @param {Function} [customizer] The function to customize comparisons.
    +     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
    +     */
    +    function baseIsMatch(object, source, matchData, customizer) {
    +      var index = matchData.length,
    +        length = index,
    +        noCustomizer = !customizer;
    +      if (object == null) {
    +        return !length;
    +      }
    +      object = Object(object);
    +      while (index--) {
    +        var data = matchData[index];
    +        if (noCustomizer && data[2] ? data[1] !== object[data[0]] : !(data[0] in object)) {
    +          return false;
    +        }
    +      }
    +      while (++index < length) {
    +        data = matchData[index];
    +        var key = data[0],
    +          objValue = object[key],
    +          srcValue = data[1];
    +        if (noCustomizer && data[2]) {
    +          if (objValue === undefined && !(key in object)) {
    +            return false;
    +          }
    +        } else {
    +          var stack = new Stack();
    +          if (customizer) {
    +            var result = customizer(objValue, srcValue, key, object, source, stack);
    +          }
    +          if (!(result === undefined ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) : result)) {
    +            return false;
    +          }
    +        }
    +      }
    +      return true;
    +    }
    +
    +    /**
    +     * The base implementation of `_.isNative` without bad shim checks.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is a native function,
    +     *  else `false`.
    +     */
    +    function baseIsNative(value) {
    +      if (!isObject(value) || isMasked(value)) {
    +        return false;
    +      }
    +      var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
    +      return pattern.test(toSource(value));
    +    }
    +
    +    /**
    +     * The base implementation of `_.isRegExp` without Node.js optimizations.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
    +     */
    +    function baseIsRegExp(value) {
    +      return isObjectLike(value) && baseGetTag(value) == regexpTag;
    +    }
    +
    +    /**
    +     * The base implementation of `_.isSet` without Node.js optimizations.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is a set, else `false`.
    +     */
    +    function baseIsSet(value) {
    +      return isObjectLike(value) && getTag(value) == setTag;
    +    }
    +
    +    /**
    +     * The base implementation of `_.isTypedArray` without Node.js optimizations.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
    +     */
    +    function baseIsTypedArray(value) {
    +      return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
    +    }
    +
    +    /**
    +     * The base implementation of `_.iteratee`.
    +     *
    +     * @private
    +     * @param {*} [value=_.identity] The value to convert to an iteratee.
    +     * @returns {Function} Returns the iteratee.
    +     */
    +    function baseIteratee(value) {
    +      // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
    +      // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
    +      if (typeof value == 'function') {
    +        return value;
    +      }
    +      if (value == null) {
    +        return identity;
    +      }
    +      if (typeof value == 'object') {
    +        return isArray(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value);
    +      }
    +      return property(value);
    +    }
    +
    +    /**
    +     * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @returns {Array} Returns the array of property names.
    +     */
    +    function baseKeys(object) {
    +      if (!isPrototype(object)) {
    +        return nativeKeys(object);
    +      }
    +      var result = [];
    +      for (var key in Object(object)) {
    +        if (hasOwnProperty.call(object, key) && key != 'constructor') {
    +          result.push(key);
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @returns {Array} Returns the array of property names.
    +     */
    +    function baseKeysIn(object) {
    +      if (!isObject(object)) {
    +        return nativeKeysIn(object);
    +      }
    +      var isProto = isPrototype(object),
    +        result = [];
    +      for (var key in object) {
    +        if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
    +          result.push(key);
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.lt` which doesn't coerce arguments.
    +     *
    +     * @private
    +     * @param {*} value The value to compare.
    +     * @param {*} other The other value to compare.
    +     * @returns {boolean} Returns `true` if `value` is less than `other`,
    +     *  else `false`.
    +     */
    +    function baseLt(value, other) {
    +      return value < other;
    +    }
    +
    +    /**
    +     * The base implementation of `_.map` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} iteratee The function invoked per iteration.
    +     * @returns {Array} Returns the new mapped array.
    +     */
    +    function baseMap(collection, iteratee) {
    +      var index = -1,
    +        result = isArrayLike(collection) ? Array(collection.length) : [];
    +      baseEach(collection, function (value, key, collection) {
    +        result[++index] = iteratee(value, key, collection);
    +      });
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.matches` which doesn't clone `source`.
    +     *
    +     * @private
    +     * @param {Object} source The object of property values to match.
    +     * @returns {Function} Returns the new spec function.
    +     */
    +    function baseMatches(source) {
    +      var matchData = getMatchData(source);
    +      if (matchData.length == 1 && matchData[0][2]) {
    +        return matchesStrictComparable(matchData[0][0], matchData[0][1]);
    +      }
    +      return function (object) {
    +        return object === source || baseIsMatch(object, source, matchData);
    +      };
    +    }
    +
    +    /**
    +     * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
    +     *
    +     * @private
    +     * @param {string} path The path of the property to get.
    +     * @param {*} srcValue The value to match.
    +     * @returns {Function} Returns the new spec function.
    +     */
    +    function baseMatchesProperty(path, srcValue) {
    +      if (isKey(path) && isStrictComparable(srcValue)) {
    +        return matchesStrictComparable(toKey(path), srcValue);
    +      }
    +      return function (object) {
    +        var objValue = get(object, path);
    +        return objValue === undefined && objValue === srcValue ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
    +      };
    +    }
    +
    +    /**
    +     * The base implementation of `_.merge` without support for multiple sources.
    +     *
    +     * @private
    +     * @param {Object} object The destination object.
    +     * @param {Object} source The source object.
    +     * @param {number} srcIndex The index of `source`.
    +     * @param {Function} [customizer] The function to customize merged values.
    +     * @param {Object} [stack] Tracks traversed source values and their merged
    +     *  counterparts.
    +     */
    +    function baseMerge(object, source, srcIndex, customizer, stack) {
    +      if (object === source) {
    +        return;
    +      }
    +      baseFor(source, function (srcValue, key) {
    +        stack || (stack = new Stack());
    +        if (isObject(srcValue)) {
    +          baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
    +        } else {
    +          var newValue = customizer ? customizer(safeGet(object, key), srcValue, key + '', object, source, stack) : undefined;
    +          if (newValue === undefined) {
    +            newValue = srcValue;
    +          }
    +          assignMergeValue(object, key, newValue);
    +        }
    +      }, keysIn);
    +    }
    +
    +    /**
    +     * A specialized version of `baseMerge` for arrays and objects which performs
    +     * deep merges and tracks traversed objects enabling objects with circular
    +     * references to be merged.
    +     *
    +     * @private
    +     * @param {Object} object The destination object.
    +     * @param {Object} source The source object.
    +     * @param {string} key The key of the value to merge.
    +     * @param {number} srcIndex The index of `source`.
    +     * @param {Function} mergeFunc The function to merge values.
    +     * @param {Function} [customizer] The function to customize assigned values.
    +     * @param {Object} [stack] Tracks traversed source values and their merged
    +     *  counterparts.
    +     */
    +    function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
    +      var objValue = safeGet(object, key),
    +        srcValue = safeGet(source, key),
    +        stacked = stack.get(srcValue);
    +      if (stacked) {
    +        assignMergeValue(object, key, stacked);
    +        return;
    +      }
    +      var newValue = customizer ? customizer(objValue, srcValue, key + '', object, source, stack) : undefined;
    +      var isCommon = newValue === undefined;
    +      if (isCommon) {
    +        var isArr = isArray(srcValue),
    +          isBuff = !isArr && isBuffer(srcValue),
    +          isTyped = !isArr && !isBuff && isTypedArray(srcValue);
    +        newValue = srcValue;
    +        if (isArr || isBuff || isTyped) {
    +          if (isArray(objValue)) {
    +            newValue = objValue;
    +          } else if (isArrayLikeObject(objValue)) {
    +            newValue = copyArray(objValue);
    +          } else if (isBuff) {
    +            isCommon = false;
    +            newValue = cloneBuffer(srcValue, true);
    +          } else if (isTyped) {
    +            isCommon = false;
    +            newValue = cloneTypedArray(srcValue, true);
    +          } else {
    +            newValue = [];
    +          }
    +        } else if (isPlainObject(srcValue) || isArguments(srcValue)) {
    +          newValue = objValue;
    +          if (isArguments(objValue)) {
    +            newValue = toPlainObject(objValue);
    +          } else if (!isObject(objValue) || isFunction(objValue)) {
    +            newValue = initCloneObject(srcValue);
    +          }
    +        } else {
    +          isCommon = false;
    +        }
    +      }
    +      if (isCommon) {
    +        // Recursively merge objects and arrays (susceptible to call stack limits).
    +        stack.set(srcValue, newValue);
    +        mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
    +        stack['delete'](srcValue);
    +      }
    +      assignMergeValue(object, key, newValue);
    +    }
    +
    +    /**
    +     * The base implementation of `_.nth` which doesn't coerce arguments.
    +     *
    +     * @private
    +     * @param {Array} array The array to query.
    +     * @param {number} n The index of the element to return.
    +     * @returns {*} Returns the nth element of `array`.
    +     */
    +    function baseNth(array, n) {
    +      var length = array.length;
    +      if (!length) {
    +        return;
    +      }
    +      n += n < 0 ? length : 0;
    +      return isIndex(n, length) ? array[n] : undefined;
    +    }
    +
    +    /**
    +     * The base implementation of `_.orderBy` without param guards.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
    +     * @param {string[]} orders The sort orders of `iteratees`.
    +     * @returns {Array} Returns the new sorted array.
    +     */
    +    function baseOrderBy(collection, iteratees, orders) {
    +      if (iteratees.length) {
    +        iteratees = arrayMap(iteratees, function (iteratee) {
    +          if (isArray(iteratee)) {
    +            return function (value) {
    +              return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee);
    +            };
    +          }
    +          return iteratee;
    +        });
    +      } else {
    +        iteratees = [identity];
    +      }
    +      var index = -1;
    +      iteratees = arrayMap(iteratees, baseUnary(getIteratee()));
    +      var result = baseMap(collection, function (value, key, collection) {
    +        var criteria = arrayMap(iteratees, function (iteratee) {
    +          return iteratee(value);
    +        });
    +        return {
    +          'criteria': criteria,
    +          'index': ++index,
    +          'value': value
    +        };
    +      });
    +      return baseSortBy(result, function (object, other) {
    +        return compareMultiple(object, other, orders);
    +      });
    +    }
    +
    +    /**
    +     * The base implementation of `_.pick` without support for individual
    +     * property identifiers.
    +     *
    +     * @private
    +     * @param {Object} object The source object.
    +     * @param {string[]} paths The property paths to pick.
    +     * @returns {Object} Returns the new object.
    +     */
    +    function basePick(object, paths) {
    +      return basePickBy(object, paths, function (value, path) {
    +        return hasIn(object, path);
    +      });
    +    }
    +
    +    /**
    +     * The base implementation of  `_.pickBy` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Object} object The source object.
    +     * @param {string[]} paths The property paths to pick.
    +     * @param {Function} predicate The function invoked per property.
    +     * @returns {Object} Returns the new object.
    +     */
    +    function basePickBy(object, paths, predicate) {
    +      var index = -1,
    +        length = paths.length,
    +        result = {};
    +      while (++index < length) {
    +        var path = paths[index],
    +          value = baseGet(object, path);
    +        if (predicate(value, path)) {
    +          baseSet(result, castPath(path, object), value);
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * A specialized version of `baseProperty` which supports deep paths.
    +     *
    +     * @private
    +     * @param {Array|string} path The path of the property to get.
    +     * @returns {Function} Returns the new accessor function.
    +     */
    +    function basePropertyDeep(path) {
    +      return function (object) {
    +        return baseGet(object, path);
    +      };
    +    }
    +
    +    /**
    +     * The base implementation of `_.pullAllBy` without support for iteratee
    +     * shorthands.
    +     *
    +     * @private
    +     * @param {Array} array The array to modify.
    +     * @param {Array} values The values to remove.
    +     * @param {Function} [iteratee] The iteratee invoked per element.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns `array`.
    +     */
    +    function basePullAll(array, values, iteratee, comparator) {
    +      var indexOf = comparator ? baseIndexOfWith : baseIndexOf,
    +        index = -1,
    +        length = values.length,
    +        seen = array;
    +      if (array === values) {
    +        values = copyArray(values);
    +      }
    +      if (iteratee) {
    +        seen = arrayMap(array, baseUnary(iteratee));
    +      }
    +      while (++index < length) {
    +        var fromIndex = 0,
    +          value = values[index],
    +          computed = iteratee ? iteratee(value) : value;
    +        while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
    +          if (seen !== array) {
    +            splice.call(seen, fromIndex, 1);
    +          }
    +          splice.call(array, fromIndex, 1);
    +        }
    +      }
    +      return array;
    +    }
    +
    +    /**
    +     * The base implementation of `_.pullAt` without support for individual
    +     * indexes or capturing the removed elements.
    +     *
    +     * @private
    +     * @param {Array} array The array to modify.
    +     * @param {number[]} indexes The indexes of elements to remove.
    +     * @returns {Array} Returns `array`.
    +     */
    +    function basePullAt(array, indexes) {
    +      var length = array ? indexes.length : 0,
    +        lastIndex = length - 1;
    +      while (length--) {
    +        var index = indexes[length];
    +        if (length == lastIndex || index !== previous) {
    +          var previous = index;
    +          if (isIndex(index)) {
    +            splice.call(array, index, 1);
    +          } else {
    +            baseUnset(array, index);
    +          }
    +        }
    +      }
    +      return array;
    +    }
    +
    +    /**
    +     * The base implementation of `_.random` without support for returning
    +     * floating-point numbers.
    +     *
    +     * @private
    +     * @param {number} lower The lower bound.
    +     * @param {number} upper The upper bound.
    +     * @returns {number} Returns the random number.
    +     */
    +    function baseRandom(lower, upper) {
    +      return lower + nativeFloor(nativeRandom() * (upper - lower + 1));
    +    }
    +
    +    /**
    +     * The base implementation of `_.range` and `_.rangeRight` which doesn't
    +     * coerce arguments.
    +     *
    +     * @private
    +     * @param {number} start The start of the range.
    +     * @param {number} end The end of the range.
    +     * @param {number} step The value to increment or decrement by.
    +     * @param {boolean} [fromRight] Specify iterating from right to left.
    +     * @returns {Array} Returns the range of numbers.
    +     */
    +    function baseRange(start, end, step, fromRight) {
    +      var index = -1,
    +        length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
    +        result = Array(length);
    +      while (length--) {
    +        result[fromRight ? length : ++index] = start;
    +        start += step;
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.repeat` which doesn't coerce arguments.
    +     *
    +     * @private
    +     * @param {string} string The string to repeat.
    +     * @param {number} n The number of times to repeat the string.
    +     * @returns {string} Returns the repeated string.
    +     */
    +    function baseRepeat(string, n) {
    +      var result = '';
    +      if (!string || n < 1 || n > MAX_SAFE_INTEGER) {
    +        return result;
    +      }
    +      // Leverage the exponentiation by squaring algorithm for a faster repeat.
    +      // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
    +      do {
    +        if (n % 2) {
    +          result += string;
    +        }
    +        n = nativeFloor(n / 2);
    +        if (n) {
    +          string += string;
    +        }
    +      } while (n);
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.rest` which doesn't validate or coerce arguments.
    +     *
    +     * @private
    +     * @param {Function} func The function to apply a rest parameter to.
    +     * @param {number} [start=func.length-1] The start position of the rest parameter.
    +     * @returns {Function} Returns the new function.
    +     */
    +    function baseRest(func, start) {
    +      return setToString(overRest(func, start, identity), func + '');
    +    }
    +
    +    /**
    +     * The base implementation of `_.sample`.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to sample.
    +     * @returns {*} Returns the random element.
    +     */
    +    function baseSample(collection) {
    +      return arraySample(values(collection));
    +    }
    +
    +    /**
    +     * The base implementation of `_.sampleSize` without param guards.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to sample.
    +     * @param {number} n The number of elements to sample.
    +     * @returns {Array} Returns the random elements.
    +     */
    +    function baseSampleSize(collection, n) {
    +      var array = values(collection);
    +      return shuffleSelf(array, baseClamp(n, 0, array.length));
    +    }
    +
    +    /**
    +     * The base implementation of `_.set`.
    +     *
    +     * @private
    +     * @param {Object} object The object to modify.
    +     * @param {Array|string} path The path of the property to set.
    +     * @param {*} value The value to set.
    +     * @param {Function} [customizer] The function to customize path creation.
    +     * @returns {Object} Returns `object`.
    +     */
    +    function baseSet(object, path, value, customizer) {
    +      if (!isObject(object)) {
    +        return object;
    +      }
    +      path = castPath(path, object);
    +      var index = -1,
    +        length = path.length,
    +        lastIndex = length - 1,
    +        nested = object;
    +      while (nested != null && ++index < length) {
    +        var key = toKey(path[index]),
    +          newValue = value;
    +        if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
    +          return object;
    +        }
    +        if (index != lastIndex) {
    +          var objValue = nested[key];
    +          newValue = customizer ? customizer(objValue, key, nested) : undefined;
    +          if (newValue === undefined) {
    +            newValue = isObject(objValue) ? objValue : isIndex(path[index + 1]) ? [] : {};
    +          }
    +        }
    +        assignValue(nested, key, newValue);
    +        nested = nested[key];
    +      }
    +      return object;
    +    }
    +
    +    /**
    +     * The base implementation of `setData` without support for hot loop shorting.
    +     *
    +     * @private
    +     * @param {Function} func The function to associate metadata with.
    +     * @param {*} data The metadata.
    +     * @returns {Function} Returns `func`.
    +     */
    +    var baseSetData = !metaMap ? identity : function (func, data) {
    +      metaMap.set(func, data);
    +      return func;
    +    };
    +
    +    /**
    +     * The base implementation of `setToString` without support for hot loop shorting.
    +     *
    +     * @private
    +     * @param {Function} func The function to modify.
    +     * @param {Function} string The `toString` result.
    +     * @returns {Function} Returns `func`.
    +     */
    +    var baseSetToString = !defineProperty ? identity : function (func, string) {
    +      return defineProperty(func, 'toString', {
    +        'configurable': true,
    +        'enumerable': false,
    +        'value': constant(string),
    +        'writable': true
    +      });
    +    };
    +
    +    /**
    +     * The base implementation of `_.shuffle`.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to shuffle.
    +     * @returns {Array} Returns the new shuffled array.
    +     */
    +    function baseShuffle(collection) {
    +      return shuffleSelf(values(collection));
    +    }
    +
    +    /**
    +     * The base implementation of `_.slice` without an iteratee call guard.
    +     *
    +     * @private
    +     * @param {Array} array The array to slice.
    +     * @param {number} [start=0] The start position.
    +     * @param {number} [end=array.length] The end position.
    +     * @returns {Array} Returns the slice of `array`.
    +     */
    +    function baseSlice(array, start, end) {
    +      var index = -1,
    +        length = array.length;
    +      if (start < 0) {
    +        start = -start > length ? 0 : length + start;
    +      }
    +      end = end > length ? length : end;
    +      if (end < 0) {
    +        end += length;
    +      }
    +      length = start > end ? 0 : end - start >>> 0;
    +      start >>>= 0;
    +      var result = Array(length);
    +      while (++index < length) {
    +        result[index] = array[index + start];
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.some` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} predicate The function invoked per iteration.
    +     * @returns {boolean} Returns `true` if any element passes the predicate check,
    +     *  else `false`.
    +     */
    +    function baseSome(collection, predicate) {
    +      var result;
    +      baseEach(collection, function (value, index, collection) {
    +        result = predicate(value, index, collection);
    +        return !result;
    +      });
    +      return !!result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which
    +     * performs a binary search of `array` to determine the index at which `value`
    +     * should be inserted into `array` in order to maintain its sort order.
    +     *
    +     * @private
    +     * @param {Array} array The sorted array to inspect.
    +     * @param {*} value The value to evaluate.
    +     * @param {boolean} [retHighest] Specify returning the highest qualified index.
    +     * @returns {number} Returns the index at which `value` should be inserted
    +     *  into `array`.
    +     */
    +    function baseSortedIndex(array, value, retHighest) {
    +      var low = 0,
    +        high = array == null ? low : array.length;
    +      if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
    +        while (low < high) {
    +          var mid = low + high >>> 1,
    +            computed = array[mid];
    +          if (computed !== null && !isSymbol(computed) && (retHighest ? computed <= value : computed < value)) {
    +            low = mid + 1;
    +          } else {
    +            high = mid;
    +          }
    +        }
    +        return high;
    +      }
    +      return baseSortedIndexBy(array, value, identity, retHighest);
    +    }
    +
    +    /**
    +     * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`
    +     * which invokes `iteratee` for `value` and each element of `array` to compute
    +     * their sort ranking. The iteratee is invoked with one argument; (value).
    +     *
    +     * @private
    +     * @param {Array} array The sorted array to inspect.
    +     * @param {*} value The value to evaluate.
    +     * @param {Function} iteratee The iteratee invoked per element.
    +     * @param {boolean} [retHighest] Specify returning the highest qualified index.
    +     * @returns {number} Returns the index at which `value` should be inserted
    +     *  into `array`.
    +     */
    +    function baseSortedIndexBy(array, value, iteratee, retHighest) {
    +      var low = 0,
    +        high = array == null ? 0 : array.length;
    +      if (high === 0) {
    +        return 0;
    +      }
    +      value = iteratee(value);
    +      var valIsNaN = value !== value,
    +        valIsNull = value === null,
    +        valIsSymbol = isSymbol(value),
    +        valIsUndefined = value === undefined;
    +      while (low < high) {
    +        var mid = nativeFloor((low + high) / 2),
    +          computed = iteratee(array[mid]),
    +          othIsDefined = computed !== undefined,
    +          othIsNull = computed === null,
    +          othIsReflexive = computed === computed,
    +          othIsSymbol = isSymbol(computed);
    +        if (valIsNaN) {
    +          var setLow = retHighest || othIsReflexive;
    +        } else if (valIsUndefined) {
    +          setLow = othIsReflexive && (retHighest || othIsDefined);
    +        } else if (valIsNull) {
    +          setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);
    +        } else if (valIsSymbol) {
    +          setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);
    +        } else if (othIsNull || othIsSymbol) {
    +          setLow = false;
    +        } else {
    +          setLow = retHighest ? computed <= value : computed < value;
    +        }
    +        if (setLow) {
    +          low = mid + 1;
    +        } else {
    +          high = mid;
    +        }
    +      }
    +      return nativeMin(high, MAX_ARRAY_INDEX);
    +    }
    +
    +    /**
    +     * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without
    +     * support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array} array The array to inspect.
    +     * @param {Function} [iteratee] The iteratee invoked per element.
    +     * @returns {Array} Returns the new duplicate free array.
    +     */
    +    function baseSortedUniq(array, iteratee) {
    +      var index = -1,
    +        length = array.length,
    +        resIndex = 0,
    +        result = [];
    +      while (++index < length) {
    +        var value = array[index],
    +          computed = iteratee ? iteratee(value) : value;
    +        if (!index || !eq(computed, seen)) {
    +          var seen = computed;
    +          result[resIndex++] = value === 0 ? 0 : value;
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.toNumber` which doesn't ensure correct
    +     * conversions of binary, hexadecimal, or octal string values.
    +     *
    +     * @private
    +     * @param {*} value The value to process.
    +     * @returns {number} Returns the number.
    +     */
    +    function baseToNumber(value) {
    +      if (typeof value == 'number') {
    +        return value;
    +      }
    +      if (isSymbol(value)) {
    +        return NAN;
    +      }
    +      return +value;
    +    }
    +
    +    /**
    +     * The base implementation of `_.toString` which doesn't convert nullish
    +     * values to empty strings.
    +     *
    +     * @private
    +     * @param {*} value The value to process.
    +     * @returns {string} Returns the string.
    +     */
    +    function baseToString(value) {
    +      // Exit early for strings to avoid a performance hit in some environments.
    +      if (typeof value == 'string') {
    +        return value;
    +      }
    +      if (isArray(value)) {
    +        // Recursively convert values (susceptible to call stack limits).
    +        return arrayMap(value, baseToString) + '';
    +      }
    +      if (isSymbol(value)) {
    +        return symbolToString ? symbolToString.call(value) : '';
    +      }
    +      var result = value + '';
    +      return result == '0' && 1 / value == -INFINITY ? '-0' : result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.uniqBy` without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array} array The array to inspect.
    +     * @param {Function} [iteratee] The iteratee invoked per element.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns the new duplicate free array.
    +     */
    +    function baseUniq(array, iteratee, comparator) {
    +      var index = -1,
    +        includes = arrayIncludes,
    +        length = array.length,
    +        isCommon = true,
    +        result = [],
    +        seen = result;
    +      if (comparator) {
    +        isCommon = false;
    +        includes = arrayIncludesWith;
    +      } else if (length >= LARGE_ARRAY_SIZE) {
    +        var set = iteratee ? null : createSet(array);
    +        if (set) {
    +          return setToArray(set);
    +        }
    +        isCommon = false;
    +        includes = cacheHas;
    +        seen = new SetCache();
    +      } else {
    +        seen = iteratee ? [] : result;
    +      }
    +      outer: while (++index < length) {
    +        var value = array[index],
    +          computed = iteratee ? iteratee(value) : value;
    +        value = comparator || value !== 0 ? value : 0;
    +        if (isCommon && computed === computed) {
    +          var seenIndex = seen.length;
    +          while (seenIndex--) {
    +            if (seen[seenIndex] === computed) {
    +              continue outer;
    +            }
    +          }
    +          if (iteratee) {
    +            seen.push(computed);
    +          }
    +          result.push(value);
    +        } else if (!includes(seen, computed, comparator)) {
    +          if (seen !== result) {
    +            seen.push(computed);
    +          }
    +          result.push(value);
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * The base implementation of `_.unset`.
    +     *
    +     * @private
    +     * @param {Object} object The object to modify.
    +     * @param {Array|string} path The property path to unset.
    +     * @returns {boolean} Returns `true` if the property is deleted, else `false`.
    +     */
    +    function baseUnset(object, path) {
    +      path = castPath(path, object);
    +      object = parent(object, path);
    +      return object == null || delete object[toKey(last(path))];
    +    }
    +
    +    /**
    +     * The base implementation of `_.update`.
    +     *
    +     * @private
    +     * @param {Object} object The object to modify.
    +     * @param {Array|string} path The path of the property to update.
    +     * @param {Function} updater The function to produce the updated value.
    +     * @param {Function} [customizer] The function to customize path creation.
    +     * @returns {Object} Returns `object`.
    +     */
    +    function baseUpdate(object, path, updater, customizer) {
    +      return baseSet(object, path, updater(baseGet(object, path)), customizer);
    +    }
    +
    +    /**
    +     * The base implementation of methods like `_.dropWhile` and `_.takeWhile`
    +     * without support for iteratee shorthands.
    +     *
    +     * @private
    +     * @param {Array} array The array to query.
    +     * @param {Function} predicate The function invoked per iteration.
    +     * @param {boolean} [isDrop] Specify dropping elements instead of taking them.
    +     * @param {boolean} [fromRight] Specify iterating from right to left.
    +     * @returns {Array} Returns the slice of `array`.
    +     */
    +    function baseWhile(array, predicate, isDrop, fromRight) {
    +      var length = array.length,
    +        index = fromRight ? length : -1;
    +      while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {}
    +      return isDrop ? baseSlice(array, fromRight ? 0 : index, fromRight ? index + 1 : length) : baseSlice(array, fromRight ? index + 1 : 0, fromRight ? length : index);
    +    }
    +
    +    /**
    +     * The base implementation of `wrapperValue` which returns the result of
    +     * performing a sequence of actions on the unwrapped `value`, where each
    +     * successive action is supplied the return value of the previous.
    +     *
    +     * @private
    +     * @param {*} value The unwrapped value.
    +     * @param {Array} actions Actions to perform to resolve the unwrapped value.
    +     * @returns {*} Returns the resolved value.
    +     */
    +    function baseWrapperValue(value, actions) {
    +      var result = value;
    +      if (result instanceof LazyWrapper) {
    +        result = result.value();
    +      }
    +      return arrayReduce(actions, function (result, action) {
    +        return action.func.apply(action.thisArg, arrayPush([result], action.args));
    +      }, result);
    +    }
    +
    +    /**
    +     * The base implementation of methods like `_.xor`, without support for
    +     * iteratee shorthands, that accepts an array of arrays to inspect.
    +     *
    +     * @private
    +     * @param {Array} arrays The arrays to inspect.
    +     * @param {Function} [iteratee] The iteratee invoked per element.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns the new array of values.
    +     */
    +    function baseXor(arrays, iteratee, comparator) {
    +      var length = arrays.length;
    +      if (length < 2) {
    +        return length ? baseUniq(arrays[0]) : [];
    +      }
    +      var index = -1,
    +        result = Array(length);
    +      while (++index < length) {
    +        var array = arrays[index],
    +          othIndex = -1;
    +        while (++othIndex < length) {
    +          if (othIndex != index) {
    +            result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator);
    +          }
    +        }
    +      }
    +      return baseUniq(baseFlatten(result, 1), iteratee, comparator);
    +    }
    +
    +    /**
    +     * This base implementation of `_.zipObject` which assigns values using `assignFunc`.
    +     *
    +     * @private
    +     * @param {Array} props The property identifiers.
    +     * @param {Array} values The property values.
    +     * @param {Function} assignFunc The function to assign values.
    +     * @returns {Object} Returns the new object.
    +     */
    +    function baseZipObject(props, values, assignFunc) {
    +      var index = -1,
    +        length = props.length,
    +        valsLength = values.length,
    +        result = {};
    +      while (++index < length) {
    +        var value = index < valsLength ? values[index] : undefined;
    +        assignFunc(result, props[index], value);
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Casts `value` to an empty array if it's not an array like object.
    +     *
    +     * @private
    +     * @param {*} value The value to inspect.
    +     * @returns {Array|Object} Returns the cast array-like object.
    +     */
    +    function castArrayLikeObject(value) {
    +      return isArrayLikeObject(value) ? value : [];
    +    }
    +
    +    /**
    +     * Casts `value` to `identity` if it's not a function.
    +     *
    +     * @private
    +     * @param {*} value The value to inspect.
    +     * @returns {Function} Returns cast function.
    +     */
    +    function castFunction(value) {
    +      return typeof value == 'function' ? value : identity;
    +    }
    +
    +    /**
    +     * Casts `value` to a path array if it's not one.
    +     *
    +     * @private
    +     * @param {*} value The value to inspect.
    +     * @param {Object} [object] The object to query keys on.
    +     * @returns {Array} Returns the cast property path array.
    +     */
    +    function castPath(value, object) {
    +      if (isArray(value)) {
    +        return value;
    +      }
    +      return isKey(value, object) ? [value] : stringToPath(toString(value));
    +    }
    +
    +    /**
    +     * A `baseRest` alias which can be replaced with `identity` by module
    +     * replacement plugins.
    +     *
    +     * @private
    +     * @type {Function}
    +     * @param {Function} func The function to apply a rest parameter to.
    +     * @returns {Function} Returns the new function.
    +     */
    +    var castRest = baseRest;
    +
    +    /**
    +     * Casts `array` to a slice if it's needed.
    +     *
    +     * @private
    +     * @param {Array} array The array to inspect.
    +     * @param {number} start The start position.
    +     * @param {number} [end=array.length] The end position.
    +     * @returns {Array} Returns the cast slice.
    +     */
    +    function castSlice(array, start, end) {
    +      var length = array.length;
    +      end = end === undefined ? length : end;
    +      return !start && end >= length ? array : baseSlice(array, start, end);
    +    }
    +
    +    /**
    +     * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout).
    +     *
    +     * @private
    +     * @param {number|Object} id The timer id or timeout object of the timer to clear.
    +     */
    +    var clearTimeout = ctxClearTimeout || function (id) {
    +      return root.clearTimeout(id);
    +    };
    +
    +    /**
    +     * Creates a clone of  `buffer`.
    +     *
    +     * @private
    +     * @param {Buffer} buffer The buffer to clone.
    +     * @param {boolean} [isDeep] Specify a deep clone.
    +     * @returns {Buffer} Returns the cloned buffer.
    +     */
    +    function cloneBuffer(buffer, isDeep) {
    +      if (isDeep) {
    +        return buffer.slice();
    +      }
    +      var length = buffer.length,
    +        result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);
    +      buffer.copy(result);
    +      return result;
    +    }
    +
    +    /**
    +     * Creates a clone of `arrayBuffer`.
    +     *
    +     * @private
    +     * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
    +     * @returns {ArrayBuffer} Returns the cloned array buffer.
    +     */
    +    function cloneArrayBuffer(arrayBuffer) {
    +      var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
    +      new Uint8Array(result).set(new Uint8Array(arrayBuffer));
    +      return result;
    +    }
    +
    +    /**
    +     * Creates a clone of `dataView`.
    +     *
    +     * @private
    +     * @param {Object} dataView The data view to clone.
    +     * @param {boolean} [isDeep] Specify a deep clone.
    +     * @returns {Object} Returns the cloned data view.
    +     */
    +    function cloneDataView(dataView, isDeep) {
    +      var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
    +      return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
    +    }
    +
    +    /**
    +     * Creates a clone of `regexp`.
    +     *
    +     * @private
    +     * @param {Object} regexp The regexp to clone.
    +     * @returns {Object} Returns the cloned regexp.
    +     */
    +    function cloneRegExp(regexp) {
    +      var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
    +      result.lastIndex = regexp.lastIndex;
    +      return result;
    +    }
    +
    +    /**
    +     * Creates a clone of the `symbol` object.
    +     *
    +     * @private
    +     * @param {Object} symbol The symbol object to clone.
    +     * @returns {Object} Returns the cloned symbol object.
    +     */
    +    function cloneSymbol(symbol) {
    +      return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
    +    }
    +
    +    /**
    +     * Creates a clone of `typedArray`.
    +     *
    +     * @private
    +     * @param {Object} typedArray The typed array to clone.
    +     * @param {boolean} [isDeep] Specify a deep clone.
    +     * @returns {Object} Returns the cloned typed array.
    +     */
    +    function cloneTypedArray(typedArray, isDeep) {
    +      var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
    +      return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
    +    }
    +
    +    /**
    +     * Compares values to sort them in ascending order.
    +     *
    +     * @private
    +     * @param {*} value The value to compare.
    +     * @param {*} other The other value to compare.
    +     * @returns {number} Returns the sort order indicator for `value`.
    +     */
    +    function compareAscending(value, other) {
    +      if (value !== other) {
    +        var valIsDefined = value !== undefined,
    +          valIsNull = value === null,
    +          valIsReflexive = value === value,
    +          valIsSymbol = isSymbol(value);
    +        var othIsDefined = other !== undefined,
    +          othIsNull = other === null,
    +          othIsReflexive = other === other,
    +          othIsSymbol = isSymbol(other);
    +        if (!othIsNull && !othIsSymbol && !valIsSymbol && value > other || valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol || valIsNull && othIsDefined && othIsReflexive || !valIsDefined && othIsReflexive || !valIsReflexive) {
    +          return 1;
    +        }
    +        if (!valIsNull && !valIsSymbol && !othIsSymbol && value < other || othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol || othIsNull && valIsDefined && valIsReflexive || !othIsDefined && valIsReflexive || !othIsReflexive) {
    +          return -1;
    +        }
    +      }
    +      return 0;
    +    }
    +
    +    /**
    +     * Used by `_.orderBy` to compare multiple properties of a value to another
    +     * and stable sort them.
    +     *
    +     * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,
    +     * specify an order of "desc" for descending or "asc" for ascending sort order
    +     * of corresponding values.
    +     *
    +     * @private
    +     * @param {Object} object The object to compare.
    +     * @param {Object} other The other object to compare.
    +     * @param {boolean[]|string[]} orders The order to sort by for each property.
    +     * @returns {number} Returns the sort order indicator for `object`.
    +     */
    +    function compareMultiple(object, other, orders) {
    +      var index = -1,
    +        objCriteria = object.criteria,
    +        othCriteria = other.criteria,
    +        length = objCriteria.length,
    +        ordersLength = orders.length;
    +      while (++index < length) {
    +        var result = compareAscending(objCriteria[index], othCriteria[index]);
    +        if (result) {
    +          if (index >= ordersLength) {
    +            return result;
    +          }
    +          var order = orders[index];
    +          return result * (order == 'desc' ? -1 : 1);
    +        }
    +      }
    +      // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
    +      // that causes it, under certain circumstances, to provide the same value for
    +      // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
    +      // for more details.
    +      //
    +      // This also ensures a stable sort in V8 and other engines.
    +      // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
    +      return object.index - other.index;
    +    }
    +
    +    /**
    +     * Creates an array that is the composition of partially applied arguments,
    +     * placeholders, and provided arguments into a single array of arguments.
    +     *
    +     * @private
    +     * @param {Array} args The provided arguments.
    +     * @param {Array} partials The arguments to prepend to those provided.
    +     * @param {Array} holders The `partials` placeholder indexes.
    +     * @params {boolean} [isCurried] Specify composing for a curried function.
    +     * @returns {Array} Returns the new array of composed arguments.
    +     */
    +    function composeArgs(args, partials, holders, isCurried) {
    +      var argsIndex = -1,
    +        argsLength = args.length,
    +        holdersLength = holders.length,
    +        leftIndex = -1,
    +        leftLength = partials.length,
    +        rangeLength = nativeMax(argsLength - holdersLength, 0),
    +        result = Array(leftLength + rangeLength),
    +        isUncurried = !isCurried;
    +      while (++leftIndex < leftLength) {
    +        result[leftIndex] = partials[leftIndex];
    +      }
    +      while (++argsIndex < holdersLength) {
    +        if (isUncurried || argsIndex < argsLength) {
    +          result[holders[argsIndex]] = args[argsIndex];
    +        }
    +      }
    +      while (rangeLength--) {
    +        result[leftIndex++] = args[argsIndex++];
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * This function is like `composeArgs` except that the arguments composition
    +     * is tailored for `_.partialRight`.
    +     *
    +     * @private
    +     * @param {Array} args The provided arguments.
    +     * @param {Array} partials The arguments to append to those provided.
    +     * @param {Array} holders The `partials` placeholder indexes.
    +     * @params {boolean} [isCurried] Specify composing for a curried function.
    +     * @returns {Array} Returns the new array of composed arguments.
    +     */
    +    function composeArgsRight(args, partials, holders, isCurried) {
    +      var argsIndex = -1,
    +        argsLength = args.length,
    +        holdersIndex = -1,
    +        holdersLength = holders.length,
    +        rightIndex = -1,
    +        rightLength = partials.length,
    +        rangeLength = nativeMax(argsLength - holdersLength, 0),
    +        result = Array(rangeLength + rightLength),
    +        isUncurried = !isCurried;
    +      while (++argsIndex < rangeLength) {
    +        result[argsIndex] = args[argsIndex];
    +      }
    +      var offset = argsIndex;
    +      while (++rightIndex < rightLength) {
    +        result[offset + rightIndex] = partials[rightIndex];
    +      }
    +      while (++holdersIndex < holdersLength) {
    +        if (isUncurried || argsIndex < argsLength) {
    +          result[offset + holders[holdersIndex]] = args[argsIndex++];
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Copies the values of `source` to `array`.
    +     *
    +     * @private
    +     * @param {Array} source The array to copy values from.
    +     * @param {Array} [array=[]] The array to copy values to.
    +     * @returns {Array} Returns `array`.
    +     */
    +    function copyArray(source, array) {
    +      var index = -1,
    +        length = source.length;
    +      array || (array = Array(length));
    +      while (++index < length) {
    +        array[index] = source[index];
    +      }
    +      return array;
    +    }
    +
    +    /**
    +     * Copies properties of `source` to `object`.
    +     *
    +     * @private
    +     * @param {Object} source The object to copy properties from.
    +     * @param {Array} props The property identifiers to copy.
    +     * @param {Object} [object={}] The object to copy properties to.
    +     * @param {Function} [customizer] The function to customize copied values.
    +     * @returns {Object} Returns `object`.
    +     */
    +    function copyObject(source, props, object, customizer) {
    +      var isNew = !object;
    +      object || (object = {});
    +      var index = -1,
    +        length = props.length;
    +      while (++index < length) {
    +        var key = props[index];
    +        var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined;
    +        if (newValue === undefined) {
    +          newValue = source[key];
    +        }
    +        if (isNew) {
    +          baseAssignValue(object, key, newValue);
    +        } else {
    +          assignValue(object, key, newValue);
    +        }
    +      }
    +      return object;
    +    }
    +
    +    /**
    +     * Copies own symbols of `source` to `object`.
    +     *
    +     * @private
    +     * @param {Object} source The object to copy symbols from.
    +     * @param {Object} [object={}] The object to copy symbols to.
    +     * @returns {Object} Returns `object`.
    +     */
    +    function copySymbols(source, object) {
    +      return copyObject(source, getSymbols(source), object);
    +    }
    +
    +    /**
    +     * Copies own and inherited symbols of `source` to `object`.
    +     *
    +     * @private
    +     * @param {Object} source The object to copy symbols from.
    +     * @param {Object} [object={}] The object to copy symbols to.
    +     * @returns {Object} Returns `object`.
    +     */
    +    function copySymbolsIn(source, object) {
    +      return copyObject(source, getSymbolsIn(source), object);
    +    }
    +
    +    /**
    +     * Creates a function like `_.groupBy`.
    +     *
    +     * @private
    +     * @param {Function} setter The function to set accumulator values.
    +     * @param {Function} [initializer] The accumulator object initializer.
    +     * @returns {Function} Returns the new aggregator function.
    +     */
    +    function createAggregator(setter, initializer) {
    +      return function (collection, iteratee) {
    +        var func = isArray(collection) ? arrayAggregator : baseAggregator,
    +          accumulator = initializer ? initializer() : {};
    +        return func(collection, setter, getIteratee(iteratee, 2), accumulator);
    +      };
    +    }
    +
    +    /**
    +     * Creates a function like `_.assign`.
    +     *
    +     * @private
    +     * @param {Function} assigner The function to assign values.
    +     * @returns {Function} Returns the new assigner function.
    +     */
    +    function createAssigner(assigner) {
    +      return baseRest(function (object, sources) {
    +        var index = -1,
    +          length = sources.length,
    +          customizer = length > 1 ? sources[length - 1] : undefined,
    +          guard = length > 2 ? sources[2] : undefined;
    +        customizer = assigner.length > 3 && typeof customizer == 'function' ? (length--, customizer) : undefined;
    +        if (guard && isIterateeCall(sources[0], sources[1], guard)) {
    +          customizer = length < 3 ? undefined : customizer;
    +          length = 1;
    +        }
    +        object = Object(object);
    +        while (++index < length) {
    +          var source = sources[index];
    +          if (source) {
    +            assigner(object, source, index, customizer);
    +          }
    +        }
    +        return object;
    +      });
    +    }
    +
    +    /**
    +     * Creates a `baseEach` or `baseEachRight` function.
    +     *
    +     * @private
    +     * @param {Function} eachFunc The function to iterate over a collection.
    +     * @param {boolean} [fromRight] Specify iterating from right to left.
    +     * @returns {Function} Returns the new base function.
    +     */
    +    function createBaseEach(eachFunc, fromRight) {
    +      return function (collection, iteratee) {
    +        if (collection == null) {
    +          return collection;
    +        }
    +        if (!isArrayLike(collection)) {
    +          return eachFunc(collection, iteratee);
    +        }
    +        var length = collection.length,
    +          index = fromRight ? length : -1,
    +          iterable = Object(collection);
    +        while (fromRight ? index-- : ++index < length) {
    +          if (iteratee(iterable[index], index, iterable) === false) {
    +            break;
    +          }
    +        }
    +        return collection;
    +      };
    +    }
    +
    +    /**
    +     * Creates a base function for methods like `_.forIn` and `_.forOwn`.
    +     *
    +     * @private
    +     * @param {boolean} [fromRight] Specify iterating from right to left.
    +     * @returns {Function} Returns the new base function.
    +     */
    +    function createBaseFor(fromRight) {
    +      return function (object, iteratee, keysFunc) {
    +        var index = -1,
    +          iterable = Object(object),
    +          props = keysFunc(object),
    +          length = props.length;
    +        while (length--) {
    +          var key = props[fromRight ? length : ++index];
    +          if (iteratee(iterable[key], key, iterable) === false) {
    +            break;
    +          }
    +        }
    +        return object;
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that wraps `func` to invoke it with the optional `this`
    +     * binding of `thisArg`.
    +     *
    +     * @private
    +     * @param {Function} func The function to wrap.
    +     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    +     * @param {*} [thisArg] The `this` binding of `func`.
    +     * @returns {Function} Returns the new wrapped function.
    +     */
    +    function createBind(func, bitmask, thisArg) {
    +      var isBind = bitmask & WRAP_BIND_FLAG,
    +        Ctor = createCtor(func);
    +      function wrapper() {
    +        var fn = this && this !== root && this instanceof wrapper ? Ctor : func;
    +        return fn.apply(isBind ? thisArg : this, arguments);
    +      }
    +      return wrapper;
    +    }
    +
    +    /**
    +     * Creates a function like `_.lowerFirst`.
    +     *
    +     * @private
    +     * @param {string} methodName The name of the `String` case method to use.
    +     * @returns {Function} Returns the new case function.
    +     */
    +    function createCaseFirst(methodName) {
    +      return function (string) {
    +        string = toString(string);
    +        var strSymbols = hasUnicode(string) ? stringToArray(string) : undefined;
    +        var chr = strSymbols ? strSymbols[0] : string.charAt(0);
    +        var trailing = strSymbols ? castSlice(strSymbols, 1).join('') : string.slice(1);
    +        return chr[methodName]() + trailing;
    +      };
    +    }
    +
    +    /**
    +     * Creates a function like `_.camelCase`.
    +     *
    +     * @private
    +     * @param {Function} callback The function to combine each word.
    +     * @returns {Function} Returns the new compounder function.
    +     */
    +    function createCompounder(callback) {
    +      return function (string) {
    +        return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that produces an instance of `Ctor` regardless of
    +     * whether it was invoked as part of a `new` expression or by `call` or `apply`.
    +     *
    +     * @private
    +     * @param {Function} Ctor The constructor to wrap.
    +     * @returns {Function} Returns the new wrapped function.
    +     */
    +    function createCtor(Ctor) {
    +      return function () {
    +        // Use a `switch` statement to work with class constructors. See
    +        // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
    +        // for more details.
    +        var args = arguments;
    +        switch (args.length) {
    +          case 0:
    +            return new Ctor();
    +          case 1:
    +            return new Ctor(args[0]);
    +          case 2:
    +            return new Ctor(args[0], args[1]);
    +          case 3:
    +            return new Ctor(args[0], args[1], args[2]);
    +          case 4:
    +            return new Ctor(args[0], args[1], args[2], args[3]);
    +          case 5:
    +            return new Ctor(args[0], args[1], args[2], args[3], args[4]);
    +          case 6:
    +            return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
    +          case 7:
    +            return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
    +        }
    +        var thisBinding = baseCreate(Ctor.prototype),
    +          result = Ctor.apply(thisBinding, args);
    +
    +        // Mimic the constructor's `return` behavior.
    +        // See https://es5.github.io/#x13.2.2 for more details.
    +        return isObject(result) ? result : thisBinding;
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that wraps `func` to enable currying.
    +     *
    +     * @private
    +     * @param {Function} func The function to wrap.
    +     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    +     * @param {number} arity The arity of `func`.
    +     * @returns {Function} Returns the new wrapped function.
    +     */
    +    function createCurry(func, bitmask, arity) {
    +      var Ctor = createCtor(func);
    +      function wrapper() {
    +        var length = arguments.length,
    +          args = Array(length),
    +          index = length,
    +          placeholder = getHolder(wrapper);
    +        while (index--) {
    +          args[index] = arguments[index];
    +        }
    +        var holders = length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder ? [] : replaceHolders(args, placeholder);
    +        length -= holders.length;
    +        if (length < arity) {
    +          return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, undefined, args, holders, undefined, undefined, arity - length);
    +        }
    +        var fn = this && this !== root && this instanceof wrapper ? Ctor : func;
    +        return apply(fn, this, args);
    +      }
    +      return wrapper;
    +    }
    +
    +    /**
    +     * Creates a `_.find` or `_.findLast` function.
    +     *
    +     * @private
    +     * @param {Function} findIndexFunc The function to find the collection index.
    +     * @returns {Function} Returns the new find function.
    +     */
    +    function createFind(findIndexFunc) {
    +      return function (collection, predicate, fromIndex) {
    +        var iterable = Object(collection);
    +        if (!isArrayLike(collection)) {
    +          var iteratee = getIteratee(predicate, 3);
    +          collection = keys(collection);
    +          predicate = function predicate(key) {
    +            return iteratee(iterable[key], key, iterable);
    +          };
    +        }
    +        var index = findIndexFunc(collection, predicate, fromIndex);
    +        return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
    +      };
    +    }
    +
    +    /**
    +     * Creates a `_.flow` or `_.flowRight` function.
    +     *
    +     * @private
    +     * @param {boolean} [fromRight] Specify iterating from right to left.
    +     * @returns {Function} Returns the new flow function.
    +     */
    +    function createFlow(fromRight) {
    +      return flatRest(function (funcs) {
    +        var length = funcs.length,
    +          index = length,
    +          prereq = LodashWrapper.prototype.thru;
    +        if (fromRight) {
    +          funcs.reverse();
    +        }
    +        while (index--) {
    +          var func = funcs[index];
    +          if (typeof func != 'function') {
    +            throw new TypeError(FUNC_ERROR_TEXT);
    +          }
    +          if (prereq && !wrapper && getFuncName(func) == 'wrapper') {
    +            var wrapper = new LodashWrapper([], true);
    +          }
    +        }
    +        index = wrapper ? index : length;
    +        while (++index < length) {
    +          func = funcs[index];
    +          var funcName = getFuncName(func),
    +            data = funcName == 'wrapper' ? getData(func) : undefined;
    +          if (data && isLaziable(data[0]) && data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && !data[4].length && data[9] == 1) {
    +            wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
    +          } else {
    +            wrapper = func.length == 1 && isLaziable(func) ? wrapper[funcName]() : wrapper.thru(func);
    +          }
    +        }
    +        return function () {
    +          var args = arguments,
    +            value = args[0];
    +          if (wrapper && args.length == 1 && isArray(value)) {
    +            return wrapper.plant(value).value();
    +          }
    +          var index = 0,
    +            result = length ? funcs[index].apply(this, args) : value;
    +          while (++index < length) {
    +            result = funcs[index].call(this, result);
    +          }
    +          return result;
    +        };
    +      });
    +    }
    +
    +    /**
    +     * Creates a function that wraps `func` to invoke it with optional `this`
    +     * binding of `thisArg`, partial application, and currying.
    +     *
    +     * @private
    +     * @param {Function|string} func The function or method name to wrap.
    +     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    +     * @param {*} [thisArg] The `this` binding of `func`.
    +     * @param {Array} [partials] The arguments to prepend to those provided to
    +     *  the new function.
    +     * @param {Array} [holders] The `partials` placeholder indexes.
    +     * @param {Array} [partialsRight] The arguments to append to those provided
    +     *  to the new function.
    +     * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
    +     * @param {Array} [argPos] The argument positions of the new function.
    +     * @param {number} [ary] The arity cap of `func`.
    +     * @param {number} [arity] The arity of `func`.
    +     * @returns {Function} Returns the new wrapped function.
    +     */
    +    function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
    +      var isAry = bitmask & WRAP_ARY_FLAG,
    +        isBind = bitmask & WRAP_BIND_FLAG,
    +        isBindKey = bitmask & WRAP_BIND_KEY_FLAG,
    +        isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),
    +        isFlip = bitmask & WRAP_FLIP_FLAG,
    +        Ctor = isBindKey ? undefined : createCtor(func);
    +      function wrapper() {
    +        var length = arguments.length,
    +          args = Array(length),
    +          index = length;
    +        while (index--) {
    +          args[index] = arguments[index];
    +        }
    +        if (isCurried) {
    +          var placeholder = getHolder(wrapper),
    +            holdersCount = countHolders(args, placeholder);
    +        }
    +        if (partials) {
    +          args = composeArgs(args, partials, holders, isCurried);
    +        }
    +        if (partialsRight) {
    +          args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
    +        }
    +        length -= holdersCount;
    +        if (isCurried && length < arity) {
    +          var newHolders = replaceHolders(args, placeholder);
    +          return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, thisArg, args, newHolders, argPos, ary, arity - length);
    +        }
    +        var thisBinding = isBind ? thisArg : this,
    +          fn = isBindKey ? thisBinding[func] : func;
    +        length = args.length;
    +        if (argPos) {
    +          args = reorder(args, argPos);
    +        } else if (isFlip && length > 1) {
    +          args.reverse();
    +        }
    +        if (isAry && ary < length) {
    +          args.length = ary;
    +        }
    +        if (this && this !== root && this instanceof wrapper) {
    +          fn = Ctor || createCtor(fn);
    +        }
    +        return fn.apply(thisBinding, args);
    +      }
    +      return wrapper;
    +    }
    +
    +    /**
    +     * Creates a function like `_.invertBy`.
    +     *
    +     * @private
    +     * @param {Function} setter The function to set accumulator values.
    +     * @param {Function} toIteratee The function to resolve iteratees.
    +     * @returns {Function} Returns the new inverter function.
    +     */
    +    function createInverter(setter, toIteratee) {
    +      return function (object, iteratee) {
    +        return baseInverter(object, setter, toIteratee(iteratee), {});
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that performs a mathematical operation on two values.
    +     *
    +     * @private
    +     * @param {Function} operator The function to perform the operation.
    +     * @param {number} [defaultValue] The value used for `undefined` arguments.
    +     * @returns {Function} Returns the new mathematical operation function.
    +     */
    +    function createMathOperation(operator, defaultValue) {
    +      return function (value, other) {
    +        var result;
    +        if (value === undefined && other === undefined) {
    +          return defaultValue;
    +        }
    +        if (value !== undefined) {
    +          result = value;
    +        }
    +        if (other !== undefined) {
    +          if (result === undefined) {
    +            return other;
    +          }
    +          if (typeof value == 'string' || typeof other == 'string') {
    +            value = baseToString(value);
    +            other = baseToString(other);
    +          } else {
    +            value = baseToNumber(value);
    +            other = baseToNumber(other);
    +          }
    +          result = operator(value, other);
    +        }
    +        return result;
    +      };
    +    }
    +
    +    /**
    +     * Creates a function like `_.over`.
    +     *
    +     * @private
    +     * @param {Function} arrayFunc The function to iterate over iteratees.
    +     * @returns {Function} Returns the new over function.
    +     */
    +    function createOver(arrayFunc) {
    +      return flatRest(function (iteratees) {
    +        iteratees = arrayMap(iteratees, baseUnary(getIteratee()));
    +        return baseRest(function (args) {
    +          var thisArg = this;
    +          return arrayFunc(iteratees, function (iteratee) {
    +            return apply(iteratee, thisArg, args);
    +          });
    +        });
    +      });
    +    }
    +
    +    /**
    +     * Creates the padding for `string` based on `length`. The `chars` string
    +     * is truncated if the number of characters exceeds `length`.
    +     *
    +     * @private
    +     * @param {number} length The padding length.
    +     * @param {string} [chars=' '] The string used as padding.
    +     * @returns {string} Returns the padding for `string`.
    +     */
    +    function createPadding(length, chars) {
    +      chars = chars === undefined ? ' ' : baseToString(chars);
    +      var charsLength = chars.length;
    +      if (charsLength < 2) {
    +        return charsLength ? baseRepeat(chars, length) : chars;
    +      }
    +      var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));
    +      return hasUnicode(chars) ? castSlice(stringToArray(result), 0, length).join('') : result.slice(0, length);
    +    }
    +
    +    /**
    +     * Creates a function that wraps `func` to invoke it with the `this` binding
    +     * of `thisArg` and `partials` prepended to the arguments it receives.
    +     *
    +     * @private
    +     * @param {Function} func The function to wrap.
    +     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    +     * @param {*} thisArg The `this` binding of `func`.
    +     * @param {Array} partials The arguments to prepend to those provided to
    +     *  the new function.
    +     * @returns {Function} Returns the new wrapped function.
    +     */
    +    function createPartial(func, bitmask, thisArg, partials) {
    +      var isBind = bitmask & WRAP_BIND_FLAG,
    +        Ctor = createCtor(func);
    +      function wrapper() {
    +        var argsIndex = -1,
    +          argsLength = arguments.length,
    +          leftIndex = -1,
    +          leftLength = partials.length,
    +          args = Array(leftLength + argsLength),
    +          fn = this && this !== root && this instanceof wrapper ? Ctor : func;
    +        while (++leftIndex < leftLength) {
    +          args[leftIndex] = partials[leftIndex];
    +        }
    +        while (argsLength--) {
    +          args[leftIndex++] = arguments[++argsIndex];
    +        }
    +        return apply(fn, isBind ? thisArg : this, args);
    +      }
    +      return wrapper;
    +    }
    +
    +    /**
    +     * Creates a `_.range` or `_.rangeRight` function.
    +     *
    +     * @private
    +     * @param {boolean} [fromRight] Specify iterating from right to left.
    +     * @returns {Function} Returns the new range function.
    +     */
    +    function createRange(fromRight) {
    +      return function (start, end, step) {
    +        if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {
    +          end = step = undefined;
    +        }
    +        // Ensure the sign of `-0` is preserved.
    +        start = toFinite(start);
    +        if (end === undefined) {
    +          end = start;
    +          start = 0;
    +        } else {
    +          end = toFinite(end);
    +        }
    +        step = step === undefined ? start < end ? 1 : -1 : toFinite(step);
    +        return baseRange(start, end, step, fromRight);
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that performs a relational operation on two values.
    +     *
    +     * @private
    +     * @param {Function} operator The function to perform the operation.
    +     * @returns {Function} Returns the new relational operation function.
    +     */
    +    function createRelationalOperation(operator) {
    +      return function (value, other) {
    +        if (!(typeof value == 'string' && typeof other == 'string')) {
    +          value = toNumber(value);
    +          other = toNumber(other);
    +        }
    +        return operator(value, other);
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that wraps `func` to continue currying.
    +     *
    +     * @private
    +     * @param {Function} func The function to wrap.
    +     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    +     * @param {Function} wrapFunc The function to create the `func` wrapper.
    +     * @param {*} placeholder The placeholder value.
    +     * @param {*} [thisArg] The `this` binding of `func`.
    +     * @param {Array} [partials] The arguments to prepend to those provided to
    +     *  the new function.
    +     * @param {Array} [holders] The `partials` placeholder indexes.
    +     * @param {Array} [argPos] The argument positions of the new function.
    +     * @param {number} [ary] The arity cap of `func`.
    +     * @param {number} [arity] The arity of `func`.
    +     * @returns {Function} Returns the new wrapped function.
    +     */
    +    function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
    +      var isCurry = bitmask & WRAP_CURRY_FLAG,
    +        newHolders = isCurry ? holders : undefined,
    +        newHoldersRight = isCurry ? undefined : holders,
    +        newPartials = isCurry ? partials : undefined,
    +        newPartialsRight = isCurry ? undefined : partials;
    +      bitmask |= isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG;
    +      bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);
    +      if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {
    +        bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);
    +      }
    +      var newData = [func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, newHoldersRight, argPos, ary, arity];
    +      var result = wrapFunc.apply(undefined, newData);
    +      if (isLaziable(func)) {
    +        setData(result, newData);
    +      }
    +      result.placeholder = placeholder;
    +      return setWrapToString(result, func, bitmask);
    +    }
    +
    +    /**
    +     * Creates a function like `_.round`.
    +     *
    +     * @private
    +     * @param {string} methodName The name of the `Math` method to use when rounding.
    +     * @returns {Function} Returns the new round function.
    +     */
    +    function createRound(methodName) {
    +      var func = Math[methodName];
    +      return function (number, precision) {
    +        number = toNumber(number);
    +        precision = precision == null ? 0 : nativeMin(toInteger(precision), 292);
    +        if (precision && nativeIsFinite(number)) {
    +          // Shift with exponential notation to avoid floating-point issues.
    +          // See [MDN](https://mdn.io/round#Examples) for more details.
    +          var pair = (toString(number) + 'e').split('e'),
    +            value = func(pair[0] + 'e' + (+pair[1] + precision));
    +          pair = (toString(value) + 'e').split('e');
    +          return +(pair[0] + 'e' + (+pair[1] - precision));
    +        }
    +        return func(number);
    +      };
    +    }
    +
    +    /**
    +     * Creates a set object of `values`.
    +     *
    +     * @private
    +     * @param {Array} values The values to add to the set.
    +     * @returns {Object} Returns the new set.
    +     */
    +    var createSet = !(Set && 1 / setToArray(new Set([, -0]))[1] == INFINITY) ? noop : function (values) {
    +      return new Set(values);
    +    };
    +
    +    /**
    +     * Creates a `_.toPairs` or `_.toPairsIn` function.
    +     *
    +     * @private
    +     * @param {Function} keysFunc The function to get the keys of a given object.
    +     * @returns {Function} Returns the new pairs function.
    +     */
    +    function createToPairs(keysFunc) {
    +      return function (object) {
    +        var tag = getTag(object);
    +        if (tag == mapTag) {
    +          return mapToArray(object);
    +        }
    +        if (tag == setTag) {
    +          return setToPairs(object);
    +        }
    +        return baseToPairs(object, keysFunc(object));
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that either curries or invokes `func` with optional
    +     * `this` binding and partially applied arguments.
    +     *
    +     * @private
    +     * @param {Function|string} func The function or method name to wrap.
    +     * @param {number} bitmask The bitmask flags.
    +     *    1 - `_.bind`
    +     *    2 - `_.bindKey`
    +     *    4 - `_.curry` or `_.curryRight` of a bound function
    +     *    8 - `_.curry`
    +     *   16 - `_.curryRight`
    +     *   32 - `_.partial`
    +     *   64 - `_.partialRight`
    +     *  128 - `_.rearg`
    +     *  256 - `_.ary`
    +     *  512 - `_.flip`
    +     * @param {*} [thisArg] The `this` binding of `func`.
    +     * @param {Array} [partials] The arguments to be partially applied.
    +     * @param {Array} [holders] The `partials` placeholder indexes.
    +     * @param {Array} [argPos] The argument positions of the new function.
    +     * @param {number} [ary] The arity cap of `func`.
    +     * @param {number} [arity] The arity of `func`.
    +     * @returns {Function} Returns the new wrapped function.
    +     */
    +    function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
    +      var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;
    +      if (!isBindKey && typeof func != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      var length = partials ? partials.length : 0;
    +      if (!length) {
    +        bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);
    +        partials = holders = undefined;
    +      }
    +      ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
    +      arity = arity === undefined ? arity : toInteger(arity);
    +      length -= holders ? holders.length : 0;
    +      if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {
    +        var partialsRight = partials,
    +          holdersRight = holders;
    +        partials = holders = undefined;
    +      }
    +      var data = isBindKey ? undefined : getData(func);
    +      var newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
    +      if (data) {
    +        mergeData(newData, data);
    +      }
    +      func = newData[0];
    +      bitmask = newData[1];
    +      thisArg = newData[2];
    +      partials = newData[3];
    +      holders = newData[4];
    +      arity = newData[9] = newData[9] === undefined ? isBindKey ? 0 : func.length : nativeMax(newData[9] - length, 0);
    +      if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {
    +        bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);
    +      }
    +      if (!bitmask || bitmask == WRAP_BIND_FLAG) {
    +        var result = createBind(func, bitmask, thisArg);
    +      } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {
    +        result = createCurry(func, bitmask, arity);
    +      } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {
    +        result = createPartial(func, bitmask, thisArg, partials);
    +      } else {
    +        result = createHybrid.apply(undefined, newData);
    +      }
    +      var setter = data ? baseSetData : setData;
    +      return setWrapToString(setter(result, newData), func, bitmask);
    +    }
    +
    +    /**
    +     * Used by `_.defaults` to customize its `_.assignIn` use to assign properties
    +     * of source objects to the destination object for all destination properties
    +     * that resolve to `undefined`.
    +     *
    +     * @private
    +     * @param {*} objValue The destination value.
    +     * @param {*} srcValue The source value.
    +     * @param {string} key The key of the property to assign.
    +     * @param {Object} object The parent object of `objValue`.
    +     * @returns {*} Returns the value to assign.
    +     */
    +    function customDefaultsAssignIn(objValue, srcValue, key, object) {
    +      if (objValue === undefined || eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key)) {
    +        return srcValue;
    +      }
    +      return objValue;
    +    }
    +
    +    /**
    +     * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source
    +     * objects into destination objects that are passed thru.
    +     *
    +     * @private
    +     * @param {*} objValue The destination value.
    +     * @param {*} srcValue The source value.
    +     * @param {string} key The key of the property to merge.
    +     * @param {Object} object The parent object of `objValue`.
    +     * @param {Object} source The parent object of `srcValue`.
    +     * @param {Object} [stack] Tracks traversed source values and their merged
    +     *  counterparts.
    +     * @returns {*} Returns the value to assign.
    +     */
    +    function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {
    +      if (isObject(objValue) && isObject(srcValue)) {
    +        // Recursively merge objects and arrays (susceptible to call stack limits).
    +        stack.set(srcValue, objValue);
    +        baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack);
    +        stack['delete'](srcValue);
    +      }
    +      return objValue;
    +    }
    +
    +    /**
    +     * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain
    +     * objects.
    +     *
    +     * @private
    +     * @param {*} value The value to inspect.
    +     * @param {string} key The key of the property to inspect.
    +     * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.
    +     */
    +    function customOmitClone(value) {
    +      return isPlainObject(value) ? undefined : value;
    +    }
    +
    +    /**
    +     * A specialized version of `baseIsEqualDeep` for arrays with support for
    +     * partial deep comparisons.
    +     *
    +     * @private
    +     * @param {Array} array The array to compare.
    +     * @param {Array} other The other array to compare.
    +     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
    +     * @param {Function} customizer The function to customize comparisons.
    +     * @param {Function} equalFunc The function to determine equivalents of values.
    +     * @param {Object} stack Tracks traversed `array` and `other` objects.
    +     * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
    +     */
    +    function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
    +      var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
    +        arrLength = array.length,
    +        othLength = other.length;
    +      if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
    +        return false;
    +      }
    +      // Check that cyclic values are equal.
    +      var arrStacked = stack.get(array);
    +      var othStacked = stack.get(other);
    +      if (arrStacked && othStacked) {
    +        return arrStacked == other && othStacked == array;
    +      }
    +      var index = -1,
    +        result = true,
    +        seen = bitmask & COMPARE_UNORDERED_FLAG ? new SetCache() : undefined;
    +      stack.set(array, other);
    +      stack.set(other, array);
    +
    +      // Ignore non-index properties.
    +      while (++index < arrLength) {
    +        var arrValue = array[index],
    +          othValue = other[index];
    +        if (customizer) {
    +          var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack);
    +        }
    +        if (compared !== undefined) {
    +          if (compared) {
    +            continue;
    +          }
    +          result = false;
    +          break;
    +        }
    +        // Recursively compare arrays (susceptible to call stack limits).
    +        if (seen) {
    +          if (!arraySome(other, function (othValue, othIndex) {
    +            if (!cacheHas(seen, othIndex) && (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
    +              return seen.push(othIndex);
    +            }
    +          })) {
    +            result = false;
    +            break;
    +          }
    +        } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
    +          result = false;
    +          break;
    +        }
    +      }
    +      stack['delete'](array);
    +      stack['delete'](other);
    +      return result;
    +    }
    +
    +    /**
    +     * A specialized version of `baseIsEqualDeep` for comparing objects of
    +     * the same `toStringTag`.
    +     *
    +     * **Note:** This function only supports comparing values with tags of
    +     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
    +     *
    +     * @private
    +     * @param {Object} object The object to compare.
    +     * @param {Object} other The other object to compare.
    +     * @param {string} tag The `toStringTag` of the objects to compare.
    +     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
    +     * @param {Function} customizer The function to customize comparisons.
    +     * @param {Function} equalFunc The function to determine equivalents of values.
    +     * @param {Object} stack Tracks traversed `object` and `other` objects.
    +     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
    +     */
    +    function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
    +      switch (tag) {
    +        case dataViewTag:
    +          if (object.byteLength != other.byteLength || object.byteOffset != other.byteOffset) {
    +            return false;
    +          }
    +          object = object.buffer;
    +          other = other.buffer;
    +        case arrayBufferTag:
    +          if (object.byteLength != other.byteLength || !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
    +            return false;
    +          }
    +          return true;
    +        case boolTag:
    +        case dateTag:
    +        case numberTag:
    +          // Coerce booleans to `1` or `0` and dates to milliseconds.
    +          // Invalid dates are coerced to `NaN`.
    +          return eq(+object, +other);
    +        case errorTag:
    +          return object.name == other.name && object.message == other.message;
    +        case regexpTag:
    +        case stringTag:
    +          // Coerce regexes to strings and treat strings, primitives and objects,
    +          // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
    +          // for more details.
    +          return object == other + '';
    +        case mapTag:
    +          var convert = mapToArray;
    +        case setTag:
    +          var isPartial = bitmask & COMPARE_PARTIAL_FLAG;
    +          convert || (convert = setToArray);
    +          if (object.size != other.size && !isPartial) {
    +            return false;
    +          }
    +          // Assume cyclic values are equal.
    +          var stacked = stack.get(object);
    +          if (stacked) {
    +            return stacked == other;
    +          }
    +          bitmask |= COMPARE_UNORDERED_FLAG;
    +
    +          // Recursively compare objects (susceptible to call stack limits).
    +          stack.set(object, other);
    +          var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
    +          stack['delete'](object);
    +          return result;
    +        case symbolTag:
    +          if (symbolValueOf) {
    +            return symbolValueOf.call(object) == symbolValueOf.call(other);
    +          }
    +      }
    +      return false;
    +    }
    +
    +    /**
    +     * A specialized version of `baseIsEqualDeep` for objects with support for
    +     * partial deep comparisons.
    +     *
    +     * @private
    +     * @param {Object} object The object to compare.
    +     * @param {Object} other The other object to compare.
    +     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
    +     * @param {Function} customizer The function to customize comparisons.
    +     * @param {Function} equalFunc The function to determine equivalents of values.
    +     * @param {Object} stack Tracks traversed `object` and `other` objects.
    +     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
    +     */
    +    function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
    +      var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
    +        objProps = getAllKeys(object),
    +        objLength = objProps.length,
    +        othProps = getAllKeys(other),
    +        othLength = othProps.length;
    +      if (objLength != othLength && !isPartial) {
    +        return false;
    +      }
    +      var index = objLength;
    +      while (index--) {
    +        var key = objProps[index];
    +        if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {
    +          return false;
    +        }
    +      }
    +      // Check that cyclic values are equal.
    +      var objStacked = stack.get(object);
    +      var othStacked = stack.get(other);
    +      if (objStacked && othStacked) {
    +        return objStacked == other && othStacked == object;
    +      }
    +      var result = true;
    +      stack.set(object, other);
    +      stack.set(other, object);
    +      var skipCtor = isPartial;
    +      while (++index < objLength) {
    +        key = objProps[index];
    +        var objValue = object[key],
    +          othValue = other[key];
    +        if (customizer) {
    +          var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack);
    +        }
    +        // Recursively compare objects (susceptible to call stack limits).
    +        if (!(compared === undefined ? objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack) : compared)) {
    +          result = false;
    +          break;
    +        }
    +        skipCtor || (skipCtor = key == 'constructor');
    +      }
    +      if (result && !skipCtor) {
    +        var objCtor = object.constructor,
    +          othCtor = other.constructor;
    +
    +        // Non `Object` object instances with different constructors are not equal.
    +        if (objCtor != othCtor && 'constructor' in object && 'constructor' in other && !(typeof objCtor == 'function' && objCtor instanceof objCtor && typeof othCtor == 'function' && othCtor instanceof othCtor)) {
    +          result = false;
    +        }
    +      }
    +      stack['delete'](object);
    +      stack['delete'](other);
    +      return result;
    +    }
    +
    +    /**
    +     * A specialized version of `baseRest` which flattens the rest array.
    +     *
    +     * @private
    +     * @param {Function} func The function to apply a rest parameter to.
    +     * @returns {Function} Returns the new function.
    +     */
    +    function flatRest(func) {
    +      return setToString(overRest(func, undefined, flatten), func + '');
    +    }
    +
    +    /**
    +     * Creates an array of own enumerable property names and symbols of `object`.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @returns {Array} Returns the array of property names and symbols.
    +     */
    +    function getAllKeys(object) {
    +      return baseGetAllKeys(object, keys, getSymbols);
    +    }
    +
    +    /**
    +     * Creates an array of own and inherited enumerable property names and
    +     * symbols of `object`.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @returns {Array} Returns the array of property names and symbols.
    +     */
    +    function getAllKeysIn(object) {
    +      return baseGetAllKeys(object, keysIn, getSymbolsIn);
    +    }
    +
    +    /**
    +     * Gets metadata for `func`.
    +     *
    +     * @private
    +     * @param {Function} func The function to query.
    +     * @returns {*} Returns the metadata for `func`.
    +     */
    +    var getData = !metaMap ? noop : function (func) {
    +      return metaMap.get(func);
    +    };
    +
    +    /**
    +     * Gets the name of `func`.
    +     *
    +     * @private
    +     * @param {Function} func The function to query.
    +     * @returns {string} Returns the function name.
    +     */
    +    function getFuncName(func) {
    +      var result = func.name + '',
    +        array = realNames[result],
    +        length = hasOwnProperty.call(realNames, result) ? array.length : 0;
    +      while (length--) {
    +        var data = array[length],
    +          otherFunc = data.func;
    +        if (otherFunc == null || otherFunc == func) {
    +          return data.name;
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Gets the argument placeholder value for `func`.
    +     *
    +     * @private
    +     * @param {Function} func The function to inspect.
    +     * @returns {*} Returns the placeholder value.
    +     */
    +    function getHolder(func) {
    +      var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
    +      return object.placeholder;
    +    }
    +
    +    /**
    +     * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
    +     * this function returns the custom method, otherwise it returns `baseIteratee`.
    +     * If arguments are provided, the chosen function is invoked with them and
    +     * its result is returned.
    +     *
    +     * @private
    +     * @param {*} [value] The value to convert to an iteratee.
    +     * @param {number} [arity] The arity of the created iteratee.
    +     * @returns {Function} Returns the chosen function or its result.
    +     */
    +    function getIteratee() {
    +      var result = lodash.iteratee || iteratee;
    +      result = result === iteratee ? baseIteratee : result;
    +      return arguments.length ? result(arguments[0], arguments[1]) : result;
    +    }
    +
    +    /**
    +     * Gets the data for `map`.
    +     *
    +     * @private
    +     * @param {Object} map The map to query.
    +     * @param {string} key The reference key.
    +     * @returns {*} Returns the map data.
    +     */
    +    function getMapData(map, key) {
    +      var data = map.__data__;
    +      return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map;
    +    }
    +
    +    /**
    +     * Gets the property names, values, and compare flags of `object`.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @returns {Array} Returns the match data of `object`.
    +     */
    +    function getMatchData(object) {
    +      var result = keys(object),
    +        length = result.length;
    +      while (length--) {
    +        var key = result[length],
    +          value = object[key];
    +        result[length] = [key, value, isStrictComparable(value)];
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Gets the native function at `key` of `object`.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @param {string} key The key of the method to get.
    +     * @returns {*} Returns the function if it's native, else `undefined`.
    +     */
    +    function getNative(object, key) {
    +      var value = getValue(object, key);
    +      return baseIsNative(value) ? value : undefined;
    +    }
    +
    +    /**
    +     * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
    +     *
    +     * @private
    +     * @param {*} value The value to query.
    +     * @returns {string} Returns the raw `toStringTag`.
    +     */
    +    function getRawTag(value) {
    +      var isOwn = hasOwnProperty.call(value, symToStringTag),
    +        tag = value[symToStringTag];
    +      try {
    +        value[symToStringTag] = undefined;
    +        var unmasked = true;
    +      } catch (e) {}
    +      var result = nativeObjectToString.call(value);
    +      if (unmasked) {
    +        if (isOwn) {
    +          value[symToStringTag] = tag;
    +        } else {
    +          delete value[symToStringTag];
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Creates an array of the own enumerable symbols of `object`.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @returns {Array} Returns the array of symbols.
    +     */
    +    var getSymbols = !nativeGetSymbols ? stubArray : function (object) {
    +      if (object == null) {
    +        return [];
    +      }
    +      object = Object(object);
    +      return arrayFilter(nativeGetSymbols(object), function (symbol) {
    +        return propertyIsEnumerable.call(object, symbol);
    +      });
    +    };
    +
    +    /**
    +     * Creates an array of the own and inherited enumerable symbols of `object`.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @returns {Array} Returns the array of symbols.
    +     */
    +    var getSymbolsIn = !nativeGetSymbols ? stubArray : function (object) {
    +      var result = [];
    +      while (object) {
    +        arrayPush(result, getSymbols(object));
    +        object = getPrototype(object);
    +      }
    +      return result;
    +    };
    +
    +    /**
    +     * Gets the `toStringTag` of `value`.
    +     *
    +     * @private
    +     * @param {*} value The value to query.
    +     * @returns {string} Returns the `toStringTag`.
    +     */
    +    var getTag = baseGetTag;
    +
    +    // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
    +    if (DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag || Map && getTag(new Map()) != mapTag || Promise && getTag(Promise.resolve()) != promiseTag || Set && getTag(new Set()) != setTag || WeakMap && getTag(new WeakMap()) != weakMapTag) {
    +      getTag = function getTag(value) {
    +        var result = baseGetTag(value),
    +          Ctor = result == objectTag ? value.constructor : undefined,
    +          ctorString = Ctor ? toSource(Ctor) : '';
    +        if (ctorString) {
    +          switch (ctorString) {
    +            case dataViewCtorString:
    +              return dataViewTag;
    +            case mapCtorString:
    +              return mapTag;
    +            case promiseCtorString:
    +              return promiseTag;
    +            case setCtorString:
    +              return setTag;
    +            case weakMapCtorString:
    +              return weakMapTag;
    +          }
    +        }
    +        return result;
    +      };
    +    }
    +
    +    /**
    +     * Gets the view, applying any `transforms` to the `start` and `end` positions.
    +     *
    +     * @private
    +     * @param {number} start The start of the view.
    +     * @param {number} end The end of the view.
    +     * @param {Array} transforms The transformations to apply to the view.
    +     * @returns {Object} Returns an object containing the `start` and `end`
    +     *  positions of the view.
    +     */
    +    function getView(start, end, transforms) {
    +      var index = -1,
    +        length = transforms.length;
    +      while (++index < length) {
    +        var data = transforms[index],
    +          size = data.size;
    +        switch (data.type) {
    +          case 'drop':
    +            start += size;
    +            break;
    +          case 'dropRight':
    +            end -= size;
    +            break;
    +          case 'take':
    +            end = nativeMin(end, start + size);
    +            break;
    +          case 'takeRight':
    +            start = nativeMax(start, end - size);
    +            break;
    +        }
    +      }
    +      return {
    +        'start': start,
    +        'end': end
    +      };
    +    }
    +
    +    /**
    +     * Extracts wrapper details from the `source` body comment.
    +     *
    +     * @private
    +     * @param {string} source The source to inspect.
    +     * @returns {Array} Returns the wrapper details.
    +     */
    +    function getWrapDetails(source) {
    +      var match = source.match(reWrapDetails);
    +      return match ? match[1].split(reSplitDetails) : [];
    +    }
    +
    +    /**
    +     * Checks if `path` exists on `object`.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @param {Array|string} path The path to check.
    +     * @param {Function} hasFunc The function to check properties.
    +     * @returns {boolean} Returns `true` if `path` exists, else `false`.
    +     */
    +    function hasPath(object, path, hasFunc) {
    +      path = castPath(path, object);
    +      var index = -1,
    +        length = path.length,
    +        result = false;
    +      while (++index < length) {
    +        var key = toKey(path[index]);
    +        if (!(result = object != null && hasFunc(object, key))) {
    +          break;
    +        }
    +        object = object[key];
    +      }
    +      if (result || ++index != length) {
    +        return result;
    +      }
    +      length = object == null ? 0 : object.length;
    +      return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object));
    +    }
    +
    +    /**
    +     * Initializes an array clone.
    +     *
    +     * @private
    +     * @param {Array} array The array to clone.
    +     * @returns {Array} Returns the initialized clone.
    +     */
    +    function initCloneArray(array) {
    +      var length = array.length,
    +        result = new array.constructor(length);
    +
    +      // Add properties assigned by `RegExp#exec`.
    +      if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
    +        result.index = array.index;
    +        result.input = array.input;
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Initializes an object clone.
    +     *
    +     * @private
    +     * @param {Object} object The object to clone.
    +     * @returns {Object} Returns the initialized clone.
    +     */
    +    function initCloneObject(object) {
    +      return typeof object.constructor == 'function' && !isPrototype(object) ? baseCreate(getPrototype(object)) : {};
    +    }
    +
    +    /**
    +     * Initializes an object clone based on its `toStringTag`.
    +     *
    +     * **Note:** This function only supports cloning values with tags of
    +     * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
    +     *
    +     * @private
    +     * @param {Object} object The object to clone.
    +     * @param {string} tag The `toStringTag` of the object to clone.
    +     * @param {boolean} [isDeep] Specify a deep clone.
    +     * @returns {Object} Returns the initialized clone.
    +     */
    +    function initCloneByTag(object, tag, isDeep) {
    +      var Ctor = object.constructor;
    +      switch (tag) {
    +        case arrayBufferTag:
    +          return cloneArrayBuffer(object);
    +        case boolTag:
    +        case dateTag:
    +          return new Ctor(+object);
    +        case dataViewTag:
    +          return cloneDataView(object, isDeep);
    +        case float32Tag:
    +        case float64Tag:
    +        case int8Tag:
    +        case int16Tag:
    +        case int32Tag:
    +        case uint8Tag:
    +        case uint8ClampedTag:
    +        case uint16Tag:
    +        case uint32Tag:
    +          return cloneTypedArray(object, isDeep);
    +        case mapTag:
    +          return new Ctor();
    +        case numberTag:
    +        case stringTag:
    +          return new Ctor(object);
    +        case regexpTag:
    +          return cloneRegExp(object);
    +        case setTag:
    +          return new Ctor();
    +        case symbolTag:
    +          return cloneSymbol(object);
    +      }
    +    }
    +
    +    /**
    +     * Inserts wrapper `details` in a comment at the top of the `source` body.
    +     *
    +     * @private
    +     * @param {string} source The source to modify.
    +     * @returns {Array} details The details to insert.
    +     * @returns {string} Returns the modified source.
    +     */
    +    function insertWrapDetails(source, details) {
    +      var length = details.length;
    +      if (!length) {
    +        return source;
    +      }
    +      var lastIndex = length - 1;
    +      details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];
    +      details = details.join(length > 2 ? ', ' : ' ');
    +      return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n');
    +    }
    +
    +    /**
    +     * Checks if `value` is a flattenable `arguments` object or array.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
    +     */
    +    function isFlattenable(value) {
    +      return isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]);
    +    }
    +
    +    /**
    +     * Checks if `value` is a valid array-like index.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
    +     * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
    +     */
    +    function isIndex(value, length) {
    +      var type = typeof value;
    +      length = length == null ? MAX_SAFE_INTEGER : length;
    +      return !!length && (type == 'number' || type != 'symbol' && reIsUint.test(value)) && value > -1 && value % 1 == 0 && value < length;
    +    }
    +
    +    /**
    +     * Checks if the given arguments are from an iteratee call.
    +     *
    +     * @private
    +     * @param {*} value The potential iteratee value argument.
    +     * @param {*} index The potential iteratee index or key argument.
    +     * @param {*} object The potential iteratee object argument.
    +     * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
    +     *  else `false`.
    +     */
    +    function isIterateeCall(value, index, object) {
    +      if (!isObject(object)) {
    +        return false;
    +      }
    +      var type = typeof index;
    +      if (type == 'number' ? isArrayLike(object) && isIndex(index, object.length) : type == 'string' && index in object) {
    +        return eq(object[index], value);
    +      }
    +      return false;
    +    }
    +
    +    /**
    +     * Checks if `value` is a property name and not a property path.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @param {Object} [object] The object to query keys on.
    +     * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
    +     */
    +    function isKey(value, object) {
    +      if (isArray(value)) {
    +        return false;
    +      }
    +      var type = typeof value;
    +      if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol(value)) {
    +        return true;
    +      }
    +      return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object(object);
    +    }
    +
    +    /**
    +     * Checks if `value` is suitable for use as unique object key.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
    +     */
    +    function isKeyable(value) {
    +      var type = typeof value;
    +      return type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean' ? value !== '__proto__' : value === null;
    +    }
    +
    +    /**
    +     * Checks if `func` has a lazy counterpart.
    +     *
    +     * @private
    +     * @param {Function} func The function to check.
    +     * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
    +     *  else `false`.
    +     */
    +    function isLaziable(func) {
    +      var funcName = getFuncName(func),
    +        other = lodash[funcName];
    +      if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
    +        return false;
    +      }
    +      if (func === other) {
    +        return true;
    +      }
    +      var data = getData(other);
    +      return !!data && func === data[0];
    +    }
    +
    +    /**
    +     * Checks if `func` has its source masked.
    +     *
    +     * @private
    +     * @param {Function} func The function to check.
    +     * @returns {boolean} Returns `true` if `func` is masked, else `false`.
    +     */
    +    function isMasked(func) {
    +      return !!maskSrcKey && maskSrcKey in func;
    +    }
    +
    +    /**
    +     * Checks if `func` is capable of being masked.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `func` is maskable, else `false`.
    +     */
    +    var isMaskable = coreJsData ? isFunction : stubFalse;
    +
    +    /**
    +     * Checks if `value` is likely a prototype object.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
    +     */
    +    function isPrototype(value) {
    +      var Ctor = value && value.constructor,
    +        proto = typeof Ctor == 'function' && Ctor.prototype || objectProto;
    +      return value === proto;
    +    }
    +
    +    /**
    +     * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
    +     *
    +     * @private
    +     * @param {*} value The value to check.
    +     * @returns {boolean} Returns `true` if `value` if suitable for strict
    +     *  equality comparisons, else `false`.
    +     */
    +    function isStrictComparable(value) {
    +      return value === value && !isObject(value);
    +    }
    +
    +    /**
    +     * A specialized version of `matchesProperty` for source values suitable
    +     * for strict equality comparisons, i.e. `===`.
    +     *
    +     * @private
    +     * @param {string} key The key of the property to get.
    +     * @param {*} srcValue The value to match.
    +     * @returns {Function} Returns the new spec function.
    +     */
    +    function matchesStrictComparable(key, srcValue) {
    +      return function (object) {
    +        if (object == null) {
    +          return false;
    +        }
    +        return object[key] === srcValue && (srcValue !== undefined || key in Object(object));
    +      };
    +    }
    +
    +    /**
    +     * A specialized version of `_.memoize` which clears the memoized function's
    +     * cache when it exceeds `MAX_MEMOIZE_SIZE`.
    +     *
    +     * @private
    +     * @param {Function} func The function to have its output memoized.
    +     * @returns {Function} Returns the new memoized function.
    +     */
    +    function memoizeCapped(func) {
    +      var result = memoize(func, function (key) {
    +        if (cache.size === MAX_MEMOIZE_SIZE) {
    +          cache.clear();
    +        }
    +        return key;
    +      });
    +      var cache = result.cache;
    +      return result;
    +    }
    +
    +    /**
    +     * Merges the function metadata of `source` into `data`.
    +     *
    +     * Merging metadata reduces the number of wrappers used to invoke a function.
    +     * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
    +     * may be applied regardless of execution order. Methods like `_.ary` and
    +     * `_.rearg` modify function arguments, making the order in which they are
    +     * executed important, preventing the merging of metadata. However, we make
    +     * an exception for a safe combined case where curried functions have `_.ary`
    +     * and or `_.rearg` applied.
    +     *
    +     * @private
    +     * @param {Array} data The destination metadata.
    +     * @param {Array} source The source metadata.
    +     * @returns {Array} Returns `data`.
    +     */
    +    function mergeData(data, source) {
    +      var bitmask = data[1],
    +        srcBitmask = source[1],
    +        newBitmask = bitmask | srcBitmask,
    +        isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG);
    +      var isCombo = srcBitmask == WRAP_ARY_FLAG && bitmask == WRAP_CURRY_FLAG || srcBitmask == WRAP_ARY_FLAG && bitmask == WRAP_REARG_FLAG && data[7].length <= source[8] || srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG) && source[7].length <= source[8] && bitmask == WRAP_CURRY_FLAG;
    +
    +      // Exit early if metadata can't be merged.
    +      if (!(isCommon || isCombo)) {
    +        return data;
    +      }
    +      // Use source `thisArg` if available.
    +      if (srcBitmask & WRAP_BIND_FLAG) {
    +        data[2] = source[2];
    +        // Set when currying a bound function.
    +        newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG;
    +      }
    +      // Compose partial arguments.
    +      var value = source[3];
    +      if (value) {
    +        var partials = data[3];
    +        data[3] = partials ? composeArgs(partials, value, source[4]) : value;
    +        data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
    +      }
    +      // Compose partial right arguments.
    +      value = source[5];
    +      if (value) {
    +        partials = data[5];
    +        data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
    +        data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
    +      }
    +      // Use source `argPos` if available.
    +      value = source[7];
    +      if (value) {
    +        data[7] = value;
    +      }
    +      // Use source `ary` if it's smaller.
    +      if (srcBitmask & WRAP_ARY_FLAG) {
    +        data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
    +      }
    +      // Use source `arity` if one is not provided.
    +      if (data[9] == null) {
    +        data[9] = source[9];
    +      }
    +      // Use source `func` and merge bitmasks.
    +      data[0] = source[0];
    +      data[1] = newBitmask;
    +      return data;
    +    }
    +
    +    /**
    +     * This function is like
    +     * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
    +     * except that it includes inherited enumerable properties.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @returns {Array} Returns the array of property names.
    +     */
    +    function nativeKeysIn(object) {
    +      var result = [];
    +      if (object != null) {
    +        for (var key in Object(object)) {
    +          result.push(key);
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Converts `value` to a string using `Object.prototype.toString`.
    +     *
    +     * @private
    +     * @param {*} value The value to convert.
    +     * @returns {string} Returns the converted string.
    +     */
    +    function objectToString(value) {
    +      return nativeObjectToString.call(value);
    +    }
    +
    +    /**
    +     * A specialized version of `baseRest` which transforms the rest array.
    +     *
    +     * @private
    +     * @param {Function} func The function to apply a rest parameter to.
    +     * @param {number} [start=func.length-1] The start position of the rest parameter.
    +     * @param {Function} transform The rest array transform.
    +     * @returns {Function} Returns the new function.
    +     */
    +    function overRest(func, start, transform) {
    +      start = nativeMax(start === undefined ? func.length - 1 : start, 0);
    +      return function () {
    +        var args = arguments,
    +          index = -1,
    +          length = nativeMax(args.length - start, 0),
    +          array = Array(length);
    +        while (++index < length) {
    +          array[index] = args[start + index];
    +        }
    +        index = -1;
    +        var otherArgs = Array(start + 1);
    +        while (++index < start) {
    +          otherArgs[index] = args[index];
    +        }
    +        otherArgs[start] = transform(array);
    +        return apply(func, this, otherArgs);
    +      };
    +    }
    +
    +    /**
    +     * Gets the parent value at `path` of `object`.
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @param {Array} path The path to get the parent value of.
    +     * @returns {*} Returns the parent value.
    +     */
    +    function parent(object, path) {
    +      return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));
    +    }
    +
    +    /**
    +     * Reorder `array` according to the specified indexes where the element at
    +     * the first index is assigned as the first element, the element at
    +     * the second index is assigned as the second element, and so on.
    +     *
    +     * @private
    +     * @param {Array} array The array to reorder.
    +     * @param {Array} indexes The arranged array indexes.
    +     * @returns {Array} Returns `array`.
    +     */
    +    function reorder(array, indexes) {
    +      var arrLength = array.length,
    +        length = nativeMin(indexes.length, arrLength),
    +        oldArray = copyArray(array);
    +      while (length--) {
    +        var index = indexes[length];
    +        array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
    +      }
    +      return array;
    +    }
    +
    +    /**
    +     * Gets the value at `key`, unless `key` is "__proto__" or "constructor".
    +     *
    +     * @private
    +     * @param {Object} object The object to query.
    +     * @param {string} key The key of the property to get.
    +     * @returns {*} Returns the property value.
    +     */
    +    function safeGet(object, key) {
    +      if (key === 'constructor' && typeof object[key] === 'function') {
    +        return;
    +      }
    +      if (key == '__proto__') {
    +        return;
    +      }
    +      return object[key];
    +    }
    +
    +    /**
    +     * Sets metadata for `func`.
    +     *
    +     * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
    +     * period of time, it will trip its breaker and transition to an identity
    +     * function to avoid garbage collection pauses in V8. See
    +     * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
    +     * for more details.
    +     *
    +     * @private
    +     * @param {Function} func The function to associate metadata with.
    +     * @param {*} data The metadata.
    +     * @returns {Function} Returns `func`.
    +     */
    +    var setData = shortOut(baseSetData);
    +
    +    /**
    +     * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout).
    +     *
    +     * @private
    +     * @param {Function} func The function to delay.
    +     * @param {number} wait The number of milliseconds to delay invocation.
    +     * @returns {number|Object} Returns the timer id or timeout object.
    +     */
    +    var setTimeout = ctxSetTimeout || function (func, wait) {
    +      return root.setTimeout(func, wait);
    +    };
    +
    +    /**
    +     * Sets the `toString` method of `func` to return `string`.
    +     *
    +     * @private
    +     * @param {Function} func The function to modify.
    +     * @param {Function} string The `toString` result.
    +     * @returns {Function} Returns `func`.
    +     */
    +    var setToString = shortOut(baseSetToString);
    +
    +    /**
    +     * Sets the `toString` method of `wrapper` to mimic the source of `reference`
    +     * with wrapper details in a comment at the top of the source body.
    +     *
    +     * @private
    +     * @param {Function} wrapper The function to modify.
    +     * @param {Function} reference The reference function.
    +     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    +     * @returns {Function} Returns `wrapper`.
    +     */
    +    function setWrapToString(wrapper, reference, bitmask) {
    +      var source = reference + '';
    +      return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));
    +    }
    +
    +    /**
    +     * Creates a function that'll short out and invoke `identity` instead
    +     * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
    +     * milliseconds.
    +     *
    +     * @private
    +     * @param {Function} func The function to restrict.
    +     * @returns {Function} Returns the new shortable function.
    +     */
    +    function shortOut(func) {
    +      var count = 0,
    +        lastCalled = 0;
    +      return function () {
    +        var stamp = nativeNow(),
    +          remaining = HOT_SPAN - (stamp - lastCalled);
    +        lastCalled = stamp;
    +        if (remaining > 0) {
    +          if (++count >= HOT_COUNT) {
    +            return arguments[0];
    +          }
    +        } else {
    +          count = 0;
    +        }
    +        return func.apply(undefined, arguments);
    +      };
    +    }
    +
    +    /**
    +     * A specialized version of `_.shuffle` which mutates and sets the size of `array`.
    +     *
    +     * @private
    +     * @param {Array} array The array to shuffle.
    +     * @param {number} [size=array.length] The size of `array`.
    +     * @returns {Array} Returns `array`.
    +     */
    +    function shuffleSelf(array, size) {
    +      var index = -1,
    +        length = array.length,
    +        lastIndex = length - 1;
    +      size = size === undefined ? length : size;
    +      while (++index < size) {
    +        var rand = baseRandom(index, lastIndex),
    +          value = array[rand];
    +        array[rand] = array[index];
    +        array[index] = value;
    +      }
    +      array.length = size;
    +      return array;
    +    }
    +
    +    /**
    +     * Converts `string` to a property path array.
    +     *
    +     * @private
    +     * @param {string} string The string to convert.
    +     * @returns {Array} Returns the property path array.
    +     */
    +    var stringToPath = memoizeCapped(function (string) {
    +      var result = [];
    +      if (string.charCodeAt(0) === 46 /* . */) {
    +        result.push('');
    +      }
    +      string.replace(rePropName, function (match, number, quote, subString) {
    +        result.push(quote ? subString.replace(reEscapeChar, '$1') : number || match);
    +      });
    +      return result;
    +    });
    +
    +    /**
    +     * Converts `value` to a string key if it's not a string or symbol.
    +     *
    +     * @private
    +     * @param {*} value The value to inspect.
    +     * @returns {string|symbol} Returns the key.
    +     */
    +    function toKey(value) {
    +      if (typeof value == 'string' || isSymbol(value)) {
    +        return value;
    +      }
    +      var result = value + '';
    +      return result == '0' && 1 / value == -INFINITY ? '-0' : result;
    +    }
    +
    +    /**
    +     * Converts `func` to its source code.
    +     *
    +     * @private
    +     * @param {Function} func The function to convert.
    +     * @returns {string} Returns the source code.
    +     */
    +    function toSource(func) {
    +      if (func != null) {
    +        try {
    +          return funcToString.call(func);
    +        } catch (e) {}
    +        try {
    +          return func + '';
    +        } catch (e) {}
    +      }
    +      return '';
    +    }
    +
    +    /**
    +     * Updates wrapper `details` based on `bitmask` flags.
    +     *
    +     * @private
    +     * @returns {Array} details The details to modify.
    +     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    +     * @returns {Array} Returns `details`.
    +     */
    +    function updateWrapDetails(details, bitmask) {
    +      arrayEach(wrapFlags, function (pair) {
    +        var value = '_.' + pair[0];
    +        if (bitmask & pair[1] && !arrayIncludes(details, value)) {
    +          details.push(value);
    +        }
    +      });
    +      return details.sort();
    +    }
    +
    +    /**
    +     * Creates a clone of `wrapper`.
    +     *
    +     * @private
    +     * @param {Object} wrapper The wrapper to clone.
    +     * @returns {Object} Returns the cloned wrapper.
    +     */
    +    function wrapperClone(wrapper) {
    +      if (wrapper instanceof LazyWrapper) {
    +        return wrapper.clone();
    +      }
    +      var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
    +      result.__actions__ = copyArray(wrapper.__actions__);
    +      result.__index__ = wrapper.__index__;
    +      result.__values__ = wrapper.__values__;
    +      return result;
    +    }
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates an array of elements split into groups the length of `size`.
    +     * If `array` can't be split evenly, the final chunk will be the remaining
    +     * elements.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to process.
    +     * @param {number} [size=1] The length of each chunk
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {Array} Returns the new array of chunks.
    +     * @example
    +     *
    +     * _.chunk(['a', 'b', 'c', 'd'], 2);
    +     * // => [['a', 'b'], ['c', 'd']]
    +     *
    +     * _.chunk(['a', 'b', 'c', 'd'], 3);
    +     * // => [['a', 'b', 'c'], ['d']]
    +     */
    +    function chunk(array, size, guard) {
    +      if (guard ? isIterateeCall(array, size, guard) : size === undefined) {
    +        size = 1;
    +      } else {
    +        size = nativeMax(toInteger(size), 0);
    +      }
    +      var length = array == null ? 0 : array.length;
    +      if (!length || size < 1) {
    +        return [];
    +      }
    +      var index = 0,
    +        resIndex = 0,
    +        result = Array(nativeCeil(length / size));
    +      while (index < length) {
    +        result[resIndex++] = baseSlice(array, index, index += size);
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Creates an array with all falsey values removed. The values `false`, `null`,
    +     * `0`, `""`, `undefined`, and `NaN` are falsey.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to compact.
    +     * @returns {Array} Returns the new array of filtered values.
    +     * @example
    +     *
    +     * _.compact([0, 1, false, 2, '', 3]);
    +     * // => [1, 2, 3]
    +     */
    +    function compact(array) {
    +      var index = -1,
    +        length = array == null ? 0 : array.length,
    +        resIndex = 0,
    +        result = [];
    +      while (++index < length) {
    +        var value = array[index];
    +        if (value) {
    +          result[resIndex++] = value;
    +        }
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Creates a new array concatenating `array` with any additional arrays
    +     * and/or values.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to concatenate.
    +     * @param {...*} [values] The values to concatenate.
    +     * @returns {Array} Returns the new concatenated array.
    +     * @example
    +     *
    +     * var array = [1];
    +     * var other = _.concat(array, 2, [3], [[4]]);
    +     *
    +     * console.log(other);
    +     * // => [1, 2, 3, [4]]
    +     *
    +     * console.log(array);
    +     * // => [1]
    +     */
    +    function concat() {
    +      var length = arguments.length;
    +      if (!length) {
    +        return [];
    +      }
    +      var args = Array(length - 1),
    +        array = arguments[0],
    +        index = length;
    +      while (index--) {
    +        args[index - 1] = arguments[index];
    +      }
    +      return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
    +    }
    +
    +    /**
    +     * Creates an array of `array` values not included in the other given arrays
    +     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    +     * for equality comparisons. The order and references of result values are
    +     * determined by the first array.
    +     *
    +     * **Note:** Unlike `_.pullAll`, this method returns a new array.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {...Array} [values] The values to exclude.
    +     * @returns {Array} Returns the new array of filtered values.
    +     * @see _.without, _.xor
    +     * @example
    +     *
    +     * _.difference([2, 1], [2, 3]);
    +     * // => [1]
    +     */
    +    var difference = baseRest(function (array, values) {
    +      return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : [];
    +    });
    +
    +    /**
    +     * This method is like `_.difference` except that it accepts `iteratee` which
    +     * is invoked for each element of `array` and `values` to generate the criterion
    +     * by which they're compared. The order and references of result values are
    +     * determined by the first array. The iteratee is invoked with one argument:
    +     * (value).
    +     *
    +     * **Note:** Unlike `_.pullAllBy`, this method returns a new array.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {...Array} [values] The values to exclude.
    +     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    +     * @returns {Array} Returns the new array of filtered values.
    +     * @example
    +     *
    +     * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);
    +     * // => [1.2]
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
    +     * // => [{ 'x': 2 }]
    +     */
    +    var differenceBy = baseRest(function (array, values) {
    +      var iteratee = last(values);
    +      if (isArrayLikeObject(iteratee)) {
    +        iteratee = undefined;
    +      }
    +      return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) : [];
    +    });
    +
    +    /**
    +     * This method is like `_.difference` except that it accepts `comparator`
    +     * which is invoked to compare elements of `array` to `values`. The order and
    +     * references of result values are determined by the first array. The comparator
    +     * is invoked with two arguments: (arrVal, othVal).
    +     *
    +     * **Note:** Unlike `_.pullAllWith`, this method returns a new array.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {...Array} [values] The values to exclude.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns the new array of filtered values.
    +     * @example
    +     *
    +     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    +     *
    +     * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
    +     * // => [{ 'x': 2, 'y': 1 }]
    +     */
    +    var differenceWith = baseRest(function (array, values) {
    +      var comparator = last(values);
    +      if (isArrayLikeObject(comparator)) {
    +        comparator = undefined;
    +      }
    +      return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) : [];
    +    });
    +
    +    /**
    +     * Creates a slice of `array` with `n` elements dropped from the beginning.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.5.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @param {number} [n=1] The number of elements to drop.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * _.drop([1, 2, 3]);
    +     * // => [2, 3]
    +     *
    +     * _.drop([1, 2, 3], 2);
    +     * // => [3]
    +     *
    +     * _.drop([1, 2, 3], 5);
    +     * // => []
    +     *
    +     * _.drop([1, 2, 3], 0);
    +     * // => [1, 2, 3]
    +     */
    +    function drop(array, n, guard) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return [];
    +      }
    +      n = guard || n === undefined ? 1 : toInteger(n);
    +      return baseSlice(array, n < 0 ? 0 : n, length);
    +    }
    +
    +    /**
    +     * Creates a slice of `array` with `n` elements dropped from the end.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @param {number} [n=1] The number of elements to drop.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * _.dropRight([1, 2, 3]);
    +     * // => [1, 2]
    +     *
    +     * _.dropRight([1, 2, 3], 2);
    +     * // => [1]
    +     *
    +     * _.dropRight([1, 2, 3], 5);
    +     * // => []
    +     *
    +     * _.dropRight([1, 2, 3], 0);
    +     * // => [1, 2, 3]
    +     */
    +    function dropRight(array, n, guard) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return [];
    +      }
    +      n = guard || n === undefined ? 1 : toInteger(n);
    +      n = length - n;
    +      return baseSlice(array, 0, n < 0 ? 0 : n);
    +    }
    +
    +    /**
    +     * Creates a slice of `array` excluding elements dropped from the end.
    +     * Elements are dropped until `predicate` returns falsey. The predicate is
    +     * invoked with three arguments: (value, index, array).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney',  'active': true },
    +     *   { 'user': 'fred',    'active': false },
    +     *   { 'user': 'pebbles', 'active': false }
    +     * ];
    +     *
    +     * _.dropRightWhile(users, function(o) { return !o.active; });
    +     * // => objects for ['barney']
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });
    +     * // => objects for ['barney', 'fred']
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.dropRightWhile(users, ['active', false]);
    +     * // => objects for ['barney']
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.dropRightWhile(users, 'active');
    +     * // => objects for ['barney', 'fred', 'pebbles']
    +     */
    +    function dropRightWhile(array, predicate) {
    +      return array && array.length ? baseWhile(array, getIteratee(predicate, 3), true, true) : [];
    +    }
    +
    +    /**
    +     * Creates a slice of `array` excluding elements dropped from the beginning.
    +     * Elements are dropped until `predicate` returns falsey. The predicate is
    +     * invoked with three arguments: (value, index, array).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney',  'active': false },
    +     *   { 'user': 'fred',    'active': false },
    +     *   { 'user': 'pebbles', 'active': true }
    +     * ];
    +     *
    +     * _.dropWhile(users, function(o) { return !o.active; });
    +     * // => objects for ['pebbles']
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.dropWhile(users, { 'user': 'barney', 'active': false });
    +     * // => objects for ['fred', 'pebbles']
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.dropWhile(users, ['active', false]);
    +     * // => objects for ['pebbles']
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.dropWhile(users, 'active');
    +     * // => objects for ['barney', 'fred', 'pebbles']
    +     */
    +    function dropWhile(array, predicate) {
    +      return array && array.length ? baseWhile(array, getIteratee(predicate, 3), true) : [];
    +    }
    +
    +    /**
    +     * Fills elements of `array` with `value` from `start` up to, but not
    +     * including, `end`.
    +     *
    +     * **Note:** This method mutates `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.2.0
    +     * @category Array
    +     * @param {Array} array The array to fill.
    +     * @param {*} value The value to fill `array` with.
    +     * @param {number} [start=0] The start position.
    +     * @param {number} [end=array.length] The end position.
    +     * @returns {Array} Returns `array`.
    +     * @example
    +     *
    +     * var array = [1, 2, 3];
    +     *
    +     * _.fill(array, 'a');
    +     * console.log(array);
    +     * // => ['a', 'a', 'a']
    +     *
    +     * _.fill(Array(3), 2);
    +     * // => [2, 2, 2]
    +     *
    +     * _.fill([4, 6, 8, 10], '*', 1, 3);
    +     * // => [4, '*', '*', 10]
    +     */
    +    function fill(array, value, start, end) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return [];
    +      }
    +      if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {
    +        start = 0;
    +        end = length;
    +      }
    +      return baseFill(array, value, start, end);
    +    }
    +
    +    /**
    +     * This method is like `_.find` except that it returns the index of the first
    +     * element `predicate` returns truthy for instead of the element itself.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 1.1.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @param {number} [fromIndex=0] The index to search from.
    +     * @returns {number} Returns the index of the found element, else `-1`.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney',  'active': false },
    +     *   { 'user': 'fred',    'active': false },
    +     *   { 'user': 'pebbles', 'active': true }
    +     * ];
    +     *
    +     * _.findIndex(users, function(o) { return o.user == 'barney'; });
    +     * // => 0
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.findIndex(users, { 'user': 'fred', 'active': false });
    +     * // => 1
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.findIndex(users, ['active', false]);
    +     * // => 0
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.findIndex(users, 'active');
    +     * // => 2
    +     */
    +    function findIndex(array, predicate, fromIndex) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return -1;
    +      }
    +      var index = fromIndex == null ? 0 : toInteger(fromIndex);
    +      if (index < 0) {
    +        index = nativeMax(length + index, 0);
    +      }
    +      return baseFindIndex(array, getIteratee(predicate, 3), index);
    +    }
    +
    +    /**
    +     * This method is like `_.findIndex` except that it iterates over elements
    +     * of `collection` from right to left.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 2.0.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @param {number} [fromIndex=array.length-1] The index to search from.
    +     * @returns {number} Returns the index of the found element, else `-1`.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney',  'active': true },
    +     *   { 'user': 'fred',    'active': false },
    +     *   { 'user': 'pebbles', 'active': false }
    +     * ];
    +     *
    +     * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
    +     * // => 2
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.findLastIndex(users, { 'user': 'barney', 'active': true });
    +     * // => 0
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.findLastIndex(users, ['active', false]);
    +     * // => 2
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.findLastIndex(users, 'active');
    +     * // => 0
    +     */
    +    function findLastIndex(array, predicate, fromIndex) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return -1;
    +      }
    +      var index = length - 1;
    +      if (fromIndex !== undefined) {
    +        index = toInteger(fromIndex);
    +        index = fromIndex < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
    +      }
    +      return baseFindIndex(array, getIteratee(predicate, 3), index, true);
    +    }
    +
    +    /**
    +     * Flattens `array` a single level deep.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to flatten.
    +     * @returns {Array} Returns the new flattened array.
    +     * @example
    +     *
    +     * _.flatten([1, [2, [3, [4]], 5]]);
    +     * // => [1, 2, [3, [4]], 5]
    +     */
    +    function flatten(array) {
    +      var length = array == null ? 0 : array.length;
    +      return length ? baseFlatten(array, 1) : [];
    +    }
    +
    +    /**
    +     * Recursively flattens `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to flatten.
    +     * @returns {Array} Returns the new flattened array.
    +     * @example
    +     *
    +     * _.flattenDeep([1, [2, [3, [4]], 5]]);
    +     * // => [1, 2, 3, 4, 5]
    +     */
    +    function flattenDeep(array) {
    +      var length = array == null ? 0 : array.length;
    +      return length ? baseFlatten(array, INFINITY) : [];
    +    }
    +
    +    /**
    +     * Recursively flatten `array` up to `depth` times.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.4.0
    +     * @category Array
    +     * @param {Array} array The array to flatten.
    +     * @param {number} [depth=1] The maximum recursion depth.
    +     * @returns {Array} Returns the new flattened array.
    +     * @example
    +     *
    +     * var array = [1, [2, [3, [4]], 5]];
    +     *
    +     * _.flattenDepth(array, 1);
    +     * // => [1, 2, [3, [4]], 5]
    +     *
    +     * _.flattenDepth(array, 2);
    +     * // => [1, 2, 3, [4], 5]
    +     */
    +    function flattenDepth(array, depth) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return [];
    +      }
    +      depth = depth === undefined ? 1 : toInteger(depth);
    +      return baseFlatten(array, depth);
    +    }
    +
    +    /**
    +     * The inverse of `_.toPairs`; this method returns an object composed
    +     * from key-value `pairs`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} pairs The key-value pairs.
    +     * @returns {Object} Returns the new object.
    +     * @example
    +     *
    +     * _.fromPairs([['a', 1], ['b', 2]]);
    +     * // => { 'a': 1, 'b': 2 }
    +     */
    +    function fromPairs(pairs) {
    +      var index = -1,
    +        length = pairs == null ? 0 : pairs.length,
    +        result = {};
    +      while (++index < length) {
    +        var pair = pairs[index];
    +        result[pair[0]] = pair[1];
    +      }
    +      return result;
    +    }
    +
    +    /**
    +     * Gets the first element of `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @alias first
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @returns {*} Returns the first element of `array`.
    +     * @example
    +     *
    +     * _.head([1, 2, 3]);
    +     * // => 1
    +     *
    +     * _.head([]);
    +     * // => undefined
    +     */
    +    function head(array) {
    +      return array && array.length ? array[0] : undefined;
    +    }
    +
    +    /**
    +     * Gets the index at which the first occurrence of `value` is found in `array`
    +     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    +     * for equality comparisons. If `fromIndex` is negative, it's used as the
    +     * offset from the end of `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {*} value The value to search for.
    +     * @param {number} [fromIndex=0] The index to search from.
    +     * @returns {number} Returns the index of the matched value, else `-1`.
    +     * @example
    +     *
    +     * _.indexOf([1, 2, 1, 2], 2);
    +     * // => 1
    +     *
    +     * // Search from the `fromIndex`.
    +     * _.indexOf([1, 2, 1, 2], 2, 2);
    +     * // => 3
    +     */
    +    function indexOf(array, value, fromIndex) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return -1;
    +      }
    +      var index = fromIndex == null ? 0 : toInteger(fromIndex);
    +      if (index < 0) {
    +        index = nativeMax(length + index, 0);
    +      }
    +      return baseIndexOf(array, value, index);
    +    }
    +
    +    /**
    +     * Gets all but the last element of `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * _.initial([1, 2, 3]);
    +     * // => [1, 2]
    +     */
    +    function initial(array) {
    +      var length = array == null ? 0 : array.length;
    +      return length ? baseSlice(array, 0, -1) : [];
    +    }
    +
    +    /**
    +     * Creates an array of unique values that are included in all given arrays
    +     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    +     * for equality comparisons. The order and references of result values are
    +     * determined by the first array.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to inspect.
    +     * @returns {Array} Returns the new array of intersecting values.
    +     * @example
    +     *
    +     * _.intersection([2, 1], [2, 3]);
    +     * // => [2]
    +     */
    +    var intersection = baseRest(function (arrays) {
    +      var mapped = arrayMap(arrays, castArrayLikeObject);
    +      return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped) : [];
    +    });
    +
    +    /**
    +     * This method is like `_.intersection` except that it accepts `iteratee`
    +     * which is invoked for each element of each `arrays` to generate the criterion
    +     * by which they're compared. The order and references of result values are
    +     * determined by the first array. The iteratee is invoked with one argument:
    +     * (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to inspect.
    +     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    +     * @returns {Array} Returns the new array of intersecting values.
    +     * @example
    +     *
    +     * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);
    +     * // => [2.1]
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
    +     * // => [{ 'x': 1 }]
    +     */
    +    var intersectionBy = baseRest(function (arrays) {
    +      var iteratee = last(arrays),
    +        mapped = arrayMap(arrays, castArrayLikeObject);
    +      if (iteratee === last(mapped)) {
    +        iteratee = undefined;
    +      } else {
    +        mapped.pop();
    +      }
    +      return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, getIteratee(iteratee, 2)) : [];
    +    });
    +
    +    /**
    +     * This method is like `_.intersection` except that it accepts `comparator`
    +     * which is invoked to compare elements of `arrays`. The order and references
    +     * of result values are determined by the first array. The comparator is
    +     * invoked with two arguments: (arrVal, othVal).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to inspect.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns the new array of intersecting values.
    +     * @example
    +     *
    +     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    +     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
    +     *
    +     * _.intersectionWith(objects, others, _.isEqual);
    +     * // => [{ 'x': 1, 'y': 2 }]
    +     */
    +    var intersectionWith = baseRest(function (arrays) {
    +      var comparator = last(arrays),
    +        mapped = arrayMap(arrays, castArrayLikeObject);
    +      comparator = typeof comparator == 'function' ? comparator : undefined;
    +      if (comparator) {
    +        mapped.pop();
    +      }
    +      return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, undefined, comparator) : [];
    +    });
    +
    +    /**
    +     * Converts all elements in `array` into a string separated by `separator`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to convert.
    +     * @param {string} [separator=','] The element separator.
    +     * @returns {string} Returns the joined string.
    +     * @example
    +     *
    +     * _.join(['a', 'b', 'c'], '~');
    +     * // => 'a~b~c'
    +     */
    +    function join(array, separator) {
    +      return array == null ? '' : nativeJoin.call(array, separator);
    +    }
    +
    +    /**
    +     * Gets the last element of `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @returns {*} Returns the last element of `array`.
    +     * @example
    +     *
    +     * _.last([1, 2, 3]);
    +     * // => 3
    +     */
    +    function last(array) {
    +      var length = array == null ? 0 : array.length;
    +      return length ? array[length - 1] : undefined;
    +    }
    +
    +    /**
    +     * This method is like `_.indexOf` except that it iterates over elements of
    +     * `array` from right to left.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {*} value The value to search for.
    +     * @param {number} [fromIndex=array.length-1] The index to search from.
    +     * @returns {number} Returns the index of the matched value, else `-1`.
    +     * @example
    +     *
    +     * _.lastIndexOf([1, 2, 1, 2], 2);
    +     * // => 3
    +     *
    +     * // Search from the `fromIndex`.
    +     * _.lastIndexOf([1, 2, 1, 2], 2, 2);
    +     * // => 1
    +     */
    +    function lastIndexOf(array, value, fromIndex) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return -1;
    +      }
    +      var index = length;
    +      if (fromIndex !== undefined) {
    +        index = toInteger(fromIndex);
    +        index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
    +      }
    +      return value === value ? strictLastIndexOf(array, value, index) : baseFindIndex(array, baseIsNaN, index, true);
    +    }
    +
    +    /**
    +     * Gets the element at index `n` of `array`. If `n` is negative, the nth
    +     * element from the end is returned.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.11.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @param {number} [n=0] The index of the element to return.
    +     * @returns {*} Returns the nth element of `array`.
    +     * @example
    +     *
    +     * var array = ['a', 'b', 'c', 'd'];
    +     *
    +     * _.nth(array, 1);
    +     * // => 'b'
    +     *
    +     * _.nth(array, -2);
    +     * // => 'c';
    +     */
    +    function nth(array, n) {
    +      return array && array.length ? baseNth(array, toInteger(n)) : undefined;
    +    }
    +
    +    /**
    +     * Removes all given values from `array` using
    +     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    +     * for equality comparisons.
    +     *
    +     * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`
    +     * to remove elements from an array by predicate.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 2.0.0
    +     * @category Array
    +     * @param {Array} array The array to modify.
    +     * @param {...*} [values] The values to remove.
    +     * @returns {Array} Returns `array`.
    +     * @example
    +     *
    +     * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
    +     *
    +     * _.pull(array, 'a', 'c');
    +     * console.log(array);
    +     * // => ['b', 'b']
    +     */
    +    var pull = baseRest(pullAll);
    +
    +    /**
    +     * This method is like `_.pull` except that it accepts an array of values to remove.
    +     *
    +     * **Note:** Unlike `_.difference`, this method mutates `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to modify.
    +     * @param {Array} values The values to remove.
    +     * @returns {Array} Returns `array`.
    +     * @example
    +     *
    +     * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
    +     *
    +     * _.pullAll(array, ['a', 'c']);
    +     * console.log(array);
    +     * // => ['b', 'b']
    +     */
    +    function pullAll(array, values) {
    +      return array && array.length && values && values.length ? basePullAll(array, values) : array;
    +    }
    +
    +    /**
    +     * This method is like `_.pullAll` except that it accepts `iteratee` which is
    +     * invoked for each element of `array` and `values` to generate the criterion
    +     * by which they're compared. The iteratee is invoked with one argument: (value).
    +     *
    +     * **Note:** Unlike `_.differenceBy`, this method mutates `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to modify.
    +     * @param {Array} values The values to remove.
    +     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    +     * @returns {Array} Returns `array`.
    +     * @example
    +     *
    +     * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
    +     *
    +     * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
    +     * console.log(array);
    +     * // => [{ 'x': 2 }]
    +     */
    +    function pullAllBy(array, values, iteratee) {
    +      return array && array.length && values && values.length ? basePullAll(array, values, getIteratee(iteratee, 2)) : array;
    +    }
    +
    +    /**
    +     * This method is like `_.pullAll` except that it accepts `comparator` which
    +     * is invoked to compare elements of `array` to `values`. The comparator is
    +     * invoked with two arguments: (arrVal, othVal).
    +     *
    +     * **Note:** Unlike `_.differenceWith`, this method mutates `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.6.0
    +     * @category Array
    +     * @param {Array} array The array to modify.
    +     * @param {Array} values The values to remove.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns `array`.
    +     * @example
    +     *
    +     * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];
    +     *
    +     * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);
    +     * console.log(array);
    +     * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
    +     */
    +    function pullAllWith(array, values, comparator) {
    +      return array && array.length && values && values.length ? basePullAll(array, values, undefined, comparator) : array;
    +    }
    +
    +    /**
    +     * Removes elements from `array` corresponding to `indexes` and returns an
    +     * array of removed elements.
    +     *
    +     * **Note:** Unlike `_.at`, this method mutates `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to modify.
    +     * @param {...(number|number[])} [indexes] The indexes of elements to remove.
    +     * @returns {Array} Returns the new array of removed elements.
    +     * @example
    +     *
    +     * var array = ['a', 'b', 'c', 'd'];
    +     * var pulled = _.pullAt(array, [1, 3]);
    +     *
    +     * console.log(array);
    +     * // => ['a', 'c']
    +     *
    +     * console.log(pulled);
    +     * // => ['b', 'd']
    +     */
    +    var pullAt = flatRest(function (array, indexes) {
    +      var length = array == null ? 0 : array.length,
    +        result = baseAt(array, indexes);
    +      basePullAt(array, arrayMap(indexes, function (index) {
    +        return isIndex(index, length) ? +index : index;
    +      }).sort(compareAscending));
    +      return result;
    +    });
    +
    +    /**
    +     * Removes all elements from `array` that `predicate` returns truthy for
    +     * and returns an array of the removed elements. The predicate is invoked
    +     * with three arguments: (value, index, array).
    +     *
    +     * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`
    +     * to pull elements from an array by value.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 2.0.0
    +     * @category Array
    +     * @param {Array} array The array to modify.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the new array of removed elements.
    +     * @example
    +     *
    +     * var array = [1, 2, 3, 4];
    +     * var evens = _.remove(array, function(n) {
    +     *   return n % 2 == 0;
    +     * });
    +     *
    +     * console.log(array);
    +     * // => [1, 3]
    +     *
    +     * console.log(evens);
    +     * // => [2, 4]
    +     */
    +    function remove(array, predicate) {
    +      var result = [];
    +      if (!(array && array.length)) {
    +        return result;
    +      }
    +      var index = -1,
    +        indexes = [],
    +        length = array.length;
    +      predicate = getIteratee(predicate, 3);
    +      while (++index < length) {
    +        var value = array[index];
    +        if (predicate(value, index, array)) {
    +          result.push(value);
    +          indexes.push(index);
    +        }
    +      }
    +      basePullAt(array, indexes);
    +      return result;
    +    }
    +
    +    /**
    +     * Reverses `array` so that the first element becomes the last, the second
    +     * element becomes the second to last, and so on.
    +     *
    +     * **Note:** This method mutates `array` and is based on
    +     * [`Array#reverse`](https://mdn.io/Array/reverse).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to modify.
    +     * @returns {Array} Returns `array`.
    +     * @example
    +     *
    +     * var array = [1, 2, 3];
    +     *
    +     * _.reverse(array);
    +     * // => [3, 2, 1]
    +     *
    +     * console.log(array);
    +     * // => [3, 2, 1]
    +     */
    +    function reverse(array) {
    +      return array == null ? array : nativeReverse.call(array);
    +    }
    +
    +    /**
    +     * Creates a slice of `array` from `start` up to, but not including, `end`.
    +     *
    +     * **Note:** This method is used instead of
    +     * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are
    +     * returned.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to slice.
    +     * @param {number} [start=0] The start position.
    +     * @param {number} [end=array.length] The end position.
    +     * @returns {Array} Returns the slice of `array`.
    +     */
    +    function slice(array, start, end) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return [];
    +      }
    +      if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
    +        start = 0;
    +        end = length;
    +      } else {
    +        start = start == null ? 0 : toInteger(start);
    +        end = end === undefined ? length : toInteger(end);
    +      }
    +      return baseSlice(array, start, end);
    +    }
    +
    +    /**
    +     * Uses a binary search to determine the lowest index at which `value`
    +     * should be inserted into `array` in order to maintain its sort order.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The sorted array to inspect.
    +     * @param {*} value The value to evaluate.
    +     * @returns {number} Returns the index at which `value` should be inserted
    +     *  into `array`.
    +     * @example
    +     *
    +     * _.sortedIndex([30, 50], 40);
    +     * // => 1
    +     */
    +    function sortedIndex(array, value) {
    +      return baseSortedIndex(array, value);
    +    }
    +
    +    /**
    +     * This method is like `_.sortedIndex` except that it accepts `iteratee`
    +     * which is invoked for `value` and each element of `array` to compute their
    +     * sort ranking. The iteratee is invoked with one argument: (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The sorted array to inspect.
    +     * @param {*} value The value to evaluate.
    +     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    +     * @returns {number} Returns the index at which `value` should be inserted
    +     *  into `array`.
    +     * @example
    +     *
    +     * var objects = [{ 'x': 4 }, { 'x': 5 }];
    +     *
    +     * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
    +     * // => 0
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.sortedIndexBy(objects, { 'x': 4 }, 'x');
    +     * // => 0
    +     */
    +    function sortedIndexBy(array, value, iteratee) {
    +      return baseSortedIndexBy(array, value, getIteratee(iteratee, 2));
    +    }
    +
    +    /**
    +     * This method is like `_.indexOf` except that it performs a binary
    +     * search on a sorted `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {*} value The value to search for.
    +     * @returns {number} Returns the index of the matched value, else `-1`.
    +     * @example
    +     *
    +     * _.sortedIndexOf([4, 5, 5, 5, 6], 5);
    +     * // => 1
    +     */
    +    function sortedIndexOf(array, value) {
    +      var length = array == null ? 0 : array.length;
    +      if (length) {
    +        var index = baseSortedIndex(array, value);
    +        if (index < length && eq(array[index], value)) {
    +          return index;
    +        }
    +      }
    +      return -1;
    +    }
    +
    +    /**
    +     * This method is like `_.sortedIndex` except that it returns the highest
    +     * index at which `value` should be inserted into `array` in order to
    +     * maintain its sort order.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The sorted array to inspect.
    +     * @param {*} value The value to evaluate.
    +     * @returns {number} Returns the index at which `value` should be inserted
    +     *  into `array`.
    +     * @example
    +     *
    +     * _.sortedLastIndex([4, 5, 5, 5, 6], 5);
    +     * // => 4
    +     */
    +    function sortedLastIndex(array, value) {
    +      return baseSortedIndex(array, value, true);
    +    }
    +
    +    /**
    +     * This method is like `_.sortedLastIndex` except that it accepts `iteratee`
    +     * which is invoked for `value` and each element of `array` to compute their
    +     * sort ranking. The iteratee is invoked with one argument: (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The sorted array to inspect.
    +     * @param {*} value The value to evaluate.
    +     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    +     * @returns {number} Returns the index at which `value` should be inserted
    +     *  into `array`.
    +     * @example
    +     *
    +     * var objects = [{ 'x': 4 }, { 'x': 5 }];
    +     *
    +     * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
    +     * // => 1
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');
    +     * // => 1
    +     */
    +    function sortedLastIndexBy(array, value, iteratee) {
    +      return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true);
    +    }
    +
    +    /**
    +     * This method is like `_.lastIndexOf` except that it performs a binary
    +     * search on a sorted `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {*} value The value to search for.
    +     * @returns {number} Returns the index of the matched value, else `-1`.
    +     * @example
    +     *
    +     * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);
    +     * // => 3
    +     */
    +    function sortedLastIndexOf(array, value) {
    +      var length = array == null ? 0 : array.length;
    +      if (length) {
    +        var index = baseSortedIndex(array, value, true) - 1;
    +        if (eq(array[index], value)) {
    +          return index;
    +        }
    +      }
    +      return -1;
    +    }
    +
    +    /**
    +     * This method is like `_.uniq` except that it's designed and optimized
    +     * for sorted arrays.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @returns {Array} Returns the new duplicate free array.
    +     * @example
    +     *
    +     * _.sortedUniq([1, 1, 2]);
    +     * // => [1, 2]
    +     */
    +    function sortedUniq(array) {
    +      return array && array.length ? baseSortedUniq(array) : [];
    +    }
    +
    +    /**
    +     * This method is like `_.uniqBy` except that it's designed and optimized
    +     * for sorted arrays.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {Function} [iteratee] The iteratee invoked per element.
    +     * @returns {Array} Returns the new duplicate free array.
    +     * @example
    +     *
    +     * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
    +     * // => [1.1, 2.3]
    +     */
    +    function sortedUniqBy(array, iteratee) {
    +      return array && array.length ? baseSortedUniq(array, getIteratee(iteratee, 2)) : [];
    +    }
    +
    +    /**
    +     * Gets all but the first element of `array`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * _.tail([1, 2, 3]);
    +     * // => [2, 3]
    +     */
    +    function tail(array) {
    +      var length = array == null ? 0 : array.length;
    +      return length ? baseSlice(array, 1, length) : [];
    +    }
    +
    +    /**
    +     * Creates a slice of `array` with `n` elements taken from the beginning.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @param {number} [n=1] The number of elements to take.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * _.take([1, 2, 3]);
    +     * // => [1]
    +     *
    +     * _.take([1, 2, 3], 2);
    +     * // => [1, 2]
    +     *
    +     * _.take([1, 2, 3], 5);
    +     * // => [1, 2, 3]
    +     *
    +     * _.take([1, 2, 3], 0);
    +     * // => []
    +     */
    +    function take(array, n, guard) {
    +      if (!(array && array.length)) {
    +        return [];
    +      }
    +      n = guard || n === undefined ? 1 : toInteger(n);
    +      return baseSlice(array, 0, n < 0 ? 0 : n);
    +    }
    +
    +    /**
    +     * Creates a slice of `array` with `n` elements taken from the end.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @param {number} [n=1] The number of elements to take.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * _.takeRight([1, 2, 3]);
    +     * // => [3]
    +     *
    +     * _.takeRight([1, 2, 3], 2);
    +     * // => [2, 3]
    +     *
    +     * _.takeRight([1, 2, 3], 5);
    +     * // => [1, 2, 3]
    +     *
    +     * _.takeRight([1, 2, 3], 0);
    +     * // => []
    +     */
    +    function takeRight(array, n, guard) {
    +      var length = array == null ? 0 : array.length;
    +      if (!length) {
    +        return [];
    +      }
    +      n = guard || n === undefined ? 1 : toInteger(n);
    +      n = length - n;
    +      return baseSlice(array, n < 0 ? 0 : n, length);
    +    }
    +
    +    /**
    +     * Creates a slice of `array` with elements taken from the end. Elements are
    +     * taken until `predicate` returns falsey. The predicate is invoked with
    +     * three arguments: (value, index, array).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney',  'active': true },
    +     *   { 'user': 'fred',    'active': false },
    +     *   { 'user': 'pebbles', 'active': false }
    +     * ];
    +     *
    +     * _.takeRightWhile(users, function(o) { return !o.active; });
    +     * // => objects for ['fred', 'pebbles']
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });
    +     * // => objects for ['pebbles']
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.takeRightWhile(users, ['active', false]);
    +     * // => objects for ['fred', 'pebbles']
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.takeRightWhile(users, 'active');
    +     * // => []
    +     */
    +    function takeRightWhile(array, predicate) {
    +      return array && array.length ? baseWhile(array, getIteratee(predicate, 3), false, true) : [];
    +    }
    +
    +    /**
    +     * Creates a slice of `array` with elements taken from the beginning. Elements
    +     * are taken until `predicate` returns falsey. The predicate is invoked with
    +     * three arguments: (value, index, array).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Array
    +     * @param {Array} array The array to query.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the slice of `array`.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney',  'active': false },
    +     *   { 'user': 'fred',    'active': false },
    +     *   { 'user': 'pebbles', 'active': true }
    +     * ];
    +     *
    +     * _.takeWhile(users, function(o) { return !o.active; });
    +     * // => objects for ['barney', 'fred']
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.takeWhile(users, { 'user': 'barney', 'active': false });
    +     * // => objects for ['barney']
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.takeWhile(users, ['active', false]);
    +     * // => objects for ['barney', 'fred']
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.takeWhile(users, 'active');
    +     * // => []
    +     */
    +    function takeWhile(array, predicate) {
    +      return array && array.length ? baseWhile(array, getIteratee(predicate, 3)) : [];
    +    }
    +
    +    /**
    +     * Creates an array of unique values, in order, from all given arrays using
    +     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    +     * for equality comparisons.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to inspect.
    +     * @returns {Array} Returns the new array of combined values.
    +     * @example
    +     *
    +     * _.union([2], [1, 2]);
    +     * // => [2, 1]
    +     */
    +    var union = baseRest(function (arrays) {
    +      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
    +    });
    +
    +    /**
    +     * This method is like `_.union` except that it accepts `iteratee` which is
    +     * invoked for each element of each `arrays` to generate the criterion by
    +     * which uniqueness is computed. Result values are chosen from the first
    +     * array in which the value occurs. The iteratee is invoked with one argument:
    +     * (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to inspect.
    +     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    +     * @returns {Array} Returns the new array of combined values.
    +     * @example
    +     *
    +     * _.unionBy([2.1], [1.2, 2.3], Math.floor);
    +     * // => [2.1, 1.2]
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
    +     * // => [{ 'x': 1 }, { 'x': 2 }]
    +     */
    +    var unionBy = baseRest(function (arrays) {
    +      var iteratee = last(arrays);
    +      if (isArrayLikeObject(iteratee)) {
    +        iteratee = undefined;
    +      }
    +      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2));
    +    });
    +
    +    /**
    +     * This method is like `_.union` except that it accepts `comparator` which
    +     * is invoked to compare elements of `arrays`. Result values are chosen from
    +     * the first array in which the value occurs. The comparator is invoked
    +     * with two arguments: (arrVal, othVal).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to inspect.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns the new array of combined values.
    +     * @example
    +     *
    +     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    +     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
    +     *
    +     * _.unionWith(objects, others, _.isEqual);
    +     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
    +     */
    +    var unionWith = baseRest(function (arrays) {
    +      var comparator = last(arrays);
    +      comparator = typeof comparator == 'function' ? comparator : undefined;
    +      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);
    +    });
    +
    +    /**
    +     * Creates a duplicate-free version of an array, using
    +     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    +     * for equality comparisons, in which only the first occurrence of each element
    +     * is kept. The order of result values is determined by the order they occur
    +     * in the array.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @returns {Array} Returns the new duplicate free array.
    +     * @example
    +     *
    +     * _.uniq([2, 1, 2]);
    +     * // => [2, 1]
    +     */
    +    function uniq(array) {
    +      return array && array.length ? baseUniq(array) : [];
    +    }
    +
    +    /**
    +     * This method is like `_.uniq` except that it accepts `iteratee` which is
    +     * invoked for each element in `array` to generate the criterion by which
    +     * uniqueness is computed. The order of result values is determined by the
    +     * order they occur in the array. The iteratee is invoked with one argument:
    +     * (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    +     * @returns {Array} Returns the new duplicate free array.
    +     * @example
    +     *
    +     * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
    +     * // => [2.1, 1.2]
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
    +     * // => [{ 'x': 1 }, { 'x': 2 }]
    +     */
    +    function uniqBy(array, iteratee) {
    +      return array && array.length ? baseUniq(array, getIteratee(iteratee, 2)) : [];
    +    }
    +
    +    /**
    +     * This method is like `_.uniq` except that it accepts `comparator` which
    +     * is invoked to compare elements of `array`. The order of result values is
    +     * determined by the order they occur in the array.The comparator is invoked
    +     * with two arguments: (arrVal, othVal).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns the new duplicate free array.
    +     * @example
    +     *
    +     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
    +     *
    +     * _.uniqWith(objects, _.isEqual);
    +     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
    +     */
    +    function uniqWith(array, comparator) {
    +      comparator = typeof comparator == 'function' ? comparator : undefined;
    +      return array && array.length ? baseUniq(array, undefined, comparator) : [];
    +    }
    +
    +    /**
    +     * This method is like `_.zip` except that it accepts an array of grouped
    +     * elements and creates an array regrouping the elements to their pre-zip
    +     * configuration.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 1.2.0
    +     * @category Array
    +     * @param {Array} array The array of grouped elements to process.
    +     * @returns {Array} Returns the new array of regrouped elements.
    +     * @example
    +     *
    +     * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);
    +     * // => [['a', 1, true], ['b', 2, false]]
    +     *
    +     * _.unzip(zipped);
    +     * // => [['a', 'b'], [1, 2], [true, false]]
    +     */
    +    function unzip(array) {
    +      if (!(array && array.length)) {
    +        return [];
    +      }
    +      var length = 0;
    +      array = arrayFilter(array, function (group) {
    +        if (isArrayLikeObject(group)) {
    +          length = nativeMax(group.length, length);
    +          return true;
    +        }
    +      });
    +      return baseTimes(length, function (index) {
    +        return arrayMap(array, baseProperty(index));
    +      });
    +    }
    +
    +    /**
    +     * This method is like `_.unzip` except that it accepts `iteratee` to specify
    +     * how regrouped values should be combined. The iteratee is invoked with the
    +     * elements of each group: (...group).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.8.0
    +     * @category Array
    +     * @param {Array} array The array of grouped elements to process.
    +     * @param {Function} [iteratee=_.identity] The function to combine
    +     *  regrouped values.
    +     * @returns {Array} Returns the new array of regrouped elements.
    +     * @example
    +     *
    +     * var zipped = _.zip([1, 2], [10, 20], [100, 200]);
    +     * // => [[1, 10, 100], [2, 20, 200]]
    +     *
    +     * _.unzipWith(zipped, _.add);
    +     * // => [3, 30, 300]
    +     */
    +    function unzipWith(array, iteratee) {
    +      if (!(array && array.length)) {
    +        return [];
    +      }
    +      var result = unzip(array);
    +      if (iteratee == null) {
    +        return result;
    +      }
    +      return arrayMap(result, function (group) {
    +        return apply(iteratee, undefined, group);
    +      });
    +    }
    +
    +    /**
    +     * Creates an array excluding all given values using
    +     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    +     * for equality comparisons.
    +     *
    +     * **Note:** Unlike `_.pull`, this method returns a new array.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {Array} array The array to inspect.
    +     * @param {...*} [values] The values to exclude.
    +     * @returns {Array} Returns the new array of filtered values.
    +     * @see _.difference, _.xor
    +     * @example
    +     *
    +     * _.without([2, 1, 2, 3], 1, 2);
    +     * // => [3]
    +     */
    +    var without = baseRest(function (array, values) {
    +      return isArrayLikeObject(array) ? baseDifference(array, values) : [];
    +    });
    +
    +    /**
    +     * Creates an array of unique values that is the
    +     * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)
    +     * of the given arrays. The order of result values is determined by the order
    +     * they occur in the arrays.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 2.4.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to inspect.
    +     * @returns {Array} Returns the new array of filtered values.
    +     * @see _.difference, _.without
    +     * @example
    +     *
    +     * _.xor([2, 1], [2, 3]);
    +     * // => [1, 3]
    +     */
    +    var xor = baseRest(function (arrays) {
    +      return baseXor(arrayFilter(arrays, isArrayLikeObject));
    +    });
    +
    +    /**
    +     * This method is like `_.xor` except that it accepts `iteratee` which is
    +     * invoked for each element of each `arrays` to generate the criterion by
    +     * which by which they're compared. The order of result values is determined
    +     * by the order they occur in the arrays. The iteratee is invoked with one
    +     * argument: (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to inspect.
    +     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    +     * @returns {Array} Returns the new array of filtered values.
    +     * @example
    +     *
    +     * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
    +     * // => [1.2, 3.4]
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
    +     * // => [{ 'x': 2 }]
    +     */
    +    var xorBy = baseRest(function (arrays) {
    +      var iteratee = last(arrays);
    +      if (isArrayLikeObject(iteratee)) {
    +        iteratee = undefined;
    +      }
    +      return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2));
    +    });
    +
    +    /**
    +     * This method is like `_.xor` except that it accepts `comparator` which is
    +     * invoked to compare elements of `arrays`. The order of result values is
    +     * determined by the order they occur in the arrays. The comparator is invoked
    +     * with two arguments: (arrVal, othVal).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to inspect.
    +     * @param {Function} [comparator] The comparator invoked per element.
    +     * @returns {Array} Returns the new array of filtered values.
    +     * @example
    +     *
    +     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    +     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
    +     *
    +     * _.xorWith(objects, others, _.isEqual);
    +     * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
    +     */
    +    var xorWith = baseRest(function (arrays) {
    +      var comparator = last(arrays);
    +      comparator = typeof comparator == 'function' ? comparator : undefined;
    +      return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);
    +    });
    +
    +    /**
    +     * Creates an array of grouped elements, the first of which contains the
    +     * first elements of the given arrays, the second of which contains the
    +     * second elements of the given arrays, and so on.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to process.
    +     * @returns {Array} Returns the new array of grouped elements.
    +     * @example
    +     *
    +     * _.zip(['a', 'b'], [1, 2], [true, false]);
    +     * // => [['a', 1, true], ['b', 2, false]]
    +     */
    +    var zip = baseRest(unzip);
    +
    +    /**
    +     * This method is like `_.fromPairs` except that it accepts two arrays,
    +     * one of property identifiers and one of corresponding values.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.4.0
    +     * @category Array
    +     * @param {Array} [props=[]] The property identifiers.
    +     * @param {Array} [values=[]] The property values.
    +     * @returns {Object} Returns the new object.
    +     * @example
    +     *
    +     * _.zipObject(['a', 'b'], [1, 2]);
    +     * // => { 'a': 1, 'b': 2 }
    +     */
    +    function zipObject(props, values) {
    +      return baseZipObject(props || [], values || [], assignValue);
    +    }
    +
    +    /**
    +     * This method is like `_.zipObject` except that it supports property paths.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.1.0
    +     * @category Array
    +     * @param {Array} [props=[]] The property identifiers.
    +     * @param {Array} [values=[]] The property values.
    +     * @returns {Object} Returns the new object.
    +     * @example
    +     *
    +     * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
    +     * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
    +     */
    +    function zipObjectDeep(props, values) {
    +      return baseZipObject(props || [], values || [], baseSet);
    +    }
    +
    +    /**
    +     * This method is like `_.zip` except that it accepts `iteratee` to specify
    +     * how grouped values should be combined. The iteratee is invoked with the
    +     * elements of each group: (...group).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.8.0
    +     * @category Array
    +     * @param {...Array} [arrays] The arrays to process.
    +     * @param {Function} [iteratee=_.identity] The function to combine
    +     *  grouped values.
    +     * @returns {Array} Returns the new array of grouped elements.
    +     * @example
    +     *
    +     * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {
    +     *   return a + b + c;
    +     * });
    +     * // => [111, 222]
    +     */
    +    var zipWith = baseRest(function (arrays) {
    +      var length = arrays.length,
    +        iteratee = length > 1 ? arrays[length - 1] : undefined;
    +      iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;
    +      return unzipWith(arrays, iteratee);
    +    });
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates a `lodash` wrapper instance that wraps `value` with explicit method
    +     * chain sequences enabled. The result of such sequences must be unwrapped
    +     * with `_#value`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 1.3.0
    +     * @category Seq
    +     * @param {*} value The value to wrap.
    +     * @returns {Object} Returns the new `lodash` wrapper instance.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney',  'age': 36 },
    +     *   { 'user': 'fred',    'age': 40 },
    +     *   { 'user': 'pebbles', 'age': 1 }
    +     * ];
    +     *
    +     * var youngest = _
    +     *   .chain(users)
    +     *   .sortBy('age')
    +     *   .map(function(o) {
    +     *     return o.user + ' is ' + o.age;
    +     *   })
    +     *   .head()
    +     *   .value();
    +     * // => 'pebbles is 1'
    +     */
    +    function chain(value) {
    +      var result = lodash(value);
    +      result.__chain__ = true;
    +      return result;
    +    }
    +
    +    /**
    +     * This method invokes `interceptor` and returns `value`. The interceptor
    +     * is invoked with one argument; (value). The purpose of this method is to
    +     * "tap into" a method chain sequence in order to modify intermediate results.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Seq
    +     * @param {*} value The value to provide to `interceptor`.
    +     * @param {Function} interceptor The function to invoke.
    +     * @returns {*} Returns `value`.
    +     * @example
    +     *
    +     * _([1, 2, 3])
    +     *  .tap(function(array) {
    +     *    // Mutate input array.
    +     *    array.pop();
    +     *  })
    +     *  .reverse()
    +     *  .value();
    +     * // => [2, 1]
    +     */
    +    function tap(value, interceptor) {
    +      interceptor(value);
    +      return value;
    +    }
    +
    +    /**
    +     * This method is like `_.tap` except that it returns the result of `interceptor`.
    +     * The purpose of this method is to "pass thru" values replacing intermediate
    +     * results in a method chain sequence.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Seq
    +     * @param {*} value The value to provide to `interceptor`.
    +     * @param {Function} interceptor The function to invoke.
    +     * @returns {*} Returns the result of `interceptor`.
    +     * @example
    +     *
    +     * _('  abc  ')
    +     *  .chain()
    +     *  .trim()
    +     *  .thru(function(value) {
    +     *    return [value];
    +     *  })
    +     *  .value();
    +     * // => ['abc']
    +     */
    +    function thru(value, interceptor) {
    +      return interceptor(value);
    +    }
    +
    +    /**
    +     * This method is the wrapper version of `_.at`.
    +     *
    +     * @name at
    +     * @memberOf _
    +     * @since 1.0.0
    +     * @category Seq
    +     * @param {...(string|string[])} [paths] The property paths to pick.
    +     * @returns {Object} Returns the new `lodash` wrapper instance.
    +     * @example
    +     *
    +     * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
    +     *
    +     * _(object).at(['a[0].b.c', 'a[1]']).value();
    +     * // => [3, 4]
    +     */
    +    var wrapperAt = flatRest(function (paths) {
    +      var length = paths.length,
    +        start = length ? paths[0] : 0,
    +        value = this.__wrapped__,
    +        interceptor = function interceptor(object) {
    +          return baseAt(object, paths);
    +        };
    +      if (length > 1 || this.__actions__.length || !(value instanceof LazyWrapper) || !isIndex(start)) {
    +        return this.thru(interceptor);
    +      }
    +      value = value.slice(start, +start + (length ? 1 : 0));
    +      value.__actions__.push({
    +        'func': thru,
    +        'args': [interceptor],
    +        'thisArg': undefined
    +      });
    +      return new LodashWrapper(value, this.__chain__).thru(function (array) {
    +        if (length && !array.length) {
    +          array.push(undefined);
    +        }
    +        return array;
    +      });
    +    });
    +
    +    /**
    +     * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
    +     *
    +     * @name chain
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Seq
    +     * @returns {Object} Returns the new `lodash` wrapper instance.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney', 'age': 36 },
    +     *   { 'user': 'fred',   'age': 40 }
    +     * ];
    +     *
    +     * // A sequence without explicit chaining.
    +     * _(users).head();
    +     * // => { 'user': 'barney', 'age': 36 }
    +     *
    +     * // A sequence with explicit chaining.
    +     * _(users)
    +     *   .chain()
    +     *   .head()
    +     *   .pick('user')
    +     *   .value();
    +     * // => { 'user': 'barney' }
    +     */
    +    function wrapperChain() {
    +      return chain(this);
    +    }
    +
    +    /**
    +     * Executes the chain sequence and returns the wrapped result.
    +     *
    +     * @name commit
    +     * @memberOf _
    +     * @since 3.2.0
    +     * @category Seq
    +     * @returns {Object} Returns the new `lodash` wrapper instance.
    +     * @example
    +     *
    +     * var array = [1, 2];
    +     * var wrapped = _(array).push(3);
    +     *
    +     * console.log(array);
    +     * // => [1, 2]
    +     *
    +     * wrapped = wrapped.commit();
    +     * console.log(array);
    +     * // => [1, 2, 3]
    +     *
    +     * wrapped.last();
    +     * // => 3
    +     *
    +     * console.log(array);
    +     * // => [1, 2, 3]
    +     */
    +    function wrapperCommit() {
    +      return new LodashWrapper(this.value(), this.__chain__);
    +    }
    +
    +    /**
    +     * Gets the next value on a wrapped object following the
    +     * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
    +     *
    +     * @name next
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Seq
    +     * @returns {Object} Returns the next iterator value.
    +     * @example
    +     *
    +     * var wrapped = _([1, 2]);
    +     *
    +     * wrapped.next();
    +     * // => { 'done': false, 'value': 1 }
    +     *
    +     * wrapped.next();
    +     * // => { 'done': false, 'value': 2 }
    +     *
    +     * wrapped.next();
    +     * // => { 'done': true, 'value': undefined }
    +     */
    +    function wrapperNext() {
    +      if (this.__values__ === undefined) {
    +        this.__values__ = toArray(this.value());
    +      }
    +      var done = this.__index__ >= this.__values__.length,
    +        value = done ? undefined : this.__values__[this.__index__++];
    +      return {
    +        'done': done,
    +        'value': value
    +      };
    +    }
    +
    +    /**
    +     * Enables the wrapper to be iterable.
    +     *
    +     * @name Symbol.iterator
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Seq
    +     * @returns {Object} Returns the wrapper object.
    +     * @example
    +     *
    +     * var wrapped = _([1, 2]);
    +     *
    +     * wrapped[Symbol.iterator]() === wrapped;
    +     * // => true
    +     *
    +     * Array.from(wrapped);
    +     * // => [1, 2]
    +     */
    +    function wrapperToIterator() {
    +      return this;
    +    }
    +
    +    /**
    +     * Creates a clone of the chain sequence planting `value` as the wrapped value.
    +     *
    +     * @name plant
    +     * @memberOf _
    +     * @since 3.2.0
    +     * @category Seq
    +     * @param {*} value The value to plant.
    +     * @returns {Object} Returns the new `lodash` wrapper instance.
    +     * @example
    +     *
    +     * function square(n) {
    +     *   return n * n;
    +     * }
    +     *
    +     * var wrapped = _([1, 2]).map(square);
    +     * var other = wrapped.plant([3, 4]);
    +     *
    +     * other.value();
    +     * // => [9, 16]
    +     *
    +     * wrapped.value();
    +     * // => [1, 4]
    +     */
    +    function wrapperPlant(value) {
    +      var result,
    +        parent = this;
    +      while (parent instanceof baseLodash) {
    +        var clone = wrapperClone(parent);
    +        clone.__index__ = 0;
    +        clone.__values__ = undefined;
    +        if (result) {
    +          previous.__wrapped__ = clone;
    +        } else {
    +          result = clone;
    +        }
    +        var previous = clone;
    +        parent = parent.__wrapped__;
    +      }
    +      previous.__wrapped__ = value;
    +      return result;
    +    }
    +
    +    /**
    +     * This method is the wrapper version of `_.reverse`.
    +     *
    +     * **Note:** This method mutates the wrapped array.
    +     *
    +     * @name reverse
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Seq
    +     * @returns {Object} Returns the new `lodash` wrapper instance.
    +     * @example
    +     *
    +     * var array = [1, 2, 3];
    +     *
    +     * _(array).reverse().value()
    +     * // => [3, 2, 1]
    +     *
    +     * console.log(array);
    +     * // => [3, 2, 1]
    +     */
    +    function wrapperReverse() {
    +      var value = this.__wrapped__;
    +      if (value instanceof LazyWrapper) {
    +        var wrapped = value;
    +        if (this.__actions__.length) {
    +          wrapped = new LazyWrapper(this);
    +        }
    +        wrapped = wrapped.reverse();
    +        wrapped.__actions__.push({
    +          'func': thru,
    +          'args': [reverse],
    +          'thisArg': undefined
    +        });
    +        return new LodashWrapper(wrapped, this.__chain__);
    +      }
    +      return this.thru(reverse);
    +    }
    +
    +    /**
    +     * Executes the chain sequence to resolve the unwrapped value.
    +     *
    +     * @name value
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @alias toJSON, valueOf
    +     * @category Seq
    +     * @returns {*} Returns the resolved unwrapped value.
    +     * @example
    +     *
    +     * _([1, 2, 3]).value();
    +     * // => [1, 2, 3]
    +     */
    +    function wrapperValue() {
    +      return baseWrapperValue(this.__wrapped__, this.__actions__);
    +    }
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Creates an object composed of keys generated from the results of running
    +     * each element of `collection` thru `iteratee`. The corresponding value of
    +     * each key is the number of times the key was returned by `iteratee`. The
    +     * iteratee is invoked with one argument: (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.5.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
    +     * @returns {Object} Returns the composed aggregate object.
    +     * @example
    +     *
    +     * _.countBy([6.1, 4.2, 6.3], Math.floor);
    +     * // => { '4': 1, '6': 2 }
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.countBy(['one', 'two', 'three'], 'length');
    +     * // => { '3': 2, '5': 1 }
    +     */
    +    var countBy = createAggregator(function (result, value, key) {
    +      if (hasOwnProperty.call(result, key)) {
    +        ++result[key];
    +      } else {
    +        baseAssignValue(result, key, 1);
    +      }
    +    });
    +
    +    /**
    +     * Checks if `predicate` returns truthy for **all** elements of `collection`.
    +     * Iteration is stopped once `predicate` returns falsey. The predicate is
    +     * invoked with three arguments: (value, index|key, collection).
    +     *
    +     * **Note:** This method returns `true` for
    +     * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because
    +     * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of
    +     * elements of empty collections.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {boolean} Returns `true` if all elements pass the predicate check,
    +     *  else `false`.
    +     * @example
    +     *
    +     * _.every([true, 1, null, 'yes'], Boolean);
    +     * // => false
    +     *
    +     * var users = [
    +     *   { 'user': 'barney', 'age': 36, 'active': false },
    +     *   { 'user': 'fred',   'age': 40, 'active': false }
    +     * ];
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.every(users, { 'user': 'barney', 'active': false });
    +     * // => false
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.every(users, ['active', false]);
    +     * // => true
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.every(users, 'active');
    +     * // => false
    +     */
    +    function every(collection, predicate, guard) {
    +      var func = isArray(collection) ? arrayEvery : baseEvery;
    +      if (guard && isIterateeCall(collection, predicate, guard)) {
    +        predicate = undefined;
    +      }
    +      return func(collection, getIteratee(predicate, 3));
    +    }
    +
    +    /**
    +     * Iterates over elements of `collection`, returning an array of all elements
    +     * `predicate` returns truthy for. The predicate is invoked with three
    +     * arguments: (value, index|key, collection).
    +     *
    +     * **Note:** Unlike `_.remove`, this method returns a new array.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the new filtered array.
    +     * @see _.reject
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney', 'age': 36, 'active': true },
    +     *   { 'user': 'fred',   'age': 40, 'active': false }
    +     * ];
    +     *
    +     * _.filter(users, function(o) { return !o.active; });
    +     * // => objects for ['fred']
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.filter(users, { 'age': 36, 'active': true });
    +     * // => objects for ['barney']
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.filter(users, ['active', false]);
    +     * // => objects for ['fred']
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.filter(users, 'active');
    +     * // => objects for ['barney']
    +     *
    +     * // Combining several predicates using `_.overEvery` or `_.overSome`.
    +     * _.filter(users, _.overSome([{ 'age': 36 }, ['age', 40]]));
    +     * // => objects for ['fred', 'barney']
    +     */
    +    function filter(collection, predicate) {
    +      var func = isArray(collection) ? arrayFilter : baseFilter;
    +      return func(collection, getIteratee(predicate, 3));
    +    }
    +
    +    /**
    +     * Iterates over elements of `collection`, returning the first element
    +     * `predicate` returns truthy for. The predicate is invoked with three
    +     * arguments: (value, index|key, collection).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to inspect.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @param {number} [fromIndex=0] The index to search from.
    +     * @returns {*} Returns the matched element, else `undefined`.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney',  'age': 36, 'active': true },
    +     *   { 'user': 'fred',    'age': 40, 'active': false },
    +     *   { 'user': 'pebbles', 'age': 1,  'active': true }
    +     * ];
    +     *
    +     * _.find(users, function(o) { return o.age < 40; });
    +     * // => object for 'barney'
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.find(users, { 'age': 1, 'active': true });
    +     * // => object for 'pebbles'
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.find(users, ['active', false]);
    +     * // => object for 'fred'
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.find(users, 'active');
    +     * // => object for 'barney'
    +     */
    +    var find = createFind(findIndex);
    +
    +    /**
    +     * This method is like `_.find` except that it iterates over elements of
    +     * `collection` from right to left.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 2.0.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to inspect.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @param {number} [fromIndex=collection.length-1] The index to search from.
    +     * @returns {*} Returns the matched element, else `undefined`.
    +     * @example
    +     *
    +     * _.findLast([1, 2, 3, 4], function(n) {
    +     *   return n % 2 == 1;
    +     * });
    +     * // => 3
    +     */
    +    var findLast = createFind(findLastIndex);
    +
    +    /**
    +     * Creates a flattened array of values by running each element in `collection`
    +     * thru `iteratee` and flattening the mapped results. The iteratee is invoked
    +     * with three arguments: (value, index|key, collection).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the new flattened array.
    +     * @example
    +     *
    +     * function duplicate(n) {
    +     *   return [n, n];
    +     * }
    +     *
    +     * _.flatMap([1, 2], duplicate);
    +     * // => [1, 1, 2, 2]
    +     */
    +    function flatMap(collection, iteratee) {
    +      return baseFlatten(map(collection, iteratee), 1);
    +    }
    +
    +    /**
    +     * This method is like `_.flatMap` except that it recursively flattens the
    +     * mapped results.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.7.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the new flattened array.
    +     * @example
    +     *
    +     * function duplicate(n) {
    +     *   return [[[n, n]]];
    +     * }
    +     *
    +     * _.flatMapDeep([1, 2], duplicate);
    +     * // => [1, 1, 2, 2]
    +     */
    +    function flatMapDeep(collection, iteratee) {
    +      return baseFlatten(map(collection, iteratee), INFINITY);
    +    }
    +
    +    /**
    +     * This method is like `_.flatMap` except that it recursively flattens the
    +     * mapped results up to `depth` times.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.7.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    +     * @param {number} [depth=1] The maximum recursion depth.
    +     * @returns {Array} Returns the new flattened array.
    +     * @example
    +     *
    +     * function duplicate(n) {
    +     *   return [[[n, n]]];
    +     * }
    +     *
    +     * _.flatMapDepth([1, 2], duplicate, 2);
    +     * // => [[1, 1], [2, 2]]
    +     */
    +    function flatMapDepth(collection, iteratee, depth) {
    +      depth = depth === undefined ? 1 : toInteger(depth);
    +      return baseFlatten(map(collection, iteratee), depth);
    +    }
    +
    +    /**
    +     * Iterates over elements of `collection` and invokes `iteratee` for each element.
    +     * The iteratee is invoked with three arguments: (value, index|key, collection).
    +     * Iteratee functions may exit iteration early by explicitly returning `false`.
    +     *
    +     * **Note:** As with other "Collections" methods, objects with a "length"
    +     * property are iterated like arrays. To avoid this behavior use `_.forIn`
    +     * or `_.forOwn` for object iteration.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @alias each
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    +     * @returns {Array|Object} Returns `collection`.
    +     * @see _.forEachRight
    +     * @example
    +     *
    +     * _.forEach([1, 2], function(value) {
    +     *   console.log(value);
    +     * });
    +     * // => Logs `1` then `2`.
    +     *
    +     * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
    +     *   console.log(key);
    +     * });
    +     * // => Logs 'a' then 'b' (iteration order is not guaranteed).
    +     */
    +    function forEach(collection, iteratee) {
    +      var func = isArray(collection) ? arrayEach : baseEach;
    +      return func(collection, getIteratee(iteratee, 3));
    +    }
    +
    +    /**
    +     * This method is like `_.forEach` except that it iterates over elements of
    +     * `collection` from right to left.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 2.0.0
    +     * @alias eachRight
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    +     * @returns {Array|Object} Returns `collection`.
    +     * @see _.forEach
    +     * @example
    +     *
    +     * _.forEachRight([1, 2], function(value) {
    +     *   console.log(value);
    +     * });
    +     * // => Logs `2` then `1`.
    +     */
    +    function forEachRight(collection, iteratee) {
    +      var func = isArray(collection) ? arrayEachRight : baseEachRight;
    +      return func(collection, getIteratee(iteratee, 3));
    +    }
    +
    +    /**
    +     * Creates an object composed of keys generated from the results of running
    +     * each element of `collection` thru `iteratee`. The order of grouped values
    +     * is determined by the order they occur in `collection`. The corresponding
    +     * value of each key is an array of elements responsible for generating the
    +     * key. The iteratee is invoked with one argument: (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
    +     * @returns {Object} Returns the composed aggregate object.
    +     * @example
    +     *
    +     * _.groupBy([6.1, 4.2, 6.3], Math.floor);
    +     * // => { '4': [4.2], '6': [6.1, 6.3] }
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.groupBy(['one', 'two', 'three'], 'length');
    +     * // => { '3': ['one', 'two'], '5': ['three'] }
    +     */
    +    var groupBy = createAggregator(function (result, value, key) {
    +      if (hasOwnProperty.call(result, key)) {
    +        result[key].push(value);
    +      } else {
    +        baseAssignValue(result, key, [value]);
    +      }
    +    });
    +
    +    /**
    +     * Checks if `value` is in `collection`. If `collection` is a string, it's
    +     * checked for a substring of `value`, otherwise
    +     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    +     * is used for equality comparisons. If `fromIndex` is negative, it's used as
    +     * the offset from the end of `collection`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object|string} collection The collection to inspect.
    +     * @param {*} value The value to search for.
    +     * @param {number} [fromIndex=0] The index to search from.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
    +     * @returns {boolean} Returns `true` if `value` is found, else `false`.
    +     * @example
    +     *
    +     * _.includes([1, 2, 3], 1);
    +     * // => true
    +     *
    +     * _.includes([1, 2, 3], 1, 2);
    +     * // => false
    +     *
    +     * _.includes({ 'a': 1, 'b': 2 }, 1);
    +     * // => true
    +     *
    +     * _.includes('abcd', 'bc');
    +     * // => true
    +     */
    +    function includes(collection, value, fromIndex, guard) {
    +      collection = isArrayLike(collection) ? collection : values(collection);
    +      fromIndex = fromIndex && !guard ? toInteger(fromIndex) : 0;
    +      var length = collection.length;
    +      if (fromIndex < 0) {
    +        fromIndex = nativeMax(length + fromIndex, 0);
    +      }
    +      return isString(collection) ? fromIndex <= length && collection.indexOf(value, fromIndex) > -1 : !!length && baseIndexOf(collection, value, fromIndex) > -1;
    +    }
    +
    +    /**
    +     * Invokes the method at `path` of each element in `collection`, returning
    +     * an array of the results of each invoked method. Any additional arguments
    +     * are provided to each invoked method. If `path` is a function, it's invoked
    +     * for, and `this` bound to, each element in `collection`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Array|Function|string} path The path of the method to invoke or
    +     *  the function invoked per iteration.
    +     * @param {...*} [args] The arguments to invoke each method with.
    +     * @returns {Array} Returns the array of results.
    +     * @example
    +     *
    +     * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');
    +     * // => [[1, 5, 7], [1, 2, 3]]
    +     *
    +     * _.invokeMap([123, 456], String.prototype.split, '');
    +     * // => [['1', '2', '3'], ['4', '5', '6']]
    +     */
    +    var invokeMap = baseRest(function (collection, path, args) {
    +      var index = -1,
    +        isFunc = typeof path == 'function',
    +        result = isArrayLike(collection) ? Array(collection.length) : [];
    +      baseEach(collection, function (value) {
    +        result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args);
    +      });
    +      return result;
    +    });
    +
    +    /**
    +     * Creates an object composed of keys generated from the results of running
    +     * each element of `collection` thru `iteratee`. The corresponding value of
    +     * each key is the last element responsible for generating the key. The
    +     * iteratee is invoked with one argument: (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
    +     * @returns {Object} Returns the composed aggregate object.
    +     * @example
    +     *
    +     * var array = [
    +     *   { 'dir': 'left', 'code': 97 },
    +     *   { 'dir': 'right', 'code': 100 }
    +     * ];
    +     *
    +     * _.keyBy(array, function(o) {
    +     *   return String.fromCharCode(o.code);
    +     * });
    +     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
    +     *
    +     * _.keyBy(array, 'dir');
    +     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
    +     */
    +    var keyBy = createAggregator(function (result, value, key) {
    +      baseAssignValue(result, key, value);
    +    });
    +
    +    /**
    +     * Creates an array of values by running each element in `collection` thru
    +     * `iteratee`. The iteratee is invoked with three arguments:
    +     * (value, index|key, collection).
    +     *
    +     * Many lodash methods are guarded to work as iteratees for methods like
    +     * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
    +     *
    +     * The guarded methods are:
    +     * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
    +     * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
    +     * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
    +     * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the new mapped array.
    +     * @example
    +     *
    +     * function square(n) {
    +     *   return n * n;
    +     * }
    +     *
    +     * _.map([4, 8], square);
    +     * // => [16, 64]
    +     *
    +     * _.map({ 'a': 4, 'b': 8 }, square);
    +     * // => [16, 64] (iteration order is not guaranteed)
    +     *
    +     * var users = [
    +     *   { 'user': 'barney' },
    +     *   { 'user': 'fred' }
    +     * ];
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.map(users, 'user');
    +     * // => ['barney', 'fred']
    +     */
    +    function map(collection, iteratee) {
    +      var func = isArray(collection) ? arrayMap : baseMap;
    +      return func(collection, getIteratee(iteratee, 3));
    +    }
    +
    +    /**
    +     * This method is like `_.sortBy` except that it allows specifying the sort
    +     * orders of the iteratees to sort by. If `orders` is unspecified, all values
    +     * are sorted in ascending order. Otherwise, specify an order of "desc" for
    +     * descending or "asc" for ascending sort order of corresponding values.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]
    +     *  The iteratees to sort by.
    +     * @param {string[]} [orders] The sort orders of `iteratees`.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
    +     * @returns {Array} Returns the new sorted array.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'fred',   'age': 48 },
    +     *   { 'user': 'barney', 'age': 34 },
    +     *   { 'user': 'fred',   'age': 40 },
    +     *   { 'user': 'barney', 'age': 36 }
    +     * ];
    +     *
    +     * // Sort by `user` in ascending order and by `age` in descending order.
    +     * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);
    +     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
    +     */
    +    function orderBy(collection, iteratees, orders, guard) {
    +      if (collection == null) {
    +        return [];
    +      }
    +      if (!isArray(iteratees)) {
    +        iteratees = iteratees == null ? [] : [iteratees];
    +      }
    +      orders = guard ? undefined : orders;
    +      if (!isArray(orders)) {
    +        orders = orders == null ? [] : [orders];
    +      }
    +      return baseOrderBy(collection, iteratees, orders);
    +    }
    +
    +    /**
    +     * Creates an array of elements split into two groups, the first of which
    +     * contains elements `predicate` returns truthy for, the second of which
    +     * contains elements `predicate` returns falsey for. The predicate is
    +     * invoked with one argument: (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the array of grouped elements.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney',  'age': 36, 'active': false },
    +     *   { 'user': 'fred',    'age': 40, 'active': true },
    +     *   { 'user': 'pebbles', 'age': 1,  'active': false }
    +     * ];
    +     *
    +     * _.partition(users, function(o) { return o.active; });
    +     * // => objects for [['fred'], ['barney', 'pebbles']]
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.partition(users, { 'age': 1, 'active': false });
    +     * // => objects for [['pebbles'], ['barney', 'fred']]
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.partition(users, ['active', false]);
    +     * // => objects for [['barney', 'pebbles'], ['fred']]
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.partition(users, 'active');
    +     * // => objects for [['fred'], ['barney', 'pebbles']]
    +     */
    +    var partition = createAggregator(function (result, value, key) {
    +      result[key ? 0 : 1].push(value);
    +    }, function () {
    +      return [[], []];
    +    });
    +
    +    /**
    +     * Reduces `collection` to a value which is the accumulated result of running
    +     * each element in `collection` thru `iteratee`, where each successive
    +     * invocation is supplied the return value of the previous. If `accumulator`
    +     * is not given, the first element of `collection` is used as the initial
    +     * value. The iteratee is invoked with four arguments:
    +     * (accumulator, value, index|key, collection).
    +     *
    +     * Many lodash methods are guarded to work as iteratees for methods like
    +     * `_.reduce`, `_.reduceRight`, and `_.transform`.
    +     *
    +     * The guarded methods are:
    +     * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
    +     * and `sortBy`
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    +     * @param {*} [accumulator] The initial value.
    +     * @returns {*} Returns the accumulated value.
    +     * @see _.reduceRight
    +     * @example
    +     *
    +     * _.reduce([1, 2], function(sum, n) {
    +     *   return sum + n;
    +     * }, 0);
    +     * // => 3
    +     *
    +     * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
    +     *   (result[value] || (result[value] = [])).push(key);
    +     *   return result;
    +     * }, {});
    +     * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
    +     */
    +    function reduce(collection, iteratee, accumulator) {
    +      var func = isArray(collection) ? arrayReduce : baseReduce,
    +        initAccum = arguments.length < 3;
    +      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
    +    }
    +
    +    /**
    +     * This method is like `_.reduce` except that it iterates over elements of
    +     * `collection` from right to left.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    +     * @param {*} [accumulator] The initial value.
    +     * @returns {*} Returns the accumulated value.
    +     * @see _.reduce
    +     * @example
    +     *
    +     * var array = [[0, 1], [2, 3], [4, 5]];
    +     *
    +     * _.reduceRight(array, function(flattened, other) {
    +     *   return flattened.concat(other);
    +     * }, []);
    +     * // => [4, 5, 2, 3, 0, 1]
    +     */
    +    function reduceRight(collection, iteratee, accumulator) {
    +      var func = isArray(collection) ? arrayReduceRight : baseReduce,
    +        initAccum = arguments.length < 3;
    +      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);
    +    }
    +
    +    /**
    +     * The opposite of `_.filter`; this method returns the elements of `collection`
    +     * that `predicate` does **not** return truthy for.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @returns {Array} Returns the new filtered array.
    +     * @see _.filter
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'barney', 'age': 36, 'active': false },
    +     *   { 'user': 'fred',   'age': 40, 'active': true }
    +     * ];
    +     *
    +     * _.reject(users, function(o) { return !o.active; });
    +     * // => objects for ['fred']
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.reject(users, { 'age': 40, 'active': true });
    +     * // => objects for ['barney']
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.reject(users, ['active', false]);
    +     * // => objects for ['fred']
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.reject(users, 'active');
    +     * // => objects for ['barney']
    +     */
    +    function reject(collection, predicate) {
    +      var func = isArray(collection) ? arrayFilter : baseFilter;
    +      return func(collection, negate(getIteratee(predicate, 3)));
    +    }
    +
    +    /**
    +     * Gets a random element from `collection`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 2.0.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to sample.
    +     * @returns {*} Returns the random element.
    +     * @example
    +     *
    +     * _.sample([1, 2, 3, 4]);
    +     * // => 2
    +     */
    +    function sample(collection) {
    +      var func = isArray(collection) ? arraySample : baseSample;
    +      return func(collection);
    +    }
    +
    +    /**
    +     * Gets `n` random elements at unique keys from `collection` up to the
    +     * size of `collection`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to sample.
    +     * @param {number} [n=1] The number of elements to sample.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {Array} Returns the random elements.
    +     * @example
    +     *
    +     * _.sampleSize([1, 2, 3], 2);
    +     * // => [3, 1]
    +     *
    +     * _.sampleSize([1, 2, 3], 4);
    +     * // => [2, 3, 1]
    +     */
    +    function sampleSize(collection, n, guard) {
    +      if (guard ? isIterateeCall(collection, n, guard) : n === undefined) {
    +        n = 1;
    +      } else {
    +        n = toInteger(n);
    +      }
    +      var func = isArray(collection) ? arraySampleSize : baseSampleSize;
    +      return func(collection, n);
    +    }
    +
    +    /**
    +     * Creates an array of shuffled values, using a version of the
    +     * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to shuffle.
    +     * @returns {Array} Returns the new shuffled array.
    +     * @example
    +     *
    +     * _.shuffle([1, 2, 3, 4]);
    +     * // => [4, 1, 3, 2]
    +     */
    +    function shuffle(collection) {
    +      var func = isArray(collection) ? arrayShuffle : baseShuffle;
    +      return func(collection);
    +    }
    +
    +    /**
    +     * Gets the size of `collection` by returning its length for array-like
    +     * values or the number of own enumerable string keyed properties for objects.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object|string} collection The collection to inspect.
    +     * @returns {number} Returns the collection size.
    +     * @example
    +     *
    +     * _.size([1, 2, 3]);
    +     * // => 3
    +     *
    +     * _.size({ 'a': 1, 'b': 2 });
    +     * // => 2
    +     *
    +     * _.size('pebbles');
    +     * // => 7
    +     */
    +    function size(collection) {
    +      if (collection == null) {
    +        return 0;
    +      }
    +      if (isArrayLike(collection)) {
    +        return isString(collection) ? stringSize(collection) : collection.length;
    +      }
    +      var tag = getTag(collection);
    +      if (tag == mapTag || tag == setTag) {
    +        return collection.size;
    +      }
    +      return baseKeys(collection).length;
    +    }
    +
    +    /**
    +     * Checks if `predicate` returns truthy for **any** element of `collection`.
    +     * Iteration is stopped once `predicate` returns truthy. The predicate is
    +     * invoked with three arguments: (value, index|key, collection).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {boolean} Returns `true` if any element passes the predicate check,
    +     *  else `false`.
    +     * @example
    +     *
    +     * _.some([null, 0, 'yes', false], Boolean);
    +     * // => true
    +     *
    +     * var users = [
    +     *   { 'user': 'barney', 'active': true },
    +     *   { 'user': 'fred',   'active': false }
    +     * ];
    +     *
    +     * // The `_.matches` iteratee shorthand.
    +     * _.some(users, { 'user': 'barney', 'active': false });
    +     * // => false
    +     *
    +     * // The `_.matchesProperty` iteratee shorthand.
    +     * _.some(users, ['active', false]);
    +     * // => true
    +     *
    +     * // The `_.property` iteratee shorthand.
    +     * _.some(users, 'active');
    +     * // => true
    +     */
    +    function some(collection, predicate, guard) {
    +      var func = isArray(collection) ? arraySome : baseSome;
    +      if (guard && isIterateeCall(collection, predicate, guard)) {
    +        predicate = undefined;
    +      }
    +      return func(collection, getIteratee(predicate, 3));
    +    }
    +
    +    /**
    +     * Creates an array of elements, sorted in ascending order by the results of
    +     * running each element in a collection thru each iteratee. This method
    +     * performs a stable sort, that is, it preserves the original sort order of
    +     * equal elements. The iteratees are invoked with one argument: (value).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Collection
    +     * @param {Array|Object} collection The collection to iterate over.
    +     * @param {...(Function|Function[])} [iteratees=[_.identity]]
    +     *  The iteratees to sort by.
    +     * @returns {Array} Returns the new sorted array.
    +     * @example
    +     *
    +     * var users = [
    +     *   { 'user': 'fred',   'age': 48 },
    +     *   { 'user': 'barney', 'age': 36 },
    +     *   { 'user': 'fred',   'age': 30 },
    +     *   { 'user': 'barney', 'age': 34 }
    +     * ];
    +     *
    +     * _.sortBy(users, [function(o) { return o.user; }]);
    +     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 30]]
    +     *
    +     * _.sortBy(users, ['user', 'age']);
    +     * // => objects for [['barney', 34], ['barney', 36], ['fred', 30], ['fred', 48]]
    +     */
    +    var sortBy = baseRest(function (collection, iteratees) {
    +      if (collection == null) {
    +        return [];
    +      }
    +      var length = iteratees.length;
    +      if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
    +        iteratees = [];
    +      } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
    +        iteratees = [iteratees[0]];
    +      }
    +      return baseOrderBy(collection, baseFlatten(iteratees, 1), []);
    +    });
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * Gets the timestamp of the number of milliseconds that have elapsed since
    +     * the Unix epoch (1 January 1970 00:00:00 UTC).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 2.4.0
    +     * @category Date
    +     * @returns {number} Returns the timestamp.
    +     * @example
    +     *
    +     * _.defer(function(stamp) {
    +     *   console.log(_.now() - stamp);
    +     * }, _.now());
    +     * // => Logs the number of milliseconds it took for the deferred invocation.
    +     */
    +    var now = ctxNow || function () {
    +      return root.Date.now();
    +    };
    +
    +    /*------------------------------------------------------------------------*/
    +
    +    /**
    +     * The opposite of `_.before`; this method creates a function that invokes
    +     * `func` once it's called `n` or more times.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Function
    +     * @param {number} n The number of calls before `func` is invoked.
    +     * @param {Function} func The function to restrict.
    +     * @returns {Function} Returns the new restricted function.
    +     * @example
    +     *
    +     * var saves = ['profile', 'settings'];
    +     *
    +     * var done = _.after(saves.length, function() {
    +     *   console.log('done saving!');
    +     * });
    +     *
    +     * _.forEach(saves, function(type) {
    +     *   asyncSave({ 'type': type, 'complete': done });
    +     * });
    +     * // => Logs 'done saving!' after the two async saves have completed.
    +     */
    +    function after(n, func) {
    +      if (typeof func != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      n = toInteger(n);
    +      return function () {
    +        if (--n < 1) {
    +          return func.apply(this, arguments);
    +        }
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that invokes `func`, with up to `n` arguments,
    +     * ignoring any additional arguments.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Function
    +     * @param {Function} func The function to cap arguments for.
    +     * @param {number} [n=func.length] The arity cap.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {Function} Returns the new capped function.
    +     * @example
    +     *
    +     * _.map(['6', '8', '10'], _.ary(parseInt, 1));
    +     * // => [6, 8, 10]
    +     */
    +    function ary(func, n, guard) {
    +      n = guard ? undefined : n;
    +      n = func && n == null ? func.length : n;
    +      return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);
    +    }
    +
    +    /**
    +     * Creates a function that invokes `func`, with the `this` binding and arguments
    +     * of the created function, while it's called less than `n` times. Subsequent
    +     * calls to the created function return the result of the last `func` invocation.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Function
    +     * @param {number} n The number of calls at which `func` is no longer invoked.
    +     * @param {Function} func The function to restrict.
    +     * @returns {Function} Returns the new restricted function.
    +     * @example
    +     *
    +     * jQuery(element).on('click', _.before(5, addContactToList));
    +     * // => Allows adding up to 4 contacts to the list.
    +     */
    +    function before(n, func) {
    +      var result;
    +      if (typeof func != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      n = toInteger(n);
    +      return function () {
    +        if (--n > 0) {
    +          result = func.apply(this, arguments);
    +        }
    +        if (n <= 1) {
    +          func = undefined;
    +        }
    +        return result;
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that invokes `func` with the `this` binding of `thisArg`
    +     * and `partials` prepended to the arguments it receives.
    +     *
    +     * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
    +     * may be used as a placeholder for partially applied arguments.
    +     *
    +     * **Note:** Unlike native `Function#bind`, this method doesn't set the "length"
    +     * property of bound functions.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Function
    +     * @param {Function} func The function to bind.
    +     * @param {*} thisArg The `this` binding of `func`.
    +     * @param {...*} [partials] The arguments to be partially applied.
    +     * @returns {Function} Returns the new bound function.
    +     * @example
    +     *
    +     * function greet(greeting, punctuation) {
    +     *   return greeting + ' ' + this.user + punctuation;
    +     * }
    +     *
    +     * var object = { 'user': 'fred' };
    +     *
    +     * var bound = _.bind(greet, object, 'hi');
    +     * bound('!');
    +     * // => 'hi fred!'
    +     *
    +     * // Bound with placeholders.
    +     * var bound = _.bind(greet, object, _, '!');
    +     * bound('hi');
    +     * // => 'hi fred!'
    +     */
    +    var bind = baseRest(function (func, thisArg, partials) {
    +      var bitmask = WRAP_BIND_FLAG;
    +      if (partials.length) {
    +        var holders = replaceHolders(partials, getHolder(bind));
    +        bitmask |= WRAP_PARTIAL_FLAG;
    +      }
    +      return createWrap(func, bitmask, thisArg, partials, holders);
    +    });
    +
    +    /**
    +     * Creates a function that invokes the method at `object[key]` with `partials`
    +     * prepended to the arguments it receives.
    +     *
    +     * This method differs from `_.bind` by allowing bound functions to reference
    +     * methods that may be redefined or don't yet exist. See
    +     * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)
    +     * for more details.
    +     *
    +     * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
    +     * builds, may be used as a placeholder for partially applied arguments.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.10.0
    +     * @category Function
    +     * @param {Object} object The object to invoke the method on.
    +     * @param {string} key The key of the method.
    +     * @param {...*} [partials] The arguments to be partially applied.
    +     * @returns {Function} Returns the new bound function.
    +     * @example
    +     *
    +     * var object = {
    +     *   'user': 'fred',
    +     *   'greet': function(greeting, punctuation) {
    +     *     return greeting + ' ' + this.user + punctuation;
    +     *   }
    +     * };
    +     *
    +     * var bound = _.bindKey(object, 'greet', 'hi');
    +     * bound('!');
    +     * // => 'hi fred!'
    +     *
    +     * object.greet = function(greeting, punctuation) {
    +     *   return greeting + 'ya ' + this.user + punctuation;
    +     * };
    +     *
    +     * bound('!');
    +     * // => 'hiya fred!'
    +     *
    +     * // Bound with placeholders.
    +     * var bound = _.bindKey(object, 'greet', _, '!');
    +     * bound('hi');
    +     * // => 'hiya fred!'
    +     */
    +    var bindKey = baseRest(function (object, key, partials) {
    +      var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG;
    +      if (partials.length) {
    +        var holders = replaceHolders(partials, getHolder(bindKey));
    +        bitmask |= WRAP_PARTIAL_FLAG;
    +      }
    +      return createWrap(key, bitmask, object, partials, holders);
    +    });
    +
    +    /**
    +     * Creates a function that accepts arguments of `func` and either invokes
    +     * `func` returning its result, if at least `arity` number of arguments have
    +     * been provided, or returns a function that accepts the remaining `func`
    +     * arguments, and so on. The arity of `func` may be specified if `func.length`
    +     * is not sufficient.
    +     *
    +     * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
    +     * may be used as a placeholder for provided arguments.
    +     *
    +     * **Note:** This method doesn't set the "length" property of curried functions.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 2.0.0
    +     * @category Function
    +     * @param {Function} func The function to curry.
    +     * @param {number} [arity=func.length] The arity of `func`.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {Function} Returns the new curried function.
    +     * @example
    +     *
    +     * var abc = function(a, b, c) {
    +     *   return [a, b, c];
    +     * };
    +     *
    +     * var curried = _.curry(abc);
    +     *
    +     * curried(1)(2)(3);
    +     * // => [1, 2, 3]
    +     *
    +     * curried(1, 2)(3);
    +     * // => [1, 2, 3]
    +     *
    +     * curried(1, 2, 3);
    +     * // => [1, 2, 3]
    +     *
    +     * // Curried with placeholders.
    +     * curried(1)(_, 3)(2);
    +     * // => [1, 2, 3]
    +     */
    +    function curry(func, arity, guard) {
    +      arity = guard ? undefined : arity;
    +      var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
    +      result.placeholder = curry.placeholder;
    +      return result;
    +    }
    +
    +    /**
    +     * This method is like `_.curry` except that arguments are applied to `func`
    +     * in the manner of `_.partialRight` instead of `_.partial`.
    +     *
    +     * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
    +     * builds, may be used as a placeholder for provided arguments.
    +     *
    +     * **Note:** This method doesn't set the "length" property of curried functions.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Function
    +     * @param {Function} func The function to curry.
    +     * @param {number} [arity=func.length] The arity of `func`.
    +     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    +     * @returns {Function} Returns the new curried function.
    +     * @example
    +     *
    +     * var abc = function(a, b, c) {
    +     *   return [a, b, c];
    +     * };
    +     *
    +     * var curried = _.curryRight(abc);
    +     *
    +     * curried(3)(2)(1);
    +     * // => [1, 2, 3]
    +     *
    +     * curried(2, 3)(1);
    +     * // => [1, 2, 3]
    +     *
    +     * curried(1, 2, 3);
    +     * // => [1, 2, 3]
    +     *
    +     * // Curried with placeholders.
    +     * curried(3)(1, _)(2);
    +     * // => [1, 2, 3]
    +     */
    +    function curryRight(func, arity, guard) {
    +      arity = guard ? undefined : arity;
    +      var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
    +      result.placeholder = curryRight.placeholder;
    +      return result;
    +    }
    +
    +    /**
    +     * Creates a debounced function that delays invoking `func` until after `wait`
    +     * milliseconds have elapsed since the last time the debounced function was
    +     * invoked. The debounced function comes with a `cancel` method to cancel
    +     * delayed `func` invocations and a `flush` method to immediately invoke them.
    +     * Provide `options` to indicate whether `func` should be invoked on the
    +     * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
    +     * with the last arguments provided to the debounced function. Subsequent
    +     * calls to the debounced function return the result of the last `func`
    +     * invocation.
    +     *
    +     * **Note:** If `leading` and `trailing` options are `true`, `func` is
    +     * invoked on the trailing edge of the timeout only if the debounced function
    +     * is invoked more than once during the `wait` timeout.
    +     *
    +     * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
    +     * until to the next tick, similar to `setTimeout` with a timeout of `0`.
    +     *
    +     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
    +     * for details over the differences between `_.debounce` and `_.throttle`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Function
    +     * @param {Function} func The function to debounce.
    +     * @param {number} [wait=0] The number of milliseconds to delay.
    +     * @param {Object} [options={}] The options object.
    +     * @param {boolean} [options.leading=false]
    +     *  Specify invoking on the leading edge of the timeout.
    +     * @param {number} [options.maxWait]
    +     *  The maximum time `func` is allowed to be delayed before it's invoked.
    +     * @param {boolean} [options.trailing=true]
    +     *  Specify invoking on the trailing edge of the timeout.
    +     * @returns {Function} Returns the new debounced function.
    +     * @example
    +     *
    +     * // Avoid costly calculations while the window size is in flux.
    +     * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
    +     *
    +     * // Invoke `sendMail` when clicked, debouncing subsequent calls.
    +     * jQuery(element).on('click', _.debounce(sendMail, 300, {
    +     *   'leading': true,
    +     *   'trailing': false
    +     * }));
    +     *
    +     * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
    +     * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
    +     * var source = new EventSource('/stream');
    +     * jQuery(source).on('message', debounced);
    +     *
    +     * // Cancel the trailing debounced invocation.
    +     * jQuery(window).on('popstate', debounced.cancel);
    +     */
    +    function debounce(func, wait, options) {
    +      var lastArgs,
    +        lastThis,
    +        maxWait,
    +        result,
    +        timerId,
    +        lastCallTime,
    +        lastInvokeTime = 0,
    +        leading = false,
    +        maxing = false,
    +        trailing = true;
    +      if (typeof func != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      wait = toNumber(wait) || 0;
    +      if (isObject(options)) {
    +        leading = !!options.leading;
    +        maxing = 'maxWait' in options;
    +        maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
    +        trailing = 'trailing' in options ? !!options.trailing : trailing;
    +      }
    +      function invokeFunc(time) {
    +        var args = lastArgs,
    +          thisArg = lastThis;
    +        lastArgs = lastThis = undefined;
    +        lastInvokeTime = time;
    +        result = func.apply(thisArg, args);
    +        return result;
    +      }
    +      function leadingEdge(time) {
    +        // Reset any `maxWait` timer.
    +        lastInvokeTime = time;
    +        // Start the timer for the trailing edge.
    +        timerId = setTimeout(timerExpired, wait);
    +        // Invoke the leading edge.
    +        return leading ? invokeFunc(time) : result;
    +      }
    +      function remainingWait(time) {
    +        var timeSinceLastCall = time - lastCallTime,
    +          timeSinceLastInvoke = time - lastInvokeTime,
    +          timeWaiting = wait - timeSinceLastCall;
    +        return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
    +      }
    +      function shouldInvoke(time) {
    +        var timeSinceLastCall = time - lastCallTime,
    +          timeSinceLastInvoke = time - lastInvokeTime;
    +
    +        // Either this is the first call, activity has stopped and we're at the
    +        // trailing edge, the system time has gone backwards and we're treating
    +        // it as the trailing edge, or we've hit the `maxWait` limit.
    +        return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
    +      }
    +      function timerExpired() {
    +        var time = now();
    +        if (shouldInvoke(time)) {
    +          return trailingEdge(time);
    +        }
    +        // Restart the timer.
    +        timerId = setTimeout(timerExpired, remainingWait(time));
    +      }
    +      function trailingEdge(time) {
    +        timerId = undefined;
    +
    +        // Only invoke if we have `lastArgs` which means `func` has been
    +        // debounced at least once.
    +        if (trailing && lastArgs) {
    +          return invokeFunc(time);
    +        }
    +        lastArgs = lastThis = undefined;
    +        return result;
    +      }
    +      function cancel() {
    +        if (timerId !== undefined) {
    +          clearTimeout(timerId);
    +        }
    +        lastInvokeTime = 0;
    +        lastArgs = lastCallTime = lastThis = timerId = undefined;
    +      }
    +      function flush() {
    +        return timerId === undefined ? result : trailingEdge(now());
    +      }
    +      function debounced() {
    +        var time = now(),
    +          isInvoking = shouldInvoke(time);
    +        lastArgs = arguments;
    +        lastThis = this;
    +        lastCallTime = time;
    +        if (isInvoking) {
    +          if (timerId === undefined) {
    +            return leadingEdge(lastCallTime);
    +          }
    +          if (maxing) {
    +            // Handle invocations in a tight loop.
    +            clearTimeout(timerId);
    +            timerId = setTimeout(timerExpired, wait);
    +            return invokeFunc(lastCallTime);
    +          }
    +        }
    +        if (timerId === undefined) {
    +          timerId = setTimeout(timerExpired, wait);
    +        }
    +        return result;
    +      }
    +      debounced.cancel = cancel;
    +      debounced.flush = flush;
    +      return debounced;
    +    }
    +
    +    /**
    +     * Defers invoking the `func` until the current call stack has cleared. Any
    +     * additional arguments are provided to `func` when it's invoked.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Function
    +     * @param {Function} func The function to defer.
    +     * @param {...*} [args] The arguments to invoke `func` with.
    +     * @returns {number} Returns the timer id.
    +     * @example
    +     *
    +     * _.defer(function(text) {
    +     *   console.log(text);
    +     * }, 'deferred');
    +     * // => Logs 'deferred' after one millisecond.
    +     */
    +    var defer = baseRest(function (func, args) {
    +      return baseDelay(func, 1, args);
    +    });
    +
    +    /**
    +     * Invokes `func` after `wait` milliseconds. Any additional arguments are
    +     * provided to `func` when it's invoked.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Function
    +     * @param {Function} func The function to delay.
    +     * @param {number} wait The number of milliseconds to delay invocation.
    +     * @param {...*} [args] The arguments to invoke `func` with.
    +     * @returns {number} Returns the timer id.
    +     * @example
    +     *
    +     * _.delay(function(text) {
    +     *   console.log(text);
    +     * }, 1000, 'later');
    +     * // => Logs 'later' after one second.
    +     */
    +    var delay = baseRest(function (func, wait, args) {
    +      return baseDelay(func, toNumber(wait) || 0, args);
    +    });
    +
    +    /**
    +     * Creates a function that invokes `func` with arguments reversed.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Function
    +     * @param {Function} func The function to flip arguments for.
    +     * @returns {Function} Returns the new flipped function.
    +     * @example
    +     *
    +     * var flipped = _.flip(function() {
    +     *   return _.toArray(arguments);
    +     * });
    +     *
    +     * flipped('a', 'b', 'c', 'd');
    +     * // => ['d', 'c', 'b', 'a']
    +     */
    +    function flip(func) {
    +      return createWrap(func, WRAP_FLIP_FLAG);
    +    }
    +
    +    /**
    +     * Creates a function that memoizes the result of `func`. If `resolver` is
    +     * provided, it determines the cache key for storing the result based on the
    +     * arguments provided to the memoized function. By default, the first argument
    +     * provided to the memoized function is used as the map cache key. The `func`
    +     * is invoked with the `this` binding of the memoized function.
    +     *
    +     * **Note:** The cache is exposed as the `cache` property on the memoized
    +     * function. Its creation may be customized by replacing the `_.memoize.Cache`
    +     * constructor with one whose instances implement the
    +     * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
    +     * method interface of `clear`, `delete`, `get`, `has`, and `set`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Function
    +     * @param {Function} func The function to have its output memoized.
    +     * @param {Function} [resolver] The function to resolve the cache key.
    +     * @returns {Function} Returns the new memoized function.
    +     * @example
    +     *
    +     * var object = { 'a': 1, 'b': 2 };
    +     * var other = { 'c': 3, 'd': 4 };
    +     *
    +     * var values = _.memoize(_.values);
    +     * values(object);
    +     * // => [1, 2]
    +     *
    +     * values(other);
    +     * // => [3, 4]
    +     *
    +     * object.a = 2;
    +     * values(object);
    +     * // => [1, 2]
    +     *
    +     * // Modify the result cache.
    +     * values.cache.set(object, ['a', 'b']);
    +     * values(object);
    +     * // => ['a', 'b']
    +     *
    +     * // Replace `_.memoize.Cache`.
    +     * _.memoize.Cache = WeakMap;
    +     */
    +    function memoize(func, resolver) {
    +      if (typeof func != 'function' || resolver != null && typeof resolver != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      var memoized = function memoized() {
    +        var args = arguments,
    +          key = resolver ? resolver.apply(this, args) : args[0],
    +          cache = memoized.cache;
    +        if (cache.has(key)) {
    +          return cache.get(key);
    +        }
    +        var result = func.apply(this, args);
    +        memoized.cache = cache.set(key, result) || cache;
    +        return result;
    +      };
    +      memoized.cache = new (memoize.Cache || MapCache)();
    +      return memoized;
    +    }
    +
    +    // Expose `MapCache`.
    +    memoize.Cache = MapCache;
    +
    +    /**
    +     * Creates a function that negates the result of the predicate `func`. The
    +     * `func` predicate is invoked with the `this` binding and arguments of the
    +     * created function.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Function
    +     * @param {Function} predicate The predicate to negate.
    +     * @returns {Function} Returns the new negated function.
    +     * @example
    +     *
    +     * function isEven(n) {
    +     *   return n % 2 == 0;
    +     * }
    +     *
    +     * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
    +     * // => [1, 3, 5]
    +     */
    +    function negate(predicate) {
    +      if (typeof predicate != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      return function () {
    +        var args = arguments;
    +        switch (args.length) {
    +          case 0:
    +            return !predicate.call(this);
    +          case 1:
    +            return !predicate.call(this, args[0]);
    +          case 2:
    +            return !predicate.call(this, args[0], args[1]);
    +          case 3:
    +            return !predicate.call(this, args[0], args[1], args[2]);
    +        }
    +        return !predicate.apply(this, args);
    +      };
    +    }
    +
    +    /**
    +     * Creates a function that is restricted to invoking `func` once. Repeat calls
    +     * to the function return the value of the first invocation. The `func` is
    +     * invoked with the `this` binding and arguments of the created function.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Function
    +     * @param {Function} func The function to restrict.
    +     * @returns {Function} Returns the new restricted function.
    +     * @example
    +     *
    +     * var initialize = _.once(createApplication);
    +     * initialize();
    +     * initialize();
    +     * // => `createApplication` is invoked once
    +     */
    +    function once(func) {
    +      return before(2, func);
    +    }
    +
    +    /**
    +     * Creates a function that invokes `func` with its arguments transformed.
    +     *
    +     * @static
    +     * @since 4.0.0
    +     * @memberOf _
    +     * @category Function
    +     * @param {Function} func The function to wrap.
    +     * @param {...(Function|Function[])} [transforms=[_.identity]]
    +     *  The argument transforms.
    +     * @returns {Function} Returns the new function.
    +     * @example
    +     *
    +     * function doubled(n) {
    +     *   return n * 2;
    +     * }
    +     *
    +     * function square(n) {
    +     *   return n * n;
    +     * }
    +     *
    +     * var func = _.overArgs(function(x, y) {
    +     *   return [x, y];
    +     * }, [square, doubled]);
    +     *
    +     * func(9, 3);
    +     * // => [81, 6]
    +     *
    +     * func(10, 5);
    +     * // => [100, 10]
    +     */
    +    var overArgs = castRest(function (func, transforms) {
    +      transforms = transforms.length == 1 && isArray(transforms[0]) ? arrayMap(transforms[0], baseUnary(getIteratee())) : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee()));
    +      var funcsLength = transforms.length;
    +      return baseRest(function (args) {
    +        var index = -1,
    +          length = nativeMin(args.length, funcsLength);
    +        while (++index < length) {
    +          args[index] = transforms[index].call(this, args[index]);
    +        }
    +        return apply(func, this, args);
    +      });
    +    });
    +
    +    /**
    +     * Creates a function that invokes `func` with `partials` prepended to the
    +     * arguments it receives. This method is like `_.bind` except it does **not**
    +     * alter the `this` binding.
    +     *
    +     * The `_.partial.placeholder` value, which defaults to `_` in monolithic
    +     * builds, may be used as a placeholder for partially applied arguments.
    +     *
    +     * **Note:** This method doesn't set the "length" property of partially
    +     * applied functions.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.2.0
    +     * @category Function
    +     * @param {Function} func The function to partially apply arguments to.
    +     * @param {...*} [partials] The arguments to be partially applied.
    +     * @returns {Function} Returns the new partially applied function.
    +     * @example
    +     *
    +     * function greet(greeting, name) {
    +     *   return greeting + ' ' + name;
    +     * }
    +     *
    +     * var sayHelloTo = _.partial(greet, 'hello');
    +     * sayHelloTo('fred');
    +     * // => 'hello fred'
    +     *
    +     * // Partially applied with placeholders.
    +     * var greetFred = _.partial(greet, _, 'fred');
    +     * greetFred('hi');
    +     * // => 'hi fred'
    +     */
    +    var partial = baseRest(function (func, partials) {
    +      var holders = replaceHolders(partials, getHolder(partial));
    +      return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders);
    +    });
    +
    +    /**
    +     * This method is like `_.partial` except that partially applied arguments
    +     * are appended to the arguments it receives.
    +     *
    +     * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
    +     * builds, may be used as a placeholder for partially applied arguments.
    +     *
    +     * **Note:** This method doesn't set the "length" property of partially
    +     * applied functions.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 1.0.0
    +     * @category Function
    +     * @param {Function} func The function to partially apply arguments to.
    +     * @param {...*} [partials] The arguments to be partially applied.
    +     * @returns {Function} Returns the new partially applied function.
    +     * @example
    +     *
    +     * function greet(greeting, name) {
    +     *   return greeting + ' ' + name;
    +     * }
    +     *
    +     * var greetFred = _.partialRight(greet, 'fred');
    +     * greetFred('hi');
    +     * // => 'hi fred'
    +     *
    +     * // Partially applied with placeholders.
    +     * var sayHelloTo = _.partialRight(greet, 'hello', _);
    +     * sayHelloTo('fred');
    +     * // => 'hello fred'
    +     */
    +    var partialRight = baseRest(function (func, partials) {
    +      var holders = replaceHolders(partials, getHolder(partialRight));
    +      return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders);
    +    });
    +
    +    /**
    +     * Creates a function that invokes `func` with arguments arranged according
    +     * to the specified `indexes` where the argument value at the first index is
    +     * provided as the first argument, the argument value at the second index is
    +     * provided as the second argument, and so on.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.0.0
    +     * @category Function
    +     * @param {Function} func The function to rearrange arguments for.
    +     * @param {...(number|number[])} indexes The arranged argument indexes.
    +     * @returns {Function} Returns the new function.
    +     * @example
    +     *
    +     * var rearged = _.rearg(function(a, b, c) {
    +     *   return [a, b, c];
    +     * }, [2, 0, 1]);
    +     *
    +     * rearged('b', 'c', 'a')
    +     * // => ['a', 'b', 'c']
    +     */
    +    var rearg = flatRest(function (func, indexes) {
    +      return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);
    +    });
    +
    +    /**
    +     * Creates a function that invokes `func` with the `this` binding of the
    +     * created function and arguments from `start` and beyond provided as
    +     * an array.
    +     *
    +     * **Note:** This method is based on the
    +     * [rest parameter](https://mdn.io/rest_parameters).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Function
    +     * @param {Function} func The function to apply a rest parameter to.
    +     * @param {number} [start=func.length-1] The start position of the rest parameter.
    +     * @returns {Function} Returns the new function.
    +     * @example
    +     *
    +     * var say = _.rest(function(what, names) {
    +     *   return what + ' ' + _.initial(names).join(', ') +
    +     *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
    +     * });
    +     *
    +     * say('hello', 'fred', 'barney', 'pebbles');
    +     * // => 'hello fred, barney, & pebbles'
    +     */
    +    function rest(func, start) {
    +      if (typeof func != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      start = start === undefined ? start : toInteger(start);
    +      return baseRest(func, start);
    +    }
    +
    +    /**
    +     * Creates a function that invokes `func` with the `this` binding of the
    +     * create function and an array of arguments much like
    +     * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply).
    +     *
    +     * **Note:** This method is based on the
    +     * [spread operator](https://mdn.io/spread_operator).
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 3.2.0
    +     * @category Function
    +     * @param {Function} func The function to spread arguments over.
    +     * @param {number} [start=0] The start position of the spread.
    +     * @returns {Function} Returns the new function.
    +     * @example
    +     *
    +     * var say = _.spread(function(who, what) {
    +     *   return who + ' says ' + what;
    +     * });
    +     *
    +     * say(['fred', 'hello']);
    +     * // => 'fred says hello'
    +     *
    +     * var numbers = Promise.all([
    +     *   Promise.resolve(40),
    +     *   Promise.resolve(36)
    +     * ]);
    +     *
    +     * numbers.then(_.spread(function(x, y) {
    +     *   return x + y;
    +     * }));
    +     * // => a Promise of 76
    +     */
    +    function spread(func, start) {
    +      if (typeof func != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      start = start == null ? 0 : nativeMax(toInteger(start), 0);
    +      return baseRest(function (args) {
    +        var array = args[start],
    +          otherArgs = castSlice(args, 0, start);
    +        if (array) {
    +          arrayPush(otherArgs, array);
    +        }
    +        return apply(func, this, otherArgs);
    +      });
    +    }
    +
    +    /**
    +     * Creates a throttled function that only invokes `func` at most once per
    +     * every `wait` milliseconds. The throttled function comes with a `cancel`
    +     * method to cancel delayed `func` invocations and a `flush` method to
    +     * immediately invoke them. Provide `options` to indicate whether `func`
    +     * should be invoked on the leading and/or trailing edge of the `wait`
    +     * timeout. The `func` is invoked with the last arguments provided to the
    +     * throttled function. Subsequent calls to the throttled function return the
    +     * result of the last `func` invocation.
    +     *
    +     * **Note:** If `leading` and `trailing` options are `true`, `func` is
    +     * invoked on the trailing edge of the timeout only if the throttled function
    +     * is invoked more than once during the `wait` timeout.
    +     *
    +     * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
    +     * until to the next tick, similar to `setTimeout` with a timeout of `0`.
    +     *
    +     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
    +     * for details over the differences between `_.throttle` and `_.debounce`.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Function
    +     * @param {Function} func The function to throttle.
    +     * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
    +     * @param {Object} [options={}] The options object.
    +     * @param {boolean} [options.leading=true]
    +     *  Specify invoking on the leading edge of the timeout.
    +     * @param {boolean} [options.trailing=true]
    +     *  Specify invoking on the trailing edge of the timeout.
    +     * @returns {Function} Returns the new throttled function.
    +     * @example
    +     *
    +     * // Avoid excessively updating the position while scrolling.
    +     * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
    +     *
    +     * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
    +     * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
    +     * jQuery(element).on('click', throttled);
    +     *
    +     * // Cancel the trailing throttled invocation.
    +     * jQuery(window).on('popstate', throttled.cancel);
    +     */
    +    function throttle(func, wait, options) {
    +      var leading = true,
    +        trailing = true;
    +      if (typeof func != 'function') {
    +        throw new TypeError(FUNC_ERROR_TEXT);
    +      }
    +      if (isObject(options)) {
    +        leading = 'leading' in options ? !!options.leading : leading;
    +        trailing = 'trailing' in options ? !!options.trailing : trailing;
    +      }
    +      return debounce(func, wait, {
    +        'leading': leading,
    +        'maxWait': wait,
    +        'trailing': trailing
    +      });
    +    }
    +
    +    /**
    +     * Creates a function that accepts up to one argument, ignoring any
    +     * additional arguments.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 4.0.0
    +     * @category Function
    +     * @param {Function} func The function to cap arguments for.
    +     * @returns {Function} Returns the new capped function.
    +     * @example
    +     *
    +     * _.map(['6', '8', '10'], _.unary(parseInt));
    +     * // => [6, 8, 10]
    +     */
    +    function unary(func) {
    +      return ary(func, 1);
    +    }
    +
    +    /**
    +     * Creates a function that provides `value` to `wrapper` as its first
    +     * argument. Any additional arguments provided to the function are appended
    +     * to those provided to the `wrapper`. The wrapper is invoked with the `this`
    +     * binding of the created function.
    +     *
    +     * @static
    +     * @memberOf _
    +     * @since 0.1.0
    +     * @category Function
    +     * @param {*} value The value to wrap.
    +     * @param {Function} [wrapper=identity] The wrapper function.
    +     * @returns {Function} Returns the new function.
    +     * @example
    +     *
    +     * var p = _.wrap(_.escape, function(func, text) {
    +     *   return '

    ' + func(text) + '

    '; + * }); + * + * p('fred, barney, & pebbles'); + * // => '

    fred, barney, & pebbles

    ' + */ + function wrap(value, wrapper) { + return partial(castFunction(wrapper), value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Casts `value` as an array if it's not one. + * + * @static + * @memberOf _ + * @since 4.4.0 + * @category Lang + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast array. + * @example + * + * _.castArray(1); + * // => [1] + * + * _.castArray({ 'a': 1 }); + * // => [{ 'a': 1 }] + * + * _.castArray('abc'); + * // => ['abc'] + * + * _.castArray(null); + * // => [null] + * + * _.castArray(undefined); + * // => [undefined] + * + * _.castArray(); + * // => [] + * + * var array = [1, 2, 3]; + * console.log(_.castArray(array) === array); + * // => true + */ + function castArray() { + if (!arguments.length) { + return []; + } + var value = arguments[0]; + return isArray(value) ? value : [value]; + } + + /** + * Creates a shallow clone of `value`. + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) + * and supports cloning arrays, array buffers, booleans, date objects, maps, + * numbers, `Object` objects, regexes, sets, strings, symbols, and typed + * arrays. The own enumerable properties of `arguments` objects are cloned + * as plain objects. An empty object is returned for uncloneable values such + * as error objects, functions, DOM nodes, and WeakMaps. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to clone. + * @returns {*} Returns the cloned value. + * @see _.cloneDeep + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var shallow = _.clone(objects); + * console.log(shallow[0] === objects[0]); + * // => true + */ + function clone(value) { + return baseClone(value, CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.clone` except that it accepts `customizer` which + * is invoked to produce the cloned value. If `customizer` returns `undefined`, + * cloning is handled by the method instead. The `customizer` is invoked with + * up to four arguments; (value [, index|key, object, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the cloned value. + * @see _.cloneDeepWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(false); + * } + * } + * + * var el = _.cloneWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 0 + */ + function cloneWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * This method is like `_.clone` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @returns {*} Returns the deep cloned value. + * @see _.clone + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var deep = _.cloneDeep(objects); + * console.log(deep[0] === objects[0]); + * // => false + */ + function cloneDeep(value) { + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.cloneWith` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the deep cloned value. + * @see _.cloneWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(true); + * } + * } + * + * var el = _.cloneDeepWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 20 + */ + function cloneDeepWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * Checks if `object` conforms to `source` by invoking the predicate + * properties of `source` with the corresponding property values of `object`. + * + * **Note:** This method is equivalent to `_.conforms` when `source` is + * partially applied. + * + * @static + * @memberOf _ + * @since 4.14.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); + * // => true + * + * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); + * // => false + */ + function conformsTo(object, source) { + return source == null || baseConformsTo(object, source, keys(source)); + } + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || value !== value && other !== other; + } + + /** + * Checks if `value` is greater than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + * @see _.lt + * @example + * + * _.gt(3, 1); + * // => true + * + * _.gt(3, 3); + * // => false + * + * _.gt(1, 3); + * // => false + */ + var gt = createRelationalOperation(baseGt); + + /** + * Checks if `value` is greater than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than or equal to + * `other`, else `false`. + * @see _.lte + * @example + * + * _.gte(3, 1); + * // => true + * + * _.gte(3, 3); + * // => true + * + * _.gte(1, 3); + * // => false + */ + var gte = createRelationalOperation(function (value, other) { + return value >= other; + }); + + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = baseIsArguments(function () { + return arguments; + }()) ? baseIsArguments : function (value) { + return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); + }; + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is classified as an `ArrayBuffer` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + * @example + * + * _.isArrayBuffer(new ArrayBuffer(2)); + * // => true + * + * _.isArrayBuffer(new Array(2)); + * // => false + */ + var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + + /** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + + /** + * Checks if `value` is classified as a boolean primitive or object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. + * @example + * + * _.isBoolean(false); + * // => true + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || isObjectLike(value) && baseGetTag(value) == boolTag; + } + + /** + * Checks if `value` is a buffer. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. + * @example + * + * _.isBuffer(new Buffer(2)); + * // => true + * + * _.isBuffer(new Uint8Array(2)); + * // => false + */ + var isBuffer = nativeIsBuffer || stubFalse; + + /** + * Checks if `value` is classified as a `Date` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + * + * _.isDate('Mon April 23 2012'); + * // => false + */ + var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; + + /** + * Checks if `value` is likely a DOM element. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + * + * _.isElement(''); + * // => false + */ + function isElement(value) { + return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); + } + + /** + * Checks if `value` is an empty object, collection, map, or set. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Array-like values such as `arguments` objects, arrays, buffers, strings, or + * jQuery-like collections are considered empty if they have a `length` of `0`. + * Similarly, maps and sets are considered empty if they have a `size` of `0`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * _.isEmpty(null); + * // => true + * + * _.isEmpty(true); + * // => true + * + * _.isEmpty(1); + * // => true + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({ 'a': 1 }); + * // => false + */ + function isEmpty(value) { + if (value == null) { + return true; + } + if (isArrayLike(value) && (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || isBuffer(value) || isTypedArray(value) || isArguments(value))) { + return !value.length; + } + var tag = getTag(value); + if (tag == mapTag || tag == setTag) { + return !value.size; + } + if (isPrototype(value)) { + return !baseKeys(value).length; + } + for (var key in value) { + if (hasOwnProperty.call(value, key)) { + return false; + } + } + return true; + } + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent. + * + * **Note:** This method supports comparing arrays, array buffers, booleans, + * date objects, error objects, maps, numbers, `Object` objects, regexes, + * sets, strings, symbols, and typed arrays. `Object` objects are compared + * by their own, not inherited, enumerable properties. Functions and DOM + * nodes are compared by strict equality, i.e. `===`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.isEqual(object, other); + * // => true + * + * object === other; + * // => false + */ + function isEqual(value, other) { + return baseIsEqual(value, other); + } + + /** + * This method is like `_.isEqual` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with up to + * six arguments: (objValue, othValue [, index|key, object, other, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, othValue) { + * if (isGreeting(objValue) && isGreeting(othValue)) { + * return true; + * } + * } + * + * var array = ['hello', 'goodbye']; + * var other = ['hi', 'goodbye']; + * + * _.isEqualWith(array, other, customizer); + * // => true + */ + function isEqualWith(value, other, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + var result = customizer ? customizer(value, other) : undefined; + return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; + } + + /** + * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, + * `SyntaxError`, `TypeError`, or `URIError` object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an error object, else `false`. + * @example + * + * _.isError(new Error); + * // => true + * + * _.isError(Error); + * // => false + */ + function isError(value) { + if (!isObjectLike(value)) { + return false; + } + var tag = baseGetTag(value); + return tag == errorTag || tag == domExcTag || typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value); + } + + /** + * Checks if `value` is a finite primitive number. + * + * **Note:** This method is based on + * [`Number.isFinite`](https://mdn.io/Number/isFinite). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. + * @example + * + * _.isFinite(3); + * // => true + * + * _.isFinite(Number.MIN_VALUE); + * // => true + * + * _.isFinite(Infinity); + * // => false + * + * _.isFinite('3'); + * // => false + */ + function isFinite(value) { + return typeof value == 'number' && nativeIsFinite(value); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + if (!isObject(value)) { + return false; + } + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 9 which returns 'object' for typed arrays and other constructors. + var tag = baseGetTag(value); + return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; + } + + /** + * Checks if `value` is an integer. + * + * **Note:** This method is based on + * [`Number.isInteger`](https://mdn.io/Number/isInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an integer, else `false`. + * @example + * + * _.isInteger(3); + * // => true + * + * _.isInteger(Number.MIN_VALUE); + * // => false + * + * _.isInteger(Infinity); + * // => false + * + * _.isInteger('3'); + * // => false + */ + function isInteger(value) { + return typeof value == 'number' && value == toInteger(value); + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return value != null && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return value != null && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Map` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + * @example + * + * _.isMap(new Map); + * // => true + * + * _.isMap(new WeakMap); + * // => false + */ + var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; + + /** + * Performs a partial deep comparison between `object` and `source` to + * determine if `object` contains equivalent property values. + * + * **Note:** This method is equivalent to `_.matches` when `source` is + * partially applied. + * + * Partial comparisons will match empty array and empty object `source` + * values against any array or object value, respectively. See `_.isEqual` + * for a list of supported value comparisons. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.isMatch(object, { 'b': 2 }); + * // => true + * + * _.isMatch(object, { 'b': 1 }); + * // => false + */ + function isMatch(object, source) { + return object === source || baseIsMatch(object, source, getMatchData(source)); + } + + /** + * This method is like `_.isMatch` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with five + * arguments: (objValue, srcValue, index|key, object, source). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, srcValue) { + * if (isGreeting(objValue) && isGreeting(srcValue)) { + * return true; + * } + * } + * + * var object = { 'greeting': 'hello' }; + * var source = { 'greeting': 'hi' }; + * + * _.isMatchWith(object, source, customizer); + * // => true + */ + function isMatchWith(object, source, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseIsMatch(object, source, getMatchData(source), customizer); + } + + /** + * Checks if `value` is `NaN`. + * + * **Note:** This method is based on + * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as + * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for + * `undefined` and other non-number values. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // An `NaN` primitive is the only value that is not equal to itself. + // Perform the `toStringTag` check first to avoid errors with some + // ActiveX objects in IE. + return isNumber(value) && value != +value; + } + + /** + * Checks if `value` is a pristine native function. + * + * **Note:** This method can't reliably detect native functions in the presence + * of the core-js package because core-js circumvents this kind of detection. + * Despite multiple requests, the core-js maintainer has made it clear: any + * attempt to fix the detection will be obstructed. As a result, we're left + * with little choice but to throw an error. Unfortunately, this also affects + * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), + * which rely on core-js. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (isMaskable(value)) { + throw new Error(CORE_ERROR_TEXT); + } + return baseIsNative(value); + } + + /** + * Checks if `value` is `null`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(void 0); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is `null` or `undefined`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is nullish, else `false`. + * @example + * + * _.isNil(null); + * // => true + * + * _.isNil(void 0); + * // => true + * + * _.isNil(NaN); + * // => false + */ + function isNil(value) { + return value == null; + } + + /** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are + * classified as numbers, use the `_.isFinite` method. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a number, else `false`. + * @example + * + * _.isNumber(3); + * // => true + * + * _.isNumber(Number.MIN_VALUE); + * // => true + * + * _.isNumber(Infinity); + * // => true + * + * _.isNumber('3'); + * // => false + */ + function isNumber(value) { + return typeof value == 'number' || isObjectLike(value) && baseGetTag(value) == numberTag; + } + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + if (!isObjectLike(value) || baseGetTag(value) != objectTag) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString; + } + + /** + * Checks if `value` is classified as a `RegExp` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + * @example + * + * _.isRegExp(/abc/); + * // => true + * + * _.isRegExp('/abc/'); + * // => false + */ + var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; + + /** + * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 + * double precision number which isn't the result of a rounded unsafe integer. + * + * **Note:** This method is based on + * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. + * @example + * + * _.isSafeInteger(3); + * // => true + * + * _.isSafeInteger(Number.MIN_VALUE); + * // => false + * + * _.isSafeInteger(Infinity); + * // => false + * + * _.isSafeInteger('3'); + * // => false + */ + function isSafeInteger(value) { + return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is classified as a `Set` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + * @example + * + * _.isSet(new Set); + * // => true + * + * _.isSet(new WeakSet); + * // => false + */ + var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; + + /** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a string, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */ + function isString(value) { + return typeof value == 'string' || !isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag; + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag; + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** + * Checks if `value` is `undefined`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + * + * _.isUndefined(null); + * // => false + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Checks if `value` is classified as a `WeakMap` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. + * @example + * + * _.isWeakMap(new WeakMap); + * // => true + * + * _.isWeakMap(new Map); + * // => false + */ + function isWeakMap(value) { + return isObjectLike(value) && getTag(value) == weakMapTag; + } + + /** + * Checks if `value` is classified as a `WeakSet` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. + * @example + * + * _.isWeakSet(new WeakSet); + * // => true + * + * _.isWeakSet(new Set); + * // => false + */ + function isWeakSet(value) { + return isObjectLike(value) && baseGetTag(value) == weakSetTag; + } + + /** + * Checks if `value` is less than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + * @see _.gt + * @example + * + * _.lt(1, 3); + * // => true + * + * _.lt(3, 3); + * // => false + * + * _.lt(3, 1); + * // => false + */ + var lt = createRelationalOperation(baseLt); + + /** + * Checks if `value` is less than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than or equal to + * `other`, else `false`. + * @see _.gte + * @example + * + * _.lte(1, 3); + * // => true + * + * _.lte(3, 3); + * // => true + * + * _.lte(3, 1); + * // => false + */ + var lte = createRelationalOperation(function (value, other) { + return value <= other; + }); + + /** + * Converts `value` to an array. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Array} Returns the converted array. + * @example + * + * _.toArray({ 'a': 1, 'b': 2 }); + * // => [1, 2] + * + * _.toArray('abc'); + * // => ['a', 'b', 'c'] + * + * _.toArray(1); + * // => [] + * + * _.toArray(null); + * // => [] + */ + function toArray(value) { + if (!value) { + return []; + } + if (isArrayLike(value)) { + return isString(value) ? stringToArray(value) : copyArray(value); + } + if (symIterator && value[symIterator]) { + return iteratorToArray(value[symIterator]()); + } + var tag = getTag(value), + func = tag == mapTag ? mapToArray : tag == setTag ? setToArray : values; + return func(value); + } + + /** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = value < 0 ? -1 : 1; + return sign * MAX_INTEGER; + } + return value === value ? value : 0; + } + + /** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toInteger(3.2); + * // => 3 + * + * _.toInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toInteger(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toInteger('3.2'); + * // => 3 + */ + function toInteger(value) { + var result = toFinite(value), + remainder = result % 1; + return result === result ? remainder ? result - remainder : result : 0; + } + + /** + * Converts `value` to an integer suitable for use as the length of an + * array-like object. + * + * **Note:** This method is based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toLength(3.2); + * // => 3 + * + * _.toLength(Number.MIN_VALUE); + * // => 0 + * + * _.toLength(Infinity); + * // => 4294967295 + * + * _.toLength('3.2'); + * // => 3 + */ + function toLength(value) { + return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0; + } + + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? other + '' : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = baseTrim(value); + var isBinary = reIsBinary.test(value); + return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value; + } + + /** + * Converts `value` to a plain object flattening inherited enumerable string + * keyed properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return copyObject(value, keysIn(value)); + } + + /** + * Converts `value` to a safe integer. A safe integer can be compared and + * represented correctly. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toSafeInteger(3.2); + * // => 3 + * + * _.toSafeInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toSafeInteger(Infinity); + * // => 9007199254740991 + * + * _.toSafeInteger('3.2'); + * // => 3 + */ + function toSafeInteger(value) { + return value ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) : value === 0 ? value : 0; + } + + /** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ + function toString(value) { + return value == null ? '' : baseToString(value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Assigns own enumerable string keyed properties of source objects to the + * destination object. Source objects are applied from left to right. + * Subsequent sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object` and is loosely based on + * [`Object.assign`](https://mdn.io/Object/assign). + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assignIn + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assign({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'c': 3 } + */ + var assign = createAssigner(function (object, source) { + if (isPrototype(source) || isArrayLike(source)) { + copyObject(source, keys(source), object); + return; + } + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + assignValue(object, key, source[key]); + } + } + }); + + /** + * This method is like `_.assign` except that it iterates over own and + * inherited source properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extend + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assign + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assignIn({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } + */ + var assignIn = createAssigner(function (object, source) { + copyObject(source, keysIn(source), object); + }); + + /** + * This method is like `_.assignIn` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extendWith + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignInWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignInWith = createAssigner(function (object, source, srcIndex, customizer) { + copyObject(source, keysIn(source), object, customizer); + }); + + /** + * This method is like `_.assign` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignInWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignWith = createAssigner(function (object, source, srcIndex, customizer) { + copyObject(source, keys(source), object, customizer); + }); + + /** + * Creates an array of values corresponding to `paths` of `object`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Array} Returns the picked values. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; + * + * _.at(object, ['a[0].b.c', 'a[1]']); + * // => [3, 4] + */ + var at = flatRest(baseAt); + + /** + * Creates an object that inherits from the `prototype` object. If a + * `properties` object is given, its own enumerable string keyed properties + * are assigned to the created object. + * + * @static + * @memberOf _ + * @since 2.3.0 + * @category Object + * @param {Object} prototype The object to inherit from. + * @param {Object} [properties] The properties to assign to the object. + * @returns {Object} Returns the new object. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * function Circle() { + * Shape.call(this); + * } + * + * Circle.prototype = _.create(Shape.prototype, { + * 'constructor': Circle + * }); + * + * var circle = new Circle; + * circle instanceof Circle; + * // => true + * + * circle instanceof Shape; + * // => true + */ + function create(prototype, properties) { + var result = baseCreate(prototype); + return properties == null ? result : baseAssign(result, properties); + } + + /** + * Assigns own and inherited enumerable string keyed properties of source + * objects to the destination object for all destination properties that + * resolve to `undefined`. Source objects are applied from left to right. + * Once a property is set, additional values of the same property are ignored. + * + * **Note:** This method mutates `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaultsDeep + * @example + * + * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var defaults = baseRest(function (object, sources) { + object = Object(object); + var index = -1; + var length = sources.length; + var guard = length > 2 ? sources[2] : undefined; + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + length = 1; + } + while (++index < length) { + var source = sources[index]; + var props = keysIn(source); + var propsIndex = -1; + var propsLength = props.length; + while (++propsIndex < propsLength) { + var key = props[propsIndex]; + var value = object[key]; + if (value === undefined || eq(value, objectProto[key]) && !hasOwnProperty.call(object, key)) { + object[key] = source[key]; + } + } + } + return object; + }); + + /** + * This method is like `_.defaults` except that it recursively assigns + * default properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaults + * @example + * + * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); + * // => { 'a': { 'b': 2, 'c': 3 } } + */ + var defaultsDeep = baseRest(function (args) { + args.push(undefined, customDefaultsMerge); + return apply(mergeWith, undefined, args); + }); + + /** + * This method is like `_.find` except that it returns the key of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findKey(users, function(o) { return o.age < 40; }); + * // => 'barney' (iteration order is not guaranteed) + * + * // The `_.matches` iteratee shorthand. + * _.findKey(users, { 'age': 1, 'active': true }); + * // => 'pebbles' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findKey(users, 'active'); + * // => 'barney' + */ + function findKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwn); + } + + /** + * This method is like `_.findKey` except that it iterates over elements of + * a collection in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findLastKey(users, function(o) { return o.age < 40; }); + * // => returns 'pebbles' assuming `_.findKey` returns 'barney' + * + * // The `_.matches` iteratee shorthand. + * _.findLastKey(users, { 'age': 36, 'active': true }); + * // => 'barney' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findLastKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findLastKey(users, 'active'); + * // => 'pebbles' + */ + function findLastKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight); + } + + /** + * Iterates over own and inherited enumerable string keyed properties of an + * object and invokes `iteratee` for each property. The iteratee is invoked + * with three arguments: (value, key, object). Iteratee functions may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forInRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forIn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). + */ + function forIn(object, iteratee) { + return object == null ? object : baseFor(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * This method is like `_.forIn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forIn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forInRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. + */ + function forInRight(object, iteratee) { + return object == null ? object : baseForRight(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * Iterates over own enumerable string keyed properties of an object and + * invokes `iteratee` for each property. The iteratee is invoked with three + * arguments: (value, key, object). Iteratee functions may exit iteration + * early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwnRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forOwn(object, iteratee) { + return object && baseForOwn(object, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.forOwn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwnRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. + */ + function forOwnRight(object, iteratee) { + return object && baseForOwnRight(object, getIteratee(iteratee, 3)); + } + + /** + * Creates an array of function property names from own enumerable properties + * of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functionsIn + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functions(new Foo); + * // => ['a', 'b'] + */ + function functions(object) { + return object == null ? [] : baseFunctions(object, keys(object)); + } + + /** + * Creates an array of function property names from own and inherited + * enumerable properties of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functions + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functionsIn(new Foo); + * // => ['a', 'b', 'c'] + */ + function functionsIn(object) { + return object == null ? [] : baseFunctions(object, keysIn(object)); + } + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct property of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = { 'a': { 'b': 2 } }; + * var other = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b'); + * // => true + * + * _.has(object, ['a', 'b']); + * // => true + * + * _.has(other, 'a'); + * // => false + */ + function has(object, path) { + return object != null && hasPath(object, path, baseHas); + } + + /** + * Checks if `path` is a direct or inherited property of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.hasIn(object, 'a'); + * // => true + * + * _.hasIn(object, 'a.b'); + * // => true + * + * _.hasIn(object, ['a', 'b']); + * // => true + * + * _.hasIn(object, 'b'); + * // => false + */ + function hasIn(object, path) { + return object != null && hasPath(object, path, baseHasIn); + } + + /** + * Creates an object composed of the inverted keys and values of `object`. + * If `object` contains duplicate values, subsequent values overwrite + * property assignments of previous values. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Object + * @param {Object} object The object to invert. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invert(object); + * // => { '1': 'c', '2': 'b' } + */ + var invert = createInverter(function (result, value, key) { + if (value != null && typeof value.toString != 'function') { + value = nativeObjectToString.call(value); + } + result[value] = key; + }, constant(identity)); + + /** + * This method is like `_.invert` except that the inverted object is generated + * from the results of running each element of `object` thru `iteratee`. The + * corresponding inverted value of each inverted key is an array of keys + * responsible for generating the inverted value. The iteratee is invoked + * with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.1.0 + * @category Object + * @param {Object} object The object to invert. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invertBy(object); + * // => { '1': ['a', 'c'], '2': ['b'] } + * + * _.invertBy(object, function(value) { + * return 'group' + value; + * }); + * // => { 'group1': ['a', 'c'], 'group2': ['b'] } + */ + var invertBy = createInverter(function (result, value, key) { + if (value != null && typeof value.toString != 'function') { + value = nativeObjectToString.call(value); + } + if (hasOwnProperty.call(result, value)) { + result[value].push(key); + } else { + result[value] = [key]; + } + }, getIteratee); + + /** + * Invokes the method at `path` of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + * @example + * + * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; + * + * _.invoke(object, 'a[0].b.c.slice', 1, 3); + * // => [2, 3] + */ + var invoke = baseRest(baseInvoke); + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); + } + + /** + * The opposite of `_.mapValues`; this method creates an object with the + * same values as `object` and keys generated by running each own enumerable + * string keyed property of `object` thru `iteratee`. The iteratee is invoked + * with three arguments: (value, key, object). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapValues + * @example + * + * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { + * return key + value; + * }); + * // => { 'a1': 1, 'b2': 2 } + */ + function mapKeys(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + baseForOwn(object, function (value, key, object) { + baseAssignValue(result, iteratee(value, key, object), value); + }); + return result; + } + + /** + * Creates an object with the same keys as `object` and values generated + * by running each own enumerable string keyed property of `object` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, key, object). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapKeys + * @example + * + * var users = { + * 'fred': { 'user': 'fred', 'age': 40 }, + * 'pebbles': { 'user': 'pebbles', 'age': 1 } + * }; + * + * _.mapValues(users, function(o) { return o.age; }); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + * + * // The `_.property` iteratee shorthand. + * _.mapValues(users, 'age'); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + */ + function mapValues(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + baseForOwn(object, function (value, key, object) { + baseAssignValue(result, key, iteratee(value, key, object)); + }); + return result; + } + + /** + * This method is like `_.assign` except that it recursively merges own and + * inherited enumerable string keyed properties of source objects into the + * destination object. Source properties that resolve to `undefined` are + * skipped if a destination value exists. Array and plain object properties + * are merged recursively. Other objects and value types are overridden by + * assignment. Source objects are applied from left to right. Subsequent + * sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * var object = { + * 'a': [{ 'b': 2 }, { 'd': 4 }] + * }; + * + * var other = { + * 'a': [{ 'c': 3 }, { 'e': 5 }] + * }; + * + * _.merge(object, other); + * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } + */ + var merge = createAssigner(function (object, source, srcIndex) { + baseMerge(object, source, srcIndex); + }); + + /** + * This method is like `_.merge` except that it accepts `customizer` which + * is invoked to produce the merged values of the destination and source + * properties. If `customizer` returns `undefined`, merging is handled by the + * method instead. The `customizer` is invoked with six arguments: + * (objValue, srcValue, key, object, source, stack). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * function customizer(objValue, srcValue) { + * if (_.isArray(objValue)) { + * return objValue.concat(srcValue); + * } + * } + * + * var object = { 'a': [1], 'b': [2] }; + * var other = { 'a': [3], 'b': [4] }; + * + * _.mergeWith(object, other, customizer); + * // => { 'a': [1, 3], 'b': [2, 4] } + */ + var mergeWith = createAssigner(function (object, source, srcIndex, customizer) { + baseMerge(object, source, srcIndex, customizer); + }); + + /** + * The opposite of `_.pick`; this method creates an object composed of the + * own and inherited enumerable property paths of `object` that are not omitted. + * + * **Note:** This method is considerably slower than `_.pick`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to omit. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omit(object, ['a', 'c']); + * // => { 'b': '2' } + */ + var omit = flatRest(function (object, paths) { + var result = {}; + if (object == null) { + return result; + } + var isDeep = false; + paths = arrayMap(paths, function (path) { + path = castPath(path, object); + isDeep || (isDeep = path.length > 1); + return path; + }); + copyObject(object, getAllKeysIn(object), result); + if (isDeep) { + result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone); + } + var length = paths.length; + while (length--) { + baseUnset(result, paths[length]); + } + return result; + }); + + /** + * The opposite of `_.pickBy`; this method creates an object composed of + * the own and inherited enumerable string keyed properties of `object` that + * `predicate` doesn't return truthy for. The predicate is invoked with two + * arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omitBy(object, _.isNumber); + * // => { 'b': '2' } + */ + function omitBy(object, predicate) { + return pickBy(object, negate(getIteratee(predicate))); + } + + /** + * Creates an object composed of the picked `object` properties. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pick(object, ['a', 'c']); + * // => { 'a': 1, 'c': 3 } + */ + var pick = flatRest(function (object, paths) { + return object == null ? {} : basePick(object, paths); + }); + + /** + * Creates an object composed of the `object` properties `predicate` returns + * truthy for. The predicate is invoked with two arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pickBy(object, _.isNumber); + * // => { 'a': 1, 'c': 3 } + */ + function pickBy(object, predicate) { + if (object == null) { + return {}; + } + var props = arrayMap(getAllKeysIn(object), function (prop) { + return [prop]; + }); + predicate = getIteratee(predicate); + return basePickBy(object, props, function (value, path) { + return predicate(value, path[0]); + }); + } + + /** + * This method is like `_.get` except that if the resolved value is a + * function it's invoked with the `this` binding of its parent object and + * its result is returned. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a[0].b.c3', 'default'); + * // => 'default' + * + * _.result(object, 'a[0].b.c3', _.constant('default')); + * // => 'default' + */ + function result(object, path, defaultValue) { + path = castPath(path, object); + var index = -1, + length = path.length; + + // Ensure the loop is entered when path is empty. + if (!length) { + length = 1; + object = undefined; + } + while (++index < length) { + var value = object == null ? undefined : object[toKey(path[index])]; + if (value === undefined) { + index = length; + value = defaultValue; + } + object = isFunction(value) ? value.call(object) : value; + } + return object; + } + + /** + * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, + * it's created. Arrays are created for missing index properties while objects + * are created for all other missing properties. Use `_.setWith` to customize + * `path` creation. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, ['x', '0', 'y', 'z'], 5); + * console.log(object.x[0].y.z); + * // => 5 + */ + function set(object, path, value) { + return object == null ? object : baseSet(object, path, value); + } + + /** + * This method is like `_.set` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.setWith(object, '[0][1]', 'a', Object); + * // => { '0': { '1': 'a' } } + */ + function setWith(object, path, value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseSet(object, path, value, customizer); + } + + /** + * Creates an array of own enumerable string keyed-value pairs for `object` + * which can be consumed by `_.fromPairs`. If `object` is a map or set, its + * entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entries + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairs(new Foo); + * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) + */ + var toPairs = createToPairs(keys); + + /** + * Creates an array of own and inherited enumerable string keyed-value pairs + * for `object` which can be consumed by `_.fromPairs`. If `object` is a map + * or set, its entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entriesIn + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairsIn(new Foo); + * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) + */ + var toPairsIn = createToPairs(keysIn); + + /** + * An alternative to `_.reduce`; this method transforms `object` to a new + * `accumulator` object which is the result of running each of its own + * enumerable string keyed properties thru `iteratee`, with each invocation + * potentially mutating the `accumulator` object. If `accumulator` is not + * provided, a new object with the same `[[Prototype]]` will be used. The + * iteratee is invoked with four arguments: (accumulator, value, key, object). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 1.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The custom accumulator value. + * @returns {*} Returns the accumulated value. + * @example + * + * _.transform([2, 3, 4], function(result, n) { + * result.push(n *= n); + * return n % 2 == 0; + * }, []); + * // => [4, 9] + * + * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { + * (result[value] || (result[value] = [])).push(key); + * }, {}); + * // => { '1': ['a', 'c'], '2': ['b'] } + */ + function transform(object, iteratee, accumulator) { + var isArr = isArray(object), + isArrLike = isArr || isBuffer(object) || isTypedArray(object); + iteratee = getIteratee(iteratee, 4); + if (accumulator == null) { + var Ctor = object && object.constructor; + if (isArrLike) { + accumulator = isArr ? new Ctor() : []; + } else if (isObject(object)) { + accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {}; + } else { + accumulator = {}; + } + } + (isArrLike ? arrayEach : baseForOwn)(object, function (value, index, object) { + return iteratee(accumulator, value, index, object); + }); + return accumulator; + } + + /** + * Removes the property at `path` of `object`. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 7 } }] }; + * _.unset(object, 'a[0].b.c'); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + * + * _.unset(object, ['a', '0', 'b', 'c']); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + */ + function unset(object, path) { + return object == null ? true : baseUnset(object, path); + } + + /** + * This method is like `_.set` except that accepts `updater` to produce the + * value to set. Use `_.updateWith` to customize `path` creation. The `updater` + * is invoked with one argument: (value). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.update(object, 'a[0].b.c', function(n) { return n * n; }); + * console.log(object.a[0].b.c); + * // => 9 + * + * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; }); + * console.log(object.x[0].y.z); + * // => 0 + */ + function update(object, path, updater) { + return object == null ? object : baseUpdate(object, path, castFunction(updater)); + } + + /** + * This method is like `_.update` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.updateWith(object, '[0][1]', _.constant('a'), Object); + * // => { '0': { '1': 'a' } } + */ + function updateWith(object, path, updater, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer); + } + + /** + * Creates an array of the own enumerable string keyed property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.values(new Foo); + * // => [1, 2] (iteration order is not guaranteed) + * + * _.values('hi'); + * // => ['h', 'i'] + */ + function values(object) { + return object == null ? [] : baseValues(object, keys(object)); + } + + /** + * Creates an array of the own and inherited enumerable string keyed property + * values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.valuesIn(new Foo); + * // => [1, 2, 3] (iteration order is not guaranteed) + */ + function valuesIn(object) { + return object == null ? [] : baseValues(object, keysIn(object)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Clamps `number` within the inclusive `lower` and `upper` bounds. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Number + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + * @example + * + * _.clamp(-10, -5, 5); + * // => -5 + * + * _.clamp(10, -5, 5); + * // => 5 + */ + function clamp(number, lower, upper) { + if (upper === undefined) { + upper = lower; + lower = undefined; + } + if (upper !== undefined) { + upper = toNumber(upper); + upper = upper === upper ? upper : 0; + } + if (lower !== undefined) { + lower = toNumber(lower); + lower = lower === lower ? lower : 0; + } + return baseClamp(toNumber(number), lower, upper); + } + + /** + * Checks if `n` is between `start` and up to, but not including, `end`. If + * `end` is not specified, it's set to `start` with `start` then set to `0`. + * If `start` is greater than `end` the params are swapped to support + * negative ranges. + * + * @static + * @memberOf _ + * @since 3.3.0 + * @category Number + * @param {number} number The number to check. + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + * @see _.range, _.rangeRight + * @example + * + * _.inRange(3, 2, 4); + * // => true + * + * _.inRange(4, 8); + * // => true + * + * _.inRange(4, 2); + * // => false + * + * _.inRange(2, 2); + * // => false + * + * _.inRange(1.2, 2); + * // => true + * + * _.inRange(5.2, 4); + * // => false + * + * _.inRange(-3, -2, -6); + * // => true + */ + function inRange(number, start, end) { + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + number = toNumber(number); + return baseInRange(number, start, end); + } + + /** + * Produces a random number between the inclusive `lower` and `upper` bounds. + * If only one argument is provided a number between `0` and the given number + * is returned. If `floating` is `true`, or either `lower` or `upper` are + * floats, a floating-point number is returned instead of an integer. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Number + * @param {number} [lower=0] The lower bound. + * @param {number} [upper=1] The upper bound. + * @param {boolean} [floating] Specify returning a floating-point number. + * @returns {number} Returns the random number. + * @example + * + * _.random(0, 5); + * // => an integer between 0 and 5 + * + * _.random(5); + * // => also an integer between 0 and 5 + * + * _.random(5, true); + * // => a floating-point number between 0 and 5 + * + * _.random(1.2, 5.2); + * // => a floating-point number between 1.2 and 5.2 + */ + function random(lower, upper, floating) { + if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { + upper = floating = undefined; + } + if (floating === undefined) { + if (typeof upper == 'boolean') { + floating = upper; + upper = undefined; + } else if (typeof lower == 'boolean') { + floating = lower; + lower = undefined; + } + } + if (lower === undefined && upper === undefined) { + lower = 0; + upper = 1; + } else { + lower = toFinite(lower); + if (upper === undefined) { + upper = lower; + lower = 0; + } else { + upper = toFinite(upper); + } + } + if (lower > upper) { + var temp = lower; + lower = upper; + upper = temp; + } + if (floating || lower % 1 || upper % 1) { + var rand = nativeRandom(); + return nativeMin(lower + rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1))), upper); + } + return baseRandom(lower, upper); + } + + /*------------------------------------------------------------------------*/ + + /** + * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the camel cased string. + * @example + * + * _.camelCase('Foo Bar'); + * // => 'fooBar' + * + * _.camelCase('--foo-bar--'); + * // => 'fooBar' + * + * _.camelCase('__FOO_BAR__'); + * // => 'fooBar' + */ + var camelCase = createCompounder(function (result, word, index) { + word = word.toLowerCase(); + return result + (index ? capitalize(word) : word); + }); + + /** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * _.capitalize('FRED'); + * // => 'Fred' + */ + function capitalize(string) { + return upperFirst(toString(string).toLowerCase()); + } + + /** + * Deburrs `string` by converting + * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) + * letters to basic Latin letters and removing + * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * _.deburr('déjà vu'); + * // => 'deja vu' + */ + function deburr(string) { + string = toString(string); + return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); + } + + /** + * Checks if `string` ends with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=string.length] The position to search up to. + * @returns {boolean} Returns `true` if `string` ends with `target`, + * else `false`. + * @example + * + * _.endsWith('abc', 'c'); + * // => true + * + * _.endsWith('abc', 'b'); + * // => false + * + * _.endsWith('abc', 'b', 2); + * // => true + */ + function endsWith(string, target, position) { + string = toString(string); + target = baseToString(target); + var length = string.length; + position = position === undefined ? length : baseClamp(toInteger(position), 0, length); + var end = position; + position -= target.length; + return position >= 0 && string.slice(position, end) == target; + } + + /** + * Converts the characters "&", "<", ">", '"', and "'" in `string` to their + * corresponding HTML entities. + * + * **Note:** No other characters are escaped. To escape additional + * characters use a third-party library like [_he_](https://mths.be/he). + * + * Though the ">" character is escaped for symmetry, characters like + * ">" and "/" don't need escaping in HTML and have no special meaning + * unless they're part of a tag or unquoted attribute value. See + * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) + * (under "semi-related fun fact") for more details. + * + * When working with HTML you should always + * [quote attribute values](http://wonko.com/post/html-escaping) to reduce + * XSS vectors. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */ + function escape(string) { + string = toString(string); + return string && reHasUnescapedHtml.test(string) ? string.replace(reUnescapedHtml, escapeHtmlChar) : string; + } + + /** + * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", + * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escapeRegExp('[lodash](https://lodash.com/)'); + * // => '\[lodash\]\(https://lodash\.com/\)' + */ + function escapeRegExp(string) { + string = toString(string); + return string && reHasRegExpChar.test(string) ? string.replace(reRegExpChar, '\\$&') : string; + } + + /** + * Converts `string` to + * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @example + * + * _.kebabCase('Foo Bar'); + * // => 'foo-bar' + * + * _.kebabCase('fooBar'); + * // => 'foo-bar' + * + * _.kebabCase('__FOO_BAR__'); + * // => 'foo-bar' + */ + var kebabCase = createCompounder(function (result, word, index) { + return result + (index ? '-' : '') + word.toLowerCase(); + }); + + /** + * Converts `string`, as space separated words, to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the lower cased string. + * @example + * + * _.lowerCase('--Foo-Bar--'); + * // => 'foo bar' + * + * _.lowerCase('fooBar'); + * // => 'foo bar' + * + * _.lowerCase('__FOO_BAR__'); + * // => 'foo bar' + */ + var lowerCase = createCompounder(function (result, word, index) { + return result + (index ? ' ' : '') + word.toLowerCase(); + }); + + /** + * Converts the first character of `string` to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.lowerFirst('Fred'); + * // => 'fred' + * + * _.lowerFirst('FRED'); + * // => 'fRED' + */ + var lowerFirst = createCaseFirst('toLowerCase'); + + /** + * Pads `string` on the left and right sides if it's shorter than `length`. + * Padding characters are truncated if they can't be evenly divided by `length`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.pad('abc', 8); + * // => ' abc ' + * + * _.pad('abc', 8, '_-'); + * // => '_-abc_-_' + * + * _.pad('abc', 3); + * // => 'abc' + */ + function pad(string, length, chars) { + string = toString(string); + length = toInteger(length); + var strLength = length ? stringSize(string) : 0; + if (!length || strLength >= length) { + return string; + } + var mid = (length - strLength) / 2; + return createPadding(nativeFloor(mid), chars) + string + createPadding(nativeCeil(mid), chars); + } + + /** + * Pads `string` on the right side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padEnd('abc', 6); + * // => 'abc ' + * + * _.padEnd('abc', 6, '_-'); + * // => 'abc_-_' + * + * _.padEnd('abc', 3); + * // => 'abc' + */ + function padEnd(string, length, chars) { + string = toString(string); + length = toInteger(length); + var strLength = length ? stringSize(string) : 0; + return length && strLength < length ? string + createPadding(length - strLength, chars) : string; + } + + /** + * Pads `string` on the left side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padStart('abc', 6); + * // => ' abc' + * + * _.padStart('abc', 6, '_-'); + * // => '_-_abc' + * + * _.padStart('abc', 3); + * // => 'abc' + */ + function padStart(string, length, chars) { + string = toString(string); + length = toInteger(length); + var strLength = length ? stringSize(string) : 0; + return length && strLength < length ? createPadding(length - strLength, chars) + string : string; + } + + /** + * Converts `string` to an integer of the specified radix. If `radix` is + * `undefined` or `0`, a `radix` of `10` is used unless `value` is a + * hexadecimal, in which case a `radix` of `16` is used. + * + * **Note:** This method aligns with the + * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category String + * @param {string} string The string to convert. + * @param {number} [radix=10] The radix to interpret `value` by. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {number} Returns the converted integer. + * @example + * + * _.parseInt('08'); + * // => 8 + * + * _.map(['6', '08', '10'], _.parseInt); + * // => [6, 8, 10] + */ + function parseInt(string, radix, guard) { + if (guard || radix == null) { + radix = 0; + } else if (radix) { + radix = +radix; + } + return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); + } + + /** + * Repeats the given string `n` times. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to repeat. + * @param {number} [n=1] The number of times to repeat the string. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {string} Returns the repeated string. + * @example + * + * _.repeat('*', 3); + * // => '***' + * + * _.repeat('abc', 2); + * // => 'abcabc' + * + * _.repeat('abc', 0); + * // => '' + */ + function repeat(string, n, guard) { + if (guard ? isIterateeCall(string, n, guard) : n === undefined) { + n = 1; + } else { + n = toInteger(n); + } + return baseRepeat(toString(string), n); + } + + /** + * Replaces matches for `pattern` in `string` with `replacement`. + * + * **Note:** This method is based on + * [`String#replace`](https://mdn.io/String/replace). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to modify. + * @param {RegExp|string} pattern The pattern to replace. + * @param {Function|string} replacement The match replacement. + * @returns {string} Returns the modified string. + * @example + * + * _.replace('Hi Fred', 'Fred', 'Barney'); + * // => 'Hi Barney' + */ + function replace() { + var args = arguments, + string = toString(args[0]); + return args.length < 3 ? string : string.replace(args[1], args[2]); + } + + /** + * Converts `string` to + * [snake case](https://en.wikipedia.org/wiki/Snake_case). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the snake cased string. + * @example + * + * _.snakeCase('Foo Bar'); + * // => 'foo_bar' + * + * _.snakeCase('fooBar'); + * // => 'foo_bar' + * + * _.snakeCase('--FOO-BAR--'); + * // => 'foo_bar' + */ + var snakeCase = createCompounder(function (result, word, index) { + return result + (index ? '_' : '') + word.toLowerCase(); + }); + + /** + * Splits `string` by `separator`. + * + * **Note:** This method is based on + * [`String#split`](https://mdn.io/String/split). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to split. + * @param {RegExp|string} separator The separator pattern to split by. + * @param {number} [limit] The length to truncate results to. + * @returns {Array} Returns the string segments. + * @example + * + * _.split('a-b-c', '-', 2); + * // => ['a', 'b'] + */ + function split(string, separator, limit) { + if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { + separator = limit = undefined; + } + limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0; + if (!limit) { + return []; + } + string = toString(string); + if (string && (typeof separator == 'string' || separator != null && !isRegExp(separator))) { + separator = baseToString(separator); + if (!separator && hasUnicode(string)) { + return castSlice(stringToArray(string), 0, limit); + } + } + return string.split(separator, limit); + } + + /** + * Converts `string` to + * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). + * + * @static + * @memberOf _ + * @since 3.1.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the start cased string. + * @example + * + * _.startCase('--foo-bar--'); + * // => 'Foo Bar' + * + * _.startCase('fooBar'); + * // => 'Foo Bar' + * + * _.startCase('__FOO_BAR__'); + * // => 'FOO BAR' + */ + var startCase = createCompounder(function (result, word, index) { + return result + (index ? ' ' : '') + upperFirst(word); + }); + + /** + * Checks if `string` starts with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=0] The position to search from. + * @returns {boolean} Returns `true` if `string` starts with `target`, + * else `false`. + * @example + * + * _.startsWith('abc', 'a'); + * // => true + * + * _.startsWith('abc', 'b'); + * // => false + * + * _.startsWith('abc', 'b', 1); + * // => true + */ + function startsWith(string, target, position) { + string = toString(string); + position = position == null ? 0 : baseClamp(toInteger(position), 0, string.length); + target = baseToString(target); + return string.slice(position, position + target.length) == target; + } + + /** + * Creates a compiled template function that can interpolate data properties + * in "interpolate" delimiters, HTML-escape interpolated data properties in + * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data + * properties may be accessed as free variables in the template. If a setting + * object is given, it takes precedence over `_.templateSettings` values. + * + * **Note:** In the development build `_.template` utilizes + * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) + * for easier debugging. + * + * For more information on precompiling templates see + * [lodash's custom builds documentation](https://lodash.com/custom-builds). + * + * For more information on Chrome extension sandboxes see + * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The template string. + * @param {Object} [options={}] The options object. + * @param {RegExp} [options.escape=_.templateSettings.escape] + * The HTML "escape" delimiter. + * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] + * The "evaluate" delimiter. + * @param {Object} [options.imports=_.templateSettings.imports] + * An object to import into the template as free variables. + * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] + * The "interpolate" delimiter. + * @param {string} [options.sourceURL='lodash.templateSources[n]'] + * The sourceURL of the compiled template. + * @param {string} [options.variable='obj'] + * The data object variable name. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the compiled template function. + * @example + * + * // Use the "interpolate" delimiter to create a compiled template. + * var compiled = _.template('hello <%= user %>!'); + * compiled({ 'user': 'fred' }); + * // => 'hello fred!' + * + * // Use the HTML "escape" delimiter to escape data property values. + * var compiled = _.template('<%- value %>'); + * compiled({ 'value': '
    \ No newline at end of file +React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js index d5043f933..844adb822 100644 --- a/pkg/webui/ui/build/static/js/main.js +++ b/pkg/webui/ui/build/static/js/main.js @@ -3233,6 +3233,478 @@ if (true) { /***/ }), +/***/ 981: +/***/ (function(module, exports, __webpack_require__) { + +var __WEBPACK_AMD_DEFINE_RESULT__;/** + * [js-sha256]{@link https://github.com/emn178/js-sha256} + * + * @version 0.9.0 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2014-2017 + * @license MIT + */ +/*jslint bitwise: true */ +(function () { + 'use strict'; + + var ERROR = 'input is invalid type'; + var WINDOW = typeof window === 'object'; + var root = WINDOW ? window : {}; + if (root.JS_SHA256_NO_WINDOW) { + WINDOW = false; + } + var WEB_WORKER = !WINDOW && typeof self === 'object'; + var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; + if (NODE_JS) { + root = __webpack_require__.g; + } else if (WEB_WORKER) { + root = self; + } + var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && "object" === 'object' && module.exports; + var AMD = true && __webpack_require__.amdO; + var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; + var HEX_CHARS = '0123456789abcdef'.split(''); + var EXTRA = [-2147483648, 8388608, 32768, 128]; + var SHIFT = [24, 16, 8, 0]; + var K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]; + var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; + var blocks = []; + if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) { + Array.isArray = function (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + }; + } + if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) { + ArrayBuffer.isView = function (obj) { + return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer; + }; + } + var createOutputMethod = function createOutputMethod(outputType, is224) { + return function (message) { + return new Sha256(is224, true).update(message)[outputType](); + }; + }; + var createMethod = function createMethod(is224) { + var method = createOutputMethod('hex', is224); + if (NODE_JS) { + method = nodeWrap(method, is224); + } + method.create = function () { + return new Sha256(is224); + }; + method.update = function (message) { + return method.create().update(message); + }; + for (var i = 0; i < OUTPUT_TYPES.length; ++i) { + var type = OUTPUT_TYPES[i]; + method[type] = createOutputMethod(type, is224); + } + return method; + }; + var nodeWrap = function nodeWrap(method, is224) { + var crypto = eval("require('crypto')"); + var Buffer = eval("require('buffer').Buffer"); + var algorithm = is224 ? 'sha224' : 'sha256'; + var nodeMethod = function nodeMethod(message) { + if (typeof message === 'string') { + return crypto.createHash(algorithm).update(message, 'utf8').digest('hex'); + } else { + if (message === null || message === undefined) { + throw new Error(ERROR); + } else if (message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } + } + if (Array.isArray(message) || ArrayBuffer.isView(message) || message.constructor === Buffer) { + return crypto.createHash(algorithm).update(new Buffer(message)).digest('hex'); + } else { + return method(message); + } + }; + return nodeMethod; + }; + var createHmacOutputMethod = function createHmacOutputMethod(outputType, is224) { + return function (key, message) { + return new HmacSha256(key, is224, true).update(message)[outputType](); + }; + }; + var createHmacMethod = function createHmacMethod(is224) { + var method = createHmacOutputMethod('hex', is224); + method.create = function (key) { + return new HmacSha256(key, is224); + }; + method.update = function (key, message) { + return method.create(key).update(message); + }; + for (var i = 0; i < OUTPUT_TYPES.length; ++i) { + var type = OUTPUT_TYPES[i]; + method[type] = createHmacOutputMethod(type, is224); + } + return method; + }; + function Sha256(is224, sharedMemory) { + if (sharedMemory) { + blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + this.blocks = blocks; + } else { + this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + if (is224) { + this.h0 = 0xc1059ed8; + this.h1 = 0x367cd507; + this.h2 = 0x3070dd17; + this.h3 = 0xf70e5939; + this.h4 = 0xffc00b31; + this.h5 = 0x68581511; + this.h6 = 0x64f98fa7; + this.h7 = 0xbefa4fa4; + } else { + // 256 + this.h0 = 0x6a09e667; + this.h1 = 0xbb67ae85; + this.h2 = 0x3c6ef372; + this.h3 = 0xa54ff53a; + this.h4 = 0x510e527f; + this.h5 = 0x9b05688c; + this.h6 = 0x1f83d9ab; + this.h7 = 0x5be0cd19; + } + this.block = this.start = this.bytes = this.hBytes = 0; + this.finalized = this.hashed = false; + this.first = true; + this.is224 = is224; + } + Sha256.prototype.update = function (message) { + if (this.finalized) { + return; + } + var notString, + type = typeof message; + if (type !== 'string') { + if (type === 'object') { + if (message === null) { + throw new Error(ERROR); + } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } else if (!Array.isArray(message)) { + if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) { + throw new Error(ERROR); + } + } + } else { + throw new Error(ERROR); + } + notString = true; + } + var code, + index = 0, + i, + length = message.length, + blocks = this.blocks; + while (index < length) { + if (this.hashed) { + this.hashed = false; + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + if (notString) { + for (i = this.start; index < length && i < 64; ++index) { + blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]; + } + } else { + for (i = this.start; index < length && i < 64; ++index) { + code = message.charCodeAt(index); + if (code < 0x80) { + blocks[i >> 2] |= code << SHIFT[i++ & 3]; + } else if (code < 0x800) { + blocks[i >> 2] |= (0xc0 | code >> 6) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3]; + } else if (code < 0xd800 || code >= 0xe000) { + blocks[i >> 2] |= (0xe0 | code >> 12) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3]; + } else { + code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++index) & 0x3ff); + blocks[i >> 2] |= (0xf0 | code >> 18) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | code >> 12 & 0x3f) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3]; + } + } + } + this.lastByteIndex = i; + this.bytes += i - this.start; + if (i >= 64) { + this.block = blocks[16]; + this.start = i - 64; + this.hash(); + this.hashed = true; + } else { + this.start = i; + } + } + if (this.bytes > 4294967295) { + this.hBytes += this.bytes / 4294967296 << 0; + this.bytes = this.bytes % 4294967296; + } + return this; + }; + Sha256.prototype.finalize = function () { + if (this.finalized) { + return; + } + this.finalized = true; + var blocks = this.blocks, + i = this.lastByteIndex; + blocks[16] = this.block; + blocks[i >> 2] |= EXTRA[i & 3]; + this.block = blocks[16]; + if (i >= 56) { + if (!this.hashed) { + this.hash(); + } + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + blocks[14] = this.hBytes << 3 | this.bytes >>> 29; + blocks[15] = this.bytes << 3; + this.hash(); + }; + Sha256.prototype.hash = function () { + var a = this.h0, + b = this.h1, + c = this.h2, + d = this.h3, + e = this.h4, + f = this.h5, + g = this.h6, + h = this.h7, + blocks = this.blocks, + j, + s0, + s1, + maj, + t1, + t2, + ch, + ab, + da, + cd, + bc; + for (j = 16; j < 64; ++j) { + // rightrotate + t1 = blocks[j - 15]; + s0 = (t1 >>> 7 | t1 << 25) ^ (t1 >>> 18 | t1 << 14) ^ t1 >>> 3; + t1 = blocks[j - 2]; + s1 = (t1 >>> 17 | t1 << 15) ^ (t1 >>> 19 | t1 << 13) ^ t1 >>> 10; + blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0; + } + bc = b & c; + for (j = 0; j < 64; j += 4) { + if (this.first) { + if (this.is224) { + ab = 300032; + t1 = blocks[0] - 1413257819; + h = t1 - 150054599 << 0; + d = t1 + 24177077 << 0; + } else { + ab = 704751109; + t1 = blocks[0] - 210244248; + h = t1 - 1521486534 << 0; + d = t1 + 143694565 << 0; + } + this.first = false; + } else { + s0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10); + s1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7); + ab = a & b; + maj = ab ^ a & c ^ bc; + ch = e & f ^ ~e & g; + t1 = h + s1 + ch + K[j] + blocks[j]; + t2 = s0 + maj; + h = d + t1 << 0; + d = t1 + t2 << 0; + } + s0 = (d >>> 2 | d << 30) ^ (d >>> 13 | d << 19) ^ (d >>> 22 | d << 10); + s1 = (h >>> 6 | h << 26) ^ (h >>> 11 | h << 21) ^ (h >>> 25 | h << 7); + da = d & a; + maj = da ^ d & b ^ ab; + ch = h & e ^ ~h & f; + t1 = g + s1 + ch + K[j + 1] + blocks[j + 1]; + t2 = s0 + maj; + g = c + t1 << 0; + c = t1 + t2 << 0; + s0 = (c >>> 2 | c << 30) ^ (c >>> 13 | c << 19) ^ (c >>> 22 | c << 10); + s1 = (g >>> 6 | g << 26) ^ (g >>> 11 | g << 21) ^ (g >>> 25 | g << 7); + cd = c & d; + maj = cd ^ c & a ^ da; + ch = g & h ^ ~g & e; + t1 = f + s1 + ch + K[j + 2] + blocks[j + 2]; + t2 = s0 + maj; + f = b + t1 << 0; + b = t1 + t2 << 0; + s0 = (b >>> 2 | b << 30) ^ (b >>> 13 | b << 19) ^ (b >>> 22 | b << 10); + s1 = (f >>> 6 | f << 26) ^ (f >>> 11 | f << 21) ^ (f >>> 25 | f << 7); + bc = b & c; + maj = bc ^ b & d ^ cd; + ch = f & g ^ ~f & h; + t1 = e + s1 + ch + K[j + 3] + blocks[j + 3]; + t2 = s0 + maj; + e = a + t1 << 0; + a = t1 + t2 << 0; + } + this.h0 = this.h0 + a << 0; + this.h1 = this.h1 + b << 0; + this.h2 = this.h2 + c << 0; + this.h3 = this.h3 + d << 0; + this.h4 = this.h4 + e << 0; + this.h5 = this.h5 + f << 0; + this.h6 = this.h6 + g << 0; + this.h7 = this.h7 + h << 0; + }; + Sha256.prototype.hex = function () { + this.finalize(); + var h0 = this.h0, + h1 = this.h1, + h2 = this.h2, + h3 = this.h3, + h4 = this.h4, + h5 = this.h5, + h6 = this.h6, + h7 = this.h7; + var hex = HEX_CHARS[h0 >> 28 & 0x0F] + HEX_CHARS[h0 >> 24 & 0x0F] + HEX_CHARS[h0 >> 20 & 0x0F] + HEX_CHARS[h0 >> 16 & 0x0F] + HEX_CHARS[h0 >> 12 & 0x0F] + HEX_CHARS[h0 >> 8 & 0x0F] + HEX_CHARS[h0 >> 4 & 0x0F] + HEX_CHARS[h0 & 0x0F] + HEX_CHARS[h1 >> 28 & 0x0F] + HEX_CHARS[h1 >> 24 & 0x0F] + HEX_CHARS[h1 >> 20 & 0x0F] + HEX_CHARS[h1 >> 16 & 0x0F] + HEX_CHARS[h1 >> 12 & 0x0F] + HEX_CHARS[h1 >> 8 & 0x0F] + HEX_CHARS[h1 >> 4 & 0x0F] + HEX_CHARS[h1 & 0x0F] + HEX_CHARS[h2 >> 28 & 0x0F] + HEX_CHARS[h2 >> 24 & 0x0F] + HEX_CHARS[h2 >> 20 & 0x0F] + HEX_CHARS[h2 >> 16 & 0x0F] + HEX_CHARS[h2 >> 12 & 0x0F] + HEX_CHARS[h2 >> 8 & 0x0F] + HEX_CHARS[h2 >> 4 & 0x0F] + HEX_CHARS[h2 & 0x0F] + HEX_CHARS[h3 >> 28 & 0x0F] + HEX_CHARS[h3 >> 24 & 0x0F] + HEX_CHARS[h3 >> 20 & 0x0F] + HEX_CHARS[h3 >> 16 & 0x0F] + HEX_CHARS[h3 >> 12 & 0x0F] + HEX_CHARS[h3 >> 8 & 0x0F] + HEX_CHARS[h3 >> 4 & 0x0F] + HEX_CHARS[h3 & 0x0F] + HEX_CHARS[h4 >> 28 & 0x0F] + HEX_CHARS[h4 >> 24 & 0x0F] + HEX_CHARS[h4 >> 20 & 0x0F] + HEX_CHARS[h4 >> 16 & 0x0F] + HEX_CHARS[h4 >> 12 & 0x0F] + HEX_CHARS[h4 >> 8 & 0x0F] + HEX_CHARS[h4 >> 4 & 0x0F] + HEX_CHARS[h4 & 0x0F] + HEX_CHARS[h5 >> 28 & 0x0F] + HEX_CHARS[h5 >> 24 & 0x0F] + HEX_CHARS[h5 >> 20 & 0x0F] + HEX_CHARS[h5 >> 16 & 0x0F] + HEX_CHARS[h5 >> 12 & 0x0F] + HEX_CHARS[h5 >> 8 & 0x0F] + HEX_CHARS[h5 >> 4 & 0x0F] + HEX_CHARS[h5 & 0x0F] + HEX_CHARS[h6 >> 28 & 0x0F] + HEX_CHARS[h6 >> 24 & 0x0F] + HEX_CHARS[h6 >> 20 & 0x0F] + HEX_CHARS[h6 >> 16 & 0x0F] + HEX_CHARS[h6 >> 12 & 0x0F] + HEX_CHARS[h6 >> 8 & 0x0F] + HEX_CHARS[h6 >> 4 & 0x0F] + HEX_CHARS[h6 & 0x0F]; + if (!this.is224) { + hex += HEX_CHARS[h7 >> 28 & 0x0F] + HEX_CHARS[h7 >> 24 & 0x0F] + HEX_CHARS[h7 >> 20 & 0x0F] + HEX_CHARS[h7 >> 16 & 0x0F] + HEX_CHARS[h7 >> 12 & 0x0F] + HEX_CHARS[h7 >> 8 & 0x0F] + HEX_CHARS[h7 >> 4 & 0x0F] + HEX_CHARS[h7 & 0x0F]; + } + return hex; + }; + Sha256.prototype.toString = Sha256.prototype.hex; + Sha256.prototype.digest = function () { + this.finalize(); + var h0 = this.h0, + h1 = this.h1, + h2 = this.h2, + h3 = this.h3, + h4 = this.h4, + h5 = this.h5, + h6 = this.h6, + h7 = this.h7; + var arr = [h0 >> 24 & 0xFF, h0 >> 16 & 0xFF, h0 >> 8 & 0xFF, h0 & 0xFF, h1 >> 24 & 0xFF, h1 >> 16 & 0xFF, h1 >> 8 & 0xFF, h1 & 0xFF, h2 >> 24 & 0xFF, h2 >> 16 & 0xFF, h2 >> 8 & 0xFF, h2 & 0xFF, h3 >> 24 & 0xFF, h3 >> 16 & 0xFF, h3 >> 8 & 0xFF, h3 & 0xFF, h4 >> 24 & 0xFF, h4 >> 16 & 0xFF, h4 >> 8 & 0xFF, h4 & 0xFF, h5 >> 24 & 0xFF, h5 >> 16 & 0xFF, h5 >> 8 & 0xFF, h5 & 0xFF, h6 >> 24 & 0xFF, h6 >> 16 & 0xFF, h6 >> 8 & 0xFF, h6 & 0xFF]; + if (!this.is224) { + arr.push(h7 >> 24 & 0xFF, h7 >> 16 & 0xFF, h7 >> 8 & 0xFF, h7 & 0xFF); + } + return arr; + }; + Sha256.prototype.array = Sha256.prototype.digest; + Sha256.prototype.arrayBuffer = function () { + this.finalize(); + var buffer = new ArrayBuffer(this.is224 ? 28 : 32); + var dataView = new DataView(buffer); + dataView.setUint32(0, this.h0); + dataView.setUint32(4, this.h1); + dataView.setUint32(8, this.h2); + dataView.setUint32(12, this.h3); + dataView.setUint32(16, this.h4); + dataView.setUint32(20, this.h5); + dataView.setUint32(24, this.h6); + if (!this.is224) { + dataView.setUint32(28, this.h7); + } + return buffer; + }; + function HmacSha256(key, is224, sharedMemory) { + var i, + type = typeof key; + if (type === 'string') { + var bytes = [], + length = key.length, + index = 0, + code; + for (i = 0; i < length; ++i) { + code = key.charCodeAt(i); + if (code < 0x80) { + bytes[index++] = code; + } else if (code < 0x800) { + bytes[index++] = 0xc0 | code >> 6; + bytes[index++] = 0x80 | code & 0x3f; + } else if (code < 0xd800 || code >= 0xe000) { + bytes[index++] = 0xe0 | code >> 12; + bytes[index++] = 0x80 | code >> 6 & 0x3f; + bytes[index++] = 0x80 | code & 0x3f; + } else { + code = 0x10000 + ((code & 0x3ff) << 10 | key.charCodeAt(++i) & 0x3ff); + bytes[index++] = 0xf0 | code >> 18; + bytes[index++] = 0x80 | code >> 12 & 0x3f; + bytes[index++] = 0x80 | code >> 6 & 0x3f; + bytes[index++] = 0x80 | code & 0x3f; + } + } + key = bytes; + } else { + if (type === 'object') { + if (key === null) { + throw new Error(ERROR); + } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) { + key = new Uint8Array(key); + } else if (!Array.isArray(key)) { + if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) { + throw new Error(ERROR); + } + } + } else { + throw new Error(ERROR); + } + } + if (key.length > 64) { + key = new Sha256(is224, true).update(key).array(); + } + var oKeyPad = [], + iKeyPad = []; + for (i = 0; i < 64; ++i) { + var b = key[i] || 0; + oKeyPad[i] = 0x5c ^ b; + iKeyPad[i] = 0x36 ^ b; + } + Sha256.call(this, is224, sharedMemory); + this.update(iKeyPad); + this.oKeyPad = oKeyPad; + this.inner = true; + this.sharedMemory = sharedMemory; + } + HmacSha256.prototype = new Sha256(); + HmacSha256.prototype.finalize = function () { + Sha256.prototype.finalize.call(this); + if (this.inner) { + this.inner = false; + var innerHash = this.array(); + Sha256.call(this, this.is224, this.sharedMemory); + this.update(this.oKeyPad); + this.update(innerHash); + Sha256.prototype.finalize.call(this); + } + }; + var exports = createMethod(); + exports.sha256 = exports; + exports.sha224 = createMethod(true); + exports.sha256.hmac = createHmacMethod(); + exports.sha224.hmac = createHmacMethod(true); + if (COMMON_JS) { + module.exports = exports; + } else { + root.sha256 = exports.sha256; + root.sha224 = exports.sha224; + if (AMD) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = (function () { + return exports; + }).call(exports, __webpack_require__, exports, module), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } + } +})(); + +/***/ }), + /***/ 763: /***/ (function(module, exports, __webpack_require__) { @@ -28614,6 +29086,11 @@ module.exports = _unsupportedIterableToArray, module.exports.__esModule = true, /******/ __webpack_require__.m = __webpack_modules__; /******/ /************************************************************************/ +/******/ /* webpack/runtime/amd options */ +/******/ !function() { +/******/ __webpack_require__.amdO = {}; +/******/ }(); +/******/ /******/ /* webpack/runtime/compat get default export */ /******/ !function() { /******/ // getDefaultExport function for compatibility with non-harmony modules @@ -51555,11 +52032,11 @@ var loadedScripts=new Map();var loadScript=function loadScript(fileUrl){var asyn ;// CONCATENATED MODULE: ./src/utils/misc.ts function getLastPathElement(url){if(url===undefined){return undefined;}if(!url){return"";}var s=url.split("/");return s[s.length-1];}function sleep(ms){return new Promise(function(resolve){return setTimeout(resolve,ms);});} ;// CONCATENATED MODULE: ./src/api.tsx -var apiUrl="/api";var staticPath="./staticdata";var ObjectType=/*#__PURE__*/function(ObjectType){ObjectType["Rendered"]="rendered";ObjectType["Remote"]="remote";ObjectType["Applied"]="applied";return ObjectType;}({});var RealOrStaticApi=/*#__PURE__*/function(){function RealOrStaticApi(){classCallCheck_classCallCheck(this,RealOrStaticApi);this.api=void 0;this.api=this.buildApi();}createClass_createClass(RealOrStaticApi,[{key:"buildApi",value:function(){var _buildApi=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var p;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:p=loadScript(staticPath+"/summaries.js");_context.prev=1;_context.next=4;return p;case 4:return _context.abrupt("return",new StaticApi());case 7:_context.prev=7;_context.t0=_context["catch"](1);return _context.abrupt("return",new RealApi());case 10:case"end":return _context.stop();}},_callee,null,[[1,7]]);}));function buildApi(){return _buildApi.apply(this,arguments);}return buildApi;}()},{key:"deployNow",value:function(){var _deployNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee2(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee2$(_context2){while(1)switch(_context2.prev=_context2.next){case 0:_context2.next=2;return this.api;case 2:return _context2.abrupt("return",_context2.sent.deployNow(cluster,name,namespace));case 3:case"end":return _context2.stop();}},_callee2,this);}));function deployNow(_x,_x2,_x3){return _deployNow.apply(this,arguments);}return deployNow;}()},{key:"getResult",value:function(){var _getResult=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee3(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee3$(_context3){while(1)switch(_context3.prev=_context3.next){case 0:_context3.next=2;return this.api;case 2:return _context3.abrupt("return",_context3.sent.getResult(resultId));case 3:case"end":return _context3.stop();}},_callee3,this);}));function getResult(_x4){return _getResult.apply(this,arguments);}return getResult;}()},{key:"getResultObject",value:function(){var _getResultObject=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee4(resultId,ref,objectType){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee4$(_context4){while(1)switch(_context4.prev=_context4.next){case 0:_context4.next=2;return this.api;case 2:return _context4.abrupt("return",_context4.sent.getResultObject(resultId,ref,objectType));case 3:case"end":return _context4.stop();}},_callee4,this);}));function getResultObject(_x5,_x6,_x7){return _getResultObject.apply(this,arguments);}return getResultObject;}()},{key:"getResultSummary",value:function(){var _getResultSummary=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee5(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee5$(_context5){while(1)switch(_context5.prev=_context5.next){case 0:_context5.next=2;return this.api;case 2:return _context5.abrupt("return",_context5.sent.getResultSummary(resultId));case 3:case"end":return _context5.stop();}},_callee5,this);}));function getResultSummary(_x8){return _getResultSummary.apply(this,arguments);}return getResultSummary;}()},{key:"getShortNames",value:function(){var _getShortNames=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee6(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee6$(_context6){while(1)switch(_context6.prev=_context6.next){case 0:_context6.next=2;return this.api;case 2:return _context6.abrupt("return",_context6.sent.getShortNames());case 3:case"end":return _context6.stop();}},_callee6,this);}));function getShortNames(){return _getShortNames.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee7(filterProject,filterSubDir,handle){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee7$(_context7){while(1)switch(_context7.prev=_context7.next){case 0:_context7.next=2;return this.api;case 2:return _context7.abrupt("return",_context7.sent.listenUpdates(filterProject,filterSubDir,handle));case 3:case"end":return _context7.stop();}},_callee7,this);}));function listenUpdates(_x9,_x10,_x11){return _listenUpdates.apply(this,arguments);}return listenUpdates;}()},{key:"reconcileNow",value:function(){var _reconcileNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee8(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee8$(_context8){while(1)switch(_context8.prev=_context8.next){case 0:_context8.next=2;return this.api;case 2:return _context8.abrupt("return",_context8.sent.reconcileNow(cluster,name,namespace));case 3:case"end":return _context8.stop();}},_callee8,this);}));function reconcileNow(_x12,_x13,_x14){return _reconcileNow.apply(this,arguments);}return reconcileNow;}()},{key:"validateNow",value:function(){var _validateNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee9(project,target){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee9$(_context9){while(1)switch(_context9.prev=_context9.next){case 0:_context9.next=2;return this.api;case 2:return _context9.abrupt("return",_context9.sent.validateNow(project,target));case 3:case"end":return _context9.stop();}},_callee9,this);}));function validateNow(_x15,_x16){return _validateNow.apply(this,arguments);}return validateNow;}()}]);return RealOrStaticApi;}();var api=new RealOrStaticApi();var RealApi=/*#__PURE__*/function(){function RealApi(){classCallCheck_classCallCheck(this,RealApi);}createClass_createClass(RealApi,[{key:"getShortNames",value:function(){var _getShortNames2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee10(){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee10$(_context10){while(1)switch(_context10.prev=_context10.next){case 0:url="".concat(apiUrl,"/getShortNames");return _context10.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.json();}));case 2:case"end":return _context10.stop();}},_callee10);}));function getShortNames(){return _getShortNames2.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee11(filterProject,filterSubDir,handle){var host,url,params,ws,cancelled,connect;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee11$(_context11){while(1)switch(_context11.prev=_context11.next){case 0:host=window.location.host;if(false){}url="ws://".concat(host).concat(apiUrl,"/ws");params=new URLSearchParams();if(filterProject){params.set("filterProject",filterProject);}if(filterSubDir){params.set("filterSubDir",filterSubDir);}url+="?"+params.toString();cancelled=false;connect=function connect(){if(cancelled){return;}console.log("ws connect: "+url);ws=new WebSocket(url);ws.onopen=function(){console.log("ws connected");};ws.onclose=function(event){console.log("ws close");if(!cancelled){sleep(5000).then(connect);}};ws.onmessage=function(event){if(cancelled){return;}var msg=JSON.parse(event.data);handle(msg);};};connect();return _context11.abrupt("return",function(){console.log("ws cancel");cancelled=true;if(ws){ws.close();}});case 11:case"end":return _context11.stop();}},_callee11);}));function listenUpdates(_x17,_x18,_x19){return _listenUpdates2.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee12(resultId){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee12$(_context12){while(1)switch(_context12.prev=_context12.next){case 0:url="".concat(apiUrl,"/getResult?resultId=").concat(resultId);return _context12.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.text();}).then(function(json){return new CommandResult(json);}));case 2:case"end":return _context12.stop();}},_callee12);}));function getResult(_x20){return _getResult2.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee13(resultId){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee13$(_context13){while(1)switch(_context13.prev=_context13.next){case 0:url="".concat(apiUrl,"/getResultSummary?resultId=").concat(resultId);return _context13.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.text();}).then(function(json){return new CommandResultSummary(json);}));case 2:case"end":return _context13.stop();}},_callee13);}));function getResultSummary(_x21){return _getResultSummary2.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee14(resultId,ref,objectType){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee14$(_context14){while(1)switch(_context14.prev=_context14.next){case 0:url="".concat(apiUrl,"/getResultObject?resultId=").concat(resultId,"&").concat(buildRefParams(ref),"&objectType=").concat(objectType);return _context14.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.json();}));case 2:case"end":return _context14.stop();}},_callee14);}));function getResultObject(_x22,_x23,_x24){return _getResultObject2.apply(this,arguments);}return getResultObject;}()},{key:"doPost",value:function(){var _doPost=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee15(f,body){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee15$(_context15){while(1)switch(_context15.prev=_context15.next){case 0:url="".concat(apiUrl,"/").concat(f);return _context15.abrupt("return",fetch(url,{method:"POST",body:JSON.stringify(body),headers:{'Accept':'application/json','Content-Type':'application/json'}}).then(handleErrors));case 2:case"end":return _context15.stop();}},_callee15);}));function doPost(_x25,_x26){return _doPost.apply(this,arguments);}return doPost;}()},{key:"validateNow",value:function(){var _validateNow2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee16(project,target){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee16$(_context16){while(1)switch(_context16.prev=_context16.next){case 0:return _context16.abrupt("return",this.doPost("validateNow",{"project":project,"target":target}));case 1:case"end":return _context16.stop();}},_callee16,this);}));function validateNow(_x27,_x28){return _validateNow2.apply(this,arguments);}return validateNow;}()},{key:"deployNow",value:function(){var _deployNow2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee17(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee17$(_context17){while(1)switch(_context17.prev=_context17.next){case 0:return _context17.abrupt("return",this.doPost("deployNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context17.stop();}},_callee17,this);}));function deployNow(_x29,_x30,_x31){return _deployNow2.apply(this,arguments);}return deployNow;}()},{key:"reconcileNow",value:function(){var _reconcileNow2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee18(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee18$(_context18){while(1)switch(_context18.prev=_context18.next){case 0:return _context18.abrupt("return",this.doPost("reconcileNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context18.stop();}},_callee18,this);}));function reconcileNow(_x32,_x33,_x34){return _reconcileNow2.apply(this,arguments);}return reconcileNow;}()}]);return RealApi;}();var StaticApi=/*#__PURE__*/function(){function StaticApi(){classCallCheck_classCallCheck(this,StaticApi);}createClass_createClass(StaticApi,[{key:"getShortNames",value:function(){var _getShortNames3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee19(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee19$(_context19){while(1)switch(_context19.prev=_context19.next){case 0:_context19.next=2;return loadScript(staticPath+"/shortnames.js");case 2:return _context19.abrupt("return",staticShortNames);case 3:case"end":return _context19.stop();}},_callee19);}));function getShortNames(){return _getShortNames3.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee20(filterProject,filterSubDir,handle){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee20$(_context20){while(1)switch(_context20.prev=_context20.next){case 0:_context20.next=2;return loadScript(staticPath+"/summaries.js");case 2:staticSummaries.forEach(function(rs){if(filterProject&&filterProject!==rs.project.normalizedGitUrl){return;}if(filterSubDir&&filterSubDir!==rs.project.subDir){return;}handle({"type":"update_summary","summary":rs});});return _context20.abrupt("return",function(){});case 4:case"end":return _context20.stop();}},_callee20);}));function listenUpdates(_x35,_x36,_x37){return _listenUpdates3.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee21(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee21$(_context21){while(1)switch(_context21.prev=_context21.next){case 0:_context21.next=2;return loadScript(staticPath+"/result-".concat(resultId,".js"));case 2:return _context21.abrupt("return",staticResults.get(resultId));case 3:case"end":return _context21.stop();}},_callee21);}));function getResult(_x38){return _getResult3.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee22(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee22$(_context22){while(1)switch(_context22.prev=_context22.next){case 0:_context22.next=2;return loadScript(staticPath+"/summaries.js");case 2:return _context22.abrupt("return",staticSummaries.filter(function(s){return s.id===resultId;}).at(0));case 3:case"end":return _context22.stop();}},_callee22);}));function getResultSummary(_x39){return _getResultSummary3.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee23(resultId,ref,objectType){var _result$objects;var result,object;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee23$(_context23){while(1)switch(_context23.prev=_context23.next){case 0:_context23.next=2;return this.getResult(resultId);case 2:result=_context23.sent;object=(_result$objects=result.objects)===null||_result$objects===void 0?void 0:_result$objects.find(function(x){return lodash_default().isEqual(x.ref,ref);});if(object){_context23.next=6;break;}throw new Error("object not found");case 6:_context23.t0=objectType;_context23.next=_context23.t0===ObjectType.Rendered?9:_context23.t0===ObjectType.Remote?10:_context23.t0===ObjectType.Applied?11:12;break;case 9:return _context23.abrupt("return",object.rendered);case 10:return _context23.abrupt("return",object.remote);case 11:return _context23.abrupt("return",object.applied);case 12:throw new Error("unknown object type "+objectType);case 13:case"end":return _context23.stop();}},_callee23,this);}));function getResultObject(_x40,_x41,_x42){return _getResultObject3.apply(this,arguments);}return getResultObject;}()},{key:"validateNow",value:function validateNow(project,target){throw new Error("not implemented");}},{key:"reconcileNow",value:function reconcileNow(cluster,name,namespace){throw new Error("not implemented");}},{key:"deployNow",value:function deployNow(cluster,name,namespace){throw new Error("not implemented");}}]);return StaticApi;}();function handleErrors(response){if(!response.ok){throw Error(response.statusText);}return response;}function buildRefParams(ref){var params=new URLSearchParams();params.set("kind",ref.kind);params.set("name",ref.name);if(ref.group){params.set("group",ref.group);}if(ref.version){params.set("version",ref.version);}if(ref.namespace){params.set("namespace",ref.namespace);}return params.toString();}function buildRefString(ref){if(ref.namespace){return"".concat(ref.namespace,"/").concat(ref.kind,"/").concat(ref.name);}else{if(ref.name){return"".concat(ref.kind,"/").concat(ref.name);}else{return ref.kind;}}}function buildRefKindElement(ref,element){var tooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{zIndex:1000,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["ApiVersion: ",[ref.group,ref.version].filter(function(x){return x;}).join("/")]}),/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["Kind: ",ref.kind]})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip,children:element?element:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.kind})});}function buildObjectRefFromObject(obj){var apiVersion=obj.apiVersion;var s=apiVersion.split("/",2);var ref=new ObjectRef();if(s.length===1){ref.version=s[0];}else{ref.group=s[0];ref.version=s[1];}ref.kind=obj.kind;ref.namespace=obj.metadata.namespace;ref.name=obj.metadata.name;return ref;}function findObjectByRef(l,ref,filter){return l===null||l===void 0?void 0:l.find(function(x){if(filter&&!filter(x)){return false;}return lodash_default().isEqual(x.ref,ref);});}function usePromise(p){if(p===undefined){throw new Promise(function(){return undefined;});}var promise=p;if(promise.status==='fulfilled'){return promise.value;}else if(promise.status==='rejected'){throw promise.reason;}else if(promise.status==='pending'){throw promise;}else{promise.status='pending';p.then(function(result){promise.status='fulfilled';promise.value=result;},function(reason){promise.status='rejected';promise.reason=reason;});throw promise;}} +var apiUrl="/api";var staticPath="./staticdata";var ObjectType=/*#__PURE__*/function(ObjectType){ObjectType["Rendered"]="rendered";ObjectType["Remote"]="remote";ObjectType["Applied"]="applied";return ObjectType;}({});var RealOrStaticApi=/*#__PURE__*/function(){function RealOrStaticApi(){classCallCheck_classCallCheck(this,RealOrStaticApi);this.api=void 0;this.api=this.buildApi();}createClass_createClass(RealOrStaticApi,[{key:"buildApi",value:function(){var _buildApi=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var p;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:p=loadScript(staticPath+"/summaries.js");_context.prev=1;_context.next=4;return p;case 4:return _context.abrupt("return",new StaticApi());case 7:_context.prev=7;_context.t0=_context["catch"](1);return _context.abrupt("return",new RealApi());case 10:case"end":return _context.stop();}},_callee,null,[[1,7]]);}));function buildApi(){return _buildApi.apply(this,arguments);}return buildApi;}()},{key:"deployNow",value:function(){var _deployNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee2(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee2$(_context2){while(1)switch(_context2.prev=_context2.next){case 0:_context2.next=2;return this.api;case 2:return _context2.abrupt("return",_context2.sent.deployNow(cluster,name,namespace));case 3:case"end":return _context2.stop();}},_callee2,this);}));function deployNow(_x,_x2,_x3){return _deployNow.apply(this,arguments);}return deployNow;}()},{key:"getResult",value:function(){var _getResult=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee3(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee3$(_context3){while(1)switch(_context3.prev=_context3.next){case 0:_context3.next=2;return this.api;case 2:return _context3.abrupt("return",_context3.sent.getResult(resultId));case 3:case"end":return _context3.stop();}},_callee3,this);}));function getResult(_x4){return _getResult.apply(this,arguments);}return getResult;}()},{key:"getResultObject",value:function(){var _getResultObject=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee4(resultId,ref,objectType){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee4$(_context4){while(1)switch(_context4.prev=_context4.next){case 0:_context4.next=2;return this.api;case 2:return _context4.abrupt("return",_context4.sent.getResultObject(resultId,ref,objectType));case 3:case"end":return _context4.stop();}},_callee4,this);}));function getResultObject(_x5,_x6,_x7){return _getResultObject.apply(this,arguments);}return getResultObject;}()},{key:"getResultSummary",value:function(){var _getResultSummary=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee5(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee5$(_context5){while(1)switch(_context5.prev=_context5.next){case 0:_context5.next=2;return this.api;case 2:return _context5.abrupt("return",_context5.sent.getResultSummary(resultId));case 3:case"end":return _context5.stop();}},_callee5,this);}));function getResultSummary(_x8){return _getResultSummary.apply(this,arguments);}return getResultSummary;}()},{key:"getShortNames",value:function(){var _getShortNames=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee6(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee6$(_context6){while(1)switch(_context6.prev=_context6.next){case 0:_context6.next=2;return this.api;case 2:return _context6.abrupt("return",_context6.sent.getShortNames());case 3:case"end":return _context6.stop();}},_callee6,this);}));function getShortNames(){return _getShortNames.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee7(filterProject,filterSubDir,handle){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee7$(_context7){while(1)switch(_context7.prev=_context7.next){case 0:_context7.next=2;return this.api;case 2:return _context7.abrupt("return",_context7.sent.listenUpdates(filterProject,filterSubDir,handle));case 3:case"end":return _context7.stop();}},_callee7,this);}));function listenUpdates(_x9,_x10,_x11){return _listenUpdates.apply(this,arguments);}return listenUpdates;}()},{key:"reconcileNow",value:function(){var _reconcileNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee8(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee8$(_context8){while(1)switch(_context8.prev=_context8.next){case 0:_context8.next=2;return this.api;case 2:return _context8.abrupt("return",_context8.sent.reconcileNow(cluster,name,namespace));case 3:case"end":return _context8.stop();}},_callee8,this);}));function reconcileNow(_x12,_x13,_x14){return _reconcileNow.apply(this,arguments);}return reconcileNow;}()},{key:"validateNow",value:function(){var _validateNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee9(project,target){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee9$(_context9){while(1)switch(_context9.prev=_context9.next){case 0:_context9.next=2;return this.api;case 2:return _context9.abrupt("return",_context9.sent.validateNow(project,target));case 3:case"end":return _context9.stop();}},_callee9,this);}));function validateNow(_x15,_x16){return _validateNow.apply(this,arguments);}return validateNow;}()}]);return RealOrStaticApi;}();var api=new RealOrStaticApi();var RealApi=/*#__PURE__*/function(){function RealApi(){classCallCheck_classCallCheck(this,RealApi);}createClass_createClass(RealApi,[{key:"getShortNames",value:function(){var _getShortNames2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee10(){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee10$(_context10){while(1)switch(_context10.prev=_context10.next){case 0:url="".concat(apiUrl,"/getShortNames");return _context10.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.json();}));case 2:case"end":return _context10.stop();}},_callee10);}));function getShortNames(){return _getShortNames2.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee11(filterProject,filterSubDir,handle){var host,url,params,ws,cancelled,connect;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee11$(_context11){while(1)switch(_context11.prev=_context11.next){case 0:host=window.location.host;if(false){}url="ws://".concat(host).concat(apiUrl,"/ws");params=new URLSearchParams();if(filterProject){params.set("filterProject",filterProject);}if(filterSubDir){params.set("filterSubDir",filterSubDir);}url+="?"+params.toString();cancelled=false;connect=function connect(){if(cancelled){return;}console.log("ws connect: "+url);ws=new WebSocket(url);ws.onopen=function(){console.log("ws connected");};ws.onclose=function(event){console.log("ws close");if(!cancelled){sleep(5000).then(connect);}};ws.onerror=function(event){console.log("ws error",event);};ws.onmessage=function(event){if(cancelled){return;}var msg=JSON.parse(event.data);handle(msg);};};connect();return _context11.abrupt("return",function(){console.log("ws cancel");cancelled=true;if(ws){ws.close();}});case 11:case"end":return _context11.stop();}},_callee11);}));function listenUpdates(_x17,_x18,_x19){return _listenUpdates2.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee12(resultId){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee12$(_context12){while(1)switch(_context12.prev=_context12.next){case 0:url="".concat(apiUrl,"/getResult?resultId=").concat(resultId);return _context12.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.text();}).then(function(json){return new CommandResult(json);}));case 2:case"end":return _context12.stop();}},_callee12);}));function getResult(_x20){return _getResult2.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee13(resultId){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee13$(_context13){while(1)switch(_context13.prev=_context13.next){case 0:url="".concat(apiUrl,"/getResultSummary?resultId=").concat(resultId);return _context13.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.text();}).then(function(json){return new CommandResultSummary(json);}));case 2:case"end":return _context13.stop();}},_callee13);}));function getResultSummary(_x21){return _getResultSummary2.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee14(resultId,ref,objectType){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee14$(_context14){while(1)switch(_context14.prev=_context14.next){case 0:url="".concat(apiUrl,"/getResultObject?resultId=").concat(resultId,"&").concat(buildRefParams(ref),"&objectType=").concat(objectType);return _context14.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.json();}));case 2:case"end":return _context14.stop();}},_callee14);}));function getResultObject(_x22,_x23,_x24){return _getResultObject2.apply(this,arguments);}return getResultObject;}()},{key:"doPost",value:function(){var _doPost=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee15(f,body){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee15$(_context15){while(1)switch(_context15.prev=_context15.next){case 0:url="".concat(apiUrl,"/").concat(f);return _context15.abrupt("return",fetch(url,{method:"POST",body:JSON.stringify(body),headers:{'Accept':'application/json','Content-Type':'application/json'}}).then(handleErrors));case 2:case"end":return _context15.stop();}},_callee15);}));function doPost(_x25,_x26){return _doPost.apply(this,arguments);}return doPost;}()},{key:"validateNow",value:function(){var _validateNow2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee16(project,target){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee16$(_context16){while(1)switch(_context16.prev=_context16.next){case 0:return _context16.abrupt("return",this.doPost("validateNow",{"project":project,"target":target}));case 1:case"end":return _context16.stop();}},_callee16,this);}));function validateNow(_x27,_x28){return _validateNow2.apply(this,arguments);}return validateNow;}()},{key:"deployNow",value:function(){var _deployNow2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee17(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee17$(_context17){while(1)switch(_context17.prev=_context17.next){case 0:return _context17.abrupt("return",this.doPost("deployNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context17.stop();}},_callee17,this);}));function deployNow(_x29,_x30,_x31){return _deployNow2.apply(this,arguments);}return deployNow;}()},{key:"reconcileNow",value:function(){var _reconcileNow2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee18(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee18$(_context18){while(1)switch(_context18.prev=_context18.next){case 0:return _context18.abrupt("return",this.doPost("reconcileNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context18.stop();}},_callee18,this);}));function reconcileNow(_x32,_x33,_x34){return _reconcileNow2.apply(this,arguments);}return reconcileNow;}()}]);return RealApi;}();var StaticApi=/*#__PURE__*/function(){function StaticApi(){classCallCheck_classCallCheck(this,StaticApi);}createClass_createClass(StaticApi,[{key:"getShortNames",value:function(){var _getShortNames3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee19(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee19$(_context19){while(1)switch(_context19.prev=_context19.next){case 0:_context19.next=2;return loadScript(staticPath+"/shortnames.js");case 2:return _context19.abrupt("return",staticShortNames);case 3:case"end":return _context19.stop();}},_callee19);}));function getShortNames(){return _getShortNames3.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee20(filterProject,filterSubDir,handle){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee20$(_context20){while(1)switch(_context20.prev=_context20.next){case 0:_context20.next=2;return loadScript(staticPath+"/summaries.js");case 2:staticSummaries.forEach(function(rs){if(filterProject&&filterProject!==rs.project.normalizedGitUrl){return;}if(filterSubDir&&filterSubDir!==rs.project.subDir){return;}handle({"type":"update_summary","summary":rs});});return _context20.abrupt("return",function(){});case 4:case"end":return _context20.stop();}},_callee20);}));function listenUpdates(_x35,_x36,_x37){return _listenUpdates3.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee21(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee21$(_context21){while(1)switch(_context21.prev=_context21.next){case 0:_context21.next=2;return loadScript(staticPath+"/result-".concat(resultId,".js"));case 2:return _context21.abrupt("return",staticResults.get(resultId));case 3:case"end":return _context21.stop();}},_callee21);}));function getResult(_x38){return _getResult3.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee22(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee22$(_context22){while(1)switch(_context22.prev=_context22.next){case 0:_context22.next=2;return loadScript(staticPath+"/summaries.js");case 2:return _context22.abrupt("return",staticSummaries.filter(function(s){return s.id===resultId;}).at(0));case 3:case"end":return _context22.stop();}},_callee22);}));function getResultSummary(_x39){return _getResultSummary3.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee23(resultId,ref,objectType){var _result$objects;var result,object;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee23$(_context23){while(1)switch(_context23.prev=_context23.next){case 0:_context23.next=2;return this.getResult(resultId);case 2:result=_context23.sent;object=(_result$objects=result.objects)===null||_result$objects===void 0?void 0:_result$objects.find(function(x){return lodash_default().isEqual(x.ref,ref);});if(object){_context23.next=6;break;}throw new Error("object not found");case 6:_context23.t0=objectType;_context23.next=_context23.t0===ObjectType.Rendered?9:_context23.t0===ObjectType.Remote?10:_context23.t0===ObjectType.Applied?11:12;break;case 9:return _context23.abrupt("return",object.rendered);case 10:return _context23.abrupt("return",object.remote);case 11:return _context23.abrupt("return",object.applied);case 12:throw new Error("unknown object type "+objectType);case 13:case"end":return _context23.stop();}},_callee23,this);}));function getResultObject(_x40,_x41,_x42){return _getResultObject3.apply(this,arguments);}return getResultObject;}()},{key:"validateNow",value:function validateNow(project,target){throw new Error("not implemented");}},{key:"reconcileNow",value:function reconcileNow(cluster,name,namespace){throw new Error("not implemented");}},{key:"deployNow",value:function deployNow(cluster,name,namespace){throw new Error("not implemented");}}]);return StaticApi;}();function handleErrors(response){if(!response.ok){throw Error(response.statusText);}return response;}function buildRefParams(ref){var params=new URLSearchParams();params.set("kind",ref.kind);params.set("name",ref.name);if(ref.group){params.set("group",ref.group);}if(ref.version){params.set("version",ref.version);}if(ref.namespace){params.set("namespace",ref.namespace);}return params.toString();}function buildRefString(ref){if(ref.namespace){return"".concat(ref.namespace,"/").concat(ref.kind,"/").concat(ref.name);}else{if(ref.name){return"".concat(ref.kind,"/").concat(ref.name);}else{return ref.kind;}}}function buildRefKindElement(ref,element){var tooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{zIndex:1000,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["ApiVersion: ",[ref.group,ref.version].filter(function(x){return x;}).join("/")]}),/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["Kind: ",ref.kind]})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip,children:element?element:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.kind})});}function buildObjectRefFromObject(obj){var apiVersion=obj.apiVersion;var s=apiVersion.split("/",2);var ref=new ObjectRef();if(s.length===1){ref.version=s[0];}else{ref.group=s[0];ref.version=s[1];}ref.kind=obj.kind;ref.namespace=obj.metadata.namespace;ref.name=obj.metadata.name;return ref;}function findObjectByRef(l,ref,filter){return l===null||l===void 0?void 0:l.find(function(x){if(filter&&!filter(x)){return false;}return lodash_default().isEqual(x.ref,ref);});}function usePromise(p){if(p===undefined){throw new Promise(function(){return undefined;});}var promise=p;if(promise.status==='fulfilled'){return promise.value;}else if(promise.status==='rejected'){throw promise.reason;}else if(promise.status==='pending'){throw promise;}else{promise.status='pending';p.then(function(result){promise.status='fulfilled';promise.value=result;},function(reason){promise.status='rejected';promise.reason=reason;});throw promise;}} ;// CONCATENATED MODULE: ./src/project-summaries.ts function compareSummaries(a,b){return b.commandInfo.startTime.localeCompare(a.commandInfo.startTime)||b.commandInfo.endTime.localeCompare(b.commandInfo.endTime)||(b.commandInfo.command||"").localeCompare(a.commandInfo.command||"");}function buildProjectSummaries(summaries,validateResults){var sorted=Array.from(summaries.values());sorted.sort(compareSummaries);var m=new Map();sorted.forEach(function(rs){var projectKey=JSON.stringify(rs.projectKey);var p=m.get(projectKey);if(!p){p={project:rs.projectKey,targets:[]};m.set(projectKey,p);}var ptKey=new ProjectTargetKey();ptKey.project=rs.projectKey;ptKey.target=rs.targetKey;var vr=validateResults.get(JSON.stringify(ptKey));var target=p.targets.find(function(t){return lodash_default().isEqual(t.target,rs.targetKey);});if(!target){target={target:rs.targetKey,lastValidateResult:vr,commandResults:[]};p.targets.push(target);}target.commandResults.push(rs);});var ret=[];m.forEach(function(p){p.targets.sort(function(a,b){var _ref;return(a.target.targetName||"").localeCompare(b.target.targetName||"")||a.target.clusterId.localeCompare(b.target.clusterId)||((_ref=a.target.discriminator||"")===null||_ref===void 0?void 0:_ref.localeCompare(b.target.discriminator||""));});ret.push(p);});ret.sort(function(a,b){return(a.project.gitRepoKey||"").localeCompare(b.project.gitRepoKey||"")||(a.project.subDir||"").localeCompare(b.project.subDir||"");});return ret;} ;// CONCATENATED MODULE: ./src/components/App.tsx -function useAppOutletContext(){return useOutletContext();}var AppContext=/*#__PURE__*/(0,react.createContext)({summaries:new Map(),projects:[],validateResults:new Map()});var App=function App(){var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),filters=_useState2[0],setFilters=_useState2[1];var summaries=(0,react.useRef)(new Map());var validateResults=(0,react.useRef)(new Map());(0,react.useEffect)(function(){var updateSummary=function updateSummary(rs){console.log("update_summary",rs.id,rs.commandInfo.startTime);summaries.current.set(rs.id,rs);};var deleteSummary=function deleteSummary(id){console.log("delete_summary",id);summaries.current.delete(id);};var updateValidateResult=function updateValidateResult(key,vr){console.log("validate_result",key);validateResults.current.set(JSON.stringify(key),vr);};console.log("starting listenResults");var cancel=api.listenUpdates(undefined,undefined,function(msg){switch(msg.type){case"update_summary":updateSummary(msg.summary);break;case"delete_summary":deleteSummary(msg.id);break;case"validate_result":updateValidateResult(msg.key,msg.result);break;}});return function(){console.log("cancel listenResults");cancel.then(function(c){return c();});};},[]);var projects=(0,react.useMemo)(function(){console.log("buildProjectSummaries");return buildProjectSummaries(summaries.current,validateResults.current);},[summaries,validateResults]);var appContext={summaries:summaries.current,projects:projects,validateResults:validateResults.current};var outletContext={filters:filters,setFilters:setFilters};return/*#__PURE__*/(0,jsx_runtime.jsx)(AppContext.Provider,{value:appContext,children:/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_light,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(LeftDrawer,{content:/*#__PURE__*/(0,jsx_runtime.jsx)(Outlet,{context:outletContext}),context:outletContext})})})});};/* harmony default export */ var components_App = (App); +function useAppOutletContext(){return useOutletContext();}var AppContext=/*#__PURE__*/(0,react.createContext)({summaries:new Map(),projects:[],validateResults:new Map()});var App=function App(){var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),filters=_useState2[0],setFilters=_useState2[1];var summariesRef=(0,react.useRef)(new Map());var validateResultsRef=(0,react.useRef)(new Map());var _useState3=(0,react.useState)(summariesRef.current),_useState4=slicedToArray_slicedToArray(_useState3,2),summaries=_useState4[0],setSummaries=_useState4[1];var _useState5=(0,react.useState)(validateResultsRef.current),_useState6=slicedToArray_slicedToArray(_useState5,2),validateResults=_useState6[0],setValidateResults=_useState6[1];(0,react.useEffect)(function(){var updateSummary=function updateSummary(rs){console.log("update_summary",rs.id,rs.commandInfo.startTime);summariesRef.current.set(rs.id,rs);setSummaries(new Map(summariesRef.current));};var deleteSummary=function deleteSummary(id){console.log("delete_summary",id);summariesRef.current.delete(id);setSummaries(new Map(summariesRef.current));};var updateValidateResult=function updateValidateResult(key,vr){console.log("validate_result",key);validateResultsRef.current.set(JSON.stringify(key),vr);setValidateResults(new Map(validateResultsRef.current));};console.log("starting listenResults");var cancel=api.listenUpdates(undefined,undefined,function(msg){switch(msg.type){case"update_summary":updateSummary(msg.summary);break;case"delete_summary":deleteSummary(msg.id);break;case"validate_result":updateValidateResult(msg.key,msg.result);break;}});return function(){console.log("cancel listenResults");cancel.then(function(c){return c();});};},[]);var projects=(0,react.useMemo)(function(){return buildProjectSummaries(summaries,validateResults);},[summaries,validateResults]);var appContext={summaries:summariesRef.current,projects:projects,validateResults:validateResults};var outletContext={filters:filters,setFilters:setFilters};return/*#__PURE__*/(0,jsx_runtime.jsx)(AppContext.Provider,{value:appContext,children:/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_light,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(LeftDrawer,{content:/*#__PURE__*/(0,jsx_runtime.jsx)(Outlet,{context:outletContext}),context:outletContext})})})});};/* harmony default export */ var components_App = (App); ;// CONCATENATED MODULE: ./src/components/targets-view/Projects.tsx var ProjectItem=function ProjectItem(props){var name=getLastPathElement(props.ps.project.gitRepoKey);var subDir=props.ps.project.subDir;var projectInfo=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{children:[props.ps.project.gitRepoKey,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),props.ps.project.subDir?/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:["SubDir: ",props.ps.project.subDir,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{})]}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'5px 16px'},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"center",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",alignItems:"center",gap:"15px",children:name&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(ProjectIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:projectInfo,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:name})})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{children:subDir?/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{textAlign:"center",children:subDir}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{marginLeft:"auto"})})})]})});}; ;// CONCATENATED MODULE: ./node_modules/@mui/material/SvgIcon/svgIconClasses.js @@ -52708,8 +53185,10 @@ var ActionsMenu=function ActionsMenu(props){var _React$useState=react.useState(n /* harmony default export */ var PublishedWithChanges = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { d: "m17.66 9.53-7.07 7.07-4.24-4.24 1.41-1.41 2.83 2.83 5.66-5.66 1.41 1.41zM4 12c0-2.33 1.02-4.42 2.62-5.88L9 8.5v-6H3l2.2 2.2C3.24 6.52 2 9.11 2 12c0 5.19 3.95 9.45 9 9.95v-2.02c-3.94-.49-7-3.86-7-7.93zm18 0c0-5.19-3.95-9.45-9-9.95v2.02c3.94.49 7 3.86 7 7.93 0 2.33-1.02 4.42-2.62 5.88L15 15.5v6h6l-2.2-2.2c1.96-1.82 3.2-4.41 3.2-7.3z" }), 'PublishedWithChanges')); +;// CONCATENATED MODULE: ./src/utils/duration.ts +function formatDuration(ms,withMs){if(ms<0)ms=-ms;var time={day:Math.floor(ms/86400000),hour:Math.floor(ms/3600000)%24,minute:Math.floor(ms/60000)%60,second:Math.floor(ms/1000)%60,millisecond:withMs?Math.floor(ms)%1000:0};return Object.entries(time).filter(function(val){return val[1]!==0;}).map(function(val){return val[1]+' '+(val[1]!==1?val[0]+'s':val[0]);}).join(', ');}function formatDurationShort(ms){if(ms<0)ms=-ms;var time={d:Math.floor(ms/86400000),h:Math.floor(ms/3600000)%24,m:Math.floor(ms/60000)%60,s:Math.floor(ms/1000)%60,ms:Math.floor(ms)%1000};var f=Object.entries(time).find(function(val){return val[1]>0;});if(f===undefined){return"0s";}return f[1]+f[0];}var calcAgo=function calcAgo(startTime){var t1=new Date(startTime);var t2=new Date();var d=t2.getTime()-t1.getTime();return formatDurationShort(d);}; ;// CONCATENATED MODULE: ./src/components/targets-view/Targets.tsx -var StatusIcon=function StatusIcon(props){var icon;var theme=styles_useTheme_useTheme();if(props.ts.lastValidateResult===undefined){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(MessageQuestionIcon,{color:theme.palette.error.main});}else if(props.ts.lastValidateResult.ready&&!props.ts.lastValidateResult.errors){var _props$ts$lastValidat,_props$ts$lastValidat2;if((_props$ts$lastValidat=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat!==void 0&&_props$ts$lastValidat.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"warning"});}else if((_props$ts$lastValidat2=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat2!==void 0&&_props$ts$lastValidat2.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"primary"});}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"success"});}}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(HeartBroken,{color:"error"});}var tooltip=[];if(props.ts.lastValidateResult===undefined){tooltip.push("No validation result available.");}else{var _props$ts$lastValidat3,_props$ts$lastValidat4,_props$ts$lastValidat5,_props$ts$lastValidat6;if(props.ts.lastValidateResult.ready&&!((_props$ts$lastValidat3=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat3!==void 0&&_props$ts$lastValidat3.length)){tooltip.push("Target is ready.");}else{tooltip.push("Target is not ready.");}if((_props$ts$lastValidat4=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat4!==void 0&&_props$ts$lastValidat4.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.errors.length," validation errors."));}if((_props$ts$lastValidat5=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat5!==void 0&&_props$ts$lastValidat5.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.warnings.length," validation warnings."));}if((_props$ts$lastValidat6=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat6!==void 0&&_props$ts$lastValidat6.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.drift.length," drifted objects."));}}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip.map(function(t,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:t},i);}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:icon})});};var TargetItem=function TargetItem(props){var _props$ts$commandResu,_props$ts$commandResu2;var actionMenuItems=[];actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Validate now",handler:function handler(){api.validateNow(props.ps.project,props.ts.target);}});var kd;var allKdEqual=true;(_props$ts$commandResu=props.ts.commandResults)===null||_props$ts$commandResu===void 0?void 0:_props$ts$commandResu.forEach(function(rs){if(rs.commandInfo.kluctlDeployment){if(!kd){kd=rs.commandInfo.kluctlDeployment;}else{if(kd.name!==rs.commandInfo.kluctlDeployment.name||kd.namespace!==rs.commandInfo.kluctlDeployment.namespace){allKdEqual=false;}}}});if(kd&&allKdEqual){actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Reconcile",handler:function handler(){api.reconcileNow(props.ts.target.clusterId,kd.name,kd.namespace);}});actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Deploy",handler:function handler(){api.deployNow(props.ts.target.clusterId,kd.name,kd.namespace);}});}var allContexts=[];var handleContext=function handleContext(c){if(!c){return;}if(allContexts.find(function(x){return x===c;})){return;}allContexts.push(c);};(_props$ts$commandResu2=props.ts.commandResults)===null||_props$ts$commandResu2===void 0?void 0:_props$ts$commandResu2.forEach(function(rs){handleContext(rs.commandInfo.contextOverride);handleContext(rs.target.context);});var contextTooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{textAlign:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:"All known contexts:"}),allContexts.map(function(context,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:context},i);})]});var targetName=props.ts.target.targetName;if(!targetName){targetName="";}return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'20px 16px 12px 16px'},onClick:function onClick(e){return props.onSelectTarget(props.ts);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flexGrow:1,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:targetName}),allContexts.length?/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:contextTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:allContexts[0]})}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Cluster ID: "+props.ts.target.clusterId,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CpuIcon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Discriminator: "+props.ts.target.discriminator,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(FingerScanIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(StatusIcon,_objectSpread2({},props)),/*#__PURE__*/(0,jsx_runtime.jsx)(ActionsMenu,{menuItems:actionMenuItems})]})]})]})});}; +var StatusIcon=function StatusIcon(props){var icon;var theme=styles_useTheme_useTheme();if(props.ts.lastValidateResult===undefined){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(MessageQuestionIcon,{color:theme.palette.error.main});}else if(props.ts.lastValidateResult.ready&&!props.ts.lastValidateResult.errors){var _props$ts$lastValidat,_props$ts$lastValidat2;if((_props$ts$lastValidat=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat!==void 0&&_props$ts$lastValidat.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"warning"});}else if((_props$ts$lastValidat2=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat2!==void 0&&_props$ts$lastValidat2.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"primary"});}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"success"});}}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(HeartBroken,{color:"error"});}var tooltip=[];if(props.ts.lastValidateResult===undefined){tooltip.push("No validation result available.");}else{var _props$ts$lastValidat3,_props$ts$lastValidat4,_props$ts$lastValidat5,_props$ts$lastValidat6;if(props.ts.lastValidateResult.ready&&!((_props$ts$lastValidat3=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat3!==void 0&&_props$ts$lastValidat3.length)){tooltip.push("Target is ready.");}else{tooltip.push("Target is not ready.");}if((_props$ts$lastValidat4=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat4!==void 0&&_props$ts$lastValidat4.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.errors.length," validation errors."));}if((_props$ts$lastValidat5=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat5!==void 0&&_props$ts$lastValidat5.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.warnings.length," validation warnings."));}if((_props$ts$lastValidat6=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat6!==void 0&&_props$ts$lastValidat6.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.drift.length," drifted objects."));}tooltip.push("Validation performed "+calcAgo(props.ts.lastValidateResult.startTime)+" ago");}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip.map(function(t){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:t},t);}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:icon})});};var TargetItem=function TargetItem(props){var _props$ts$commandResu,_props$ts$commandResu2;var actionMenuItems=[];actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Validate now",handler:function handler(){api.validateNow(props.ps.project,props.ts.target);}});var kd;var allKdEqual=true;(_props$ts$commandResu=props.ts.commandResults)===null||_props$ts$commandResu===void 0?void 0:_props$ts$commandResu.forEach(function(rs){if(rs.commandInfo.kluctlDeployment){if(!kd){kd=rs.commandInfo.kluctlDeployment;}else{if(kd.name!==rs.commandInfo.kluctlDeployment.name||kd.namespace!==rs.commandInfo.kluctlDeployment.namespace){allKdEqual=false;}}}});if(kd&&allKdEqual){actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Reconcile",handler:function handler(){api.reconcileNow(props.ts.target.clusterId,kd.name,kd.namespace);}});actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Deploy",handler:function handler(){api.deployNow(props.ts.target.clusterId,kd.name,kd.namespace);}});}var allContexts=[];var handleContext=function handleContext(c){if(!c){return;}if(allContexts.find(function(x){return x===c;})){return;}allContexts.push(c);};(_props$ts$commandResu2=props.ts.commandResults)===null||_props$ts$commandResu2===void 0?void 0:_props$ts$commandResu2.forEach(function(rs){handleContext(rs.commandInfo.contextOverride);handleContext(rs.target.context);});var contextTooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{textAlign:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:"All known contexts:"}),allContexts.map(function(context){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:context},context);})]});var targetName=props.ts.target.targetName;if(!targetName){targetName="";}return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'20px 16px 12px 16px'},onClick:function onClick(e){return props.onSelectTarget(props.ts);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flexGrow:1,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:targetName}),allContexts.length?/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:contextTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:allContexts[0]})}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Cluster ID: "+props.ts.target.clusterId,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CpuIcon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Discriminator: "+props.ts.target.discriminator,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(FingerScanIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(StatusIcon,_objectSpread2({},props)),/*#__PURE__*/(0,jsx_runtime.jsx)(ActionsMenu,{menuItems:actionMenuItems})]})]})]})});}; ;// CONCATENATED MODULE: ./node_modules/js-yaml/dist/js-yaml.mjs /*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT */ function isNothing(subject) { @@ -56501,11 +56980,9 @@ esm_light.registerLanguage('yaml',hljs_yaml);esm_light.registerLanguage('diff',h */} ;// CONCATENATED MODULE: ./src/components/result-view/CommandResultStatusLine.tsx -var StatusLine=function StatusLine(props){var children=[];var doPush=function doPush(n,t,icon){if(n){children.push(/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:n+" "+t,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",children:icon})}),/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{fontSize:"10px",align:"center",children:n})]},children.length));}};doPush(props.errors,"total errors.",/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorIcon,{}));doPush(props.warnings,"total warnings.",/*#__PURE__*/(0,jsx_runtime.jsx)(WarningIcon,{}));doPush(props.newObjects,"new objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(AddedIcon,{}));doPush(props.deletedObjects,"deleted objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(TrashIcon,{}));doPush(props.orphanObjects,"orphan objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(OrphanIcon,{}));doPush(props.changedObjects,"changed objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(ChangedIcon,{}));return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",width:"100%",children:children});};var CommandResultStatusLine=function CommandResultStatusLine(props){var _props$rs$errors,_props$rs$warnings;return/*#__PURE__*/(0,jsx_runtime.jsx)(StatusLine,{errors:(_props$rs$errors=props.rs.errors)===null||_props$rs$errors===void 0?void 0:_props$rs$errors.length,warnings:(_props$rs$warnings=props.rs.warnings)===null||_props$rs$warnings===void 0?void 0:_props$rs$warnings.length,changedObjects:props.rs.changedObjects,newObjects:props.rs.newObjects,deletedObjects:props.rs.deletedObjects,orphanObjects:props.rs.orphanObjects});};var ValidateResultStatusLine=function ValidateResultStatusLine(props){var _props$vr,_props$vr$errors,_props$vr2,_props$vr2$warnings,_props$vr3,_props$vr3$drift;return/*#__PURE__*/_jsx(StatusLine,{errors:(_props$vr=props.vr)===null||_props$vr===void 0?void 0:(_props$vr$errors=_props$vr.errors)===null||_props$vr$errors===void 0?void 0:_props$vr$errors.length,warnings:(_props$vr2=props.vr)===null||_props$vr2===void 0?void 0:(_props$vr2$warnings=_props$vr2.warnings)===null||_props$vr2$warnings===void 0?void 0:_props$vr2$warnings.length,changedObjects:(_props$vr3=props.vr)===null||_props$vr3===void 0?void 0:(_props$vr3$drift=_props$vr3.drift)===null||_props$vr3$drift===void 0?void 0:_props$vr3$drift.length});}; -;// CONCATENATED MODULE: ./src/utils/duration.ts -function formatDuration(ms,withMs){if(ms<0)ms=-ms;var time={day:Math.floor(ms/86400000),hour:Math.floor(ms/3600000)%24,minute:Math.floor(ms/60000)%60,second:Math.floor(ms/1000)%60,millisecond:withMs?Math.floor(ms)%1000:0};return Object.entries(time).filter(function(val){return val[1]!==0;}).map(function(val){return val[1]+' '+(val[1]!==1?val[0]+'s':val[0]);}).join(', ');}function formatDurationShort(ms){if(ms<0)ms=-ms;var time={d:Math.floor(ms/86400000),h:Math.floor(ms/3600000)%24,m:Math.floor(ms/60000)%60,s:Math.floor(ms/1000)%60,ms:Math.floor(ms)%1000};var f=Object.entries(time).find(function(val){return val[1]>0;});if(f===undefined){return"0s";}return f[1]+f[0];} +var StatusLine=function StatusLine(props){var children=[];var doPush=function doPush(n,t,icon){if(n){children.push(/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:n+" "+t,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",children:icon})}),/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{fontSize:"10px",align:"center",children:n})]},t));}};doPush(props.errors,"total errors.",/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorIcon,{}));doPush(props.warnings,"total warnings.",/*#__PURE__*/(0,jsx_runtime.jsx)(WarningIcon,{}));doPush(props.newObjects,"new objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(AddedIcon,{}));doPush(props.deletedObjects,"deleted objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(TrashIcon,{}));doPush(props.orphanObjects,"orphan objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(OrphanIcon,{}));doPush(props.changedObjects,"changed objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(ChangedIcon,{}));return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",width:"100%",children:children});};var CommandResultStatusLine=function CommandResultStatusLine(props){var _props$rs$errors,_props$rs$warnings;return/*#__PURE__*/(0,jsx_runtime.jsx)(StatusLine,{errors:(_props$rs$errors=props.rs.errors)===null||_props$rs$errors===void 0?void 0:_props$rs$errors.length,warnings:(_props$rs$warnings=props.rs.warnings)===null||_props$rs$warnings===void 0?void 0:_props$rs$warnings.length,changedObjects:props.rs.changedObjects,newObjects:props.rs.newObjects,deletedObjects:props.rs.deletedObjects,orphanObjects:props.rs.orphanObjects});};var ValidateResultStatusLine=function ValidateResultStatusLine(props){var _props$vr,_props$vr$errors,_props$vr2,_props$vr2$warnings,_props$vr3,_props$vr3$drift;return/*#__PURE__*/_jsx(StatusLine,{errors:(_props$vr=props.vr)===null||_props$vr===void 0?void 0:(_props$vr$errors=_props$vr.errors)===null||_props$vr$errors===void 0?void 0:_props$vr$errors.length,warnings:(_props$vr2=props.vr)===null||_props$vr2===void 0?void 0:(_props$vr2$warnings=_props$vr2.warnings)===null||_props$vr2$warnings===void 0?void 0:_props$vr2$warnings.length,changedObjects:(_props$vr3=props.vr)===null||_props$vr3===void 0?void 0:(_props$vr3$drift=_props$vr3.drift)===null||_props$vr3$drift===void 0?void 0:_props$vr3$drift.length});}; ;// CONCATENATED MODULE: ./src/components/targets-view/CommandResultItem.tsx -var calcAgo=function calcAgo(startTime){var t1=new Date(startTime);var t2=new Date();var d=t2.getTime()-t1.getTime();return formatDurationShort(d);};var CommandResultItem=/*#__PURE__*/react.memo(function(props){var _rs$commandInfo,_rs$commandInfo2;var rs=props.rs,onSelectCommandResult=props.onSelectCommandResult,selected=props.selected;var navigate=dist_useNavigate();var _useState=(0,react.useState)(calcAgo(rs.commandInfo.startTime)),_useState2=slicedToArray_slicedToArray(_useState,2),ago=_useState2[0],setAgo=_useState2[1];var Icon=DiffIcon;switch((_rs$commandInfo=rs.commandInfo)===null||_rs$commandInfo===void 0?void 0:_rs$commandInfo.command){case"delete":Icon=PruneIcon;break;case"deploy":Icon=DeployIcon;break;case"diff":Icon=DiffIcon;break;case"poke-images":Icon=DeployIcon;break;case"prune":Icon=PruneIcon;break;}var iconTooltip=(0,react.useMemo)(function(){var cmdInfoYaml=dump(rs.commandInfo);return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:cmdInfoYaml,language:"yaml"});},[rs.commandInfo]);(0,react.useEffect)(function(){var interval=setInterval(function(){return setAgo(calcAgo(rs.commandInfo.startTime));},5000);return function(){return clearInterval(interval);};},[rs.commandInfo.startTime]);return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'20px 16px 5px 16px',outline:selected?'8px solid #59A588':'none'},onClick:function onClick(){return onSelectCommandResult(rs);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:iconTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"45px",height:"45px",flex:"0 0 auto",justifyContent:"center",alignItems:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Icon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flexGrow:1,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:(_rs$commandInfo2=rs.commandInfo)===null||_rs$commandInfo2===void 0?void 0:_rs$commandInfo2.command}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:rs.commandInfo.startTime,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:ago})})]})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultStatusLine,{rs:rs})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",height:"39px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:function onClick(e){e.stopPropagation();navigate("/results/".concat(rs.id));},sx:{padding:0,width:26,height:26},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Open Result Tree",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TreeViewIcon,{})})})})})]})]})});}); +var CommandResultItem=/*#__PURE__*/react.memo(function(props){var _rs$commandInfo,_rs$commandInfo2;var rs=props.rs,onSelectCommandResult=props.onSelectCommandResult,selected=props.selected;var navigate=dist_useNavigate();var _useState=(0,react.useState)(calcAgo(rs.commandInfo.startTime)),_useState2=slicedToArray_slicedToArray(_useState,2),ago=_useState2[0],setAgo=_useState2[1];var Icon=DiffIcon;switch((_rs$commandInfo=rs.commandInfo)===null||_rs$commandInfo===void 0?void 0:_rs$commandInfo.command){case"delete":Icon=PruneIcon;break;case"deploy":Icon=DeployIcon;break;case"diff":Icon=DiffIcon;break;case"poke-images":Icon=DeployIcon;break;case"prune":Icon=PruneIcon;break;}var iconTooltip=(0,react.useMemo)(function(){var cmdInfoYaml=dump(rs.commandInfo);return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:cmdInfoYaml,language:"yaml"});},[rs.commandInfo]);(0,react.useEffect)(function(){var interval=setInterval(function(){return setAgo(calcAgo(rs.commandInfo.startTime));},5000);return function(){return clearInterval(interval);};},[rs.commandInfo.startTime]);return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'20px 16px 5px 16px',outline:selected?'8px solid #59A588':'none'},onClick:function onClick(){return onSelectCommandResult(rs);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:iconTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"45px",height:"45px",flex:"0 0 auto",justifyContent:"center",alignItems:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Icon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flexGrow:1,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:(_rs$commandInfo2=rs.commandInfo)===null||_rs$commandInfo2===void 0?void 0:_rs$commandInfo2.command}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:rs.commandInfo.startTime,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:ago})})]})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultStatusLine,{rs:rs})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",height:"39px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:function onClick(e){e.stopPropagation();navigate("/results/".concat(rs.id));},sx:{padding:0,width:26,height:26},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Open Result Tree",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TreeViewIcon,{})})})})})]})]})});}); ;// CONCATENATED MODULE: ./node_modules/@mui/material/Table/TableContext.js @@ -57069,10 +57546,14 @@ var TableRow = /*#__PURE__*/react.forwardRef(function TableRow(inProps, ref) { }); false ? 0 : void 0; /* harmony default export */ var TableRow_TableRow = (TableRow); +// EXTERNAL MODULE: ./node_modules/js-sha256/src/sha256.js +var sha256 = __webpack_require__(981); +;// CONCATENATED MODULE: ./src/utils/listKey.ts +function buildListKey(o){var j=JSON.stringify(o);return (0,sha256.sha256)(j);} ;// CONCATENATED MODULE: ./src/components/result-view/ChangesTable.tsx -var RefList=function RefList(props){return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:props.title}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Kind"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Namespace"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Name"})})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:props.refs.map(function(ref,i){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:buildRefKindElement(ref)}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.namespace})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.name})})]},i);})})]})})]});};function ChangesTable(props){var changedObjects;if(props.diffStatus.changedObjects.length){changedObjects=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:"Changed Objects"}),props.diffStatus.changedObjects.map(function(co,i){var _co$changes;return/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(TableHead_TableHead,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableRow_TableRow,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",colSpan:2,sx:{padding:'24px 16px 5px 16px'},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{sx:{fontWeight:500,fontSize:'20px',lineHeight:'27px',letterSpacing:'1px'},children:buildRefString(co.ref)})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Path"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Changes"})]})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_co$changes=co.changes)===null||_co$changes===void 0?void 0:_co$changes.map(function(c,i){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{minWidth:"100px",sx:{overflowWrap:"anywhere"},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:c.jsonPath})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:c.unifiedDiff||"",language:"diff"})})]},i);})})]})},i);})]});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{width:"100%",display:"flex",flexDirection:"column",children:[props.diffStatus.newObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"New Objects",refs:props.diffStatus.newObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.deletedObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Deleted Objects",refs:props.diffStatus.deletedObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.orphanObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Orphan Objects",refs:props.diffStatus.orphanObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),changedObjects]});} +var RefList=function RefList(props){return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:props.title}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Kind"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Namespace"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Name"})})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:props.refs.map(function(ref){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:buildRefKindElement(ref)}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.namespace})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.name})})]},buildRefString(ref));})})]})})]});};function ChangesTable(props){var changedObjects;if(props.diffStatus.changedObjects.length){changedObjects=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:"Changed Objects"}),props.diffStatus.changedObjects.map(function(co){var _co$changes;return/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(TableHead_TableHead,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableRow_TableRow,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",colSpan:2,sx:{padding:'24px 16px 5px 16px'},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{sx:{fontWeight:500,fontSize:'20px',lineHeight:'27px',letterSpacing:'1px'},children:buildRefString(co.ref)})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Path"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Changes"})]})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_co$changes=co.changes)===null||_co$changes===void 0?void 0:_co$changes.map(function(c){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{minWidth:"100px",sx:{overflowWrap:"anywhere"},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:c.jsonPath})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:c.unifiedDiff||"",language:"diff"})})]},buildListKey(c));})})]})},buildRefString(co.ref));})]});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{width:"100%",display:"flex",flexDirection:"column",children:[props.diffStatus.newObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"New Objects",refs:props.diffStatus.newObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.deletedObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Deleted Objects",refs:props.diffStatus.deletedObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.orphanObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Orphan Objects",refs:props.diffStatus.orphanObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),changedObjects]});} ;// CONCATENATED MODULE: ./src/components/ErrorsTable.tsx -function ErrorsTable(props){var _props$errors;return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Ref"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Message"})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_props$errors=props.errors)===null||_props$errors===void 0?void 0:_props$errors.map(function(e,i){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{sx:{minWidth:"150px"},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(List_List,{disablePadding:true,children:[buildRefKindElement(e.ref,/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.kind})})})),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.name})}),e.ref.namespace&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.namespace})})]})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:e.message})]},i);})})]})})})});} +function ErrorsTable(props){var _props$errors;return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Ref"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Message"})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_props$errors=props.errors)===null||_props$errors===void 0?void 0:_props$errors.map(function(e){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{sx:{minWidth:"150px"},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(List_List,{disablePadding:true,children:[buildRefKindElement(e.ref,/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.kind})})})),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.name})}),e.ref.namespace&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.namespace})})]})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:e.message})]},buildListKey(e));})})]})})})});} ;// CONCATENATED MODULE: ./node_modules/@mui/material/CircularProgress/circularProgressClasses.js @@ -58672,18 +59153,18 @@ var TabPanel = /*#__PURE__*/react.forwardRef(function TabPanel(inProps, ref) { var SidePanel=function SidePanel(props){var _props$provider;var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),selectedTab=_useState2[0],setSelectedTab=_useState2[1];var theme=styles_useTheme_useTheme();function handleTabChange(_e,value){setSelectedTab(value);}var tabs=(_props$provider=props.provider)===null||_props$provider===void 0?void 0:_props$provider.buildSidePanelTabs();if(!tabs){tabs=[];}(0,react.useEffect)(function(){var _tabs;if(!((_tabs=tabs)!==null&&_tabs!==void 0&&_tabs.length)){setSelectedTab("");return;}if(!selectedTab){setSelectedTab(tabs[0].label);return;}if(!tabs.find(function(x){return x.label===selectedTab;})){// reset it after the type of selected item has changed setSelectedTab(tabs[0].label);}// ignore that it wants us to add selectedTab to the deps (it would cause and endless loop) // eslint-disable-next-line -},[props.provider]);if(!selectedTab||!tabs.find(function(x){return x.label===selectedTab;})){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{});}if(!props.provider){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{});}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"100%",display:"flex",flexDirection:"column",overflow:"hidden",children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TabContext,{value:selectedTab,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{minHeight:theme.consts.appBarHeight,display:"flex",flexDirection:"column",flex:"0 0 auto",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flex:"1 1 auto",display:"flex",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"1 1 auto",pt:"25px",pl:"35px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h4",children:props.provider.buildSidePanelTitle()})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",pt:"10px",pr:"10px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(CloseIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"36px",flex:"0 0 auto",p:"0 30px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabList_TabList,{onChange:handleTabChange,children:tabs.map(function(tab,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Tab_Tab,{label:tab.label,value:tab.label},tab.label);})})})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:0}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{overflow:"auto",p:"30px",children:tabs.map(function(tab,index){return/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_light,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabPanel_TabPanel,{value:tab.label,sx:{padding:0},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{sx:{padding:'10px 0'},children:tab.content})})},index);})})]})});}; +},[props.provider]);if(!selectedTab||!tabs.find(function(x){return x.label===selectedTab;})){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{});}if(!props.provider){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{});}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"100%",display:"flex",flexDirection:"column",overflow:"hidden",children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TabContext,{value:selectedTab,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{minHeight:theme.consts.appBarHeight,display:"flex",flexDirection:"column",flex:"0 0 auto",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flex:"1 1 auto",display:"flex",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"1 1 auto",pt:"25px",pl:"35px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h4",children:props.provider.buildSidePanelTitle()})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",pt:"10px",pr:"10px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(CloseIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"36px",flex:"0 0 auto",p:"0 30px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabList_TabList,{onChange:handleTabChange,children:tabs.map(function(tab,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Tab_Tab,{label:tab.label,value:tab.label},tab.label);})})})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:0}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{overflow:"auto",p:"30px",children:tabs.map(function(tab){return/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_light,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabPanel_TabPanel,{value:tab.label,sx:{padding:0},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{sx:{padding:'10px 0'},children:tab.content})})},tab.label);})})]})});}; ;// CONCATENATED MODULE: ./src/components/targets-view/Card.tsx var Card_excluded=["children"],Card_excluded2=["children"],Card_excluded3=["children"];var cardWidth=247;var projectCardHeight=80;var cardHeight=126;var cardGap=20;function Card(_ref){var children=_ref.children,rest=objectWithoutProperties_objectWithoutProperties(_ref,Card_excluded);return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2(_objectSpread2({display:"flex",flexShrink:0,width:cardWidth,height:cardHeight},rest),{},{children:children}));}function CardCol(_ref2){var children=_ref2.children,rest=objectWithoutProperties_objectWithoutProperties(_ref2,Card_excluded2);return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2(_objectSpread2({display:"flex",flexDirection:"column",gap:"".concat(cardGap,"px")},rest),{},{children:children}));}function CardRow(_ref3){var children=_ref3.children,rest=objectWithoutProperties_objectWithoutProperties(_ref3,Card_excluded3);return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2(_objectSpread2({display:"flex",gap:"".concat(cardGap,"px")},rest),{},{children:children}));} ;// CONCATENATED MODULE: ./src/components/targets-view/CommandResultDetailsDrawer.tsx -var sidePanelWidth=720;function doGetRootNode(_x){return _doGetRootNode.apply(this,arguments);}function _doGetRootNode(){_doGetRootNode=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(rs){var shortNames,r,builder,_builder$buildRoot,_builder$buildRoot2,node;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:shortNames=api.getShortNames();r=api.getResult(rs.id);_context.t0=NodeBuilder;_context.next=5;return shortNames;case 5:_context.t1=_context.sent;_context.t2=rs;_context.next=9;return r;case 9:_context.t3=_context.sent;_context.t4={shortNames:_context.t1,summary:_context.t2,commandResult:_context.t3};builder=new _context.t0(_context.t4);_builder$buildRoot=builder.buildRoot(),_builder$buildRoot2=slicedToArray_slicedToArray(_builder$buildRoot,1),node=_builder$buildRoot2[0];return _context.abrupt("return",node);case 14:case"end":return _context.stop();}},_callee);}));return _doGetRootNode.apply(this,arguments);}var CommandResultDetailsDrawer=/*#__PURE__*/react.memo(function(props){var ps=props.ps,ts=props.ts,selectedCardRect=props.selectedCardRect;var theme=styles_useTheme_useTheme();var _useState=(0,react.useState)(new Promise(function(){return undefined;})),_useState2=slicedToArray_slicedToArray(_useState,2),promise=_useState2[0],setPromise=_useState2[1];var _useState3=(0,react.useState)(),_useState4=slicedToArray_slicedToArray(_useState3,2),selectedCommandResult=_useState4[0],setSelectedCommandResult=_useState4[1];var _useState5=(0,react.useState)(ts),_useState6=slicedToArray_slicedToArray(_useState5,2),prevTargetSummary=_useState6[0],setPrevTargetSummary=_useState6[1];var _useState7=(0,react.useState)([]),_useState8=slicedToArray_slicedToArray(_useState7,2),cardsCoords=_useState8[0],setCardsCoords=_useState8[1];var _useState9=(0,react.useState)(false),_useState10=slicedToArray_slicedToArray(_useState9,2),transitionRunning=_useState10[0],setTransitionRunning=_useState10[1];if(prevTargetSummary!==ts){var _ts$commandResults;setPrevTargetSummary(ts);setSelectedCommandResult(ts===null||ts===void 0?void 0:(_ts$commandResults=ts.commandResults)===null||_ts$commandResults===void 0?void 0:_ts$commandResults[0]);}(0,react.useEffect)(function(){if(selectedCommandResult===undefined){return;}setPromise(doGetRootNode(selectedCommandResult));},[selectedCommandResult]);var Content=function Content(props){var node=usePromise(promise);return/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:node,onClose:props.onClose});};var cardsContainerElem=(0,react.useRef)();(0,react.useEffect)(function(){var _cardsContainerElem$c;var rect=(_cardsContainerElem$c=cardsContainerElem.current)===null||_cardsContainerElem$c===void 0?void 0:_cardsContainerElem$c.getBoundingClientRect();if(!rect||!selectedCardRect||!(ts!==null&&ts!==void 0&&ts.commandResults)){setCardsCoords([]);return;}var initialCoords=ts.commandResults.map(function(){return{left:selectedCardRect.left-rect.left,top:selectedCardRect.top-rect.top};});setCardsCoords(initialCoords);setTransitionRunning(true);},[selectedCardRect,ts===null||ts===void 0?void 0:ts.commandResults]);(0,react.useEffect)(function(){if(cardsCoords.length>0){var targetCoords=cardsCoords.map(function(_,i){return{left:0,top:i*(cardHeight+cardGap)};});if(cardsCoords.length===targetCoords.length&&cardsCoords.every(function(_ref,i){var left=_ref.left,top=_ref.top;return targetCoords[i].left===left&&targetCoords[i].top===top;})){return;}setTimeout(function(){setCardsCoords(targetCoords);setTimeout(function(){setTransitionRunning(false);},theme.transitions.duration.enteringScreen);},10);}},[cardsCoords,theme.transitions.duration.enteringScreen]);var zIndex=theme.zIndex.modal+1;var cards=(0,react.useMemo)(function(){if(!(ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&cardsCoords.length>0)){return null;}return ts.commandResults.map(function(rs,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Card,{sx:{position:'absolute',translate:"".concat(cardsCoords[i].left,"px ").concat(cardsCoords[i].top,"px"),zIndex:zIndex,transition:theme.transitions.create(['translate'],{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.enteringScreen})},children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultItem,{ps:ps,ts:ts,rs:rs,onSelectCommandResult:setSelectedCommandResult,selected:rs===selectedCommandResult})},i);});},[ps,ts,cardsCoords,zIndex,theme.transitions,selectedCommandResult]);return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{sx:{position:'fixed',top:0,bottom:0,right:sidePanelWidth,width:"calc(100% - ".concat(sidePanelWidth,"px)"),overflowX:'visible',overflowY:transitionRunning?'visible':'auto',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',padding:'25px 0',zIndex:zIndex},onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{onClick:function onClick(e){return e.stopPropagation();},position:"relative",width:cardWidth,height:cardHeight*ts.commandResults.length+cardGap*(ts.commandResults.length-1),ref:cardsContainerElem,children:cards})}),/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.rs!==undefined,onClose:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:sidePanelWidth,height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(react.Suspense,{fallback:/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(Content,{onClose:props.onClose})})})})})]});}); +var sidePanelWidth=720;function doGetRootNode(_x){return _doGetRootNode.apply(this,arguments);}function _doGetRootNode(){_doGetRootNode=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(rs){var shortNames,r,builder,_builder$buildRoot,_builder$buildRoot2,node;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:shortNames=api.getShortNames();r=api.getResult(rs.id);_context.t0=NodeBuilder;_context.next=5;return shortNames;case 5:_context.t1=_context.sent;_context.t2=rs;_context.next=9;return r;case 9:_context.t3=_context.sent;_context.t4={shortNames:_context.t1,summary:_context.t2,commandResult:_context.t3};builder=new _context.t0(_context.t4);_builder$buildRoot=builder.buildRoot(),_builder$buildRoot2=slicedToArray_slicedToArray(_builder$buildRoot,1),node=_builder$buildRoot2[0];return _context.abrupt("return",node);case 14:case"end":return _context.stop();}},_callee);}));return _doGetRootNode.apply(this,arguments);}var CommandResultDetailsDrawer=/*#__PURE__*/react.memo(function(props){var ps=props.ps,ts=props.ts,selectedCardRect=props.selectedCardRect;var theme=styles_useTheme_useTheme();var _useState=(0,react.useState)(new Promise(function(){return undefined;})),_useState2=slicedToArray_slicedToArray(_useState,2),promise=_useState2[0],setPromise=_useState2[1];var _useState3=(0,react.useState)(),_useState4=slicedToArray_slicedToArray(_useState3,2),selectedCommandResult=_useState4[0],setSelectedCommandResult=_useState4[1];var _useState5=(0,react.useState)(ts),_useState6=slicedToArray_slicedToArray(_useState5,2),prevTargetSummary=_useState6[0],setPrevTargetSummary=_useState6[1];var _useState7=(0,react.useState)([]),_useState8=slicedToArray_slicedToArray(_useState7,2),cardsCoords=_useState8[0],setCardsCoords=_useState8[1];var _useState9=(0,react.useState)(false),_useState10=slicedToArray_slicedToArray(_useState9,2),transitionRunning=_useState10[0],setTransitionRunning=_useState10[1];if(prevTargetSummary!==ts){var _ts$commandResults;setPrevTargetSummary(ts);setSelectedCommandResult(ts===null||ts===void 0?void 0:(_ts$commandResults=ts.commandResults)===null||_ts$commandResults===void 0?void 0:_ts$commandResults[0]);}(0,react.useEffect)(function(){if(selectedCommandResult===undefined){return;}setPromise(doGetRootNode(selectedCommandResult));},[selectedCommandResult]);var Content=function Content(props){var node=usePromise(promise);return/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:node,onClose:props.onClose});};var cardsContainerElem=(0,react.useRef)();(0,react.useEffect)(function(){var _cardsContainerElem$c;var rect=(_cardsContainerElem$c=cardsContainerElem.current)===null||_cardsContainerElem$c===void 0?void 0:_cardsContainerElem$c.getBoundingClientRect();if(!rect||!selectedCardRect||!(ts!==null&&ts!==void 0&&ts.commandResults)){setCardsCoords([]);return;}var initialCoords=ts.commandResults.map(function(){return{left:selectedCardRect.left-rect.left,top:selectedCardRect.top-rect.top};});setCardsCoords(initialCoords);setTransitionRunning(true);},[selectedCardRect,ts===null||ts===void 0?void 0:ts.commandResults]);(0,react.useEffect)(function(){if(cardsCoords.length>0){var targetCoords=cardsCoords.map(function(_,i){return{left:0,top:i*(cardHeight+cardGap)};});if(cardsCoords.length===targetCoords.length&&cardsCoords.every(function(_ref,i){var left=_ref.left,top=_ref.top;return targetCoords[i].left===left&&targetCoords[i].top===top;})){return;}setTimeout(function(){setCardsCoords(targetCoords);setTimeout(function(){setTransitionRunning(false);},theme.transitions.duration.enteringScreen);},10);}},[cardsCoords,theme.transitions.duration.enteringScreen]);var zIndex=theme.zIndex.modal+1;var cards=(0,react.useMemo)(function(){if(!(ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&cardsCoords.length>0)){return null;}return ts.commandResults.map(function(rs,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Card,{sx:{position:'absolute',translate:"".concat(cardsCoords[i].left,"px ").concat(cardsCoords[i].top,"px"),zIndex:zIndex,transition:theme.transitions.create(['translate'],{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.enteringScreen})},children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultItem,{ps:ps,ts:ts,rs:rs,onSelectCommandResult:setSelectedCommandResult,selected:rs===selectedCommandResult})},rs.id);});},[ps,ts,cardsCoords,zIndex,theme.transitions,selectedCommandResult]);return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{sx:{position:'fixed',top:0,bottom:0,right:sidePanelWidth,width:"calc(100% - ".concat(sidePanelWidth,"px)"),overflowX:'visible',overflowY:transitionRunning?'visible':'auto',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',padding:'25px 0',zIndex:zIndex},onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{onClick:function onClick(e){return e.stopPropagation();},position:"relative",width:cardWidth,height:cardHeight*ts.commandResults.length+cardGap*(ts.commandResults.length-1),ref:cardsContainerElem,children:cards})}),/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.rs!==undefined,onClose:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:sidePanelWidth,height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(react.Suspense,{fallback:/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(Content,{onClose:props.onClose})})})})})]});}); ;// CONCATENATED MODULE: ./src/components/targets-view/TargetDetailsDrawer.tsx var MyProvider=/*#__PURE__*/function(){function MyProvider(ts){var _this$ts,_this$ts$lastValidate,_this$ts$lastValidate2,_this=this;classCallCheck_classCallCheck(this,MyProvider);this.ts=void 0;this.diffStatus=void 0;this.ts=ts;this.diffStatus=new DiffStatus();(_this$ts=this.ts)===null||_this$ts===void 0?void 0:(_this$ts$lastValidate=_this$ts.lastValidateResult)===null||_this$ts$lastValidate===void 0?void 0:(_this$ts$lastValidate2=_this$ts$lastValidate.drift)===null||_this$ts$lastValidate2===void 0?void 0:_this$ts$lastValidate2.forEach(function(co){_this.diffStatus.addChangedObject(co);});}createClass_createClass(MyProvider,[{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var _this$ts$lastValidate3,_this$ts$lastValidate4,_this$ts$lastValidate5,_this$ts$lastValidate6;if(!this.ts){return[];}var tabs=[{label:"Summary",content:this.buildSummaryTab()}];if(this.ts.target)if(this.diffStatus.changedObjects.length){tabs.push({label:"Drift",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ChangesTable,{diffStatus:this.diffStatus})});}if((_this$ts$lastValidate3=this.ts.lastValidateResult)!==null&&_this$ts$lastValidate3!==void 0&&(_this$ts$lastValidate4=_this$ts$lastValidate3.errors)!==null&&_this$ts$lastValidate4!==void 0&&_this$ts$lastValidate4.length){tabs.push({label:"Errors",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.ts.lastValidateResult.errors})});}if((_this$ts$lastValidate5=this.ts.lastValidateResult)!==null&&_this$ts$lastValidate5!==void 0&&(_this$ts$lastValidate6=_this$ts$lastValidate5.warnings)!==null&&_this$ts$lastValidate6!==void 0&&_this$ts$lastValidate6.length){tabs.push({label:"Warnings",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.ts.lastValidateResult.warnings})});}return tabs;}},{key:"buildSummaryTab",value:function buildSummaryTab(){var _this$ts2,_this$ts3,_this$ts4,_this$ts4$lastValidat,_this$ts4$lastValidat2,_this$ts5,_this$ts5$lastValidat,_this$ts5$lastValidat2,_this$ts6,_this$ts6$lastValidat,_this$ts6$lastValidat2;var props=[{name:"Target Name",value:this.getTargetName()},{name:"Discriminator",value:(_this$ts2=this.ts)===null||_this$ts2===void 0?void 0:_this$ts2.target.discriminator}];if((_this$ts3=this.ts)!==null&&_this$ts3!==void 0&&_this$ts3.lastValidateResult){props.push({name:"Ready",value:this.ts.lastValidateResult.ready+""});}if((_this$ts4=this.ts)!==null&&_this$ts4!==void 0&&(_this$ts4$lastValidat=_this$ts4.lastValidateResult)!==null&&_this$ts4$lastValidat!==void 0&&(_this$ts4$lastValidat2=_this$ts4$lastValidat.errors)!==null&&_this$ts4$lastValidat2!==void 0&&_this$ts4$lastValidat2.length){props.push({name:"Errors",value:this.ts.lastValidateResult.errors.length+""});}if((_this$ts5=this.ts)!==null&&_this$ts5!==void 0&&(_this$ts5$lastValidat=_this$ts5.lastValidateResult)!==null&&_this$ts5$lastValidat!==void 0&&(_this$ts5$lastValidat2=_this$ts5$lastValidat.warnings)!==null&&_this$ts5$lastValidat2!==void 0&&_this$ts5$lastValidat2.length){props.push({name:"Warnings",value:this.ts.lastValidateResult.warnings.length+""});}if((_this$ts6=this.ts)!==null&&_this$ts6!==void 0&&(_this$ts6$lastValidat=_this$ts6.lastValidateResult)!==null&&_this$ts6$lastValidat!==void 0&&(_this$ts6$lastValidat2=_this$ts6$lastValidat.drift)!==null&&_this$ts6$lastValidat2!==void 0&&_this$ts6$lastValidat2.length){props.push({name:"Drifted Objects",value:this.ts.lastValidateResult.drift.length+""});}return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}},{key:"getTargetName",value:function getTargetName(){if(!this.ts){return"";}var name="";if(this.ts.target.targetName){name=this.ts.target.targetName;}return name;}},{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return this.getTargetName();}}]);return MyProvider;}();var TargetDetailsDrawer=/*#__PURE__*/react.memo(function(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.ts!==undefined,onClose:props.onClose,ModalProps:{BackdropProps:{invisible:true}},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"600px",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:new MyProvider(props.ts),onClose:props.onClose})})})});}); ;// CONCATENATED MODULE: ./src/components/targets-view/TargetsView.tsx var colWidth=416;var curveRadius=12;var circleRadius=5;var strokeWidth=2;function ColHeader(_ref){var children=_ref.children;return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{minWidth:colWidth,width:colWidth,height:"42px",display:"flex",alignItems:"center",sx:{borderLeft:'0.8px solid rgba(0,0,0,0.5)',paddingLeft:'15px','&:first-of-type':{borderLeft:'none',paddingLeft:0}},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h2",textAlign:"left",children:children})});}var Circle=/*#__PURE__*/react.memo(function(props){var theme=styles_useTheme_useTheme();return/*#__PURE__*/(0,jsx_runtime.jsx)("circle",_objectSpread2({r:circleRadius,fill:theme.palette.background.default,stroke:theme.palette.secondary.main,strokeWidth:strokeWidth},props));});var RelationTree=/*#__PURE__*/react.memo(function(_ref2){var targetCount=_ref2.targetCount;var theme=styles_useTheme_useTheme();var height=targetCount*cardHeight+(targetCount-1)*cardGap;var width=152;if(targetCount<=0){return null;}var targetsCenterYs=Array(targetCount).fill(0).map(function(_,i){return cardHeight/2+i*(cardHeight+cardGap);});var rootCenterY=height/2;return/*#__PURE__*/(0,jsx_runtime.jsxs)("svg",{width:width,height:height,viewBox:"0 0 ".concat(width," ").concat(height),fill:"none",children:[targetsCenterYs.map(function(cy,i){var d;if(targetCount%2===1&&i===Math.floor(targetCount/2)){// target is in the middle. d="\n M ".concat(circleRadius,",").concat(rootCenterY,"\n h ").concat(width-circleRadius,"\n ");}else if(i Date: Sat, 10 Jun 2023 01:04:22 +0200 Subject: [PATCH 1166/2268] fix: Fix tests and controller client creation --- cmd/kluctl/commands/cmd_controller_run.go | 8 ++++---- e2e/gitops_errors_test.go | 2 +- e2e/results_test.go | 2 +- e2e/test-utils/envtest_cluster.go | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/kluctl/commands/cmd_controller_run.go b/cmd/kluctl/commands/cmd_controller_run.go index a08a09016..dc9bfb130 100644 --- a/cmd/kluctl/commands/cmd_controller_run.go +++ b/cmd/kluctl/commands/cmd_controller_run.go @@ -131,11 +131,11 @@ func (cmd *controllerRunCmd) Run(ctx context.Context) error { } if cmd.WriteCommandResult { - wc, ok := mgr.GetClient().(client.WithWatch) - if !ok { - return fmt.Errorf("client does not implement WithWatch") + c, err := client.NewWithWatch(restConfig, client.Options{}) + if err != nil { + return err } - resultStore, err := results.NewResultStoreSecrets(ctx, wc, cmd.CommandResultNamespace, cmd.KeepCommandResultsCount) + resultStore, err := results.NewResultStoreSecrets(ctx, c, cmd.CommandResultNamespace, cmd.KeepCommandResultsCount) if err != nil { return err } diff --git a/e2e/gitops_errors_test.go b/e2e/gitops_errors_test.go index c2df3e467..2d015ffaa 100644 --- a/e2e/gitops_errors_test.go +++ b/e2e/gitops_errors_test.go @@ -41,7 +41,7 @@ func (suite *GitopsTestSuite) assertErrors(key client.ObjectKey, rstatus metav1. g.Expect(readinessCondition.Reason).To(Equal(rreason)) g.Expect(readinessCondition.Message).To(ContainSubstring(rmessage)) - rs, err := results.NewResultStoreSecrets(context.TODO(), suite.k.Client, nil, "", 0) + rs, err := results.NewResultStoreSecrets(context.TODO(), suite.k.Client, "", 0) g.Expect(err).To(Succeed()) lastDeployResult, err := kd.Status.GetLastDeployResult() diff --git a/e2e/results_test.go b/e2e/results_test.go index 000265647..ae859d7c8 100644 --- a/e2e/results_test.go +++ b/e2e/results_test.go @@ -42,7 +42,7 @@ func TestWriteResult(t *testing.T) { p.KluctlMust("deploy", "--yes", "-t", "test", "--write-command-result") assertConfigMapExists(t, k, p.TestSlug(), "cm") - rs, err := results.NewResultStoreSecrets(context.Background(), k.Client, nil, "kluctl-results", 0) + rs, err := results.NewResultStoreSecrets(context.Background(), k.Client, "kluctl-results", 0) assert.NoError(t, err) opts := results.ListCommandResultSummariesOptions{ diff --git a/e2e/test-utils/envtest_cluster.go b/e2e/test-utils/envtest_cluster.go index c6e6d9fd6..4afba9da3 100644 --- a/e2e/test-utils/envtest_cluster.go +++ b/e2e/test-utils/envtest_cluster.go @@ -36,7 +36,7 @@ type EnvTestCluster struct { HttpClient *http.Client DynamicClient dynamic.Interface - Client client.Client + Client client.WithWatch ServerVersion *version.Info callbackServer webhook.Server @@ -103,7 +103,7 @@ func (k *EnvTestCluster) Start() error { } k.DynamicClient = dynamicClient - c, err := client.New(k.config, client.Options{ + c, err := client.NewWithWatch(k.config, client.Options{ HTTPClient: httpClient, Scheme: k.env.Scheme, }) From 3fd345bcbde9e3d8fe62fe046c262a8f76d5b05e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 11 Jun 2023 22:05:36 +0200 Subject: [PATCH 1167/2268] ci: Remove cross-compile workflow (#559) It's redundant as the goreleaser workflows already perform cross compilation. --- .github/workflows/cross-compile.yaml | 36 ---------------------------- 1 file changed, 36 deletions(-) delete mode 100644 .github/workflows/cross-compile.yaml diff --git a/.github/workflows/cross-compile.yaml b/.github/workflows/cross-compile.yaml deleted file mode 100644 index e9651b820..000000000 --- a/.github/workflows/cross-compile.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: cross-compile - -on: - push: - branches: - - main - -jobs: - cross-compile: - runs-on: ubuntu-20.04 - if: github.event_name != 'pull_request' - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - uses: actions/setup-go@v4 - with: - go-version: '1.19' - - uses: actions/cache@v3 - with: - path: | - ~/go/pkg/mod - ~/.cache/go-build - key: cross-compile-go-${{ runner.os }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - cross-compile-go-${{ runner.os }}- - - name: Build kluctl (linux) - run: | - make build GOARCH=amd64 GOOS=linux - - name: Build kluctl (darwin) - run: | - make build GOARCH=amd64 GOOS=darwin - - name: Build kluctl (windows) - run: | - make build GOARCH=amd64 GOOS=windows From c9f389344a933acfe49c5681588ace0a08660bb5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 12 Jun 2023 23:19:01 +0200 Subject: [PATCH 1168/2268] ci: Also run tests workflow on releaser branch (#561) --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cb0239ea7..74553224f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - release-v* pull_request: branches: - main From d78fc8783696b2bfc306acbaec8b0e1c7362a013 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 13 Jun 2023 08:51:42 +0200 Subject: [PATCH 1169/2268] refactor: Use ResultStore interface instead of ResultsCollector --- pkg/webui/server.go | 24 ++++++++++++------------ pkg/webui/websocket.go | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/webui/server.go b/pkg/webui/server.go index 63db414c0..55fe6abeb 100644 --- a/pkg/webui/server.go +++ b/pkg/webui/server.go @@ -23,16 +23,16 @@ import ( const webuiManager = "kluctl-webui" type CommandResultsServer struct { - ctx context.Context - collector *results.ResultsCollector - cam *clusterAccessorManager - vam *validatorManager + ctx context.Context + store results.ResultStore + cam *clusterAccessorManager + vam *validatorManager } -func NewCommandResultsServer(ctx context.Context, collector *results.ResultsCollector, configs []*rest.Config) *CommandResultsServer { +func NewCommandResultsServer(ctx context.Context, store *results.ResultsCollector, configs []*rest.Config) *CommandResultsServer { ret := &CommandResultsServer{ - ctx: ctx, - collector: collector, + ctx: ctx, + store: store, cam: &clusterAccessorManager{ ctx: ctx, }, @@ -42,13 +42,13 @@ func NewCommandResultsServer(ctx context.Context, collector *results.ResultsColl ret.cam.add(config) } - ret.vam = newValidatorManager(ctx, collector, ret.cam) + ret.vam = newValidatorManager(ctx, store, ret.cam) return ret } func (s *CommandResultsServer) Run(port int) error { - l, ch, cancel, err := s.collector.WatchCommandResultSummaries(results.ListCommandResultSummariesOptions{}) + l, ch, cancel, err := s.store.WatchCommandResultSummaries(results.ListCommandResultSummariesOptions{}) if err != nil { return err } @@ -165,7 +165,7 @@ func (s *CommandResultsServer) getResult(c *gin.Context) { return } - sr, err := s.collector.GetCommandResult(results.GetCommandResultOptions{ + sr, err := s.store.GetCommandResult(results.GetCommandResultOptions{ Id: params.ResultId, Reduced: true, }) @@ -190,7 +190,7 @@ func (s *CommandResultsServer) getResultSummary(c *gin.Context) { return } - sr, err := s.collector.GetCommandResultSummary(params.ResultId) + sr, err := s.store.GetCommandResultSummary(params.ResultId) if err != nil { _ = c.AbortWithError(http.StatusBadRequest, err) return @@ -224,7 +224,7 @@ func (s *CommandResultsServer) getResultObject(c *gin.Context) { return } - sr, err := s.collector.GetCommandResult(results.GetCommandResultOptions{ + sr, err := s.store.GetCommandResult(results.GetCommandResultOptions{ Id: params.ResultId, Reduced: false, }) diff --git a/pkg/webui/websocket.go b/pkg/webui/websocket.go index a0690c752..2a21cb462 100644 --- a/pkg/webui/websocket.go +++ b/pkg/webui/websocket.go @@ -68,7 +68,7 @@ func (s *CommandResultsServer) ws(c *gin.Context) { } func (s *CommandResultsServer) wsHandle(c *websocket.Conn, filter *result.ProjectKey) error { - initial, ch, cancel, err := s.collector.WatchCommandResultSummaries(results.ListCommandResultSummariesOptions{ProjectFilter: filter}) + initial, ch, cancel, err := s.store.WatchCommandResultSummaries(results.ListCommandResultSummariesOptions{ProjectFilter: filter}) if err != nil { return err } From 335923f122ab2812d7f69bea9da1e79cf40b0880 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 13 Jun 2023 08:59:49 +0200 Subject: [PATCH 1170/2268] refactor: Move static routes setup into own function --- pkg/webui/server.go | 50 ++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/pkg/webui/server.go b/pkg/webui/server.go index 55fe6abeb..6aad01bd9 100644 --- a/pkg/webui/server.go +++ b/pkg/webui/server.go @@ -76,31 +76,11 @@ func (s *CommandResultsServer) Run(port int) error { router := gin.Default() - dis, err := fs.ReadDir(uiFS, ".") + err = s.setupStaticRoutes(router) if err != nil { return err } - for _, di := range dis { - if di.IsDir() { - x, err := fs.Sub(uiFS, di.Name()) - if err != nil { - return err - } - router.StaticFS("/"+di.Name(), http.FS(x)) - } else { - router.StaticFileFS("/"+di.Name(), di.Name(), http.FS(uiFS)) - } - } - router.GET("/", func(c *gin.Context) { - b, err := fs.ReadFile(uiFS, "index.html") - if err != nil { - _ = c.AbortWithError(http.StatusInternalServerError, err) - return - } - c.Data(http.StatusOK, "text/html; charset=utf-8", b) - }) - api := router.Group("/api") api.GET("/getShortNames", s.getShortNames) api.GET("/getResult", s.getResult) @@ -128,6 +108,34 @@ func (s *CommandResultsServer) Run(port int) error { return httpServer.Serve(listener) } +func (s *CommandResultsServer) setupStaticRoutes(router gin.IRouter) error { + dis, err := fs.ReadDir(uiFS, ".") + if err != nil { + return err + } + + for _, di := range dis { + if di.IsDir() { + x, err := fs.Sub(uiFS, di.Name()) + if err != nil { + return err + } + router.StaticFS("/"+di.Name(), http.FS(x)) + } else { + router.StaticFileFS("/"+di.Name(), di.Name(), http.FS(uiFS)) + } + } + router.GET("/", func(c *gin.Context) { + b, err := fs.ReadFile(uiFS, "index.html") + if err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, err) + return + } + c.Data(http.StatusOK, "text/html; charset=utf-8", b) + }) + return nil +} + func (s *CommandResultsServer) getShortNames(c *gin.Context) { c.JSON(http.StatusOK, GetShortNames()) } From 52db53c5bee29d38d2392ef181cb21bee9d9168e Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Jun 2023 14:40:47 +0200 Subject: [PATCH 1171/2268] refactor: Move conversion of watch events into own function --- pkg/results/result-store-secrets.go | 55 +++++++++++++++++------------ 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/pkg/results/result-store-secrets.go b/pkg/results/result-store-secrets.go index 7f3e5cfa5..28a61cccb 100644 --- a/pkg/results/result-store-secrets.go +++ b/pkg/results/result-store-secrets.go @@ -251,6 +251,35 @@ func (s *ResultStoreSecrets) filterSummary(summary *result.CommandResultSummary, return true } +func (s *ResultStoreSecrets) convertWatchEvent(event watch.Event, filter *result.ProjectKey) *WatchCommandResultSummaryEvent { + if event.Object == nil { + return nil + } + x2, ok := event.Object.(client.Object) + if !ok { + return nil + } + summary, err := s.parseSummary(x2.GetAnnotations()) + if err != nil { + return nil + } + if !s.filterSummary(summary, filter) { + return nil + } + switch event.Type { + case watch.Deleted: + return &WatchCommandResultSummaryEvent{ + Delete: true, + Summary: summary, + } + case watch.Added, watch.Modified: + return &WatchCommandResultSummaryEvent{ + Summary: summary, + } + } + return nil +} + func (s *ResultStoreSecrets) WatchCommandResultSummaries(options ListCommandResultSummariesOptions) ([]*result.CommandResultSummary, <-chan WatchCommandResultSummaryEvent, context.CancelFunc, error) { var l metav1.PartialObjectMetadataList l.SetGroupVersionKind(schema.GroupVersionKind{Version: "v1", Kind: "SecretList"}) @@ -275,31 +304,11 @@ func (s *ResultStoreSecrets) WatchCommandResultSummaries(options ListCommandResu go func() { for x := range w.ResultChan() { - if x.Object == nil { + we := s.convertWatchEvent(x, options.ProjectFilter) + if we == nil { continue } - x2, ok := x.Object.(client.Object) - if !ok { - continue - } - summary, err := s.parseSummary(x2.GetAnnotations()) - if err != nil { - continue - } - if !s.filterSummary(summary, options.ProjectFilter) { - continue - } - switch x.Type { - case watch.Deleted: - ch <- WatchCommandResultSummaryEvent{ - Delete: true, - Summary: summary, - } - case watch.Added, watch.Modified: - ch <- WatchCommandResultSummaryEvent{ - Summary: summary, - } - } + ch <- *we } close(ch) }() From 9ad61feb931e5ec68068c80078bc28b260ac8d20 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Jun 2023 14:42:42 +0200 Subject: [PATCH 1172/2268] fix: Actually delete temporary files --- pkg/webui/staticbuilder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/webui/staticbuilder.go b/pkg/webui/staticbuilder.go index 142b5f148..70b209c3f 100644 --- a/pkg/webui/staticbuilder.go +++ b/pkg/webui/staticbuilder.go @@ -27,7 +27,7 @@ func (swb *StaticWebuiBuilder) Build(path string) error { if err != nil { return err } - //defer os.RemoveAll(tmpDir) + defer os.RemoveAll(tmpDir) summaries, err := swb.store.ListCommandResultSummaries(results.ListCommandResultSummariesOptions{}) if err != nil { From ffb3ffd9bc3cc74f276b464d35c37daf4ff10693 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Jun 2023 15:06:03 +0200 Subject: [PATCH 1173/2268] fix: More reliable WaitForResults --- cmd/kluctl/commands/cmd_webui.go | 8 +++++++- pkg/results/results-collector.go | 32 ++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/cmd/kluctl/commands/cmd_webui.go b/cmd/kluctl/commands/cmd_webui.go index 2ae1a520c..e3614cb85 100644 --- a/cmd/kluctl/commands/cmd_webui.go +++ b/cmd/kluctl/commands/cmd_webui.go @@ -32,7 +32,13 @@ func (cmd *webuiCmd) Run(ctx context.Context) error { collector.Start() if cmd.StaticPath != "" { - collector.WaitForInitialSync() + st := status.Start(ctx, "Collecting results") + defer st.Failed() + err = collector.WaitForResults(time.Second, time.Second*30) + if err != nil { + return err + } + st.Success() sbw := webui.NewStaticWebuiBuilder(collector) return sbw.Build(cmd.StaticPath) } else { diff --git a/pkg/results/results-collector.go b/pkg/results/results-collector.go index 2237c33a8..f053406d5 100644 --- a/pkg/results/results-collector.go +++ b/pkg/results/results-collector.go @@ -17,8 +17,7 @@ type ResultsCollector struct { resultSummaries map[string]summaryEntry watches []*watchEntry - initialWG sync.WaitGroup - mutex sync.Mutex + mutex sync.Mutex } type summaryEntry struct { @@ -40,8 +39,6 @@ func NewResultsCollector(ctx context.Context, stores []ResultStore) *ResultsColl resultSummaries: map[string]summaryEntry{}, } - ret.initialWG.Add(len(stores)) - return ret } @@ -49,8 +46,25 @@ func (rc *ResultsCollector) Start() { rc.startWatchResults() } -func (rc *ResultsCollector) WaitForInitialSync() { - rc.initialWG.Wait() +func (rc *ResultsCollector) WaitForResults(idleTimeout time.Duration, totalTimeout time.Duration) error { + _, ch, cancel, err := rc.WatchCommandResultSummaries(ListCommandResultSummariesOptions{}) + if err != nil { + return err + } + defer cancel() + + totalCh := time.After(totalTimeout) + for { + idleCh := time.After(idleTimeout) + select { + case <-ch: + continue + case <-idleCh: + return nil + case <-totalCh: + return fmt.Errorf("result collector kept receiving results even after the total timeout") + } + } } func (rc *ResultsCollector) startWatchResults() { @@ -60,7 +74,6 @@ func (rc *ResultsCollector) startWatchResults() { } func (rc *ResultsCollector) runWatchResults(store ResultStore) { - initial := true for { l, ch, _, err := store.WatchCommandResultSummaries(ListCommandResultSummariesOptions{}) if err != nil { @@ -68,11 +81,6 @@ func (rc *ResultsCollector) runWatchResults(store ResultStore) { continue } - if initial { - rc.initialWG.Done() - initial = false - } - for _, rs := range l { rc.handleUpdate(store, WatchCommandResultSummaryEvent{ Summary: rs, From 2f512ceeab0e6348f364d8c2051c0d59d3437141 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Jun 2023 15:22:17 +0200 Subject: [PATCH 1174/2268] feat: Implement webui auth based on a simple admin account --- cmd/kluctl/commands/cmd_webui.go | 23 +- go.mod | 5 +- go.sum | 13 +- pkg/webui/auth.go | 274 ++++++++++++++++++ pkg/webui/server.go | 45 ++- pkg/webui/ui/src/api.tsx | 244 ++++++++-------- pkg/webui/ui/src/components/App.tsx | 113 +++++++- pkg/webui/ui/src/components/Loading.tsx | 26 +- pkg/webui/ui/src/components/Login.tsx | 56 ++++ pkg/webui/ui/src/components/ObjectYaml.tsx | 35 +-- pkg/webui/ui/src/components/Router.tsx | 3 +- .../result-view/CommandResultView.tsx | 39 ++- .../CommandResultDetailsDrawer.tsx | 43 +-- .../src/components/targets-view/Targets.tsx | 5 +- pkg/webui/websocket.go | 62 +++- 15 files changed, 789 insertions(+), 197 deletions(-) create mode 100644 pkg/webui/auth.go create mode 100644 pkg/webui/ui/src/components/Login.tsx diff --git a/cmd/kluctl/commands/cmd_webui.go b/cmd/kluctl/commands/cmd_webui.go index e3614cb85..344882505 100644 --- a/cmd/kluctl/commands/cmd_webui.go +++ b/cmd/kluctl/commands/cmd_webui.go @@ -4,10 +4,12 @@ import ( "context" "fmt" "github.com/kluctl/kluctl/v2/pkg/results" + "github.com/kluctl/kluctl/v2/pkg/status" "github.com/kluctl/kluctl/v2/pkg/webui" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/controller-runtime/pkg/client" + "time" ) type webuiCmd struct { @@ -15,6 +17,9 @@ type webuiCmd struct { Context []string `group:"misc" help:"List of kubernetes contexts to use."` AllContexts bool `group:"misc" help:"Use all Kubernetes contexts found in the kubeconfig."` StaticPath string `group:"misc" help:"Build static webui."` + + InCluster bool `group:"misc" help:"This enables in-cluster functionality."` + InClusterContext string `group:"misc" help:"The context to use fo in-cluster functionality."` } func (cmd *webuiCmd) Help() string { @@ -22,6 +27,22 @@ func (cmd *webuiCmd) Help() string { } func (cmd *webuiCmd) Run(ctx context.Context) error { + var inClusterClient client.Client + if cmd.InCluster { + configOverrides := &clientcmd.ConfigOverrides{ + CurrentContext: cmd.InClusterContext, + } + config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), + configOverrides).ClientConfig() + if err != nil { + return err + } + inClusterClient, err = client.NewWithWatch(config, client.Options{}) + if err != nil { + return err + } + } stores, configs, err := cmd.createResultStores(ctx) if err != nil { @@ -42,7 +63,7 @@ func (cmd *webuiCmd) Run(ctx context.Context) error { sbw := webui.NewStaticWebuiBuilder(collector) return sbw.Build(cmd.StaticPath) } else { - server := webui.NewCommandResultsServer(ctx, collector, configs) + server := webui.NewCommandResultsServer(ctx, collector, configs, inClusterClient, inClusterClient != nil) return server.Run(cmd.Port) } } diff --git a/go.mod b/go.mod index d79fdb2ba..07201c049 100644 --- a/go.mod +++ b/go.mod @@ -74,6 +74,7 @@ require ( github.com/sergi/go-diff v1.3.1 github.com/tkrajina/typescriptify-golang-structs v0.1.10 go.mozilla.org/sops/v3 v3.7.4-0.20220901181616-9124783930b1 + nhooyr.io/websocket v1.8.7 sigs.k8s.io/cli-utils v0.34.0 sigs.k8s.io/controller-runtime v0.15.0 sigs.k8s.io/kustomize/api v0.13.4 @@ -176,6 +177,7 @@ require ( github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/karrick/godirwalk v1.17.0 // indirect github.com/klauspost/compress v1.16.5 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -186,6 +188,7 @@ require ( github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -201,6 +204,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc3 // indirect + github.com/opencontainers/runc v1.1.6 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect @@ -259,7 +263,6 @@ require ( k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/kubectl v0.27.1 // indirect k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect - nhooyr.io/websocket v1.8.7 // indirect oras.land/oras-go v1.2.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect ) diff --git a/go.sum b/go.sum index 7e2749e3d..ff2d7461d 100644 --- a/go.sum +++ b/go.sum @@ -343,8 +343,11 @@ github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XE github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= @@ -456,6 +459,7 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= @@ -549,8 +553,9 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= +github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -627,8 +632,9 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/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= @@ -691,7 +697,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= +github.com/opencontainers/runc v1.1.6 h1:XbhB8IfG/EsnhNvZtNdLB0GBw92GYEFvKlhaJk9jUgA= +github.com/opencontainers/runc v1.1.6/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= diff --git a/pkg/webui/auth.go b/pkg/webui/auth.go new file mode 100644 index 000000000..fb9cc97e1 --- /dev/null +++ b/pkg/webui/auth.go @@ -0,0 +1,274 @@ +package webui + +import ( + "context" + "fmt" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v4" + corev1 "k8s.io/api/core/v1" + "net/http" + "sigs.k8s.io/controller-runtime/pkg/client" + "strings" + "time" +) + +const ( + tokenValidTime = 1 * time.Hour + tokenMaxRefreshTime = 2 * time.Hour +) + +type login struct { + Username string `form:"username" json:"username" binding:"required"` + Password string `form:"password" json:"password" binding:"required"` +} + +type authHandler struct { + ctx context.Context + + adminEnabled bool + + serverClient client.Client + webuiSecretName string +} + +type User struct { + Username string `json:"username"` + IsAdmin bool `json:"isAdmin"` +} + +func (s *authHandler) setupAuth(r gin.IRouter) error { + r.POST("/auth/login", s.loginHandler) + r.POST("/auth/refresh", s.refreshTokenHandler) + r.GET("/auth/user", s.authHandler, s.userHandler) + + return nil +} + +func (s *authHandler) loginHandler(c *gin.Context) { + if !s.adminEnabled { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + var loginVals login + if err := c.ShouldBind(&loginVals); err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + userID := loginVals.Username + password := loginVals.Password + + as, err := s.getAdminSecrets() + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + if userID != "admin" || password != as.adminPassword { + // we only support the admin account at the moment + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + token, err := s.createAdminToken(userID, as) + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + c.JSON(http.StatusOK, map[string]any{ + "token": token, + }) +} + +func (s *authHandler) refreshTokenHandler(c *gin.Context) { + if !s.adminEnabled { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + oldToken := s.getBearerToken(c) + if oldToken == "" { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + as, err := s.getAdminSecrets() + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + claims, err := s.checkIfAdminTokenExpired(oldToken, as) + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + user := s.getAdminUserFromClaims(claims) + + newToken, err := s.createAdminToken(user.Username, as) + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + c.JSON(http.StatusOK, map[string]any{ + "token": newToken, + }) +} + +func (s *authHandler) createAdminToken(username string, as adminSecrets) (string, error) { + // Create the token + token := jwt.New(jwt.SigningMethodHS256) + claims := token.Claims.(jwt.MapClaims) + claims["id"] = username + + expire := time.Now().Add(tokenValidTime) + claims["exp"] = expire.Unix() + claims["orig_iat"] = time.Now().Unix() + tokenString, err := token.SignedString(as.secret) + if err != nil { + return "", err + } + return tokenString, nil +} + +func (s *authHandler) checkIfAdminTokenExpired(token string, as adminSecrets) (jwt.MapClaims, error) { + jt, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) { + return as.secret, nil + }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) + if err != nil { + validationErr, ok := err.(*jwt.ValidationError) + if !ok || validationErr.Errors != jwt.ValidationErrorExpired { + return nil, err + } + } + + claims := jt.Claims.(jwt.MapClaims) + + origIat := int64(claims["orig_iat"].(float64)) + + if origIat < time.Now().Add(-tokenMaxRefreshTime).Unix() { + return nil, fmt.Errorf("token expired") + } + + return claims, nil +} + +func (s *authHandler) getBearerToken(c *gin.Context) string { + authHeader := c.GetHeader("Authorization") + if authHeader == "" { + return "" + } + + parts := strings.SplitN(authHeader, " ", 2) + if len(parts) != 2 || parts[0] != "Bearer" { + c.AbortWithStatus(http.StatusUnauthorized) + return "" + } + + return parts[1] +} + +func (s *authHandler) getUser(c *gin.Context) *User { + if s == nil { + return nil + } + token := s.getBearerToken(c) + if token == "" { + return nil + } + return s.getUserFromToken(token) +} + +func (s *authHandler) getUserFromToken(token string) *User { + user := s.getAdminUser(token) + if user != nil { + return user + } + return nil +} + +func (s *authHandler) getAdminUser(token string) *User { + as, err := s.getAdminSecrets() + if err != nil { + return nil + } + + jt, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) { + return as.secret, nil + }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) + if err != nil { + return nil + } + + claims, ok := jt.Claims.(jwt.MapClaims) + if !ok { + return nil + } + + return s.getAdminUserFromClaims(claims) +} + +func (s *authHandler) getAdminUserFromClaims(claims jwt.MapClaims) *User { + x, ok := claims["id"] + if !ok { + return nil + } + id, ok := x.(string) + if !ok { + return nil + } + + return &User{ + Username: id, + IsAdmin: true, + } +} + +func (s *authHandler) authHandler(c *gin.Context) { + if s == nil { + return + } + if s.getUser(c) == nil { + c.AbortWithStatus(http.StatusUnauthorized) + } +} + +func (s *authHandler) userHandler(c *gin.Context) { + user := s.getUser(c) + c.JSON(http.StatusOK, user) +} + +type adminSecrets struct { + secret []byte + adminPassword string +} + +func (s *authHandler) getAdminSecrets() (adminSecrets, error) { + if s.serverClient == nil { + return adminSecrets{}, fmt.Errorf("no serverClient set") + } + + var secret corev1.Secret + err := s.serverClient.Get(s.ctx, client.ObjectKey{Name: s.webuiSecretName, Namespace: "kluctl-system"}, &secret) + if err != nil { + return adminSecrets{}, err + } + + var ret adminSecrets + + x, ok := secret.Data["secret"] + if !ok { + return adminSecrets{}, fmt.Errorf("admin secret %s has no 'secret' key", s.webuiSecretName) + } + ret.secret = x + + x, ok = secret.Data["adminPassword"] + if !ok { + return adminSecrets{}, fmt.Errorf("admin secret %s has no adminPassword", s.webuiSecretName) + } + ret.adminPassword = string(x) + + return ret, nil +} diff --git a/pkg/webui/server.go b/pkg/webui/server.go index 6aad01bd9..31b45181c 100644 --- a/pkg/webui/server.go +++ b/pkg/webui/server.go @@ -27,15 +27,35 @@ type CommandResultsServer struct { store results.ResultStore cam *clusterAccessorManager vam *validatorManager + + // this is the client for the k8s cluster where the server runs on + serverClient client.Client + + auth *authHandler } -func NewCommandResultsServer(ctx context.Context, store *results.ResultsCollector, configs []*rest.Config) *CommandResultsServer { +func NewCommandResultsServer(ctx context.Context, store *results.ResultsCollector, configs []*rest.Config, serverClient client.Client, authEnabled bool) *CommandResultsServer { ret := &CommandResultsServer{ ctx: ctx, store: store, cam: &clusterAccessorManager{ ctx: ctx, }, + serverClient: serverClient, + } + + adminEnabled := false + if serverClient != nil { + adminEnabled = true + } + + if authEnabled { + ret.auth = &authHandler{ + ctx: ctx, + adminEnabled: adminEnabled, + serverClient: serverClient, + webuiSecretName: "admin-secret", + } } for _, config := range configs { @@ -76,19 +96,28 @@ func (s *CommandResultsServer) Run(port int) error { router := gin.Default() + if s.auth != nil { + err = s.auth.setupAuth(router) + if err != nil { + return err + } + } + err = s.setupStaticRoutes(router) if err != nil { return err } api := router.Group("/api") - api.GET("/getShortNames", s.getShortNames) - api.GET("/getResult", s.getResult) - api.GET("/getResultSummary", s.getResultSummary) - api.GET("/getResultObject", s.getResultObject) - api.POST("/validateNow", s.validateNow) - api.POST("/reconcileNow", s.reconcileNow) - api.POST("/deployNow", s.deployNow) + api.GET("/getShortNames", s.auth.authHandler, s.getShortNames) + api.GET("/getResult", s.auth.authHandler, s.getResult) + api.GET("/getResultSummary", s.auth.authHandler, s.getResultSummary) + api.GET("/getResultObject", s.auth.authHandler, s.getResultObject) + api.POST("/validateNow", s.auth.authHandler, s.validateNow) + api.POST("/reconcileNow", s.auth.authHandler, s.reconcileNow) + api.POST("/deployNow", s.auth.authHandler, s.deployNow) + + // handles authentication via the first message api.Any("/ws", s.ws) address := fmt.Sprintf(":%d", port) diff --git a/pkg/webui/ui/src/api.tsx b/pkg/webui/ui/src/api.tsx index f175bbd9a..b70f1d8bc 100644 --- a/pkg/webui/ui/src/api.tsx +++ b/pkg/webui/ui/src/api.tsx @@ -15,7 +15,6 @@ import "./staticbuild.d.ts" import { loadScript } from "./loadscript"; import { sleep } from "./utils/misc"; -const apiUrl = "/api" const staticPath = "./staticdata" export enum ObjectType { @@ -24,6 +23,11 @@ export enum ObjectType { Applied = "applied", } +export interface User { + username: string + isAdmin: boolean +} + export interface Api { getShortNames(): Promise listenUpdates(filterProject: string | undefined, filterSubDir: string | undefined, handle: (msg: any) => void): Promise<() => void> @@ -35,64 +39,109 @@ export interface Api { deployNow(cluster: string, name: string, namespace: string): Promise } -class RealOrStaticApi implements Api { - api: Promise - - constructor() { - this.api = this.buildApi() - } - - async buildApi(): Promise { - const p = loadScript(staticPath + "/summaries.js") - try { - await p - return new StaticApi() - } catch (error) { - return new RealApi() - } - } - - async deployNow(cluster: string, name: string, namespace: string): Promise { - return (await this.api).deployNow(cluster, name, namespace) - } - - async getResult(resultId: string): Promise { - return (await this.api).getResult(resultId) +export async function checkStaticBuild() { + const p = loadScript(staticPath + "/summaries.js") + try { + await p + return true + } catch (error) { + return false } +} - async getResultObject(resultId: string, ref: ObjectRef, objectType: string): Promise { - return (await this.api).getResultObject(resultId, ref, objectType) - } +export class RealApi implements Api { + getToken: (() => string) | undefined; + onUnauthorized: () => void; + onTokenRefresh: (newToken: string) => void; - async getResultSummary(resultId: string): Promise { - return (await this.api).getResultSummary(resultId) + constructor(getToken: (() => string) | undefined, onUnauthorized: () => void, onTokenRefresh: (newToken: string) => void) { + this.getToken = getToken + this.onUnauthorized = onUnauthorized + this.onTokenRefresh = onTokenRefresh } - async getShortNames(): Promise { - return (await this.api).getShortNames() + setAuthorizationHeader(headers: any) { + if (!this.getToken) { + return + } + headers['Authorization'] = "Bearer " + this.getToken() } - async listenUpdates(filterProject: string | undefined, filterSubDir: string | undefined, handle: (msg: any) => void): Promise<() => void> { - return (await this.api).listenUpdates(filterProject, filterSubDir, handle) + async refreshToken() { + const headers = { + 'Accept': 'application/json', + } + this.setAuthorizationHeader(headers) + const resp = await fetch("/auth/refresh", { + method: "POST", + headers: headers, + }) + if (resp.status === 401) { + this.onUnauthorized() + throw Error(resp.statusText) + } + const j = await resp.json() + this.onTokenRefresh(j.token) + } + + async handleErrors(response: Response, retry?: () => Promise): Promise { + if (!response.ok) { + if (response.status === 401) { + if (retry) { + console.log("retrying with token refresh") + await this.refreshToken() + const newResp = await retry() + return await this.handleErrors(newResp) + } else { + this.onUnauthorized() + } + } + throw Error(response.statusText) + } + return response } - async reconcileNow(cluster: string, name: string, namespace: string): Promise { - return (await this.api).reconcileNow(cluster, name, namespace) + async doGet(path: string, params?: URLSearchParams) { + let url = path + if (params) { + url += "?" + params.toString() + } + const doFetch = () => { + const headers = { + 'Accept': 'application/json', + } + this.setAuthorizationHeader(headers) + return fetch(url, { + method: "GET", + headers: headers, + }) + } + let resp = await doFetch() + resp = await this.handleErrors(resp, doFetch) + return resp.json() } - async validateNow(project: ProjectKey, target: TargetKey): Promise { - return (await this.api).validateNow(project, target) + async doPost(path: string, body: any) { + let url = path + const doFetch = () => { + const headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + } + this.setAuthorizationHeader(headers) + return fetch(url, { + method: "POST", + body: JSON.stringify(body), + headers: headers, + }) + } + let resp = await doFetch() + resp = await this.handleErrors(resp, doFetch) + return resp } -} - -export const api = new RealOrStaticApi() -class RealApi implements Api { async getShortNames(): Promise { - let url = `${apiUrl}/getShortNames` - return fetch(url) - .then(handleErrors) - .then((response) => response.json()); + return this.doGet("/api/getShortNames") } async listenUpdates(filterProject: string | undefined, filterSubDir: string | undefined, handle: (msg: any) => void): Promise<() => void> { @@ -100,7 +149,7 @@ class RealApi implements Api { if (process.env.NODE_ENV === 'development') { host = "localhost:9090" } - let url = `ws://${host}${apiUrl}/ws` + let url = `ws://${host}/api/ws` const params = new URLSearchParams() if (filterProject) { params.set("filterProject", filterProject) @@ -110,10 +159,11 @@ class RealApi implements Api { } url += "?" + params.toString() + const getToken = this.getToken let ws: WebSocket | undefined; let cancelled = false - const connect = () => { + const connect = async () => { if (cancelled) { return } @@ -122,6 +172,9 @@ class RealApi implements Api { ws = new WebSocket(url); ws.onopen = function () { console.log("ws connected") + if (getToken) { + ws!.send(JSON.stringify({ "type": "auth", "token": getToken() })) + } } ws.onclose = function (event) { console.log("ws close") @@ -141,7 +194,7 @@ class RealApi implements Api { } } - connect() + await connect() return () => { console.log("ws cancel") @@ -153,53 +206,36 @@ class RealApi implements Api { } async getResult(resultId: string) { - let url = `${apiUrl}/getResult?resultId=${resultId}` - return fetch(url) - .then(handleErrors) - .then(response => response.text()) - .then(json => { - return new CommandResult(json) - }); + const params = new URLSearchParams() + params.set("resultId", resultId) + const json = await this.doGet("/api/getResult", params) + return new CommandResult(json) } async getResultSummary(resultId: string) { - let url = `${apiUrl}/getResultSummary?resultId=${resultId}` - return fetch(url) - .then(handleErrors) - .then(response => response.text()) - .then(json => { - return new CommandResultSummary(json) - }); + const params = new URLSearchParams() + params.set("resultId", resultId) + const json = await this.doGet("/api/getResultSummary", params) + return new CommandResultSummary(json) } async getResultObject(resultId: string, ref: ObjectRef, objectType: string) { - let url = `${apiUrl}/getResultObject?resultId=${resultId}&${buildRefParams(ref)}&objectType=${objectType}` - return fetch(url) - .then(handleErrors) - .then(response => response.json()); - } - - async doPost(f: string, body: any) { - let url = `${apiUrl}/${f}` - return fetch(url, { - method: "POST", - body: JSON.stringify(body), - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }, - }).then(handleErrors); + const params = new URLSearchParams() + params.set("resultId", resultId) + params.set("objectType", objectType) + buildRefParams(ref, params) + return await this.doGet("/api/getResultObject", params) } async validateNow(project: ProjectKey, target: TargetKey) { - return this.doPost("validateNow", { + return this.doPost("/api/validateNow", { "project": project, "target": target, }) } async deployNow(cluster: string, name: string, namespace: string): Promise { - return this.doPost("deployNow", { + return this.doPost("/api/deployNow", { "cluster": cluster, "name": name, "namespace": namespace, @@ -207,7 +243,7 @@ class RealApi implements Api { } async reconcileNow(cluster: string, name: string, namespace: string): Promise { - return this.doPost("reconcileNow", { + return this.doPost("/api/reconcileNow", { "cluster": cluster, "name": name, "namespace": namespace, @@ -215,7 +251,7 @@ class RealApi implements Api { } } -class StaticApi implements Api { +export class StaticApi implements Api { async getShortNames(): Promise { await loadScript(staticPath + "/shortnames.js") return staticShortNames @@ -244,12 +280,14 @@ class StaticApi implements Api { await loadScript(staticPath + `/result-${resultId}.js`) return staticResults.get(resultId) } + async getResultSummary(resultId: string): Promise { await loadScript(staticPath + "/summaries.js") return staticSummaries.filter(s => { return s.id === resultId }).at(0) } + async getResultObject(resultId: string, ref: ObjectRef, objectType: string): Promise { const result = await this.getResult(resultId) const object = result.objects?.find(x => _.isEqual(x.ref, ref)) @@ -267,26 +305,21 @@ class StaticApi implements Api { throw new Error("unknown object type " + objectType) } } + validateNow(project: ProjectKey, target: TargetKey): Promise { throw new Error("not implemented") } + reconcileNow(cluster: string, name: string, namespace: string): Promise { throw new Error("not implemented") } + deployNow(cluster: string, name: string, namespace: string): Promise { throw new Error("not implemented") } } -function handleErrors(response: Response) { - if (!response.ok) { - throw Error(response.statusText) - } - return response -} - -function buildRefParams(ref: ObjectRef): string { - const params = new URLSearchParams() +function buildRefParams(ref: ObjectRef, params: URLSearchParams) { params.set("kind", ref.kind) params.set("name", ref.name) if (ref.group) { @@ -298,7 +331,6 @@ function buildRefParams(ref: ObjectRef): string { if (ref.namespace) { params.set("namespace", ref.namespace) } - return params.toString() } export function buildRefString(ref: ObjectRef): string { @@ -347,31 +379,3 @@ export function findObjectByRef(l: ResultObject[] | undefined, ref: ObjectRef, f return _.isEqual(x.ref, ref) }) } - -export function usePromise(p?: Promise): T { - if (p === undefined) { - throw new Promise(() => undefined) - } - - const promise = p as any - if (promise.status === 'fulfilled') { - return promise.value; - } else if (promise.status === 'rejected') { - throw promise.reason; - } else if (promise.status === 'pending') { - throw promise; - } else { - promise.status = 'pending'; - p.then( - result => { - promise.status = 'fulfilled'; - promise.value = result; - }, - reason => { - promise.status = 'rejected'; - promise.reason = reason; - }, - ); - throw promise; - } -} \ No newline at end of file diff --git a/pkg/webui/ui/src/components/App.tsx b/pkg/webui/ui/src/components/App.tsx index 82f605ddb..e7c100e0f 100644 --- a/pkg/webui/ui/src/components/App.tsx +++ b/pkg/webui/ui/src/components/App.tsx @@ -1,4 +1,13 @@ -import React, { createContext, Dispatch, SetStateAction, useEffect, useMemo, useRef, useState } from 'react'; +import React, { + createContext, + Dispatch, + SetStateAction, + useContext, + useEffect, + useMemo, + useRef, + useState +} from 'react'; import '../index.css'; import { Box, ThemeProvider } from "@mui/material"; @@ -7,8 +16,10 @@ import LeftDrawer from "./LeftDrawer"; import { light } from './theme'; import { ActiveFilters } from './result-view/NodeStatusFilter'; import { CommandResultSummary, ProjectTargetKey, ValidateResult } from "../models"; -import { api } from "../api"; +import { Api, checkStaticBuild, RealApi, StaticApi } from "../api"; import { buildProjectSummaries, ProjectSummary } from "../project-summaries"; +import Login from "./Login"; +import { Loading } from "./Loading"; export interface AppOutletContext { filters?: ActiveFilters @@ -29,7 +40,10 @@ export const AppContext = createContext({ validateResults: new Map(), }); -const App = () => { +export const ApiContext = createContext(new StaticApi()) + +const LoggedInApp = (props: {onUnauthorized: () => void}) => { + const api = useContext(ApiContext) const [filters, setFilters] = useState() const summariesRef = useRef>(new Map()) @@ -37,6 +51,8 @@ const App = () => { const [summaries, setSummaries] = useState(summariesRef.current) const [validateResults, setValidateResults] = useState(validateResultsRef.current) + const onUnauthorized = props.onUnauthorized + useEffect(() => { const updateSummary = (rs: CommandResultSummary) => { console.log("update_summary", rs.id, rs.commandInfo.startTime) @@ -57,7 +73,8 @@ const App = () => { } console.log("starting listenResults") - const cancel = api.listenUpdates(undefined, undefined, msg => { + let cancel: Promise<() => void> + cancel = api.listenUpdates(undefined, undefined, msg => { switch(msg.type) { case "update_summary": updateSummary(msg.summary) @@ -68,13 +85,18 @@ const App = () => { case "validate_result": updateValidateResult(msg.key, msg.result) break + case "auth_result": + if (!msg.success) { + cancel.then(c => c()) + onUnauthorized() + } } }) return () => { console.log("cancel listenResults") cancel.then(c => c()) } - }, []) + }, [api, onUnauthorized]) const projects = useMemo(() => { return buildProjectSummaries(summaries, validateResults) @@ -102,4 +124,85 @@ const App = () => { ); }; +const App = () => { + const [api, setApi] = useState() + const [needToken, setNeedToken] = useState(false) + + const storage = localStorage + + const getToken = () => { + const token = storage.getItem("token") + if (!token) { + return "" + } + return JSON.parse(token) + } + const setToken = (token?: string) => { + if (!token) { + storage.removeItem("token") + } else { + storage.setItem("token", JSON.stringify(token)) + } + } + + const onUnauthorized = () => { + console.log("handle onUnauthorized") + setToken(undefined) + setApi(undefined) + setNeedToken(true) + } + const onTokenRefresh = (newToken: string) => { + console.log("handle onTokenRefresh") + setToken(newToken) + } + + const handleLoginSucceeded = (token: string) => { + console.log("handle saveToken") + setToken(token); + setApi(new RealApi(getToken, onUnauthorized, onTokenRefresh)) + }; + + useEffect(() => { + if (api) { + return + } + + const doInit = async () => { + const isStatic = await checkStaticBuild() + if (isStatic) { + setApi(new StaticApi()) + } else { + // check if we don't need auth (running locally?) + const noAuthApi = new RealApi(undefined, onUnauthorized, onTokenRefresh) + try { + await noAuthApi.getShortNames() + setToken(undefined) + setNeedToken(false) + setApi(noAuthApi) + } catch (error) { + if (!getToken()) { + setNeedToken(true) + } else { + setApi(new RealApi(getToken, onUnauthorized, onTokenRefresh)) + } + } + } + } + doInit() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + if (needToken && !getToken()) { + return + } + + if (!api) { + return + } + + return + + +} + export default App; diff --git a/pkg/webui/ui/src/components/Loading.tsx b/pkg/webui/ui/src/components/Loading.tsx index 9b26bf95e..307f9d34b 100644 --- a/pkg/webui/ui/src/components/Loading.tsx +++ b/pkg/webui/ui/src/components/Loading.tsx @@ -1,4 +1,5 @@ import { Box, CircularProgress } from "@mui/material"; +import { DependencyList, useEffect, useState } from "react"; export const Loading = () => { return @@ -12,4 +13,27 @@ export const Loading = () => { -} \ No newline at end of file +} + +export function useLoadingHelper(load: () => Promise, deps: DependencyList): [boolean, any, T | undefined] { + const [loading, setLoading] = useState(true) + const [error, setError] = useState() + const [content, setContent] = useState() + + useEffect(() => { + const doStartLoading = async () => { + try { + const c = await load() + setContent(c) + setLoading(false) + } catch (error) { + setError(error) + setLoading(false) + } + } + doStartLoading() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [...deps, load]) + + return [loading, error, content] +} diff --git a/pkg/webui/ui/src/components/Login.tsx b/pkg/webui/ui/src/components/Login.tsx new file mode 100644 index 000000000..b1fd66064 --- /dev/null +++ b/pkg/webui/ui/src/components/Login.tsx @@ -0,0 +1,56 @@ +import React, { SyntheticEvent, useState } from 'react'; + +//import './Login.css'; + +interface loginCredentials { + username: string + password: string +} + +async function loginUser(creds: loginCredentials) { + const url = `/auth/login` + return fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(creds) + }).then(data => data.json()) + .then(token => token.token) +} + +export default function Login(props: { setToken: (token: string) => void}) { + const [username, setUserName] = useState(); + const [password, setPassword] = useState(); + + const handleSubmit = async (e: SyntheticEvent) => { + e.preventDefault(); + if (!username || !password) { + return + } + const token = await loginUser({ + username: username, + password: password, + }); + props.setToken(token); + } + + return( +
    +

    Please Log In

    +
    + + +
    + +
    +
    +
    + ) +} diff --git a/pkg/webui/ui/src/components/ObjectYaml.tsx b/pkg/webui/ui/src/components/ObjectYaml.tsx index 80a71a2b8..388931dcd 100644 --- a/pkg/webui/ui/src/components/ObjectYaml.tsx +++ b/pkg/webui/ui/src/components/ObjectYaml.tsx @@ -1,28 +1,25 @@ import { CommandResultProps } from "./result-view/CommandResultView"; import { ObjectRef } from "../models"; -import { api, ObjectType, usePromise } from "../api"; -import React, { Suspense, useEffect, useState } from "react"; +import { ObjectType } from "../api"; +import React, { useContext } from "react"; import { CodeViewer } from "./CodeViewer"; +import { Loading, useLoadingHelper } from "./Loading"; +import { ApiContext } from "./App"; import * as yaml from 'js-yaml'; -import { Loading } from "./Loading"; export const ObjectYaml = (props: {treeProps: CommandResultProps, objectRef: ObjectRef, objectType: ObjectType}) => { - const [promise, setPromise] = useState>() + const api = useContext(ApiContext) + const [loading, error, content] = useLoadingHelper(async () => { + const o = await api.getResultObject(props.treeProps.summary.id, props.objectRef, props.objectType) + return yaml.dump(o) + }, [props.treeProps.summary.id, props.objectRef, props.objectType]) - useEffect(() => { - const getData = async () => { - const o = await api.getResultObject(props.treeProps.summary.id, props.objectRef, props.objectType) - return yaml.dump(o) - } - setPromise(getData()) - }, [props.treeProps, props.objectRef, props.objectType]) - - const Content = () => { - return + if (loading) { + return + } else if (error) { + return <>Error + } else { + return } - - return }> - - -} \ No newline at end of file +} diff --git a/pkg/webui/ui/src/components/Router.tsx b/pkg/webui/ui/src/components/Router.tsx index a08c8abd5..54af5c317 100644 --- a/pkg/webui/ui/src/components/Router.tsx +++ b/pkg/webui/ui/src/components/Router.tsx @@ -2,7 +2,7 @@ import { createHashRouter, useRouteError } from "react-router-dom"; import React from "react"; import App from "./App"; import { TargetsView } from "./targets-view/TargetsView"; -import { commandResultLoader, CommandResultView } from "./result-view/CommandResultView"; +import { CommandResultView } from "./result-view/CommandResultView"; function ErrorPage() { const error = useRouteError() as any; @@ -32,7 +32,6 @@ export const Router = createHashRouter([ { path: "results/:id", element: , - loader: commandResultLoader, errorElement: , }, ], diff --git a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx index 70c16cb4b..3bb0b0edc 100644 --- a/pkg/webui/ui/src/components/result-view/CommandResultView.tsx +++ b/pkg/webui/ui/src/components/result-view/CommandResultView.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { useState } from 'react'; +import { useContext, useState } from 'react'; import { Box, Checkbox, @@ -15,11 +15,12 @@ import { NodeData } from "./nodes/NodeData"; import { SidePanel } from "./SidePanel"; import { ActiveFilters } from "./NodeStatusFilter"; import CommandResultTree from "./CommandResultTree"; -import { useLoaderData } from "react-router-dom"; -import { useAppOutletContext } from "../App"; +import { useParams } from "react-router-dom"; +import { ApiContext, useAppOutletContext } from "../App"; import { ChangesIcon, CheckboxCheckedIcon, CheckboxIcon, StarIcon, WarningSignIcon } from '../../icons/Icons'; import { dark } from '../theme'; -import { api } from "../../api"; +import { Api } from "../../api"; +import { Loading, useLoadingHelper } from "../Loading"; export interface CommandResultProps { shortNames: ShortName[] @@ -27,15 +28,15 @@ export interface CommandResultProps { commandResult: CommandResult } -export async function commandResultLoader({ params }: any) { - const result = api.getResult(params.id) - const shortNames = api.getShortNames() - const rs = api.getResult(params.id) +async function doLoadCommandResult(api: Api, resultId: string): Promise { + const shortNames = await api.getShortNames() + const rs = await api.getResultSummary(resultId) + const result = await api.getResult(resultId) return { - shortNames: await shortNames, - summary: await rs, - commandResult: await result, + shortNames: shortNames, + summary: rs, + commandResult: result, } } @@ -117,9 +118,17 @@ const defaultFilters = { export const CommandResultView = () => { const context = useAppOutletContext(); - const commandResultProps = useLoaderData() as CommandResultProps; + const api = useContext(ApiContext) const [sidePanelNode, setSidePanelNode] = useState(); + const {id} = useParams() + const [loading, loadingError, commandResultProps] = useLoadingHelper(() => { + if (!id) { + return Promise.resolve(undefined) + } + return doLoadCommandResult(api, id) + }, [id]) + const divider = { })); } + if (loading) { + return + } else if (loadingError) { + return <>Error + } + return { const { ps, ts, selectedCardRect } = props; + const api = useContext(ApiContext) const theme = useTheme(); - const [promise, setPromise] = useState>(new Promise(() => undefined)); const [selectedCommandResult, setSelectedCommandResult] = useState(); const [prevTargetSummary, setPrevTargetSummary] = useState(ts); const [cardsCoords, setCardsCoords] = useState<{ left: number, top: number }[]>([]); @@ -45,18 +47,13 @@ export const CommandResultDetailsDrawer = React.memo((props: { setSelectedCommandResult(ts?.commandResults?.[0]); } - useEffect(() => { + const [loading, loadingError, nodeData] = useLoadingHelper(() => { if (selectedCommandResult === undefined) { - return + return Promise.resolve(undefined) } - setPromise(doGetRootNode(selectedCommandResult)); + return doGetRootNode(api, selectedCommandResult) }, [selectedCommandResult]) - const Content = (props: { onClose: () => void }) => { - const node = usePromise(promise) - return - } - const cardsContainerElem = useRef(); useEffect(() => { @@ -130,6 +127,10 @@ export const CommandResultDetailsDrawer = React.memo((props: { }) }, [ps, ts, cardsCoords, zIndex, theme.transitions, selectedCommandResult]) + if (loadingError) { + return <>Error + } + return <> {ps && ts && ts.commandResults && ts.commandResults.length > 0 && - }> - - + {loading ? : + + } diff --git a/pkg/webui/ui/src/components/targets-view/Targets.tsx b/pkg/webui/ui/src/components/targets-view/Targets.tsx index 4a7bf1f4a..b12ffa5e1 100644 --- a/pkg/webui/ui/src/components/targets-view/Targets.tsx +++ b/pkg/webui/ui/src/components/targets-view/Targets.tsx @@ -2,13 +2,13 @@ import { KluctlDeploymentInfo } from "../../models"; import { ActionMenuItem, ActionsMenu } from "../ActionsMenu"; import Paper from "@mui/material/Paper"; import { Box, Typography, useTheme } from "@mui/material"; -import React from "react"; +import React, { useContext } from "react"; import Tooltip from "@mui/material/Tooltip"; import { Favorite, HeartBroken, PublishedWithChanges } from "@mui/icons-material"; -import { api } from "../../api"; import { CpuIcon, FingerScanIcon, MessageQuestionIcon, TargetIcon } from "../../icons/Icons"; import { ProjectSummary, TargetSummary } from "../../project-summaries"; import { calcAgo } from "../../utils/duration"; +import { ApiContext } from "../App"; const StatusIcon = (props: { ps: ProjectSummary, ts: TargetSummary }) => { let icon: React.ReactElement @@ -55,6 +55,7 @@ const StatusIcon = (props: { ps: ProjectSummary, ts: TargetSummary }) => { } export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSelectTarget: (ts?: TargetSummary) => void }) => { + const api = useContext(ApiContext) const actionMenuItems: ActionMenuItem[] = [] actionMenuItems.push({ diff --git a/pkg/webui/websocket.go b/pkg/webui/websocket.go index 2a21cb462..c01bd3ff0 100644 --- a/pkg/webui/websocket.go +++ b/pkg/webui/websocket.go @@ -43,6 +43,13 @@ func (s *CommandResultsServer) ws(c *gin.Context) { } defer conn.Close(websocket.StatusInternalError, "the sky is falling") + // don't try anything else before we get the auth message + user, err := s.wsHandleAuth(conn) + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + repoKey, err := types.ParseGitRepoKey(args.FilterProject) if err != nil { _ = c.AbortWithError(http.StatusBadRequest, err) @@ -57,7 +64,7 @@ func (s *CommandResultsServer) ws(c *gin.Context) { } } - err = s.wsHandle(conn, filter) + err = s.wsHandle(conn, user, filter) if err != nil { cs := websocket.CloseStatus(err) if cs == websocket.StatusNormalClosure || cs == websocket.StatusGoingAway { @@ -67,7 +74,58 @@ func (s *CommandResultsServer) ws(c *gin.Context) { } } -func (s *CommandResultsServer) wsHandle(c *websocket.Conn, filter *result.ProjectKey) error { +func (s *CommandResultsServer) wsHandleAuth(c *websocket.Conn) (*User, error) { + if s.auth == nil { + return nil, nil + } + + t, msg, err := c.Read(s.ctx) + if err != nil { + return nil, err + } + if t != websocket.MessageText { + return nil, fmt.Errorf("unexpected message type") + } + + type authMsg struct { + Type string `json:"type"` + Token string `json:"token"` + } + type authResult struct { + Type string `json:"type"` + Success bool `json:"success"` + } + var am authMsg + err = yaml.ReadYamlString(string(msg), &am) + if err != nil { + return nil, err + } + if am.Type != "auth" { + return nil, fmt.Errorf("unexpected message type") + } + + user := s.auth.getUserFromToken(am.Token) + if user == nil { + msg, _ := yaml.WriteJsonString(authResult{ + Type: "auth_result", + Success: false, + }) + _ = c.Write(s.ctx, websocket.MessageText, []byte(msg)) + return nil, fmt.Errorf("invalid token") + } else { + msg, _ := yaml.WriteJsonString(authResult{ + Type: "auth_result", + Success: true, + }) + err = c.Write(s.ctx, websocket.MessageText, []byte(msg)) + if err != nil { + return nil, err + } + return user, nil + } +} + +func (s *CommandResultsServer) wsHandle(c *websocket.Conn, user *User, filter *result.ProjectKey) error { initial, ch, cancel, err := s.store.WatchCommandResultSummaries(results.ListCommandResultSummariesOptions{ProjectFilter: filter}) if err != nil { return err From 20ab3bf15d126383135e3d3b0ea62b3ce2867dd1 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Jun 2023 15:58:24 +0200 Subject: [PATCH 1175/2268] fix: Don't try refresh/logout when doing the initial creds check --- pkg/webui/ui/src/api.tsx | 22 ++++++++++++++-------- pkg/webui/ui/src/components/App.tsx | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/pkg/webui/ui/src/api.tsx b/pkg/webui/ui/src/api.tsx index b70f1d8bc..2bd092be2 100644 --- a/pkg/webui/ui/src/api.tsx +++ b/pkg/webui/ui/src/api.tsx @@ -50,11 +50,11 @@ export async function checkStaticBuild() { } export class RealApi implements Api { - getToken: (() => string) | undefined; - onUnauthorized: () => void; - onTokenRefresh: (newToken: string) => void; + getToken?: (() => string); + onUnauthorized?: () => void; + onTokenRefresh?: (newToken: string) => void; - constructor(getToken: (() => string) | undefined, onUnauthorized: () => void, onTokenRefresh: (newToken: string) => void) { + constructor(getToken?: (() => string), onUnauthorized?: () => void, onTokenRefresh?: (newToken: string) => void) { this.getToken = getToken this.onUnauthorized = onUnauthorized this.onTokenRefresh = onTokenRefresh @@ -77,23 +77,29 @@ export class RealApi implements Api { headers: headers, }) if (resp.status === 401) { - this.onUnauthorized() + if (this.onUnauthorized) { + this.onUnauthorized() + } throw Error(resp.statusText) } const j = await resp.json() - this.onTokenRefresh(j.token) + if (this.onTokenRefresh) { + this.onTokenRefresh(j.token) + } } async handleErrors(response: Response, retry?: () => Promise): Promise { if (!response.ok) { if (response.status === 401) { - if (retry) { + if (this.getToken && retry) { console.log("retrying with token refresh") await this.refreshToken() const newResp = await retry() return await this.handleErrors(newResp) } else { - this.onUnauthorized() + if (this.onUnauthorized) { + this.onUnauthorized() + } } } throw Error(response.statusText) diff --git a/pkg/webui/ui/src/components/App.tsx b/pkg/webui/ui/src/components/App.tsx index e7c100e0f..c330fd775 100644 --- a/pkg/webui/ui/src/components/App.tsx +++ b/pkg/webui/ui/src/components/App.tsx @@ -173,7 +173,7 @@ const App = () => { setApi(new StaticApi()) } else { // check if we don't need auth (running locally?) - const noAuthApi = new RealApi(undefined, onUnauthorized, onTokenRefresh) + const noAuthApi = new RealApi(undefined, undefined, undefined) try { await noAuthApi.getShortNames() setToken(undefined) From d8c2c696e85bc0ec122e6251e5a1f82925869976 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Jun 2023 15:58:40 +0200 Subject: [PATCH 1176/2268] fix: Don't use load as dep in useLoadingHelper --- pkg/webui/ui/src/components/Loading.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/webui/ui/src/components/Loading.tsx b/pkg/webui/ui/src/components/Loading.tsx index 307f9d34b..4cf7b8189 100644 --- a/pkg/webui/ui/src/components/Loading.tsx +++ b/pkg/webui/ui/src/components/Loading.tsx @@ -33,7 +33,7 @@ export function useLoadingHelper(load: () => Promise, deps: DependencyList } doStartLoading() // eslint-disable-next-line react-hooks/exhaustive-deps - }, [...deps, load]) + }, deps) return [loading, error, content] } From 61bcb9cb7597898b0f63108352b2d08018cd0545 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Jun 2023 15:22:36 +0200 Subject: [PATCH 1177/2268] chore: Run npm run build --- pkg/webui/ui/build/index.html | 2 +- pkg/webui/ui/build/static/js/main.js | 382 ++++++++++++++------------- 2 files changed, 195 insertions(+), 189 deletions(-) diff --git a/pkg/webui/ui/build/index.html b/pkg/webui/ui/build/index.html index 5e73bd915..08726c8bf 100644 --- a/pkg/webui/ui/build/index.html +++ b/pkg/webui/ui/build/index.html @@ -1 +1 @@ -React App
    \ No newline at end of file +React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js index 844adb822..4a9d496ae 100644 --- a/pkg/webui/ui/build/static/js/main.js +++ b/pkg/webui/ui/build/static/js/main.js @@ -34880,7 +34880,7 @@ function useOutlet(context) { * @see https://reactrouter.com/hooks/use-params */ function useParams() { - var _React$useContext5 = React.useContext(RouteContext), + var _React$useContext5 = react.useContext(RouteContext), matches = _React$useContext5.matches; var routeMatch = matches[matches.length - 1]; return routeMatch ? routeMatch.params : {}; @@ -52032,11 +52032,196 @@ var loadedScripts=new Map();var loadScript=function loadScript(fileUrl){var asyn ;// CONCATENATED MODULE: ./src/utils/misc.ts function getLastPathElement(url){if(url===undefined){return undefined;}if(!url){return"";}var s=url.split("/");return s[s.length-1];}function sleep(ms){return new Promise(function(resolve){return setTimeout(resolve,ms);});} ;// CONCATENATED MODULE: ./src/api.tsx -var apiUrl="/api";var staticPath="./staticdata";var ObjectType=/*#__PURE__*/function(ObjectType){ObjectType["Rendered"]="rendered";ObjectType["Remote"]="remote";ObjectType["Applied"]="applied";return ObjectType;}({});var RealOrStaticApi=/*#__PURE__*/function(){function RealOrStaticApi(){classCallCheck_classCallCheck(this,RealOrStaticApi);this.api=void 0;this.api=this.buildApi();}createClass_createClass(RealOrStaticApi,[{key:"buildApi",value:function(){var _buildApi=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var p;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:p=loadScript(staticPath+"/summaries.js");_context.prev=1;_context.next=4;return p;case 4:return _context.abrupt("return",new StaticApi());case 7:_context.prev=7;_context.t0=_context["catch"](1);return _context.abrupt("return",new RealApi());case 10:case"end":return _context.stop();}},_callee,null,[[1,7]]);}));function buildApi(){return _buildApi.apply(this,arguments);}return buildApi;}()},{key:"deployNow",value:function(){var _deployNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee2(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee2$(_context2){while(1)switch(_context2.prev=_context2.next){case 0:_context2.next=2;return this.api;case 2:return _context2.abrupt("return",_context2.sent.deployNow(cluster,name,namespace));case 3:case"end":return _context2.stop();}},_callee2,this);}));function deployNow(_x,_x2,_x3){return _deployNow.apply(this,arguments);}return deployNow;}()},{key:"getResult",value:function(){var _getResult=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee3(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee3$(_context3){while(1)switch(_context3.prev=_context3.next){case 0:_context3.next=2;return this.api;case 2:return _context3.abrupt("return",_context3.sent.getResult(resultId));case 3:case"end":return _context3.stop();}},_callee3,this);}));function getResult(_x4){return _getResult.apply(this,arguments);}return getResult;}()},{key:"getResultObject",value:function(){var _getResultObject=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee4(resultId,ref,objectType){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee4$(_context4){while(1)switch(_context4.prev=_context4.next){case 0:_context4.next=2;return this.api;case 2:return _context4.abrupt("return",_context4.sent.getResultObject(resultId,ref,objectType));case 3:case"end":return _context4.stop();}},_callee4,this);}));function getResultObject(_x5,_x6,_x7){return _getResultObject.apply(this,arguments);}return getResultObject;}()},{key:"getResultSummary",value:function(){var _getResultSummary=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee5(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee5$(_context5){while(1)switch(_context5.prev=_context5.next){case 0:_context5.next=2;return this.api;case 2:return _context5.abrupt("return",_context5.sent.getResultSummary(resultId));case 3:case"end":return _context5.stop();}},_callee5,this);}));function getResultSummary(_x8){return _getResultSummary.apply(this,arguments);}return getResultSummary;}()},{key:"getShortNames",value:function(){var _getShortNames=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee6(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee6$(_context6){while(1)switch(_context6.prev=_context6.next){case 0:_context6.next=2;return this.api;case 2:return _context6.abrupt("return",_context6.sent.getShortNames());case 3:case"end":return _context6.stop();}},_callee6,this);}));function getShortNames(){return _getShortNames.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee7(filterProject,filterSubDir,handle){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee7$(_context7){while(1)switch(_context7.prev=_context7.next){case 0:_context7.next=2;return this.api;case 2:return _context7.abrupt("return",_context7.sent.listenUpdates(filterProject,filterSubDir,handle));case 3:case"end":return _context7.stop();}},_callee7,this);}));function listenUpdates(_x9,_x10,_x11){return _listenUpdates.apply(this,arguments);}return listenUpdates;}()},{key:"reconcileNow",value:function(){var _reconcileNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee8(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee8$(_context8){while(1)switch(_context8.prev=_context8.next){case 0:_context8.next=2;return this.api;case 2:return _context8.abrupt("return",_context8.sent.reconcileNow(cluster,name,namespace));case 3:case"end":return _context8.stop();}},_callee8,this);}));function reconcileNow(_x12,_x13,_x14){return _reconcileNow.apply(this,arguments);}return reconcileNow;}()},{key:"validateNow",value:function(){var _validateNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee9(project,target){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee9$(_context9){while(1)switch(_context9.prev=_context9.next){case 0:_context9.next=2;return this.api;case 2:return _context9.abrupt("return",_context9.sent.validateNow(project,target));case 3:case"end":return _context9.stop();}},_callee9,this);}));function validateNow(_x15,_x16){return _validateNow.apply(this,arguments);}return validateNow;}()}]);return RealOrStaticApi;}();var api=new RealOrStaticApi();var RealApi=/*#__PURE__*/function(){function RealApi(){classCallCheck_classCallCheck(this,RealApi);}createClass_createClass(RealApi,[{key:"getShortNames",value:function(){var _getShortNames2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee10(){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee10$(_context10){while(1)switch(_context10.prev=_context10.next){case 0:url="".concat(apiUrl,"/getShortNames");return _context10.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.json();}));case 2:case"end":return _context10.stop();}},_callee10);}));function getShortNames(){return _getShortNames2.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee11(filterProject,filterSubDir,handle){var host,url,params,ws,cancelled,connect;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee11$(_context11){while(1)switch(_context11.prev=_context11.next){case 0:host=window.location.host;if(false){}url="ws://".concat(host).concat(apiUrl,"/ws");params=new URLSearchParams();if(filterProject){params.set("filterProject",filterProject);}if(filterSubDir){params.set("filterSubDir",filterSubDir);}url+="?"+params.toString();cancelled=false;connect=function connect(){if(cancelled){return;}console.log("ws connect: "+url);ws=new WebSocket(url);ws.onopen=function(){console.log("ws connected");};ws.onclose=function(event){console.log("ws close");if(!cancelled){sleep(5000).then(connect);}};ws.onerror=function(event){console.log("ws error",event);};ws.onmessage=function(event){if(cancelled){return;}var msg=JSON.parse(event.data);handle(msg);};};connect();return _context11.abrupt("return",function(){console.log("ws cancel");cancelled=true;if(ws){ws.close();}});case 11:case"end":return _context11.stop();}},_callee11);}));function listenUpdates(_x17,_x18,_x19){return _listenUpdates2.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee12(resultId){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee12$(_context12){while(1)switch(_context12.prev=_context12.next){case 0:url="".concat(apiUrl,"/getResult?resultId=").concat(resultId);return _context12.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.text();}).then(function(json){return new CommandResult(json);}));case 2:case"end":return _context12.stop();}},_callee12);}));function getResult(_x20){return _getResult2.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee13(resultId){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee13$(_context13){while(1)switch(_context13.prev=_context13.next){case 0:url="".concat(apiUrl,"/getResultSummary?resultId=").concat(resultId);return _context13.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.text();}).then(function(json){return new CommandResultSummary(json);}));case 2:case"end":return _context13.stop();}},_callee13);}));function getResultSummary(_x21){return _getResultSummary2.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee14(resultId,ref,objectType){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee14$(_context14){while(1)switch(_context14.prev=_context14.next){case 0:url="".concat(apiUrl,"/getResultObject?resultId=").concat(resultId,"&").concat(buildRefParams(ref),"&objectType=").concat(objectType);return _context14.abrupt("return",fetch(url).then(handleErrors).then(function(response){return response.json();}));case 2:case"end":return _context14.stop();}},_callee14);}));function getResultObject(_x22,_x23,_x24){return _getResultObject2.apply(this,arguments);}return getResultObject;}()},{key:"doPost",value:function(){var _doPost=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee15(f,body){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee15$(_context15){while(1)switch(_context15.prev=_context15.next){case 0:url="".concat(apiUrl,"/").concat(f);return _context15.abrupt("return",fetch(url,{method:"POST",body:JSON.stringify(body),headers:{'Accept':'application/json','Content-Type':'application/json'}}).then(handleErrors));case 2:case"end":return _context15.stop();}},_callee15);}));function doPost(_x25,_x26){return _doPost.apply(this,arguments);}return doPost;}()},{key:"validateNow",value:function(){var _validateNow2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee16(project,target){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee16$(_context16){while(1)switch(_context16.prev=_context16.next){case 0:return _context16.abrupt("return",this.doPost("validateNow",{"project":project,"target":target}));case 1:case"end":return _context16.stop();}},_callee16,this);}));function validateNow(_x27,_x28){return _validateNow2.apply(this,arguments);}return validateNow;}()},{key:"deployNow",value:function(){var _deployNow2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee17(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee17$(_context17){while(1)switch(_context17.prev=_context17.next){case 0:return _context17.abrupt("return",this.doPost("deployNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context17.stop();}},_callee17,this);}));function deployNow(_x29,_x30,_x31){return _deployNow2.apply(this,arguments);}return deployNow;}()},{key:"reconcileNow",value:function(){var _reconcileNow2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee18(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee18$(_context18){while(1)switch(_context18.prev=_context18.next){case 0:return _context18.abrupt("return",this.doPost("reconcileNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context18.stop();}},_callee18,this);}));function reconcileNow(_x32,_x33,_x34){return _reconcileNow2.apply(this,arguments);}return reconcileNow;}()}]);return RealApi;}();var StaticApi=/*#__PURE__*/function(){function StaticApi(){classCallCheck_classCallCheck(this,StaticApi);}createClass_createClass(StaticApi,[{key:"getShortNames",value:function(){var _getShortNames3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee19(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee19$(_context19){while(1)switch(_context19.prev=_context19.next){case 0:_context19.next=2;return loadScript(staticPath+"/shortnames.js");case 2:return _context19.abrupt("return",staticShortNames);case 3:case"end":return _context19.stop();}},_callee19);}));function getShortNames(){return _getShortNames3.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee20(filterProject,filterSubDir,handle){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee20$(_context20){while(1)switch(_context20.prev=_context20.next){case 0:_context20.next=2;return loadScript(staticPath+"/summaries.js");case 2:staticSummaries.forEach(function(rs){if(filterProject&&filterProject!==rs.project.normalizedGitUrl){return;}if(filterSubDir&&filterSubDir!==rs.project.subDir){return;}handle({"type":"update_summary","summary":rs});});return _context20.abrupt("return",function(){});case 4:case"end":return _context20.stop();}},_callee20);}));function listenUpdates(_x35,_x36,_x37){return _listenUpdates3.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee21(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee21$(_context21){while(1)switch(_context21.prev=_context21.next){case 0:_context21.next=2;return loadScript(staticPath+"/result-".concat(resultId,".js"));case 2:return _context21.abrupt("return",staticResults.get(resultId));case 3:case"end":return _context21.stop();}},_callee21);}));function getResult(_x38){return _getResult3.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee22(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee22$(_context22){while(1)switch(_context22.prev=_context22.next){case 0:_context22.next=2;return loadScript(staticPath+"/summaries.js");case 2:return _context22.abrupt("return",staticSummaries.filter(function(s){return s.id===resultId;}).at(0));case 3:case"end":return _context22.stop();}},_callee22);}));function getResultSummary(_x39){return _getResultSummary3.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject3=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee23(resultId,ref,objectType){var _result$objects;var result,object;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee23$(_context23){while(1)switch(_context23.prev=_context23.next){case 0:_context23.next=2;return this.getResult(resultId);case 2:result=_context23.sent;object=(_result$objects=result.objects)===null||_result$objects===void 0?void 0:_result$objects.find(function(x){return lodash_default().isEqual(x.ref,ref);});if(object){_context23.next=6;break;}throw new Error("object not found");case 6:_context23.t0=objectType;_context23.next=_context23.t0===ObjectType.Rendered?9:_context23.t0===ObjectType.Remote?10:_context23.t0===ObjectType.Applied?11:12;break;case 9:return _context23.abrupt("return",object.rendered);case 10:return _context23.abrupt("return",object.remote);case 11:return _context23.abrupt("return",object.applied);case 12:throw new Error("unknown object type "+objectType);case 13:case"end":return _context23.stop();}},_callee23,this);}));function getResultObject(_x40,_x41,_x42){return _getResultObject3.apply(this,arguments);}return getResultObject;}()},{key:"validateNow",value:function validateNow(project,target){throw new Error("not implemented");}},{key:"reconcileNow",value:function reconcileNow(cluster,name,namespace){throw new Error("not implemented");}},{key:"deployNow",value:function deployNow(cluster,name,namespace){throw new Error("not implemented");}}]);return StaticApi;}();function handleErrors(response){if(!response.ok){throw Error(response.statusText);}return response;}function buildRefParams(ref){var params=new URLSearchParams();params.set("kind",ref.kind);params.set("name",ref.name);if(ref.group){params.set("group",ref.group);}if(ref.version){params.set("version",ref.version);}if(ref.namespace){params.set("namespace",ref.namespace);}return params.toString();}function buildRefString(ref){if(ref.namespace){return"".concat(ref.namespace,"/").concat(ref.kind,"/").concat(ref.name);}else{if(ref.name){return"".concat(ref.kind,"/").concat(ref.name);}else{return ref.kind;}}}function buildRefKindElement(ref,element){var tooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{zIndex:1000,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["ApiVersion: ",[ref.group,ref.version].filter(function(x){return x;}).join("/")]}),/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["Kind: ",ref.kind]})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip,children:element?element:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.kind})});}function buildObjectRefFromObject(obj){var apiVersion=obj.apiVersion;var s=apiVersion.split("/",2);var ref=new ObjectRef();if(s.length===1){ref.version=s[0];}else{ref.group=s[0];ref.version=s[1];}ref.kind=obj.kind;ref.namespace=obj.metadata.namespace;ref.name=obj.metadata.name;return ref;}function findObjectByRef(l,ref,filter){return l===null||l===void 0?void 0:l.find(function(x){if(filter&&!filter(x)){return false;}return lodash_default().isEqual(x.ref,ref);});}function usePromise(p){if(p===undefined){throw new Promise(function(){return undefined;});}var promise=p;if(promise.status==='fulfilled'){return promise.value;}else if(promise.status==='rejected'){throw promise.reason;}else if(promise.status==='pending'){throw promise;}else{promise.status='pending';p.then(function(result){promise.status='fulfilled';promise.value=result;},function(reason){promise.status='rejected';promise.reason=reason;});throw promise;}} +var staticPath="./staticdata";var ObjectType=/*#__PURE__*/function(ObjectType){ObjectType["Rendered"]="rendered";ObjectType["Remote"]="remote";ObjectType["Applied"]="applied";return ObjectType;}({});function checkStaticBuild(){return _checkStaticBuild.apply(this,arguments);}function _checkStaticBuild(){_checkStaticBuild=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee19(){var p;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee19$(_context19){while(1)switch(_context19.prev=_context19.next){case 0:p=loadScript(staticPath+"/summaries.js");_context19.prev=1;_context19.next=4;return p;case 4:return _context19.abrupt("return",true);case 7:_context19.prev=7;_context19.t0=_context19["catch"](1);return _context19.abrupt("return",false);case 10:case"end":return _context19.stop();}},_callee19,null,[[1,7]]);}));return _checkStaticBuild.apply(this,arguments);}var RealApi=/*#__PURE__*/function(){function RealApi(getToken,onUnauthorized,onTokenRefresh){classCallCheck_classCallCheck(this,RealApi);this.getToken=void 0;this.onUnauthorized=void 0;this.onTokenRefresh=void 0;this.getToken=getToken;this.onUnauthorized=onUnauthorized;this.onTokenRefresh=onTokenRefresh;}createClass_createClass(RealApi,[{key:"setAuthorizationHeader",value:function setAuthorizationHeader(headers){if(!this.getToken){return;}headers['Authorization']="Bearer "+this.getToken();}},{key:"refreshToken",value:function(){var _refreshToken=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var headers,resp,j;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:headers={'Accept':'application/json'};this.setAuthorizationHeader(headers);_context.next=4;return fetch("/auth/refresh",{method:"POST",headers:headers});case 4:resp=_context.sent;if(!(resp.status===401)){_context.next=8;break;}if(this.onUnauthorized){this.onUnauthorized();}throw Error(resp.statusText);case 8:_context.next=10;return resp.json();case 10:j=_context.sent;if(this.onTokenRefresh){this.onTokenRefresh(j.token);}case 12:case"end":return _context.stop();}},_callee,this);}));function refreshToken(){return _refreshToken.apply(this,arguments);}return refreshToken;}()},{key:"handleErrors",value:function(){var _handleErrors=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee2(response,retry){var newResp;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee2$(_context2){while(1)switch(_context2.prev=_context2.next){case 0:if(response.ok){_context2.next=16;break;}if(!(response.status===401)){_context2.next=15;break;}if(!(this.getToken&&retry)){_context2.next=14;break;}console.log("retrying with token refresh");_context2.next=6;return this.refreshToken();case 6:_context2.next=8;return retry();case 8:newResp=_context2.sent;_context2.next=11;return this.handleErrors(newResp);case 11:return _context2.abrupt("return",_context2.sent);case 14:if(this.onUnauthorized){this.onUnauthorized();}case 15:throw Error(response.statusText);case 16:return _context2.abrupt("return",response);case 17:case"end":return _context2.stop();}},_callee2,this);}));function handleErrors(_x,_x2){return _handleErrors.apply(this,arguments);}return handleErrors;}()},{key:"doGet",value:function(){var _doGet=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee3(path,params){var _this=this;var url,doFetch,resp;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee3$(_context3){while(1)switch(_context3.prev=_context3.next){case 0:url=path;if(params){url+="?"+params.toString();}doFetch=function doFetch(){var headers={'Accept':'application/json'};_this.setAuthorizationHeader(headers);return fetch(url,{method:"GET",headers:headers});};_context3.next=5;return doFetch();case 5:resp=_context3.sent;_context3.next=8;return this.handleErrors(resp,doFetch);case 8:resp=_context3.sent;return _context3.abrupt("return",resp.json());case 10:case"end":return _context3.stop();}},_callee3,this);}));function doGet(_x3,_x4){return _doGet.apply(this,arguments);}return doGet;}()},{key:"doPost",value:function(){var _doPost=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee4(path,body){var _this2=this;var url,doFetch,resp;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee4$(_context4){while(1)switch(_context4.prev=_context4.next){case 0:url=path;doFetch=function doFetch(){var headers={'Accept':'application/json','Content-Type':'application/json'};_this2.setAuthorizationHeader(headers);return fetch(url,{method:"POST",body:JSON.stringify(body),headers:headers});};_context4.next=4;return doFetch();case 4:resp=_context4.sent;_context4.next=7;return this.handleErrors(resp,doFetch);case 7:resp=_context4.sent;return _context4.abrupt("return",resp);case 9:case"end":return _context4.stop();}},_callee4,this);}));function doPost(_x5,_x6){return _doPost.apply(this,arguments);}return doPost;}()},{key:"getShortNames",value:function(){var _getShortNames=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee5(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee5$(_context5){while(1)switch(_context5.prev=_context5.next){case 0:return _context5.abrupt("return",this.doGet("/api/getShortNames"));case 1:case"end":return _context5.stop();}},_callee5,this);}));function getShortNames(){return _getShortNames.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee7(filterProject,filterSubDir,handle){var host,url,params,getToken,ws,cancelled,connect;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee7$(_context7){while(1)switch(_context7.prev=_context7.next){case 0:host=window.location.host;if(false){}url="ws://".concat(host,"/api/ws");params=new URLSearchParams();if(filterProject){params.set("filterProject",filterProject);}if(filterSubDir){params.set("filterSubDir",filterSubDir);}url+="?"+params.toString();getToken=this.getToken;cancelled=false;connect=/*#__PURE__*/function(){var _ref=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee6(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee6$(_context6){while(1)switch(_context6.prev=_context6.next){case 0:if(!cancelled){_context6.next=2;break;}return _context6.abrupt("return");case 2:console.log("ws connect: "+url);ws=new WebSocket(url);ws.onopen=function(){console.log("ws connected");if(getToken){ws.send(JSON.stringify({"type":"auth","token":getToken()}));}};ws.onclose=function(event){console.log("ws close");if(!cancelled){sleep(5000).then(connect);}};ws.onerror=function(event){console.log("ws error",event);};ws.onmessage=function(event){if(cancelled){return;}var msg=JSON.parse(event.data);handle(msg);};case 8:case"end":return _context6.stop();}},_callee6);}));return function connect(){return _ref.apply(this,arguments);};}();_context7.next=12;return connect();case 12:return _context7.abrupt("return",function(){console.log("ws cancel");cancelled=true;if(ws){ws.close();}});case 13:case"end":return _context7.stop();}},_callee7,this);}));function listenUpdates(_x7,_x8,_x9){return _listenUpdates.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee8(resultId){var params,json;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee8$(_context8){while(1)switch(_context8.prev=_context8.next){case 0:params=new URLSearchParams();params.set("resultId",resultId);_context8.next=4;return this.doGet("/api/getResult",params);case 4:json=_context8.sent;return _context8.abrupt("return",new CommandResult(json));case 6:case"end":return _context8.stop();}},_callee8,this);}));function getResult(_x10){return _getResult.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee9(resultId){var params,json;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee9$(_context9){while(1)switch(_context9.prev=_context9.next){case 0:params=new URLSearchParams();params.set("resultId",resultId);_context9.next=4;return this.doGet("/api/getResultSummary",params);case 4:json=_context9.sent;return _context9.abrupt("return",new CommandResultSummary(json));case 6:case"end":return _context9.stop();}},_callee9,this);}));function getResultSummary(_x11){return _getResultSummary.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee10(resultId,ref,objectType){var params;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee10$(_context10){while(1)switch(_context10.prev=_context10.next){case 0:params=new URLSearchParams();params.set("resultId",resultId);params.set("objectType",objectType);buildRefParams(ref,params);_context10.next=6;return this.doGet("/api/getResultObject",params);case 6:return _context10.abrupt("return",_context10.sent);case 7:case"end":return _context10.stop();}},_callee10,this);}));function getResultObject(_x12,_x13,_x14){return _getResultObject.apply(this,arguments);}return getResultObject;}()},{key:"validateNow",value:function(){var _validateNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee11(project,target){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee11$(_context11){while(1)switch(_context11.prev=_context11.next){case 0:return _context11.abrupt("return",this.doPost("/api/validateNow",{"project":project,"target":target}));case 1:case"end":return _context11.stop();}},_callee11,this);}));function validateNow(_x15,_x16){return _validateNow.apply(this,arguments);}return validateNow;}()},{key:"deployNow",value:function(){var _deployNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee12(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee12$(_context12){while(1)switch(_context12.prev=_context12.next){case 0:return _context12.abrupt("return",this.doPost("/api/deployNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context12.stop();}},_callee12,this);}));function deployNow(_x17,_x18,_x19){return _deployNow.apply(this,arguments);}return deployNow;}()},{key:"reconcileNow",value:function(){var _reconcileNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee13(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee13$(_context13){while(1)switch(_context13.prev=_context13.next){case 0:return _context13.abrupt("return",this.doPost("/api/reconcileNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context13.stop();}},_callee13,this);}));function reconcileNow(_x20,_x21,_x22){return _reconcileNow.apply(this,arguments);}return reconcileNow;}()}]);return RealApi;}();var StaticApi=/*#__PURE__*/function(){function StaticApi(){classCallCheck_classCallCheck(this,StaticApi);}createClass_createClass(StaticApi,[{key:"getShortNames",value:function(){var _getShortNames2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee14(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee14$(_context14){while(1)switch(_context14.prev=_context14.next){case 0:_context14.next=2;return loadScript(staticPath+"/shortnames.js");case 2:return _context14.abrupt("return",staticShortNames);case 3:case"end":return _context14.stop();}},_callee14);}));function getShortNames(){return _getShortNames2.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee15(filterProject,filterSubDir,handle){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee15$(_context15){while(1)switch(_context15.prev=_context15.next){case 0:_context15.next=2;return loadScript(staticPath+"/summaries.js");case 2:staticSummaries.forEach(function(rs){if(filterProject&&filterProject!==rs.project.normalizedGitUrl){return;}if(filterSubDir&&filterSubDir!==rs.project.subDir){return;}handle({"type":"update_summary","summary":rs});});return _context15.abrupt("return",function(){});case 4:case"end":return _context15.stop();}},_callee15);}));function listenUpdates(_x23,_x24,_x25){return _listenUpdates2.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee16(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee16$(_context16){while(1)switch(_context16.prev=_context16.next){case 0:_context16.next=2;return loadScript(staticPath+"/result-".concat(resultId,".js"));case 2:return _context16.abrupt("return",staticResults.get(resultId));case 3:case"end":return _context16.stop();}},_callee16);}));function getResult(_x26){return _getResult2.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee17(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee17$(_context17){while(1)switch(_context17.prev=_context17.next){case 0:_context17.next=2;return loadScript(staticPath+"/summaries.js");case 2:return _context17.abrupt("return",staticSummaries.filter(function(s){return s.id===resultId;}).at(0));case 3:case"end":return _context17.stop();}},_callee17);}));function getResultSummary(_x27){return _getResultSummary2.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee18(resultId,ref,objectType){var _result$objects;var result,object;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee18$(_context18){while(1)switch(_context18.prev=_context18.next){case 0:_context18.next=2;return this.getResult(resultId);case 2:result=_context18.sent;object=(_result$objects=result.objects)===null||_result$objects===void 0?void 0:_result$objects.find(function(x){return lodash_default().isEqual(x.ref,ref);});if(object){_context18.next=6;break;}throw new Error("object not found");case 6:_context18.t0=objectType;_context18.next=_context18.t0===ObjectType.Rendered?9:_context18.t0===ObjectType.Remote?10:_context18.t0===ObjectType.Applied?11:12;break;case 9:return _context18.abrupt("return",object.rendered);case 10:return _context18.abrupt("return",object.remote);case 11:return _context18.abrupt("return",object.applied);case 12:throw new Error("unknown object type "+objectType);case 13:case"end":return _context18.stop();}},_callee18,this);}));function getResultObject(_x28,_x29,_x30){return _getResultObject2.apply(this,arguments);}return getResultObject;}()},{key:"validateNow",value:function validateNow(project,target){throw new Error("not implemented");}},{key:"reconcileNow",value:function reconcileNow(cluster,name,namespace){throw new Error("not implemented");}},{key:"deployNow",value:function deployNow(cluster,name,namespace){throw new Error("not implemented");}}]);return StaticApi;}();function buildRefParams(ref,params){params.set("kind",ref.kind);params.set("name",ref.name);if(ref.group){params.set("group",ref.group);}if(ref.version){params.set("version",ref.version);}if(ref.namespace){params.set("namespace",ref.namespace);}}function buildRefString(ref){if(ref.namespace){return"".concat(ref.namespace,"/").concat(ref.kind,"/").concat(ref.name);}else{if(ref.name){return"".concat(ref.kind,"/").concat(ref.name);}else{return ref.kind;}}}function buildRefKindElement(ref,element){var tooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{zIndex:1000,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["ApiVersion: ",[ref.group,ref.version].filter(function(x){return x;}).join("/")]}),/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["Kind: ",ref.kind]})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip,children:element?element:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.kind})});}function buildObjectRefFromObject(obj){var apiVersion=obj.apiVersion;var s=apiVersion.split("/",2);var ref=new ObjectRef();if(s.length===1){ref.version=s[0];}else{ref.group=s[0];ref.version=s[1];}ref.kind=obj.kind;ref.namespace=obj.metadata.namespace;ref.name=obj.metadata.name;return ref;}function findObjectByRef(l,ref,filter){return l===null||l===void 0?void 0:l.find(function(x){if(filter&&!filter(x)){return false;}return lodash_default().isEqual(x.ref,ref);});} ;// CONCATENATED MODULE: ./src/project-summaries.ts function compareSummaries(a,b){return b.commandInfo.startTime.localeCompare(a.commandInfo.startTime)||b.commandInfo.endTime.localeCompare(b.commandInfo.endTime)||(b.commandInfo.command||"").localeCompare(a.commandInfo.command||"");}function buildProjectSummaries(summaries,validateResults){var sorted=Array.from(summaries.values());sorted.sort(compareSummaries);var m=new Map();sorted.forEach(function(rs){var projectKey=JSON.stringify(rs.projectKey);var p=m.get(projectKey);if(!p){p={project:rs.projectKey,targets:[]};m.set(projectKey,p);}var ptKey=new ProjectTargetKey();ptKey.project=rs.projectKey;ptKey.target=rs.targetKey;var vr=validateResults.get(JSON.stringify(ptKey));var target=p.targets.find(function(t){return lodash_default().isEqual(t.target,rs.targetKey);});if(!target){target={target:rs.targetKey,lastValidateResult:vr,commandResults:[]};p.targets.push(target);}target.commandResults.push(rs);});var ret=[];m.forEach(function(p){p.targets.sort(function(a,b){var _ref;return(a.target.targetName||"").localeCompare(b.target.targetName||"")||a.target.clusterId.localeCompare(b.target.clusterId)||((_ref=a.target.discriminator||"")===null||_ref===void 0?void 0:_ref.localeCompare(b.target.discriminator||""));});ret.push(p);});ret.sort(function(a,b){return(a.project.gitRepoKey||"").localeCompare(b.project.gitRepoKey||"")||(a.project.subDir||"").localeCompare(b.project.subDir||"");});return ret;} +;// CONCATENATED MODULE: ./src/components/Login.tsx +//import './Login.css'; +function loginUser(_x){return _loginUser.apply(this,arguments);}function _loginUser(){_loginUser=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee2(creds){var url;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee2$(_context2){while(1)switch(_context2.prev=_context2.next){case 0:url="/auth/login";return _context2.abrupt("return",fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(creds)}).then(function(data){return data.json();}).then(function(token){return token.token;}));case 2:case"end":return _context2.stop();}},_callee2);}));return _loginUser.apply(this,arguments);}function Login(props){var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),username=_useState2[0],setUserName=_useState2[1];var _useState3=(0,react.useState)(),_useState4=slicedToArray_slicedToArray(_useState3,2),password=_useState4[0],setPassword=_useState4[1];var handleSubmit=/*#__PURE__*/function(){var _ref=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(e){var token;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:e.preventDefault();if(!(!username||!password)){_context.next=3;break;}return _context.abrupt("return");case 3:_context.next=5;return loginUser({username:username,password:password});case 5:token=_context.sent;props.setToken(token);case 7:case"end":return _context.stop();}},_callee);}));return function handleSubmit(_x2){return _ref.apply(this,arguments);};}();return/*#__PURE__*/(0,jsx_runtime.jsxs)("div",{className:"login-wrapper",children:[/*#__PURE__*/(0,jsx_runtime.jsx)("h1",{children:"Please Log In"}),/*#__PURE__*/(0,jsx_runtime.jsxs)("form",{onSubmit:handleSubmit,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)("label",{children:[/*#__PURE__*/(0,jsx_runtime.jsx)("p",{children:"Username"}),/*#__PURE__*/(0,jsx_runtime.jsx)("input",{type:"text",onChange:function onChange(e){return setUserName(e.target.value);}})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)("label",{children:[/*#__PURE__*/(0,jsx_runtime.jsx)("p",{children:"Password"}),/*#__PURE__*/(0,jsx_runtime.jsx)("input",{type:"password",onChange:function onChange(e){return setPassword(e.target.value);}})]}),/*#__PURE__*/(0,jsx_runtime.jsx)("div",{children:/*#__PURE__*/(0,jsx_runtime.jsx)("button",{type:"submit",children:"Submit"})})]})]});} +;// CONCATENATED MODULE: ./node_modules/@mui/material/CircularProgress/circularProgressClasses.js + + +function getCircularProgressUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiCircularProgress', slot); +} +var circularProgressClasses = generateUtilityClasses('MuiCircularProgress', ['root', 'determinate', 'indeterminate', 'colorPrimary', 'colorSecondary', 'svg', 'circle', 'circleDeterminate', 'circleIndeterminate', 'circleDisableShrink']); +/* harmony default export */ var CircularProgress_circularProgressClasses = ((/* unused pure expression or super */ null && (circularProgressClasses))); +;// CONCATENATED MODULE: ./node_modules/@mui/material/CircularProgress/CircularProgress.js + +var CircularProgress_templateObject, CircularProgress_templateObject2, CircularProgress_templateObject3, CircularProgress_templateObject4; + + +var CircularProgress_excluded = ["className", "color", "disableShrink", "size", "style", "thickness", "value", "variant"]; +var CircularProgress_ = function _(t) { + return t; + }, + CircularProgress_t, + CircularProgress_t2, + CircularProgress_t3, + CircularProgress_t4; + + + + + + + + + + + +var SIZE = 44; +var circularRotateKeyframe = keyframes(CircularProgress_t || (CircularProgress_t = CircularProgress_(CircularProgress_templateObject || (CircularProgress_templateObject = _taggedTemplateLiteral(["\n 0% {\n transform: rotate(0deg);\n }\n\n 100% {\n transform: rotate(360deg);\n }\n"]))))); +var circularDashKeyframe = keyframes(CircularProgress_t2 || (CircularProgress_t2 = CircularProgress_(CircularProgress_templateObject2 || (CircularProgress_templateObject2 = _taggedTemplateLiteral(["\n 0% {\n stroke-dasharray: 1px, 200px;\n stroke-dashoffset: 0;\n }\n\n 50% {\n stroke-dasharray: 100px, 200px;\n stroke-dashoffset: -15px;\n }\n\n 100% {\n stroke-dasharray: 100px, 200px;\n stroke-dashoffset: -125px;\n }\n"]))))); +var CircularProgress_useUtilityClasses = function useUtilityClasses(ownerState) { + var classes = ownerState.classes, + variant = ownerState.variant, + color = ownerState.color, + disableShrink = ownerState.disableShrink; + var slots = { + root: ['root', variant, "color".concat(utils_capitalize(color))], + svg: ['svg'], + circle: ['circle', "circle".concat(utils_capitalize(variant)), disableShrink && 'circleDisableShrink'] + }; + return composeClasses(slots, getCircularProgressUtilityClass, classes); +}; +var CircularProgressRoot = styles_styled('span', { + name: 'MuiCircularProgress', + slot: 'Root', + overridesResolver: function overridesResolver(props, styles) { + var ownerState = props.ownerState; + return [styles.root, styles[ownerState.variant], styles["color".concat(utils_capitalize(ownerState.color))]]; + } +})(function (_ref) { + var ownerState = _ref.ownerState, + theme = _ref.theme; + return extends_extends({ + display: 'inline-block' + }, ownerState.variant === 'determinate' && { + transition: theme.transitions.create('transform') + }, ownerState.color !== 'inherit' && { + color: (theme.vars || theme).palette[ownerState.color].main + }); +}, function (_ref2) { + var ownerState = _ref2.ownerState; + return ownerState.variant === 'indeterminate' && css(CircularProgress_t3 || (CircularProgress_t3 = CircularProgress_(CircularProgress_templateObject3 || (CircularProgress_templateObject3 = _taggedTemplateLiteral(["\n animation: ", " 1.4s linear infinite;\n "])), 0)), circularRotateKeyframe); +}); +var CircularProgressSVG = styles_styled('svg', { + name: 'MuiCircularProgress', + slot: 'Svg', + overridesResolver: function overridesResolver(props, styles) { + return styles.svg; + } +})({ + display: 'block' // Keeps the progress centered +}); + +var CircularProgressCircle = styles_styled('circle', { + name: 'MuiCircularProgress', + slot: 'Circle', + overridesResolver: function overridesResolver(props, styles) { + var ownerState = props.ownerState; + return [styles.circle, styles["circle".concat(utils_capitalize(ownerState.variant))], ownerState.disableShrink && styles.circleDisableShrink]; + } +})(function (_ref3) { + var ownerState = _ref3.ownerState, + theme = _ref3.theme; + return extends_extends({ + stroke: 'currentColor' + }, ownerState.variant === 'determinate' && { + transition: theme.transitions.create('stroke-dashoffset') + }, ownerState.variant === 'indeterminate' && { + // Some default value that looks fine waiting for the animation to kicks in. + strokeDasharray: '80px, 200px', + strokeDashoffset: 0 // Add the unit to fix a Edge 16 and below bug. + }); +}, function (_ref4) { + var ownerState = _ref4.ownerState; + return ownerState.variant === 'indeterminate' && !ownerState.disableShrink && css(CircularProgress_t4 || (CircularProgress_t4 = CircularProgress_(CircularProgress_templateObject4 || (CircularProgress_templateObject4 = _taggedTemplateLiteral(["\n animation: ", " 1.4s ease-in-out infinite;\n "])), 0)), circularDashKeyframe); +}); + +/** + * ## ARIA + * + * If the progress bar is describing the loading progress of a particular region of a page, + * you should use `aria-describedby` to point to the progress bar, and set the `aria-busy` + * attribute to `true` on that region until it has finished loading. + */ +var CircularProgress = /*#__PURE__*/react.forwardRef(function CircularProgress(inProps, ref) { + var props = useThemeProps_useThemeProps({ + props: inProps, + name: 'MuiCircularProgress' + }); + var className = props.className, + _props$color = props.color, + color = _props$color === void 0 ? 'primary' : _props$color, + _props$disableShrink = props.disableShrink, + disableShrink = _props$disableShrink === void 0 ? false : _props$disableShrink, + _props$size = props.size, + size = _props$size === void 0 ? 40 : _props$size, + style = props.style, + _props$thickness = props.thickness, + thickness = _props$thickness === void 0 ? 3.6 : _props$thickness, + _props$value = props.value, + value = _props$value === void 0 ? 0 : _props$value, + _props$variant = props.variant, + variant = _props$variant === void 0 ? 'indeterminate' : _props$variant, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, CircularProgress_excluded); + var ownerState = extends_extends({}, props, { + color: color, + disableShrink: disableShrink, + size: size, + thickness: thickness, + value: value, + variant: variant + }); + var classes = CircularProgress_useUtilityClasses(ownerState); + var circleStyle = {}; + var rootStyle = {}; + var rootProps = {}; + if (variant === 'determinate') { + var circumference = 2 * Math.PI * ((SIZE - thickness) / 2); + circleStyle.strokeDasharray = circumference.toFixed(3); + rootProps['aria-valuenow'] = Math.round(value); + circleStyle.strokeDashoffset = "".concat(((100 - value) / 100 * circumference).toFixed(3), "px"); + rootStyle.transform = 'rotate(-90deg)'; + } + return /*#__PURE__*/(0,jsx_runtime.jsx)(CircularProgressRoot, extends_extends({ + className: clsx_m(classes.root, className), + style: extends_extends({ + width: size, + height: size + }, rootStyle, style), + ownerState: ownerState, + ref: ref, + role: "progressbar" + }, rootProps, other, { + children: /*#__PURE__*/(0,jsx_runtime.jsx)(CircularProgressSVG, { + className: classes.svg, + ownerState: ownerState, + viewBox: "".concat(SIZE / 2, " ").concat(SIZE / 2, " ").concat(SIZE, " ").concat(SIZE), + children: /*#__PURE__*/(0,jsx_runtime.jsx)(CircularProgressCircle, { + className: classes.circle, + style: circleStyle, + ownerState: ownerState, + cx: SIZE, + cy: SIZE, + r: (SIZE - thickness) / 2, + fill: "none", + strokeWidth: thickness + }) + }) + })); +}); + false ? 0 : void 0; +/* harmony default export */ var CircularProgress_CircularProgress = (CircularProgress); +;// CONCATENATED MODULE: ./src/components/Loading.tsx +var Loading=function Loading(){return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",width:"100%",height:"100%",alignItems:"center",justifyContent:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CircularProgress_CircularProgress,{})})});};function useLoadingHelper(load,deps){var _useState=(0,react.useState)(true),_useState2=slicedToArray_slicedToArray(_useState,2),loading=_useState2[0],setLoading=_useState2[1];var _useState3=(0,react.useState)(),_useState4=slicedToArray_slicedToArray(_useState3,2),error=_useState4[0],setError=_useState4[1];var _useState5=(0,react.useState)(),_useState6=slicedToArray_slicedToArray(_useState5,2),content=_useState6[0],setContent=_useState6[1];(0,react.useEffect)(function(){var doStartLoading=/*#__PURE__*/function(){var _ref=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var c;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.prev=0;_context.next=3;return load();case 3:c=_context.sent;setContent(c);setLoading(false);_context.next=12;break;case 8:_context.prev=8;_context.t0=_context["catch"](0);setError(_context.t0);setLoading(false);case 12:case"end":return _context.stop();}},_callee,null,[[0,8]]);}));return function doStartLoading(){return _ref.apply(this,arguments);};}();doStartLoading();// eslint-disable-next-line react-hooks/exhaustive-deps +},deps);return[loading,error,content];} ;// CONCATENATED MODULE: ./src/components/App.tsx -function useAppOutletContext(){return useOutletContext();}var AppContext=/*#__PURE__*/(0,react.createContext)({summaries:new Map(),projects:[],validateResults:new Map()});var App=function App(){var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),filters=_useState2[0],setFilters=_useState2[1];var summariesRef=(0,react.useRef)(new Map());var validateResultsRef=(0,react.useRef)(new Map());var _useState3=(0,react.useState)(summariesRef.current),_useState4=slicedToArray_slicedToArray(_useState3,2),summaries=_useState4[0],setSummaries=_useState4[1];var _useState5=(0,react.useState)(validateResultsRef.current),_useState6=slicedToArray_slicedToArray(_useState5,2),validateResults=_useState6[0],setValidateResults=_useState6[1];(0,react.useEffect)(function(){var updateSummary=function updateSummary(rs){console.log("update_summary",rs.id,rs.commandInfo.startTime);summariesRef.current.set(rs.id,rs);setSummaries(new Map(summariesRef.current));};var deleteSummary=function deleteSummary(id){console.log("delete_summary",id);summariesRef.current.delete(id);setSummaries(new Map(summariesRef.current));};var updateValidateResult=function updateValidateResult(key,vr){console.log("validate_result",key);validateResultsRef.current.set(JSON.stringify(key),vr);setValidateResults(new Map(validateResultsRef.current));};console.log("starting listenResults");var cancel=api.listenUpdates(undefined,undefined,function(msg){switch(msg.type){case"update_summary":updateSummary(msg.summary);break;case"delete_summary":deleteSummary(msg.id);break;case"validate_result":updateValidateResult(msg.key,msg.result);break;}});return function(){console.log("cancel listenResults");cancel.then(function(c){return c();});};},[]);var projects=(0,react.useMemo)(function(){return buildProjectSummaries(summaries,validateResults);},[summaries,validateResults]);var appContext={summaries:summariesRef.current,projects:projects,validateResults:validateResults};var outletContext={filters:filters,setFilters:setFilters};return/*#__PURE__*/(0,jsx_runtime.jsx)(AppContext.Provider,{value:appContext,children:/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_light,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(LeftDrawer,{content:/*#__PURE__*/(0,jsx_runtime.jsx)(Outlet,{context:outletContext}),context:outletContext})})})});};/* harmony default export */ var components_App = (App); +function useAppOutletContext(){return useOutletContext();}var AppContext=/*#__PURE__*/(0,react.createContext)({summaries:new Map(),projects:[],validateResults:new Map()});var ApiContext=/*#__PURE__*/(0,react.createContext)(new StaticApi());var LoggedInApp=function LoggedInApp(props){var api=(0,react.useContext)(ApiContext);var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),filters=_useState2[0],setFilters=_useState2[1];var summariesRef=(0,react.useRef)(new Map());var validateResultsRef=(0,react.useRef)(new Map());var _useState3=(0,react.useState)(summariesRef.current),_useState4=slicedToArray_slicedToArray(_useState3,2),summaries=_useState4[0],setSummaries=_useState4[1];var _useState5=(0,react.useState)(validateResultsRef.current),_useState6=slicedToArray_slicedToArray(_useState5,2),validateResults=_useState6[0],setValidateResults=_useState6[1];var onUnauthorized=props.onUnauthorized;(0,react.useEffect)(function(){var updateSummary=function updateSummary(rs){console.log("update_summary",rs.id,rs.commandInfo.startTime);summariesRef.current.set(rs.id,rs);setSummaries(new Map(summariesRef.current));};var deleteSummary=function deleteSummary(id){console.log("delete_summary",id);summariesRef.current.delete(id);setSummaries(new Map(summariesRef.current));};var updateValidateResult=function updateValidateResult(key,vr){console.log("validate_result",key);validateResultsRef.current.set(JSON.stringify(key),vr);setValidateResults(new Map(validateResultsRef.current));};console.log("starting listenResults");var cancel;cancel=api.listenUpdates(undefined,undefined,function(msg){switch(msg.type){case"update_summary":updateSummary(msg.summary);break;case"delete_summary":deleteSummary(msg.id);break;case"validate_result":updateValidateResult(msg.key,msg.result);break;case"auth_result":if(!msg.success){cancel.then(function(c){return c();});onUnauthorized();}}});return function(){console.log("cancel listenResults");cancel.then(function(c){return c();});};},[api,onUnauthorized]);var projects=(0,react.useMemo)(function(){return buildProjectSummaries(summaries,validateResults);},[summaries,validateResults]);var appContext={summaries:summariesRef.current,projects:projects,validateResults:validateResults};var outletContext={filters:filters,setFilters:setFilters};return/*#__PURE__*/(0,jsx_runtime.jsx)(AppContext.Provider,{value:appContext,children:/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_light,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(LeftDrawer,{content:/*#__PURE__*/(0,jsx_runtime.jsx)(Outlet,{context:outletContext}),context:outletContext})})})});};var App=function App(){var _useState7=(0,react.useState)(),_useState8=slicedToArray_slicedToArray(_useState7,2),api=_useState8[0],setApi=_useState8[1];var _useState9=(0,react.useState)(false),_useState10=slicedToArray_slicedToArray(_useState9,2),needToken=_useState10[0],setNeedToken=_useState10[1];var storage=localStorage;var getToken=function getToken(){var token=storage.getItem("token");if(!token){return"";}return JSON.parse(token);};var setToken=function setToken(token){if(!token){storage.removeItem("token");}else{storage.setItem("token",JSON.stringify(token));}};var onUnauthorized=function onUnauthorized(){console.log("handle onUnauthorized");setToken(undefined);setApi(undefined);setNeedToken(true);};var onTokenRefresh=function onTokenRefresh(newToken){console.log("handle onTokenRefresh");setToken(newToken);};var handleLoginSucceeded=function handleLoginSucceeded(token){console.log("handle saveToken");setToken(token);setApi(new RealApi(getToken,onUnauthorized,onTokenRefresh));};(0,react.useEffect)(function(){if(api){return;}var doInit=/*#__PURE__*/function(){var _ref=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var isStatic,noAuthApi;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return checkStaticBuild();case 2:isStatic=_context.sent;if(!isStatic){_context.next=7;break;}setApi(new StaticApi());_context.next=19;break;case 7:// check if we don't need auth (running locally?) +noAuthApi=new RealApi(undefined,undefined,undefined);_context.prev=8;_context.next=11;return noAuthApi.getShortNames();case 11:setToken(undefined);setNeedToken(false);setApi(noAuthApi);_context.next=19;break;case 16:_context.prev=16;_context.t0=_context["catch"](8);if(!getToken()){setNeedToken(true);}else{setApi(new RealApi(getToken,onUnauthorized,onTokenRefresh));}case 19:case"end":return _context.stop();}},_callee,null,[[8,16]]);}));return function doInit(){return _ref.apply(this,arguments);};}();doInit();// eslint-disable-next-line react-hooks/exhaustive-deps +},[]);if(needToken&&!getToken()){return/*#__PURE__*/(0,jsx_runtime.jsx)(Login,{setToken:handleLoginSucceeded});}if(!api){return/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{});}return/*#__PURE__*/(0,jsx_runtime.jsx)(ApiContext.Provider,{value:api,children:/*#__PURE__*/(0,jsx_runtime.jsx)(LoggedInApp,{onUnauthorized:onUnauthorized})});};/* harmony default export */ var components_App = (App); ;// CONCATENATED MODULE: ./src/components/targets-view/Projects.tsx var ProjectItem=function ProjectItem(props){var name=getLastPathElement(props.ps.project.gitRepoKey);var subDir=props.ps.project.subDir;var projectInfo=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{children:[props.ps.project.gitRepoKey,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),props.ps.project.subDir?/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:["SubDir: ",props.ps.project.subDir,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{})]}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'5px 16px'},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"center",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",alignItems:"center",gap:"15px",children:name&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(ProjectIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:projectInfo,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:name})})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{children:subDir?/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{textAlign:"center",children:subDir}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{marginLeft:"auto"})})})]})});}; ;// CONCATENATED MODULE: ./node_modules/@mui/material/SvgIcon/svgIconClasses.js @@ -53188,7 +53373,7 @@ var ActionsMenu=function ActionsMenu(props){var _React$useState=react.useState(n ;// CONCATENATED MODULE: ./src/utils/duration.ts function formatDuration(ms,withMs){if(ms<0)ms=-ms;var time={day:Math.floor(ms/86400000),hour:Math.floor(ms/3600000)%24,minute:Math.floor(ms/60000)%60,second:Math.floor(ms/1000)%60,millisecond:withMs?Math.floor(ms)%1000:0};return Object.entries(time).filter(function(val){return val[1]!==0;}).map(function(val){return val[1]+' '+(val[1]!==1?val[0]+'s':val[0]);}).join(', ');}function formatDurationShort(ms){if(ms<0)ms=-ms;var time={d:Math.floor(ms/86400000),h:Math.floor(ms/3600000)%24,m:Math.floor(ms/60000)%60,s:Math.floor(ms/1000)%60,ms:Math.floor(ms)%1000};var f=Object.entries(time).find(function(val){return val[1]>0;});if(f===undefined){return"0s";}return f[1]+f[0];}var calcAgo=function calcAgo(startTime){var t1=new Date(startTime);var t2=new Date();var d=t2.getTime()-t1.getTime();return formatDurationShort(d);}; ;// CONCATENATED MODULE: ./src/components/targets-view/Targets.tsx -var StatusIcon=function StatusIcon(props){var icon;var theme=styles_useTheme_useTheme();if(props.ts.lastValidateResult===undefined){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(MessageQuestionIcon,{color:theme.palette.error.main});}else if(props.ts.lastValidateResult.ready&&!props.ts.lastValidateResult.errors){var _props$ts$lastValidat,_props$ts$lastValidat2;if((_props$ts$lastValidat=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat!==void 0&&_props$ts$lastValidat.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"warning"});}else if((_props$ts$lastValidat2=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat2!==void 0&&_props$ts$lastValidat2.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"primary"});}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"success"});}}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(HeartBroken,{color:"error"});}var tooltip=[];if(props.ts.lastValidateResult===undefined){tooltip.push("No validation result available.");}else{var _props$ts$lastValidat3,_props$ts$lastValidat4,_props$ts$lastValidat5,_props$ts$lastValidat6;if(props.ts.lastValidateResult.ready&&!((_props$ts$lastValidat3=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat3!==void 0&&_props$ts$lastValidat3.length)){tooltip.push("Target is ready.");}else{tooltip.push("Target is not ready.");}if((_props$ts$lastValidat4=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat4!==void 0&&_props$ts$lastValidat4.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.errors.length," validation errors."));}if((_props$ts$lastValidat5=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat5!==void 0&&_props$ts$lastValidat5.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.warnings.length," validation warnings."));}if((_props$ts$lastValidat6=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat6!==void 0&&_props$ts$lastValidat6.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.drift.length," drifted objects."));}tooltip.push("Validation performed "+calcAgo(props.ts.lastValidateResult.startTime)+" ago");}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip.map(function(t){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:t},t);}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:icon})});};var TargetItem=function TargetItem(props){var _props$ts$commandResu,_props$ts$commandResu2;var actionMenuItems=[];actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Validate now",handler:function handler(){api.validateNow(props.ps.project,props.ts.target);}});var kd;var allKdEqual=true;(_props$ts$commandResu=props.ts.commandResults)===null||_props$ts$commandResu===void 0?void 0:_props$ts$commandResu.forEach(function(rs){if(rs.commandInfo.kluctlDeployment){if(!kd){kd=rs.commandInfo.kluctlDeployment;}else{if(kd.name!==rs.commandInfo.kluctlDeployment.name||kd.namespace!==rs.commandInfo.kluctlDeployment.namespace){allKdEqual=false;}}}});if(kd&&allKdEqual){actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Reconcile",handler:function handler(){api.reconcileNow(props.ts.target.clusterId,kd.name,kd.namespace);}});actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Deploy",handler:function handler(){api.deployNow(props.ts.target.clusterId,kd.name,kd.namespace);}});}var allContexts=[];var handleContext=function handleContext(c){if(!c){return;}if(allContexts.find(function(x){return x===c;})){return;}allContexts.push(c);};(_props$ts$commandResu2=props.ts.commandResults)===null||_props$ts$commandResu2===void 0?void 0:_props$ts$commandResu2.forEach(function(rs){handleContext(rs.commandInfo.contextOverride);handleContext(rs.target.context);});var contextTooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{textAlign:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:"All known contexts:"}),allContexts.map(function(context){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:context},context);})]});var targetName=props.ts.target.targetName;if(!targetName){targetName="";}return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'20px 16px 12px 16px'},onClick:function onClick(e){return props.onSelectTarget(props.ts);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flexGrow:1,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:targetName}),allContexts.length?/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:contextTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:allContexts[0]})}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Cluster ID: "+props.ts.target.clusterId,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CpuIcon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Discriminator: "+props.ts.target.discriminator,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(FingerScanIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(StatusIcon,_objectSpread2({},props)),/*#__PURE__*/(0,jsx_runtime.jsx)(ActionsMenu,{menuItems:actionMenuItems})]})]})]})});}; +var StatusIcon=function StatusIcon(props){var icon;var theme=styles_useTheme_useTheme();if(props.ts.lastValidateResult===undefined){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(MessageQuestionIcon,{color:theme.palette.error.main});}else if(props.ts.lastValidateResult.ready&&!props.ts.lastValidateResult.errors){var _props$ts$lastValidat,_props$ts$lastValidat2;if((_props$ts$lastValidat=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat!==void 0&&_props$ts$lastValidat.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"warning"});}else if((_props$ts$lastValidat2=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat2!==void 0&&_props$ts$lastValidat2.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"primary"});}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"success"});}}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(HeartBroken,{color:"error"});}var tooltip=[];if(props.ts.lastValidateResult===undefined){tooltip.push("No validation result available.");}else{var _props$ts$lastValidat3,_props$ts$lastValidat4,_props$ts$lastValidat5,_props$ts$lastValidat6;if(props.ts.lastValidateResult.ready&&!((_props$ts$lastValidat3=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat3!==void 0&&_props$ts$lastValidat3.length)){tooltip.push("Target is ready.");}else{tooltip.push("Target is not ready.");}if((_props$ts$lastValidat4=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat4!==void 0&&_props$ts$lastValidat4.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.errors.length," validation errors."));}if((_props$ts$lastValidat5=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat5!==void 0&&_props$ts$lastValidat5.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.warnings.length," validation warnings."));}if((_props$ts$lastValidat6=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat6!==void 0&&_props$ts$lastValidat6.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.drift.length," drifted objects."));}tooltip.push("Validation performed "+calcAgo(props.ts.lastValidateResult.startTime)+" ago");}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip.map(function(t){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:t},t);}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:icon})});};var TargetItem=function TargetItem(props){var _props$ts$commandResu,_props$ts$commandResu2;var api=(0,react.useContext)(ApiContext);var actionMenuItems=[];actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Validate now",handler:function handler(){api.validateNow(props.ps.project,props.ts.target);}});var kd;var allKdEqual=true;(_props$ts$commandResu=props.ts.commandResults)===null||_props$ts$commandResu===void 0?void 0:_props$ts$commandResu.forEach(function(rs){if(rs.commandInfo.kluctlDeployment){if(!kd){kd=rs.commandInfo.kluctlDeployment;}else{if(kd.name!==rs.commandInfo.kluctlDeployment.name||kd.namespace!==rs.commandInfo.kluctlDeployment.namespace){allKdEqual=false;}}}});if(kd&&allKdEqual){actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Reconcile",handler:function handler(){api.reconcileNow(props.ts.target.clusterId,kd.name,kd.namespace);}});actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Deploy",handler:function handler(){api.deployNow(props.ts.target.clusterId,kd.name,kd.namespace);}});}var allContexts=[];var handleContext=function handleContext(c){if(!c){return;}if(allContexts.find(function(x){return x===c;})){return;}allContexts.push(c);};(_props$ts$commandResu2=props.ts.commandResults)===null||_props$ts$commandResu2===void 0?void 0:_props$ts$commandResu2.forEach(function(rs){handleContext(rs.commandInfo.contextOverride);handleContext(rs.target.context);});var contextTooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{textAlign:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:"All known contexts:"}),allContexts.map(function(context){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:context},context);})]});var targetName=props.ts.target.targetName;if(!targetName){targetName="";}return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'20px 16px 12px 16px'},onClick:function onClick(e){return props.onSelectTarget(props.ts);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flexGrow:1,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:targetName}),allContexts.length?/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:contextTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:allContexts[0]})}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Cluster ID: "+props.ts.target.clusterId,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CpuIcon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Discriminator: "+props.ts.target.discriminator,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(FingerScanIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(StatusIcon,_objectSpread2({},props)),/*#__PURE__*/(0,jsx_runtime.jsx)(ActionsMenu,{menuItems:actionMenuItems})]})]})]})});}; ;// CONCATENATED MODULE: ./node_modules/js-yaml/dist/js-yaml.mjs /*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT */ function isNothing(subject) { @@ -57554,187 +57739,8 @@ function buildListKey(o){var j=JSON.stringify(o);return (0,sha256.sha256)(j);} var RefList=function RefList(props){return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:props.title}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Kind"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Namespace"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Name"})})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:props.refs.map(function(ref){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:buildRefKindElement(ref)}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.namespace})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.name})})]},buildRefString(ref));})})]})})]});};function ChangesTable(props){var changedObjects;if(props.diffStatus.changedObjects.length){changedObjects=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:"Changed Objects"}),props.diffStatus.changedObjects.map(function(co){var _co$changes;return/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(TableHead_TableHead,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableRow_TableRow,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",colSpan:2,sx:{padding:'24px 16px 5px 16px'},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{sx:{fontWeight:500,fontSize:'20px',lineHeight:'27px',letterSpacing:'1px'},children:buildRefString(co.ref)})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Path"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Changes"})]})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_co$changes=co.changes)===null||_co$changes===void 0?void 0:_co$changes.map(function(c){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{minWidth:"100px",sx:{overflowWrap:"anywhere"},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:c.jsonPath})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:c.unifiedDiff||"",language:"diff"})})]},buildListKey(c));})})]})},buildRefString(co.ref));})]});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{width:"100%",display:"flex",flexDirection:"column",children:[props.diffStatus.newObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"New Objects",refs:props.diffStatus.newObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.deletedObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Deleted Objects",refs:props.diffStatus.deletedObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.orphanObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Orphan Objects",refs:props.diffStatus.orphanObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),changedObjects]});} ;// CONCATENATED MODULE: ./src/components/ErrorsTable.tsx function ErrorsTable(props){var _props$errors;return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Ref"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Message"})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_props$errors=props.errors)===null||_props$errors===void 0?void 0:_props$errors.map(function(e){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{sx:{minWidth:"150px"},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(List_List,{disablePadding:true,children:[buildRefKindElement(e.ref,/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.kind})})})),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.name})}),e.ref.namespace&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.namespace})})]})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:e.message})]},buildListKey(e));})})]})})})});} -;// CONCATENATED MODULE: ./node_modules/@mui/material/CircularProgress/circularProgressClasses.js - - -function getCircularProgressUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiCircularProgress', slot); -} -var circularProgressClasses = generateUtilityClasses('MuiCircularProgress', ['root', 'determinate', 'indeterminate', 'colorPrimary', 'colorSecondary', 'svg', 'circle', 'circleDeterminate', 'circleIndeterminate', 'circleDisableShrink']); -/* harmony default export */ var CircularProgress_circularProgressClasses = ((/* unused pure expression or super */ null && (circularProgressClasses))); -;// CONCATENATED MODULE: ./node_modules/@mui/material/CircularProgress/CircularProgress.js - -var CircularProgress_templateObject, CircularProgress_templateObject2, CircularProgress_templateObject3, CircularProgress_templateObject4; - - -var CircularProgress_excluded = ["className", "color", "disableShrink", "size", "style", "thickness", "value", "variant"]; -var CircularProgress_ = function _(t) { - return t; - }, - CircularProgress_t, - CircularProgress_t2, - CircularProgress_t3, - CircularProgress_t4; - - - - - - - - - - - -var SIZE = 44; -var circularRotateKeyframe = keyframes(CircularProgress_t || (CircularProgress_t = CircularProgress_(CircularProgress_templateObject || (CircularProgress_templateObject = _taggedTemplateLiteral(["\n 0% {\n transform: rotate(0deg);\n }\n\n 100% {\n transform: rotate(360deg);\n }\n"]))))); -var circularDashKeyframe = keyframes(CircularProgress_t2 || (CircularProgress_t2 = CircularProgress_(CircularProgress_templateObject2 || (CircularProgress_templateObject2 = _taggedTemplateLiteral(["\n 0% {\n stroke-dasharray: 1px, 200px;\n stroke-dashoffset: 0;\n }\n\n 50% {\n stroke-dasharray: 100px, 200px;\n stroke-dashoffset: -15px;\n }\n\n 100% {\n stroke-dasharray: 100px, 200px;\n stroke-dashoffset: -125px;\n }\n"]))))); -var CircularProgress_useUtilityClasses = function useUtilityClasses(ownerState) { - var classes = ownerState.classes, - variant = ownerState.variant, - color = ownerState.color, - disableShrink = ownerState.disableShrink; - var slots = { - root: ['root', variant, "color".concat(utils_capitalize(color))], - svg: ['svg'], - circle: ['circle', "circle".concat(utils_capitalize(variant)), disableShrink && 'circleDisableShrink'] - }; - return composeClasses(slots, getCircularProgressUtilityClass, classes); -}; -var CircularProgressRoot = styles_styled('span', { - name: 'MuiCircularProgress', - slot: 'Root', - overridesResolver: function overridesResolver(props, styles) { - var ownerState = props.ownerState; - return [styles.root, styles[ownerState.variant], styles["color".concat(utils_capitalize(ownerState.color))]]; - } -})(function (_ref) { - var ownerState = _ref.ownerState, - theme = _ref.theme; - return extends_extends({ - display: 'inline-block' - }, ownerState.variant === 'determinate' && { - transition: theme.transitions.create('transform') - }, ownerState.color !== 'inherit' && { - color: (theme.vars || theme).palette[ownerState.color].main - }); -}, function (_ref2) { - var ownerState = _ref2.ownerState; - return ownerState.variant === 'indeterminate' && css(CircularProgress_t3 || (CircularProgress_t3 = CircularProgress_(CircularProgress_templateObject3 || (CircularProgress_templateObject3 = _taggedTemplateLiteral(["\n animation: ", " 1.4s linear infinite;\n "])), 0)), circularRotateKeyframe); -}); -var CircularProgressSVG = styles_styled('svg', { - name: 'MuiCircularProgress', - slot: 'Svg', - overridesResolver: function overridesResolver(props, styles) { - return styles.svg; - } -})({ - display: 'block' // Keeps the progress centered -}); - -var CircularProgressCircle = styles_styled('circle', { - name: 'MuiCircularProgress', - slot: 'Circle', - overridesResolver: function overridesResolver(props, styles) { - var ownerState = props.ownerState; - return [styles.circle, styles["circle".concat(utils_capitalize(ownerState.variant))], ownerState.disableShrink && styles.circleDisableShrink]; - } -})(function (_ref3) { - var ownerState = _ref3.ownerState, - theme = _ref3.theme; - return extends_extends({ - stroke: 'currentColor' - }, ownerState.variant === 'determinate' && { - transition: theme.transitions.create('stroke-dashoffset') - }, ownerState.variant === 'indeterminate' && { - // Some default value that looks fine waiting for the animation to kicks in. - strokeDasharray: '80px, 200px', - strokeDashoffset: 0 // Add the unit to fix a Edge 16 and below bug. - }); -}, function (_ref4) { - var ownerState = _ref4.ownerState; - return ownerState.variant === 'indeterminate' && !ownerState.disableShrink && css(CircularProgress_t4 || (CircularProgress_t4 = CircularProgress_(CircularProgress_templateObject4 || (CircularProgress_templateObject4 = _taggedTemplateLiteral(["\n animation: ", " 1.4s ease-in-out infinite;\n "])), 0)), circularDashKeyframe); -}); - -/** - * ## ARIA - * - * If the progress bar is describing the loading progress of a particular region of a page, - * you should use `aria-describedby` to point to the progress bar, and set the `aria-busy` - * attribute to `true` on that region until it has finished loading. - */ -var CircularProgress = /*#__PURE__*/react.forwardRef(function CircularProgress(inProps, ref) { - var props = useThemeProps_useThemeProps({ - props: inProps, - name: 'MuiCircularProgress' - }); - var className = props.className, - _props$color = props.color, - color = _props$color === void 0 ? 'primary' : _props$color, - _props$disableShrink = props.disableShrink, - disableShrink = _props$disableShrink === void 0 ? false : _props$disableShrink, - _props$size = props.size, - size = _props$size === void 0 ? 40 : _props$size, - style = props.style, - _props$thickness = props.thickness, - thickness = _props$thickness === void 0 ? 3.6 : _props$thickness, - _props$value = props.value, - value = _props$value === void 0 ? 0 : _props$value, - _props$variant = props.variant, - variant = _props$variant === void 0 ? 'indeterminate' : _props$variant, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, CircularProgress_excluded); - var ownerState = extends_extends({}, props, { - color: color, - disableShrink: disableShrink, - size: size, - thickness: thickness, - value: value, - variant: variant - }); - var classes = CircularProgress_useUtilityClasses(ownerState); - var circleStyle = {}; - var rootStyle = {}; - var rootProps = {}; - if (variant === 'determinate') { - var circumference = 2 * Math.PI * ((SIZE - thickness) / 2); - circleStyle.strokeDasharray = circumference.toFixed(3); - rootProps['aria-valuenow'] = Math.round(value); - circleStyle.strokeDashoffset = "".concat(((100 - value) / 100 * circumference).toFixed(3), "px"); - rootStyle.transform = 'rotate(-90deg)'; - } - return /*#__PURE__*/(0,jsx_runtime.jsx)(CircularProgressRoot, extends_extends({ - className: clsx_m(classes.root, className), - style: extends_extends({ - width: size, - height: size - }, rootStyle, style), - ownerState: ownerState, - ref: ref, - role: "progressbar" - }, rootProps, other, { - children: /*#__PURE__*/(0,jsx_runtime.jsx)(CircularProgressSVG, { - className: classes.svg, - ownerState: ownerState, - viewBox: "".concat(SIZE / 2, " ").concat(SIZE / 2, " ").concat(SIZE, " ").concat(SIZE), - children: /*#__PURE__*/(0,jsx_runtime.jsx)(CircularProgressCircle, { - className: classes.circle, - style: circleStyle, - ownerState: ownerState, - cx: SIZE, - cy: SIZE, - r: (SIZE - thickness) / 2, - fill: "none", - strokeWidth: thickness - }) - }) - })); -}); - false ? 0 : void 0; -/* harmony default export */ var CircularProgress_CircularProgress = (CircularProgress); -;// CONCATENATED MODULE: ./src/components/Loading.tsx -var Loading=function Loading(){return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",width:"100%",height:"100%",alignItems:"center",justifyContent:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CircularProgress_CircularProgress,{})})});}; ;// CONCATENATED MODULE: ./src/components/ObjectYaml.tsx -var ObjectYaml=function ObjectYaml(props){var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),promise=_useState2[0],setPromise=_useState2[1];(0,react.useEffect)(function(){var getData=/*#__PURE__*/function(){var _ref=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var o;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return api.getResultObject(props.treeProps.summary.id,props.objectRef,props.objectType);case 2:o=_context.sent;return _context.abrupt("return",dump(o));case 4:case"end":return _context.stop();}},_callee);}));return function getData(){return _ref.apply(this,arguments);};}();setPromise(getData());},[props.treeProps,props.objectRef,props.objectType]);var Content=function Content(){return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:usePromise(promise),language:"yaml"});};return/*#__PURE__*/(0,jsx_runtime.jsx)(react.Suspense,{fallback:/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(Content,{})});}; +var ObjectYaml=function ObjectYaml(props){var api=(0,react.useContext)(ApiContext);var _useLoadingHelper=useLoadingHelper(/*#__PURE__*/asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var o;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return api.getResultObject(props.treeProps.summary.id,props.objectRef,props.objectType);case 2:o=_context.sent;return _context.abrupt("return",dump(o));case 4:case"end":return _context.stop();}},_callee);})),[props.treeProps.summary.id,props.objectRef,props.objectType]),_useLoadingHelper2=slicedToArray_slicedToArray(_useLoadingHelper,3),loading=_useLoadingHelper2[0],error=_useLoadingHelper2[1],content=_useLoadingHelper2[2];if(loading){return/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{});}else if(error){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:"Error"});}else{return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:content,language:"yaml"});}}; ;// CONCATENATED MODULE: ./src/components/result-view/nodes/NodeData.tsx var DiffStatus=/*#__PURE__*/function(){function DiffStatus(){classCallCheck_classCallCheck(this,DiffStatus);this.newObjects=[];this.deletedObjects=[];this.orphanObjects=[];this.changedObjects=[];this.totalInsertions=0;this.totalDeletions=0;this.totalUpdates=0;}createClass_createClass(DiffStatus,[{key:"addChangedObject",value:function addChangedObject(co){var _co$changes,_this=this;this.changedObjects.push(co);(_co$changes=co.changes)===null||_co$changes===void 0?void 0:_co$changes.forEach(function(x){switch(x.type){case"insert":_this.totalInsertions++;break;case"delete":_this.totalDeletions++;break;case"update":_this.totalUpdates++;break;}});}},{key:"merge",value:function merge(other){this.newObjects=this.newObjects.concat(other.newObjects);this.deletedObjects=this.deletedObjects.concat(other.deletedObjects);this.orphanObjects=this.orphanObjects.concat(other.orphanObjects);this.changedObjects=this.changedObjects.concat(other.changedObjects);this.totalInsertions+=other.totalInsertions;this.totalDeletions+=other.totalDeletions;this.totalUpdates+=other.totalUpdates;}},{key:"hasDiffs",value:function hasDiffs(){if(this.newObjects.length||this.deletedObjects.length||this.orphanObjects.length){return true;}if(this.changedObjects.find(function(co){var _co$changes2;return((_co$changes2=co.changes)===null||_co$changes2===void 0?void 0:_co$changes2.length)!==0;})){return true;}return false;}}]);return DiffStatus;}();var HealthStatus=/*#__PURE__*/function(){function HealthStatus(){classCallCheck_classCallCheck(this,HealthStatus);this.errors=[];this.warnings=[];}createClass_createClass(HealthStatus,[{key:"merge",value:function merge(other){this.errors=this.errors.concat(other.errors);this.warnings=this.warnings.concat(other.warnings);}}]);return HealthStatus;}();var NodeData=/*#__PURE__*/function(){function NodeData(props,id,hasHealthStatus,hasDiffStatus){classCallCheck_classCallCheck(this,NodeData);this.props=void 0;this.id=void 0;this.children=[];this.healthStatus=void 0;this.diffStatus=void 0;this.props=props;this.id=id;if(hasHealthStatus){this.healthStatus=new HealthStatus();}if(hasDiffStatus){this.diffStatus=new DiffStatus();}}createClass_createClass(NodeData,[{key:"pushChild",value:function pushChild(child,front){if(front){this.children.unshift(child);}else{this.children.push(child);}this.merge(child);}},{key:"merge",value:function merge(other){if(this.diffStatus&&other.diffStatus){this.diffStatus.merge(other.diffStatus);}if(this.healthStatus&&other.healthStatus){this.healthStatus.merge(other.healthStatus);}}},{key:"buildStatusLine",value:function buildStatusLine(){var _this$healthStatus,_this$healthStatus2,_this$diffStatus,_this$diffStatus2,_this$diffStatus3,_this$diffStatus4;return/*#__PURE__*/(0,jsx_runtime.jsx)(StatusLine,{errors:(_this$healthStatus=this.healthStatus)===null||_this$healthStatus===void 0?void 0:_this$healthStatus.errors.length,warnings:(_this$healthStatus2=this.healthStatus)===null||_this$healthStatus2===void 0?void 0:_this$healthStatus2.warnings.length,changedObjects:(_this$diffStatus=this.diffStatus)===null||_this$diffStatus===void 0?void 0:_this$diffStatus.changedObjects.length,newObjects:(_this$diffStatus2=this.diffStatus)===null||_this$diffStatus2===void 0?void 0:_this$diffStatus2.newObjects.length,deletedObjects:(_this$diffStatus3=this.diffStatus)===null||_this$diffStatus3===void 0?void 0:_this$diffStatus3.deletedObjects.length,orphanObjects:(_this$diffStatus4=this.diffStatus)===null||_this$diffStatus4===void 0?void 0:_this$diffStatus4.orphanObjects.length});}},{key:"buildTreeItem",value:function buildTreeItem(hasChildren){var _this$healthStatus3,_this$healthStatus4,_this$diffStatus5,_this$diffStatus6,_this$diffStatus7,_this$diffStatus8;var _this$buildIcon=this.buildIcon(),_this$buildIcon2=slicedToArray_slicedToArray(_this$buildIcon,2),icon=_this$buildIcon2[0],iconText=_this$buildIcon2[1];var hasStatusLine=[(_this$healthStatus3=this.healthStatus)===null||_this$healthStatus3===void 0?void 0:_this$healthStatus3.errors.length,(_this$healthStatus4=this.healthStatus)===null||_this$healthStatus4===void 0?void 0:_this$healthStatus4.warnings.length,(_this$diffStatus5=this.diffStatus)===null||_this$diffStatus5===void 0?void 0:_this$diffStatus5.changedObjects.length,(_this$diffStatus6=this.diffStatus)===null||_this$diffStatus6===void 0?void 0:_this$diffStatus6.newObjects.length,(_this$diffStatus7=this.diffStatus)===null||_this$diffStatus7===void 0?void 0:_this$diffStatus7.deletedObjects.length,(_this$diffStatus8=this.diffStatus)===null||_this$diffStatus8===void 0?void 0:_this$diffStatus8.orphanObjects.length].some(function(x){return(x||0)>0;});return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",height:"100%",flex:"1 1 auto",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",width:"30px",height:"100%",flex:"0 0 auto",mr:"13px",sx:{'& svg':{width:'30px',height:'30px'}},children:[icon,/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",component:"div",fontSize:"12px",fontWeight:400,lineHeight:"16px",height:"16px",children:iconText})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",height:"100%",flex:"1 1 auto",py:"15px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,_objectSpread2(_objectSpread2({variant:"h6",component:"div",sx:{wordBreak:'break-all'}},hasChildren?{}:{fontSize:'16px',lineHeight:'22px'}),{},{children:this.buildSidePanelTitle()}))}),hasStatusLine&&/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{height:"100%",width:"172px",flex:"0 0 auto",display:"flex",alignItems:"center",px:"14px",ml:"14px",position:"relative",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{orientation:"vertical",sx:{height:'40px',position:'absolute',left:0}}),this.buildStatusLine()]})]});}},{key:"buildDiffAndHealthPages",value:function buildDiffAndHealthPages(tabs){this.buildChangesPage(tabs);this.buildErrorsPage(tabs);this.buildWarningsPage(tabs);}},{key:"buildObjectPage",value:function buildObjectPage(ref,objectType){return/*#__PURE__*/(0,jsx_runtime.jsx)(ObjectYaml,{treeProps:this.props,objectRef:ref,objectType:objectType});}},{key:"buildChangesPage",value:function buildChangesPage(tabs){var _this$diffStatus9;if(!((_this$diffStatus9=this.diffStatus)!==null&&_this$diffStatus9!==void 0&&_this$diffStatus9.hasDiffs())){return undefined;}tabs.push({label:"Changes",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ChangesTable,{diffStatus:this.diffStatus})});}},{key:"buildErrorsPage",value:function buildErrorsPage(tabs){var _this$healthStatus5;if(!((_this$healthStatus5=this.healthStatus)!==null&&_this$healthStatus5!==void 0&&_this$healthStatus5.errors.length)){return undefined;}tabs.push({label:"Errors",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.healthStatus.errors})});}},{key:"buildWarningsPage",value:function buildWarningsPage(tabs){var _this$healthStatus6;if(!((_this$healthStatus6=this.healthStatus)!==null&&_this$healthStatus6!==void 0&&_this$healthStatus6.warnings.length)){return undefined;}tabs.push({label:"Warnings",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.healthStatus.warnings})});}}]);return NodeData;}(); ;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Category.js @@ -59157,7 +59163,7 @@ setSelectedTab(tabs[0].label);}// ignore that it wants us to add selectedTab to ;// CONCATENATED MODULE: ./src/components/targets-view/Card.tsx var Card_excluded=["children"],Card_excluded2=["children"],Card_excluded3=["children"];var cardWidth=247;var projectCardHeight=80;var cardHeight=126;var cardGap=20;function Card(_ref){var children=_ref.children,rest=objectWithoutProperties_objectWithoutProperties(_ref,Card_excluded);return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2(_objectSpread2({display:"flex",flexShrink:0,width:cardWidth,height:cardHeight},rest),{},{children:children}));}function CardCol(_ref2){var children=_ref2.children,rest=objectWithoutProperties_objectWithoutProperties(_ref2,Card_excluded2);return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2(_objectSpread2({display:"flex",flexDirection:"column",gap:"".concat(cardGap,"px")},rest),{},{children:children}));}function CardRow(_ref3){var children=_ref3.children,rest=objectWithoutProperties_objectWithoutProperties(_ref3,Card_excluded3);return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2(_objectSpread2({display:"flex",gap:"".concat(cardGap,"px")},rest),{},{children:children}));} ;// CONCATENATED MODULE: ./src/components/targets-view/CommandResultDetailsDrawer.tsx -var sidePanelWidth=720;function doGetRootNode(_x){return _doGetRootNode.apply(this,arguments);}function _doGetRootNode(){_doGetRootNode=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(rs){var shortNames,r,builder,_builder$buildRoot,_builder$buildRoot2,node;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:shortNames=api.getShortNames();r=api.getResult(rs.id);_context.t0=NodeBuilder;_context.next=5;return shortNames;case 5:_context.t1=_context.sent;_context.t2=rs;_context.next=9;return r;case 9:_context.t3=_context.sent;_context.t4={shortNames:_context.t1,summary:_context.t2,commandResult:_context.t3};builder=new _context.t0(_context.t4);_builder$buildRoot=builder.buildRoot(),_builder$buildRoot2=slicedToArray_slicedToArray(_builder$buildRoot,1),node=_builder$buildRoot2[0];return _context.abrupt("return",node);case 14:case"end":return _context.stop();}},_callee);}));return _doGetRootNode.apply(this,arguments);}var CommandResultDetailsDrawer=/*#__PURE__*/react.memo(function(props){var ps=props.ps,ts=props.ts,selectedCardRect=props.selectedCardRect;var theme=styles_useTheme_useTheme();var _useState=(0,react.useState)(new Promise(function(){return undefined;})),_useState2=slicedToArray_slicedToArray(_useState,2),promise=_useState2[0],setPromise=_useState2[1];var _useState3=(0,react.useState)(),_useState4=slicedToArray_slicedToArray(_useState3,2),selectedCommandResult=_useState4[0],setSelectedCommandResult=_useState4[1];var _useState5=(0,react.useState)(ts),_useState6=slicedToArray_slicedToArray(_useState5,2),prevTargetSummary=_useState6[0],setPrevTargetSummary=_useState6[1];var _useState7=(0,react.useState)([]),_useState8=slicedToArray_slicedToArray(_useState7,2),cardsCoords=_useState8[0],setCardsCoords=_useState8[1];var _useState9=(0,react.useState)(false),_useState10=slicedToArray_slicedToArray(_useState9,2),transitionRunning=_useState10[0],setTransitionRunning=_useState10[1];if(prevTargetSummary!==ts){var _ts$commandResults;setPrevTargetSummary(ts);setSelectedCommandResult(ts===null||ts===void 0?void 0:(_ts$commandResults=ts.commandResults)===null||_ts$commandResults===void 0?void 0:_ts$commandResults[0]);}(0,react.useEffect)(function(){if(selectedCommandResult===undefined){return;}setPromise(doGetRootNode(selectedCommandResult));},[selectedCommandResult]);var Content=function Content(props){var node=usePromise(promise);return/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:node,onClose:props.onClose});};var cardsContainerElem=(0,react.useRef)();(0,react.useEffect)(function(){var _cardsContainerElem$c;var rect=(_cardsContainerElem$c=cardsContainerElem.current)===null||_cardsContainerElem$c===void 0?void 0:_cardsContainerElem$c.getBoundingClientRect();if(!rect||!selectedCardRect||!(ts!==null&&ts!==void 0&&ts.commandResults)){setCardsCoords([]);return;}var initialCoords=ts.commandResults.map(function(){return{left:selectedCardRect.left-rect.left,top:selectedCardRect.top-rect.top};});setCardsCoords(initialCoords);setTransitionRunning(true);},[selectedCardRect,ts===null||ts===void 0?void 0:ts.commandResults]);(0,react.useEffect)(function(){if(cardsCoords.length>0){var targetCoords=cardsCoords.map(function(_,i){return{left:0,top:i*(cardHeight+cardGap)};});if(cardsCoords.length===targetCoords.length&&cardsCoords.every(function(_ref,i){var left=_ref.left,top=_ref.top;return targetCoords[i].left===left&&targetCoords[i].top===top;})){return;}setTimeout(function(){setCardsCoords(targetCoords);setTimeout(function(){setTransitionRunning(false);},theme.transitions.duration.enteringScreen);},10);}},[cardsCoords,theme.transitions.duration.enteringScreen]);var zIndex=theme.zIndex.modal+1;var cards=(0,react.useMemo)(function(){if(!(ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&cardsCoords.length>0)){return null;}return ts.commandResults.map(function(rs,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Card,{sx:{position:'absolute',translate:"".concat(cardsCoords[i].left,"px ").concat(cardsCoords[i].top,"px"),zIndex:zIndex,transition:theme.transitions.create(['translate'],{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.enteringScreen})},children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultItem,{ps:ps,ts:ts,rs:rs,onSelectCommandResult:setSelectedCommandResult,selected:rs===selectedCommandResult})},rs.id);});},[ps,ts,cardsCoords,zIndex,theme.transitions,selectedCommandResult]);return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{sx:{position:'fixed',top:0,bottom:0,right:sidePanelWidth,width:"calc(100% - ".concat(sidePanelWidth,"px)"),overflowX:'visible',overflowY:transitionRunning?'visible':'auto',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',padding:'25px 0',zIndex:zIndex},onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{onClick:function onClick(e){return e.stopPropagation();},position:"relative",width:cardWidth,height:cardHeight*ts.commandResults.length+cardGap*(ts.commandResults.length-1),ref:cardsContainerElem,children:cards})}),/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.rs!==undefined,onClose:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:sidePanelWidth,height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(react.Suspense,{fallback:/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(Content,{onClose:props.onClose})})})})})]});}); +var sidePanelWidth=720;function doGetRootNode(_x,_x2){return _doGetRootNode.apply(this,arguments);}function _doGetRootNode(){_doGetRootNode=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(api,rs){var shortNames,r,builder,_builder$buildRoot,_builder$buildRoot2,node;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return api.getShortNames();case 2:shortNames=_context.sent;_context.next=5;return api.getResult(rs.id);case 5:r=_context.sent;builder=new NodeBuilder({shortNames:shortNames,summary:rs,commandResult:r});_builder$buildRoot=builder.buildRoot(),_builder$buildRoot2=slicedToArray_slicedToArray(_builder$buildRoot,1),node=_builder$buildRoot2[0];return _context.abrupt("return",node);case 9:case"end":return _context.stop();}},_callee);}));return _doGetRootNode.apply(this,arguments);}var CommandResultDetailsDrawer=/*#__PURE__*/react.memo(function(props){var ps=props.ps,ts=props.ts,selectedCardRect=props.selectedCardRect;var api=(0,react.useContext)(ApiContext);var theme=styles_useTheme_useTheme();var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),selectedCommandResult=_useState2[0],setSelectedCommandResult=_useState2[1];var _useState3=(0,react.useState)(ts),_useState4=slicedToArray_slicedToArray(_useState3,2),prevTargetSummary=_useState4[0],setPrevTargetSummary=_useState4[1];var _useState5=(0,react.useState)([]),_useState6=slicedToArray_slicedToArray(_useState5,2),cardsCoords=_useState6[0],setCardsCoords=_useState6[1];var _useState7=(0,react.useState)(false),_useState8=slicedToArray_slicedToArray(_useState7,2),transitionRunning=_useState8[0],setTransitionRunning=_useState8[1];if(prevTargetSummary!==ts){var _ts$commandResults;setPrevTargetSummary(ts);setSelectedCommandResult(ts===null||ts===void 0?void 0:(_ts$commandResults=ts.commandResults)===null||_ts$commandResults===void 0?void 0:_ts$commandResults[0]);}var _useLoadingHelper=useLoadingHelper(function(){if(selectedCommandResult===undefined){return Promise.resolve(undefined);}return doGetRootNode(api,selectedCommandResult);},[selectedCommandResult]),_useLoadingHelper2=slicedToArray_slicedToArray(_useLoadingHelper,3),loading=_useLoadingHelper2[0],loadingError=_useLoadingHelper2[1],nodeData=_useLoadingHelper2[2];var cardsContainerElem=(0,react.useRef)();(0,react.useEffect)(function(){var _cardsContainerElem$c;var rect=(_cardsContainerElem$c=cardsContainerElem.current)===null||_cardsContainerElem$c===void 0?void 0:_cardsContainerElem$c.getBoundingClientRect();if(!rect||!selectedCardRect||!(ts!==null&&ts!==void 0&&ts.commandResults)){setCardsCoords([]);return;}var initialCoords=ts.commandResults.map(function(){return{left:selectedCardRect.left-rect.left,top:selectedCardRect.top-rect.top};});setCardsCoords(initialCoords);setTransitionRunning(true);},[selectedCardRect,ts===null||ts===void 0?void 0:ts.commandResults]);(0,react.useEffect)(function(){if(cardsCoords.length>0){var targetCoords=cardsCoords.map(function(_,i){return{left:0,top:i*(cardHeight+cardGap)};});if(cardsCoords.length===targetCoords.length&&cardsCoords.every(function(_ref,i){var left=_ref.left,top=_ref.top;return targetCoords[i].left===left&&targetCoords[i].top===top;})){return;}setTimeout(function(){setCardsCoords(targetCoords);setTimeout(function(){setTransitionRunning(false);},theme.transitions.duration.enteringScreen);},10);}},[cardsCoords,theme.transitions.duration.enteringScreen]);var zIndex=theme.zIndex.modal+1;var cards=(0,react.useMemo)(function(){if(!(ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&cardsCoords.length>0)){return null;}return ts.commandResults.map(function(rs,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Card,{sx:{position:'absolute',translate:"".concat(cardsCoords[i].left,"px ").concat(cardsCoords[i].top,"px"),zIndex:zIndex,transition:theme.transitions.create(['translate'],{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.enteringScreen})},children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultItem,{ps:ps,ts:ts,rs:rs,onSelectCommandResult:setSelectedCommandResult,selected:rs===selectedCommandResult})},rs.id);});},[ps,ts,cardsCoords,zIndex,theme.transitions,selectedCommandResult]);if(loadingError){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:"Error"});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{sx:{position:'fixed',top:0,bottom:0,right:sidePanelWidth,width:"calc(100% - ".concat(sidePanelWidth,"px)"),overflowX:'visible',overflowY:transitionRunning?'visible':'auto',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',padding:'25px 0',zIndex:zIndex},onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{onClick:function onClick(e){return e.stopPropagation();},position:"relative",width:cardWidth,height:cardHeight*ts.commandResults.length+cardGap*(ts.commandResults.length-1),ref:cardsContainerElem,children:cards})}),/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.rs!==undefined,onClose:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:sidePanelWidth,height:"100%",children:loading?/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{}):/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:nodeData,onClose:props.onClose})})})})]});}); ;// CONCATENATED MODULE: ./src/components/targets-view/TargetDetailsDrawer.tsx var MyProvider=/*#__PURE__*/function(){function MyProvider(ts){var _this$ts,_this$ts$lastValidate,_this$ts$lastValidate2,_this=this;classCallCheck_classCallCheck(this,MyProvider);this.ts=void 0;this.diffStatus=void 0;this.ts=ts;this.diffStatus=new DiffStatus();(_this$ts=this.ts)===null||_this$ts===void 0?void 0:(_this$ts$lastValidate=_this$ts.lastValidateResult)===null||_this$ts$lastValidate===void 0?void 0:(_this$ts$lastValidate2=_this$ts$lastValidate.drift)===null||_this$ts$lastValidate2===void 0?void 0:_this$ts$lastValidate2.forEach(function(co){_this.diffStatus.addChangedObject(co);});}createClass_createClass(MyProvider,[{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var _this$ts$lastValidate3,_this$ts$lastValidate4,_this$ts$lastValidate5,_this$ts$lastValidate6;if(!this.ts){return[];}var tabs=[{label:"Summary",content:this.buildSummaryTab()}];if(this.ts.target)if(this.diffStatus.changedObjects.length){tabs.push({label:"Drift",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ChangesTable,{diffStatus:this.diffStatus})});}if((_this$ts$lastValidate3=this.ts.lastValidateResult)!==null&&_this$ts$lastValidate3!==void 0&&(_this$ts$lastValidate4=_this$ts$lastValidate3.errors)!==null&&_this$ts$lastValidate4!==void 0&&_this$ts$lastValidate4.length){tabs.push({label:"Errors",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.ts.lastValidateResult.errors})});}if((_this$ts$lastValidate5=this.ts.lastValidateResult)!==null&&_this$ts$lastValidate5!==void 0&&(_this$ts$lastValidate6=_this$ts$lastValidate5.warnings)!==null&&_this$ts$lastValidate6!==void 0&&_this$ts$lastValidate6.length){tabs.push({label:"Warnings",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.ts.lastValidateResult.warnings})});}return tabs;}},{key:"buildSummaryTab",value:function buildSummaryTab(){var _this$ts2,_this$ts3,_this$ts4,_this$ts4$lastValidat,_this$ts4$lastValidat2,_this$ts5,_this$ts5$lastValidat,_this$ts5$lastValidat2,_this$ts6,_this$ts6$lastValidat,_this$ts6$lastValidat2;var props=[{name:"Target Name",value:this.getTargetName()},{name:"Discriminator",value:(_this$ts2=this.ts)===null||_this$ts2===void 0?void 0:_this$ts2.target.discriminator}];if((_this$ts3=this.ts)!==null&&_this$ts3!==void 0&&_this$ts3.lastValidateResult){props.push({name:"Ready",value:this.ts.lastValidateResult.ready+""});}if((_this$ts4=this.ts)!==null&&_this$ts4!==void 0&&(_this$ts4$lastValidat=_this$ts4.lastValidateResult)!==null&&_this$ts4$lastValidat!==void 0&&(_this$ts4$lastValidat2=_this$ts4$lastValidat.errors)!==null&&_this$ts4$lastValidat2!==void 0&&_this$ts4$lastValidat2.length){props.push({name:"Errors",value:this.ts.lastValidateResult.errors.length+""});}if((_this$ts5=this.ts)!==null&&_this$ts5!==void 0&&(_this$ts5$lastValidat=_this$ts5.lastValidateResult)!==null&&_this$ts5$lastValidat!==void 0&&(_this$ts5$lastValidat2=_this$ts5$lastValidat.warnings)!==null&&_this$ts5$lastValidat2!==void 0&&_this$ts5$lastValidat2.length){props.push({name:"Warnings",value:this.ts.lastValidateResult.warnings.length+""});}if((_this$ts6=this.ts)!==null&&_this$ts6!==void 0&&(_this$ts6$lastValidat=_this$ts6.lastValidateResult)!==null&&_this$ts6$lastValidat!==void 0&&(_this$ts6$lastValidat2=_this$ts6$lastValidat.drift)!==null&&_this$ts6$lastValidat2!==void 0&&_this$ts6$lastValidat2.length){props.push({name:"Drifted Objects",value:this.ts.lastValidateResult.drift.length+""});}return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}},{key:"getTargetName",value:function getTargetName(){if(!this.ts){return"";}var name="";if(this.ts.target.targetName){name=this.ts.target.targetName;}return name;}},{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return this.getTargetName();}}]);return MyProvider;}();var TargetDetailsDrawer=/*#__PURE__*/react.memo(function(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.ts!==undefined,onClose:props.onClose,ModalProps:{BackdropProps:{invisible:true}},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"600px",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:new MyProvider(props.ts),onClose:props.onClose})})})});}); ;// CONCATENATED MODULE: ./src/components/targets-view/TargetsView.tsx @@ -61438,9 +61444,9 @@ function FilterNode(node,activeFilters){var _node$diffStatus,_node$healthStatus, ;// CONCATENATED MODULE: ./src/components/result-view/CommandResultTree.tsx var CommandResultTree=function CommandResultTree(props){var theme=styles_useTheme_useTheme();var _useState=(0,react.useState)(["root"]),_useState2=slicedToArray_slicedToArray(_useState,2),expanded=_useState2[0],setExpanded=_useState2[1];var _useMemo=(0,react.useMemo)(function(){if(!props.commandResultProps){return[undefined,undefined];}var builder=new NodeBuilder(props.commandResultProps);return builder.buildRoot();},[props.commandResultProps]),_useMemo2=slicedToArray_slicedToArray(_useMemo,1),rootNode=_useMemo2[0];var handleToggle=function handleToggle(event,nodeIds){setExpanded(nodeIds);};var handleDoubleClick=function handleDoubleClick(e,node){if(expanded.includes(node.id)){setExpanded(expanded.filter(function(item){return item!==node.id;}));}else{setExpanded([].concat(toConsumableArray_toConsumableArray(expanded),[node.id]));}e.stopPropagation();};var handleItemClick=function handleItemClick(e,node){props.onSelectNode(node);e.stopPropagation();};var renderTree=function renderTree(nodes){if(!FilterNode(nodes,props.activeFilters)){return null;}return/*#__PURE__*/(0,jsx_runtime.jsx)(TreeItem_TreeItem,{nodeId:nodes.id,label:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",onClick:function onClick(e){return handleItemClick(e,nodes);},pl:"22px",height:"100%",flex:"1 1 auto",position:"relative",children:[nodes.children.length!==0&&/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{orientation:"vertical",sx:{height:'40px',position:'absolute',left:0}}),nodes.buildTreeItem(nodes.children.length!==0)]}),sx:{'& .MuiTreeItem-content':{height:'78px',borderBottom:"0.5px solid ".concat(theme.palette.secondary.main),padding:0,overflow:'hidden','& .MuiTreeItem-iconContainer':{width:'50px',height:'50px',flex:'0 0 auto',margin:0,padding:0,display:nodes.children.length!==0?'flex':'none',justifyContent:'center',alignItems:'center'},'& .MuiTreeItem-label':{height:'100%',margin:0,padding:0,flex:'1 1 auto',display:'flex',alignItems:'center'}},'& .MuiTreeItem-group':{margin:'0 0 0 38px'}},onDoubleClick:function onDoubleClick(e){return handleDoubleClick(e,nodes);},children:Array.isArray(nodes.children)?nodes.children.map(function(node){return renderTree(node);}):null},nodes.id);};if(!rootNode){return/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{});}return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{sx:{padding:'20px 40px'},children:/*#__PURE__*/(0,jsx_runtime.jsx)(TreeView_TreeView,{expanded:expanded,onNodeToggle:handleToggle,"aria-label":"rich object",defaultCollapseIcon:/*#__PURE__*/(0,jsx_runtime.jsx)(TriangleDownIcon,{}),defaultExpandIcon:/*#__PURE__*/(0,jsx_runtime.jsx)(TriangleRightIcon,{}),sx:{width:"100%"},children:renderTree(rootNode)})});};/* harmony default export */ var result_view_CommandResultTree = (CommandResultTree); ;// CONCATENATED MODULE: ./src/components/result-view/CommandResultView.tsx -function commandResultLoader(_x){return _commandResultLoader.apply(this,arguments);}function _commandResultLoader(){_commandResultLoader=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(_ref){var params,result,shortNames,rs;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:params=_ref.params;result=api.getResult(params.id);shortNames=api.getShortNames();rs=api.getResult(params.id);_context.next=6;return shortNames;case 6:_context.t0=_context.sent;_context.next=9;return rs;case 9:_context.t1=_context.sent;_context.next=12;return result;case 12:_context.t2=_context.sent;return _context.abrupt("return",{shortNames:_context.t0,summary:_context.t1,commandResult:_context.t2});case 14:case"end":return _context.stop();}},_callee);}));return _commandResultLoader.apply(this,arguments);}var FilterCheckbox=function FilterCheckbox(props){var text=props.text,checked=props.checked,Icon=props.Icon,onChange=props.onChange;return/*#__PURE__*/(0,jsx_runtime.jsx)(FormControlLabel_FormControlLabel,{sx:{display:'flex',justifyContent:'center',alignItems:'center',margin:0,padding:0},control:/*#__PURE__*/(0,jsx_runtime.jsx)(Checkbox_Checkbox,{checked:checked,sx:{display:'flex',justifyContent:'center',alignItems:'center'},onChange:onChange,icon:/*#__PURE__*/(0,jsx_runtime.jsx)(CheckboxIcon,{}),checkedIcon:/*#__PURE__*/(0,jsx_runtime.jsx)(CheckboxCheckedIcon,{})}),slotProps:{typography:{sx:{display:'flex',justifyContent:'center',alignItems:'center',gap:'10px'}}},label:/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h2",children:text}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",display:"flex",alignItems:"center",justifyContent:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Icon,{})})]})});};function DetailsDrawer(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.nodeData!==undefined,onClose:props.onClose,ModalProps:{BackdropProps:{invisible:true}},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"720px",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:props.nodeData,onClose:props.onClose})})})});}var defaultFilters={onlyImportant:false,onlyChanged:false,onlyWithErrorsOrWarnings:false};var CommandResultView=function CommandResultView(){var _context$filters,_context$filters2,_context$filters3;var context=useAppOutletContext();var commandResultProps=useLoaderData();var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),sidePanelNode=_useState2[0],setSidePanelNode=_useState2[1];var divider=/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{orientation:"vertical",sx:{height:'40px',margin:'0 20px 0 30px'}});var handleFilterChange=function handleFilterChange(filter){return function(_,checked){context.setFilters(function(fs){return _objectSpread2(_objectSpread2({},fs||defaultFilters),{},defineProperty_defineProperty({},filter,checked));});};};return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{width:"100%",height:"100%",display:"flex",flexDirection:"column",overflow:"hidden",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(DetailsDrawer,{nodeData:sidePanelNode,onClose:function onClose(){return setSidePanelNode(undefined);}}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",minHeight:"70px",p:"0 40px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(FilterCheckbox,{text:"Only important",checked:!!((_context$filters=context.filters)!==null&&_context$filters!==void 0&&_context$filters.onlyImportant),Icon:StarIcon,onChange:handleFilterChange('onlyImportant')}),divider,/*#__PURE__*/(0,jsx_runtime.jsx)(FilterCheckbox,{text:"Only with changes",checked:!!((_context$filters2=context.filters)!==null&&_context$filters2!==void 0&&_context$filters2.onlyChanged),Icon:ChangesIcon,onChange:handleFilterChange('onlyChanged')}),divider,/*#__PURE__*/(0,jsx_runtime.jsx)(FilterCheckbox,{text:"Only with errors and warnings",checked:!!((_context$filters3=context.filters)!==null&&_context$filters3!==void 0&&_context$filters3.onlyWithErrorsOrWarnings),Icon:WarningSignIcon,onChange:handleFilterChange('onlyWithErrorsOrWarnings')})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:'0 40px'}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{p:"25px 40px",overflow:"auto",children:/*#__PURE__*/(0,jsx_runtime.jsx)(result_view_CommandResultTree,{commandResultProps:commandResultProps,onSelectNode:setSidePanelNode,activeFilters:context.filters})})]});}; +function doLoadCommandResult(_x,_x2){return _doLoadCommandResult.apply(this,arguments);}function _doLoadCommandResult(){_doLoadCommandResult=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(api,resultId){var shortNames,rs,result;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return api.getShortNames();case 2:shortNames=_context.sent;_context.next=5;return api.getResultSummary(resultId);case 5:rs=_context.sent;_context.next=8;return api.getResult(resultId);case 8:result=_context.sent;return _context.abrupt("return",{shortNames:shortNames,summary:rs,commandResult:result});case 10:case"end":return _context.stop();}},_callee);}));return _doLoadCommandResult.apply(this,arguments);}var FilterCheckbox=function FilterCheckbox(props){var text=props.text,checked=props.checked,Icon=props.Icon,onChange=props.onChange;return/*#__PURE__*/(0,jsx_runtime.jsx)(FormControlLabel_FormControlLabel,{sx:{display:'flex',justifyContent:'center',alignItems:'center',margin:0,padding:0},control:/*#__PURE__*/(0,jsx_runtime.jsx)(Checkbox_Checkbox,{checked:checked,sx:{display:'flex',justifyContent:'center',alignItems:'center'},onChange:onChange,icon:/*#__PURE__*/(0,jsx_runtime.jsx)(CheckboxIcon,{}),checkedIcon:/*#__PURE__*/(0,jsx_runtime.jsx)(CheckboxCheckedIcon,{})}),slotProps:{typography:{sx:{display:'flex',justifyContent:'center',alignItems:'center',gap:'10px'}}},label:/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h2",children:text}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",display:"flex",alignItems:"center",justifyContent:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Icon,{})})]})});};function DetailsDrawer(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.nodeData!==undefined,onClose:props.onClose,ModalProps:{BackdropProps:{invisible:true}},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"720px",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:props.nodeData,onClose:props.onClose})})})});}var defaultFilters={onlyImportant:false,onlyChanged:false,onlyWithErrorsOrWarnings:false};var CommandResultView=function CommandResultView(){var _context$filters,_context$filters2,_context$filters3;var context=useAppOutletContext();var api=(0,react.useContext)(ApiContext);var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),sidePanelNode=_useState2[0],setSidePanelNode=_useState2[1];var _useParams=useParams(),id=_useParams.id;var _useLoadingHelper=useLoadingHelper(function(){if(!id){return Promise.resolve(undefined);}return doLoadCommandResult(api,id);},[id]),_useLoadingHelper2=slicedToArray_slicedToArray(_useLoadingHelper,3),loading=_useLoadingHelper2[0],loadingError=_useLoadingHelper2[1],commandResultProps=_useLoadingHelper2[2];var divider=/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{orientation:"vertical",sx:{height:'40px',margin:'0 20px 0 30px'}});var handleFilterChange=function handleFilterChange(filter){return function(_,checked){context.setFilters(function(fs){return _objectSpread2(_objectSpread2({},fs||defaultFilters),{},defineProperty_defineProperty({},filter,checked));});};};if(loading){return/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{});}else if(loadingError){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:"Error"});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{width:"100%",height:"100%",display:"flex",flexDirection:"column",overflow:"hidden",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(DetailsDrawer,{nodeData:sidePanelNode,onClose:function onClose(){return setSidePanelNode(undefined);}}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",minHeight:"70px",p:"0 40px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(FilterCheckbox,{text:"Only important",checked:!!((_context$filters=context.filters)!==null&&_context$filters!==void 0&&_context$filters.onlyImportant),Icon:StarIcon,onChange:handleFilterChange('onlyImportant')}),divider,/*#__PURE__*/(0,jsx_runtime.jsx)(FilterCheckbox,{text:"Only with changes",checked:!!((_context$filters2=context.filters)!==null&&_context$filters2!==void 0&&_context$filters2.onlyChanged),Icon:ChangesIcon,onChange:handleFilterChange('onlyChanged')}),divider,/*#__PURE__*/(0,jsx_runtime.jsx)(FilterCheckbox,{text:"Only with errors and warnings",checked:!!((_context$filters3=context.filters)!==null&&_context$filters3!==void 0&&_context$filters3.onlyWithErrorsOrWarnings),Icon:WarningSignIcon,onChange:handleFilterChange('onlyWithErrorsOrWarnings')})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:'0 40px'}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{p:"25px 40px",overflow:"auto",children:/*#__PURE__*/(0,jsx_runtime.jsx)(result_view_CommandResultTree,{commandResultProps:commandResultProps,onSelectNode:setSidePanelNode,activeFilters:context.filters})})]});}; ;// CONCATENATED MODULE: ./src/components/Router.tsx -function ErrorPage(){var error=useRouteError();return/*#__PURE__*/(0,jsx_runtime.jsxs)("div",{id:"error-page",children:[/*#__PURE__*/(0,jsx_runtime.jsx)("h1",{children:"Oops!"}),/*#__PURE__*/(0,jsx_runtime.jsx)("p",{children:"Sorry, an unexpected error has occurred."}),/*#__PURE__*/(0,jsx_runtime.jsx)("p",{children:/*#__PURE__*/(0,jsx_runtime.jsx)("i",{children:error.statusText||error.message})})]});}var Router_Router=createHashRouter([{path:"/",element:/*#__PURE__*/(0,jsx_runtime.jsx)(components_App,{}),errorElement:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorPage,{}),children:[{path:"targets",element:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetsView,{}),errorElement:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorPage,{})},{path:"results/:id",element:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultView,{}),loader:commandResultLoader,errorElement:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorPage,{})}]}]); +function ErrorPage(){var error=useRouteError();return/*#__PURE__*/(0,jsx_runtime.jsxs)("div",{id:"error-page",children:[/*#__PURE__*/(0,jsx_runtime.jsx)("h1",{children:"Oops!"}),/*#__PURE__*/(0,jsx_runtime.jsx)("p",{children:"Sorry, an unexpected error has occurred."}),/*#__PURE__*/(0,jsx_runtime.jsx)("p",{children:/*#__PURE__*/(0,jsx_runtime.jsx)("i",{children:error.statusText||error.message})})]});}var Router_Router=createHashRouter([{path:"/",element:/*#__PURE__*/(0,jsx_runtime.jsx)(components_App,{}),errorElement:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorPage,{}),children:[{path:"targets",element:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetsView,{}),errorElement:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorPage,{})},{path:"results/:id",element:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultView,{}),errorElement:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorPage,{})}]}]); ;// CONCATENATED MODULE: ./src/index.tsx var root=client.createRoot(document.getElementById('root'));root.render(/*#__PURE__*/(0,jsx_runtime.jsx)(react.StrictMode,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(RouterProvider,{router:Router_Router})}));// If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) @@ -61449,4 +61455,4 @@ src_reportWebVitals(); }(); /******/ })() ; -//# sourceMappingURL=main.82849393.js.map \ No newline at end of file +//# sourceMappingURL=main.1b77f1d8.js.map \ No newline at end of file From 01ad4e06194ad4309409d76fbd854a7aeea66d52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:23:52 +0200 Subject: [PATCH 1178/2268] chore(deps): Bump github.com/gin-gonic/gin from 1.9.0 to 1.9.1 (#562) Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.9.0 to 1.9.1. - [Release notes](https://github.com/gin-gonic/gin/releases) - [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md) - [Commits](https://github.com/gin-gonic/gin/compare/v1.9.0...v1.9.1) --- updated-dependencies: - dependency-name: github.com/gin-gonic/gin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 07201c049..5833c1fdf 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 github.com/aws/smithy-go v1.13.5 github.com/dimchansky/utfbom v1.1.1 - github.com/gin-gonic/gin v1.9.0 + github.com/gin-gonic/gin v1.9.1 github.com/go-git/go-git/v5 v5.7.0 github.com/go-logr/logr v1.2.4 github.com/google/uuid v1.3.0 @@ -111,7 +111,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect - github.com/bytedance/sonic v1.8.0 // indirect + github.com/bytedance/sonic v1.9.1 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect @@ -148,7 +148,7 @@ require ( github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/goccy/go-json v0.10.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -179,7 +179,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/karrick/godirwalk v1.17.0 // indirect github.com/klauspost/compress v1.16.5 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect @@ -241,7 +241,7 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + golang.org/x/arch v0.3.0 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/go.sum b/go.sum index ff2d7461d..7f7709829 100644 --- a/go.sum +++ b/go.sum @@ -165,8 +165,8 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= -github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= @@ -281,8 +281,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= @@ -349,8 +349,8 @@ github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= -github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -564,8 +564,9 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2 h1:K96SwIr8MzBQ3kFFz2H/pA2y+EEk04vZ4fWj/YZghBU= github.com/kluctl/go-embed-python v0.0.0-3.10.9-20230206-2/go.mod h1:vzngsPshNKUtq0gxkYQKNJafrcH7Qy7Qt6yGNt7JmQI= github.com/kluctl/go-jinja2 v0.0.0-20230428103343-a832225dc94c h1:qAIvhYamCEU/tY6NaENEIQCynGV5sdON7zgZKnbrhhw= @@ -919,8 +920,9 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 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/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -1122,6 +1124,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 4214363906da18c36e89631c74b15287362c7e2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:24:31 +0200 Subject: [PATCH 1179/2268] chore(deps): Bump golang.org/x/sys from 0.8.0 to 0.9.0 (#563) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.8.0 to 0.9.0. - [Commits](https://github.com/golang/sys/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5833c1fdf..72a25c589 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( golang.org/x/crypto v0.9.0 golang.org/x/net v0.10.0 golang.org/x/sync v0.2.0 - golang.org/x/sys v0.8.0 + golang.org/x/sys v0.9.0 golang.org/x/term v0.8.0 golang.org/x/text v0.9.0 helm.sh/helm/v3 v3.12.0 diff --git a/go.sum b/go.sum index 7f7709829..6754581c7 100644 --- a/go.sum +++ b/go.sum @@ -1136,8 +1136,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/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/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From dbcca4081ae58a542bedcfb0049621d9a1b6ea0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:24:46 +0200 Subject: [PATCH 1180/2268] chore(deps): Bump golang.org/x/text from 0.9.0 to 0.10.0 (#564) Bumps [golang.org/x/text](https://github.com/golang/text) from 0.9.0 to 0.10.0. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.9.0...v0.10.0) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 72a25c589..5bc40aef5 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( golang.org/x/sync v0.2.0 golang.org/x/sys v0.9.0 golang.org/x/term v0.8.0 - golang.org/x/text v0.9.0 + golang.org/x/text v0.10.0 helm.sh/helm/v3 v3.12.0 k8s.io/api v0.27.2 k8s.io/apiextensions-apiserver v0.27.2 diff --git a/go.sum b/go.sum index 6754581c7..13ae2b76b 100644 --- a/go.sum +++ b/go.sum @@ -1161,8 +1161,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -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/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= From a779e4aeea0ad49662a53c49bda81973dabd8b2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:25:04 +0200 Subject: [PATCH 1181/2268] chore(deps): Bump github.com/aws/aws-sdk-go-v2/config (#567) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.25 to 1.18.26. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.25...config/v1.18.26) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 22 +++++++++++----------- go.sum | 41 ++++++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 5bc40aef5..26ad793f4 100644 --- a/go.mod +++ b/go.mod @@ -54,11 +54,11 @@ require ( filippo.io/age v1.1.1 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 - github.com/aws/aws-sdk-go-v2 v1.18.0 - github.com/aws/aws-sdk-go-v2/config v1.18.25 - github.com/aws/aws-sdk-go-v2/credentials v1.13.24 + github.com/aws/aws-sdk-go-v2 v1.18.1 + github.com/aws/aws-sdk-go-v2/config v1.18.26 + github.com/aws/aws-sdk-go-v2/credentials v1.13.25 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.8 - github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.19.1 github.com/aws/smithy-go v1.13.5 github.com/dimchansky/utfbom v1.1.1 github.com/gin-gonic/gin v1.9.1 @@ -101,14 +101,14 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.11 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.11 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/bytedance/sonic v1.9.1 // indirect diff --git a/go.sum b/go.sum index 13ae2b76b..9a4d94557 100644 --- a/go.sum +++ b/go.sum @@ -115,34 +115,37 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.16.7/go.mod h1:6CpKuLXg2w7If3ABZCl/qZ6rEgwtjZTn4eAf4RcEyuw= -github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.25 h1:JuYyZcnMPBiFqn87L2cRppo+rNwgah6YwD3VuyvaW6Q= -github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4= -github.com/aws/aws-sdk-go-v2/credentials v1.13.24 h1:PjiYyls3QdCrzqUN35jMWtUK1vqVZ+zLfdOa/UPFDp0= -github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= +github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo= +github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.26 h1:ivCHcSmKd1+9rBlqVsxZHB35eCW88KWbMdG2VL3BuBw= +github.com/aws/aws-sdk-go-v2/config v1.18.26/go.mod h1:NVmd//z/PNl7U+ZU2EnuffxOA060JWzgbH3BnqQrUoY= +github.com/aws/aws-sdk-go-v2/credentials v1.13.25 h1:5wROoMcUC7nAE66e0b3IIht6Tos76M4HC+GQw8MeqxU= +github.com/aws/aws-sdk-go-v2/credentials v1.13.25/go.mod h1:W9I2660WXSwZQ23mM1Ks72+UGeyirIxuU7/KzN7daeA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 h1:LxK/bitrAr4lnh9LnIS6i7zWbCOdMsfzKFBI6LUCS0I= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4/go.mod h1:E1hLXN/BL2e6YizK1zFlYd8vsfi2GTjbjBazinMmeaM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14/go.mod h1:kdjrMwHwrC3+FsKhNcCMJ7tUVj/8uSD5CZXeQ4wV6fM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 h1:A5UqQEmPaCFpedKouS4v+dHCTUo2sKqhoKO9U5kxyWo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8/go.mod h1:ZIV8GYoC6WLBW5KGs+o4rsc65/ozd+eQ0L31XF5VDwk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 h1:srIVS45eQuewqz6fKKu6ZGXaq6FuFg5NzgQBAM6g8Y4= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 h1:bkRyG4a929RCnpVSTvLM2j/T4ls015ZhhYApbmYs15s= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5 h1:DkubF+BSEy0uX59+pYySzWFReN3fCcXobIO8L5Phh24= github.com/aws/aws-sdk-go-v2/service/kms v1.17.5/go.mod h1:ubAtMGRUMVv5kX8lpbeDguxZ64pR4kXTGApY4sCM0io= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.8 h1:eB91eEYUlh8+O2dXr189W8GJJd+/T8N/c5HocH2KzVo= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.8/go.mod h1:3ARttS6G6U3auEdKfaN4GlnfS9UxYE9nqub1+0YGycA= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= -github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 h1:2DQLAKDteoEDI8zpCzqBMaZlJuoE9iTYD0gFmXVax9E= -github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.11 h1:cNrMc266RsZJ8V1u1OQQONKcf9HmfxQFqgcpY7ZJBhY= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.11/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.11 h1:h2VhtCE5PBiJefmlVCjJRSzBfFcQeAE10SXIGkXw1jQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.11/go.mod h1:E4VrHCPzmVB/KFXtqBGKb3c8zpbNBgKe3fisDNLAW5w= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.1 h1:ehPTnLR/es8TL1fpBfq8qw9cAwOpQr47fLmZD9yhHjk= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.1/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg= github.com/aws/smithy-go v1.12.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From b96e275f620b9ebc59985f876065e038b9629656 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 16:27:02 +0200 Subject: [PATCH 1182/2268] chore(deps): Bump golang.org/x/term from 0.8.0 to 0.9.0 (#565) Bumps [golang.org/x/term](https://github.com/golang/term) from 0.8.0 to 0.9.0. - [Commits](https://github.com/golang/term/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 26ad793f4..e0219a7be 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( golang.org/x/net v0.10.0 golang.org/x/sync v0.2.0 golang.org/x/sys v0.9.0 - golang.org/x/term v0.8.0 + golang.org/x/term v0.9.0 golang.org/x/text v0.10.0 helm.sh/helm/v3 v3.12.0 k8s.io/api v0.27.2 diff --git a/go.sum b/go.sum index 9a4d94557..e55b9ac74 100644 --- a/go.sum +++ b/go.sum @@ -1148,8 +1148,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -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/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= 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= From 755680b0d82022a3464ce407e54478167bc926f2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Jun 2023 17:16:47 +0200 Subject: [PATCH 1183/2268] fix: Fix DEPLOYED printer column (#570) --- api/v1beta1/kluctldeployment_types.go | 3 +-- config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml | 5 +---- install/controller/controller/crd.yaml | 5 +---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/api/v1beta1/kluctldeployment_types.go b/api/v1beta1/kluctldeployment_types.go index 3150f4130..4d961bb07 100644 --- a/api/v1beta1/kluctldeployment_types.go +++ b/api/v1beta1/kluctldeployment_types.go @@ -395,8 +395,7 @@ func (s *KluctlDeploymentStatus) GetLastValidateResult() (*result.ValidateResult //+kubebuilder:object:root=true //+kubebuilder:subresource:status //+kubebuilder:printcolumn:name="DryRun",type="boolean",JSONPath=".spec.dryRun",description="" -//+kubebuilder:printcolumn:name="Deployed",type="date",JSONPath=".status.lastDeployResult.command.endTime",description="" -//+kubebuilder:printcolumn:name="Pruned",type="date",JSONPath=".status.lastPruneResult.command.endTime",description="" +//+kubebuilder:printcolumn:name="Deployed",type="date",JSONPath=".status.lastDeployResult.commandInfo.endTime",description="" //+kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" //+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="" //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" diff --git a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml index 535e56610..7b18591d0 100644 --- a/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml +++ b/config/crd/bases/gitops.kluctl.io_kluctldeployments.yaml @@ -18,12 +18,9 @@ spec: - jsonPath: .spec.dryRun name: DryRun type: boolean - - jsonPath: .status.lastDeployResult.command.endTime + - jsonPath: .status.lastDeployResult.commandInfo.endTime name: Deployed type: date - - jsonPath: .status.lastPruneResult.command.endTime - name: Pruned - type: date - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string diff --git a/install/controller/controller/crd.yaml b/install/controller/controller/crd.yaml index b8c794f09..dd1e01c15 100644 --- a/install/controller/controller/crd.yaml +++ b/install/controller/controller/crd.yaml @@ -17,12 +17,9 @@ spec: - jsonPath: .spec.dryRun name: DryRun type: boolean - - jsonPath: .status.lastDeployResult.command.endTime + - jsonPath: .status.lastDeployResult.commandInfo.endTime name: Deployed type: date - - jsonPath: .status.lastPruneResult.command.endTime - name: Pruned - type: date - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string From 9b004cf674cddf5489f18da5e03295114dcbbde3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 00:18:36 +0200 Subject: [PATCH 1184/2268] fix: Set controller-runtime logger --- cmd/kluctl/commands/root.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/kluctl/commands/root.go b/cmd/kluctl/commands/root.go index 67dbc8cfe..54c1750c9 100644 --- a/cmd/kluctl/commands/root.go +++ b/cmd/kluctl/commands/root.go @@ -25,6 +25,7 @@ import ( "os" "path/filepath" "runtime/pprof" + ctrl "sigs.k8s.io/controller-runtime" "strings" "time" @@ -110,7 +111,7 @@ func redirectLogsAndStderr(ctxGetter func() context.Context) { klog.LogToStderr(false) klog.SetOutput(lr1) log.SetOutput(lr2) - //ctrl.SetLogger(klog.NewKlogr()) + ctrl.SetLogger(klog.NewKlogr()) pr, pw, err := os.Pipe() if err != nil { From 72567a230176d87a97a31b78e37749aef787d3b6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 00:19:15 +0200 Subject: [PATCH 1185/2268] feat: Implement impersonation support in the webui --- pkg/controllers/kluctl_project.go | 2 +- pkg/k8s/client_factory.go | 23 +++++--- pkg/k8s/k8s_cluster.go | 20 ------- pkg/kluctl_project/target_context.go | 2 +- pkg/webui/auth.go | 3 ++ pkg/webui/clusteraccessor.go | 81 ++++++++++++++++++++-------- pkg/webui/server.go | 17 ++++-- pkg/webui/validator.go | 14 +++-- 8 files changed, 106 insertions(+), 56 deletions(-) diff --git a/pkg/controllers/kluctl_project.go b/pkg/controllers/kluctl_project.go index 4b426668b..45a214962 100644 --- a/pkg/controllers/kluctl_project.go +++ b/pkg/controllers/kluctl_project.go @@ -760,7 +760,7 @@ func (pt *preparedTarget) kluctlDelete(ctx context.Context, discriminator string if err != nil { return nil, err } - clientFactory, err := k8s2.NewClientFactory(ctx, restConfig) + clientFactory, err := k8s2.NewClientFactoryFromConfig(ctx, restConfig) if err != nil { return nil, err } diff --git a/pkg/k8s/client_factory.go b/pkg/k8s/client_factory.go index 255f35fb7..c530186d9 100644 --- a/pkg/k8s/client_factory.go +++ b/pkg/k8s/client_factory.go @@ -16,6 +16,7 @@ import ( "net/url" "path/filepath" "sigs.k8s.io/controller-runtime/pkg/client" + "strings" "time" ) @@ -38,7 +39,7 @@ type realClientFactory struct { httpClient *http.Client discoveryClient discovery.DiscoveryInterface - mapper meta.ResettableRESTMapper + mapper meta.RESTMapper } func (r *realClientFactory) RESTConfig() *rest.Config { @@ -91,12 +92,12 @@ func (r *realClientFactory) CloseIdleConnections() { r.httpClient.CloseIdleConnections() } -func initDiscoveryClient(ctx context.Context, config *rest.Config) (discovery.CachedDiscoveryInterface, error) { +func CreateDiscoveryClient(ctx context.Context, config *rest.Config) (discovery.CachedDiscoveryInterface, error) { apiHost, err := url.Parse(config.Host) if err != nil { return nil, err } - discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(ctx), "kube-cache/discovery", apiHost.Hostname()) + discoveryCacheDir := filepath.Join(utils.GetTmpBaseDir(ctx), "kube-cache/discovery", strings.ReplaceAll(apiHost.Host, ":", "-")) discovery2, err := disk.NewCachedDiscoveryClientForConfig(dynamic.ConfigFor(config), discoveryCacheDir, "", time.Hour*24) if err != nil { return nil, err @@ -104,7 +105,7 @@ func initDiscoveryClient(ctx context.Context, config *rest.Config) (discovery.Ca return discovery2, nil } -func NewClientFactory(ctx context.Context, configIn *rest.Config) (ClientFactory, error) { +func NewClientFactoryFromConfig(ctx context.Context, configIn *rest.Config) (ClientFactory, error) { restConfig := rest.CopyConfig(configIn) restConfig.QPS = 10 restConfig.Burst = 20 @@ -114,7 +115,7 @@ func NewClientFactory(ctx context.Context, configIn *rest.Config) (ClientFactory return nil, err } - dc, err := initDiscoveryClient(ctx, restConfig) + dc, err := CreateDiscoveryClient(ctx, restConfig) if err != nil { return nil, err } @@ -143,5 +144,15 @@ func NewClientFactoryFromDefaultConfig(ctx context.Context, context *string) (Cl return nil, err } - return NewClientFactory(ctx, config) + return NewClientFactoryFromConfig(ctx, config) +} + +func NewClientFactory(ctx context.Context, config *rest.Config, httpClient *http.Client, dc discovery.DiscoveryInterface, mapper meta.RESTMapper) (ClientFactory, error) { + return &realClientFactory{ + ctx: ctx, + config: config, + httpClient: httpClient, + discoveryClient: dc, + mapper: mapper, + }, nil } diff --git a/pkg/k8s/k8s_cluster.go b/pkg/k8s/k8s_cluster.go index 082224646..8e1ff12da 100644 --- a/pkg/k8s/k8s_cluster.go +++ b/pkg/k8s/k8s_cluster.go @@ -78,26 +78,6 @@ func (k *K8sCluster) ReadWrite() *K8sCluster { return &k2 } -func (k *K8sCluster) GetCA() []byte { - return k.clientFactory.GetCA() -} - -func (k *K8sCluster) buildLabelSelector(labels map[string]string) string { - ret := "" - - for k, v := range labels { - if len(ret) != 0 { - ret += "," - } - if v == "" { - ret += k - } else { - ret += fmt.Sprintf("%s=%s", k, v) - } - } - return ret -} - func (k *K8sCluster) doList(l client.ObjectList, namespace string, labels map[string]string) ([]*uo.UnstructuredObject, []ApiWarning, error) { apiWarnings, err := k.clients.withCClientFromPool(true, func(c client.Client) error { return c.List(k.ctx, l, client.InNamespace(namespace), client.MatchingLabels(labels)) diff --git a/pkg/kluctl_project/target_context.go b/pkg/kluctl_project/target_context.go index 4761c9a0d..57c0b0a27 100644 --- a/pkg/kluctl_project/target_context.go +++ b/pkg/kluctl_project/target_context.go @@ -98,7 +98,7 @@ func (p *LoadedKluctlProject) NewTargetContext(ctx context.Context, params Targe var k *k8s.K8sCluster if clientConfig != nil { s := status.Start(ctx, fmt.Sprintf("Initializing k8s client")) - clientFactory, err := k8s.NewClientFactory(ctx, clientConfig) + clientFactory, err := k8s.NewClientFactoryFromConfig(ctx, clientConfig) if err != nil { return nil, err } diff --git a/pkg/webui/auth.go b/pkg/webui/auth.go index fb9cc97e1..8428482cb 100644 --- a/pkg/webui/auth.go +++ b/pkg/webui/auth.go @@ -29,11 +29,13 @@ type authHandler struct { serverClient client.Client webuiSecretName string + adminRbacUser string } type User struct { Username string `json:"username"` IsAdmin bool `json:"isAdmin"` + RbacUser string `json:"RbacUser"` } func (s *authHandler) setupAuth(r gin.IRouter) error { @@ -223,6 +225,7 @@ func (s *authHandler) getAdminUserFromClaims(claims jwt.MapClaims) *User { return &User{ Username: id, IsAdmin: true, + RbacUser: s.adminRbacUser, } } diff --git a/pkg/webui/clusteraccessor.go b/pkg/webui/clusteraccessor.go index 1e44dcdbb..e83a75dec 100644 --- a/pkg/webui/clusteraccessor.go +++ b/pkg/webui/clusteraccessor.go @@ -5,10 +5,14 @@ import ( kluctlv1 "github.com/kluctl/kluctl/v2/api/v1beta1" k8s2 "github.com/kluctl/kluctl/v2/pkg/k8s" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/discovery" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" + "net/http" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sync" "time" ) @@ -19,12 +23,14 @@ type clusterAccessorManager struct { } type clusterAccessor struct { - ctx context.Context - config *rest.Config - client client.Client - k *k8s2.K8sCluster - clusterId string - mutex sync.Mutex + ctx context.Context + config *rest.Config + httpClient *http.Client + scheme *runtime.Scheme + discovery discovery.DiscoveryInterface + mapper meta.RESTMapper + clusterId string + mutex sync.Mutex } func (cam *clusterAccessorManager) add(config *rest.Config) { @@ -64,6 +70,9 @@ func (ca *clusterAccessor) initClient() { } func (ca *clusterAccessor) tryInitClient() error { + ca.mutex.Lock() + defer ca.mutex.Unlock() + scheme := runtime.NewScheme() err := clientgoscheme.AddToScheme(scheme) if err != nil { @@ -73,33 +82,37 @@ func (ca *clusterAccessor) tryInitClient() error { if err != nil { return err } + ca.scheme = scheme - c, err := client.New(ca.config, client.Options{ - Scheme: scheme, - }) + httpClient, err := rest.HTTPClientFor(ca.config) if err != nil { return err } - var ns corev1.Namespace - err = c.Get(context.Background(), client.ObjectKey{Name: "kube-system"}, &ns) + ca.httpClient = httpClient + + dc, err := k8s2.CreateDiscoveryClient(context.Background(), ca.config) if err != nil { return err } + ca.discovery = dc - cf, err := k8s2.NewClientFactory(context.Background(), ca.config) + mapper, err := apiutil.NewDynamicRESTMapper(ca.config, ca.httpClient) if err != nil { return err } + ca.mapper = mapper - k, err := k8s2.NewK8sCluster(context.Background(), cf, true) + c, err := ca.getClientLocked("", nil) + if err != nil { + return err + } + + var ns corev1.Namespace + err = c.Get(context.Background(), client.ObjectKey{Name: "kube-system"}, &ns) if err != nil { return err } - ca.mutex.Lock() - defer ca.mutex.Unlock() - ca.client = c - ca.k = k ca.clusterId = string(ns.UID) return nil @@ -111,14 +124,40 @@ func (ca *clusterAccessor) getClusterId() string { return ca.clusterId } -func (ca *clusterAccessor) getClient() client.Client { +func (ca *clusterAccessor) getClient(asUser string, asGroups []string) (client.Client, error) { ca.mutex.Lock() defer ca.mutex.Unlock() - return ca.client + return ca.getClientLocked(asUser, asGroups) } -func (ca *clusterAccessor) getK() *k8s2.K8sCluster { +func (ca *clusterAccessor) getClientLocked(asUser string, asGroups []string) (client.Client, error) { + config := rest.CopyConfig(ca.config) + config.Impersonate.UserName = asUser + config.Impersonate.Groups = asGroups + + c, err := client.New(config, client.Options{ + HTTPClient: ca.httpClient, + Scheme: ca.scheme, + Mapper: ca.mapper, + }) + if err != nil { + return nil, err + } + return c, nil +} + +func (ca *clusterAccessor) getK(ctx context.Context, asUser string, asGroups []string) (*k8s2.K8sCluster, error) { ca.mutex.Lock() defer ca.mutex.Unlock() - return ca.k + + config := rest.CopyConfig(ca.config) + config.Impersonate.UserName = asUser + config.Impersonate.Groups = asGroups + + cf, err := k8s2.NewClientFactory(ctx, config, ca.httpClient, ca.discovery, ca.mapper) + if err != nil { + return nil, err + } + + return k8s2.NewK8sCluster(ctx, cf, false) } diff --git a/pkg/webui/server.go b/pkg/webui/server.go index 31b45181c..6f0fa5bde 100644 --- a/pkg/webui/server.go +++ b/pkg/webui/server.go @@ -44,6 +44,8 @@ func NewCommandResultsServer(ctx context.Context, store *results.ResultsCollecto serverClient: serverClient, } + adminUser := "kluctl-webui-admin" + adminEnabled := false if serverClient != nil { adminEnabled = true @@ -55,6 +57,7 @@ func NewCommandResultsServer(ctx context.Context, store *results.ResultsCollecto adminEnabled: adminEnabled, serverClient: serverClient, webuiSecretName: "admin-secret", + adminRbacUser: adminUser, } } @@ -62,7 +65,7 @@ func NewCommandResultsServer(ctx context.Context, store *results.ResultsCollecto ret.cam.add(config) } - ret.vam = newValidatorManager(ctx, store, ret.cam) + ret.vam = newValidatorManager(ctx, store, ret.cam, adminUser) return ret } @@ -340,17 +343,25 @@ func (s *CommandResultsServer) doSetAnnotation(c *gin.Context, aname string, ava return } + user := s.auth.getUser(c) + ca := s.cam.getForClusterId(params.Cluster) if ca == nil { _ = c.AbortWithError(http.StatusNotFound, err) return } + kc, err := ca.getClient(user.RbacUser, nil) + if err != nil { + _ = c.AbortWithError(http.StatusInternalServerError, err) + return + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() var kd kluctlv1.KluctlDeployment - err = ca.getClient().Get(ctx, client.ObjectKey{Name: params.Name, Namespace: params.Namespace}, &kd) + err = kc.Get(ctx, client.ObjectKey{Name: params.Name, Namespace: params.Namespace}, &kd) if err != nil { if errors.IsNotFound(err) { _ = c.AbortWithError(http.StatusNotFound, err) @@ -362,7 +373,7 @@ func (s *CommandResultsServer) doSetAnnotation(c *gin.Context, aname string, ava patch := client.MergeFrom(kd.DeepCopy()) metav1.SetMetaDataAnnotation(&kd.ObjectMeta, aname, avalue) - err = ca.getClient().Patch(ctx, &kd, patch, client.FieldOwner(webuiManager)) + err = kc.Patch(ctx, &kd, patch, client.FieldOwner(webuiManager)) if err != nil { _ = c.AbortWithError(http.StatusInternalServerError, err) return diff --git a/pkg/webui/validator.go b/pkg/webui/validator.go index 277d9c77b..76e7e7ff1 100644 --- a/pkg/webui/validator.go +++ b/pkg/webui/validator.go @@ -27,8 +27,9 @@ type validationWatch struct { type validatorManager struct { ctx context.Context - store results.ResultStore - cam *clusterAccessorManager + store results.ResultStore + cam *clusterAccessorManager + adminUser string validators map[ProjectTargetKey]*validatorEntry watches []*validationWatch @@ -49,11 +50,12 @@ type validatorEntry struct { mutex sync.Mutex } -func newValidatorManager(ctx context.Context, store results.ResultStore, cam *clusterAccessorManager) *validatorManager { +func newValidatorManager(ctx context.Context, store results.ResultStore, cam *clusterAccessorManager, adminUser string) *validatorManager { return &validatorManager{ ctx: ctx, store: store, cam: cam, + adminUser: adminUser, validators: map[ProjectTargetKey]*validatorEntry{}, } } @@ -244,7 +246,11 @@ func (v *validatorEntry) runOnce(summaries []result.CommandResultSummary) bool { s.FailedWithMessage("No cluster accessor for %v", v.key) return false } - k := ca.getK() + k, err := ca.getK(context.Background(), v.vm.adminUser, nil) + if err != nil { + s.FailedWithMessage("Failed to create K8sCluster: %v", err) + return false + } if k == nil { return false } From 9d5072973730eba0957c119e7a99adb05ce35ee2 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 00:19:41 +0200 Subject: [PATCH 1186/2268] fix: Use default context when using --in-cluster --- cmd/kluctl/commands/cmd_webui.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/kluctl/commands/cmd_webui.go b/cmd/kluctl/commands/cmd_webui.go index 344882505..1c7ee15f4 100644 --- a/cmd/kluctl/commands/cmd_webui.go +++ b/cmd/kluctl/commands/cmd_webui.go @@ -84,6 +84,9 @@ func (cmd *webuiCmd) createResultStores(ctx context.Context) ([]results.ResultSt for name, _ := range kcfg.Contexts { contexts = append(contexts, name) } + } else if cmd.InCluster { + // placeholder for current context + contexts = append(contexts, "") } else { if len(cmd.Context) == 0 { // placeholder for current context From ae78653ca96fb587169b15aeec1f6c8863ed2fd5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 00:22:19 +0200 Subject: [PATCH 1187/2268] feat: Add webui deployment --- hack/prepare-release.sh | 6 ++- install/webui/.kluctl.yaml | 9 ++++ install/webui/deployment.yaml | 3 ++ install/webui/webui/admin-rbac.yaml | 41 ++++++++++++++++++ install/webui/webui/deployment.yaml | 65 +++++++++++++++++++++++++++++ install/webui/webui/webui-rbac.yaml | 48 +++++++++++++++++++++ 6 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 install/webui/.kluctl.yaml create mode 100644 install/webui/deployment.yaml create mode 100644 install/webui/webui/admin-rbac.yaml create mode 100644 install/webui/webui/deployment.yaml create mode 100644 install/webui/webui/webui-rbac.yaml diff --git a/hack/prepare-release.sh b/hack/prepare-release.sh index f63be7543..b306fb781 100755 --- a/hack/prepare-release.sh +++ b/hack/prepare-release.sh @@ -24,7 +24,11 @@ fi echo VERSION=$VERSION -FILES="install/controller/.kluctl.yaml install/controller/controller/kustomization.yaml docs/installation.md" +FILES="" +FILES="$FILES install/controller/.kluctl.yaml" +FILES="$FILES install/controller/controller/kustomization.yaml" +FILES="$FILES install/webui/.kluctl.yaml" +FILES="$FILES docs/installation.md" for f in $FILES; do cat $f | sed "s/$VERSION_REGEX_SED/$VERSION/g" > $f.tmp diff --git a/install/webui/.kluctl.yaml b/install/webui/.kluctl.yaml new file mode 100644 index 000000000..961fb60c4 --- /dev/null +++ b/install/webui/.kluctl.yaml @@ -0,0 +1,9 @@ +discriminator: kluctl.io-webui + +args: + - name: kluctl_version + default: v2.20.4 + - name: webui_args + default: [] + - name: webui_envs + default: [] diff --git a/install/webui/deployment.yaml b/install/webui/deployment.yaml new file mode 100644 index 000000000..cf765cdb9 --- /dev/null +++ b/install/webui/deployment.yaml @@ -0,0 +1,3 @@ + +deployments: + - path: webui diff --git a/install/webui/webui/admin-rbac.yaml b/install/webui/webui/admin-rbac.yaml new file mode 100644 index 000000000..5215ec007 --- /dev/null +++ b/install/webui/webui/admin-rbac.yaml @@ -0,0 +1,41 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kluctl-webui-admin-role +rules: + - apiGroups: + - gitops.kluctl.io + resources: + - kluctldeployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + # Read access for all other Kubernetes objects + - apiGroups: ["*"] + resources: ["*"] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/instance: kluctl-webui-rolebinding + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/part-of: controller + name: kluctl-webui-admin-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kluctl-webui-admin-role +subjects: + - kind: User + apiGroup: rbac.authorization.k8s.io + name: kluctl-webui-admin diff --git a/install/webui/webui/deployment.yaml b/install/webui/webui/deployment.yaml new file mode 100644 index 000000000..109a6eea4 --- /dev/null +++ b/install/webui/webui/deployment.yaml @@ -0,0 +1,65 @@ +{% set kluctl_version = get_var("args.kluctl_version", "v2.20.4") %} +{% set pull_policy = "Always" if "-devel" in kluctl_version or "-snapshot" in kluctl_version else "IfNotPresent" %} + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: kluctl-webui + app.kubernetes.io/instance: kluctl-controller + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: kluctl-webui + control-plane: kluctl-webui + name: kluctl-webui + namespace: kluctl-system +spec: + replicas: 1 + selector: + matchLabels: + control-plane: kluctl-controller + template: + metadata: + labels: + control-plane: kluctl-controller + spec: + containers: + - name: webui + image: ghcr.io/kluctl/kluctl:{{ kluctl_version }} + imagePullPolicy: {{ pull_policy }} + command: + - kluctl + - webui + - --in-cluster + args: {{ get_var(["args.webui_args", "webui_args"], []) | to_json }} + env: {{ get_var(["args.webui_envs", "webui_envs"], []) | to_json }} + ports: + - containerPort: 8080 + name: http + livenessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 2000m + memory: 512Mi + requests: + cpu: 500m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + securityContext: + runAsNonRoot: true + serviceAccountName: kluctl-webui + terminationGracePeriodSeconds: 10 diff --git a/install/webui/webui/webui-rbac.yaml b/install/webui/webui/webui-rbac.yaml new file mode 100644 index 000000000..1e7622111 --- /dev/null +++ b/install/webui/webui/webui-rbac.yaml @@ -0,0 +1,48 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/instance: kluctl-webui-sa + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: serviceaccount + app.kubernetes.io/part-of: controller + name: kluctl-webui + namespace: kluctl-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kluctl-webui-cluster-role +rules: + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["get", "list"] + # allow access to results + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch"] + # allow to impersonate other users, groups and serviceaccounts + - apiGroups: [""] + resources: ["users", "groups", "serviceaccounts"] + verbs: ["impersonate"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: controller + app.kubernetes.io/instance: kluctl-webui-rolebinding + app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/part-of: controller + name: kluctl-webui-cluster-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kluctl-webui-cluster-role +subjects: + - kind: ServiceAccount + name: kluctl-webui + namespace: kluctl-system From 9064f420dd79664a440d7aef8927fd5dd9661875 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 07:51:39 +0200 Subject: [PATCH 1188/2268] fix: Use new http clients to fix impersonation --- pkg/webui/clusteraccessor.go | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/pkg/webui/clusteraccessor.go b/pkg/webui/clusteraccessor.go index e83a75dec..65bd1853d 100644 --- a/pkg/webui/clusteraccessor.go +++ b/pkg/webui/clusteraccessor.go @@ -10,7 +10,6 @@ import ( "k8s.io/client-go/discovery" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - "net/http" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sync" @@ -23,14 +22,13 @@ type clusterAccessorManager struct { } type clusterAccessor struct { - ctx context.Context - config *rest.Config - httpClient *http.Client - scheme *runtime.Scheme - discovery discovery.DiscoveryInterface - mapper meta.RESTMapper - clusterId string - mutex sync.Mutex + ctx context.Context + config *rest.Config + scheme *runtime.Scheme + discovery discovery.DiscoveryInterface + mapper meta.RESTMapper + clusterId string + mutex sync.Mutex } func (cam *clusterAccessorManager) add(config *rest.Config) { @@ -88,7 +86,6 @@ func (ca *clusterAccessor) tryInitClient() error { if err != nil { return err } - ca.httpClient = httpClient dc, err := k8s2.CreateDiscoveryClient(context.Background(), ca.config) if err != nil { @@ -96,7 +93,7 @@ func (ca *clusterAccessor) tryInitClient() error { } ca.discovery = dc - mapper, err := apiutil.NewDynamicRESTMapper(ca.config, ca.httpClient) + mapper, err := apiutil.NewDynamicRESTMapper(ca.config, httpClient) if err != nil { return err } @@ -135,10 +132,9 @@ func (ca *clusterAccessor) getClientLocked(asUser string, asGroups []string) (cl config.Impersonate.UserName = asUser config.Impersonate.Groups = asGroups - c, err := client.New(config, client.Options{ - HTTPClient: ca.httpClient, - Scheme: ca.scheme, - Mapper: ca.mapper, + c, err := client.NewWithWatch(config, client.Options{ + Scheme: ca.scheme, + Mapper: ca.mapper, }) if err != nil { return nil, err @@ -154,7 +150,12 @@ func (ca *clusterAccessor) getK(ctx context.Context, asUser string, asGroups []s config.Impersonate.UserName = asUser config.Impersonate.Groups = asGroups - cf, err := k8s2.NewClientFactory(ctx, config, ca.httpClient, ca.discovery, ca.mapper) + httpClient, err := rest.HTTPClientFor(config) + if err != nil { + return nil, err + } + + cf, err := k8s2.NewClientFactory(ctx, config, httpClient, ca.discovery, ca.mapper) if err != nil { return nil, err } From 79fc4999336c1a1ae7db3a265fa10f67bd158e3e Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Wed, 14 Jun 2023 00:12:55 +0600 Subject: [PATCH 1189/2268] WIP history cards. --- .../src/components/result-view/SidePanel.tsx | 34 ++-- .../ui/src/components/targets-view/Card.tsx | 36 ++-- .../CommandResultDetailsDrawer.tsx | 3 +- .../targets-view/CommandResultItem.tsx | 100 +++++----- .../components/targets-view/HistoryCards.tsx | 172 ++++++++++++++++++ .../src/components/targets-view/Projects.tsx | 17 +- .../src/components/targets-view/Targets.tsx | 18 +- .../components/targets-view/TargetsView.tsx | 20 +- 8 files changed, 288 insertions(+), 112 deletions(-) create mode 100644 pkg/webui/ui/src/components/targets-view/HistoryCards.tsx diff --git a/pkg/webui/ui/src/components/result-view/SidePanel.tsx b/pkg/webui/ui/src/components/result-view/SidePanel.tsx index c2d6a420a..a2ea88709 100644 --- a/pkg/webui/ui/src/components/result-view/SidePanel.tsx +++ b/pkg/webui/ui/src/components/result-view/SidePanel.tsx @@ -1,5 +1,5 @@ import { Box, Divider, IconButton, Paper, Tab, ThemeProvider, Typography, useTheme } from "@mui/material"; -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { TabContext, TabList, TabPanel } from "@mui/lab"; import { CloseIcon } from "../../icons/Icons"; import { light } from "../theme"; @@ -19,37 +19,41 @@ export interface SidePanelProps { onClose?: () => void; } -export const SidePanel = (props: SidePanelProps) => { +export function useSidePanelTabs(provider?: SidePanelProvider) { const [selectedTab, setSelectedTab] = useState(); - const theme = useTheme(); - function handleTabChange(_e: React.SyntheticEvent, value: string) { + const handleTabChange = useCallback((_e: React.SyntheticEvent, value: string) => { setSelectedTab(value); - } + }, []); - let tabs = props.provider?.buildSidePanelTabs() - if (!tabs) { - tabs = [] - } + const tabs = useMemo( + () => provider?.buildSidePanelTabs() || [], + [provider] + ); useEffect(() => { if (!tabs?.length) { - setSelectedTab("") + setSelectedTab(""); return } if (!selectedTab) { - setSelectedTab(tabs[0].label) + setSelectedTab(tabs[0].label); return } if (!tabs.find(x => x.label === selectedTab)) { // reset it after the type of selected item has changed - setSelectedTab(tabs[0].label) + setSelectedTab(tabs[0].label); } - // ignore that it wants us to add selectedTab to the deps (it would cause and endless loop) - // eslint-disable-next-line - }, [props.provider]) + }, [selectedTab, tabs]); + + return { tabs, selectedTab, handleTabChange } +} + +export const SidePanel = (props: SidePanelProps) => { + const theme = useTheme(); + const { tabs, selectedTab, handleTabChange } = useSidePanelTabs(props.provider) if (!selectedTab || !tabs.find(x => x.label === selectedTab)) { return <> diff --git a/pkg/webui/ui/src/components/targets-view/Card.tsx b/pkg/webui/ui/src/components/targets-view/Card.tsx index 5c550fc9e..6e2cf2226 100644 --- a/pkg/webui/ui/src/components/targets-view/Card.tsx +++ b/pkg/webui/ui/src/components/targets-view/Card.tsx @@ -1,24 +1,34 @@ -import { Box, BoxProps } from "@mui/material" +import { Box, BoxProps, Paper, PaperProps } from "@mui/material" export const cardWidth = 247; export const projectCardHeight = 80; export const cardHeight = 126; export const cardGap = 20; -export function Card({ children, ...rest }: BoxProps) { - return - {children} - +export function CardPaper(props: PaperProps) { + const { sx, ...rest } = props; + return } -export function CardCol({ children, ...rest }: BoxProps) { - return - {children} - +export function Card(props: BoxProps) { + return } -export function CardRow({ children, ...rest }: BoxProps) { - return - {children} - +export function CardCol(props: BoxProps) { + return +} + +export function CardRow(props: BoxProps) { + return } diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx index 705bb623f..a6b6bb6a2 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultDetailsDrawer.tsx @@ -121,11 +121,10 @@ export const CommandResultDetailsDrawer = React.memo((props: { ts={ts} rs={rs} onSelectCommandResult={setSelectedCommandResult} - selected={rs === selectedCommandResult} /> }) - }, [ps, ts, cardsCoords, zIndex, theme.transitions, selectedCommandResult]) + }, [ps, ts, cardsCoords, zIndex, theme.transitions]) if (loadingError) { return <>Error diff --git a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx index 21c6c12c6..f2f7bf2eb 100644 --- a/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx +++ b/pkg/webui/ui/src/components/targets-view/CommandResultItem.tsx @@ -2,23 +2,16 @@ import React, { useEffect, useMemo, useState } from "react"; import { CommandResultSummary } from "../../models"; import * as yaml from "js-yaml"; import { CodeViewer } from "../CodeViewer"; -import Paper from "@mui/material/Paper"; import { Box, IconButton, Tooltip, Typography } from "@mui/material"; import { CommandResultStatusLine } from "../result-view/CommandResultStatusLine"; import { useNavigate } from "react-router"; import { DeployIcon, DiffIcon, PruneIcon, TreeViewIcon } from "../../icons/Icons"; import { ProjectSummary, TargetSummary } from "../../project-summaries"; import { calcAgo } from "../../utils/duration"; +import { CardPaper } from "./Card"; -export const CommandResultItem = React.memo((props: { - ps: ProjectSummary, - ts: TargetSummary, - rs: CommandResultSummary, - onSelectCommandResult: (rs: CommandResultSummary) => void, - selected?: boolean; -}) => { - const { rs, onSelectCommandResult, selected } = props; - const navigate = useNavigate() +export const CommandResultItemHeader = React.memo((props: { rs: CommandResultSummary }) => { + const { rs } = props; const [ago, setAgo] = useState(calcAgo(rs.commandInfo.startTime)) let Icon: () => JSX.Element = DiffIcon @@ -50,50 +43,53 @@ export const CommandResultItem = React.memo((props: { return () => clearInterval(interval); }, [rs.commandInfo.startTime]); - return + + + + + + + + {rs.commandInfo?.command} + + + {ago} + + + +}); + +export const CommandResultItem = React.memo((props: { + ps: ProjectSummary, + ts: TargetSummary, + rs: CommandResultSummary, + onSelectCommandResult: (rs: CommandResultSummary) => void, +}) => { + const { rs, onSelectCommandResult } = props; + const navigate = useNavigate() + + return onSelectCommandResult(rs)} > - - - - - - - - - {rs.commandInfo?.command} - - - {ago} - - - + @@ -117,5 +113,5 @@ export const CommandResultItem = React.memo((props: { - + }); diff --git a/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx b/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx new file mode 100644 index 000000000..63baafce3 --- /dev/null +++ b/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx @@ -0,0 +1,172 @@ +import React, { Suspense, useEffect, useRef, useState } from "react"; +import { Box, Tab, useTheme } from "@mui/material"; +import { CommandResultSummary } from "../../models"; +import { ProjectSummary, TargetSummary } from "../../project-summaries"; +import { CardPaper, cardHeight, cardWidth } from "./Card"; +import { CommandResultItemHeader } from "./CommandResultItem"; +import { Loading } from "../Loading"; +import { NodeData } from "../result-view/nodes/NodeData"; +import { api, usePromise } from "../../api"; +import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; +import { SidePanelProvider, useSidePanelTabs } from "../result-view/SidePanel"; +import { TabContext, TabList, TabPanel } from "@mui/lab"; + +async function doGetRootNode(rs: CommandResultSummary) { + const shortNames = api.getShortNames() + const r = api.getResult(rs.id) + const builder = new NodeBuilder({ + shortNames: await shortNames, + summary: rs, + commandResult: await r, + }) + const [node] = builder.buildRoot() + return node +} + +export interface HistoryCardsProps { + rs: CommandResultSummary, + ts: TargetSummary, + ps: ProjectSummary, + initialCardRect: DOMRect +} + +const paddingY = 25; +const paddingX = 120; + +interface Rect { + left: number, + top: number, + width: number | string, + height: number | string +} + +type TransitionStatus = 'not-started' | 'running' | 'finished' + +function CardContent(props: { provider: SidePanelProvider }) { + const { tabs, selectedTab, handleTabChange } = useSidePanelTabs(props.provider) + + if (!props.provider + || !selectedTab + || !tabs.find(x => x.label === selectedTab) + ) { + return null; + } + + return + + {tabs.map((tab, i) => { + return + })} + + + {tabs.map(tab => { + return + {tab.content} + + })} + + +} + +export const HistoryCards = React.memo((props: HistoryCardsProps) => { + const theme = useTheme(); + const containerElem = useRef(); + const [cardRect, setCardRect] = useState(); + const [transitionStatus, setTransitionStatus] = useState('not-started'); + const [promise, setPromise] = useState>(new Promise(() => undefined)); + + const Content = () => { + const node = usePromise(promise) + return ; + } + + useEffect(() => { + if (props.rs === undefined) { + return + } + setPromise(doGetRootNode(props.rs)); + }, [props.rs]); + + useEffect(() => { + const rect = containerElem.current?.getBoundingClientRect(); + if (!rect) { + setCardRect(undefined); + return; + } + + const initialRect = { + left: props.initialCardRect.left - rect.left - paddingX, + top: props.initialCardRect.top - rect.top - paddingY, + width: cardWidth, + height: cardHeight + }; + + setCardRect(initialRect); + }, [props.initialCardRect]); + + useEffect(() => { + if (!cardRect) { + return; + } + + const targetRect = { + left: 0, + top: 0, + width: '100%', + height: '100%' + }; + + if (cardRect.left === targetRect.left + && cardRect.top === targetRect.top + && cardRect.width === targetRect.width + && cardRect.height === targetRect.height + ) { + return; + } + + setTimeout(() => { + setCardRect(targetRect); + setTransitionStatus('running'); + setTimeout(() => { + setTransitionStatus('finished'); + }, theme.transitions.duration.enteringScreen) + }, 10); + }, [cardRect, theme.transitions.duration.enteringScreen]); + + return + {cardRect && + + + + {transitionStatus === 'finished' && + }> + + + } + + + } + ; +}); diff --git a/pkg/webui/ui/src/components/targets-view/Projects.tsx b/pkg/webui/ui/src/components/targets-view/Projects.tsx index 1ead486b3..cbf643318 100644 --- a/pkg/webui/ui/src/components/targets-view/Projects.tsx +++ b/pkg/webui/ui/src/components/targets-view/Projects.tsx @@ -1,10 +1,10 @@ import { getLastPathElement } from "../../utils/misc"; -import Paper from "@mui/material/Paper"; import { Box, Typography } from "@mui/material"; import React from "react"; import Tooltip from "@mui/material/Tooltip"; import { ProjectIcon } from "../../icons/Icons"; import { ProjectSummary } from "../../project-summaries"; +import { CardPaper } from "./Card"; export const ProjectItem = (props: { ps: ProjectSummary }) => { const name = getLastPathElement(props.ps.project.gitRepoKey) @@ -17,16 +17,9 @@ export const ProjectItem = (props: { ps: ProjectSummary }) => { : <>}
    - return + return {name && ( @@ -59,5 +52,5 @@ export const ProjectItem = (props: { ps: ProjectSummary }) => {
    - + } diff --git a/pkg/webui/ui/src/components/targets-view/Targets.tsx b/pkg/webui/ui/src/components/targets-view/Targets.tsx index b12ffa5e1..5127a4b09 100644 --- a/pkg/webui/ui/src/components/targets-view/Targets.tsx +++ b/pkg/webui/ui/src/components/targets-view/Targets.tsx @@ -1,6 +1,5 @@ import { KluctlDeploymentInfo } from "../../models"; import { ActionMenuItem, ActionsMenu } from "../ActionsMenu"; -import Paper from "@mui/material/Paper"; import { Box, Typography, useTheme } from "@mui/material"; import React, { useContext } from "react"; import Tooltip from "@mui/material/Tooltip"; @@ -9,6 +8,7 @@ import { CpuIcon, FingerScanIcon, MessageQuestionIcon, TargetIcon } from "../../ import { ProjectSummary, TargetSummary } from "../../project-summaries"; import { calcAgo } from "../../utils/duration"; import { ApiContext } from "../App"; +import { CardPaper } from "./Card"; const StatusIcon = (props: { ps: ProjectSummary, ts: TargetSummary }) => { let icon: React.ReactElement @@ -116,7 +116,7 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel const contextTooltip = All known contexts: - {allContexts.map(context=> ( + {allContexts.map(context => ( {context} ))} @@ -126,16 +126,8 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel targetName = "" } - return props.onSelectTarget(props.ts)} > @@ -184,5 +176,5 @@ export const TargetItem = (props: { ps: ProjectSummary, ts: TargetSummary, onSel - + } diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 228519b0d..e4dfe1b66 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -11,6 +11,7 @@ import { TargetDetailsDrawer } from "./TargetDetailsDrawer"; import { Card, CardCol, cardGap, cardHeight, CardRow, cardWidth, projectCardHeight } from "./Card"; import { ProjectSummary, TargetSummary } from "../../project-summaries"; import { buildListKey } from "../../utils/listKey"; +import { HistoryCards } from "./HistoryCards"; const colWidth = 416; const curveRadius = 12; @@ -140,7 +141,7 @@ export const TargetsView = () => { setSelectedCardRect(undefined); }, []); - const doSetSelectedCommandResult = useCallback((o?: {rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary}) => { + const onSelectCommandResult = useCallback((o?: {rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary}) => { onTargetDetailsDrawerClose(); setSelectedCommandResult(o); }, [onTargetDetailsDrawerClose]); @@ -150,14 +151,23 @@ export const TargetsView = () => { setSelectedTargetSummary(ts); }, [onCommandResultDetailsDrawerClose]); + if (selectedCommandResult && selectedCardRect) { + return ; + } + return - + /> */} { { ps={ps} ts={ts} rs={rs} - onSelectCommandResult={(rs) => doSetSelectedCommandResult({rs, ts, ps})} + onSelectCommandResult={(rs) => onSelectCommandResult({rs, ts, ps})} /> })} From cdd437fcf11e536514dae46361b5d57a70cc15a6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 14 Jun 2023 15:57:42 +0200 Subject: [PATCH 1190/2268] fix: Migrate to new way of loading --- .../components/targets-view/HistoryCards.tsx | 42 ++++++++----------- .../components/targets-view/TargetsView.tsx | 1 - 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx b/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx index 63baafce3..7b74b1be9 100644 --- a/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx +++ b/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx @@ -1,23 +1,23 @@ -import React, { Suspense, useEffect, useRef, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { Box, Tab, useTheme } from "@mui/material"; import { CommandResultSummary } from "../../models"; import { ProjectSummary, TargetSummary } from "../../project-summaries"; import { CardPaper, cardHeight, cardWidth } from "./Card"; import { CommandResultItemHeader } from "./CommandResultItem"; -import { Loading } from "../Loading"; -import { NodeData } from "../result-view/nodes/NodeData"; -import { api, usePromise } from "../../api"; +import { Loading, useLoadingHelper } from "../Loading"; import { NodeBuilder } from "../result-view/nodes/NodeBuilder"; import { SidePanelProvider, useSidePanelTabs } from "../result-view/SidePanel"; import { TabContext, TabList, TabPanel } from "@mui/lab"; +import { Api } from "../../api"; +import { ApiContext } from "../App"; -async function doGetRootNode(rs: CommandResultSummary) { - const shortNames = api.getShortNames() - const r = api.getResult(rs.id) +async function doGetRootNode(api: Api, rs: CommandResultSummary) { + const shortNames = await api.getShortNames() + const r = await api.getResult(rs.id) const builder = new NodeBuilder({ - shortNames: await shortNames, + shortNames: shortNames, summary: rs, - commandResult: await r, + commandResult: r, }) const [node] = builder.buildRoot() return node @@ -69,23 +69,15 @@ function CardContent(props: { provider: SidePanelProvider }) { } export const HistoryCards = React.memo((props: HistoryCardsProps) => { + const api = useContext(ApiContext) const theme = useTheme(); const containerElem = useRef(); const [cardRect, setCardRect] = useState(); const [transitionStatus, setTransitionStatus] = useState('not-started'); - const [promise, setPromise] = useState>(new Promise(() => undefined)); - const Content = () => { - const node = usePromise(promise) - return ; - } - - useEffect(() => { - if (props.rs === undefined) { - return - } - setPromise(doGetRootNode(props.rs)); - }, [props.rs]); + const [loading, loadingError, node] = useLoadingHelper(() => { + return doGetRootNode(api, props.rs) + }, [api, props.rs]) useEffect(() => { const rect = containerElem.current?.getBoundingClientRect(); @@ -133,6 +125,10 @@ export const HistoryCards = React.memo((props: HistoryCardsProps) => { }, 10); }, [cardRect, theme.transitions.duration.enteringScreen]); + if (loadingError) { + return <>Error + } + return { {transitionStatus === 'finished' && - }> - - + (loading ? : ) } diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index e4dfe1b66..77683560d 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -6,7 +6,6 @@ import { ProjectItem } from "./Projects"; import { TargetItem } from "./Targets"; import Divider from "@mui/material/Divider"; import { CommandResultItem } from "./CommandResultItem"; -import { CommandResultDetailsDrawer } from "./CommandResultDetailsDrawer"; import { TargetDetailsDrawer } from "./TargetDetailsDrawer"; import { Card, CardCol, cardGap, cardHeight, CardRow, cardWidth, projectCardHeight } from "./Card"; import { ProjectSummary, TargetSummary } from "../../project-summaries"; From 4fb00ad1ab11d5d62f4e9d809e9df9e7652fa50f Mon Sep 17 00:00:00 2001 From: Alexey Vagarenko Date: Thu, 15 Jun 2023 05:22:16 +0600 Subject: [PATCH 1191/2268] Added new history cards view. --- .../components/targets-view/HistoryCards.tsx | 251 ++++++++++++++---- .../components/targets-view/TargetsView.tsx | 38 +-- pkg/webui/ui/src/components/theme.ts | 30 +++ pkg/webui/ui/src/icons/Icons.tsx | 16 +- pkg/webui/ui/src/icons/close-light.svg | 3 + .../ui/src/icons/triangle-left-light.svg | 4 + .../ui/src/icons/triangle-right-light.svg | 4 + 7 files changed, 266 insertions(+), 80 deletions(-) create mode 100644 pkg/webui/ui/src/icons/close-light.svg create mode 100644 pkg/webui/ui/src/icons/triangle-left-light.svg create mode 100644 pkg/webui/ui/src/icons/triangle-right-light.svg diff --git a/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx b/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx index 7b74b1be9..2570361f8 100644 --- a/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx +++ b/pkg/webui/ui/src/components/targets-view/HistoryCards.tsx @@ -1,7 +1,7 @@ -import React, { useContext, useEffect, useRef, useState } from "react"; -import { Box, Tab, useTheme } from "@mui/material"; +import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; +import { Box, Divider, IconButton, SxProps, Tab, Tooltip, useTheme } from "@mui/material"; import { CommandResultSummary } from "../../models"; -import { ProjectSummary, TargetSummary } from "../../project-summaries"; +import { TargetSummary } from "../../project-summaries"; import { CardPaper, cardHeight, cardWidth } from "./Card"; import { CommandResultItemHeader } from "./CommandResultItem"; import { Loading, useLoadingHelper } from "../Loading"; @@ -10,6 +10,8 @@ import { SidePanelProvider, useSidePanelTabs } from "../result-view/SidePanel"; import { TabContext, TabList, TabPanel } from "@mui/lab"; import { Api } from "../../api"; import { ApiContext } from "../App"; +import { useNavigate } from "react-router"; +import { CloseLightIcon, TreeViewIcon, TriangleLeftLightIcon, TriangleRightLightIcon } from "../../icons/Icons"; async function doGetRootNode(api: Api, rs: CommandResultSummary) { const shortNames = await api.getShortNames() @@ -26,13 +28,10 @@ async function doGetRootNode(api: Api, rs: CommandResultSummary) { export interface HistoryCardsProps { rs: CommandResultSummary, ts: TargetSummary, - ps: ProjectSummary, - initialCardRect: DOMRect + initialCardRect: DOMRect, + onClose: () => void } -const paddingY = 25; -const paddingX = 120; - interface Rect { left: number, top: number, @@ -42,7 +41,7 @@ interface Rect { type TransitionStatus = 'not-started' | 'running' | 'finished' -function CardContent(props: { provider: SidePanelProvider }) { +const CardContent = React.memo((props: { provider: SidePanelProvider }) => { const { tabs, selectedTab, handleTabChange } = useSidePanelTabs(props.provider) if (!props.provider @@ -53,31 +52,126 @@ function CardContent(props: { provider: SidePanelProvider }) { } return - - {tabs.map((tab, i) => { - return - })} - + + + {tabs.map((tab, i) => { + return + })} + + + {tabs.map(tab => { - return + return {tab.content} })} -} +}); + +const ArrowButton = React.memo((props: { + direction: 'left' | 'right', + onClick: () => void, + hidden: boolean +}) => { + const Icon = { + left: TriangleLeftLightIcon, + right: TriangleRightLightIcon + }[props.direction]; + + return + {!props.hidden && + + + + } + +}); + +const HistoryCard = React.memo((props: { + rs: CommandResultSummary, + sx?: SxProps + transitionFinished?: boolean, + onClose?: () => void; +}) => { + const navigate = useNavigate(); + const api = useContext(ApiContext); + const [loading, loadingError, node] = useLoadingHelper(() => { + return doGetRootNode(api, props.rs) + }, [api, props.rs]); + + if (loadingError) { + return <>Error + } + + return + + {props.transitionFinished && ( + + + + )} + + + + + + + {props.transitionFinished && ( + loading + ? + : + )} + + + { + e.stopPropagation(); + navigate(`/results/${props.rs.id}`); + }} + sx={{ + padding: 0, + width: 32, + height: 32 + }} + > + + + + + + + +}); export const HistoryCards = React.memo((props: HistoryCardsProps) => { - const api = useContext(ApiContext) const theme = useTheme(); const containerElem = useRef(); const [cardRect, setCardRect] = useState(); const [transitionStatus, setTransitionStatus] = useState('not-started'); - - const [loading, loadingError, node] = useLoadingHelper(() => { - return doGetRootNode(api, props.rs) - }, [api, props.rs]) + const [currentRS, setCurrentRS] = useState(props.rs); useEffect(() => { const rect = containerElem.current?.getBoundingClientRect(); @@ -87,8 +181,8 @@ export const HistoryCards = React.memo((props: HistoryCardsProps) => { } const initialRect = { - left: props.initialCardRect.left - rect.left - paddingX, - top: props.initialCardRect.top - rect.top - paddingY, + left: props.initialCardRect.left - rect.left, + top: props.initialCardRect.top - rect.top, width: cardWidth, height: cardHeight }; @@ -121,46 +215,95 @@ export const HistoryCards = React.memo((props: HistoryCardsProps) => { setTransitionStatus('running'); setTimeout(() => { setTransitionStatus('finished'); - }, theme.transitions.duration.enteringScreen) + }, theme.transitions.duration.enteringScreen); }, 10); }, [cardRect, theme.transitions.duration.enteringScreen]); - if (loadingError) { - return <>Error - } + const currentRSIndex = useMemo( + () => props.ts.commandResults.indexOf(currentRS), + [currentRS, props.ts.commandResults] + ) + + const onLeftArrowClick = useCallback(() => { + if (currentRSIndex > 0) { + setCurrentRS(props.ts.commandResults[currentRSIndex - 1]); + } + }, [currentRSIndex, props.ts.commandResults]); + + const onRightArrowClick = useCallback(() => { + if (currentRSIndex < props.ts.commandResults.length - 1) { + setCurrentRS(props.ts.commandResults[currentRSIndex + 1]); + } + }, [currentRSIndex, props.ts.commandResults]); return - {cardRect && + - - - - {transitionStatus === 'finished' && - (loading ? : ) - } + {transitionStatus !== 'finished' && cardRect && } + {transitionStatus === 'finished' && + + {props.ts.commandResults.map((rs) => + + )} - - } + } + + ; }); diff --git a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx index 77683560d..b46d9a909 100644 --- a/pkg/webui/ui/src/components/targets-view/TargetsView.tsx +++ b/pkg/webui/ui/src/components/targets-view/TargetsView.tsx @@ -8,7 +8,7 @@ import Divider from "@mui/material/Divider"; import { CommandResultItem } from "./CommandResultItem"; import { TargetDetailsDrawer } from "./TargetDetailsDrawer"; import { Card, CardCol, cardGap, cardHeight, CardRow, cardWidth, projectCardHeight } from "./Card"; -import { ProjectSummary, TargetSummary } from "../../project-summaries"; +import { TargetSummary } from "../../project-summaries"; import { buildListKey } from "../../utils/listKey"; import { HistoryCards } from "./HistoryCards"; @@ -124,7 +124,7 @@ const RelationTree = React.memo(({ targetCount }: { targetCount: number }): JSX. export const TargetsView = () => { const theme = useTheme(); - const [selectedCommandResult, setSelectedCommandResult] = useState<{rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary} | undefined>(); + const [selectedCommandResult, setSelectedCommandResult] = useState<{ rs: CommandResultSummary, ts: TargetSummary } | undefined>(); const [selectedTargetSummary, setSelectedTargetSummary] = useState(); const [selectedCardRect, setSelectedCardRect] = useState(); @@ -135,38 +135,26 @@ export const TargetsView = () => { setSelectedTargetSummary(undefined); }, []); - const onCommandResultDetailsDrawerClose = useCallback(() => { - setSelectedCommandResult(undefined); - setSelectedCardRect(undefined); - }, []); - - const onSelectCommandResult = useCallback((o?: {rs: CommandResultSummary, ts: TargetSummary, ps: ProjectSummary}) => { + const onSelectCommandResult = useCallback((o?: { rs: CommandResultSummary, ts: TargetSummary }) => { onTargetDetailsDrawerClose(); setSelectedCommandResult(o); }, [onTargetDetailsDrawerClose]); - - const doSetSelectedTargetSummary = useCallback((ts?: TargetSummary) => { - onCommandResultDetailsDrawerClose(); - setSelectedTargetSummary(ts); - }, [onCommandResultDetailsDrawerClose]); + + const onHistoryCardsClose = useCallback(() => { + setSelectedCommandResult(undefined); + setSelectedCardRect(undefined); + }, []); if (selectedCommandResult && selectedCardRect) { - return ; } return - {/* */} { return doSetSelectedTargetSummary(ts)} /> + onSelectTarget={setSelectedTargetSummary} /> { ps={ps} ts={ts} rs={rs} - onSelectCommandResult={(rs) => onSelectCommandResult({rs, ts, ps})} + onSelectCommandResult={(rs) => onSelectCommandResult({ rs, ts })} /> })} diff --git a/pkg/webui/ui/src/components/theme.ts b/pkg/webui/ui/src/components/theme.ts index 85c0250e6..639a52406 100644 --- a/pkg/webui/ui/src/components/theme.ts +++ b/pkg/webui/ui/src/components/theme.ts @@ -95,6 +95,36 @@ export const light = createTheme(common, { } } }, + MuiTabs: { + styleOverrides: { + root: { + height: '36px', + minHeight: 0, + textTransform: 'none' + }, + indicator: { + backgroundColor: '#59A588' + } + } + }, + MuiTab: { + styleOverrides: { + root: { + height: '36px', + minHeight: 0, + fontWeight: 400, + fontSize: '16px', + lineHeight: '22px', + letterSpacing: '1px', + textTransform: 'none', + padding: '7px 5px', + color: '#8A8E91', + '&.Mui-selected': { + color: paletteLight.text.primary + } + } + } + }, MuiAppBar: { styleOverrides: { root: { diff --git a/pkg/webui/ui/src/icons/Icons.tsx b/pkg/webui/ui/src/icons/Icons.tsx index 7abba98ce..967aa08ca 100644 --- a/pkg/webui/ui/src/icons/Icons.tsx +++ b/pkg/webui/ui/src/icons/Icons.tsx @@ -10,6 +10,7 @@ import { ReactComponent as CpuIconSvg } from './cpu.svg'; import { ReactComponent as FingerScanIconSvg } from './finger-scan.svg'; import { ReactComponent as TreeViewIconSvg } from './tree-view.svg'; import { ReactComponent as CloseIconSvg } from './close.svg'; +import { ReactComponent as CloseLightIconSvg } from './close-light.svg'; import { ReactComponent as DeployIconSvg } from './deploy.svg'; import { ReactComponent as PruneIconSvg } from './prune.svg'; import { ReactComponent as DiffIconSvg } from './diff.svg'; @@ -27,6 +28,8 @@ import { ReactComponent as WarningSignIconSvg } from './warning-sign.svg'; import { ReactComponent as ChangesIconSvg } from './changes.svg'; import { ReactComponent as StarIconSvg } from './star.svg'; import { ReactComponent as TriangleDownIconSvg } from './triangle-down.svg'; +import { ReactComponent as TriangleLeftLightIconSvg } from './triangle-left-light.svg'; +import { ReactComponent as TriangleRightLightIconSvg } from './triangle-right-light.svg'; import { ReactComponent as TriangleRightIconSvg } from './triangle-right.svg'; import { ReactComponent as BracketsCurlyIconSvg } from './brackets-curly.svg'; import { ReactComponent as BracketsSquareIconSvg } from './brackets-square.svg'; @@ -103,6 +106,10 @@ export const CloseIcon = () => { return } +export const CloseLightIcon = () => { + return +} + export const WarningIcon = () => { return } @@ -159,10 +166,17 @@ export const TriangleDownIcon = () => { return } +export const TriangleLeftLightIcon = () => { + return +} + +export const TriangleRightLightIcon = () => { + return +} + export const TriangleRightIcon = () => { return } - export const BracketsCurlyIcon = () => { return } diff --git a/pkg/webui/ui/src/icons/close-light.svg b/pkg/webui/ui/src/icons/close-light.svg new file mode 100644 index 000000000..a00259719 --- /dev/null +++ b/pkg/webui/ui/src/icons/close-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/pkg/webui/ui/src/icons/triangle-left-light.svg b/pkg/webui/ui/src/icons/triangle-left-light.svg new file mode 100644 index 000000000..6731d308d --- /dev/null +++ b/pkg/webui/ui/src/icons/triangle-left-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/ui/src/icons/triangle-right-light.svg b/pkg/webui/ui/src/icons/triangle-right-light.svg new file mode 100644 index 000000000..500b3f1e8 --- /dev/null +++ b/pkg/webui/ui/src/icons/triangle-right-light.svg @@ -0,0 +1,4 @@ + + + + From 5abe6caee130d332b40ae18255cb138cb111f9c3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 08:23:17 +0200 Subject: [PATCH 1192/2268] fix: Make sure it's clear that search is not implemneted yet --- pkg/webui/ui/src/components/LeftDrawer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/webui/ui/src/components/LeftDrawer.tsx b/pkg/webui/ui/src/components/LeftDrawer.tsx index 75e4d18bb..b3dab16ae 100644 --- a/pkg/webui/ui/src/components/LeftDrawer.tsx +++ b/pkg/webui/ui/src/components/LeftDrawer.tsx @@ -170,7 +170,7 @@ export default function LeftDrawer(props: { content: React.ReactNode, context: A lineHeight: '20px', fontSize: '18px' }} - placeholder='Search' + placeholder='Search (not impl. yet)' /> From 863f46b5661c68f3aeea0bf977bada8e3d9b9619 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 08:23:46 +0200 Subject: [PATCH 1193/2268] chore: Run npm run build --- pkg/webui/ui/build/asset-manifest.json | 3 + pkg/webui/ui/build/index.html | 2 +- pkg/webui/ui/build/static/js/main.js | 3612 +++++++++-------- .../ui/build/static/media/close-light.svg | 3 + .../static/media/triangle-left-light.svg | 4 + .../static/media/triangle-right-light.svg | 4 + 6 files changed, 1868 insertions(+), 1760 deletions(-) create mode 100644 pkg/webui/ui/build/static/media/close-light.svg create mode 100644 pkg/webui/ui/build/static/media/triangle-left-light.svg create mode 100644 pkg/webui/ui/build/static/media/triangle-right-light.svg diff --git a/pkg/webui/ui/build/asset-manifest.json b/pkg/webui/ui/build/asset-manifest.json index 351d6bcf7..dfdfa5fab 100644 --- a/pkg/webui/ui/build/asset-manifest.json +++ b/pkg/webui/ui/build/asset-manifest.json @@ -16,6 +16,7 @@ "static/media/checkbox-checked.svg": "static/media/checkbox-checked.svg", "static/media/checkbox-disabled.svg": "static/media/checkbox-disabled.svg", "static/media/checkbox.svg": "static/media/checkbox.svg", + "static/media/close-light.svg": "static/media/close-light.svg", "static/media/close.svg": "static/media/close.svg", "static/media/cpu.svg": "static/media/cpu.svg", "static/media/deploy.svg": "static/media/deploy.svg", @@ -44,6 +45,8 @@ "static/media/trash.svg": "static/media/trash.svg", "static/media/tree-view.svg": "static/media/tree-view.svg", "static/media/triangle-down.svg": "static/media/triangle-down.svg", + "static/media/triangle-left-light.svg": "static/media/triangle-left-light.svg", + "static/media/triangle-right-light.svg": "static/media/triangle-right-light.svg", "static/media/triangle-right.svg": "static/media/triangle-right.svg", "static/media/warning-sign.svg": "static/media/warning-sign.svg", "static/media/warning.svg": "static/media/warning.svg" diff --git a/pkg/webui/ui/build/index.html b/pkg/webui/ui/build/index.html index 08726c8bf..95134f6bf 100644 --- a/pkg/webui/ui/build/index.html +++ b/pkg/webui/ui/build/index.html @@ -1 +1 @@ -React App
    \ No newline at end of file +React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js index 4a9d496ae..361e5411f 100644 --- a/pkg/webui/ui/build/static/js/main.js +++ b/pkg/webui/ui/build/static/js/main.js @@ -48107,6 +48107,35 @@ function SvgClose(_ref, svgRef) { var close_ForwardRef = /*#__PURE__*/react.forwardRef(SvgClose); /* harmony default export */ var icons_close = (__webpack_require__.p + "static/media/close.svg?f=close.76fdca7f91598011401cb97d4b490d52.svg"); +;// CONCATENATED MODULE: ./src/icons/close-light.svg +var close_light_path; +var close_light_excluded = ["title", "titleId"]; +function close_light_extends() { close_light_extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return close_light_extends.apply(this, arguments); } +function close_light_objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = close_light_objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } +function close_light_objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } + +function SvgCloseLight(_ref, svgRef) { + var title = _ref.title, + titleId = _ref.titleId, + props = close_light_objectWithoutProperties(_ref, close_light_excluded); + return /*#__PURE__*/react.createElement("svg", close_light_extends({ + width: 24, + height: 24, + viewBox: "0 0 24 24", + fill: "none", + xmlns: "http://www.w3.org/2000/svg", + ref: svgRef, + "aria-labelledby": titleId + }, props), title ? /*#__PURE__*/react.createElement("title", { + id: titleId + }, title) : null, close_light_path || (close_light_path = /*#__PURE__*/react.createElement("path", { + d: "M6.4 19L5 17.6L10.6 12L5 6.4L6.4 5L12 10.6L17.6 5L19 6.4L13.4 12L19 17.6L17.6 19L12 13.4L6.4 19Z", + fill: "#39403E" + }))); +} +var close_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCloseLight); +/* harmony default export */ var close_light = (__webpack_require__.p + "static/media/close-light.svg?f=close-light.eb8a7d21cff89473a30809cdaacf6446.svg"); + ;// CONCATENATED MODULE: ./src/icons/deploy.svg var deploy_circle, deploy_path, _g; var deploy_excluded = ["title", "titleId"]; @@ -48789,6 +48818,73 @@ function SvgTriangleDown(_ref, svgRef) { var triangle_down_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleDown); /* harmony default export */ var triangle_down = (__webpack_require__.p + "static/media/triangle-down.svg?f=triangle-down.cb859bf44378ddfd76730eb4a9f90a89.svg"); +;// CONCATENATED MODULE: ./src/icons/triangle-left-light.svg +var triangle_left_light_path, triangle_left_light_path2; +var triangle_left_light_excluded = ["title", "titleId"]; +function triangle_left_light_extends() { triangle_left_light_extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return triangle_left_light_extends.apply(this, arguments); } +function triangle_left_light_objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = triangle_left_light_objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } +function triangle_left_light_objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } + +function SvgTriangleLeftLight(_ref, svgRef) { + var title = _ref.title, + titleId = _ref.titleId, + props = triangle_left_light_objectWithoutProperties(_ref, triangle_left_light_excluded); + return /*#__PURE__*/react.createElement("svg", triangle_left_light_extends({ + width: 50, + height: 50, + viewBox: "0 0 50 50", + fill: "none", + xmlns: "http://www.w3.org/2000/svg", + ref: svgRef, + "aria-labelledby": titleId + }, props), title ? /*#__PURE__*/react.createElement("title", { + id: titleId + }, title) : null, triangle_left_light_path || (triangle_left_light_path = /*#__PURE__*/react.createElement("path", { + opacity: 0.4, + d: "M22.4377 32.25L32.9585 24.3542V12.6667C32.9585 10.6667 30.5418 9.66667 29.1252 11.0833L18.3335 21.875C16.6043 23.6042 16.6043 26.4167 18.3335 28.1458L22.4377 32.25Z", + fill: "#DFEBE9" + })), triangle_left_light_path2 || (triangle_left_light_path2 = /*#__PURE__*/react.createElement("path", { + d: "M32.4585 25.3546V37.3333C32.4585 38.8863 30.5801 39.6671 29.4773 38.5826C29.4769 38.5822 29.4765 38.5817 29.4761 38.5813L23.1987 32.304L32.4585 25.3546Z", + fill: "#DFEBE9", + stroke: "#DFEBE9" + }))); +} +var triangle_left_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleLeftLight); +/* harmony default export */ var triangle_left_light = (__webpack_require__.p + "static/media/triangle-left-light.svg?f=triangle-left-light.723aa352c7195ed6c2c94748badb018c.svg"); + +;// CONCATENATED MODULE: ./src/icons/triangle-right-light.svg +var triangle_right_light_path, triangle_right_light_path2; +var triangle_right_light_excluded = ["title", "titleId"]; +function triangle_right_light_extends() { triangle_right_light_extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return triangle_right_light_extends.apply(this, arguments); } +function triangle_right_light_objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = triangle_right_light_objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } +function triangle_right_light_objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } + +function SvgTriangleRightLight(_ref, svgRef) { + var title = _ref.title, + titleId = _ref.titleId, + props = triangle_right_light_objectWithoutProperties(_ref, triangle_right_light_excluded); + return /*#__PURE__*/react.createElement("svg", triangle_right_light_extends({ + width: 50, + height: 50, + viewBox: "0 0 50 50", + fill: "none", + xmlns: "http://www.w3.org/2000/svg", + ref: svgRef, + "aria-labelledby": titleId + }, props), title ? /*#__PURE__*/react.createElement("title", { + id: titleId + }, title) : null, triangle_right_light_path || (triangle_right_light_path = /*#__PURE__*/react.createElement("path", { + opacity: 0.4, + d: "M27.5623 17.75L17.0415 25.6458V37.3333C17.0415 39.3333 19.4582 40.3333 20.8748 38.9167L31.6665 28.125C33.3957 26.3958 33.3957 23.5833 31.6665 21.8542L27.5623 17.75Z", + fill: "#DFEBE9" + })), triangle_right_light_path2 || (triangle_right_light_path2 = /*#__PURE__*/react.createElement("path", { + d: "M17.0415 12.6667V25.6458L27.5623 17.75L20.8748 11.0625C19.4582 9.66667 17.0415 10.6667 17.0415 12.6667Z", + fill: "#DFEBE9" + }))); +} +var triangle_right_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleRightLight); +/* harmony default export */ var triangle_right_light = (__webpack_require__.p + "static/media/triangle-right-light.svg?f=triangle-right-light.22d8903e553c49bf9b0100b61fe7254f.svg"); + ;// CONCATENATED MODULE: ./src/icons/triangle-right.svg var triangle_right_path, triangle_right_path2; var triangle_right_excluded = ["title", "titleId"]; @@ -49032,11 +49128,11 @@ var include_ForwardRef = /*#__PURE__*/react.forwardRef(SvgInclude); /* harmony default export */ var include = (__webpack_require__.p + "static/media/include.svg?f=include.6bf4795a2f5623b297789116effd0bec.svg"); ;// CONCATENATED MODULE: ./src/icons/Icons.tsx -var KluctlText=function KluctlText(){return/*#__PURE__*/(0,jsx_runtime.jsx)(ForwardRef,{width:"115px",height:"33px"});};var GitIcon=function GitIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(git_ForwardRef,{width:"35px",height:"35px"});};var KluctlLogo=function KluctlLogo(){return/*#__PURE__*/(0,jsx_runtime.jsx)(kluctl_logo_ForwardRef,{width:"50px",height:"50px"});};var SearchIcon=function SearchIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(search_ForwardRef,{width:"27px",height:"27px"});};var TargetsIcon=function TargetsIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(targets_ForwardRef,{width:"48px",height:"48px"});};var TargetIcon=function TargetIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(target_ForwardRef,{width:"45px",height:"45px"});};var RelationHLine=function RelationHLine(){return/*#__PURE__*/_jsx(RelationHLineSvg,{width:"169px",height:"12px"});};var ProjectIcon=function ProjectIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(project_ForwardRef,{width:"45px",height:"45px"});};var DeployIcon=function DeployIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(deploy_ForwardRef,{width:"45px",height:"45px"});};var PruneIcon=function PruneIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(prune_ForwardRef,{width:"45px",height:"45px"});};var DiffIcon=function DiffIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(diff_ForwardRef,{width:"45px",height:"45px"});};var CpuIcon=function CpuIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(cpu_ForwardRef,{width:"24px",height:"24px"});};var FingerScanIcon=function FingerScanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(finger_scan_ForwardRef,{width:"24px",height:"24px"});};var MessageQuestionIcon=function MessageQuestionIcon(props){return/*#__PURE__*/(0,jsx_runtime.jsx)("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:/*#__PURE__*/(0,jsx_runtime.jsx)("path",{d:"M17 2.42999H7C4 2.42999 2 4.42999 2 7.42999V13.43C2 16.43 4 18.43 7 18.43V20.56C7 21.36 7.89 21.84 8.55 21.39L13 18.43H17C20 18.43 22 16.43 22 13.43V7.42999C22 4.42999 20 2.42999 17 2.42999ZM12 14.6C11.58 14.6 11.25 14.26 11.25 13.85C11.25 13.44 11.58 13.1 12 13.1C12.42 13.1 12.75 13.44 12.75 13.85C12.75 14.26 12.42 14.6 12 14.6ZM13.26 10.45C12.87 10.71 12.75 10.88 12.75 11.16V11.37C12.75 11.78 12.41 12.12 12 12.12C11.59 12.12 11.25 11.78 11.25 11.37V11.16C11.25 9.99999 12.1 9.42999 12.42 9.20999C12.79 8.95999 12.91 8.78999 12.91 8.52999C12.91 8.02999 12.5 7.61999 12 7.61999C11.5 7.61999 11.09 8.02999 11.09 8.52999C11.09 8.93999 10.75 9.27999 10.34 9.27999C9.93 9.27999 9.59 8.93999 9.59 8.52999C9.59 7.19999 10.67 6.11999 12 6.11999C13.33 6.11999 14.41 7.19999 14.41 8.52999C14.41 9.66999 13.57 10.24 13.26 10.45Z",fill:props.color})});};var TreeViewIcon=function TreeViewIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(tree_view_ForwardRef,{width:"26px",height:"26px"});};var CloseIcon=function CloseIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(close_ForwardRef,{width:"24px",height:"24px"});};var WarningIcon=function WarningIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_ForwardRef,{width:"24px",height:"24px"});};var ErrorIcon=function ErrorIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(error_ForwardRef,{width:"24px",height:"24px"});};var TrashIcon=function TrashIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(trash_ForwardRef,{width:"24px",height:"24px"});};var OrphanIcon=function OrphanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(orphan_ForwardRef,{width:"24px",height:"24px"});};var AddedIcon=function AddedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(added_ForwardRef,{width:"24px",height:"24px"});};var ChangedIcon=function ChangedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changed_ForwardRef,{width:"24px",height:"24px"});};var CheckboxIcon=function CheckboxIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_ForwardRef,{width:"24px",height:"24px"});};var CheckboxCheckedIcon=function CheckboxCheckedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_checked_ForwardRef,{width:"24px",height:"24px"});};var CheckboxDisabledIcon=function CheckboxDisabledIcon(){return/*#__PURE__*/_jsx(CheckboxDisabledIconSvg,{width:"24px",height:"24px"});};var ArrowLeftIcon=function ArrowLeftIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(arrow_left_ForwardRef,{width:"40px",height:"40px"});};var WarningSignIcon=function WarningSignIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_sign_ForwardRef,{width:"21px",height:"21px"});};var ChangesIcon=function ChangesIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changes_ForwardRef,{width:"21px",height:"21px"});};var StarIcon=function StarIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(star_ForwardRef,{width:"21px",height:"21px"});};var TriangleDownIcon=function TriangleDownIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_down_ForwardRef,{width:"50px",height:"50px"});};var TriangleRightIcon=function TriangleRightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_right_ForwardRef,{width:"50px",height:"50px"});};var BracketsCurlyIcon=function BracketsCurlyIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_curly_ForwardRef,{width:"22px",height:"18px"});};var BracketsSquareIcon=function BracketsSquareIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_square_ForwardRef,{width:"22px",height:"18px"});};var FileIcon=function FileIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(file_ForwardRef,{width:"40px",height:"40px"});};var ResultIcon=function ResultIcon(){return/*#__PURE__*/_jsx(ResultIconSvg,{width:"30px",height:"30px"});};var IncludeIcon=function IncludeIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(include_ForwardRef,{width:"30px",height:"30px"});}; +var KluctlText=function KluctlText(){return/*#__PURE__*/(0,jsx_runtime.jsx)(ForwardRef,{width:"115px",height:"33px"});};var GitIcon=function GitIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(git_ForwardRef,{width:"35px",height:"35px"});};var KluctlLogo=function KluctlLogo(){return/*#__PURE__*/(0,jsx_runtime.jsx)(kluctl_logo_ForwardRef,{width:"50px",height:"50px"});};var SearchIcon=function SearchIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(search_ForwardRef,{width:"27px",height:"27px"});};var TargetsIcon=function TargetsIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(targets_ForwardRef,{width:"48px",height:"48px"});};var TargetIcon=function TargetIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(target_ForwardRef,{width:"45px",height:"45px"});};var RelationHLine=function RelationHLine(){return/*#__PURE__*/_jsx(RelationHLineSvg,{width:"169px",height:"12px"});};var ProjectIcon=function ProjectIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(project_ForwardRef,{width:"45px",height:"45px"});};var DeployIcon=function DeployIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(deploy_ForwardRef,{width:"45px",height:"45px"});};var PruneIcon=function PruneIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(prune_ForwardRef,{width:"45px",height:"45px"});};var DiffIcon=function DiffIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(diff_ForwardRef,{width:"45px",height:"45px"});};var CpuIcon=function CpuIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(cpu_ForwardRef,{width:"24px",height:"24px"});};var FingerScanIcon=function FingerScanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(finger_scan_ForwardRef,{width:"24px",height:"24px"});};var MessageQuestionIcon=function MessageQuestionIcon(props){return/*#__PURE__*/(0,jsx_runtime.jsx)("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:/*#__PURE__*/(0,jsx_runtime.jsx)("path",{d:"M17 2.42999H7C4 2.42999 2 4.42999 2 7.42999V13.43C2 16.43 4 18.43 7 18.43V20.56C7 21.36 7.89 21.84 8.55 21.39L13 18.43H17C20 18.43 22 16.43 22 13.43V7.42999C22 4.42999 20 2.42999 17 2.42999ZM12 14.6C11.58 14.6 11.25 14.26 11.25 13.85C11.25 13.44 11.58 13.1 12 13.1C12.42 13.1 12.75 13.44 12.75 13.85C12.75 14.26 12.42 14.6 12 14.6ZM13.26 10.45C12.87 10.71 12.75 10.88 12.75 11.16V11.37C12.75 11.78 12.41 12.12 12 12.12C11.59 12.12 11.25 11.78 11.25 11.37V11.16C11.25 9.99999 12.1 9.42999 12.42 9.20999C12.79 8.95999 12.91 8.78999 12.91 8.52999C12.91 8.02999 12.5 7.61999 12 7.61999C11.5 7.61999 11.09 8.02999 11.09 8.52999C11.09 8.93999 10.75 9.27999 10.34 9.27999C9.93 9.27999 9.59 8.93999 9.59 8.52999C9.59 7.19999 10.67 6.11999 12 6.11999C13.33 6.11999 14.41 7.19999 14.41 8.52999C14.41 9.66999 13.57 10.24 13.26 10.45Z",fill:props.color})});};var TreeViewIcon=function TreeViewIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(tree_view_ForwardRef,{width:"26px",height:"26px"});};var CloseIcon=function CloseIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(close_ForwardRef,{width:"24px",height:"24px"});};var CloseLightIcon=function CloseLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(close_light_ForwardRef,{width:"24px",height:"24px"});};var WarningIcon=function WarningIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_ForwardRef,{width:"24px",height:"24px"});};var ErrorIcon=function ErrorIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(error_ForwardRef,{width:"24px",height:"24px"});};var TrashIcon=function TrashIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(trash_ForwardRef,{width:"24px",height:"24px"});};var OrphanIcon=function OrphanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(orphan_ForwardRef,{width:"24px",height:"24px"});};var AddedIcon=function AddedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(added_ForwardRef,{width:"24px",height:"24px"});};var ChangedIcon=function ChangedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changed_ForwardRef,{width:"24px",height:"24px"});};var CheckboxIcon=function CheckboxIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_ForwardRef,{width:"24px",height:"24px"});};var CheckboxCheckedIcon=function CheckboxCheckedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_checked_ForwardRef,{width:"24px",height:"24px"});};var CheckboxDisabledIcon=function CheckboxDisabledIcon(){return/*#__PURE__*/_jsx(CheckboxDisabledIconSvg,{width:"24px",height:"24px"});};var ArrowLeftIcon=function ArrowLeftIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(arrow_left_ForwardRef,{width:"40px",height:"40px"});};var WarningSignIcon=function WarningSignIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_sign_ForwardRef,{width:"21px",height:"21px"});};var ChangesIcon=function ChangesIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changes_ForwardRef,{width:"21px",height:"21px"});};var StarIcon=function StarIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(star_ForwardRef,{width:"21px",height:"21px"});};var TriangleDownIcon=function TriangleDownIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_down_ForwardRef,{width:"50px",height:"50px"});};var TriangleLeftLightIcon=function TriangleLeftLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_left_light_ForwardRef,{width:"50px",height:"50px"});};var TriangleRightLightIcon=function TriangleRightLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_right_light_ForwardRef,{width:"50px",height:"50px"});};var TriangleRightIcon=function TriangleRightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_right_ForwardRef,{width:"50px",height:"50px"});};var BracketsCurlyIcon=function BracketsCurlyIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_curly_ForwardRef,{width:"22px",height:"18px"});};var BracketsSquareIcon=function BracketsSquareIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_square_ForwardRef,{width:"22px",height:"18px"});};var FileIcon=function FileIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(file_ForwardRef,{width:"40px",height:"40px"});};var ResultIcon=function ResultIcon(){return/*#__PURE__*/_jsx(ResultIconSvg,{width:"30px",height:"30px"});};var IncludeIcon=function IncludeIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(include_ForwardRef,{width:"30px",height:"30px"});}; ;// CONCATENATED MODULE: ./src/components/theme.ts -var paletteDark={primary:{main:'#DFEBE9'},background:{default:'#222222',paper:'#222222'},text:{primary:'#DFEBE9'}};var paletteLight={primary:{main:'#222222'},secondary:{main:'#39403E'},background:{default:'#DFEBE9',paper:'#DFEBE9'},text:{primary:'#222222'}};var theme_common=styles_createTheme({consts:{appBarHeight:106,leftDrawerWidthOpen:224,leftDrawerWidthClosed:96},typography:{fontFamily:'Nunito Variable'},components:{MuiBackdrop:{styleOverrides:{root:{backgroundColor:'rgba(0, 0, 0, 0.65)'},invisible:{backgroundColor:'transparent'}}}}});var theme_light=styles_createTheme(theme_common,{palette:paletteLight,typography:{h1:{color:paletteLight.text.primary,fontWeight:700,fontSize:'32px',lineHeight:'44px',letterSpacing:'1px'},h2:{color:paletteLight.text.primary,fontWeight:700,fontSize:'20px',lineHeight:'27px',letterSpacing:'1px'},h5:{color:paletteLight.secondary.main,fontWeight:700,fontSize:'22px',lineHeight:'30px',letterSpacing:'1px'},h6:{color:paletteLight.secondary.main,fontWeight:800,fontSize:'20px',lineHeight:'27px'},subtitle1:{color:paletteLight.secondary.main},subtitle2:{fontSize:'14px',lineHeight:1.2}},components:{MuiDivider:{styleOverrides:{root:{borderColor:paletteLight.secondary.main}}},MuiAppBar:{styleOverrides:{root:{color:paletteLight.primary.main}}},MuiTableCell:{styleOverrides:{root:{border:'none',borderTop:"0.5px solid ".concat(paletteLight.secondary.main),fontWeight:400,fontSize:'16px',lineHeight:'22px',letterSpacing:'1px',position:'relative',':after':{content:'""',position:'absolute',top:'10px',right:0,bottom:'10px',display:'block',borderRight:"0.5px solid ".concat(paletteLight.secondary.main)},':last-of-type:after':{content:'none'},':last-of-type':{overflowWrap:'anywhere'}},head:{border:'none'}}}}});var theme_dark=styles_createTheme(theme_common,{palette:paletteDark,typography:{allVariants:{color:paletteDark.text.primary},h4:{color:paletteDark.text.primary,fontWeight:700,fontSize:'24px',lineHeight:'33px',letterSpacing:'1px'}},components:{MuiListItem:{styleOverrides:{root:{color:paletteDark.primary.main,background:paletteDark.background.default}}},MuiButtonBase:{styleOverrides:{root:{color:paletteDark.primary.main,background:paletteDark.background.default}}},MuiDrawer:{styleOverrides:{root:{border:'none'},paper:{border:'none'}}},MuiDivider:{styleOverrides:{root:{background:paletteDark.primary.main,opacity:0.2,margin:'0 13px'}}},MuiTabs:{styleOverrides:{root:{height:'36px',minHeight:0,textTransform:'none'},indicator:{backgroundColor:'#59A588'}}},MuiTab:{styleOverrides:{root:{height:'36px',minHeight:0,fontWeight:400,fontSize:'16px',lineHeight:'22px',letterSpacing:'1px',textTransform:'none',padding:'7px 5px',color:'#8A8E91','&.Mui-selected':{color:paletteDark.text.primary}}}}}}); +var paletteDark={primary:{main:'#DFEBE9'},background:{default:'#222222',paper:'#222222'},text:{primary:'#DFEBE9'}};var paletteLight={primary:{main:'#222222'},secondary:{main:'#39403E'},background:{default:'#DFEBE9',paper:'#DFEBE9'},text:{primary:'#222222'}};var theme_common=styles_createTheme({consts:{appBarHeight:106,leftDrawerWidthOpen:224,leftDrawerWidthClosed:96},typography:{fontFamily:'Nunito Variable'},components:{MuiBackdrop:{styleOverrides:{root:{backgroundColor:'rgba(0, 0, 0, 0.65)'},invisible:{backgroundColor:'transparent'}}}}});var theme_light=styles_createTheme(theme_common,{palette:paletteLight,typography:{h1:{color:paletteLight.text.primary,fontWeight:700,fontSize:'32px',lineHeight:'44px',letterSpacing:'1px'},h2:{color:paletteLight.text.primary,fontWeight:700,fontSize:'20px',lineHeight:'27px',letterSpacing:'1px'},h5:{color:paletteLight.secondary.main,fontWeight:700,fontSize:'22px',lineHeight:'30px',letterSpacing:'1px'},h6:{color:paletteLight.secondary.main,fontWeight:800,fontSize:'20px',lineHeight:'27px'},subtitle1:{color:paletteLight.secondary.main},subtitle2:{fontSize:'14px',lineHeight:1.2}},components:{MuiDivider:{styleOverrides:{root:{borderColor:paletteLight.secondary.main}}},MuiTabs:{styleOverrides:{root:{height:'36px',minHeight:0,textTransform:'none'},indicator:{backgroundColor:'#59A588'}}},MuiTab:{styleOverrides:{root:{height:'36px',minHeight:0,fontWeight:400,fontSize:'16px',lineHeight:'22px',letterSpacing:'1px',textTransform:'none',padding:'7px 5px',color:'#8A8E91','&.Mui-selected':{color:paletteLight.text.primary}}}},MuiAppBar:{styleOverrides:{root:{color:paletteLight.primary.main}}},MuiTableCell:{styleOverrides:{root:{border:'none',borderTop:"0.5px solid ".concat(paletteLight.secondary.main),fontWeight:400,fontSize:'16px',lineHeight:'22px',letterSpacing:'1px',position:'relative',':after':{content:'""',position:'absolute',top:'10px',right:0,bottom:'10px',display:'block',borderRight:"0.5px solid ".concat(paletteLight.secondary.main)},':last-of-type:after':{content:'none'},':last-of-type':{overflowWrap:'anywhere'}},head:{border:'none'}}}}});var theme_dark=styles_createTheme(theme_common,{palette:paletteDark,typography:{allVariants:{color:paletteDark.text.primary},h4:{color:paletteDark.text.primary,fontWeight:700,fontSize:'24px',lineHeight:'33px',letterSpacing:'1px'}},components:{MuiListItem:{styleOverrides:{root:{color:paletteDark.primary.main,background:paletteDark.background.default}}},MuiButtonBase:{styleOverrides:{root:{color:paletteDark.primary.main,background:paletteDark.background.default}}},MuiDrawer:{styleOverrides:{root:{border:'none'},paper:{border:'none'}}},MuiDivider:{styleOverrides:{root:{background:paletteDark.primary.main,opacity:0.2,margin:'0 13px'}}},MuiTabs:{styleOverrides:{root:{height:'36px',minHeight:0,textTransform:'none'},indicator:{backgroundColor:'#59A588'}}},MuiTab:{styleOverrides:{root:{height:'36px',minHeight:0,fontWeight:400,fontSize:'16px',lineHeight:'22px',letterSpacing:'1px',textTransform:'none',padding:'7px 5px',color:'#8A8E91','&.Mui-selected':{color:paletteDark.text.primary}}}}}}); ;// CONCATENATED MODULE: ./src/components/LeftDrawer.tsx -var openedMixin=function openedMixin(theme){return{width:theme.consts.leftDrawerWidthOpen,transition:theme.transitions.create('width',{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.enteringScreen}),overflowX:'hidden'};};var closedMixin=function closedMixin(theme){return{transition:theme.transitions.create('width',{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.leavingScreen}),overflowX:'hidden',width:theme.consts.leftDrawerWidthClosed};};var DrawerHeader=styles_styled('div')(function(_ref){var theme=_ref.theme;return _objectSpread2({height:theme.consts.appBarHeight,padding:'31px 23px 0 23px'},theme.mixins.toolbar);});var LeftDrawer_AppBar=styles_styled(AppBar_AppBar,{shouldForwardProp:function shouldForwardProp(prop){return prop!=='open';}})(function(_ref2){var theme=_ref2.theme,open=_ref2.open;return _objectSpread2({height:theme.consts.appBarHeight,border:'none',boxShadow:'none',background:'transparent',padding:'40px 40px 0 40px',marginLeft:theme.consts.leftDrawerWidthClosed,justifyContent:'space-between',width:"calc(100% - ".concat(theme.consts.leftDrawerWidthClosed,"px)"),zIndex:theme.zIndex.drawer+1,transition:theme.transitions.create(['width','margin'],{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.leavingScreen})},open&&{marginLeft:theme.consts.leftDrawerWidthOpen,width:"calc(100% - ".concat(theme.consts.leftDrawerWidthOpen,"px)"),transition:theme.transitions.create(['width','margin'],{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.enteringScreen})});});var LeftDrawer_Drawer=styles_styled(Drawer_Drawer,{shouldForwardProp:function shouldForwardProp(prop){return prop!=='open';}})(function(_ref3){var theme=_ref3.theme,open=_ref3.open;return _objectSpread2(_objectSpread2({width:theme.consts.leftDrawerWidthOpen,flexShrink:0,whiteSpace:'nowrap',boxSizing:'border-box',borderRadius:'0px 20px 20px 0px'},open&&_objectSpread2(_objectSpread2({},openedMixin(theme)),{},{'& .MuiDrawer-paper':_objectSpread2(_objectSpread2({},openedMixin(theme)),{},{borderRadius:'0px 20px 20px 0px'})})),!open&&_objectSpread2(_objectSpread2({},closedMixin(theme)),{},{'& .MuiDrawer-paper':_objectSpread2(_objectSpread2({},closedMixin(theme)),{},{borderRadius:'0px 20px 20px 0px'})}));});function Item(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{component:Link,to:props.to,disablePadding:true,sx:{display:'block',margin:'14px 0'},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(ListItemButton_ListItemButton,{sx:{height:'60px',justifyContent:'start',alignItems:'center',gap:'15px',padding:'0 24px'},children:[/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemIcon_ListItemIcon,{sx:{minWidth:0,flex:'0 0 auto',justifyContent:'center',alignItems:'center'},children:props.icon}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:props.text,sx:{display:props.open?'block':'none',margin:0},primaryTypographyProps:{fontWeight:500,fontSize:'24px',lineHeight:'33px',letterSpacing:'1px'}})]})},props.text);}function LeftDrawer(props){var _useState=(0,react.useState)(true),_useState2=slicedToArray_slicedToArray(_useState,2),open=_useState2[0],setOpen=_useState2[1];var location=dist_useLocation();var navigate=dist_useNavigate();var theme=styles_useTheme_useTheme();var toggleDrawer=function toggleDrawer(){setOpen(function(o){return!o;});};var path=location.pathname.split('/')[1];var header=null;switch(path){case'targets':header=/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h1",children:"Dashboard"}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{height:"40px",maxWidth:"314px",flexGrow:1,borderRadius:"10px",display:"flex",justifyContent:"space-between",alignItems:"center",padding:"0 9px 0 15px",sx:{background:theme.palette.background.default},children:[/*#__PURE__*/(0,jsx_runtime.jsx)("input",{type:"text",style:{background:'none',border:'none',outline:'none',height:'20px',lineHeight:'20px',fontSize:'18px'},placeholder:"Search"}),/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{sx:{padding:0,height:40,width:40},children:/*#__PURE__*/(0,jsx_runtime.jsx)(SearchIcon,{})})]})]});break;case'results':header=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"start",gap:"12px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{sx:{padding:0},onClick:function onClick(){return navigate('/targets');},children:/*#__PURE__*/(0,jsx_runtime.jsx)(ArrowLeftIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h1",children:"Result Tree"})]});break;}return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(LeftDrawer_AppBar,{position:"fixed",open:open,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",justifyContent:"space-between",children:header})}),/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsxs)(LeftDrawer_Drawer,{variant:"permanent",open:open,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(DrawerHeader,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(IconButton_IconButton,{onClick:toggleDrawer,sx:{gap:'13px',padding:0},children:[/*#__PURE__*/(0,jsx_runtime.jsx)(KluctlLogo,{}),open&&/*#__PURE__*/(0,jsx_runtime.jsx)(KluctlText,{})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(List_List,{sx:{padding:'10px 0 0 0'},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Item,{text:"Targets",open:open,icon:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetsIcon,{}),to:"targets"})})]})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{component:"main",sx:{flexGrow:1,height:'100%',overflow:'hidden'},minWidth:0,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(DrawerHeader,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:'0 40px'}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"calc(100% - ".concat(theme.consts.appBarHeight,"px)"),overflow:"auto",children:props.content})]})]});} +var openedMixin=function openedMixin(theme){return{width:theme.consts.leftDrawerWidthOpen,transition:theme.transitions.create('width',{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.enteringScreen}),overflowX:'hidden'};};var closedMixin=function closedMixin(theme){return{transition:theme.transitions.create('width',{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.leavingScreen}),overflowX:'hidden',width:theme.consts.leftDrawerWidthClosed};};var DrawerHeader=styles_styled('div')(function(_ref){var theme=_ref.theme;return _objectSpread2({height:theme.consts.appBarHeight,padding:'31px 23px 0 23px'},theme.mixins.toolbar);});var LeftDrawer_AppBar=styles_styled(AppBar_AppBar,{shouldForwardProp:function shouldForwardProp(prop){return prop!=='open';}})(function(_ref2){var theme=_ref2.theme,open=_ref2.open;return _objectSpread2({height:theme.consts.appBarHeight,border:'none',boxShadow:'none',background:'transparent',padding:'40px 40px 0 40px',marginLeft:theme.consts.leftDrawerWidthClosed,justifyContent:'space-between',width:"calc(100% - ".concat(theme.consts.leftDrawerWidthClosed,"px)"),zIndex:theme.zIndex.drawer+1,transition:theme.transitions.create(['width','margin'],{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.leavingScreen})},open&&{marginLeft:theme.consts.leftDrawerWidthOpen,width:"calc(100% - ".concat(theme.consts.leftDrawerWidthOpen,"px)"),transition:theme.transitions.create(['width','margin'],{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.enteringScreen})});});var LeftDrawer_Drawer=styles_styled(Drawer_Drawer,{shouldForwardProp:function shouldForwardProp(prop){return prop!=='open';}})(function(_ref3){var theme=_ref3.theme,open=_ref3.open;return _objectSpread2(_objectSpread2({width:theme.consts.leftDrawerWidthOpen,flexShrink:0,whiteSpace:'nowrap',boxSizing:'border-box',borderRadius:'0px 20px 20px 0px'},open&&_objectSpread2(_objectSpread2({},openedMixin(theme)),{},{'& .MuiDrawer-paper':_objectSpread2(_objectSpread2({},openedMixin(theme)),{},{borderRadius:'0px 20px 20px 0px'})})),!open&&_objectSpread2(_objectSpread2({},closedMixin(theme)),{},{'& .MuiDrawer-paper':_objectSpread2(_objectSpread2({},closedMixin(theme)),{},{borderRadius:'0px 20px 20px 0px'})}));});function Item(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{component:Link,to:props.to,disablePadding:true,sx:{display:'block',margin:'14px 0'},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(ListItemButton_ListItemButton,{sx:{height:'60px',justifyContent:'start',alignItems:'center',gap:'15px',padding:'0 24px'},children:[/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemIcon_ListItemIcon,{sx:{minWidth:0,flex:'0 0 auto',justifyContent:'center',alignItems:'center'},children:props.icon}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:props.text,sx:{display:props.open?'block':'none',margin:0},primaryTypographyProps:{fontWeight:500,fontSize:'24px',lineHeight:'33px',letterSpacing:'1px'}})]})},props.text);}function LeftDrawer(props){var _useState=(0,react.useState)(true),_useState2=slicedToArray_slicedToArray(_useState,2),open=_useState2[0],setOpen=_useState2[1];var location=dist_useLocation();var navigate=dist_useNavigate();var theme=styles_useTheme_useTheme();var toggleDrawer=function toggleDrawer(){setOpen(function(o){return!o;});};var path=location.pathname.split('/')[1];var header=null;switch(path){case'targets':header=/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h1",children:"Dashboard"}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{height:"40px",maxWidth:"314px",flexGrow:1,borderRadius:"10px",display:"flex",justifyContent:"space-between",alignItems:"center",padding:"0 9px 0 15px",sx:{background:theme.palette.background.default},children:[/*#__PURE__*/(0,jsx_runtime.jsx)("input",{type:"text",style:{background:'none',border:'none',outline:'none',height:'20px',lineHeight:'20px',fontSize:'18px'},placeholder:"Search (not impl. yet)"}),/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{sx:{padding:0,height:40,width:40},children:/*#__PURE__*/(0,jsx_runtime.jsx)(SearchIcon,{})})]})]});break;case'results':header=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"start",gap:"12px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{sx:{padding:0},onClick:function onClick(){return navigate('/targets');},children:/*#__PURE__*/(0,jsx_runtime.jsx)(ArrowLeftIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h1",children:"Result Tree"})]});break;}return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(LeftDrawer_AppBar,{position:"fixed",open:open,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",justifyContent:"space-between",children:header})}),/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsxs)(LeftDrawer_Drawer,{variant:"permanent",open:open,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(DrawerHeader,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(IconButton_IconButton,{onClick:toggleDrawer,sx:{gap:'13px',padding:0},children:[/*#__PURE__*/(0,jsx_runtime.jsx)(KluctlLogo,{}),open&&/*#__PURE__*/(0,jsx_runtime.jsx)(KluctlText,{})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(List_List,{sx:{padding:'10px 0 0 0'},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Item,{text:"Targets",open:open,icon:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetsIcon,{}),to:"targets"})})]})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{component:"main",sx:{flexGrow:1,height:'100%',overflow:'hidden'},minWidth:0,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(DrawerHeader,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:'0 40px'}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"calc(100% - ".concat(theme.consts.appBarHeight,"px)"),overflow:"auto",children:props.content})]})]});} ;// CONCATENATED MODULE: ./src/models.ts /* Do not change, this code is generated from Golang structs */var DeploymentError=/*#__PURE__*/function(){function DeploymentError(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,DeploymentError);this.ref=void 0;this.message=void 0;if('string'===typeof source)source=JSON.parse(source);this.ref=this.convertValues(source["ref"],models_ObjectRef);this.message=source["message"];}createClass_createClass(DeploymentError,[{key:"convertValues",value:function convertValues(a,classs){var _this=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i=0,_Object$keys=Object.keys(a);_i<_Object$keys.length;_i++){var _key=_Object$keys[_i];a[_key]=new classs(a[_key]);}return a;}return new classs(a);}return a;}}]);return DeploymentError;}();var Change=/*#__PURE__*/createClass_createClass(function Change(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,Change);this.type=void 0;this.jsonPath=void 0;this.oldValue=void 0;this.newValue=void 0;this.unifiedDiff=void 0;if('string'===typeof source)source=JSON.parse(source);this.type=source["type"];this.jsonPath=source["jsonPath"];this.oldValue=source["oldValue"];this.newValue=source["newValue"];this.unifiedDiff=source["unifiedDiff"];});var ResultObject=/*#__PURE__*/function(){function ResultObject(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,ResultObject);this.ref=void 0;this.changes=void 0;this.new=void 0;this.orphan=void 0;this.deleted=void 0;this.hook=void 0;this.rendered=void 0;this.remote=void 0;this.applied=void 0;if('string'===typeof source)source=JSON.parse(source);this.ref=this.convertValues(source["ref"],models_ObjectRef);this.changes=this.convertValues(source["changes"],Change);this.new=source["new"];this.orphan=source["orphan"];this.deleted=source["deleted"];this.hook=source["hook"];this.rendered=source["rendered"];this.remote=source["remote"];this.applied=source["applied"];}createClass_createClass(ResultObject,[{key:"convertValues",value:function convertValues(a,classs){var _this2=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this2.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i2=0,_Object$keys2=Object.keys(a);_i2<_Object$keys2.length;_i2++){var _key2=_Object$keys2[_i2];a[_key2]=new classs(a[_key2]);}return a;}return new classs(a);}return a;}}]);return ResultObject;}();var IgnoreForDiffItemConfig=/*#__PURE__*/createClass_createClass(function IgnoreForDiffItemConfig(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,IgnoreForDiffItemConfig);this.fieldPath=void 0;this.fieldPathRegex=void 0;this.group=void 0;this.kind=void 0;this.name=void 0;this.namespace=void 0;if('string'===typeof source)source=JSON.parse(source);this.fieldPath=source["fieldPath"];this.fieldPathRegex=source["fieldPathRegex"];this.group=source["group"];this.kind=source["kind"];this.name=source["name"];this.namespace=source["namespace"];});var HelmChartConfig=/*#__PURE__*/createClass_createClass(function HelmChartConfig(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,HelmChartConfig);this.repo=void 0;this.path=void 0;this.credentialsId=void 0;this.chartName=void 0;this.chartVersion=void 0;this.updateConstraints=void 0;this.releaseName=void 0;this.namespace=void 0;this.output=void 0;this.skipCRDs=void 0;this.skipUpdate=void 0;this.skipPrePull=void 0;if('string'===typeof source)source=JSON.parse(source);this.repo=source["repo"];this.path=source["path"];this.credentialsId=source["credentialsId"];this.chartName=source["chartName"];this.chartVersion=source["chartVersion"];this.updateConstraints=source["updateConstraints"];this.releaseName=source["releaseName"];this.namespace=source["namespace"];this.output=source["output"];this.skipCRDs=source["skipCRDs"];this.skipUpdate=source["skipUpdate"];this.skipPrePull=source["skipPrePull"];});var DeleteObjectItemConfig=/*#__PURE__*/createClass_createClass(function DeleteObjectItemConfig(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,DeleteObjectItemConfig);this.group=void 0;this.kind=void 0;this.name=void 0;this.namespace=void 0;if('string'===typeof source)source=JSON.parse(source);this.group=source["group"];this.kind=source["kind"];this.name=source["name"];this.namespace=source["namespace"];});var GitProject=/*#__PURE__*/createClass_createClass(function GitProject(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,GitProject);this.url=void 0;this.ref=void 0;this.subDir=void 0;if('string'===typeof source)source=JSON.parse(source);this.url=source["url"];this.ref=source["ref"];this.subDir=source["subDir"];});var DeploymentItemConfig=/*#__PURE__*/function(){function DeploymentItemConfig(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,DeploymentItemConfig);this.path=void 0;this.include=void 0;this.git=void 0;this.tags=void 0;this.barrier=void 0;this.message=void 0;this.waitReadiness=void 0;this.vars=void 0;this.skipDeleteIfTags=void 0;this.onlyRender=void 0;this.alwaysDeploy=void 0;this.deleteObjects=void 0;this.when=void 0;this.renderedHelmChartConfig=void 0;this.renderedObjects=void 0;this.renderedInclude=void 0;if('string'===typeof source)source=JSON.parse(source);this.path=source["path"];this.include=source["include"];this.git=this.convertValues(source["git"],GitProject);this.tags=source["tags"];this.barrier=source["barrier"];this.message=source["message"];this.waitReadiness=source["waitReadiness"];this.vars=this.convertValues(source["vars"],VarsSource);this.skipDeleteIfTags=source["skipDeleteIfTags"];this.onlyRender=source["onlyRender"];this.alwaysDeploy=source["alwaysDeploy"];this.deleteObjects=this.convertValues(source["deleteObjects"],DeleteObjectItemConfig);this.when=source["when"];this.renderedHelmChartConfig=this.convertValues(source["renderedHelmChartConfig"],HelmChartConfig);this.renderedObjects=this.convertValues(source["renderedObjects"],models_ObjectRef);this.renderedInclude=this.convertValues(source["renderedInclude"],DeploymentProjectConfig);}createClass_createClass(DeploymentItemConfig,[{key:"convertValues",value:function convertValues(a,classs){var _this3=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this3.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i3=0,_Object$keys3=Object.keys(a);_i3<_Object$keys3.length;_i3++){var _key3=_Object$keys3[_i3];a[_key3]=new classs(a[_key3]);}return a;}return new classs(a);}return a;}}]);return DeploymentItemConfig;}();var SealedSecretsConfig=/*#__PURE__*/createClass_createClass(function SealedSecretsConfig(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,SealedSecretsConfig);this.outputPattern=void 0;if('string'===typeof source)source=JSON.parse(source);this.outputPattern=source["outputPattern"];});var VarsSourceVault=/*#__PURE__*/createClass_createClass(function VarsSourceVault(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,VarsSourceVault);this.address=void 0;this.path=void 0;if('string'===typeof source)source=JSON.parse(source);this.address=source["address"];this.path=source["path"];});var VarsSourceAwsSecretsManager=/*#__PURE__*/createClass_createClass(function VarsSourceAwsSecretsManager(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,VarsSourceAwsSecretsManager);this.secretName=void 0;this.region=void 0;this.profile=void 0;if('string'===typeof source)source=JSON.parse(source);this.secretName=source["secretName"];this.region=source["region"];this.profile=source["profile"];});var VarsSourceHttp=/*#__PURE__*/createClass_createClass(function VarsSourceHttp(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,VarsSourceHttp);this.url=void 0;this.method=void 0;this.body=void 0;this.headers=void 0;this.jsonPath=void 0;if('string'===typeof source)source=JSON.parse(source);this.url=source["url"];this.method=source["method"];this.body=source["body"];this.headers=source["headers"];this.jsonPath=source["jsonPath"];});var VarsSourceClusterConfigMapOrSecret=/*#__PURE__*/createClass_createClass(function VarsSourceClusterConfigMapOrSecret(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,VarsSourceClusterConfigMapOrSecret);this.name=void 0;this.labels=void 0;this.namespace=void 0;this.key=void 0;this.targetPath=void 0;if('string'===typeof source)source=JSON.parse(source);this.name=source["name"];this.labels=source["labels"];this.namespace=source["namespace"];this.key=source["key"];this.targetPath=source["targetPath"];});var VarsSourceGit=/*#__PURE__*/createClass_createClass(function VarsSourceGit(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,VarsSourceGit);this.url=void 0;this.ref=void 0;this.path=void 0;if('string'===typeof source)source=JSON.parse(source);this.url=source["url"];this.ref=source["ref"];this.path=source["path"];});var VarsSource=/*#__PURE__*/function(){function VarsSource(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,VarsSource);this.ignoreMissing=void 0;this.noOverride=void 0;this.values=void 0;this.file=void 0;this.git=void 0;this.clusterConfigMap=void 0;this.clusterSecret=void 0;this.systemEnvVars=void 0;this.http=void 0;this.awsSecretsManager=void 0;this.vault=void 0;this.when=void 0;this.renderedVars=void 0;if('string'===typeof source)source=JSON.parse(source);this.ignoreMissing=source["ignoreMissing"];this.noOverride=source["noOverride"];this.values=source["values"];this.file=source["file"];this.git=this.convertValues(source["git"],VarsSourceGit);this.clusterConfigMap=this.convertValues(source["clusterConfigMap"],VarsSourceClusterConfigMapOrSecret);this.clusterSecret=this.convertValues(source["clusterSecret"],VarsSourceClusterConfigMapOrSecret);this.systemEnvVars=source["systemEnvVars"];this.http=this.convertValues(source["http"],VarsSourceHttp);this.awsSecretsManager=this.convertValues(source["awsSecretsManager"],VarsSourceAwsSecretsManager);this.vault=this.convertValues(source["vault"],VarsSourceVault);this.when=source["when"];this.renderedVars=source["renderedVars"];}createClass_createClass(VarsSource,[{key:"convertValues",value:function convertValues(a,classs){var _this4=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this4.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i4=0,_Object$keys4=Object.keys(a);_i4<_Object$keys4.length;_i4++){var _key4=_Object$keys4[_i4];a[_key4]=new classs(a[_key4]);}return a;}return new classs(a);}return a;}}]);return VarsSource;}();var DeploymentProjectConfig=/*#__PURE__*/function(){function DeploymentProjectConfig(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,DeploymentProjectConfig);this.vars=void 0;this.sealedSecrets=void 0;this.when=void 0;this.deployments=void 0;this.commonLabels=void 0;this.commonAnnotations=void 0;this.overrideNamespace=void 0;this.tags=void 0;this.ignoreForDiff=void 0;if('string'===typeof source)source=JSON.parse(source);this.vars=this.convertValues(source["vars"],VarsSource);this.sealedSecrets=this.convertValues(source["sealedSecrets"],SealedSecretsConfig);this.when=source["when"];this.deployments=this.convertValues(source["deployments"],DeploymentItemConfig);this.commonLabels=source["commonLabels"];this.commonAnnotations=source["commonAnnotations"];this.overrideNamespace=source["overrideNamespace"];this.tags=source["tags"];this.ignoreForDiff=this.convertValues(source["ignoreForDiff"],IgnoreForDiffItemConfig);}createClass_createClass(DeploymentProjectConfig,[{key:"convertValues",value:function convertValues(a,classs){var _this5=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this5.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i5=0,_Object$keys5=Object.keys(a);_i5<_Object$keys5.length;_i5++){var _key5=_Object$keys5[_i5];a[_key5]=new classs(a[_key5]);}return a;}return new classs(a);}return a;}}]);return DeploymentProjectConfig;}();var ClusterInfo=/*#__PURE__*/createClass_createClass(function ClusterInfo(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,ClusterInfo);this.clusterId=void 0;if('string'===typeof source)source=JSON.parse(source);this.clusterId=source["clusterId"];});var GitInfo=/*#__PURE__*/createClass_createClass(function GitInfo(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,GitInfo);this.url=void 0;this.ref=void 0;this.subDir=void 0;this.commit=void 0;this.dirty=void 0;if('string'===typeof source)source=JSON.parse(source);this.url=source["url"];this.ref=source["ref"];this.subDir=source["subDir"];this.commit=source["commit"];this.dirty=source["dirty"];});var KluctlDeploymentInfo=/*#__PURE__*/createClass_createClass(function KluctlDeploymentInfo(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,KluctlDeploymentInfo);this.name=void 0;this.namespace=void 0;if('string'===typeof source)source=JSON.parse(source);this.name=source["name"];this.namespace=source["namespace"];});var CommandInfo=/*#__PURE__*/function(){function CommandInfo(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,CommandInfo);this.initiator=void 0;this.startTime=void 0;this.endTime=void 0;this.kluctlDeployment=void 0;this.command=void 0;this.target=void 0;this.targetNameOverride=void 0;this.contextOverride=void 0;this.args=void 0;this.images=void 0;this.dryRun=void 0;this.noWait=void 0;this.forceApply=void 0;this.replaceOnError=void 0;this.forceReplaceOnError=void 0;this.abortOnError=void 0;this.includeTags=void 0;this.excludeTags=void 0;this.includeDeploymentDirs=void 0;this.excludeDeploymentDirs=void 0;if('string'===typeof source)source=JSON.parse(source);this.initiator=source["initiator"];this.startTime=source["startTime"];this.endTime=source["endTime"];this.kluctlDeployment=this.convertValues(source["kluctlDeployment"],KluctlDeploymentInfo);this.command=source["command"];this.target=source["target"];this.targetNameOverride=source["targetNameOverride"];this.contextOverride=source["contextOverride"];this.args=source["args"];this.images=this.convertValues(source["images"],FixedImage);this.dryRun=source["dryRun"];this.noWait=source["noWait"];this.forceApply=source["forceApply"];this.replaceOnError=source["replaceOnError"];this.forceReplaceOnError=source["forceReplaceOnError"];this.abortOnError=source["abortOnError"];this.includeTags=source["includeTags"];this.excludeTags=source["excludeTags"];this.includeDeploymentDirs=source["includeDeploymentDirs"];this.excludeDeploymentDirs=source["excludeDeploymentDirs"];}createClass_createClass(CommandInfo,[{key:"convertValues",value:function convertValues(a,classs){var _this6=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this6.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i6=0,_Object$keys6=Object.keys(a);_i6<_Object$keys6.length;_i6++){var _key6=_Object$keys6[_i6];a[_key6]=new classs(a[_key6]);}return a;}return new classs(a);}return a;}}]);return CommandInfo;}();var models_ObjectRef=/*#__PURE__*/createClass_createClass(function ObjectRef(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,ObjectRef);this.group=void 0;this.version=void 0;this.kind=void 0;this.name=void 0;this.namespace=void 0;if('string'===typeof source)source=JSON.parse(source);this.group=source["group"];this.version=source["version"];this.kind=source["kind"];this.name=source["name"];this.namespace=source["namespace"];});var FixedImage=/*#__PURE__*/function(){function FixedImage(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,FixedImage);this.image=void 0;this.resultImage=void 0;this.deployedImage=void 0;this.namespace=void 0;this.object=void 0;this.deployment=void 0;this.container=void 0;this.deployTags=void 0;this.deploymentDir=void 0;if('string'===typeof source)source=JSON.parse(source);this.image=source["image"];this.resultImage=source["resultImage"];this.deployedImage=source["deployedImage"];this.namespace=source["namespace"];this.object=this.convertValues(source["object"],models_ObjectRef);this.deployment=source["deployment"];this.container=source["container"];this.deployTags=source["deployTags"];this.deploymentDir=source["deploymentDir"];}createClass_createClass(FixedImage,[{key:"convertValues",value:function convertValues(a,classs){var _this7=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this7.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i7=0,_Object$keys7=Object.keys(a);_i7<_Object$keys7.length;_i7++){var _key7=_Object$keys7[_i7];a[_key7]=new classs(a[_key7]);}return a;}return new classs(a);}return a;}}]);return FixedImage;}();var SealingConfig=/*#__PURE__*/createClass_createClass(function SealingConfig(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,SealingConfig);this.args=void 0;this.secretSets=void 0;this.certFile=void 0;if('string'===typeof source)source=JSON.parse(source);this.args=source["args"];this.secretSets=source["secretSets"];this.certFile=source["certFile"];});var Target=/*#__PURE__*/function(){function Target(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,Target);this.name=void 0;this.context=void 0;this.args=void 0;this.sealingConfig=void 0;this.images=void 0;this.discriminator=void 0;if('string'===typeof source)source=JSON.parse(source);this.name=source["name"];this.context=source["context"];this.args=source["args"];this.sealingConfig=this.convertValues(source["sealingConfig"],SealingConfig);this.images=this.convertValues(source["images"],FixedImage);this.discriminator=source["discriminator"];}createClass_createClass(Target,[{key:"convertValues",value:function convertValues(a,classs){var _this8=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this8.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i8=0,_Object$keys8=Object.keys(a);_i8<_Object$keys8.length;_i8++){var _key8=_Object$keys8[_i8];a[_key8]=new classs(a[_key8]);}return a;}return new classs(a);}return a;}}]);return Target;}();var TargetKey=/*#__PURE__*/createClass_createClass(function TargetKey(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,TargetKey);this.targetName=void 0;this.clusterId=void 0;this.discriminator=void 0;if('string'===typeof source)source=JSON.parse(source);this.targetName=source["targetName"];this.clusterId=source["clusterId"];this.discriminator=source["discriminator"];});var ProjectKey=/*#__PURE__*/createClass_createClass(function ProjectKey(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,ProjectKey);this.gitRepoKey=void 0;this.subDir=void 0;if('string'===typeof source)source=JSON.parse(source);this.gitRepoKey=source["gitRepoKey"];this.subDir=source["subDir"];});var CommandResult=/*#__PURE__*/function(){function CommandResult(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,CommandResult);this.id=void 0;this.projectKey=void 0;this.targetKey=void 0;this.target=void 0;this.command=void 0;this.gitInfo=void 0;this.clusterInfo=void 0;this.deployment=void 0;this.objects=void 0;this.errors=void 0;this.warnings=void 0;this.seenImages=void 0;if('string'===typeof source)source=JSON.parse(source);this.id=source["id"];this.projectKey=this.convertValues(source["projectKey"],ProjectKey);this.targetKey=this.convertValues(source["targetKey"],TargetKey);this.target=this.convertValues(source["target"],Target);this.command=this.convertValues(source["command"],CommandInfo);this.gitInfo=this.convertValues(source["gitInfo"],GitInfo);this.clusterInfo=this.convertValues(source["clusterInfo"],ClusterInfo);this.deployment=this.convertValues(source["deployment"],DeploymentProjectConfig);this.objects=this.convertValues(source["objects"],ResultObject);this.errors=this.convertValues(source["errors"],DeploymentError);this.warnings=this.convertValues(source["warnings"],DeploymentError);this.seenImages=this.convertValues(source["seenImages"],FixedImage);}createClass_createClass(CommandResult,[{key:"convertValues",value:function convertValues(a,classs){var _this9=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this9.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i9=0,_Object$keys9=Object.keys(a);_i9<_Object$keys9.length;_i9++){var _key9=_Object$keys9[_i9];a[_key9]=new classs(a[_key9]);}return a;}return new classs(a);}return a;}}]);return CommandResult;}();var CommandResultSummary=/*#__PURE__*/function(){function CommandResultSummary(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,CommandResultSummary);this.id=void 0;this.projectKey=void 0;this.targetKey=void 0;this.target=void 0;this.commandInfo=void 0;this.gitInfo=void 0;this.clusterInfo=void 0;this.renderedObjects=void 0;this.remoteObjects=void 0;this.appliedObjects=void 0;this.appliedHookObjects=void 0;this.newObjects=void 0;this.changedObjects=void 0;this.orphanObjects=void 0;this.deletedObjects=void 0;this.errors=void 0;this.warnings=void 0;this.totalChanges=void 0;if('string'===typeof source)source=JSON.parse(source);this.id=source["id"];this.projectKey=this.convertValues(source["projectKey"],ProjectKey);this.targetKey=this.convertValues(source["targetKey"],TargetKey);this.target=this.convertValues(source["target"],Target);this.commandInfo=this.convertValues(source["commandInfo"],CommandInfo);this.gitInfo=this.convertValues(source["gitInfo"],GitInfo);this.clusterInfo=this.convertValues(source["clusterInfo"],ClusterInfo);this.renderedObjects=source["renderedObjects"];this.remoteObjects=source["remoteObjects"];this.appliedObjects=source["appliedObjects"];this.appliedHookObjects=source["appliedHookObjects"];this.newObjects=source["newObjects"];this.changedObjects=source["changedObjects"];this.orphanObjects=source["orphanObjects"];this.deletedObjects=source["deletedObjects"];this.errors=this.convertValues(source["errors"],DeploymentError);this.warnings=this.convertValues(source["warnings"],DeploymentError);this.totalChanges=source["totalChanges"];}createClass_createClass(CommandResultSummary,[{key:"convertValues",value:function convertValues(a,classs){var _this10=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this10.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i10=0,_Object$keys10=Object.keys(a);_i10<_Object$keys10.length;_i10++){var _key10=_Object$keys10[_i10];a[_key10]=new classs(a[_key10]);}return a;}return new classs(a);}return a;}}]);return CommandResultSummary;}();var ChangedObject=/*#__PURE__*/(/* unused pure expression or super */ null && (function(){function ChangedObject(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};_classCallCheck(this,ChangedObject);this.ref=void 0;this.changes=void 0;if('string'===typeof source)source=JSON.parse(source);this.ref=this.convertValues(source["ref"],models_ObjectRef);this.changes=this.convertValues(source["changes"],Change);}_createClass(ChangedObject,[{key:"convertValues",value:function convertValues(a,classs){var _this11=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this11.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i11=0,_Object$keys11=Object.keys(a);_i11<_Object$keys11.length;_i11++){var _key11=_Object$keys11[_i11];a[_key11]=new classs(a[_key11]);}return a;}return new classs(a);}return a;}}]);return ChangedObject;}()));var ValidateResultEntry=/*#__PURE__*/(/* unused pure expression or super */ null && (function(){function ValidateResultEntry(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};_classCallCheck(this,ValidateResultEntry);this.ref=void 0;this.annotation=void 0;this.message=void 0;if('string'===typeof source)source=JSON.parse(source);this.ref=this.convertValues(source["ref"],models_ObjectRef);this.annotation=source["annotation"];this.message=source["message"];}_createClass(ValidateResultEntry,[{key:"convertValues",value:function convertValues(a,classs){var _this12=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this12.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i12=0,_Object$keys12=Object.keys(a);_i12<_Object$keys12.length;_i12++){var _key12=_Object$keys12[_i12];a[_key12]=new classs(a[_key12]);}return a;}return new classs(a);}return a;}}]);return ValidateResultEntry;}()));var ValidateResult=/*#__PURE__*/(/* unused pure expression or super */ null && (function(){function ValidateResult(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};_classCallCheck(this,ValidateResult);this.id=void 0;this.startTime=void 0;this.endTime=void 0;this.ready=void 0;this.warnings=void 0;this.errors=void 0;this.results=void 0;this.drift=void 0;if('string'===typeof source)source=JSON.parse(source);this.id=source["id"];this.startTime=source["startTime"];this.endTime=source["endTime"];this.ready=source["ready"];this.warnings=this.convertValues(source["warnings"],DeploymentError);this.errors=this.convertValues(source["errors"],DeploymentError);this.results=this.convertValues(source["results"],ValidateResultEntry);this.drift=this.convertValues(source["drift"],ChangedObject);}_createClass(ValidateResult,[{key:"convertValues",value:function convertValues(a,classs){var _this13=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this13.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i13=0,_Object$keys13=Object.keys(a);_i13<_Object$keys13.length;_i13++){var _key13=_Object$keys13[_i13];a[_key13]=new classs(a[_key13]);}return a;}return new classs(a);}return a;}}]);return ValidateResult;}()));var ShortName=/*#__PURE__*/(/* unused pure expression or super */ null && (_createClass(function ShortName(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};_classCallCheck(this,ShortName);this.group=void 0;this.kind=void 0;this.shortName=void 0;if('string'===typeof source)source=JSON.parse(source);this.group=source["group"];this.kind=source["kind"];this.shortName=source["shortName"];})));var UnstructuredObject=/*#__PURE__*/(/* unused pure expression or super */ null && (_createClass(function UnstructuredObject(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};_classCallCheck(this,UnstructuredObject);if('string'===typeof source)source=JSON.parse(source);})));var ProjectTargetKey=/*#__PURE__*/function(){function ProjectTargetKey(){var source=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};classCallCheck_classCallCheck(this,ProjectTargetKey);this.project=void 0;this.target=void 0;if('string'===typeof source)source=JSON.parse(source);this.project=this.convertValues(source["project"],ProjectKey);this.target=this.convertValues(source["target"],TargetKey);}createClass_createClass(ProjectTargetKey,[{key:"convertValues",value:function convertValues(a,classs){var _this14=this;var asMap=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(!a){return a;}if(a.slice){return a.map(function(elem){return _this14.convertValues(elem,classs);});}else if("object"===typeof a){if(asMap){for(var _i14=0,_Object$keys14=Object.keys(a);_i14<_Object$keys14.length;_i14++){var _key14=_Object$keys14[_i14];a[_key14]=new classs(a[_key14]);}return a;}return new classs(a);}return a;}}]);return ProjectTargetKey;}(); // EXTERNAL MODULE: ./node_modules/lodash/lodash.js @@ -52222,8 +52318,27 @@ var Loading=function Loading(){return/*#__PURE__*/(0,jsx_runtime.jsx)(material_B function useAppOutletContext(){return useOutletContext();}var AppContext=/*#__PURE__*/(0,react.createContext)({summaries:new Map(),projects:[],validateResults:new Map()});var ApiContext=/*#__PURE__*/(0,react.createContext)(new StaticApi());var LoggedInApp=function LoggedInApp(props){var api=(0,react.useContext)(ApiContext);var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),filters=_useState2[0],setFilters=_useState2[1];var summariesRef=(0,react.useRef)(new Map());var validateResultsRef=(0,react.useRef)(new Map());var _useState3=(0,react.useState)(summariesRef.current),_useState4=slicedToArray_slicedToArray(_useState3,2),summaries=_useState4[0],setSummaries=_useState4[1];var _useState5=(0,react.useState)(validateResultsRef.current),_useState6=slicedToArray_slicedToArray(_useState5,2),validateResults=_useState6[0],setValidateResults=_useState6[1];var onUnauthorized=props.onUnauthorized;(0,react.useEffect)(function(){var updateSummary=function updateSummary(rs){console.log("update_summary",rs.id,rs.commandInfo.startTime);summariesRef.current.set(rs.id,rs);setSummaries(new Map(summariesRef.current));};var deleteSummary=function deleteSummary(id){console.log("delete_summary",id);summariesRef.current.delete(id);setSummaries(new Map(summariesRef.current));};var updateValidateResult=function updateValidateResult(key,vr){console.log("validate_result",key);validateResultsRef.current.set(JSON.stringify(key),vr);setValidateResults(new Map(validateResultsRef.current));};console.log("starting listenResults");var cancel;cancel=api.listenUpdates(undefined,undefined,function(msg){switch(msg.type){case"update_summary":updateSummary(msg.summary);break;case"delete_summary":deleteSummary(msg.id);break;case"validate_result":updateValidateResult(msg.key,msg.result);break;case"auth_result":if(!msg.success){cancel.then(function(c){return c();});onUnauthorized();}}});return function(){console.log("cancel listenResults");cancel.then(function(c){return c();});};},[api,onUnauthorized]);var projects=(0,react.useMemo)(function(){return buildProjectSummaries(summaries,validateResults);},[summaries,validateResults]);var appContext={summaries:summariesRef.current,projects:projects,validateResults:validateResults};var outletContext={filters:filters,setFilters:setFilters};return/*#__PURE__*/(0,jsx_runtime.jsx)(AppContext.Provider,{value:appContext,children:/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_light,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(LeftDrawer,{content:/*#__PURE__*/(0,jsx_runtime.jsx)(Outlet,{context:outletContext}),context:outletContext})})})});};var App=function App(){var _useState7=(0,react.useState)(),_useState8=slicedToArray_slicedToArray(_useState7,2),api=_useState8[0],setApi=_useState8[1];var _useState9=(0,react.useState)(false),_useState10=slicedToArray_slicedToArray(_useState9,2),needToken=_useState10[0],setNeedToken=_useState10[1];var storage=localStorage;var getToken=function getToken(){var token=storage.getItem("token");if(!token){return"";}return JSON.parse(token);};var setToken=function setToken(token){if(!token){storage.removeItem("token");}else{storage.setItem("token",JSON.stringify(token));}};var onUnauthorized=function onUnauthorized(){console.log("handle onUnauthorized");setToken(undefined);setApi(undefined);setNeedToken(true);};var onTokenRefresh=function onTokenRefresh(newToken){console.log("handle onTokenRefresh");setToken(newToken);};var handleLoginSucceeded=function handleLoginSucceeded(token){console.log("handle saveToken");setToken(token);setApi(new RealApi(getToken,onUnauthorized,onTokenRefresh));};(0,react.useEffect)(function(){if(api){return;}var doInit=/*#__PURE__*/function(){var _ref=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var isStatic,noAuthApi;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return checkStaticBuild();case 2:isStatic=_context.sent;if(!isStatic){_context.next=7;break;}setApi(new StaticApi());_context.next=19;break;case 7:// check if we don't need auth (running locally?) noAuthApi=new RealApi(undefined,undefined,undefined);_context.prev=8;_context.next=11;return noAuthApi.getShortNames();case 11:setToken(undefined);setNeedToken(false);setApi(noAuthApi);_context.next=19;break;case 16:_context.prev=16;_context.t0=_context["catch"](8);if(!getToken()){setNeedToken(true);}else{setApi(new RealApi(getToken,onUnauthorized,onTokenRefresh));}case 19:case"end":return _context.stop();}},_callee,null,[[8,16]]);}));return function doInit(){return _ref.apply(this,arguments);};}();doInit();// eslint-disable-next-line react-hooks/exhaustive-deps },[]);if(needToken&&!getToken()){return/*#__PURE__*/(0,jsx_runtime.jsx)(Login,{setToken:handleLoginSucceeded});}if(!api){return/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{});}return/*#__PURE__*/(0,jsx_runtime.jsx)(ApiContext.Provider,{value:api,children:/*#__PURE__*/(0,jsx_runtime.jsx)(LoggedInApp,{onUnauthorized:onUnauthorized})});};/* harmony default export */ var components_App = (App); +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/objectWithoutProperties.js + +function objectWithoutProperties_objectWithoutProperties(source, excluded) { + if (source == null) return {}; + var target = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(source, excluded); + var key, i; + if (Object.getOwnPropertySymbols) { + var sourceSymbolKeys = Object.getOwnPropertySymbols(source); + for (i = 0; i < sourceSymbolKeys.length; i++) { + key = sourceSymbolKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; + target[key] = source[key]; + } + } + return target; +} +;// CONCATENATED MODULE: ./src/components/targets-view/Card.tsx +var Card_excluded=["sx"];var cardWidth=247;var projectCardHeight=80;var cardHeight=126;var cardGap=20;function CardPaper(props){var sx=props.sx,rest=objectWithoutProperties_objectWithoutProperties(props,Card_excluded);return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,_objectSpread2({elevation:5,sx:_objectSpread2({width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A'},sx)},rest));}function Card(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2({display:"flex",flexShrink:0,width:cardWidth,height:cardHeight},props));}function CardCol(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2({display:"flex",flexDirection:"column",gap:"".concat(cardGap,"px")},props));}function CardRow(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2({display:"flex",gap:"".concat(cardGap,"px")},props));} ;// CONCATENATED MODULE: ./src/components/targets-view/Projects.tsx -var ProjectItem=function ProjectItem(props){var name=getLastPathElement(props.ps.project.gitRepoKey);var subDir=props.ps.project.subDir;var projectInfo=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{children:[props.ps.project.gitRepoKey,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),props.ps.project.subDir?/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:["SubDir: ",props.ps.project.subDir,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{})]}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'5px 16px'},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"center",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",alignItems:"center",gap:"15px",children:name&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(ProjectIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:projectInfo,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:name})})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{children:subDir?/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{textAlign:"center",children:subDir}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{marginLeft:"auto"})})})]})});}; +var ProjectItem=function ProjectItem(props){var name=getLastPathElement(props.ps.project.gitRepoKey);var subDir=props.ps.project.subDir;var projectInfo=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{children:[props.ps.project.gitRepoKey,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),props.ps.project.subDir?/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:["SubDir: ",props.ps.project.subDir,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{})]}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(CardPaper,{sx:{padding:'5px 16px'},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"center",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",alignItems:"center",gap:"15px",children:name&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(ProjectIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:projectInfo,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:name})})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{children:subDir?/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{textAlign:"center",children:subDir}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{marginLeft:"auto"})})})]})});}; ;// CONCATENATED MODULE: ./node_modules/@mui/material/SvgIcon/svgIconClasses.js @@ -53373,7 +53488,7 @@ var ActionsMenu=function ActionsMenu(props){var _React$useState=react.useState(n ;// CONCATENATED MODULE: ./src/utils/duration.ts function formatDuration(ms,withMs){if(ms<0)ms=-ms;var time={day:Math.floor(ms/86400000),hour:Math.floor(ms/3600000)%24,minute:Math.floor(ms/60000)%60,second:Math.floor(ms/1000)%60,millisecond:withMs?Math.floor(ms)%1000:0};return Object.entries(time).filter(function(val){return val[1]!==0;}).map(function(val){return val[1]+' '+(val[1]!==1?val[0]+'s':val[0]);}).join(', ');}function formatDurationShort(ms){if(ms<0)ms=-ms;var time={d:Math.floor(ms/86400000),h:Math.floor(ms/3600000)%24,m:Math.floor(ms/60000)%60,s:Math.floor(ms/1000)%60,ms:Math.floor(ms)%1000};var f=Object.entries(time).find(function(val){return val[1]>0;});if(f===undefined){return"0s";}return f[1]+f[0];}var calcAgo=function calcAgo(startTime){var t1=new Date(startTime);var t2=new Date();var d=t2.getTime()-t1.getTime();return formatDurationShort(d);}; ;// CONCATENATED MODULE: ./src/components/targets-view/Targets.tsx -var StatusIcon=function StatusIcon(props){var icon;var theme=styles_useTheme_useTheme();if(props.ts.lastValidateResult===undefined){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(MessageQuestionIcon,{color:theme.palette.error.main});}else if(props.ts.lastValidateResult.ready&&!props.ts.lastValidateResult.errors){var _props$ts$lastValidat,_props$ts$lastValidat2;if((_props$ts$lastValidat=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat!==void 0&&_props$ts$lastValidat.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"warning"});}else if((_props$ts$lastValidat2=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat2!==void 0&&_props$ts$lastValidat2.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"primary"});}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"success"});}}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(HeartBroken,{color:"error"});}var tooltip=[];if(props.ts.lastValidateResult===undefined){tooltip.push("No validation result available.");}else{var _props$ts$lastValidat3,_props$ts$lastValidat4,_props$ts$lastValidat5,_props$ts$lastValidat6;if(props.ts.lastValidateResult.ready&&!((_props$ts$lastValidat3=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat3!==void 0&&_props$ts$lastValidat3.length)){tooltip.push("Target is ready.");}else{tooltip.push("Target is not ready.");}if((_props$ts$lastValidat4=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat4!==void 0&&_props$ts$lastValidat4.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.errors.length," validation errors."));}if((_props$ts$lastValidat5=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat5!==void 0&&_props$ts$lastValidat5.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.warnings.length," validation warnings."));}if((_props$ts$lastValidat6=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat6!==void 0&&_props$ts$lastValidat6.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.drift.length," drifted objects."));}tooltip.push("Validation performed "+calcAgo(props.ts.lastValidateResult.startTime)+" ago");}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip.map(function(t){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:t},t);}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:icon})});};var TargetItem=function TargetItem(props){var _props$ts$commandResu,_props$ts$commandResu2;var api=(0,react.useContext)(ApiContext);var actionMenuItems=[];actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Validate now",handler:function handler(){api.validateNow(props.ps.project,props.ts.target);}});var kd;var allKdEqual=true;(_props$ts$commandResu=props.ts.commandResults)===null||_props$ts$commandResu===void 0?void 0:_props$ts$commandResu.forEach(function(rs){if(rs.commandInfo.kluctlDeployment){if(!kd){kd=rs.commandInfo.kluctlDeployment;}else{if(kd.name!==rs.commandInfo.kluctlDeployment.name||kd.namespace!==rs.commandInfo.kluctlDeployment.namespace){allKdEqual=false;}}}});if(kd&&allKdEqual){actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Reconcile",handler:function handler(){api.reconcileNow(props.ts.target.clusterId,kd.name,kd.namespace);}});actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Deploy",handler:function handler(){api.deployNow(props.ts.target.clusterId,kd.name,kd.namespace);}});}var allContexts=[];var handleContext=function handleContext(c){if(!c){return;}if(allContexts.find(function(x){return x===c;})){return;}allContexts.push(c);};(_props$ts$commandResu2=props.ts.commandResults)===null||_props$ts$commandResu2===void 0?void 0:_props$ts$commandResu2.forEach(function(rs){handleContext(rs.commandInfo.contextOverride);handleContext(rs.target.context);});var contextTooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{textAlign:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:"All known contexts:"}),allContexts.map(function(context){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:context},context);})]});var targetName=props.ts.target.targetName;if(!targetName){targetName="";}return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'20px 16px 12px 16px'},onClick:function onClick(e){return props.onSelectTarget(props.ts);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flexGrow:1,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:targetName}),allContexts.length?/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:contextTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:allContexts[0]})}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Cluster ID: "+props.ts.target.clusterId,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CpuIcon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Discriminator: "+props.ts.target.discriminator,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(FingerScanIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(StatusIcon,_objectSpread2({},props)),/*#__PURE__*/(0,jsx_runtime.jsx)(ActionsMenu,{menuItems:actionMenuItems})]})]})]})});}; +var StatusIcon=function StatusIcon(props){var icon;var theme=styles_useTheme_useTheme();if(props.ts.lastValidateResult===undefined){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(MessageQuestionIcon,{color:theme.palette.error.main});}else if(props.ts.lastValidateResult.ready&&!props.ts.lastValidateResult.errors){var _props$ts$lastValidat,_props$ts$lastValidat2;if((_props$ts$lastValidat=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat!==void 0&&_props$ts$lastValidat.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"warning"});}else if((_props$ts$lastValidat2=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat2!==void 0&&_props$ts$lastValidat2.length){icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"primary"});}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(Favorite,{color:"success"});}}else{icon=/*#__PURE__*/(0,jsx_runtime.jsx)(HeartBroken,{color:"error"});}var tooltip=[];if(props.ts.lastValidateResult===undefined){tooltip.push("No validation result available.");}else{var _props$ts$lastValidat3,_props$ts$lastValidat4,_props$ts$lastValidat5,_props$ts$lastValidat6;if(props.ts.lastValidateResult.ready&&!((_props$ts$lastValidat3=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat3!==void 0&&_props$ts$lastValidat3.length)){tooltip.push("Target is ready.");}else{tooltip.push("Target is not ready.");}if((_props$ts$lastValidat4=props.ts.lastValidateResult.errors)!==null&&_props$ts$lastValidat4!==void 0&&_props$ts$lastValidat4.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.errors.length," validation errors."));}if((_props$ts$lastValidat5=props.ts.lastValidateResult.warnings)!==null&&_props$ts$lastValidat5!==void 0&&_props$ts$lastValidat5.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.warnings.length," validation warnings."));}if((_props$ts$lastValidat6=props.ts.lastValidateResult.drift)!==null&&_props$ts$lastValidat6!==void 0&&_props$ts$lastValidat6.length){tooltip.push("Target has ".concat(props.ts.lastValidateResult.drift.length," drifted objects."));}tooltip.push("Validation performed "+calcAgo(props.ts.lastValidateResult.startTime)+" ago");}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip.map(function(t){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:t},t);}),children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:icon})});};var TargetItem=function TargetItem(props){var _props$ts$commandResu,_props$ts$commandResu2;var api=(0,react.useContext)(ApiContext);var actionMenuItems=[];actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Validate now",handler:function handler(){api.validateNow(props.ps.project,props.ts.target);}});var kd;var allKdEqual=true;(_props$ts$commandResu=props.ts.commandResults)===null||_props$ts$commandResu===void 0?void 0:_props$ts$commandResu.forEach(function(rs){if(rs.commandInfo.kluctlDeployment){if(!kd){kd=rs.commandInfo.kluctlDeployment;}else{if(kd.name!==rs.commandInfo.kluctlDeployment.name||kd.namespace!==rs.commandInfo.kluctlDeployment.namespace){allKdEqual=false;}}}});if(kd&&allKdEqual){actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Reconcile",handler:function handler(){api.reconcileNow(props.ts.target.clusterId,kd.name,kd.namespace);}});actionMenuItems.push({icon:/*#__PURE__*/(0,jsx_runtime.jsx)(PublishedWithChanges,{}),text:"Deploy",handler:function handler(){api.deployNow(props.ts.target.clusterId,kd.name,kd.namespace);}});}var allContexts=[];var handleContext=function handleContext(c){if(!c){return;}if(allContexts.find(function(x){return x===c;})){return;}allContexts.push(c);};(_props$ts$commandResu2=props.ts.commandResults)===null||_props$ts$commandResu2===void 0?void 0:_props$ts$commandResu2.forEach(function(rs){handleContext(rs.commandInfo.contextOverride);handleContext(rs.target.context);});var contextTooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{textAlign:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:"All known contexts:"}),allContexts.map(function(context){return/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle2",children:context},context);})]});var targetName=props.ts.target.targetName;if(!targetName){targetName="";}return/*#__PURE__*/(0,jsx_runtime.jsx)(CardPaper,{sx:{padding:'20px 16px 12px 16px'},onClick:function onClick(e){return props.onSelectTarget(props.ts);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flexShrink:0,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TargetIcon,{})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flexGrow:1,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:targetName}),allContexts.length?/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:contextTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:allContexts[0]})}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{})]})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Cluster ID: "+props.ts.target.clusterId,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CpuIcon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Discriminator: "+props.ts.target.discriminator,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(FingerScanIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(StatusIcon,_objectSpread2({},props)),/*#__PURE__*/(0,jsx_runtime.jsx)(ActionsMenu,{menuItems:actionMenuItems})]})]})]})});}; ;// CONCATENATED MODULE: ./node_modules/js-yaml/dist/js-yaml.mjs /*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT */ function isNothing(subject) { @@ -56487,23 +56602,6 @@ var jsYaml = { }; /* harmony default export */ var js_yaml = ((/* unused pure expression or super */ null && (jsYaml))); -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/objectWithoutProperties.js - -function objectWithoutProperties_objectWithoutProperties(source, excluded) { - if (source == null) return {}; - var target = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(source, excluded); - var key, i; - if (Object.getOwnPropertySymbols) { - var sourceSymbolKeys = Object.getOwnPropertySymbols(source); - for (i = 0; i < sourceSymbolKeys.length; i++) { - key = sourceSymbolKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; - target[key] = source[key]; - } - } - return target; -} ;// CONCATENATED MODULE: ./node_modules/react-syntax-highlighter/dist/esm/create-element.js @@ -57167,28 +57265,21 @@ esm_light.registerLanguage('yaml',hljs_yaml);esm_light.registerLanguage('diff',h ;// CONCATENATED MODULE: ./src/components/result-view/CommandResultStatusLine.tsx var StatusLine=function StatusLine(props){var children=[];var doPush=function doPush(n,t,icon){if(n){children.push(/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:n+" "+t,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",children:icon})}),/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{fontSize:"10px",align:"center",children:n})]},t));}};doPush(props.errors,"total errors.",/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorIcon,{}));doPush(props.warnings,"total warnings.",/*#__PURE__*/(0,jsx_runtime.jsx)(WarningIcon,{}));doPush(props.newObjects,"new objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(AddedIcon,{}));doPush(props.deletedObjects,"deleted objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(TrashIcon,{}));doPush(props.orphanObjects,"orphan objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(OrphanIcon,{}));doPush(props.changedObjects,"changed objects.",/*#__PURE__*/(0,jsx_runtime.jsx)(ChangedIcon,{}));return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",width:"100%",children:children});};var CommandResultStatusLine=function CommandResultStatusLine(props){var _props$rs$errors,_props$rs$warnings;return/*#__PURE__*/(0,jsx_runtime.jsx)(StatusLine,{errors:(_props$rs$errors=props.rs.errors)===null||_props$rs$errors===void 0?void 0:_props$rs$errors.length,warnings:(_props$rs$warnings=props.rs.warnings)===null||_props$rs$warnings===void 0?void 0:_props$rs$warnings.length,changedObjects:props.rs.changedObjects,newObjects:props.rs.newObjects,deletedObjects:props.rs.deletedObjects,orphanObjects:props.rs.orphanObjects});};var ValidateResultStatusLine=function ValidateResultStatusLine(props){var _props$vr,_props$vr$errors,_props$vr2,_props$vr2$warnings,_props$vr3,_props$vr3$drift;return/*#__PURE__*/_jsx(StatusLine,{errors:(_props$vr=props.vr)===null||_props$vr===void 0?void 0:(_props$vr$errors=_props$vr.errors)===null||_props$vr$errors===void 0?void 0:_props$vr$errors.length,warnings:(_props$vr2=props.vr)===null||_props$vr2===void 0?void 0:(_props$vr2$warnings=_props$vr2.warnings)===null||_props$vr2$warnings===void 0?void 0:_props$vr2$warnings.length,changedObjects:(_props$vr3=props.vr)===null||_props$vr3===void 0?void 0:(_props$vr3$drift=_props$vr3.drift)===null||_props$vr3$drift===void 0?void 0:_props$vr3$drift.length});}; ;// CONCATENATED MODULE: ./src/components/targets-view/CommandResultItem.tsx -var CommandResultItem=/*#__PURE__*/react.memo(function(props){var _rs$commandInfo,_rs$commandInfo2;var rs=props.rs,onSelectCommandResult=props.onSelectCommandResult,selected=props.selected;var navigate=dist_useNavigate();var _useState=(0,react.useState)(calcAgo(rs.commandInfo.startTime)),_useState2=slicedToArray_slicedToArray(_useState,2),ago=_useState2[0],setAgo=_useState2[1];var Icon=DiffIcon;switch((_rs$commandInfo=rs.commandInfo)===null||_rs$commandInfo===void 0?void 0:_rs$commandInfo.command){case"delete":Icon=PruneIcon;break;case"deploy":Icon=DeployIcon;break;case"diff":Icon=DiffIcon;break;case"poke-images":Icon=DeployIcon;break;case"prune":Icon=PruneIcon;break;}var iconTooltip=(0,react.useMemo)(function(){var cmdInfoYaml=dump(rs.commandInfo);return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:cmdInfoYaml,language:"yaml"});},[rs.commandInfo]);(0,react.useEffect)(function(){var interval=setInterval(function(){return setAgo(calcAgo(rs.commandInfo.startTime));},5000);return function(){return clearInterval(interval);};},[rs.commandInfo.startTime]);return/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{elevation:5,sx:{width:"100%",height:"100%",borderRadius:'12px',border:'1px solid #59A588',boxShadow:'4px 4px 10px #1E617A',padding:'20px 16px 5px 16px',outline:selected?'8px solid #59A588':'none'},onClick:function onClick(){return onSelectCommandResult(rs);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:iconTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"45px",height:"45px",flex:"0 0 auto",justifyContent:"center",alignItems:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Icon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flexGrow:1,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",flexGrow:1,children:(_rs$commandInfo2=rs.commandInfo)===null||_rs$commandInfo2===void 0?void 0:_rs$commandInfo2.command}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:rs.commandInfo.startTime,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:ago})})]})]}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultStatusLine,{rs:rs})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",height:"39px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:function onClick(e){e.stopPropagation();navigate("/results/".concat(rs.id));},sx:{padding:0,width:26,height:26},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Open Result Tree",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TreeViewIcon,{})})})})})]})]})});}); -;// CONCATENATED MODULE: ./node_modules/@mui/material/Table/TableContext.js +var CommandResultItemHeader=/*#__PURE__*/react.memo(function(props){var _rs$commandInfo,_rs$commandInfo2;var rs=props.rs;var _useState=(0,react.useState)(calcAgo(rs.commandInfo.startTime)),_useState2=slicedToArray_slicedToArray(_useState,2),ago=_useState2[0],setAgo=_useState2[1];var Icon=DiffIcon;switch((_rs$commandInfo=rs.commandInfo)===null||_rs$commandInfo===void 0?void 0:_rs$commandInfo.command){case"delete":Icon=PruneIcon;break;case"deploy":Icon=DeployIcon;break;case"diff":Icon=DiffIcon;break;case"poke-images":Icon=DeployIcon;break;case"prune":Icon=PruneIcon;break;}var iconTooltip=(0,react.useMemo)(function(){var cmdInfoYaml=dump(rs.commandInfo);return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:cmdInfoYaml,language:"yaml"});},[rs.commandInfo]);(0,react.useEffect)(function(){var interval=setInterval(function(){return setAgo(calcAgo(rs.commandInfo.startTime));},5000);return function(){return clearInterval(interval);};},[rs.commandInfo.startTime]);return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",gap:"15px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:iconTooltip,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"45px",height:"45px",flex:"0 0 auto",justifyContent:"center",alignItems:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Icon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h6",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",children:(_rs$commandInfo2=rs.commandInfo)===null||_rs$commandInfo2===void 0?void 0:_rs$commandInfo2.command}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:rs.commandInfo.startTime,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",textAlign:"left",textOverflow:"ellipsis",overflow:"hidden",whiteSpace:"nowrap",fontSize:"14px",fontWeight:500,lineHeight:"19px",children:ago})})]})]});});var CommandResultItem=/*#__PURE__*/react.memo(function(props){var rs=props.rs,onSelectCommandResult=props.onSelectCommandResult;var navigate=dist_useNavigate();return/*#__PURE__*/(0,jsx_runtime.jsx)(CardPaper,{sx:{padding:'20px 16px 5px 16px'},onClick:function onClick(){return onSelectCommandResult(rs);},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",justifyContent:"space-between",height:"100%",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultItemHeader,{rs:rs}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",alignItems:"center",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultStatusLine,{rs:rs})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",gap:"6px",alignItems:"center",height:"39px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:function onClick(e){e.stopPropagation();navigate("/results/".concat(rs.id));},sx:{padding:0,width:26,height:26},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Open Result Tree",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TreeViewIcon,{})})})})})]})]})});}); +;// CONCATENATED MODULE: ./node_modules/@mui/material/Tab/tabClasses.js -/** - * @ignore - internal component. - */ -var TableContext = /*#__PURE__*/react.createContext(); -if (false) {} -/* harmony default export */ var Table_TableContext = (TableContext); -;// CONCATENATED MODULE: ./node_modules/@mui/material/Table/tableClasses.js +function getTabUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTab', slot); +} +var tabClasses = generateUtilityClasses('MuiTab', ['root', 'labelIcon', 'textColorInherit', 'textColorPrimary', 'textColorSecondary', 'selected', 'disabled', 'fullWidth', 'wrapped', 'iconWrapper']); +/* harmony default export */ var Tab_tabClasses = (tabClasses); +;// CONCATENATED MODULE: ./node_modules/@mui/material/Tab/Tab.js -function getTableUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTable', slot); -} -var tableClasses = generateUtilityClasses('MuiTable', ['root', 'stickyHeader']); -/* harmony default export */ var Table_tableClasses = ((/* unused pure expression or super */ null && (tableClasses))); -;// CONCATENATED MODULE: ./node_modules/@mui/material/Table/Table.js +var Tab_excluded = ["className", "disabled", "disableFocusRipple", "fullWidth", "icon", "iconPosition", "indicator", "label", "onChange", "onClick", "onFocus", "selected", "selectionFollowsFocus", "textColor", "value", "wrapped"]; -var Table_excluded = ["className", "component", "padding", "size", "stickyHeader"]; @@ -57198,171 +57289,428 @@ var Table_excluded = ["className", "component", "padding", "size", "stickyHeader -var Table_useUtilityClasses = function useUtilityClasses(ownerState) { + +var Tab_useUtilityClasses = function useUtilityClasses(ownerState) { var classes = ownerState.classes, - stickyHeader = ownerState.stickyHeader; + textColor = ownerState.textColor, + fullWidth = ownerState.fullWidth, + wrapped = ownerState.wrapped, + icon = ownerState.icon, + label = ownerState.label, + selected = ownerState.selected, + disabled = ownerState.disabled; var slots = { - root: ['root', stickyHeader && 'stickyHeader'] + root: ['root', icon && label && 'labelIcon', "textColor".concat(utils_capitalize(textColor)), fullWidth && 'fullWidth', wrapped && 'wrapped', selected && 'selected', disabled && 'disabled'], + iconWrapper: ['iconWrapper'] }; - return composeClasses(slots, getTableUtilityClass, classes); + return composeClasses(slots, getTabUtilityClass, classes); }; -var TableRoot = styles_styled('table', { - name: 'MuiTable', +var TabRoot = styles_styled(ButtonBase_ButtonBase, { + name: 'MuiTab', slot: 'Root', overridesResolver: function overridesResolver(props, styles) { var ownerState = props.ownerState; - return [styles.root, ownerState.stickyHeader && styles.stickyHeader]; + return [styles.root, ownerState.label && ownerState.icon && styles.labelIcon, styles["textColor".concat(utils_capitalize(ownerState.textColor))], ownerState.fullWidth && styles.fullWidth, ownerState.wrapped && styles.wrapped]; } })(function (_ref) { + var _ref3, _ref4, _ref5; var theme = _ref.theme, ownerState = _ref.ownerState; - return extends_extends({ - display: 'table', - width: '100%', - borderCollapse: 'collapse', - borderSpacing: 0, - '& caption': extends_extends({}, theme.typography.body2, { - padding: theme.spacing(2), - color: (theme.vars || theme).palette.text.secondary, - textAlign: 'left', - captionSide: 'bottom' - }) - }, ownerState.stickyHeader && { - borderCollapse: 'separate' + return extends_extends({}, theme.typography.button, { + maxWidth: 360, + minWidth: 90, + position: 'relative', + minHeight: 48, + flexShrink: 0, + padding: '12px 16px', + overflow: 'hidden', + whiteSpace: 'normal', + textAlign: 'center' + }, ownerState.label && { + flexDirection: ownerState.iconPosition === 'top' || ownerState.iconPosition === 'bottom' ? 'column' : 'row' + }, { + lineHeight: 1.25 + }, ownerState.icon && ownerState.label && defineProperty_defineProperty({ + minHeight: 72, + paddingTop: 9, + paddingBottom: 9 + }, "& > .".concat(Tab_tabClasses.iconWrapper), extends_extends({}, ownerState.iconPosition === 'top' && { + marginBottom: 6 + }, ownerState.iconPosition === 'bottom' && { + marginTop: 6 + }, ownerState.iconPosition === 'start' && { + marginRight: theme.spacing(1) + }, ownerState.iconPosition === 'end' && { + marginLeft: theme.spacing(1) + })), ownerState.textColor === 'inherit' && (_ref3 = { + color: 'inherit', + opacity: 0.6 + }, defineProperty_defineProperty(_ref3, "&.".concat(Tab_tabClasses.selected), { + opacity: 1 + }), defineProperty_defineProperty(_ref3, "&.".concat(Tab_tabClasses.disabled), { + opacity: (theme.vars || theme).palette.action.disabledOpacity + }), _ref3), ownerState.textColor === 'primary' && (_ref4 = { + color: (theme.vars || theme).palette.text.secondary + }, defineProperty_defineProperty(_ref4, "&.".concat(Tab_tabClasses.selected), { + color: (theme.vars || theme).palette.primary.main + }), defineProperty_defineProperty(_ref4, "&.".concat(Tab_tabClasses.disabled), { + color: (theme.vars || theme).palette.text.disabled + }), _ref4), ownerState.textColor === 'secondary' && (_ref5 = { + color: (theme.vars || theme).palette.text.secondary + }, defineProperty_defineProperty(_ref5, "&.".concat(Tab_tabClasses.selected), { + color: (theme.vars || theme).palette.secondary.main + }), defineProperty_defineProperty(_ref5, "&.".concat(Tab_tabClasses.disabled), { + color: (theme.vars || theme).palette.text.disabled + }), _ref5), ownerState.fullWidth && { + flexShrink: 1, + flexGrow: 1, + flexBasis: 0, + maxWidth: 'none' + }, ownerState.wrapped && { + fontSize: theme.typography.pxToRem(12) }); }); -var defaultComponent = 'table'; -var Table = /*#__PURE__*/react.forwardRef(function Table(inProps, ref) { +var Tab = /*#__PURE__*/react.forwardRef(function Tab(inProps, ref) { var props = useThemeProps_useThemeProps({ props: inProps, - name: 'MuiTable' + name: 'MuiTab' }); var className = props.className, - _props$component = props.component, - component = _props$component === void 0 ? defaultComponent : _props$component, - _props$padding = props.padding, - padding = _props$padding === void 0 ? 'normal' : _props$padding, - _props$size = props.size, - size = _props$size === void 0 ? 'medium' : _props$size, - _props$stickyHeader = props.stickyHeader, - stickyHeader = _props$stickyHeader === void 0 ? false : _props$stickyHeader, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, Table_excluded); + _props$disabled = props.disabled, + disabled = _props$disabled === void 0 ? false : _props$disabled, + _props$disableFocusRi = props.disableFocusRipple, + disableFocusRipple = _props$disableFocusRi === void 0 ? false : _props$disableFocusRi, + fullWidth = props.fullWidth, + iconProp = props.icon, + _props$iconPosition = props.iconPosition, + iconPosition = _props$iconPosition === void 0 ? 'top' : _props$iconPosition, + indicator = props.indicator, + label = props.label, + onChange = props.onChange, + onClick = props.onClick, + onFocus = props.onFocus, + selected = props.selected, + selectionFollowsFocus = props.selectionFollowsFocus, + _props$textColor = props.textColor, + textColor = _props$textColor === void 0 ? 'inherit' : _props$textColor, + value = props.value, + _props$wrapped = props.wrapped, + wrapped = _props$wrapped === void 0 ? false : _props$wrapped, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, Tab_excluded); var ownerState = extends_extends({}, props, { - component: component, - padding: padding, - size: size, - stickyHeader: stickyHeader - }); - var classes = Table_useUtilityClasses(ownerState); - var table = react.useMemo(function () { - return { - padding: padding, - size: size, - stickyHeader: stickyHeader - }; - }, [padding, size, stickyHeader]); - return /*#__PURE__*/(0,jsx_runtime.jsx)(Table_TableContext.Provider, { - value: table, - children: /*#__PURE__*/(0,jsx_runtime.jsx)(TableRoot, extends_extends({ - as: component, - role: component === defaultComponent ? null : 'table', - ref: ref, - className: clsx_m(classes.root, className), - ownerState: ownerState - }, other)) + disabled: disabled, + disableFocusRipple: disableFocusRipple, + selected: selected, + icon: !!iconProp, + iconPosition: iconPosition, + label: !!label, + fullWidth: fullWidth, + textColor: textColor, + wrapped: wrapped }); + var classes = Tab_useUtilityClasses(ownerState); + var icon = iconProp && label && /*#__PURE__*/react.isValidElement(iconProp) ? /*#__PURE__*/react.cloneElement(iconProp, { + className: clsx_m(classes.iconWrapper, iconProp.props.className) + }) : iconProp; + var handleClick = function handleClick(event) { + if (!selected && onChange) { + onChange(event, value); + } + if (onClick) { + onClick(event); + } + }; + var handleFocus = function handleFocus(event) { + if (selectionFollowsFocus && !selected && onChange) { + onChange(event, value); + } + if (onFocus) { + onFocus(event); + } + }; + return /*#__PURE__*/(0,jsx_runtime.jsxs)(TabRoot, extends_extends({ + focusRipple: !disableFocusRipple, + className: clsx_m(classes.root, className), + ref: ref, + role: "tab", + "aria-selected": selected, + disabled: disabled, + onClick: handleClick, + onFocus: handleFocus, + ownerState: ownerState, + tabIndex: selected ? 0 : -1 + }, other, { + children: [iconPosition === 'top' || iconPosition === 'start' ? /*#__PURE__*/(0,jsx_runtime.jsxs)(react.Fragment, { + children: [icon, label] + }) : /*#__PURE__*/(0,jsx_runtime.jsxs)(react.Fragment, { + children: [label, icon] + }), indicator] + })); }); false ? 0 : void 0; -/* harmony default export */ var Table_Table = (Table); -;// CONCATENATED MODULE: ./node_modules/@mui/material/Table/Tablelvl2Context.js +/* harmony default export */ var Tab_Tab = (Tab); +;// CONCATENATED MODULE: ./node_modules/@mui/lab/TabContext/TabContext.js + + /** - * @ignore - internal component. + * @type {React.Context<{ idPrefix: string; value: string } | null>} */ -var Tablelvl2Context = /*#__PURE__*/react.createContext(); -if (false) {} -/* harmony default export */ var Table_Tablelvl2Context = (Tablelvl2Context); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableBody/tableBodyClasses.js +var Context = /*#__PURE__*/react.createContext(null); +if (false) {} +function useUniquePrefix() { + var _React$useState = react.useState(null), + _React$useState2 = slicedToArray_slicedToArray(_React$useState, 2), + id = _React$useState2[0], + setId = _React$useState2[1]; + react.useEffect(function () { + setId("mui-p-".concat(Math.round(Math.random() * 1e5))); + }, []); + return id; +} +function TabContext(props) { + var children = props.children, + value = props.value; + var idPrefix = useUniquePrefix(); + var context = react.useMemo(function () { + return { + idPrefix: idPrefix, + value: value + }; + }, [idPrefix, value]); + return /*#__PURE__*/(0,jsx_runtime.jsx)(Context.Provider, { + value: context, + children: children + }); +} + false ? 0 : void 0; -function getTableBodyUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTableBody', slot); +/** + * @returns {unknown} + */ +function useTabContext() { + return react.useContext(Context); } -var tableBodyClasses = generateUtilityClasses('MuiTableBody', ['root']); -/* harmony default export */ var TableBody_tableBodyClasses = ((/* unused pure expression or super */ null && (tableBodyClasses))); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableBody/TableBody.js +function getPanelId(context, value) { + var idPrefix = context.idPrefix; + if (idPrefix === null) { + return null; + } + return "".concat(context.idPrefix, "-P-").concat(value); +} +function getTabId(context, value) { + var idPrefix = context.idPrefix; + if (idPrefix === null) { + return null; + } + return "".concat(context.idPrefix, "-T-").concat(value); +} +;// CONCATENATED MODULE: ./node_modules/@mui/utils/esm/scrollLeft.js +// Source from https://github.com/alitaheri/normalize-scroll-left +var cachedType; +/** + * Based on the jquery plugin https://github.com/othree/jquery.rtl-scroll-type + * + * Types of scrollLeft, assuming scrollWidth=100 and direction is rtl. + * + * Type | <- Most Left | Most Right -> | Initial + * ---------------- | ------------ | ------------- | ------- + * default | 0 | 100 | 100 + * negative (spec*) | -100 | 0 | 0 + * reverse | 100 | 0 | 0 + * + * Edge 85: default + * Safari 14: negative + * Chrome 85: negative + * Firefox 81: negative + * IE11: reverse + * + * spec* https://drafts.csswg.org/cssom-view/#dom-window-scroll + */ +function detectScrollType() { + if (cachedType) { + return cachedType; + } + var dummy = document.createElement('div'); + var container = document.createElement('div'); + container.style.width = '10px'; + container.style.height = '1px'; + dummy.appendChild(container); + dummy.dir = 'rtl'; + dummy.style.fontSize = '14px'; + dummy.style.width = '4px'; + dummy.style.height = '1px'; + dummy.style.position = 'absolute'; + dummy.style.top = '-1000px'; + dummy.style.overflow = 'scroll'; + document.body.appendChild(dummy); + cachedType = 'reverse'; + if (dummy.scrollLeft > 0) { + cachedType = 'default'; + } else { + dummy.scrollLeft = 1; + if (dummy.scrollLeft === 0) { + cachedType = 'negative'; + } + } + document.body.removeChild(dummy); + return cachedType; +} -var TableBody_excluded = ["className", "component"]; +// Based on https://stackoverflow.com/a/24394376 +function getNormalizedScrollLeft(element, direction) { + var scrollLeft = element.scrollLeft; + // Perform the calculations only when direction is rtl to avoid messing up the ltr behavior + if (direction !== 'rtl') { + return scrollLeft; + } + var type = detectScrollType(); + switch (type) { + case 'negative': + return element.scrollWidth - element.clientWidth + scrollLeft; + case 'reverse': + return element.scrollWidth - element.clientWidth - scrollLeft; + default: + return scrollLeft; + } +} +;// CONCATENATED MODULE: ./node_modules/@mui/material/internal/animate.js +function easeInOutSin(time) { + return (1 + Math.sin(Math.PI * time - Math.PI / 2)) / 2; +} +function animate(property, element, to) { + var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + var cb = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : function () {}; + var _options$ease = options.ease, + ease = _options$ease === void 0 ? easeInOutSin : _options$ease, + _options$duration = options.duration, + duration = _options$duration === void 0 ? 300 : _options$duration; + var start = null; + var from = element[property]; + var cancelled = false; + var cancel = function cancel() { + cancelled = true; + }; + var step = function step(timestamp) { + if (cancelled) { + cb(new Error('Animation cancelled')); + return; + } + if (start === null) { + start = timestamp; + } + var time = Math.min(1, (timestamp - start) / duration); + element[property] = ease(time) * (to - from) + from; + if (time >= 1) { + requestAnimationFrame(function () { + cb(null); + }); + return; + } + requestAnimationFrame(step); + }; + if (from === to) { + cb(new Error('Element already at target position')); + return cancel; + } + requestAnimationFrame(step); + return cancel; +} +;// CONCATENATED MODULE: ./node_modules/@mui/material/Tabs/ScrollbarSize.js +var ScrollbarSize_excluded = ["onChange"]; +var ScrollbarSize_styles = { + width: 99, + height: 99, + position: 'absolute', + top: -9999, + overflow: 'scroll' +}; -var TableBody_useUtilityClasses = function useUtilityClasses(ownerState) { - var classes = ownerState.classes; - var slots = { - root: ['root'] +/** + * @ignore - internal component. + * The component originates from https://github.com/STORIS/react-scrollbar-size. + * It has been moved into the core in order to minimize the bundle size. + */ +function ScrollbarSize(props) { + var onChange = props.onChange, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, ScrollbarSize_excluded); + var scrollbarHeight = react.useRef(); + var nodeRef = react.useRef(null); + var setMeasurements = function setMeasurements() { + scrollbarHeight.current = nodeRef.current.offsetHeight - nodeRef.current.clientHeight; }; - return composeClasses(slots, getTableBodyUtilityClass, classes); -}; -var TableBodyRoot = styles_styled('tbody', { - name: 'MuiTableBody', - slot: 'Root', - overridesResolver: function overridesResolver(props, styles) { - return styles.root; - } -})({ - display: 'table-row-group' -}); -var tablelvl2 = { - variant: 'body' -}; -var TableBody_defaultComponent = 'tbody'; -var TableBody = /*#__PURE__*/react.forwardRef(function TableBody(inProps, ref) { - var props = useThemeProps_useThemeProps({ - props: inProps, - name: 'MuiTableBody' - }); - var className = props.className, - _props$component = props.component, - component = _props$component === void 0 ? TableBody_defaultComponent : _props$component, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableBody_excluded); - var ownerState = extends_extends({}, props, { - component: component - }); - var classes = TableBody_useUtilityClasses(ownerState); - return /*#__PURE__*/(0,jsx_runtime.jsx)(Table_Tablelvl2Context.Provider, { - value: tablelvl2, - children: /*#__PURE__*/(0,jsx_runtime.jsx)(TableBodyRoot, extends_extends({ - className: clsx_m(classes.root, className), - as: component, - ref: ref, - role: component === TableBody_defaultComponent ? null : 'rowgroup', - ownerState: ownerState - }, other)) - }); -}); + utils_useEnhancedEffect(function () { + var handleResize = utils_debounce(function () { + var prevHeight = scrollbarHeight.current; + setMeasurements(); + if (prevHeight !== scrollbarHeight.current) { + onChange(scrollbarHeight.current); + } + }); + var containerWindow = utils_ownerWindow(nodeRef.current); + containerWindow.addEventListener('resize', handleResize); + return function () { + handleResize.clear(); + containerWindow.removeEventListener('resize', handleResize); + }; + }, [onChange]); + react.useEffect(function () { + setMeasurements(); + onChange(scrollbarHeight.current); + }, [onChange]); + return /*#__PURE__*/(0,jsx_runtime.jsx)("div", extends_extends({ + style: ScrollbarSize_styles, + ref: nodeRef + }, other)); +} false ? 0 : void 0; -/* harmony default export */ var TableBody_TableBody = (TableBody); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableCell/tableCellClasses.js +;// CONCATENATED MODULE: ./node_modules/@mui/material/internal/svg-icons/KeyboardArrowLeft.js -function getTableCellUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTableCell', slot); + +/** + * @ignore - internal component. + */ + +/* harmony default export */ var KeyboardArrowLeft = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z" +}), 'KeyboardArrowLeft')); +;// CONCATENATED MODULE: ./node_modules/@mui/material/internal/svg-icons/KeyboardArrowRight.js + + + +/** + * @ignore - internal component. + */ + +/* harmony default export */ var KeyboardArrowRight = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z" +}), 'KeyboardArrowRight')); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TabScrollButton/tabScrollButtonClasses.js + + +function getTabScrollButtonUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTabScrollButton', slot); } -var tableCellClasses = generateUtilityClasses('MuiTableCell', ['root', 'head', 'body', 'footer', 'sizeSmall', 'sizeMedium', 'paddingCheckbox', 'paddingNone', 'alignLeft', 'alignCenter', 'alignRight', 'alignJustify', 'stickyHeader']); -/* harmony default export */ var TableCell_tableCellClasses = (tableCellClasses); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableCell/TableCell.js +var tabScrollButtonClasses = generateUtilityClasses('MuiTabScrollButton', ['root', 'vertical', 'horizontal', 'disabled']); +/* harmony default export */ var TabScrollButton_tabScrollButtonClasses = (tabScrollButtonClasses); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TabScrollButton/TabScrollButton.js -var TableCell_excluded = ["align", "className", "component", "padding", "scope", "size", "sortDirection", "variant"]; +var TabScrollButton_excluded = ["className", "slots", "slotProps", "direction", "orientation", "disabled"]; +/* eslint-disable jsx-a11y/aria-role */ @@ -57375,279 +57723,113 @@ var TableCell_excluded = ["align", "className", "component", "padding", "scope", -var TableCell_useUtilityClasses = function useUtilityClasses(ownerState) { +var TabScrollButton_useUtilityClasses = function useUtilityClasses(ownerState) { var classes = ownerState.classes, - variant = ownerState.variant, - align = ownerState.align, - padding = ownerState.padding, - size = ownerState.size, - stickyHeader = ownerState.stickyHeader; + orientation = ownerState.orientation, + disabled = ownerState.disabled; var slots = { - root: ['root', variant, stickyHeader && 'stickyHeader', align !== 'inherit' && "align".concat(utils_capitalize(align)), padding !== 'normal' && "padding".concat(utils_capitalize(padding)), "size".concat(utils_capitalize(size))] + root: ['root', orientation, disabled && 'disabled'] }; - return composeClasses(slots, getTableCellUtilityClass, classes); + return composeClasses(slots, getTabScrollButtonUtilityClass, classes); }; -var TableCellRoot = styles_styled('td', { - name: 'MuiTableCell', +var TabScrollButtonRoot = styles_styled(ButtonBase_ButtonBase, { + name: 'MuiTabScrollButton', slot: 'Root', overridesResolver: function overridesResolver(props, styles) { var ownerState = props.ownerState; - return [styles.root, styles[ownerState.variant], styles["size".concat(utils_capitalize(ownerState.size))], ownerState.padding !== 'normal' && styles["padding".concat(utils_capitalize(ownerState.padding))], ownerState.align !== 'inherit' && styles["align".concat(utils_capitalize(ownerState.align))], ownerState.stickyHeader && styles.stickyHeader]; + return [styles.root, ownerState.orientation && styles[ownerState.orientation]]; } })(function (_ref) { - var theme = _ref.theme, - ownerState = _ref.ownerState; - return extends_extends({}, theme.typography.body2, { - display: 'table-cell', - verticalAlign: 'inherit', - // Workaround for a rendering bug with spanned columns in Chrome 62.0. - // Removes the alpha (sets it to 1), and lightens or darkens the theme color. - borderBottom: theme.vars ? "1px solid ".concat(theme.vars.palette.TableCell.border) : "1px solid\n ".concat(theme.palette.mode === 'light' ? lighten(alpha(theme.palette.divider, 1), 0.88) : darken(alpha(theme.palette.divider, 1), 0.68)), - textAlign: 'left', - padding: 16 - }, ownerState.variant === 'head' && { - color: (theme.vars || theme).palette.text.primary, - lineHeight: theme.typography.pxToRem(24), - fontWeight: theme.typography.fontWeightMedium - }, ownerState.variant === 'body' && { - color: (theme.vars || theme).palette.text.primary - }, ownerState.variant === 'footer' && { - color: (theme.vars || theme).palette.text.secondary, - lineHeight: theme.typography.pxToRem(21), - fontSize: theme.typography.pxToRem(12) - }, ownerState.size === 'small' && defineProperty_defineProperty({ - padding: '6px 16px' - }, "&.".concat(TableCell_tableCellClasses.paddingCheckbox), { - width: 24, - // prevent the checkbox column from growing - padding: '0 12px 0 16px', - '& > *': { - padding: 0 + var ownerState = _ref.ownerState; + return extends_extends(defineProperty_defineProperty({ + width: 40, + flexShrink: 0, + opacity: 0.8 + }, "&.".concat(TabScrollButton_tabScrollButtonClasses.disabled), { + opacity: 0 + }), ownerState.orientation === 'vertical' && { + width: '100%', + height: 40, + '& svg': { + transform: "rotate(".concat(ownerState.isRtl ? -90 : 90, "deg)") } - }), ownerState.padding === 'checkbox' && { - width: 48, - // prevent the checkbox column from growing - padding: '0 0 0 4px' - }, ownerState.padding === 'none' && { - padding: 0 - }, ownerState.align === 'left' && { - textAlign: 'left' - }, ownerState.align === 'center' && { - textAlign: 'center' - }, ownerState.align === 'right' && { - textAlign: 'right', - flexDirection: 'row-reverse' - }, ownerState.align === 'justify' && { - textAlign: 'justify' - }, ownerState.stickyHeader && { - position: 'sticky', - top: 0, - zIndex: 2, - backgroundColor: (theme.vars || theme).palette.background.default }); }); - -/** - * The component renders a `` element when the parent context is a header - * or otherwise a `` element. - */ -var TableCell = /*#__PURE__*/react.forwardRef(function TableCell(inProps, ref) { +var TabScrollButton = /*#__PURE__*/react.forwardRef(function TabScrollButton(inProps, ref) { + var _slots$StartScrollBut, _slots$EndScrollButto; var props = useThemeProps_useThemeProps({ props: inProps, - name: 'MuiTableCell' + name: 'MuiTabScrollButton' }); - var _props$align = props.align, - align = _props$align === void 0 ? 'inherit' : _props$align, - className = props.className, - componentProp = props.component, - paddingProp = props.padding, - scopeProp = props.scope, - sizeProp = props.size, - sortDirection = props.sortDirection, - variantProp = props.variant, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableCell_excluded); - var table = react.useContext(Table_TableContext); - var tablelvl2 = react.useContext(Table_Tablelvl2Context); - var isHeadCell = tablelvl2 && tablelvl2.variant === 'head'; - var component; - if (componentProp) { - component = componentProp; - } else { - component = isHeadCell ? 'th' : 'td'; - } - var scope = scopeProp; - // scope is not a valid attribute for elements. - // source: https://html.spec.whatwg.org/multipage/tables.html#the-td-element - if (component === 'td') { - scope = undefined; - } else if (!scope && isHeadCell) { - scope = 'col'; - } - var variant = variantProp || tablelvl2 && tablelvl2.variant; - var ownerState = extends_extends({}, props, { - align: align, - component: component, - padding: paddingProp || (table && table.padding ? table.padding : 'normal'), - size: sizeProp || (table && table.size ? table.size : 'medium'), - sortDirection: sortDirection, - stickyHeader: variant === 'head' && table && table.stickyHeader, - variant: variant + var className = props.className, + _props$slots = props.slots, + slots = _props$slots === void 0 ? {} : _props$slots, + _props$slotProps = props.slotProps, + slotProps = _props$slotProps === void 0 ? {} : _props$slotProps, + direction = props.direction, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TabScrollButton_excluded); + var theme = styles_useTheme_useTheme(); + var isRtl = theme.direction === 'rtl'; + var ownerState = extends_extends({ + isRtl: isRtl + }, props); + var classes = TabScrollButton_useUtilityClasses(ownerState); + var StartButtonIcon = (_slots$StartScrollBut = slots.StartScrollButtonIcon) != null ? _slots$StartScrollBut : KeyboardArrowLeft; + var EndButtonIcon = (_slots$EndScrollButto = slots.EndScrollButtonIcon) != null ? _slots$EndScrollButto : KeyboardArrowRight; + var startButtonIconProps = useSlotProps({ + elementType: StartButtonIcon, + externalSlotProps: slotProps.startScrollButtonIcon, + additionalProps: { + fontSize: 'small' + }, + ownerState: ownerState }); - var classes = TableCell_useUtilityClasses(ownerState); - var ariaSort = null; - if (sortDirection) { - ariaSort = sortDirection === 'asc' ? 'ascending' : 'descending'; - } - return /*#__PURE__*/(0,jsx_runtime.jsx)(TableCellRoot, extends_extends({ - as: component, - ref: ref, - className: clsx_m(classes.root, className), - "aria-sort": ariaSort, - scope: scope, + var endButtonIconProps = useSlotProps({ + elementType: EndButtonIcon, + externalSlotProps: slotProps.endScrollButtonIcon, + additionalProps: { + fontSize: 'small' + }, ownerState: ownerState - }, other)); -}); - false ? 0 : void 0; -/* harmony default export */ var TableCell_TableCell = (TableCell); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableContainer/tableContainerClasses.js - - -function getTableContainerUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTableContainer', slot); -} -var tableContainerClasses = generateUtilityClasses('MuiTableContainer', ['root']); -/* harmony default export */ var TableContainer_tableContainerClasses = ((/* unused pure expression or super */ null && (tableContainerClasses))); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableContainer/TableContainer.js - - -var TableContainer_excluded = ["className", "component"]; - - - - - - - - -var TableContainer_useUtilityClasses = function useUtilityClasses(ownerState) { - var classes = ownerState.classes; - var slots = { - root: ['root'] - }; - return composeClasses(slots, getTableContainerUtilityClass, classes); -}; -var TableContainerRoot = styles_styled('div', { - name: 'MuiTableContainer', - slot: 'Root', - overridesResolver: function overridesResolver(props, styles) { - return styles.root; - } -})({ - width: '100%', - overflowX: 'auto' -}); -var TableContainer = /*#__PURE__*/react.forwardRef(function TableContainer(inProps, ref) { - var props = useThemeProps_useThemeProps({ - props: inProps, - name: 'MuiTableContainer' - }); - var className = props.className, - _props$component = props.component, - component = _props$component === void 0 ? 'div' : _props$component, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableContainer_excluded); - var ownerState = extends_extends({}, props, { - component: component }); - var classes = TableContainer_useUtilityClasses(ownerState); - return /*#__PURE__*/(0,jsx_runtime.jsx)(TableContainerRoot, extends_extends({ - ref: ref, - as: component, + return /*#__PURE__*/(0,jsx_runtime.jsx)(TabScrollButtonRoot, extends_extends({ + component: "div", className: clsx_m(classes.root, className), - ownerState: ownerState - }, other)); + ref: ref, + role: null, + ownerState: ownerState, + tabIndex: null + }, other, { + children: direction === 'left' ? /*#__PURE__*/(0,jsx_runtime.jsx)(StartButtonIcon, extends_extends({}, startButtonIconProps)) : /*#__PURE__*/(0,jsx_runtime.jsx)(EndButtonIcon, extends_extends({}, endButtonIconProps)) + })); }); false ? 0 : void 0; -/* harmony default export */ var TableContainer_TableContainer = (TableContainer); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableHead/tableHeadClasses.js +/* harmony default export */ var TabScrollButton_TabScrollButton = (TabScrollButton); +;// CONCATENATED MODULE: ./node_modules/@mui/material/Tabs/tabsClasses.js -function getTableHeadUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTableHead', slot); +function getTabsUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTabs', slot); } -var tableHeadClasses = generateUtilityClasses('MuiTableHead', ['root']); -/* harmony default export */ var TableHead_tableHeadClasses = ((/* unused pure expression or super */ null && (tableHeadClasses))); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableHead/TableHead.js - +var tabsClasses = generateUtilityClasses('MuiTabs', ['root', 'vertical', 'flexContainer', 'flexContainerVertical', 'centered', 'scroller', 'fixed', 'scrollableX', 'scrollableY', 'hideScrollbar', 'scrollButtons', 'scrollButtonsHideMobile', 'indicator']); +/* harmony default export */ var Tabs_tabsClasses = (tabsClasses); +;// CONCATENATED MODULE: ./node_modules/@mui/material/Tabs/Tabs.js -var TableHead_excluded = ["className", "component"]; +var Tabs_excluded = ["aria-label", "aria-labelledby", "action", "centered", "children", "className", "component", "allowScrollButtonsMobile", "indicatorColor", "onChange", "orientation", "ScrollButtonComponent", "scrollButtons", "selectionFollowsFocus", "slots", "slotProps", "TabIndicatorProps", "TabScrollButtonProps", "textColor", "value", "variant", "visibleScrollbar"]; -var TableHead_useUtilityClasses = function useUtilityClasses(ownerState) { - var classes = ownerState.classes; - var slots = { - root: ['root'] - }; - return composeClasses(slots, getTableHeadUtilityClass, classes); -}; -var TableHeadRoot = styles_styled('thead', { - name: 'MuiTableHead', - slot: 'Root', - overridesResolver: function overridesResolver(props, styles) { - return styles.root; - } -})({ - display: 'table-header-group' -}); -var TableHead_tablelvl2 = { - variant: 'head' -}; -var TableHead_defaultComponent = 'thead'; -var TableHead = /*#__PURE__*/react.forwardRef(function TableHead(inProps, ref) { - var props = useThemeProps_useThemeProps({ - props: inProps, - name: 'MuiTableHead' - }); - var className = props.className, - _props$component = props.component, - component = _props$component === void 0 ? TableHead_defaultComponent : _props$component, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableHead_excluded); - var ownerState = extends_extends({}, props, { - component: component - }); - var classes = TableHead_useUtilityClasses(ownerState); - return /*#__PURE__*/(0,jsx_runtime.jsx)(Table_Tablelvl2Context.Provider, { - value: TableHead_tablelvl2, - children: /*#__PURE__*/(0,jsx_runtime.jsx)(TableHeadRoot, extends_extends({ - as: component, - className: clsx_m(classes.root, className), - ref: ref, - role: component === TableHead_defaultComponent ? null : 'rowgroup', - ownerState: ownerState - }, other)) - }); -}); - false ? 0 : void 0; -/* harmony default export */ var TableHead_TableHead = (TableHead); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableRow/tableRowClasses.js -function getTableRowUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTableRow', slot); -} -var tableRowClasses = generateUtilityClasses('MuiTableRow', ['root', 'selected', 'hover', 'head', 'footer']); -/* harmony default export */ var TableRow_tableRowClasses = (tableRowClasses); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TableRow/TableRow.js -var TableRow_excluded = ["className", "component", "hover", "selected"]; @@ -57657,752 +57839,773 @@ var TableRow_excluded = ["className", "component", "hover", "selected"]; +var Tabs_nextItem = function nextItem(list, item) { + if (list === item) { + return list.firstChild; + } + if (item && item.nextElementSibling) { + return item.nextElementSibling; + } + return list.firstChild; +}; +var Tabs_previousItem = function previousItem(list, item) { + if (list === item) { + return list.lastChild; + } + if (item && item.previousElementSibling) { + return item.previousElementSibling; + } + return list.lastChild; +}; +var Tabs_moveFocus = function moveFocus(list, currentFocus, traversalFunction) { + var wrappedOnce = false; + var nextFocus = traversalFunction(list, currentFocus); + while (nextFocus) { + // Prevent infinite loop. + if (nextFocus === list.firstChild) { + if (wrappedOnce) { + return; + } + wrappedOnce = true; + } -var TableRow_useUtilityClasses = function useUtilityClasses(ownerState) { - var classes = ownerState.classes, - selected = ownerState.selected, - hover = ownerState.hover, - head = ownerState.head, - footer = ownerState.footer; + // Same logic as useAutocomplete.js + var nextFocusDisabled = nextFocus.disabled || nextFocus.getAttribute('aria-disabled') === 'true'; + if (!nextFocus.hasAttribute('tabindex') || nextFocusDisabled) { + // Move to the next element. + nextFocus = traversalFunction(list, nextFocus); + } else { + nextFocus.focus(); + return; + } + } +}; +var Tabs_useUtilityClasses = function useUtilityClasses(ownerState) { + var vertical = ownerState.vertical, + fixed = ownerState.fixed, + hideScrollbar = ownerState.hideScrollbar, + scrollableX = ownerState.scrollableX, + scrollableY = ownerState.scrollableY, + centered = ownerState.centered, + scrollButtonsHideMobile = ownerState.scrollButtonsHideMobile, + classes = ownerState.classes; var slots = { - root: ['root', selected && 'selected', hover && 'hover', head && 'head', footer && 'footer'] + root: ['root', vertical && 'vertical'], + scroller: ['scroller', fixed && 'fixed', hideScrollbar && 'hideScrollbar', scrollableX && 'scrollableX', scrollableY && 'scrollableY'], + flexContainer: ['flexContainer', vertical && 'flexContainerVertical', centered && 'centered'], + indicator: ['indicator'], + scrollButtons: ['scrollButtons', scrollButtonsHideMobile && 'scrollButtonsHideMobile'], + scrollableX: [scrollableX && 'scrollableX'], + hideScrollbar: [hideScrollbar && 'hideScrollbar'] }; - return composeClasses(slots, getTableRowUtilityClass, classes); + return composeClasses(slots, getTabsUtilityClass, classes); }; -var TableRowRoot = styles_styled('tr', { - name: 'MuiTableRow', +var TabsRoot = styles_styled('div', { + name: 'MuiTabs', slot: 'Root', overridesResolver: function overridesResolver(props, styles) { var ownerState = props.ownerState; - return [styles.root, ownerState.head && styles.head, ownerState.footer && styles.footer]; + return [defineProperty_defineProperty({}, "& .".concat(Tabs_tabsClasses.scrollButtons), styles.scrollButtons), defineProperty_defineProperty({}, "& .".concat(Tabs_tabsClasses.scrollButtons), ownerState.scrollButtonsHideMobile && styles.scrollButtonsHideMobile), styles.root, ownerState.vertical && styles.vertical]; } -})(function (_ref) { - var _ref2; - var theme = _ref.theme; - return _ref2 = { - color: 'inherit', - display: 'table-row', - verticalAlign: 'middle', - // We disable the focus ring for mouse, touch and keyboard users. - outline: 0 - }, defineProperty_defineProperty(_ref2, "&.".concat(TableRow_tableRowClasses.hover, ":hover"), { - backgroundColor: (theme.vars || theme).palette.action.hover - }), defineProperty_defineProperty(_ref2, "&.".concat(TableRow_tableRowClasses.selected), { - backgroundColor: theme.vars ? "rgba(".concat(theme.vars.palette.primary.mainChannel, " / ").concat(theme.vars.palette.action.selectedOpacity, ")") : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), - '&:hover': { - backgroundColor: theme.vars ? "rgba(".concat(theme.vars.palette.primary.mainChannel, " / calc(").concat(theme.vars.palette.action.selectedOpacity, " + ").concat(theme.vars.palette.action.hoverOpacity, "))") : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity) - } - }), _ref2; +})(function (_ref3) { + var ownerState = _ref3.ownerState, + theme = _ref3.theme; + return extends_extends({ + overflow: 'hidden', + minHeight: 48, + // Add iOS momentum scrolling for iOS < 13.0 + WebkitOverflowScrolling: 'touch', + display: 'flex' + }, ownerState.vertical && { + flexDirection: 'column' + }, ownerState.scrollButtonsHideMobile && defineProperty_defineProperty({}, "& .".concat(Tabs_tabsClasses.scrollButtons), defineProperty_defineProperty({}, theme.breakpoints.down('sm'), { + display: 'none' + }))); }); -var TableRow_defaultComponent = 'tr'; -/** - * Will automatically set dynamic row height - * based on the material table element parent (head, body, etc). - */ -var TableRow = /*#__PURE__*/react.forwardRef(function TableRow(inProps, ref) { - var props = useThemeProps_useThemeProps({ - props: inProps, - name: 'MuiTableRow' - }); - var className = props.className, - _props$component = props.component, - component = _props$component === void 0 ? TableRow_defaultComponent : _props$component, - _props$hover = props.hover, - hover = _props$hover === void 0 ? false : _props$hover, - _props$selected = props.selected, - selected = _props$selected === void 0 ? false : _props$selected, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableRow_excluded); - var tablelvl2 = react.useContext(Table_Tablelvl2Context); - var ownerState = extends_extends({}, props, { - component: component, - hover: hover, - selected: selected, - head: tablelvl2 && tablelvl2.variant === 'head', - footer: tablelvl2 && tablelvl2.variant === 'footer' - }); - var classes = TableRow_useUtilityClasses(ownerState); - return /*#__PURE__*/(0,jsx_runtime.jsx)(TableRowRoot, extends_extends({ - as: component, - ref: ref, - className: clsx_m(classes.root, className), - role: component === TableRow_defaultComponent ? null : 'row', - ownerState: ownerState - }, other)); -}); - false ? 0 : void 0; -/* harmony default export */ var TableRow_TableRow = (TableRow); -// EXTERNAL MODULE: ./node_modules/js-sha256/src/sha256.js -var sha256 = __webpack_require__(981); -;// CONCATENATED MODULE: ./src/utils/listKey.ts -function buildListKey(o){var j=JSON.stringify(o);return (0,sha256.sha256)(j);} -;// CONCATENATED MODULE: ./src/components/result-view/ChangesTable.tsx -var RefList=function RefList(props){return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:props.title}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Kind"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Namespace"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Name"})})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:props.refs.map(function(ref){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:buildRefKindElement(ref)}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.namespace})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.name})})]},buildRefString(ref));})})]})})]});};function ChangesTable(props){var changedObjects;if(props.diffStatus.changedObjects.length){changedObjects=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:"Changed Objects"}),props.diffStatus.changedObjects.map(function(co){var _co$changes;return/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(TableHead_TableHead,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableRow_TableRow,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",colSpan:2,sx:{padding:'24px 16px 5px 16px'},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{sx:{fontWeight:500,fontSize:'20px',lineHeight:'27px',letterSpacing:'1px'},children:buildRefString(co.ref)})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Path"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Changes"})]})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_co$changes=co.changes)===null||_co$changes===void 0?void 0:_co$changes.map(function(c){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{minWidth:"100px",sx:{overflowWrap:"anywhere"},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:c.jsonPath})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:c.unifiedDiff||"",language:"diff"})})]},buildListKey(c));})})]})},buildRefString(co.ref));})]});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{width:"100%",display:"flex",flexDirection:"column",children:[props.diffStatus.newObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"New Objects",refs:props.diffStatus.newObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.deletedObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Deleted Objects",refs:props.diffStatus.deletedObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.orphanObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Orphan Objects",refs:props.diffStatus.orphanObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),changedObjects]});} -;// CONCATENATED MODULE: ./src/components/ErrorsTable.tsx -function ErrorsTable(props){var _props$errors;return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Ref"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Message"})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_props$errors=props.errors)===null||_props$errors===void 0?void 0:_props$errors.map(function(e){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{sx:{minWidth:"150px"},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(List_List,{disablePadding:true,children:[buildRefKindElement(e.ref,/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.kind})})})),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.name})}),e.ref.namespace&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.namespace})})]})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:e.message})]},buildListKey(e));})})]})})})});} -;// CONCATENATED MODULE: ./src/components/ObjectYaml.tsx -var ObjectYaml=function ObjectYaml(props){var api=(0,react.useContext)(ApiContext);var _useLoadingHelper=useLoadingHelper(/*#__PURE__*/asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var o;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return api.getResultObject(props.treeProps.summary.id,props.objectRef,props.objectType);case 2:o=_context.sent;return _context.abrupt("return",dump(o));case 4:case"end":return _context.stop();}},_callee);})),[props.treeProps.summary.id,props.objectRef,props.objectType]),_useLoadingHelper2=slicedToArray_slicedToArray(_useLoadingHelper,3),loading=_useLoadingHelper2[0],error=_useLoadingHelper2[1],content=_useLoadingHelper2[2];if(loading){return/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{});}else if(error){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:"Error"});}else{return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:content,language:"yaml"});}}; -;// CONCATENATED MODULE: ./src/components/result-view/nodes/NodeData.tsx -var DiffStatus=/*#__PURE__*/function(){function DiffStatus(){classCallCheck_classCallCheck(this,DiffStatus);this.newObjects=[];this.deletedObjects=[];this.orphanObjects=[];this.changedObjects=[];this.totalInsertions=0;this.totalDeletions=0;this.totalUpdates=0;}createClass_createClass(DiffStatus,[{key:"addChangedObject",value:function addChangedObject(co){var _co$changes,_this=this;this.changedObjects.push(co);(_co$changes=co.changes)===null||_co$changes===void 0?void 0:_co$changes.forEach(function(x){switch(x.type){case"insert":_this.totalInsertions++;break;case"delete":_this.totalDeletions++;break;case"update":_this.totalUpdates++;break;}});}},{key:"merge",value:function merge(other){this.newObjects=this.newObjects.concat(other.newObjects);this.deletedObjects=this.deletedObjects.concat(other.deletedObjects);this.orphanObjects=this.orphanObjects.concat(other.orphanObjects);this.changedObjects=this.changedObjects.concat(other.changedObjects);this.totalInsertions+=other.totalInsertions;this.totalDeletions+=other.totalDeletions;this.totalUpdates+=other.totalUpdates;}},{key:"hasDiffs",value:function hasDiffs(){if(this.newObjects.length||this.deletedObjects.length||this.orphanObjects.length){return true;}if(this.changedObjects.find(function(co){var _co$changes2;return((_co$changes2=co.changes)===null||_co$changes2===void 0?void 0:_co$changes2.length)!==0;})){return true;}return false;}}]);return DiffStatus;}();var HealthStatus=/*#__PURE__*/function(){function HealthStatus(){classCallCheck_classCallCheck(this,HealthStatus);this.errors=[];this.warnings=[];}createClass_createClass(HealthStatus,[{key:"merge",value:function merge(other){this.errors=this.errors.concat(other.errors);this.warnings=this.warnings.concat(other.warnings);}}]);return HealthStatus;}();var NodeData=/*#__PURE__*/function(){function NodeData(props,id,hasHealthStatus,hasDiffStatus){classCallCheck_classCallCheck(this,NodeData);this.props=void 0;this.id=void 0;this.children=[];this.healthStatus=void 0;this.diffStatus=void 0;this.props=props;this.id=id;if(hasHealthStatus){this.healthStatus=new HealthStatus();}if(hasDiffStatus){this.diffStatus=new DiffStatus();}}createClass_createClass(NodeData,[{key:"pushChild",value:function pushChild(child,front){if(front){this.children.unshift(child);}else{this.children.push(child);}this.merge(child);}},{key:"merge",value:function merge(other){if(this.diffStatus&&other.diffStatus){this.diffStatus.merge(other.diffStatus);}if(this.healthStatus&&other.healthStatus){this.healthStatus.merge(other.healthStatus);}}},{key:"buildStatusLine",value:function buildStatusLine(){var _this$healthStatus,_this$healthStatus2,_this$diffStatus,_this$diffStatus2,_this$diffStatus3,_this$diffStatus4;return/*#__PURE__*/(0,jsx_runtime.jsx)(StatusLine,{errors:(_this$healthStatus=this.healthStatus)===null||_this$healthStatus===void 0?void 0:_this$healthStatus.errors.length,warnings:(_this$healthStatus2=this.healthStatus)===null||_this$healthStatus2===void 0?void 0:_this$healthStatus2.warnings.length,changedObjects:(_this$diffStatus=this.diffStatus)===null||_this$diffStatus===void 0?void 0:_this$diffStatus.changedObjects.length,newObjects:(_this$diffStatus2=this.diffStatus)===null||_this$diffStatus2===void 0?void 0:_this$diffStatus2.newObjects.length,deletedObjects:(_this$diffStatus3=this.diffStatus)===null||_this$diffStatus3===void 0?void 0:_this$diffStatus3.deletedObjects.length,orphanObjects:(_this$diffStatus4=this.diffStatus)===null||_this$diffStatus4===void 0?void 0:_this$diffStatus4.orphanObjects.length});}},{key:"buildTreeItem",value:function buildTreeItem(hasChildren){var _this$healthStatus3,_this$healthStatus4,_this$diffStatus5,_this$diffStatus6,_this$diffStatus7,_this$diffStatus8;var _this$buildIcon=this.buildIcon(),_this$buildIcon2=slicedToArray_slicedToArray(_this$buildIcon,2),icon=_this$buildIcon2[0],iconText=_this$buildIcon2[1];var hasStatusLine=[(_this$healthStatus3=this.healthStatus)===null||_this$healthStatus3===void 0?void 0:_this$healthStatus3.errors.length,(_this$healthStatus4=this.healthStatus)===null||_this$healthStatus4===void 0?void 0:_this$healthStatus4.warnings.length,(_this$diffStatus5=this.diffStatus)===null||_this$diffStatus5===void 0?void 0:_this$diffStatus5.changedObjects.length,(_this$diffStatus6=this.diffStatus)===null||_this$diffStatus6===void 0?void 0:_this$diffStatus6.newObjects.length,(_this$diffStatus7=this.diffStatus)===null||_this$diffStatus7===void 0?void 0:_this$diffStatus7.deletedObjects.length,(_this$diffStatus8=this.diffStatus)===null||_this$diffStatus8===void 0?void 0:_this$diffStatus8.orphanObjects.length].some(function(x){return(x||0)>0;});return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",height:"100%",flex:"1 1 auto",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",width:"30px",height:"100%",flex:"0 0 auto",mr:"13px",sx:{'& svg':{width:'30px',height:'30px'}},children:[icon,/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",component:"div",fontSize:"12px",fontWeight:400,lineHeight:"16px",height:"16px",children:iconText})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",height:"100%",flex:"1 1 auto",py:"15px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,_objectSpread2(_objectSpread2({variant:"h6",component:"div",sx:{wordBreak:'break-all'}},hasChildren?{}:{fontSize:'16px',lineHeight:'22px'}),{},{children:this.buildSidePanelTitle()}))}),hasStatusLine&&/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{height:"100%",width:"172px",flex:"0 0 auto",display:"flex",alignItems:"center",px:"14px",ml:"14px",position:"relative",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{orientation:"vertical",sx:{height:'40px',position:'absolute',left:0}}),this.buildStatusLine()]})]});}},{key:"buildDiffAndHealthPages",value:function buildDiffAndHealthPages(tabs){this.buildChangesPage(tabs);this.buildErrorsPage(tabs);this.buildWarningsPage(tabs);}},{key:"buildObjectPage",value:function buildObjectPage(ref,objectType){return/*#__PURE__*/(0,jsx_runtime.jsx)(ObjectYaml,{treeProps:this.props,objectRef:ref,objectType:objectType});}},{key:"buildChangesPage",value:function buildChangesPage(tabs){var _this$diffStatus9;if(!((_this$diffStatus9=this.diffStatus)!==null&&_this$diffStatus9!==void 0&&_this$diffStatus9.hasDiffs())){return undefined;}tabs.push({label:"Changes",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ChangesTable,{diffStatus:this.diffStatus})});}},{key:"buildErrorsPage",value:function buildErrorsPage(tabs){var _this$healthStatus5;if(!((_this$healthStatus5=this.healthStatus)!==null&&_this$healthStatus5!==void 0&&_this$healthStatus5.errors.length)){return undefined;}tabs.push({label:"Errors",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.healthStatus.errors})});}},{key:"buildWarningsPage",value:function buildWarningsPage(tabs){var _this$healthStatus6;if(!((_this$healthStatus6=this.healthStatus)!==null&&_this$healthStatus6!==void 0&&_this$healthStatus6.warnings.length)){return undefined;}tabs.push({label:"Warnings",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.healthStatus.warnings})});}}]);return NodeData;}(); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Category.js - - -/* harmony default export */ var Category = (createSvgIcon([/*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "m12 2-5.5 9h11z" -}, "0"), /*#__PURE__*/(0,jsx_runtime.jsx)("circle", { - cx: "17.5", - cy: "17.5", - r: "4.5" -}, "1"), /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M3 13.5h8v8H3z" -}, "2")], 'Category')); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Settings.js - - -/* harmony default export */ var Settings = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z" -}), 'Settings')); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Lock.js - - -/* harmony default export */ var Lock = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" -}), 'Lock')); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Dvr.js - - -/* harmony default export */ var Dvr = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12zm-2-9H8v2h11V8zm0 4H8v2h11v-2zM7 8H5v2h2V8zm0 4H5v2h2v-2z" -}), 'Dvr')); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Http.js - - -/* harmony default export */ var Http = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M4.5 11h-2V9H1v6h1.5v-2.5h2V15H6V9H4.5v2zm2.5-.5h1.5V15H10v-4.5h1.5V9H7v1.5zm5.5 0H14V15h1.5v-4.5H17V9h-4.5v1.5zm9-1.5H18v6h1.5v-2h2c.8 0 1.5-.7 1.5-1.5v-1c0-.8-.7-1.5-1.5-1.5zm0 2.5h-2v-1h2v1z" -}), 'Http')); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Cloud.js - - -/* harmony default export */ var Cloud = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z" -}), 'Cloud')); -;// CONCATENATED MODULE: ./src/components/PropertiesTable.tsx -function PropertiesTable(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Name"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Value"})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:props.properties.map(function(row){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:row.name}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:row.value})]},row.name);})})]})});} -;// CONCATENATED MODULE: ./src/components/result-view/nodes/VarsSourceNode.tsx -var VarsSourceNodeData=/*#__PURE__*/function(_NodeData){_inherits(VarsSourceNodeData,_NodeData);var _super=_createSuper(VarsSourceNodeData);function VarsSourceNodeData(props,id,varsSource){var _this$varsSource$clus;var _this;classCallCheck_classCallCheck(this,VarsSourceNodeData);_this=_super.call(this,props,id,false,false);_this.varsSource=void 0;_this.labelsYaml=void 0;_this.renderedVarsYaml=void 0;_this.varsSource=varsSource;var labels=(_this$varsSource$clus=_this.varsSource.clusterConfigMap)===null||_this$varsSource$clus===void 0?void 0:_this$varsSource$clus.labels;if(!labels){var _this$varsSource$clus2;labels=(_this$varsSource$clus2=_this.varsSource.clusterSecret)===null||_this$varsSource$clus2===void 0?void 0:_this$varsSource$clus2.labels;}if(labels){_this.labelsYaml=dump(labels);}_this.renderedVarsYaml=dump(_this.varsSource.renderedVars);return _this;}createClass_createClass(VarsSourceNodeData,[{key:"getVarsSourceHandler",value:function getVarsSourceHandler(){var _this2=this;if(this.varsSource.values){return{type:"values",label:function label(){if(_this2.varsSource.renderedVars){return"values: "+Object.keys(_this2.varsSource.renderedVars).length;}else{return"empty values";}},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Category,{fontSize:"large"});},sourceProps:function sourceProps(){return[];}};}else if(this.varsSource.file){return{type:"file",label:function label(){return _this2.varsSource.file;},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(FileIcon,{});},sourceProps:function sourceProps(){return[{name:"File",value:_this2.varsSource.file}];}};}else if(this.varsSource.git){return{type:"git",label:function label(){var s=_this2.varsSource.git.url.split("/");var name=s[s.length-1];return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[name,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),_this2.varsSource.git.path]});},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(GitIcon,{});},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"Url",value:_this2.varsSource.git.url});sourceProps.push({name:"Path",value:_this2.varsSource.git.path});var ref="HEAD";if(_this2.varsSource.git.ref){ref=_this2.varsSource.git.ref;}sourceProps.push({name:"Ref",value:ref});return sourceProps;}};}else if(this.varsSource.clusterConfigMap||this.varsSource.clusterSecret){var vs=this.varsSource.clusterConfigMap?this.varsSource.clusterConfigMap:this.varsSource.clusterSecret;var type=this.varsSource.clusterConfigMap?"cm":"secret";var _icon=this.varsSource.clusterConfigMap?/*#__PURE__*/(0,jsx_runtime.jsx)(Settings,{fontSize:"large"}):/*#__PURE__*/(0,jsx_runtime.jsx)(Lock,{fontSize:"large"});return{type:type,label:function label(){var _this2$varsSource$clu;return(_this2$varsSource$clu=_this2.varsSource.clusterConfigMap)===null||_this2$varsSource$clu===void 0?void 0:_this2$varsSource$clu.name;},icon:function icon(){return _icon;},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"Name",value:vs.name});sourceProps.push({name:"Namespace",value:vs.namespace});sourceProps.push({name:"Key",value:vs.key});if(vs.labels){sourceProps.push({name:"Labels",value:/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:_this2.labelsYaml,language:"yaml"})});}return sourceProps;}};}else if(this.varsSource.systemEnvVars){return{type:"systemEnvVar",label:function label(){return"systemEnvVars";},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Dvr,{fontSize:"large"});},sourceProps:function sourceProps(){// TODO -return[];}};}else if(this.varsSource.http){return{type:"http",label:function label(){return _this2.varsSource.http.url;},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Http,{fontSize:"large"});},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"Url",value:_this2.varsSource.http.url});sourceProps.push({name:"Method",value:_this2.varsSource.http.method||"GET"});if(_this2.varsSource.http.jsonPath){sourceProps.push({name:"JsonPath",value:_this2.varsSource.http.jsonPath});}return sourceProps;}};}else if(this.varsSource.awsSecretsManager){return{type:"awsSecretsManager",label:function label(){return _this2.varsSource.awsSecretsManager.secretName;},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Cloud,{fontSize:"large"});},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"SecretName",value:_this2.varsSource.awsSecretsManager.secretName});if(_this2.varsSource.awsSecretsManager.region){sourceProps.push({name:"Region",value:_this2.varsSource.awsSecretsManager.region});}if(_this2.varsSource.awsSecretsManager.profile){sourceProps.push({name:"Profile",value:_this2.varsSource.awsSecretsManager.profile});}return sourceProps;}};}else if(this.varsSource.vault){return{type:"vault",label:function label(){return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[_this2.varsSource.vault.address,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),_this2.varsSource.vault.path]});},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Cloud,{fontSize:"large"});},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"Address",value:_this2.varsSource.vault.address});sourceProps.push({name:"Path",value:_this2.varsSource.vault.path});return sourceProps;}};}else{return{type:"unknown",label:function label(){return"values: "+Object.keys(_this2.varsSource.renderedVars).length;},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Category,{fontSize:"large"});},sourceProps:function sourceProps(){return[];}};}}},{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return this.getVarsSourceHandler().label();}},{key:"buildIcon",value:function buildIcon(){var h=this.getVarsSourceHandler();return[h.icon(),h.type];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()},{label:"Vars",content:this.buildVarsPage()}];this.buildDiffAndHealthPages(tabs);return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var h=this.getVarsSourceHandler();var props=[{name:"Type",value:h.type}].concat(toConsumableArray_toConsumableArray(h.sourceProps()),[{name:"IgnoreMissing",value:!!this.varsSource.ignoreMissing+""},{name:"NoOverride",value:!!this.varsSource.noOverride+""}]);if(this.varsSource.when){props.push({name:"When",value:this.varsSource.when});}return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}},{key:"buildVarsPage",value:function buildVarsPage(){return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{sx:{overflowY:'scroll',// Enable vertical scrolling -//maxHeight: '400px', // Set a fixed height -border:'1px solid #ccc',// Optional border -padding:'10px'// Optional padding -},children:/*#__PURE__*/(0,jsx_runtime.jsx)("pre",{children:/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:this.renderedVarsYaml,language:"yaml"})})});}}]);return VarsSourceNodeData;}(NodeData); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Source.js - - -/* harmony default export */ var Source = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-6 10H6v-2h8v2zm4-4H6v-2h12v2z" -}), 'Source')); -;// CONCATENATED MODULE: ./src/components/result-view/nodes/DeploymentItemNode.tsx -var DeploymentItemNodeData=/*#__PURE__*/function(_NodeData){_inherits(DeploymentItemNodeData,_NodeData);var _super=_createSuper(DeploymentItemNodeData);function DeploymentItemNodeData(props,id,deploymentItem){var _this;classCallCheck_classCallCheck(this,DeploymentItemNodeData);_this=_super.call(this,props,id,true,true);_this.deploymentItem=void 0;_this.deploymentItem=deploymentItem;return _this;}createClass_createClass(DeploymentItemNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return this.deploymentItem.path;}},{key:"buildIcon",value:function buildIcon(){var iconText="di";return[/*#__PURE__*/(0,jsx_runtime.jsx)(Source,{fontSize:"large"}),iconText];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()}];this.buildDiffAndHealthPages(tabs);return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var props=[];props.push({name:"Path",value:this.deploymentItem.path});buildDeploymentItemSummaryProps(this.deploymentItem,props);return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}}]);return DeploymentItemNodeData;}(NodeData);function buildDeploymentItemSummaryProps(di,props){if(di.barrier!==undefined){props.push({name:"Barrier",value:di.barrier+""});}if(di.waitReadiness!==undefined){props.push({name:"WaitReadiness",value:di.waitReadiness+""});}if(di.skipDeleteIfTags!==undefined){props.push({name:"SkipDeleteIfTags",value:di.skipDeleteIfTags+""});}if(di.onlyRender!==undefined){props.push({name:"OnlyRender",value:di.onlyRender+""});}if(di.alwaysDeploy!==undefined){props.push({name:"AlwaysDeploy",value:di.alwaysDeploy+""});}if(di.deleteObjects){// TODO this is ugly -props.push({name:"DeleteObjects",value:JSON.stringify(di.deleteObjects)});}if(di.when){props.push({name:"When",value:di.when});}} -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/SettingsEthernet.js - - -/* harmony default export */ var SettingsEthernet = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M7.77 6.76 6.23 5.48.82 12l5.41 6.52 1.54-1.28L3.42 12l4.35-5.24zM7 13h2v-2H7v2zm10-2h-2v2h2v-2zm-6 2h2v-2h-2v2zm6.77-7.52-1.54 1.28L20.58 12l-4.35 5.24 1.54 1.28L23.18 12l-5.41-6.52z" -}), 'SettingsEthernet')); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/SmartToy.js - - -/* harmony default export */ var SmartToy = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M20 9V7c0-1.1-.9-2-2-2h-3c0-1.66-1.34-3-3-3S9 3.34 9 5H6c-1.1 0-2 .9-2 2v2c-1.66 0-3 1.34-3 3s1.34 3 3 3v4c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-4c1.66 0 3-1.34 3-3s-1.34-3-3-3zM7.5 11.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5S9.83 13 9 13s-1.5-.67-1.5-1.5zM16 17H8v-2h8v2zm-1-4c-.83 0-1.5-.67-1.5-1.5S14.17 10 15 10s1.5.67 1.5 1.5S15.83 13 15 13z" -}), 'SmartToy')); -;// CONCATENATED MODULE: ./src/components/result-view/nodes/ObjectNode.tsx -var kindMapping={"/ConfigMap":{icon:Settings},"apps/Deployment":{icon:PublishedWithChanges},"Service":{icon:SettingsEthernet},"ServiceAccount":{icon:SmartToy}};var ObjectNodeData=/*#__PURE__*/function(_NodeData){_inherits(ObjectNodeData,_NodeData);var _super=_createSuper(ObjectNodeData);function ObjectNodeData(props,id,objectRef){var _this;classCallCheck_classCallCheck(this,ObjectNodeData);_this=_super.call(this,props,id,true,true);_this.objectRef=void 0;_this.objectRef=objectRef;return _this;}createClass_createClass(ObjectNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return this.objectRef.name;}},{key:"buildIcon",value:function buildIcon(){var _this2=this;var sn=this.props.shortNames.find(function(sn){return sn.group===_this2.objectRef.group&&sn.kind===_this2.objectRef.kind;});var snStr=(sn===null||sn===void 0?void 0:sn.shortName)||"";var m=kindMapping[this.objectRef.group+"/"+this.objectRef.kind];if(m!==undefined){return[/*#__PURE__*/react.createElement(m.icon,{fontSize:"large"}),snStr];}return[/*#__PURE__*/(0,jsx_runtime.jsx)(BracketsCurlyIcon,{}),snStr];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var _findObjectByRef,_findObjectByRef2,_findObjectByRef3;var tabs=[{label:"Summary",content:this.buildSummaryPage()}];this.buildDiffAndHealthPages(tabs);if((_findObjectByRef=findObjectByRef(this.props.commandResult.objects,this.objectRef))!==null&&_findObjectByRef!==void 0&&_findObjectByRef.rendered){tabs.push({label:"Rendered",content:this.buildObjectPage(this.objectRef,ObjectType.Rendered)});}if((_findObjectByRef2=findObjectByRef(this.props.commandResult.objects,this.objectRef))!==null&&_findObjectByRef2!==void 0&&_findObjectByRef2.remote){tabs.push({label:"Remote",content:this.buildObjectPage(this.objectRef,ObjectType.Remote)});}if((_findObjectByRef3=findObjectByRef(this.props.commandResult.objects,this.objectRef))!==null&&_findObjectByRef3!==void 0&&_findObjectByRef3.applied){tabs.push({label:"Applied",content:this.buildObjectPage(this.objectRef,ObjectType.Applied)});}return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var _o$rendered;var props=[];var apiVersion=this.objectRef.version;if(this.objectRef.group){apiVersion=this.objectRef.group+"/"+this.objectRef.version;}props.push({name:"ApiVersion",value:apiVersion});props.push({name:"Kind",value:this.objectRef.kind});props.push({name:"Name",value:this.objectRef.name});if(this.objectRef.namespace){props.push({name:"Namespace",value:this.objectRef.namespace});}var o=findObjectByRef(this.props.commandResult.objects,this.objectRef);var annotations=o===null||o===void 0?void 0:(_o$rendered=o.rendered)===null||_o$rendered===void 0?void 0:_o$rendered.metadata.annotations;if(annotations){Object.keys(annotations).forEach(function(k){if(k.indexOf("kluctl.io/")!==-1){props.push({name:k,value:annotations[k]});}});}return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}}]);return ObjectNodeData;}(NodeData); -;// CONCATENATED MODULE: ./src/components/result-view/nodes/CommandResultNode.tsx -var CommandResultNodeData=/*#__PURE__*/function(_NodeData){_inherits(CommandResultNodeData,_NodeData);var _super=_createSuper(CommandResultNodeData);function CommandResultNodeData(props,id){var _this$props$commandRe;var _this;classCallCheck_classCallCheck(this,CommandResultNodeData);_this=_super.call(this,props,id,true,true);_this.dumpedTargetYaml=void 0;if((_this$props$commandRe=_this.props.commandResult.command)!==null&&_this$props$commandRe!==void 0&&_this$props$commandRe.target){_this.dumpedTargetYaml=dump(_this.props.commandResult.command.target);}return _this;}createClass_createClass(CommandResultNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return"CommandResult";}},{key:"buildIcon",value:function buildIcon(){return[/*#__PURE__*/(0,jsx_runtime.jsx)(DeployIcon,{}),"result"];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()}];if(this.dumpedTargetYaml){var page=this.buildTargetPage();tabs.push({label:"Target",content:page});}this.buildDiffAndHealthPages(tabs);return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var _this$props$commandRe2,_this$props$commandRe3;var props=[{name:"Initiator",value:(_this$props$commandRe2=this.props.commandResult.command)===null||_this$props$commandRe2===void 0?void 0:_this$props$commandRe2.initiator},{name:"Command",value:(_this$props$commandRe3=this.props.commandResult.command)===null||_this$props$commandRe3===void 0?void 0:_this$props$commandRe3.command}];return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}},{key:"buildTargetPage",value:function buildTargetPage(){return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:this.dumpedTargetYaml,language:"yaml"});}}]);return CommandResultNodeData;}(NodeData); -;// CONCATENATED MODULE: ./src/components/result-view/nodes/VarsSourceCollectionNode.tsx -var VarsSourceCollectionNodeData=/*#__PURE__*/function(_NodeData){_inherits(VarsSourceCollectionNodeData,_NodeData);var _super=_createSuper(VarsSourceCollectionNodeData);function VarsSourceCollectionNodeData(props,id){var _this;classCallCheck_classCallCheck(this,VarsSourceCollectionNodeData);_this=_super.call(this,props,id,false,false);_this.varsSources=[];return _this;}createClass_createClass(VarsSourceCollectionNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return"vars: "+this.varsSources.length;}},{key:"buildIcon",value:function buildIcon(){return[/*#__PURE__*/(0,jsx_runtime.jsx)(BracketsSquareIcon,{}),"vars"];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){return[];}}]);return VarsSourceCollectionNodeData;}(NodeData); -;// CONCATENATED MODULE: ./src/components/result-view/nodes/DeploymentItemIncludeNode.tsx -var DeploymentItemIncludeNodeData=/*#__PURE__*/function(_NodeData){_inherits(DeploymentItemIncludeNodeData,_NodeData);var _super=_createSuper(DeploymentItemIncludeNodeData);function DeploymentItemIncludeNodeData(props,id,deploymentItem,includedDeployment){var _this;classCallCheck_classCallCheck(this,DeploymentItemIncludeNodeData);_this=_super.call(this,props,id,true,true);_this.deploymentItem=void 0;_this.includedDeployment=void 0;_this.deploymentItem=deploymentItem;_this.includedDeployment=includedDeployment;return _this;}createClass_createClass(DeploymentItemIncludeNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){if(this.deploymentItem.include){return this.deploymentItem.include;}else if(this.deploymentItem.git){var s=this.deploymentItem.git.url.split("/");var name=s[s.length-1];return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[name,this.deploymentItem.git.subDir&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),this.deploymentItem.git.subDir]})]});}else{return"unknown include";}}},{key:"buildIcon",value:function buildIcon(){if(this.deploymentItem.git){return[/*#__PURE__*/(0,jsx_runtime.jsx)(GitIcon,{}),"git"];}return[/*#__PURE__*/(0,jsx_runtime.jsx)(IncludeIcon,{}),"include"];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()}];this.buildDiffAndHealthPages(tabs);return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var props=[];if(this.deploymentItem.include){props.push({name:"Type",value:"LocalInclude"});props.push({name:"Path",value:this.deploymentItem.include});}else if(this.deploymentItem.git){props.push({name:"Type",value:"GitInclude"});props.push({name:"Url",value:this.deploymentItem.git.url});props.push({name:"SubDir",value:this.deploymentItem.git.subDir});var ref="HEAD";if(this.deploymentItem.git.ref){ref=this.deploymentItem.git.ref;}props.push({name:"Ref",value:ref});}else{props.push({name:"Type",value:"Unknown"});}buildDeploymentItemSummaryProps(this.deploymentItem,props);return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}}]);return DeploymentItemIncludeNodeData;}(NodeData); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Delete.js - - -/* harmony default export */ var Delete = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" -}), 'Delete')); -;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/LinkOff.js - - -/* harmony default export */ var LinkOff = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M17 7h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1 0 1.43-.98 2.63-2.31 2.98l1.46 1.46C20.88 15.61 22 13.95 22 12c0-2.76-2.24-5-5-5zm-1 4h-2.19l2 2H16zM2 4.27l3.11 3.11C3.29 8.12 2 9.91 2 12c0 2.76 2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1 0-1.59 1.21-2.9 2.76-3.07L8.73 11H8v2h2.73L13 15.27V17h1.73l4.01 4L20 19.74 3.27 3 2 4.27z" -}), 'LinkOff')); -;// CONCATENATED MODULE: ./src/components/result-view/nodes/DeletedOrOrphanObjectsCollectionNode.tsx -var DeletedOrOrphanObjectsCollectionNode=/*#__PURE__*/function(_NodeData){_inherits(DeletedOrOrphanObjectsCollectionNode,_NodeData);var _super=_createSuper(DeletedOrOrphanObjectsCollectionNode);function DeletedOrOrphanObjectsCollectionNode(props,id,deleted){var _this;classCallCheck_classCallCheck(this,DeletedOrOrphanObjectsCollectionNode);_this=_super.call(this,props,id,false,true);_this.deleted=void 0;_this.deleted=deleted;return _this;}createClass_createClass(DeletedOrOrphanObjectsCollectionNode,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){if(this.deleted){var _this$diffStatus;return"".concat((_this$diffStatus=this.diffStatus)===null||_this$diffStatus===void 0?void 0:_this$diffStatus.deletedObjects.length," objects deleted");}else{var _this$diffStatus2;return"".concat((_this$diffStatus2=this.diffStatus)===null||_this$diffStatus2===void 0?void 0:_this$diffStatus2.orphanObjects.length," objects orphaned");}}},{key:"buildIcon",value:function buildIcon(){if(this.deleted){return[/*#__PURE__*/(0,jsx_runtime.jsx)(Delete,{fontSize:"large"}),"deleted"];}else{return[/*#__PURE__*/(0,jsx_runtime.jsx)(LinkOff,{fontSize:"large"}),"orphans"];}}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()}];return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var _this$diffStatus3,_this$diffStatus5;var props=[];if((_this$diffStatus3=this.diffStatus)!==null&&_this$diffStatus3!==void 0&&_this$diffStatus3.deletedObjects.length){var _this$diffStatus4;props.push({name:"Deleted",value:(_this$diffStatus4=this.diffStatus)===null||_this$diffStatus4===void 0?void 0:_this$diffStatus4.deletedObjects.length});}if((_this$diffStatus5=this.diffStatus)!==null&&_this$diffStatus5!==void 0&&_this$diffStatus5.orphanObjects.length){var _this$diffStatus6;props.push({name:"Orphaned",value:(_this$diffStatus6=this.diffStatus)===null||_this$diffStatus6===void 0?void 0:_this$diffStatus6.orphanObjects.length});}return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}}]);return DeletedOrOrphanObjectsCollectionNode;}(NodeData); -;// CONCATENATED MODULE: ./src/components/result-view/nodes/NodeBuilder.ts -var NodeBuilder=/*#__PURE__*/function(){function NodeBuilder(props){var _props$commandResult$,_this=this,_props$commandResult$2,_props$commandResult$3;classCallCheck_classCallCheck(this,NodeBuilder);this.props=void 0;this.changedObjectsMap=new Map();this.newObjectsMap=new Map();this.orphanObjectsMap=new Map();this.deletedObjectsMap=new Map();this.errorsMap=new Map();this.warningsMap=new Map();this.props=props;(_props$commandResult$=props.commandResult.objects)===null||_props$commandResult$===void 0?void 0:_props$commandResult$.forEach(function(o){var _o$changes;var key=buildObjectRefKey(o.ref);if((_o$changes=o.changes)!==null&&_o$changes!==void 0&&_o$changes.length){_this.changedObjectsMap.set(key,o);}if(o.new){_this.newObjectsMap.set(key,o.ref);}if(o.orphan){_this.orphanObjectsMap.set(key,o.ref);}if(o.deleted){_this.deletedObjectsMap.set(key,o.ref);}});(_props$commandResult$2=props.commandResult.errors)===null||_props$commandResult$2===void 0?void 0:_props$commandResult$2.forEach(function(e){var key=buildObjectRefKey(e.ref);var l=_this.errorsMap.get(key);if(!l){l=[e];_this.errorsMap.set(key,l);}else{l.push(e);}});(_props$commandResult$3=props.commandResult.warnings)===null||_props$commandResult$3===void 0?void 0:_props$commandResult$3.forEach(function(e){var key=buildObjectRefKey(e.ref);var l=_this.warningsMap.get(key);if(!l){l=[e];_this.warningsMap.set(key,l);}else{l.push(e);}});}createClass_createClass(NodeBuilder,[{key:"buildRoot",value:function buildRoot(){var rootNode=new CommandResultNodeData(this.props,"root");if(this.props.commandResult.deployment){this.buildDeploymentProjectChildren(rootNode,this.props.commandResult.deployment);}if(this.deletedObjectsMap.size){this.buildDeletedOrOrphanNode(rootNode,true,Array.from(this.deletedObjectsMap.values()));}if(this.orphanObjectsMap.size){this.buildDeletedOrOrphanNode(rootNode,false,Array.from(this.orphanObjectsMap.values()));}var nodeMap=new Map();function collect(n){nodeMap.set(n.id,n);n.children.forEach(function(c){collect(c);});}collect(rootNode);return[rootNode,nodeMap];}},{key:"buildDeploymentProjectChildren",value:function buildDeploymentProjectChildren(node,deploymentProject){var _deploymentProject$de,_this2=this;if(deploymentProject.vars){this.buildVarsSourceCollectionNode(node,deploymentProject.vars);}(_deploymentProject$de=deploymentProject.deployments)===null||_deploymentProject$de===void 0?void 0:_deploymentProject$de.forEach(function(deploymentItem,i){_this2.buildDeploymentItemNode(node,deploymentItem,i);});}},{key:"buildVarsSourceCollectionNode",value:function buildVarsSourceCollectionNode(parentNode,varsSources){var _node$varsSources,_this3=this;if(varsSources===undefined){return;}var newId="".concat(parentNode.id,"/(vars)");var node=new VarsSourceCollectionNodeData(this.props,newId);(_node$varsSources=node.varsSources).push.apply(_node$varsSources,toConsumableArray_toConsumableArray(varsSources));varsSources.forEach(function(vs,i){_this3.buildVarsSourceNode(node,vs,i);});parentNode.pushChild(node);return node;}},{key:"buildVarsSourceNode",value:function buildVarsSourceNode(parentNode,varsSource,index){var newId="".concat(parentNode.id,"/").concat(index,"}");var node=new VarsSourceNodeData(this.props,newId,varsSource);parentNode.pushChild(node);return node;}},{key:"buildDeploymentItemNode",value:function buildDeploymentItemNode(parentNode,deploymentItem,index){var _this4=this;var node;var newId="".concat(parentNode.id,"/(dis)/").concat(index);if(deploymentItem.path){var _deploymentItem$rende;node=new DeploymentItemNodeData(this.props,newId,deploymentItem);this.buildVarsSourceCollectionNode(node,deploymentItem.vars);(_deploymentItem$rende=deploymentItem.renderedObjects)===null||_deploymentItem$rende===void 0?void 0:_deploymentItem$rende.forEach(function(renderedObject){_this4.buildObjectNode(node,renderedObject);});}else if(deploymentItem.include||deploymentItem.git){node=new DeploymentItemIncludeNodeData(this.props,newId,deploymentItem,deploymentItem.renderedInclude);this.buildVarsSourceCollectionNode(node,deploymentItem.vars);if(deploymentItem.renderedInclude){this.buildDeploymentProjectChildren(node,deploymentItem.renderedInclude);}}else{return node;}parentNode.pushChild(node);return node;}},{key:"buildObjectNode",value:function buildObjectNode(parentNode,objectRef){var _this$errorsMap$get,_this$warningsMap$get;var newId="".concat(parentNode.id,"/(obj)/").concat(buildObjectRefKey(objectRef));var node=new ObjectNodeData(this.props,newId,objectRef);var key=buildObjectRefKey(objectRef);if(this.changedObjectsMap.has(key)){var _node$diffStatus;(_node$diffStatus=node.diffStatus)===null||_node$diffStatus===void 0?void 0:_node$diffStatus.addChangedObject(this.changedObjectsMap.get(key));}if(this.newObjectsMap.has(key)){var _node$diffStatus2;(_node$diffStatus2=node.diffStatus)===null||_node$diffStatus2===void 0?void 0:_node$diffStatus2.newObjects.push(objectRef);}if(this.deletedObjectsMap.has(key)){var _node$diffStatus3;(_node$diffStatus3=node.diffStatus)===null||_node$diffStatus3===void 0?void 0:_node$diffStatus3.deletedObjects.push(objectRef);}if(this.orphanObjectsMap.has(key)){var _node$diffStatus4;(_node$diffStatus4=node.diffStatus)===null||_node$diffStatus4===void 0?void 0:_node$diffStatus4.orphanObjects.push(objectRef);}(_this$errorsMap$get=this.errorsMap.get(key))===null||_this$errorsMap$get===void 0?void 0:_this$errorsMap$get.forEach(function(e){var _node$healthStatus;(_node$healthStatus=node.healthStatus)===null||_node$healthStatus===void 0?void 0:_node$healthStatus.errors.push(e);});(_this$warningsMap$get=this.warningsMap.get(key))===null||_this$warningsMap$get===void 0?void 0:_this$warningsMap$get.forEach(function(e){var _node$healthStatus2;(_node$healthStatus2=node.healthStatus)===null||_node$healthStatus2===void 0?void 0:_node$healthStatus2.warnings.push(e);});parentNode.pushChild(node);return node;}},{key:"buildDeletedOrOrphanNode",value:function buildDeletedOrOrphanNode(parentNode,deleted,refs){var _this5=this;var idType=deleted?"deleted":"orphaned";var newId="".concat(parentNode.id,"/(").concat(idType,")");var node=new DeletedOrOrphanObjectsCollectionNode(this.props,newId,deleted);refs.forEach(function(ref){_this5.buildObjectNode(node,ref);});parentNode.pushChild(node,true);return node;}}]);return NodeBuilder;}();function buildObjectRefKey(ref){return[ref.group,ref.version,ref.kind,ref.namespace,ref.name].join("+");} -;// CONCATENATED MODULE: ./node_modules/@mui/material/Tab/tabClasses.js - - -function getTabUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTab', slot); -} -var tabClasses = generateUtilityClasses('MuiTab', ['root', 'labelIcon', 'textColorInherit', 'textColorPrimary', 'textColorSecondary', 'selected', 'disabled', 'fullWidth', 'wrapped', 'iconWrapper']); -/* harmony default export */ var Tab_tabClasses = (tabClasses); -;// CONCATENATED MODULE: ./node_modules/@mui/material/Tab/Tab.js - - - -var Tab_excluded = ["className", "disabled", "disableFocusRipple", "fullWidth", "icon", "iconPosition", "indicator", "label", "onChange", "onClick", "onFocus", "selected", "selectionFollowsFocus", "textColor", "value", "wrapped"]; - - - - - - - - - - - -var Tab_useUtilityClasses = function useUtilityClasses(ownerState) { - var classes = ownerState.classes, - textColor = ownerState.textColor, - fullWidth = ownerState.fullWidth, - wrapped = ownerState.wrapped, - icon = ownerState.icon, - label = ownerState.label, - selected = ownerState.selected, - disabled = ownerState.disabled; - var slots = { - root: ['root', icon && label && 'labelIcon', "textColor".concat(utils_capitalize(textColor)), fullWidth && 'fullWidth', wrapped && 'wrapped', selected && 'selected', disabled && 'disabled'], - iconWrapper: ['iconWrapper'] - }; - return composeClasses(slots, getTabUtilityClass, classes); -}; -var TabRoot = styles_styled(ButtonBase_ButtonBase, { - name: 'MuiTab', - slot: 'Root', +var TabsScroller = styles_styled('div', { + name: 'MuiTabs', + slot: 'Scroller', overridesResolver: function overridesResolver(props, styles) { var ownerState = props.ownerState; - return [styles.root, ownerState.label && ownerState.icon && styles.labelIcon, styles["textColor".concat(utils_capitalize(ownerState.textColor))], ownerState.fullWidth && styles.fullWidth, ownerState.wrapped && styles.wrapped]; + return [styles.scroller, ownerState.fixed && styles.fixed, ownerState.hideScrollbar && styles.hideScrollbar, ownerState.scrollableX && styles.scrollableX, ownerState.scrollableY && styles.scrollableY]; } -})(function (_ref) { - var _ref3, _ref4, _ref5; - var theme = _ref.theme, - ownerState = _ref.ownerState; - return extends_extends({}, theme.typography.button, { - maxWidth: 360, - minWidth: 90, +})(function (_ref5) { + var ownerState = _ref5.ownerState; + return extends_extends({ position: 'relative', - minHeight: 48, - flexShrink: 0, - padding: '12px 16px', - overflow: 'hidden', - whiteSpace: 'normal', - textAlign: 'center' - }, ownerState.label && { - flexDirection: ownerState.iconPosition === 'top' || ownerState.iconPosition === 'bottom' ? 'column' : 'row' - }, { - lineHeight: 1.25 - }, ownerState.icon && ownerState.label && defineProperty_defineProperty({ - minHeight: 72, - paddingTop: 9, - paddingBottom: 9 - }, "& > .".concat(Tab_tabClasses.iconWrapper), extends_extends({}, ownerState.iconPosition === 'top' && { - marginBottom: 6 - }, ownerState.iconPosition === 'bottom' && { - marginTop: 6 - }, ownerState.iconPosition === 'start' && { - marginRight: theme.spacing(1) - }, ownerState.iconPosition === 'end' && { - marginLeft: theme.spacing(1) - })), ownerState.textColor === 'inherit' && (_ref3 = { - color: 'inherit', - opacity: 0.6 - }, defineProperty_defineProperty(_ref3, "&.".concat(Tab_tabClasses.selected), { - opacity: 1 - }), defineProperty_defineProperty(_ref3, "&.".concat(Tab_tabClasses.disabled), { - opacity: (theme.vars || theme).palette.action.disabledOpacity - }), _ref3), ownerState.textColor === 'primary' && (_ref4 = { - color: (theme.vars || theme).palette.text.secondary - }, defineProperty_defineProperty(_ref4, "&.".concat(Tab_tabClasses.selected), { - color: (theme.vars || theme).palette.primary.main - }), defineProperty_defineProperty(_ref4, "&.".concat(Tab_tabClasses.disabled), { - color: (theme.vars || theme).palette.text.disabled - }), _ref4), ownerState.textColor === 'secondary' && (_ref5 = { - color: (theme.vars || theme).palette.text.secondary - }, defineProperty_defineProperty(_ref5, "&.".concat(Tab_tabClasses.selected), { - color: (theme.vars || theme).palette.secondary.main - }), defineProperty_defineProperty(_ref5, "&.".concat(Tab_tabClasses.disabled), { - color: (theme.vars || theme).palette.text.disabled - }), _ref5), ownerState.fullWidth && { - flexShrink: 1, - flexGrow: 1, - flexBasis: 0, - maxWidth: 'none' - }, ownerState.wrapped && { - fontSize: theme.typography.pxToRem(12) + display: 'inline-block', + flex: '1 1 auto', + whiteSpace: 'nowrap' + }, ownerState.fixed && { + overflowX: 'hidden', + width: '100%' + }, ownerState.hideScrollbar && { + // Hide dimensionless scrollbar on macOS + scrollbarWidth: 'none', + // Firefox + '&::-webkit-scrollbar': { + display: 'none' // Safari + Chrome + } + }, ownerState.scrollableX && { + overflowX: 'auto', + overflowY: 'hidden' + }, ownerState.scrollableY && { + overflowY: 'auto', + overflowX: 'hidden' }); }); -var Tab = /*#__PURE__*/react.forwardRef(function Tab(inProps, ref) { - var props = useThemeProps_useThemeProps({ - props: inProps, - name: 'MuiTab' +var FlexContainer = styles_styled('div', { + name: 'MuiTabs', + slot: 'FlexContainer', + overridesResolver: function overridesResolver(props, styles) { + var ownerState = props.ownerState; + return [styles.flexContainer, ownerState.vertical && styles.flexContainerVertical, ownerState.centered && styles.centered]; + } +})(function (_ref6) { + var ownerState = _ref6.ownerState; + return extends_extends({ + display: 'flex' + }, ownerState.vertical && { + flexDirection: 'column' + }, ownerState.centered && { + justifyContent: 'center' }); - var className = props.className, - _props$disabled = props.disabled, - disabled = _props$disabled === void 0 ? false : _props$disabled, - _props$disableFocusRi = props.disableFocusRipple, - disableFocusRipple = _props$disableFocusRi === void 0 ? false : _props$disableFocusRi, - fullWidth = props.fullWidth, - iconProp = props.icon, - _props$iconPosition = props.iconPosition, - iconPosition = _props$iconPosition === void 0 ? 'top' : _props$iconPosition, - indicator = props.indicator, - label = props.label, +}); +var TabsIndicator = styles_styled('span', { + name: 'MuiTabs', + slot: 'Indicator', + overridesResolver: function overridesResolver(props, styles) { + return styles.indicator; + } +})(function (_ref7) { + var ownerState = _ref7.ownerState, + theme = _ref7.theme; + return extends_extends({ + position: 'absolute', + height: 2, + bottom: 0, + width: '100%', + transition: theme.transitions.create() + }, ownerState.indicatorColor === 'primary' && { + backgroundColor: (theme.vars || theme).palette.primary.main + }, ownerState.indicatorColor === 'secondary' && { + backgroundColor: (theme.vars || theme).palette.secondary.main + }, ownerState.vertical && { + height: '100%', + width: 2, + right: 0 + }); +}); +var TabsScrollbarSize = styles_styled(ScrollbarSize, { + name: 'MuiTabs', + slot: 'ScrollbarSize' +})({ + overflowX: 'auto', + overflowY: 'hidden', + // Hide dimensionless scrollbar on macOS + scrollbarWidth: 'none', + // Firefox + '&::-webkit-scrollbar': { + display: 'none' // Safari + Chrome + } +}); + +var defaultIndicatorStyle = {}; +var warnedOnceTabPresent = false; +var Tabs = /*#__PURE__*/react.forwardRef(function Tabs(inProps, ref) { + var props = useThemeProps_useThemeProps({ + props: inProps, + name: 'MuiTabs' + }); + var theme = styles_useTheme_useTheme(); + var isRtl = theme.direction === 'rtl'; + var ariaLabel = props['aria-label'], + ariaLabelledBy = props['aria-labelledby'], + action = props.action, + _props$centered = props.centered, + centered = _props$centered === void 0 ? false : _props$centered, + childrenProp = props.children, + className = props.className, + _props$component = props.component, + component = _props$component === void 0 ? 'div' : _props$component, + _props$allowScrollBut = props.allowScrollButtonsMobile, + allowScrollButtonsMobile = _props$allowScrollBut === void 0 ? false : _props$allowScrollBut, + _props$indicatorColor = props.indicatorColor, + indicatorColor = _props$indicatorColor === void 0 ? 'primary' : _props$indicatorColor, onChange = props.onChange, - onClick = props.onClick, - onFocus = props.onFocus, - selected = props.selected, + _props$orientation = props.orientation, + orientation = _props$orientation === void 0 ? 'horizontal' : _props$orientation, + _props$ScrollButtonCo = props.ScrollButtonComponent, + ScrollButtonComponent = _props$ScrollButtonCo === void 0 ? TabScrollButton_TabScrollButton : _props$ScrollButtonCo, + _props$scrollButtons = props.scrollButtons, + scrollButtons = _props$scrollButtons === void 0 ? 'auto' : _props$scrollButtons, selectionFollowsFocus = props.selectionFollowsFocus, + _props$slots = props.slots, + slots = _props$slots === void 0 ? {} : _props$slots, + _props$slotProps = props.slotProps, + slotProps = _props$slotProps === void 0 ? {} : _props$slotProps, + _props$TabIndicatorPr = props.TabIndicatorProps, + TabIndicatorProps = _props$TabIndicatorPr === void 0 ? {} : _props$TabIndicatorPr, + _props$TabScrollButto = props.TabScrollButtonProps, + TabScrollButtonProps = _props$TabScrollButto === void 0 ? {} : _props$TabScrollButto, _props$textColor = props.textColor, - textColor = _props$textColor === void 0 ? 'inherit' : _props$textColor, + textColor = _props$textColor === void 0 ? 'primary' : _props$textColor, value = props.value, - _props$wrapped = props.wrapped, - wrapped = _props$wrapped === void 0 ? false : _props$wrapped, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, Tab_excluded); + _props$variant = props.variant, + variant = _props$variant === void 0 ? 'standard' : _props$variant, + _props$visibleScrollb = props.visibleScrollbar, + visibleScrollbar = _props$visibleScrollb === void 0 ? false : _props$visibleScrollb, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, Tabs_excluded); + var scrollable = variant === 'scrollable'; + var vertical = orientation === 'vertical'; + var scrollStart = vertical ? 'scrollTop' : 'scrollLeft'; + var start = vertical ? 'top' : 'left'; + var end = vertical ? 'bottom' : 'right'; + var clientSize = vertical ? 'clientHeight' : 'clientWidth'; + var size = vertical ? 'height' : 'width'; var ownerState = extends_extends({}, props, { - disabled: disabled, - disableFocusRipple: disableFocusRipple, - selected: selected, - icon: !!iconProp, - iconPosition: iconPosition, - label: !!label, - fullWidth: fullWidth, + component: component, + allowScrollButtonsMobile: allowScrollButtonsMobile, + indicatorColor: indicatorColor, + orientation: orientation, + vertical: vertical, + scrollButtons: scrollButtons, textColor: textColor, - wrapped: wrapped + variant: variant, + visibleScrollbar: visibleScrollbar, + fixed: !scrollable, + hideScrollbar: scrollable && !visibleScrollbar, + scrollableX: scrollable && !vertical, + scrollableY: scrollable && vertical, + centered: centered && !scrollable, + scrollButtonsHideMobile: !allowScrollButtonsMobile }); - var classes = Tab_useUtilityClasses(ownerState); - var icon = iconProp && label && /*#__PURE__*/react.isValidElement(iconProp) ? /*#__PURE__*/react.cloneElement(iconProp, { - className: clsx_m(classes.iconWrapper, iconProp.props.className) - }) : iconProp; - var handleClick = function handleClick(event) { - if (!selected && onChange) { - onChange(event, value); + var classes = Tabs_useUtilityClasses(ownerState); + var startScrollButtonIconProps = useSlotProps({ + elementType: slots.StartScrollButtonIcon, + externalSlotProps: slotProps.startScrollButtonIcon, + ownerState: ownerState + }); + var endScrollButtonIconProps = useSlotProps({ + elementType: slots.EndScrollButtonIcon, + externalSlotProps: slotProps.endScrollButtonIcon, + ownerState: ownerState + }); + if (false) {} + var _React$useState = react.useState(false), + _React$useState2 = slicedToArray_slicedToArray(_React$useState, 2), + mounted = _React$useState2[0], + setMounted = _React$useState2[1]; + var _React$useState3 = react.useState(defaultIndicatorStyle), + _React$useState4 = slicedToArray_slicedToArray(_React$useState3, 2), + indicatorStyle = _React$useState4[0], + setIndicatorStyle = _React$useState4[1]; + var _React$useState5 = react.useState({ + start: false, + end: false + }), + _React$useState6 = slicedToArray_slicedToArray(_React$useState5, 2), + displayScroll = _React$useState6[0], + setDisplayScroll = _React$useState6[1]; + var _React$useState7 = react.useState({ + overflow: 'hidden', + scrollbarWidth: 0 + }), + _React$useState8 = slicedToArray_slicedToArray(_React$useState7, 2), + scrollerStyle = _React$useState8[0], + setScrollerStyle = _React$useState8[1]; + var valueToIndex = new Map(); + var tabsRef = react.useRef(null); + var tabListRef = react.useRef(null); + var getTabsMeta = function getTabsMeta() { + var tabsNode = tabsRef.current; + var tabsMeta; + if (tabsNode) { + var rect = tabsNode.getBoundingClientRect(); + // create a new object with ClientRect class props + scrollLeft + tabsMeta = { + clientWidth: tabsNode.clientWidth, + scrollLeft: tabsNode.scrollLeft, + scrollTop: tabsNode.scrollTop, + scrollLeftNormalized: getNormalizedScrollLeft(tabsNode, theme.direction), + scrollWidth: tabsNode.scrollWidth, + top: rect.top, + bottom: rect.bottom, + left: rect.left, + right: rect.right + }; } - if (onClick) { - onClick(event); + var tabMeta; + if (tabsNode && value !== false) { + var _children = tabListRef.current.children; + if (_children.length > 0) { + var tab = _children[valueToIndex.get(value)]; + if (false) {} + tabMeta = tab ? tab.getBoundingClientRect() : null; + if (false) {} + } } + return { + tabsMeta: tabsMeta, + tabMeta: tabMeta + }; }; - var handleFocus = function handleFocus(event) { - if (selectionFollowsFocus && !selected && onChange) { - onChange(event, value); + var updateIndicatorState = utils_useEventCallback(function () { + var _newIndicatorStyle; + var _getTabsMeta = getTabsMeta(), + tabsMeta = _getTabsMeta.tabsMeta, + tabMeta = _getTabsMeta.tabMeta; + var startValue = 0; + var startIndicator; + if (vertical) { + startIndicator = 'top'; + if (tabMeta && tabsMeta) { + startValue = tabMeta.top - tabsMeta.top + tabsMeta.scrollTop; + } + } else { + startIndicator = isRtl ? 'right' : 'left'; + if (tabMeta && tabsMeta) { + var correction = isRtl ? tabsMeta.scrollLeftNormalized + tabsMeta.clientWidth - tabsMeta.scrollWidth : tabsMeta.scrollLeft; + startValue = (isRtl ? -1 : 1) * (tabMeta[startIndicator] - tabsMeta[startIndicator] + correction); + } } - if (onFocus) { - onFocus(event); + var newIndicatorStyle = (_newIndicatorStyle = {}, defineProperty_defineProperty(_newIndicatorStyle, startIndicator, startValue), defineProperty_defineProperty(_newIndicatorStyle, size, tabMeta ? tabMeta[size] : 0), _newIndicatorStyle); + + // IE11 support, replace with Number.isNaN + // eslint-disable-next-line no-restricted-globals + if (isNaN(indicatorStyle[startIndicator]) || isNaN(indicatorStyle[size])) { + setIndicatorStyle(newIndicatorStyle); + } else { + var dStart = Math.abs(indicatorStyle[startIndicator] - newIndicatorStyle[startIndicator]); + var dSize = Math.abs(indicatorStyle[size] - newIndicatorStyle[size]); + if (dStart >= 1 || dSize >= 1) { + setIndicatorStyle(newIndicatorStyle); + } } - }; - return /*#__PURE__*/(0,jsx_runtime.jsxs)(TabRoot, extends_extends({ - focusRipple: !disableFocusRipple, - className: clsx_m(classes.root, className), - ref: ref, - role: "tab", - "aria-selected": selected, - disabled: disabled, - onClick: handleClick, - onFocus: handleFocus, - ownerState: ownerState, - tabIndex: selected ? 0 : -1 - }, other, { - children: [iconPosition === 'top' || iconPosition === 'start' ? /*#__PURE__*/(0,jsx_runtime.jsxs)(react.Fragment, { - children: [icon, label] - }) : /*#__PURE__*/(0,jsx_runtime.jsxs)(react.Fragment, { - children: [label, icon] - }), indicator] - })); -}); - false ? 0 : void 0; -/* harmony default export */ var Tab_Tab = (Tab); -;// CONCATENATED MODULE: ./node_modules/@mui/lab/TabContext/TabContext.js - - - - -/** - * @type {React.Context<{ idPrefix: string; value: string } | null>} - */ - -var Context = /*#__PURE__*/react.createContext(null); -if (false) {} -function useUniquePrefix() { - var _React$useState = react.useState(null), - _React$useState2 = slicedToArray_slicedToArray(_React$useState, 2), - id = _React$useState2[0], - setId = _React$useState2[1]; - react.useEffect(function () { - setId("mui-p-".concat(Math.round(Math.random() * 1e5))); - }, []); - return id; -} -function TabContext(props) { - var children = props.children, - value = props.value; - var idPrefix = useUniquePrefix(); - var context = react.useMemo(function () { - return { - idPrefix: idPrefix, - value: value - }; - }, [idPrefix, value]); - return /*#__PURE__*/(0,jsx_runtime.jsx)(Context.Provider, { - value: context, - children: children }); -} - false ? 0 : void 0; - -/** - * @returns {unknown} - */ -function useTabContext() { - return react.useContext(Context); -} -function getPanelId(context, value) { - var idPrefix = context.idPrefix; - if (idPrefix === null) { - return null; - } - return "".concat(context.idPrefix, "-P-").concat(value); -} -function getTabId(context, value) { - var idPrefix = context.idPrefix; - if (idPrefix === null) { - return null; - } - return "".concat(context.idPrefix, "-T-").concat(value); -} -;// CONCATENATED MODULE: ./node_modules/@mui/utils/esm/scrollLeft.js -// Source from https://github.com/alitaheri/normalize-scroll-left -var cachedType; - -/** - * Based on the jquery plugin https://github.com/othree/jquery.rtl-scroll-type - * - * Types of scrollLeft, assuming scrollWidth=100 and direction is rtl. - * - * Type | <- Most Left | Most Right -> | Initial - * ---------------- | ------------ | ------------- | ------- - * default | 0 | 100 | 100 - * negative (spec*) | -100 | 0 | 0 - * reverse | 100 | 0 | 0 - * - * Edge 85: default - * Safari 14: negative - * Chrome 85: negative - * Firefox 81: negative - * IE11: reverse - * - * spec* https://drafts.csswg.org/cssom-view/#dom-window-scroll - */ -function detectScrollType() { - if (cachedType) { - return cachedType; - } - var dummy = document.createElement('div'); - var container = document.createElement('div'); - container.style.width = '10px'; - container.style.height = '1px'; - dummy.appendChild(container); - dummy.dir = 'rtl'; - dummy.style.fontSize = '14px'; - dummy.style.width = '4px'; - dummy.style.height = '1px'; - dummy.style.position = 'absolute'; - dummy.style.top = '-1000px'; - dummy.style.overflow = 'scroll'; - document.body.appendChild(dummy); - cachedType = 'reverse'; - if (dummy.scrollLeft > 0) { - cachedType = 'default'; - } else { - dummy.scrollLeft = 1; - if (dummy.scrollLeft === 0) { - cachedType = 'negative'; + var scroll = function scroll(scrollValue) { + var _ref8 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref8$animation = _ref8.animation, + animation = _ref8$animation === void 0 ? true : _ref8$animation; + if (animation) { + animate(scrollStart, tabsRef.current, scrollValue, { + duration: theme.transitions.duration.standard + }); + } else { + tabsRef.current[scrollStart] = scrollValue; } - } - document.body.removeChild(dummy); - return cachedType; -} - -// Based on https://stackoverflow.com/a/24394376 -function getNormalizedScrollLeft(element, direction) { - var scrollLeft = element.scrollLeft; - - // Perform the calculations only when direction is rtl to avoid messing up the ltr behavior - if (direction !== 'rtl') { - return scrollLeft; - } - var type = detectScrollType(); - switch (type) { - case 'negative': - return element.scrollWidth - element.clientWidth + scrollLeft; - case 'reverse': - return element.scrollWidth - element.clientWidth - scrollLeft; - default: - return scrollLeft; - } -} -;// CONCATENATED MODULE: ./node_modules/@mui/material/internal/animate.js -function easeInOutSin(time) { - return (1 + Math.sin(Math.PI * time - Math.PI / 2)) / 2; -} -function animate(property, element, to) { - var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; - var cb = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : function () {}; - var _options$ease = options.ease, - ease = _options$ease === void 0 ? easeInOutSin : _options$ease, - _options$duration = options.duration, - duration = _options$duration === void 0 ? 300 : _options$duration; - var start = null; - var from = element[property]; - var cancelled = false; - var cancel = function cancel() { - cancelled = true; }; - var step = function step(timestamp) { - if (cancelled) { - cb(new Error('Animation cancelled')); - return; - } - if (start === null) { - start = timestamp; + var moveTabsScroll = function moveTabsScroll(delta) { + var scrollValue = tabsRef.current[scrollStart]; + if (vertical) { + scrollValue += delta; + } else { + scrollValue += delta * (isRtl ? -1 : 1); + // Fix for Edge + scrollValue *= isRtl && detectScrollType() === 'reverse' ? -1 : 1; } - var time = Math.min(1, (timestamp - start) / duration); - element[property] = ease(time) * (to - from) + from; - if (time >= 1) { - requestAnimationFrame(function () { - cb(null); - }); - return; + scroll(scrollValue); + }; + var getScrollSize = function getScrollSize() { + var containerSize = tabsRef.current[clientSize]; + var totalSize = 0; + var children = Array.from(tabListRef.current.children); + for (var i = 0; i < children.length; i += 1) { + var tab = children[i]; + if (totalSize + tab[clientSize] > containerSize) { + // If the first item is longer than the container size, then only scroll + // by the container size. + if (i === 0) { + totalSize = containerSize; + } + break; + } + totalSize += tab[clientSize]; } - requestAnimationFrame(step); + return totalSize; }; - if (from === to) { - cb(new Error('Element already at target position')); - return cancel; - } - requestAnimationFrame(step); - return cancel; -} -;// CONCATENATED MODULE: ./node_modules/@mui/material/Tabs/ScrollbarSize.js - - -var ScrollbarSize_excluded = ["onChange"]; - - - - - -var ScrollbarSize_styles = { - width: 99, - height: 99, - position: 'absolute', - top: -9999, - overflow: 'scroll' -}; - -/** - * @ignore - internal component. - * The component originates from https://github.com/STORIS/react-scrollbar-size. - * It has been moved into the core in order to minimize the bundle size. - */ -function ScrollbarSize(props) { - var onChange = props.onChange, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, ScrollbarSize_excluded); - var scrollbarHeight = react.useRef(); - var nodeRef = react.useRef(null); - var setMeasurements = function setMeasurements() { - scrollbarHeight.current = nodeRef.current.offsetHeight - nodeRef.current.clientHeight; + var handleStartScrollClick = function handleStartScrollClick() { + moveTabsScroll(-1 * getScrollSize()); }; - utils_useEnhancedEffect(function () { - var handleResize = utils_debounce(function () { - var prevHeight = scrollbarHeight.current; - setMeasurements(); - if (prevHeight !== scrollbarHeight.current) { - onChange(scrollbarHeight.current); + var handleEndScrollClick = function handleEndScrollClick() { + moveTabsScroll(getScrollSize()); + }; + + // TODO Remove as browser support for hiding the scrollbar + // with CSS improves. + var handleScrollbarSizeChange = react.useCallback(function (scrollbarWidth) { + setScrollerStyle({ + overflow: null, + scrollbarWidth: scrollbarWidth + }); + }, []); + var getConditionalElements = function getConditionalElements() { + var conditionalElements = {}; + conditionalElements.scrollbarSizeListener = scrollable ? /*#__PURE__*/(0,jsx_runtime.jsx)(TabsScrollbarSize, { + onChange: handleScrollbarSizeChange, + className: clsx_m(classes.scrollableX, classes.hideScrollbar) + }) : null; + var scrollButtonsActive = displayScroll.start || displayScroll.end; + var showScrollButtons = scrollable && (scrollButtons === 'auto' && scrollButtonsActive || scrollButtons === true); + conditionalElements.scrollButtonStart = showScrollButtons ? /*#__PURE__*/(0,jsx_runtime.jsx)(ScrollButtonComponent, extends_extends({ + slots: { + StartScrollButtonIcon: slots.StartScrollButtonIcon + }, + slotProps: { + startScrollButtonIcon: startScrollButtonIconProps + }, + orientation: orientation, + direction: isRtl ? 'right' : 'left', + onClick: handleStartScrollClick, + disabled: !displayScroll.start + }, TabScrollButtonProps, { + className: clsx_m(classes.scrollButtons, TabScrollButtonProps.className) + })) : null; + conditionalElements.scrollButtonEnd = showScrollButtons ? /*#__PURE__*/(0,jsx_runtime.jsx)(ScrollButtonComponent, extends_extends({ + slots: { + EndScrollButtonIcon: slots.EndScrollButtonIcon + }, + slotProps: { + endScrollButtonIcon: endScrollButtonIconProps + }, + orientation: orientation, + direction: isRtl ? 'left' : 'right', + onClick: handleEndScrollClick, + disabled: !displayScroll.end + }, TabScrollButtonProps, { + className: clsx_m(classes.scrollButtons, TabScrollButtonProps.className) + })) : null; + return conditionalElements; + }; + var scrollSelectedIntoView = utils_useEventCallback(function (animation) { + var _getTabsMeta2 = getTabsMeta(), + tabsMeta = _getTabsMeta2.tabsMeta, + tabMeta = _getTabsMeta2.tabMeta; + if (!tabMeta || !tabsMeta) { + return; + } + if (tabMeta[start] < tabsMeta[start]) { + // left side of button is out of view + var nextScrollStart = tabsMeta[scrollStart] + (tabMeta[start] - tabsMeta[start]); + scroll(nextScrollStart, { + animation: animation + }); + } else if (tabMeta[end] > tabsMeta[end]) { + // right side of button is out of view + var _nextScrollStart = tabsMeta[scrollStart] + (tabMeta[end] - tabsMeta[end]); + scroll(_nextScrollStart, { + animation: animation + }); + } + }); + var updateScrollButtonState = utils_useEventCallback(function () { + if (scrollable && scrollButtons !== false) { + var _tabsRef$current = tabsRef.current, + scrollTop = _tabsRef$current.scrollTop, + scrollHeight = _tabsRef$current.scrollHeight, + clientHeight = _tabsRef$current.clientHeight, + scrollWidth = _tabsRef$current.scrollWidth, + clientWidth = _tabsRef$current.clientWidth; + var showStartScroll; + var showEndScroll; + if (vertical) { + showStartScroll = scrollTop > 1; + showEndScroll = scrollTop < scrollHeight - clientHeight - 1; + } else { + var scrollLeft = getNormalizedScrollLeft(tabsRef.current, theme.direction); + // use 1 for the potential rounding error with browser zooms. + showStartScroll = isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1; + showEndScroll = !isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1; + } + if (showStartScroll !== displayScroll.start || showEndScroll !== displayScroll.end) { + setDisplayScroll({ + start: showStartScroll, + end: showEndScroll + }); + } + } + }); + react.useEffect(function () { + var handleResize = utils_debounce(function () { + // If the Tabs component is replaced by Suspense with a fallback, the last + // ResizeObserver's handler that runs because of the change in the layout is trying to + // access a dom node that is no longer there (as the fallback component is being shown instead). + // See https://github.com/mui/material-ui/issues/33276 + // TODO: Add tests that will ensure the component is not failing when + // replaced by Suspense with a fallback, once React is updated to version 18 + if (tabsRef.current) { + updateIndicatorState(); + updateScrollButtonState(); } }); - var containerWindow = utils_ownerWindow(nodeRef.current); - containerWindow.addEventListener('resize', handleResize); + var win = utils_ownerWindow(tabsRef.current); + win.addEventListener('resize', handleResize); + var resizeObserver; + if (typeof ResizeObserver !== 'undefined') { + resizeObserver = new ResizeObserver(handleResize); + Array.from(tabListRef.current.children).forEach(function (child) { + resizeObserver.observe(child); + }); + } return function () { handleResize.clear(); - containerWindow.removeEventListener('resize', handleResize); + win.removeEventListener('resize', handleResize); + if (resizeObserver) { + resizeObserver.disconnect(); + } }; - }, [onChange]); + }, [updateIndicatorState, updateScrollButtonState]); + var handleTabsScroll = react.useMemo(function () { + return utils_debounce(function () { + updateScrollButtonState(); + }); + }, [updateScrollButtonState]); react.useEffect(function () { - setMeasurements(); - onChange(scrollbarHeight.current); - }, [onChange]); - return /*#__PURE__*/(0,jsx_runtime.jsx)("div", extends_extends({ - style: ScrollbarSize_styles, - ref: nodeRef - }, other)); -} - false ? 0 : void 0; -;// CONCATENATED MODULE: ./node_modules/@mui/material/internal/svg-icons/KeyboardArrowLeft.js - - - -/** - * @ignore - internal component. - */ - -/* harmony default export */ var KeyboardArrowLeft = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z" -}), 'KeyboardArrowLeft')); -;// CONCATENATED MODULE: ./node_modules/@mui/material/internal/svg-icons/KeyboardArrowRight.js - - - -/** - * @ignore - internal component. - */ - -/* harmony default export */ var KeyboardArrowRight = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { - d: "M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z" -}), 'KeyboardArrowRight')); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TabScrollButton/tabScrollButtonClasses.js - - -function getTabScrollButtonUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTabScrollButton', slot); -} -var tabScrollButtonClasses = generateUtilityClasses('MuiTabScrollButton', ['root', 'vertical', 'horizontal', 'disabled']); -/* harmony default export */ var TabScrollButton_tabScrollButtonClasses = (tabScrollButtonClasses); -;// CONCATENATED MODULE: ./node_modules/@mui/material/TabScrollButton/TabScrollButton.js - - - -var TabScrollButton_excluded = ["className", "slots", "slotProps", "direction", "orientation", "disabled"]; -/* eslint-disable jsx-a11y/aria-role */ - - - - - - - - - - - - -var TabScrollButton_useUtilityClasses = function useUtilityClasses(ownerState) { - var classes = ownerState.classes, - orientation = ownerState.orientation, - disabled = ownerState.disabled; - var slots = { - root: ['root', orientation, disabled && 'disabled'] - }; - return composeClasses(slots, getTabScrollButtonUtilityClass, classes); -}; -var TabScrollButtonRoot = styles_styled(ButtonBase_ButtonBase, { - name: 'MuiTabScrollButton', - slot: 'Root', - overridesResolver: function overridesResolver(props, styles) { - var ownerState = props.ownerState; - return [styles.root, ownerState.orientation && styles[ownerState.orientation]]; - } -})(function (_ref) { - var ownerState = _ref.ownerState; - return extends_extends(defineProperty_defineProperty({ - width: 40, - flexShrink: 0, - opacity: 0.8 - }, "&.".concat(TabScrollButton_tabScrollButtonClasses.disabled), { - opacity: 0 - }), ownerState.orientation === 'vertical' && { - width: '100%', - height: 40, - '& svg': { - transform: "rotate(".concat(ownerState.isRtl ? -90 : 90, "deg)") - } - }); -}); -var TabScrollButton = /*#__PURE__*/react.forwardRef(function TabScrollButton(inProps, ref) { - var _slots$StartScrollBut, _slots$EndScrollButto; - var props = useThemeProps_useThemeProps({ - props: inProps, - name: 'MuiTabScrollButton' + return function () { + handleTabsScroll.clear(); + }; + }, [handleTabsScroll]); + react.useEffect(function () { + setMounted(true); + }, []); + react.useEffect(function () { + updateIndicatorState(); + updateScrollButtonState(); }); - var className = props.className, - _props$slots = props.slots, - slots = _props$slots === void 0 ? {} : _props$slots, - _props$slotProps = props.slotProps, - slotProps = _props$slotProps === void 0 ? {} : _props$slotProps, - direction = props.direction, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TabScrollButton_excluded); - var theme = styles_useTheme_useTheme(); - var isRtl = theme.direction === 'rtl'; - var ownerState = extends_extends({ - isRtl: isRtl - }, props); - var classes = TabScrollButton_useUtilityClasses(ownerState); - var StartButtonIcon = (_slots$StartScrollBut = slots.StartScrollButtonIcon) != null ? _slots$StartScrollBut : KeyboardArrowLeft; - var EndButtonIcon = (_slots$EndScrollButto = slots.EndScrollButtonIcon) != null ? _slots$EndScrollButto : KeyboardArrowRight; - var startButtonIconProps = useSlotProps({ - elementType: StartButtonIcon, - externalSlotProps: slotProps.startScrollButtonIcon, - additionalProps: { - fontSize: 'small' - }, - ownerState: ownerState - }); - var endButtonIconProps = useSlotProps({ - elementType: EndButtonIcon, - externalSlotProps: slotProps.endScrollButtonIcon, - additionalProps: { - fontSize: 'small' - }, - ownerState: ownerState + react.useEffect(function () { + // Don't animate on the first render. + scrollSelectedIntoView(defaultIndicatorStyle !== indicatorStyle); + }, [scrollSelectedIntoView, indicatorStyle]); + react.useImperativeHandle(action, function () { + return { + updateIndicator: updateIndicatorState, + updateScrollButtons: updateScrollButtonState + }; + }, [updateIndicatorState, updateScrollButtonState]); + var indicator = /*#__PURE__*/(0,jsx_runtime.jsx)(TabsIndicator, extends_extends({}, TabIndicatorProps, { + className: clsx_m(classes.indicator, TabIndicatorProps.className), + ownerState: ownerState, + style: extends_extends({}, indicatorStyle, TabIndicatorProps.style) + })); + var childIndex = 0; + var children = react.Children.map(childrenProp, function (child) { + if (! /*#__PURE__*/react.isValidElement(child)) { + return null; + } + if (false) {} + var childValue = child.props.value === undefined ? childIndex : child.props.value; + valueToIndex.set(childValue, childIndex); + var selected = childValue === value; + childIndex += 1; + return /*#__PURE__*/react.cloneElement(child, extends_extends({ + fullWidth: variant === 'fullWidth', + indicator: selected && !mounted && indicator, + selected: selected, + selectionFollowsFocus: selectionFollowsFocus, + onChange: onChange, + textColor: textColor, + value: childValue + }, childIndex === 1 && value === false && !child.props.tabIndex ? { + tabIndex: 0 + } : {})); }); - return /*#__PURE__*/(0,jsx_runtime.jsx)(TabScrollButtonRoot, extends_extends({ - component: "div", + var handleKeyDown = function handleKeyDown(event) { + var list = tabListRef.current; + var currentFocus = utils_ownerDocument(list).activeElement; + // Keyboard navigation assumes that [role="tab"] are siblings + // though we might warn in the future about nested, interactive elements + // as a a11y violation + var role = currentFocus.getAttribute('role'); + if (role !== 'tab') { + return; + } + var previousItemKey = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp'; + var nextItemKey = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown'; + if (orientation === 'horizontal' && isRtl) { + // swap previousItemKey with nextItemKey + previousItemKey = 'ArrowRight'; + nextItemKey = 'ArrowLeft'; + } + switch (event.key) { + case previousItemKey: + event.preventDefault(); + Tabs_moveFocus(list, currentFocus, Tabs_previousItem); + break; + case nextItemKey: + event.preventDefault(); + Tabs_moveFocus(list, currentFocus, Tabs_nextItem); + break; + case 'Home': + event.preventDefault(); + Tabs_moveFocus(list, null, Tabs_nextItem); + break; + case 'End': + event.preventDefault(); + Tabs_moveFocus(list, null, Tabs_previousItem); + break; + default: + break; + } + }; + var conditionalElements = getConditionalElements(); + return /*#__PURE__*/(0,jsx_runtime.jsxs)(TabsRoot, extends_extends({ className: clsx_m(classes.root, className), - ref: ref, - role: null, ownerState: ownerState, - tabIndex: null + ref: ref, + as: component }, other, { - children: direction === 'left' ? /*#__PURE__*/(0,jsx_runtime.jsx)(StartButtonIcon, extends_extends({}, startButtonIconProps)) : /*#__PURE__*/(0,jsx_runtime.jsx)(EndButtonIcon, extends_extends({}, endButtonIconProps)) + children: [conditionalElements.scrollButtonStart, conditionalElements.scrollbarSizeListener, /*#__PURE__*/(0,jsx_runtime.jsxs)(TabsScroller, { + className: classes.scroller, + ownerState: ownerState, + style: defineProperty_defineProperty({ + overflow: scrollerStyle.overflow + }, vertical ? "margin".concat(isRtl ? 'Left' : 'Right') : 'marginBottom', visibleScrollbar ? undefined : -scrollerStyle.scrollbarWidth), + ref: tabsRef, + onScroll: handleTabsScroll, + children: [/*#__PURE__*/(0,jsx_runtime.jsx)(FlexContainer, { + "aria-label": ariaLabel, + "aria-labelledby": ariaLabelledBy, + "aria-orientation": orientation === 'vertical' ? 'vertical' : null, + className: classes.flexContainer, + ownerState: ownerState, + onKeyDown: handleKeyDown, + ref: tabListRef, + role: "tablist", + children: children + }), mounted && indicator] + }), conditionalElements.scrollButtonEnd] })); }); false ? 0 : void 0; -/* harmony default export */ var TabScrollButton_TabScrollButton = (TabScrollButton); -;// CONCATENATED MODULE: ./node_modules/@mui/material/Tabs/tabsClasses.js +/* harmony default export */ var Tabs_Tabs = (Tabs); +;// CONCATENATED MODULE: ./node_modules/@mui/lab/TabList/TabList.js -function getTabsUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTabs', slot); +var TabList_excluded = ["children"]; + + + + + +var TabList = /*#__PURE__*/react.forwardRef(function TabList(props, ref) { + var childrenProp = props.children, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TabList_excluded); + var context = useTabContext(); + if (context === null) { + throw new TypeError('No TabContext provided'); + } + var children = react.Children.map(childrenProp, function (child) { + if (! /*#__PURE__*/react.isValidElement(child)) { + return null; + } + return /*#__PURE__*/react.cloneElement(child, { + // SOMEDAY: `Tabs` will set those themselves + 'aria-controls': getPanelId(context, child.props.value), + id: getTabId(context, child.props.value) + }); + }); + return /*#__PURE__*/(0,jsx_runtime.jsx)(Tabs_Tabs, extends_extends({}, other, { + ref: ref, + value: context.value, + children: children + })); +}); + false ? 0 : void 0; +/* harmony default export */ var TabList_TabList = (TabList); +;// CONCATENATED MODULE: ./node_modules/@mui/lab/TabPanel/tabPanelClasses.js + + +function getTabPanelUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTabPanel', slot); } -var tabsClasses = generateUtilityClasses('MuiTabs', ['root', 'vertical', 'flexContainer', 'flexContainerVertical', 'centered', 'scroller', 'fixed', 'scrollableX', 'scrollableY', 'hideScrollbar', 'scrollButtons', 'scrollButtonsHideMobile', 'indicator']); -/* harmony default export */ var Tabs_tabsClasses = (tabsClasses); -;// CONCATENATED MODULE: ./node_modules/@mui/material/Tabs/Tabs.js +var tabPanelClasses = generateUtilityClasses('MuiTabPanel', ['root']); +/* harmony default export */ var TabPanel_tabPanelClasses = ((/* unused pure expression or super */ null && (tabPanelClasses))); +;// CONCATENATED MODULE: ./node_modules/@mui/lab/TabPanel/TabPanel.js + +var TabPanel_excluded = ["children", "className", "value"]; -var Tabs_excluded = ["aria-label", "aria-labelledby", "action", "centered", "children", "className", "component", "allowScrollButtonsMobile", "indicatorColor", "onChange", "orientation", "ScrollButtonComponent", "scrollButtons", "selectionFollowsFocus", "slots", "slotProps", "TabIndicatorProps", "TabScrollButtonProps", "textColor", "value", "variant", "visibleScrollbar"]; +var TabPanel_useUtilityClasses = function useUtilityClasses(ownerState) { + var classes = ownerState.classes; + var slots = { + root: ['root'] + }; + return composeClasses(slots, getTabPanelUtilityClass, classes); +}; +var TabPanelRoot = styles_styled('div', { + name: 'MuiTabPanel', + slot: 'Root', + overridesResolver: function overridesResolver(props, styles) { + return styles.root; + } +})(function (_ref) { + var theme = _ref.theme; + return { + padding: theme.spacing(3) + }; +}); +var TabPanel = /*#__PURE__*/react.forwardRef(function TabPanel(inProps, ref) { + var props = useThemeProps_useThemeProps({ + props: inProps, + name: 'MuiTabPanel' + }); + var children = props.children, + className = props.className, + value = props.value, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TabPanel_excluded); + var ownerState = extends_extends({}, props); + var classes = TabPanel_useUtilityClasses(ownerState); + var context = useTabContext(); + if (context === null) { + throw new TypeError('No TabContext provided'); + } + var id = getPanelId(context, value); + var tabId = getTabId(context, value); + return /*#__PURE__*/(0,jsx_runtime.jsx)(TabPanelRoot, extends_extends({ + "aria-labelledby": tabId, + className: clsx_m(classes.root, className), + hidden: value !== context.value, + id: id, + ref: ref, + role: "tabpanel", + ownerState: ownerState + }, other, { + children: value === context.value && children + })); +}); + false ? 0 : void 0; +/* harmony default export */ var TabPanel_TabPanel = (TabPanel); +;// CONCATENATED MODULE: ./src/components/result-view/SidePanel.tsx +function useSidePanelTabs(provider){var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),selectedTab=_useState2[0],setSelectedTab=_useState2[1];var handleTabChange=(0,react.useCallback)(function(_e,value){setSelectedTab(value);},[]);var tabs=(0,react.useMemo)(function(){return(provider===null||provider===void 0?void 0:provider.buildSidePanelTabs())||[];},[provider]);(0,react.useEffect)(function(){if(!(tabs!==null&&tabs!==void 0&&tabs.length)){setSelectedTab("");return;}if(!selectedTab){setSelectedTab(tabs[0].label);return;}if(!tabs.find(function(x){return x.label===selectedTab;})){// reset it after the type of selected item has changed +setSelectedTab(tabs[0].label);}},[selectedTab,tabs]);return{tabs:tabs,selectedTab:selectedTab,handleTabChange:handleTabChange};}var SidePanel=function SidePanel(props){var theme=styles_useTheme_useTheme();var _useSidePanelTabs=useSidePanelTabs(props.provider),tabs=_useSidePanelTabs.tabs,selectedTab=_useSidePanelTabs.selectedTab,handleTabChange=_useSidePanelTabs.handleTabChange;if(!selectedTab||!tabs.find(function(x){return x.label===selectedTab;})){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{});}if(!props.provider){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{});}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"100%",display:"flex",flexDirection:"column",overflow:"hidden",children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TabContext,{value:selectedTab,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{minHeight:theme.consts.appBarHeight,display:"flex",flexDirection:"column",flex:"0 0 auto",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flex:"1 1 auto",display:"flex",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"1 1 auto",pt:"25px",pl:"35px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h4",children:props.provider.buildSidePanelTitle()})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",pt:"10px",pr:"10px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(CloseIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"36px",flex:"0 0 auto",p:"0 30px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabList_TabList,{onChange:handleTabChange,children:tabs.map(function(tab,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Tab_Tab,{label:tab.label,value:tab.label},tab.label);})})})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:0}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{overflow:"auto",p:"30px",children:tabs.map(function(tab){return/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_light,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabPanel_TabPanel,{value:tab.label,sx:{padding:0},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{sx:{padding:'10px 0'},children:tab.content})})},tab.label);})})]})});}; +;// CONCATENATED MODULE: ./node_modules/@mui/material/Table/TableContext.js +/** + * @ignore - internal component. + */ +var TableContext = /*#__PURE__*/react.createContext(); +if (false) {} +/* harmony default export */ var Table_TableContext = (TableContext); +;// CONCATENATED MODULE: ./node_modules/@mui/material/Table/tableClasses.js +function getTableUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTable', slot); +} +var tableClasses = generateUtilityClasses('MuiTable', ['root', 'stickyHeader']); +/* harmony default export */ var Table_tableClasses = ((/* unused pure expression or super */ null && (tableClasses))); +;// CONCATENATED MODULE: ./node_modules/@mui/material/Table/Table.js +var Table_excluded = ["className", "component", "padding", "size", "stickyHeader"]; @@ -58412,691 +58615,458 @@ var Tabs_excluded = ["aria-label", "aria-labelledby", "action", "centered", "chi -var Tabs_nextItem = function nextItem(list, item) { - if (list === item) { - return list.firstChild; - } - if (item && item.nextElementSibling) { - return item.nextElementSibling; - } - return list.firstChild; +var Table_useUtilityClasses = function useUtilityClasses(ownerState) { + var classes = ownerState.classes, + stickyHeader = ownerState.stickyHeader; + var slots = { + root: ['root', stickyHeader && 'stickyHeader'] + }; + return composeClasses(slots, getTableUtilityClass, classes); }; -var Tabs_previousItem = function previousItem(list, item) { - if (list === item) { - return list.lastChild; - } - if (item && item.previousElementSibling) { - return item.previousElementSibling; - } - return list.lastChild; -}; -var Tabs_moveFocus = function moveFocus(list, currentFocus, traversalFunction) { - var wrappedOnce = false; - var nextFocus = traversalFunction(list, currentFocus); - while (nextFocus) { - // Prevent infinite loop. - if (nextFocus === list.firstChild) { - if (wrappedOnce) { - return; - } - wrappedOnce = true; - } - - // Same logic as useAutocomplete.js - var nextFocusDisabled = nextFocus.disabled || nextFocus.getAttribute('aria-disabled') === 'true'; - if (!nextFocus.hasAttribute('tabindex') || nextFocusDisabled) { - // Move to the next element. - nextFocus = traversalFunction(list, nextFocus); - } else { - nextFocus.focus(); - return; - } - } -}; -var Tabs_useUtilityClasses = function useUtilityClasses(ownerState) { - var vertical = ownerState.vertical, - fixed = ownerState.fixed, - hideScrollbar = ownerState.hideScrollbar, - scrollableX = ownerState.scrollableX, - scrollableY = ownerState.scrollableY, - centered = ownerState.centered, - scrollButtonsHideMobile = ownerState.scrollButtonsHideMobile, - classes = ownerState.classes; - var slots = { - root: ['root', vertical && 'vertical'], - scroller: ['scroller', fixed && 'fixed', hideScrollbar && 'hideScrollbar', scrollableX && 'scrollableX', scrollableY && 'scrollableY'], - flexContainer: ['flexContainer', vertical && 'flexContainerVertical', centered && 'centered'], - indicator: ['indicator'], - scrollButtons: ['scrollButtons', scrollButtonsHideMobile && 'scrollButtonsHideMobile'], - scrollableX: [scrollableX && 'scrollableX'], - hideScrollbar: [hideScrollbar && 'hideScrollbar'] - }; - return composeClasses(slots, getTabsUtilityClass, classes); -}; -var TabsRoot = styles_styled('div', { - name: 'MuiTabs', +var TableRoot = styles_styled('table', { + name: 'MuiTable', slot: 'Root', overridesResolver: function overridesResolver(props, styles) { var ownerState = props.ownerState; - return [defineProperty_defineProperty({}, "& .".concat(Tabs_tabsClasses.scrollButtons), styles.scrollButtons), defineProperty_defineProperty({}, "& .".concat(Tabs_tabsClasses.scrollButtons), ownerState.scrollButtonsHideMobile && styles.scrollButtonsHideMobile), styles.root, ownerState.vertical && styles.vertical]; - } -})(function (_ref3) { - var ownerState = _ref3.ownerState, - theme = _ref3.theme; - return extends_extends({ - overflow: 'hidden', - minHeight: 48, - // Add iOS momentum scrolling for iOS < 13.0 - WebkitOverflowScrolling: 'touch', - display: 'flex' - }, ownerState.vertical && { - flexDirection: 'column' - }, ownerState.scrollButtonsHideMobile && defineProperty_defineProperty({}, "& .".concat(Tabs_tabsClasses.scrollButtons), defineProperty_defineProperty({}, theme.breakpoints.down('sm'), { - display: 'none' - }))); -}); -var TabsScroller = styles_styled('div', { - name: 'MuiTabs', - slot: 'Scroller', - overridesResolver: function overridesResolver(props, styles) { - var ownerState = props.ownerState; - return [styles.scroller, ownerState.fixed && styles.fixed, ownerState.hideScrollbar && styles.hideScrollbar, ownerState.scrollableX && styles.scrollableX, ownerState.scrollableY && styles.scrollableY]; + return [styles.root, ownerState.stickyHeader && styles.stickyHeader]; } -})(function (_ref5) { - var ownerState = _ref5.ownerState; +})(function (_ref) { + var theme = _ref.theme, + ownerState = _ref.ownerState; return extends_extends({ - position: 'relative', - display: 'inline-block', - flex: '1 1 auto', - whiteSpace: 'nowrap' - }, ownerState.fixed && { - overflowX: 'hidden', - width: '100%' - }, ownerState.hideScrollbar && { - // Hide dimensionless scrollbar on macOS - scrollbarWidth: 'none', - // Firefox - '&::-webkit-scrollbar': { - display: 'none' // Safari + Chrome - } - }, ownerState.scrollableX && { - overflowX: 'auto', - overflowY: 'hidden' - }, ownerState.scrollableY && { - overflowY: 'auto', - overflowX: 'hidden' + display: 'table', + width: '100%', + borderCollapse: 'collapse', + borderSpacing: 0, + '& caption': extends_extends({}, theme.typography.body2, { + padding: theme.spacing(2), + color: (theme.vars || theme).palette.text.secondary, + textAlign: 'left', + captionSide: 'bottom' + }) + }, ownerState.stickyHeader && { + borderCollapse: 'separate' }); }); -var FlexContainer = styles_styled('div', { - name: 'MuiTabs', - slot: 'FlexContainer', - overridesResolver: function overridesResolver(props, styles) { - var ownerState = props.ownerState; - return [styles.flexContainer, ownerState.vertical && styles.flexContainerVertical, ownerState.centered && styles.centered]; - } -})(function (_ref6) { - var ownerState = _ref6.ownerState; - return extends_extends({ - display: 'flex' - }, ownerState.vertical && { - flexDirection: 'column' - }, ownerState.centered && { - justifyContent: 'center' +var defaultComponent = 'table'; +var Table = /*#__PURE__*/react.forwardRef(function Table(inProps, ref) { + var props = useThemeProps_useThemeProps({ + props: inProps, + name: 'MuiTable' + }); + var className = props.className, + _props$component = props.component, + component = _props$component === void 0 ? defaultComponent : _props$component, + _props$padding = props.padding, + padding = _props$padding === void 0 ? 'normal' : _props$padding, + _props$size = props.size, + size = _props$size === void 0 ? 'medium' : _props$size, + _props$stickyHeader = props.stickyHeader, + stickyHeader = _props$stickyHeader === void 0 ? false : _props$stickyHeader, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, Table_excluded); + var ownerState = extends_extends({}, props, { + component: component, + padding: padding, + size: size, + stickyHeader: stickyHeader + }); + var classes = Table_useUtilityClasses(ownerState); + var table = react.useMemo(function () { + return { + padding: padding, + size: size, + stickyHeader: stickyHeader + }; + }, [padding, size, stickyHeader]); + return /*#__PURE__*/(0,jsx_runtime.jsx)(Table_TableContext.Provider, { + value: table, + children: /*#__PURE__*/(0,jsx_runtime.jsx)(TableRoot, extends_extends({ + as: component, + role: component === defaultComponent ? null : 'table', + ref: ref, + className: clsx_m(classes.root, className), + ownerState: ownerState + }, other)) }); }); -var TabsIndicator = styles_styled('span', { - name: 'MuiTabs', - slot: 'Indicator', + false ? 0 : void 0; +/* harmony default export */ var Table_Table = (Table); +;// CONCATENATED MODULE: ./node_modules/@mui/material/Table/Tablelvl2Context.js + + +/** + * @ignore - internal component. + */ +var Tablelvl2Context = /*#__PURE__*/react.createContext(); +if (false) {} +/* harmony default export */ var Table_Tablelvl2Context = (Tablelvl2Context); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableBody/tableBodyClasses.js + + +function getTableBodyUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTableBody', slot); +} +var tableBodyClasses = generateUtilityClasses('MuiTableBody', ['root']); +/* harmony default export */ var TableBody_tableBodyClasses = ((/* unused pure expression or super */ null && (tableBodyClasses))); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableBody/TableBody.js + + +var TableBody_excluded = ["className", "component"]; + + + + + + + + + +var TableBody_useUtilityClasses = function useUtilityClasses(ownerState) { + var classes = ownerState.classes; + var slots = { + root: ['root'] + }; + return composeClasses(slots, getTableBodyUtilityClass, classes); +}; +var TableBodyRoot = styles_styled('tbody', { + name: 'MuiTableBody', + slot: 'Root', overridesResolver: function overridesResolver(props, styles) { - return styles.indicator; + return styles.root; } -})(function (_ref7) { - var ownerState = _ref7.ownerState, - theme = _ref7.theme; - return extends_extends({ - position: 'absolute', - height: 2, - bottom: 0, - width: '100%', - transition: theme.transitions.create() - }, ownerState.indicatorColor === 'primary' && { - backgroundColor: (theme.vars || theme).palette.primary.main - }, ownerState.indicatorColor === 'secondary' && { - backgroundColor: (theme.vars || theme).palette.secondary.main - }, ownerState.vertical && { - height: '100%', - width: 2, - right: 0 - }); -}); -var TabsScrollbarSize = styles_styled(ScrollbarSize, { - name: 'MuiTabs', - slot: 'ScrollbarSize' })({ - overflowX: 'auto', - overflowY: 'hidden', - // Hide dimensionless scrollbar on macOS - scrollbarWidth: 'none', - // Firefox - '&::-webkit-scrollbar': { - display: 'none' // Safari + Chrome - } + display: 'table-row-group' }); - -var defaultIndicatorStyle = {}; -var warnedOnceTabPresent = false; -var Tabs = /*#__PURE__*/react.forwardRef(function Tabs(inProps, ref) { +var tablelvl2 = { + variant: 'body' +}; +var TableBody_defaultComponent = 'tbody'; +var TableBody = /*#__PURE__*/react.forwardRef(function TableBody(inProps, ref) { var props = useThemeProps_useThemeProps({ props: inProps, - name: 'MuiTabs' + name: 'MuiTableBody' }); - var theme = styles_useTheme_useTheme(); - var isRtl = theme.direction === 'rtl'; - var ariaLabel = props['aria-label'], - ariaLabelledBy = props['aria-labelledby'], - action = props.action, - _props$centered = props.centered, - centered = _props$centered === void 0 ? false : _props$centered, - childrenProp = props.children, - className = props.className, + var className = props.className, _props$component = props.component, - component = _props$component === void 0 ? 'div' : _props$component, - _props$allowScrollBut = props.allowScrollButtonsMobile, - allowScrollButtonsMobile = _props$allowScrollBut === void 0 ? false : _props$allowScrollBut, - _props$indicatorColor = props.indicatorColor, - indicatorColor = _props$indicatorColor === void 0 ? 'primary' : _props$indicatorColor, - onChange = props.onChange, - _props$orientation = props.orientation, - orientation = _props$orientation === void 0 ? 'horizontal' : _props$orientation, - _props$ScrollButtonCo = props.ScrollButtonComponent, - ScrollButtonComponent = _props$ScrollButtonCo === void 0 ? TabScrollButton_TabScrollButton : _props$ScrollButtonCo, - _props$scrollButtons = props.scrollButtons, - scrollButtons = _props$scrollButtons === void 0 ? 'auto' : _props$scrollButtons, - selectionFollowsFocus = props.selectionFollowsFocus, - _props$slots = props.slots, - slots = _props$slots === void 0 ? {} : _props$slots, - _props$slotProps = props.slotProps, - slotProps = _props$slotProps === void 0 ? {} : _props$slotProps, - _props$TabIndicatorPr = props.TabIndicatorProps, - TabIndicatorProps = _props$TabIndicatorPr === void 0 ? {} : _props$TabIndicatorPr, - _props$TabScrollButto = props.TabScrollButtonProps, - TabScrollButtonProps = _props$TabScrollButto === void 0 ? {} : _props$TabScrollButto, - _props$textColor = props.textColor, - textColor = _props$textColor === void 0 ? 'primary' : _props$textColor, - value = props.value, - _props$variant = props.variant, - variant = _props$variant === void 0 ? 'standard' : _props$variant, - _props$visibleScrollb = props.visibleScrollbar, - visibleScrollbar = _props$visibleScrollb === void 0 ? false : _props$visibleScrollb, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, Tabs_excluded); - var scrollable = variant === 'scrollable'; - var vertical = orientation === 'vertical'; - var scrollStart = vertical ? 'scrollTop' : 'scrollLeft'; - var start = vertical ? 'top' : 'left'; - var end = vertical ? 'bottom' : 'right'; - var clientSize = vertical ? 'clientHeight' : 'clientWidth'; - var size = vertical ? 'height' : 'width'; + component = _props$component === void 0 ? TableBody_defaultComponent : _props$component, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableBody_excluded); var ownerState = extends_extends({}, props, { - component: component, - allowScrollButtonsMobile: allowScrollButtonsMobile, - indicatorColor: indicatorColor, - orientation: orientation, - vertical: vertical, - scrollButtons: scrollButtons, - textColor: textColor, - variant: variant, - visibleScrollbar: visibleScrollbar, - fixed: !scrollable, - hideScrollbar: scrollable && !visibleScrollbar, - scrollableX: scrollable && !vertical, - scrollableY: scrollable && vertical, - centered: centered && !scrollable, - scrollButtonsHideMobile: !allowScrollButtonsMobile - }); - var classes = Tabs_useUtilityClasses(ownerState); - var startScrollButtonIconProps = useSlotProps({ - elementType: slots.StartScrollButtonIcon, - externalSlotProps: slotProps.startScrollButtonIcon, - ownerState: ownerState + component: component }); - var endScrollButtonIconProps = useSlotProps({ - elementType: slots.EndScrollButtonIcon, - externalSlotProps: slotProps.endScrollButtonIcon, - ownerState: ownerState + var classes = TableBody_useUtilityClasses(ownerState); + return /*#__PURE__*/(0,jsx_runtime.jsx)(Table_Tablelvl2Context.Provider, { + value: tablelvl2, + children: /*#__PURE__*/(0,jsx_runtime.jsx)(TableBodyRoot, extends_extends({ + className: clsx_m(classes.root, className), + as: component, + ref: ref, + role: component === TableBody_defaultComponent ? null : 'rowgroup', + ownerState: ownerState + }, other)) }); - if (false) {} - var _React$useState = react.useState(false), - _React$useState2 = slicedToArray_slicedToArray(_React$useState, 2), - mounted = _React$useState2[0], - setMounted = _React$useState2[1]; - var _React$useState3 = react.useState(defaultIndicatorStyle), - _React$useState4 = slicedToArray_slicedToArray(_React$useState3, 2), - indicatorStyle = _React$useState4[0], - setIndicatorStyle = _React$useState4[1]; - var _React$useState5 = react.useState({ - start: false, - end: false - }), - _React$useState6 = slicedToArray_slicedToArray(_React$useState5, 2), - displayScroll = _React$useState6[0], - setDisplayScroll = _React$useState6[1]; - var _React$useState7 = react.useState({ - overflow: 'hidden', - scrollbarWidth: 0 - }), - _React$useState8 = slicedToArray_slicedToArray(_React$useState7, 2), - scrollerStyle = _React$useState8[0], - setScrollerStyle = _React$useState8[1]; - var valueToIndex = new Map(); - var tabsRef = react.useRef(null); - var tabListRef = react.useRef(null); - var getTabsMeta = function getTabsMeta() { - var tabsNode = tabsRef.current; - var tabsMeta; - if (tabsNode) { - var rect = tabsNode.getBoundingClientRect(); - // create a new object with ClientRect class props + scrollLeft - tabsMeta = { - clientWidth: tabsNode.clientWidth, - scrollLeft: tabsNode.scrollLeft, - scrollTop: tabsNode.scrollTop, - scrollLeftNormalized: getNormalizedScrollLeft(tabsNode, theme.direction), - scrollWidth: tabsNode.scrollWidth, - top: rect.top, - bottom: rect.bottom, - left: rect.left, - right: rect.right - }; - } - var tabMeta; - if (tabsNode && value !== false) { - var _children = tabListRef.current.children; - if (_children.length > 0) { - var tab = _children[valueToIndex.get(value)]; - if (false) {} - tabMeta = tab ? tab.getBoundingClientRect() : null; - if (false) {} - } - } - return { - tabsMeta: tabsMeta, - tabMeta: tabMeta - }; +}); + false ? 0 : void 0; +/* harmony default export */ var TableBody_TableBody = (TableBody); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableCell/tableCellClasses.js + + +function getTableCellUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTableCell', slot); +} +var tableCellClasses = generateUtilityClasses('MuiTableCell', ['root', 'head', 'body', 'footer', 'sizeSmall', 'sizeMedium', 'paddingCheckbox', 'paddingNone', 'alignLeft', 'alignCenter', 'alignRight', 'alignJustify', 'stickyHeader']); +/* harmony default export */ var TableCell_tableCellClasses = (tableCellClasses); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableCell/TableCell.js + + + +var TableCell_excluded = ["align", "className", "component", "padding", "scope", "size", "sortDirection", "variant"]; + + + + + + + + + + + + +var TableCell_useUtilityClasses = function useUtilityClasses(ownerState) { + var classes = ownerState.classes, + variant = ownerState.variant, + align = ownerState.align, + padding = ownerState.padding, + size = ownerState.size, + stickyHeader = ownerState.stickyHeader; + var slots = { + root: ['root', variant, stickyHeader && 'stickyHeader', align !== 'inherit' && "align".concat(utils_capitalize(align)), padding !== 'normal' && "padding".concat(utils_capitalize(padding)), "size".concat(utils_capitalize(size))] }; - var updateIndicatorState = utils_useEventCallback(function () { - var _newIndicatorStyle; - var _getTabsMeta = getTabsMeta(), - tabsMeta = _getTabsMeta.tabsMeta, - tabMeta = _getTabsMeta.tabMeta; - var startValue = 0; - var startIndicator; - if (vertical) { - startIndicator = 'top'; - if (tabMeta && tabsMeta) { - startValue = tabMeta.top - tabsMeta.top + tabsMeta.scrollTop; - } - } else { - startIndicator = isRtl ? 'right' : 'left'; - if (tabMeta && tabsMeta) { - var correction = isRtl ? tabsMeta.scrollLeftNormalized + tabsMeta.clientWidth - tabsMeta.scrollWidth : tabsMeta.scrollLeft; - startValue = (isRtl ? -1 : 1) * (tabMeta[startIndicator] - tabsMeta[startIndicator] + correction); - } + return composeClasses(slots, getTableCellUtilityClass, classes); +}; +var TableCellRoot = styles_styled('td', { + name: 'MuiTableCell', + slot: 'Root', + overridesResolver: function overridesResolver(props, styles) { + var ownerState = props.ownerState; + return [styles.root, styles[ownerState.variant], styles["size".concat(utils_capitalize(ownerState.size))], ownerState.padding !== 'normal' && styles["padding".concat(utils_capitalize(ownerState.padding))], ownerState.align !== 'inherit' && styles["align".concat(utils_capitalize(ownerState.align))], ownerState.stickyHeader && styles.stickyHeader]; + } +})(function (_ref) { + var theme = _ref.theme, + ownerState = _ref.ownerState; + return extends_extends({}, theme.typography.body2, { + display: 'table-cell', + verticalAlign: 'inherit', + // Workaround for a rendering bug with spanned columns in Chrome 62.0. + // Removes the alpha (sets it to 1), and lightens or darkens the theme color. + borderBottom: theme.vars ? "1px solid ".concat(theme.vars.palette.TableCell.border) : "1px solid\n ".concat(theme.palette.mode === 'light' ? lighten(alpha(theme.palette.divider, 1), 0.88) : darken(alpha(theme.palette.divider, 1), 0.68)), + textAlign: 'left', + padding: 16 + }, ownerState.variant === 'head' && { + color: (theme.vars || theme).palette.text.primary, + lineHeight: theme.typography.pxToRem(24), + fontWeight: theme.typography.fontWeightMedium + }, ownerState.variant === 'body' && { + color: (theme.vars || theme).palette.text.primary + }, ownerState.variant === 'footer' && { + color: (theme.vars || theme).palette.text.secondary, + lineHeight: theme.typography.pxToRem(21), + fontSize: theme.typography.pxToRem(12) + }, ownerState.size === 'small' && defineProperty_defineProperty({ + padding: '6px 16px' + }, "&.".concat(TableCell_tableCellClasses.paddingCheckbox), { + width: 24, + // prevent the checkbox column from growing + padding: '0 12px 0 16px', + '& > *': { + padding: 0 } - var newIndicatorStyle = (_newIndicatorStyle = {}, defineProperty_defineProperty(_newIndicatorStyle, startIndicator, startValue), defineProperty_defineProperty(_newIndicatorStyle, size, tabMeta ? tabMeta[size] : 0), _newIndicatorStyle); + }), ownerState.padding === 'checkbox' && { + width: 48, + // prevent the checkbox column from growing + padding: '0 0 0 4px' + }, ownerState.padding === 'none' && { + padding: 0 + }, ownerState.align === 'left' && { + textAlign: 'left' + }, ownerState.align === 'center' && { + textAlign: 'center' + }, ownerState.align === 'right' && { + textAlign: 'right', + flexDirection: 'row-reverse' + }, ownerState.align === 'justify' && { + textAlign: 'justify' + }, ownerState.stickyHeader && { + position: 'sticky', + top: 0, + zIndex: 2, + backgroundColor: (theme.vars || theme).palette.background.default + }); +}); - // IE11 support, replace with Number.isNaN - // eslint-disable-next-line no-restricted-globals - if (isNaN(indicatorStyle[startIndicator]) || isNaN(indicatorStyle[size])) { - setIndicatorStyle(newIndicatorStyle); - } else { - var dStart = Math.abs(indicatorStyle[startIndicator] - newIndicatorStyle[startIndicator]); - var dSize = Math.abs(indicatorStyle[size] - newIndicatorStyle[size]); - if (dStart >= 1 || dSize >= 1) { - setIndicatorStyle(newIndicatorStyle); - } - } +/** + * The component renders a `` element when the parent context is a header + * or otherwise a `` element. + */ +var TableCell = /*#__PURE__*/react.forwardRef(function TableCell(inProps, ref) { + var props = useThemeProps_useThemeProps({ + props: inProps, + name: 'MuiTableCell' }); - var scroll = function scroll(scrollValue) { - var _ref8 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - _ref8$animation = _ref8.animation, - animation = _ref8$animation === void 0 ? true : _ref8$animation; - if (animation) { - animate(scrollStart, tabsRef.current, scrollValue, { - duration: theme.transitions.duration.standard - }); - } else { - tabsRef.current[scrollStart] = scrollValue; - } - }; - var moveTabsScroll = function moveTabsScroll(delta) { - var scrollValue = tabsRef.current[scrollStart]; - if (vertical) { - scrollValue += delta; - } else { - scrollValue += delta * (isRtl ? -1 : 1); - // Fix for Edge - scrollValue *= isRtl && detectScrollType() === 'reverse' ? -1 : 1; - } - scroll(scrollValue); - }; - var getScrollSize = function getScrollSize() { - var containerSize = tabsRef.current[clientSize]; - var totalSize = 0; - var children = Array.from(tabListRef.current.children); - for (var i = 0; i < children.length; i += 1) { - var tab = children[i]; - if (totalSize + tab[clientSize] > containerSize) { - // If the first item is longer than the container size, then only scroll - // by the container size. - if (i === 0) { - totalSize = containerSize; - } - break; - } - totalSize += tab[clientSize]; - } - return totalSize; - }; - var handleStartScrollClick = function handleStartScrollClick() { - moveTabsScroll(-1 * getScrollSize()); - }; - var handleEndScrollClick = function handleEndScrollClick() { - moveTabsScroll(getScrollSize()); - }; + var _props$align = props.align, + align = _props$align === void 0 ? 'inherit' : _props$align, + className = props.className, + componentProp = props.component, + paddingProp = props.padding, + scopeProp = props.scope, + sizeProp = props.size, + sortDirection = props.sortDirection, + variantProp = props.variant, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableCell_excluded); + var table = react.useContext(Table_TableContext); + var tablelvl2 = react.useContext(Table_Tablelvl2Context); + var isHeadCell = tablelvl2 && tablelvl2.variant === 'head'; + var component; + if (componentProp) { + component = componentProp; + } else { + component = isHeadCell ? 'th' : 'td'; + } + var scope = scopeProp; + // scope is not a valid attribute for elements. + // source: https://html.spec.whatwg.org/multipage/tables.html#the-td-element + if (component === 'td') { + scope = undefined; + } else if (!scope && isHeadCell) { + scope = 'col'; + } + var variant = variantProp || tablelvl2 && tablelvl2.variant; + var ownerState = extends_extends({}, props, { + align: align, + component: component, + padding: paddingProp || (table && table.padding ? table.padding : 'normal'), + size: sizeProp || (table && table.size ? table.size : 'medium'), + sortDirection: sortDirection, + stickyHeader: variant === 'head' && table && table.stickyHeader, + variant: variant + }); + var classes = TableCell_useUtilityClasses(ownerState); + var ariaSort = null; + if (sortDirection) { + ariaSort = sortDirection === 'asc' ? 'ascending' : 'descending'; + } + return /*#__PURE__*/(0,jsx_runtime.jsx)(TableCellRoot, extends_extends({ + as: component, + ref: ref, + className: clsx_m(classes.root, className), + "aria-sort": ariaSort, + scope: scope, + ownerState: ownerState + }, other)); +}); + false ? 0 : void 0; +/* harmony default export */ var TableCell_TableCell = (TableCell); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableContainer/tableContainerClasses.js - // TODO Remove as browser support for hiding the scrollbar - // with CSS improves. - var handleScrollbarSizeChange = react.useCallback(function (scrollbarWidth) { - setScrollerStyle({ - overflow: null, - scrollbarWidth: scrollbarWidth - }); - }, []); - var getConditionalElements = function getConditionalElements() { - var conditionalElements = {}; - conditionalElements.scrollbarSizeListener = scrollable ? /*#__PURE__*/(0,jsx_runtime.jsx)(TabsScrollbarSize, { - onChange: handleScrollbarSizeChange, - className: clsx_m(classes.scrollableX, classes.hideScrollbar) - }) : null; - var scrollButtonsActive = displayScroll.start || displayScroll.end; - var showScrollButtons = scrollable && (scrollButtons === 'auto' && scrollButtonsActive || scrollButtons === true); - conditionalElements.scrollButtonStart = showScrollButtons ? /*#__PURE__*/(0,jsx_runtime.jsx)(ScrollButtonComponent, extends_extends({ - slots: { - StartScrollButtonIcon: slots.StartScrollButtonIcon - }, - slotProps: { - startScrollButtonIcon: startScrollButtonIconProps - }, - orientation: orientation, - direction: isRtl ? 'right' : 'left', - onClick: handleStartScrollClick, - disabled: !displayScroll.start - }, TabScrollButtonProps, { - className: clsx_m(classes.scrollButtons, TabScrollButtonProps.className) - })) : null; - conditionalElements.scrollButtonEnd = showScrollButtons ? /*#__PURE__*/(0,jsx_runtime.jsx)(ScrollButtonComponent, extends_extends({ - slots: { - EndScrollButtonIcon: slots.EndScrollButtonIcon - }, - slotProps: { - endScrollButtonIcon: endScrollButtonIconProps - }, - orientation: orientation, - direction: isRtl ? 'left' : 'right', - onClick: handleEndScrollClick, - disabled: !displayScroll.end - }, TabScrollButtonProps, { - className: clsx_m(classes.scrollButtons, TabScrollButtonProps.className) - })) : null; - return conditionalElements; + +function getTableContainerUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTableContainer', slot); +} +var tableContainerClasses = generateUtilityClasses('MuiTableContainer', ['root']); +/* harmony default export */ var TableContainer_tableContainerClasses = ((/* unused pure expression or super */ null && (tableContainerClasses))); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableContainer/TableContainer.js + + +var TableContainer_excluded = ["className", "component"]; + + + + + + + + +var TableContainer_useUtilityClasses = function useUtilityClasses(ownerState) { + var classes = ownerState.classes; + var slots = { + root: ['root'] }; - var scrollSelectedIntoView = utils_useEventCallback(function (animation) { - var _getTabsMeta2 = getTabsMeta(), - tabsMeta = _getTabsMeta2.tabsMeta, - tabMeta = _getTabsMeta2.tabMeta; - if (!tabMeta || !tabsMeta) { - return; - } - if (tabMeta[start] < tabsMeta[start]) { - // left side of button is out of view - var nextScrollStart = tabsMeta[scrollStart] + (tabMeta[start] - tabsMeta[start]); - scroll(nextScrollStart, { - animation: animation - }); - } else if (tabMeta[end] > tabsMeta[end]) { - // right side of button is out of view - var _nextScrollStart = tabsMeta[scrollStart] + (tabMeta[end] - tabsMeta[end]); - scroll(_nextScrollStart, { - animation: animation - }); - } + return composeClasses(slots, getTableContainerUtilityClass, classes); +}; +var TableContainerRoot = styles_styled('div', { + name: 'MuiTableContainer', + slot: 'Root', + overridesResolver: function overridesResolver(props, styles) { + return styles.root; + } +})({ + width: '100%', + overflowX: 'auto' +}); +var TableContainer = /*#__PURE__*/react.forwardRef(function TableContainer(inProps, ref) { + var props = useThemeProps_useThemeProps({ + props: inProps, + name: 'MuiTableContainer' }); - var updateScrollButtonState = utils_useEventCallback(function () { - if (scrollable && scrollButtons !== false) { - var _tabsRef$current = tabsRef.current, - scrollTop = _tabsRef$current.scrollTop, - scrollHeight = _tabsRef$current.scrollHeight, - clientHeight = _tabsRef$current.clientHeight, - scrollWidth = _tabsRef$current.scrollWidth, - clientWidth = _tabsRef$current.clientWidth; - var showStartScroll; - var showEndScroll; - if (vertical) { - showStartScroll = scrollTop > 1; - showEndScroll = scrollTop < scrollHeight - clientHeight - 1; - } else { - var scrollLeft = getNormalizedScrollLeft(tabsRef.current, theme.direction); - // use 1 for the potential rounding error with browser zooms. - showStartScroll = isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1; - showEndScroll = !isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1; - } - if (showStartScroll !== displayScroll.start || showEndScroll !== displayScroll.end) { - setDisplayScroll({ - start: showStartScroll, - end: showEndScroll - }); - } - } + var className = props.className, + _props$component = props.component, + component = _props$component === void 0 ? 'div' : _props$component, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableContainer_excluded); + var ownerState = extends_extends({}, props, { + component: component }); - react.useEffect(function () { - var handleResize = utils_debounce(function () { - // If the Tabs component is replaced by Suspense with a fallback, the last - // ResizeObserver's handler that runs because of the change in the layout is trying to - // access a dom node that is no longer there (as the fallback component is being shown instead). - // See https://github.com/mui/material-ui/issues/33276 - // TODO: Add tests that will ensure the component is not failing when - // replaced by Suspense with a fallback, once React is updated to version 18 - if (tabsRef.current) { - updateIndicatorState(); - updateScrollButtonState(); - } - }); - var win = utils_ownerWindow(tabsRef.current); - win.addEventListener('resize', handleResize); - var resizeObserver; - if (typeof ResizeObserver !== 'undefined') { - resizeObserver = new ResizeObserver(handleResize); - Array.from(tabListRef.current.children).forEach(function (child) { - resizeObserver.observe(child); - }); - } - return function () { - handleResize.clear(); - win.removeEventListener('resize', handleResize); - if (resizeObserver) { - resizeObserver.disconnect(); - } - }; - }, [updateIndicatorState, updateScrollButtonState]); - var handleTabsScroll = react.useMemo(function () { - return utils_debounce(function () { - updateScrollButtonState(); - }); - }, [updateScrollButtonState]); - react.useEffect(function () { - return function () { - handleTabsScroll.clear(); - }; - }, [handleTabsScroll]); - react.useEffect(function () { - setMounted(true); - }, []); - react.useEffect(function () { - updateIndicatorState(); - updateScrollButtonState(); + var classes = TableContainer_useUtilityClasses(ownerState); + return /*#__PURE__*/(0,jsx_runtime.jsx)(TableContainerRoot, extends_extends({ + ref: ref, + as: component, + className: clsx_m(classes.root, className), + ownerState: ownerState + }, other)); +}); + false ? 0 : void 0; +/* harmony default export */ var TableContainer_TableContainer = (TableContainer); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableHead/tableHeadClasses.js + + +function getTableHeadUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTableHead', slot); +} +var tableHeadClasses = generateUtilityClasses('MuiTableHead', ['root']); +/* harmony default export */ var TableHead_tableHeadClasses = ((/* unused pure expression or super */ null && (tableHeadClasses))); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableHead/TableHead.js + + +var TableHead_excluded = ["className", "component"]; + + + + + + + + + +var TableHead_useUtilityClasses = function useUtilityClasses(ownerState) { + var classes = ownerState.classes; + var slots = { + root: ['root'] + }; + return composeClasses(slots, getTableHeadUtilityClass, classes); +}; +var TableHeadRoot = styles_styled('thead', { + name: 'MuiTableHead', + slot: 'Root', + overridesResolver: function overridesResolver(props, styles) { + return styles.root; + } +})({ + display: 'table-header-group' +}); +var TableHead_tablelvl2 = { + variant: 'head' +}; +var TableHead_defaultComponent = 'thead'; +var TableHead = /*#__PURE__*/react.forwardRef(function TableHead(inProps, ref) { + var props = useThemeProps_useThemeProps({ + props: inProps, + name: 'MuiTableHead' }); - react.useEffect(function () { - // Don't animate on the first render. - scrollSelectedIntoView(defaultIndicatorStyle !== indicatorStyle); - }, [scrollSelectedIntoView, indicatorStyle]); - react.useImperativeHandle(action, function () { - return { - updateIndicator: updateIndicatorState, - updateScrollButtons: updateScrollButtonState - }; - }, [updateIndicatorState, updateScrollButtonState]); - var indicator = /*#__PURE__*/(0,jsx_runtime.jsx)(TabsIndicator, extends_extends({}, TabIndicatorProps, { - className: clsx_m(classes.indicator, TabIndicatorProps.className), - ownerState: ownerState, - style: extends_extends({}, indicatorStyle, TabIndicatorProps.style) - })); - var childIndex = 0; - var children = react.Children.map(childrenProp, function (child) { - if (! /*#__PURE__*/react.isValidElement(child)) { - return null; - } - if (false) {} - var childValue = child.props.value === undefined ? childIndex : child.props.value; - valueToIndex.set(childValue, childIndex); - var selected = childValue === value; - childIndex += 1; - return /*#__PURE__*/react.cloneElement(child, extends_extends({ - fullWidth: variant === 'fullWidth', - indicator: selected && !mounted && indicator, - selected: selected, - selectionFollowsFocus: selectionFollowsFocus, - onChange: onChange, - textColor: textColor, - value: childValue - }, childIndex === 1 && value === false && !child.props.tabIndex ? { - tabIndex: 0 - } : {})); + var className = props.className, + _props$component = props.component, + component = _props$component === void 0 ? TableHead_defaultComponent : _props$component, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableHead_excluded); + var ownerState = extends_extends({}, props, { + component: component + }); + var classes = TableHead_useUtilityClasses(ownerState); + return /*#__PURE__*/(0,jsx_runtime.jsx)(Table_Tablelvl2Context.Provider, { + value: TableHead_tablelvl2, + children: /*#__PURE__*/(0,jsx_runtime.jsx)(TableHeadRoot, extends_extends({ + as: component, + className: clsx_m(classes.root, className), + ref: ref, + role: component === TableHead_defaultComponent ? null : 'rowgroup', + ownerState: ownerState + }, other)) }); - var handleKeyDown = function handleKeyDown(event) { - var list = tabListRef.current; - var currentFocus = utils_ownerDocument(list).activeElement; - // Keyboard navigation assumes that [role="tab"] are siblings - // though we might warn in the future about nested, interactive elements - // as a a11y violation - var role = currentFocus.getAttribute('role'); - if (role !== 'tab') { - return; - } - var previousItemKey = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp'; - var nextItemKey = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown'; - if (orientation === 'horizontal' && isRtl) { - // swap previousItemKey with nextItemKey - previousItemKey = 'ArrowRight'; - nextItemKey = 'ArrowLeft'; - } - switch (event.key) { - case previousItemKey: - event.preventDefault(); - Tabs_moveFocus(list, currentFocus, Tabs_previousItem); - break; - case nextItemKey: - event.preventDefault(); - Tabs_moveFocus(list, currentFocus, Tabs_nextItem); - break; - case 'Home': - event.preventDefault(); - Tabs_moveFocus(list, null, Tabs_nextItem); - break; - case 'End': - event.preventDefault(); - Tabs_moveFocus(list, null, Tabs_previousItem); - break; - default: - break; - } - }; - var conditionalElements = getConditionalElements(); - return /*#__PURE__*/(0,jsx_runtime.jsxs)(TabsRoot, extends_extends({ - className: clsx_m(classes.root, className), - ownerState: ownerState, - ref: ref, - as: component - }, other, { - children: [conditionalElements.scrollButtonStart, conditionalElements.scrollbarSizeListener, /*#__PURE__*/(0,jsx_runtime.jsxs)(TabsScroller, { - className: classes.scroller, - ownerState: ownerState, - style: defineProperty_defineProperty({ - overflow: scrollerStyle.overflow - }, vertical ? "margin".concat(isRtl ? 'Left' : 'Right') : 'marginBottom', visibleScrollbar ? undefined : -scrollerStyle.scrollbarWidth), - ref: tabsRef, - onScroll: handleTabsScroll, - children: [/*#__PURE__*/(0,jsx_runtime.jsx)(FlexContainer, { - "aria-label": ariaLabel, - "aria-labelledby": ariaLabelledBy, - "aria-orientation": orientation === 'vertical' ? 'vertical' : null, - className: classes.flexContainer, - ownerState: ownerState, - onKeyDown: handleKeyDown, - ref: tabListRef, - role: "tablist", - children: children - }), mounted && indicator] - }), conditionalElements.scrollButtonEnd] - })); }); false ? 0 : void 0; -/* harmony default export */ var Tabs_Tabs = (Tabs); -;// CONCATENATED MODULE: ./node_modules/@mui/lab/TabList/TabList.js - - -var TabList_excluded = ["children"]; - - +/* harmony default export */ var TableHead_TableHead = (TableHead); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableRow/tableRowClasses.js +function getTableRowUtilityClass(slot) { + return generateUtilityClass_generateUtilityClass('MuiTableRow', slot); +} +var tableRowClasses = generateUtilityClasses('MuiTableRow', ['root', 'selected', 'hover', 'head', 'footer']); +/* harmony default export */ var TableRow_tableRowClasses = (tableRowClasses); +;// CONCATENATED MODULE: ./node_modules/@mui/material/TableRow/TableRow.js -var TabList = /*#__PURE__*/react.forwardRef(function TabList(props, ref) { - var childrenProp = props.children, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TabList_excluded); - var context = useTabContext(); - if (context === null) { - throw new TypeError('No TabContext provided'); - } - var children = react.Children.map(childrenProp, function (child) { - if (! /*#__PURE__*/react.isValidElement(child)) { - return null; - } - return /*#__PURE__*/react.cloneElement(child, { - // SOMEDAY: `Tabs` will set those themselves - 'aria-controls': getPanelId(context, child.props.value), - id: getTabId(context, child.props.value) - }); - }); - return /*#__PURE__*/(0,jsx_runtime.jsx)(Tabs_Tabs, extends_extends({}, other, { - ref: ref, - value: context.value, - children: children - })); -}); - false ? 0 : void 0; -/* harmony default export */ var TabList_TabList = (TabList); -;// CONCATENATED MODULE: ./node_modules/@mui/lab/TabPanel/tabPanelClasses.js -function getTabPanelUtilityClass(slot) { - return generateUtilityClass_generateUtilityClass('MuiTabPanel', slot); -} -var tabPanelClasses = generateUtilityClasses('MuiTabPanel', ['root']); -/* harmony default export */ var TabPanel_tabPanelClasses = ((/* unused pure expression or super */ null && (tabPanelClasses))); -;// CONCATENATED MODULE: ./node_modules/@mui/lab/TabPanel/TabPanel.js +var TableRow_excluded = ["className", "component", "hover", "selected"]; -var TabPanel_excluded = ["children", "className", "value"]; @@ -59105,72 +59075,196 @@ var TabPanel_excluded = ["children", "className", "value"]; -var TabPanel_useUtilityClasses = function useUtilityClasses(ownerState) { - var classes = ownerState.classes; +var TableRow_useUtilityClasses = function useUtilityClasses(ownerState) { + var classes = ownerState.classes, + selected = ownerState.selected, + hover = ownerState.hover, + head = ownerState.head, + footer = ownerState.footer; var slots = { - root: ['root'] + root: ['root', selected && 'selected', hover && 'hover', head && 'head', footer && 'footer'] }; - return composeClasses(slots, getTabPanelUtilityClass, classes); + return composeClasses(slots, getTableRowUtilityClass, classes); }; -var TabPanelRoot = styles_styled('div', { - name: 'MuiTabPanel', +var TableRowRoot = styles_styled('tr', { + name: 'MuiTableRow', slot: 'Root', overridesResolver: function overridesResolver(props, styles) { - return styles.root; + var ownerState = props.ownerState; + return [styles.root, ownerState.head && styles.head, ownerState.footer && styles.footer]; } })(function (_ref) { + var _ref2; var theme = _ref.theme; - return { - padding: theme.spacing(3) - }; + return _ref2 = { + color: 'inherit', + display: 'table-row', + verticalAlign: 'middle', + // We disable the focus ring for mouse, touch and keyboard users. + outline: 0 + }, defineProperty_defineProperty(_ref2, "&.".concat(TableRow_tableRowClasses.hover, ":hover"), { + backgroundColor: (theme.vars || theme).palette.action.hover + }), defineProperty_defineProperty(_ref2, "&.".concat(TableRow_tableRowClasses.selected), { + backgroundColor: theme.vars ? "rgba(".concat(theme.vars.palette.primary.mainChannel, " / ").concat(theme.vars.palette.action.selectedOpacity, ")") : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), + '&:hover': { + backgroundColor: theme.vars ? "rgba(".concat(theme.vars.palette.primary.mainChannel, " / calc(").concat(theme.vars.palette.action.selectedOpacity, " + ").concat(theme.vars.palette.action.hoverOpacity, "))") : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity) + } + }), _ref2; }); -var TabPanel = /*#__PURE__*/react.forwardRef(function TabPanel(inProps, ref) { +var TableRow_defaultComponent = 'tr'; +/** + * Will automatically set dynamic row height + * based on the material table element parent (head, body, etc). + */ +var TableRow = /*#__PURE__*/react.forwardRef(function TableRow(inProps, ref) { var props = useThemeProps_useThemeProps({ props: inProps, - name: 'MuiTabPanel' + name: 'MuiTableRow' }); - var children = props.children, - className = props.className, - value = props.value, - other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TabPanel_excluded); - var ownerState = extends_extends({}, props); - var classes = TabPanel_useUtilityClasses(ownerState); - var context = useTabContext(); - if (context === null) { - throw new TypeError('No TabContext provided'); - } - var id = getPanelId(context, value); - var tabId = getTabId(context, value); - return /*#__PURE__*/(0,jsx_runtime.jsx)(TabPanelRoot, extends_extends({ - "aria-labelledby": tabId, - className: clsx_m(classes.root, className), - hidden: value !== context.value, - id: id, + var className = props.className, + _props$component = props.component, + component = _props$component === void 0 ? TableRow_defaultComponent : _props$component, + _props$hover = props.hover, + hover = _props$hover === void 0 ? false : _props$hover, + _props$selected = props.selected, + selected = _props$selected === void 0 ? false : _props$selected, + other = objectWithoutPropertiesLoose_objectWithoutPropertiesLoose(props, TableRow_excluded); + var tablelvl2 = react.useContext(Table_Tablelvl2Context); + var ownerState = extends_extends({}, props, { + component: component, + hover: hover, + selected: selected, + head: tablelvl2 && tablelvl2.variant === 'head', + footer: tablelvl2 && tablelvl2.variant === 'footer' + }); + var classes = TableRow_useUtilityClasses(ownerState); + return /*#__PURE__*/(0,jsx_runtime.jsx)(TableRowRoot, extends_extends({ + as: component, ref: ref, - role: "tabpanel", + className: clsx_m(classes.root, className), + role: component === TableRow_defaultComponent ? null : 'row', ownerState: ownerState - }, other, { - children: value === context.value && children - })); + }, other)); }); false ? 0 : void 0; -/* harmony default export */ var TabPanel_TabPanel = (TabPanel); -;// CONCATENATED MODULE: ./src/components/result-view/SidePanel.tsx -var SidePanel=function SidePanel(props){var _props$provider;var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),selectedTab=_useState2[0],setSelectedTab=_useState2[1];var theme=styles_useTheme_useTheme();function handleTabChange(_e,value){setSelectedTab(value);}var tabs=(_props$provider=props.provider)===null||_props$provider===void 0?void 0:_props$provider.buildSidePanelTabs();if(!tabs){tabs=[];}(0,react.useEffect)(function(){var _tabs;if(!((_tabs=tabs)!==null&&_tabs!==void 0&&_tabs.length)){setSelectedTab("");return;}if(!selectedTab){setSelectedTab(tabs[0].label);return;}if(!tabs.find(function(x){return x.label===selectedTab;})){// reset it after the type of selected item has changed -setSelectedTab(tabs[0].label);}// ignore that it wants us to add selectedTab to the deps (it would cause and endless loop) -// eslint-disable-next-line -},[props.provider]);if(!selectedTab||!tabs.find(function(x){return x.label===selectedTab;})){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{});}if(!props.provider){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{});}return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",height:"100%",display:"flex",flexDirection:"column",overflow:"hidden",children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TabContext,{value:selectedTab,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{minHeight:theme.consts.appBarHeight,display:"flex",flexDirection:"column",flex:"0 0 auto",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{flex:"1 1 auto",display:"flex",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"1 1 auto",pt:"25px",pl:"35px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h4",children:props.provider.buildSidePanelTitle()})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",pt:"10px",pr:"10px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(CloseIcon,{})})})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"36px",flex:"0 0 auto",p:"0 30px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabList_TabList,{onChange:handleTabChange,children:tabs.map(function(tab,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Tab_Tab,{label:tab.label,value:tab.label},tab.label);})})})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:0}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{overflow:"auto",p:"30px",children:tabs.map(function(tab){return/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_light,children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabPanel_TabPanel,{value:tab.label,sx:{padding:0},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Paper_Paper,{sx:{padding:'10px 0'},children:tab.content})})},tab.label);})})]})});}; -;// CONCATENATED MODULE: ./src/components/targets-view/Card.tsx -var Card_excluded=["children"],Card_excluded2=["children"],Card_excluded3=["children"];var cardWidth=247;var projectCardHeight=80;var cardHeight=126;var cardGap=20;function Card(_ref){var children=_ref.children,rest=objectWithoutProperties_objectWithoutProperties(_ref,Card_excluded);return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2(_objectSpread2({display:"flex",flexShrink:0,width:cardWidth,height:cardHeight},rest),{},{children:children}));}function CardCol(_ref2){var children=_ref2.children,rest=objectWithoutProperties_objectWithoutProperties(_ref2,Card_excluded2);return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2(_objectSpread2({display:"flex",flexDirection:"column",gap:"".concat(cardGap,"px")},rest),{},{children:children}));}function CardRow(_ref3){var children=_ref3.children,rest=objectWithoutProperties_objectWithoutProperties(_ref3,Card_excluded3);return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,_objectSpread2(_objectSpread2({display:"flex",gap:"".concat(cardGap,"px")},rest),{},{children:children}));} -;// CONCATENATED MODULE: ./src/components/targets-view/CommandResultDetailsDrawer.tsx -var sidePanelWidth=720;function doGetRootNode(_x,_x2){return _doGetRootNode.apply(this,arguments);}function _doGetRootNode(){_doGetRootNode=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(api,rs){var shortNames,r,builder,_builder$buildRoot,_builder$buildRoot2,node;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return api.getShortNames();case 2:shortNames=_context.sent;_context.next=5;return api.getResult(rs.id);case 5:r=_context.sent;builder=new NodeBuilder({shortNames:shortNames,summary:rs,commandResult:r});_builder$buildRoot=builder.buildRoot(),_builder$buildRoot2=slicedToArray_slicedToArray(_builder$buildRoot,1),node=_builder$buildRoot2[0];return _context.abrupt("return",node);case 9:case"end":return _context.stop();}},_callee);}));return _doGetRootNode.apply(this,arguments);}var CommandResultDetailsDrawer=/*#__PURE__*/react.memo(function(props){var ps=props.ps,ts=props.ts,selectedCardRect=props.selectedCardRect;var api=(0,react.useContext)(ApiContext);var theme=styles_useTheme_useTheme();var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),selectedCommandResult=_useState2[0],setSelectedCommandResult=_useState2[1];var _useState3=(0,react.useState)(ts),_useState4=slicedToArray_slicedToArray(_useState3,2),prevTargetSummary=_useState4[0],setPrevTargetSummary=_useState4[1];var _useState5=(0,react.useState)([]),_useState6=slicedToArray_slicedToArray(_useState5,2),cardsCoords=_useState6[0],setCardsCoords=_useState6[1];var _useState7=(0,react.useState)(false),_useState8=slicedToArray_slicedToArray(_useState7,2),transitionRunning=_useState8[0],setTransitionRunning=_useState8[1];if(prevTargetSummary!==ts){var _ts$commandResults;setPrevTargetSummary(ts);setSelectedCommandResult(ts===null||ts===void 0?void 0:(_ts$commandResults=ts.commandResults)===null||_ts$commandResults===void 0?void 0:_ts$commandResults[0]);}var _useLoadingHelper=useLoadingHelper(function(){if(selectedCommandResult===undefined){return Promise.resolve(undefined);}return doGetRootNode(api,selectedCommandResult);},[selectedCommandResult]),_useLoadingHelper2=slicedToArray_slicedToArray(_useLoadingHelper,3),loading=_useLoadingHelper2[0],loadingError=_useLoadingHelper2[1],nodeData=_useLoadingHelper2[2];var cardsContainerElem=(0,react.useRef)();(0,react.useEffect)(function(){var _cardsContainerElem$c;var rect=(_cardsContainerElem$c=cardsContainerElem.current)===null||_cardsContainerElem$c===void 0?void 0:_cardsContainerElem$c.getBoundingClientRect();if(!rect||!selectedCardRect||!(ts!==null&&ts!==void 0&&ts.commandResults)){setCardsCoords([]);return;}var initialCoords=ts.commandResults.map(function(){return{left:selectedCardRect.left-rect.left,top:selectedCardRect.top-rect.top};});setCardsCoords(initialCoords);setTransitionRunning(true);},[selectedCardRect,ts===null||ts===void 0?void 0:ts.commandResults]);(0,react.useEffect)(function(){if(cardsCoords.length>0){var targetCoords=cardsCoords.map(function(_,i){return{left:0,top:i*(cardHeight+cardGap)};});if(cardsCoords.length===targetCoords.length&&cardsCoords.every(function(_ref,i){var left=_ref.left,top=_ref.top;return targetCoords[i].left===left&&targetCoords[i].top===top;})){return;}setTimeout(function(){setCardsCoords(targetCoords);setTimeout(function(){setTransitionRunning(false);},theme.transitions.duration.enteringScreen);},10);}},[cardsCoords,theme.transitions.duration.enteringScreen]);var zIndex=theme.zIndex.modal+1;var cards=(0,react.useMemo)(function(){if(!(ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&cardsCoords.length>0)){return null;}return ts.commandResults.map(function(rs,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Card,{sx:{position:'absolute',translate:"".concat(cardsCoords[i].left,"px ").concat(cardsCoords[i].top,"px"),zIndex:zIndex,transition:theme.transitions.create(['translate'],{easing:theme.transitions.easing.sharp,duration:theme.transitions.duration.enteringScreen})},children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultItem,{ps:ps,ts:ts,rs:rs,onSelectCommandResult:setSelectedCommandResult,selected:rs===selectedCommandResult})},rs.id);});},[ps,ts,cardsCoords,zIndex,theme.transitions,selectedCommandResult]);if(loadingError){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:"Error"});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[ps&&ts&&ts.commandResults&&ts.commandResults.length>0&&/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{sx:{position:'fixed',top:0,bottom:0,right:sidePanelWidth,width:"calc(100% - ".concat(sidePanelWidth,"px)"),overflowX:'visible',overflowY:transitionRunning?'visible':'auto',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',padding:'25px 0',zIndex:zIndex},onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{onClick:function onClick(e){return e.stopPropagation();},position:"relative",width:cardWidth,height:cardHeight*ts.commandResults.length+cardGap*(ts.commandResults.length-1),ref:cardsContainerElem,children:cards})}),/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.rs!==undefined,onClose:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:sidePanelWidth,height:"100%",children:loading?/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{}):/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:nodeData,onClose:props.onClose})})})})]});}); +/* harmony default export */ var TableRow_TableRow = (TableRow); +;// CONCATENATED MODULE: ./src/components/PropertiesTable.tsx +function PropertiesTable(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Name"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Value"})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:props.properties.map(function(row){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:row.name}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:row.value})]},row.name);})})]})});} +// EXTERNAL MODULE: ./node_modules/js-sha256/src/sha256.js +var sha256 = __webpack_require__(981); +;// CONCATENATED MODULE: ./src/utils/listKey.ts +function buildListKey(o){var j=JSON.stringify(o);return (0,sha256.sha256)(j);} +;// CONCATENATED MODULE: ./src/components/result-view/ChangesTable.tsx +var RefList=function RefList(props){return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:props.title}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Kind"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Namespace"})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:"Name"})})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:props.refs.map(function(ref){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:buildRefKindElement(ref)}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.namespace})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.name})})]},buildRefString(ref));})})]})})]});};function ChangesTable(props){var changedObjects;if(props.diffStatus.changedObjects.length){changedObjects=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{py:"10px",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{align:"center",variant:"h5",children:"Changed Objects"}),props.diffStatus.changedObjects.map(function(co){var _co$changes;return/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(TableHead_TableHead,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableRow_TableRow,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{align:"left",colSpan:2,sx:{padding:'24px 16px 5px 16px'},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{sx:{fontWeight:500,fontSize:'20px',lineHeight:'27px',letterSpacing:'1px'},children:buildRefString(co.ref)})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Path"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Changes"})]})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_co$changes=co.changes)===null||_co$changes===void 0?void 0:_co$changes.map(function(c){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{minWidth:"100px",sx:{overflowWrap:"anywhere"},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:c.jsonPath})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:c.unifiedDiff||"",language:"diff"})})]},buildListKey(c));})})]})},buildRefString(co.ref));})]});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{width:"100%",display:"flex",flexDirection:"column",children:[props.diffStatus.newObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"New Objects",refs:props.diffStatus.newObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.deletedObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Deleted Objects",refs:props.diffStatus.deletedObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),props.diffStatus.orphanObjects.length?/*#__PURE__*/(0,jsx_runtime.jsx)(RefList,{title:"Orphan Objects",refs:props.diffStatus.orphanObjects}):/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{}),changedObjects]});} +;// CONCATENATED MODULE: ./src/components/ErrorsTable.tsx +function ErrorsTable(props){var _props$errors;return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TableContainer_TableContainer,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(Table_Table,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableHead_TableHead,{children:/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Ref"}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:"Message"})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableBody_TableBody,{children:(_props$errors=props.errors)===null||_props$errors===void 0?void 0:_props$errors.map(function(e){return/*#__PURE__*/(0,jsx_runtime.jsxs)(TableRow_TableRow,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{sx:{minWidth:"150px"},children:/*#__PURE__*/(0,jsx_runtime.jsxs)(List_List,{disablePadding:true,children:[buildRefKindElement(e.ref,/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.kind})})})),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.name})}),e.ref.namespace&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{}),/*#__PURE__*/(0,jsx_runtime.jsx)(ListItem_ListItem,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(ListItemText_ListItemText,{primary:e.ref.namespace})})]})]})}),/*#__PURE__*/(0,jsx_runtime.jsx)(TableCell_TableCell,{children:e.message})]},buildListKey(e));})})]})})})});} +;// CONCATENATED MODULE: ./src/components/ObjectYaml.tsx +var ObjectYaml=function ObjectYaml(props){var api=(0,react.useContext)(ApiContext);var _useLoadingHelper=useLoadingHelper(/*#__PURE__*/asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var o;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return api.getResultObject(props.treeProps.summary.id,props.objectRef,props.objectType);case 2:o=_context.sent;return _context.abrupt("return",dump(o));case 4:case"end":return _context.stop();}},_callee);})),[props.treeProps.summary.id,props.objectRef,props.objectType]),_useLoadingHelper2=slicedToArray_slicedToArray(_useLoadingHelper,3),loading=_useLoadingHelper2[0],error=_useLoadingHelper2[1],content=_useLoadingHelper2[2];if(loading){return/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{});}else if(error){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:"Error"});}else{return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:content,language:"yaml"});}}; +;// CONCATENATED MODULE: ./src/components/result-view/nodes/NodeData.tsx +var DiffStatus=/*#__PURE__*/function(){function DiffStatus(){classCallCheck_classCallCheck(this,DiffStatus);this.newObjects=[];this.deletedObjects=[];this.orphanObjects=[];this.changedObjects=[];this.totalInsertions=0;this.totalDeletions=0;this.totalUpdates=0;}createClass_createClass(DiffStatus,[{key:"addChangedObject",value:function addChangedObject(co){var _co$changes,_this=this;this.changedObjects.push(co);(_co$changes=co.changes)===null||_co$changes===void 0?void 0:_co$changes.forEach(function(x){switch(x.type){case"insert":_this.totalInsertions++;break;case"delete":_this.totalDeletions++;break;case"update":_this.totalUpdates++;break;}});}},{key:"merge",value:function merge(other){this.newObjects=this.newObjects.concat(other.newObjects);this.deletedObjects=this.deletedObjects.concat(other.deletedObjects);this.orphanObjects=this.orphanObjects.concat(other.orphanObjects);this.changedObjects=this.changedObjects.concat(other.changedObjects);this.totalInsertions+=other.totalInsertions;this.totalDeletions+=other.totalDeletions;this.totalUpdates+=other.totalUpdates;}},{key:"hasDiffs",value:function hasDiffs(){if(this.newObjects.length||this.deletedObjects.length||this.orphanObjects.length){return true;}if(this.changedObjects.find(function(co){var _co$changes2;return((_co$changes2=co.changes)===null||_co$changes2===void 0?void 0:_co$changes2.length)!==0;})){return true;}return false;}}]);return DiffStatus;}();var HealthStatus=/*#__PURE__*/function(){function HealthStatus(){classCallCheck_classCallCheck(this,HealthStatus);this.errors=[];this.warnings=[];}createClass_createClass(HealthStatus,[{key:"merge",value:function merge(other){this.errors=this.errors.concat(other.errors);this.warnings=this.warnings.concat(other.warnings);}}]);return HealthStatus;}();var NodeData=/*#__PURE__*/function(){function NodeData(props,id,hasHealthStatus,hasDiffStatus){classCallCheck_classCallCheck(this,NodeData);this.props=void 0;this.id=void 0;this.children=[];this.healthStatus=void 0;this.diffStatus=void 0;this.props=props;this.id=id;if(hasHealthStatus){this.healthStatus=new HealthStatus();}if(hasDiffStatus){this.diffStatus=new DiffStatus();}}createClass_createClass(NodeData,[{key:"pushChild",value:function pushChild(child,front){if(front){this.children.unshift(child);}else{this.children.push(child);}this.merge(child);}},{key:"merge",value:function merge(other){if(this.diffStatus&&other.diffStatus){this.diffStatus.merge(other.diffStatus);}if(this.healthStatus&&other.healthStatus){this.healthStatus.merge(other.healthStatus);}}},{key:"buildStatusLine",value:function buildStatusLine(){var _this$healthStatus,_this$healthStatus2,_this$diffStatus,_this$diffStatus2,_this$diffStatus3,_this$diffStatus4;return/*#__PURE__*/(0,jsx_runtime.jsx)(StatusLine,{errors:(_this$healthStatus=this.healthStatus)===null||_this$healthStatus===void 0?void 0:_this$healthStatus.errors.length,warnings:(_this$healthStatus2=this.healthStatus)===null||_this$healthStatus2===void 0?void 0:_this$healthStatus2.warnings.length,changedObjects:(_this$diffStatus=this.diffStatus)===null||_this$diffStatus===void 0?void 0:_this$diffStatus.changedObjects.length,newObjects:(_this$diffStatus2=this.diffStatus)===null||_this$diffStatus2===void 0?void 0:_this$diffStatus2.newObjects.length,deletedObjects:(_this$diffStatus3=this.diffStatus)===null||_this$diffStatus3===void 0?void 0:_this$diffStatus3.deletedObjects.length,orphanObjects:(_this$diffStatus4=this.diffStatus)===null||_this$diffStatus4===void 0?void 0:_this$diffStatus4.orphanObjects.length});}},{key:"buildTreeItem",value:function buildTreeItem(hasChildren){var _this$healthStatus3,_this$healthStatus4,_this$diffStatus5,_this$diffStatus6,_this$diffStatus7,_this$diffStatus8;var _this$buildIcon=this.buildIcon(),_this$buildIcon2=slicedToArray_slicedToArray(_this$buildIcon,2),icon=_this$buildIcon2[0],iconText=_this$buildIcon2[1];var hasStatusLine=[(_this$healthStatus3=this.healthStatus)===null||_this$healthStatus3===void 0?void 0:_this$healthStatus3.errors.length,(_this$healthStatus4=this.healthStatus)===null||_this$healthStatus4===void 0?void 0:_this$healthStatus4.warnings.length,(_this$diffStatus5=this.diffStatus)===null||_this$diffStatus5===void 0?void 0:_this$diffStatus5.changedObjects.length,(_this$diffStatus6=this.diffStatus)===null||_this$diffStatus6===void 0?void 0:_this$diffStatus6.newObjects.length,(_this$diffStatus7=this.diffStatus)===null||_this$diffStatus7===void 0?void 0:_this$diffStatus7.deletedObjects.length,(_this$diffStatus8=this.diffStatus)===null||_this$diffStatus8===void 0?void 0:_this$diffStatus8.orphanObjects.length].some(function(x){return(x||0)>0;});return/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",height:"100%",flex:"1 1 auto",children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",width:"30px",height:"100%",flex:"0 0 auto",mr:"13px",sx:{'& svg':{width:'30px',height:'30px'}},children:[icon,/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"subtitle1",component:"div",fontSize:"12px",fontWeight:400,lineHeight:"16px",height:"16px",children:iconText})]}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",height:"100%",flex:"1 1 auto",py:"15px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,_objectSpread2(_objectSpread2({variant:"h6",component:"div",sx:{wordBreak:'break-all'}},hasChildren?{}:{fontSize:'16px',lineHeight:'22px'}),{},{children:this.buildSidePanelTitle()}))}),hasStatusLine&&/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{height:"100%",width:"172px",flex:"0 0 auto",display:"flex",alignItems:"center",px:"14px",ml:"14px",position:"relative",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{orientation:"vertical",sx:{height:'40px',position:'absolute',left:0}}),this.buildStatusLine()]})]});}},{key:"buildDiffAndHealthPages",value:function buildDiffAndHealthPages(tabs){this.buildChangesPage(tabs);this.buildErrorsPage(tabs);this.buildWarningsPage(tabs);}},{key:"buildObjectPage",value:function buildObjectPage(ref,objectType){return/*#__PURE__*/(0,jsx_runtime.jsx)(ObjectYaml,{treeProps:this.props,objectRef:ref,objectType:objectType});}},{key:"buildChangesPage",value:function buildChangesPage(tabs){var _this$diffStatus9;if(!((_this$diffStatus9=this.diffStatus)!==null&&_this$diffStatus9!==void 0&&_this$diffStatus9.hasDiffs())){return undefined;}tabs.push({label:"Changes",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ChangesTable,{diffStatus:this.diffStatus})});}},{key:"buildErrorsPage",value:function buildErrorsPage(tabs){var _this$healthStatus5;if(!((_this$healthStatus5=this.healthStatus)!==null&&_this$healthStatus5!==void 0&&_this$healthStatus5.errors.length)){return undefined;}tabs.push({label:"Errors",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.healthStatus.errors})});}},{key:"buildWarningsPage",value:function buildWarningsPage(tabs){var _this$healthStatus6;if(!((_this$healthStatus6=this.healthStatus)!==null&&_this$healthStatus6!==void 0&&_this$healthStatus6.warnings.length)){return undefined;}tabs.push({label:"Warnings",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.healthStatus.warnings})});}}]);return NodeData;}(); ;// CONCATENATED MODULE: ./src/components/targets-view/TargetDetailsDrawer.tsx var MyProvider=/*#__PURE__*/function(){function MyProvider(ts){var _this$ts,_this$ts$lastValidate,_this$ts$lastValidate2,_this=this;classCallCheck_classCallCheck(this,MyProvider);this.ts=void 0;this.diffStatus=void 0;this.ts=ts;this.diffStatus=new DiffStatus();(_this$ts=this.ts)===null||_this$ts===void 0?void 0:(_this$ts$lastValidate=_this$ts.lastValidateResult)===null||_this$ts$lastValidate===void 0?void 0:(_this$ts$lastValidate2=_this$ts$lastValidate.drift)===null||_this$ts$lastValidate2===void 0?void 0:_this$ts$lastValidate2.forEach(function(co){_this.diffStatus.addChangedObject(co);});}createClass_createClass(MyProvider,[{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var _this$ts$lastValidate3,_this$ts$lastValidate4,_this$ts$lastValidate5,_this$ts$lastValidate6;if(!this.ts){return[];}var tabs=[{label:"Summary",content:this.buildSummaryTab()}];if(this.ts.target)if(this.diffStatus.changedObjects.length){tabs.push({label:"Drift",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ChangesTable,{diffStatus:this.diffStatus})});}if((_this$ts$lastValidate3=this.ts.lastValidateResult)!==null&&_this$ts$lastValidate3!==void 0&&(_this$ts$lastValidate4=_this$ts$lastValidate3.errors)!==null&&_this$ts$lastValidate4!==void 0&&_this$ts$lastValidate4.length){tabs.push({label:"Errors",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.ts.lastValidateResult.errors})});}if((_this$ts$lastValidate5=this.ts.lastValidateResult)!==null&&_this$ts$lastValidate5!==void 0&&(_this$ts$lastValidate6=_this$ts$lastValidate5.warnings)!==null&&_this$ts$lastValidate6!==void 0&&_this$ts$lastValidate6.length){tabs.push({label:"Warnings",content:/*#__PURE__*/(0,jsx_runtime.jsx)(ErrorsTable,{errors:this.ts.lastValidateResult.warnings})});}return tabs;}},{key:"buildSummaryTab",value:function buildSummaryTab(){var _this$ts2,_this$ts3,_this$ts4,_this$ts4$lastValidat,_this$ts4$lastValidat2,_this$ts5,_this$ts5$lastValidat,_this$ts5$lastValidat2,_this$ts6,_this$ts6$lastValidat,_this$ts6$lastValidat2;var props=[{name:"Target Name",value:this.getTargetName()},{name:"Discriminator",value:(_this$ts2=this.ts)===null||_this$ts2===void 0?void 0:_this$ts2.target.discriminator}];if((_this$ts3=this.ts)!==null&&_this$ts3!==void 0&&_this$ts3.lastValidateResult){props.push({name:"Ready",value:this.ts.lastValidateResult.ready+""});}if((_this$ts4=this.ts)!==null&&_this$ts4!==void 0&&(_this$ts4$lastValidat=_this$ts4.lastValidateResult)!==null&&_this$ts4$lastValidat!==void 0&&(_this$ts4$lastValidat2=_this$ts4$lastValidat.errors)!==null&&_this$ts4$lastValidat2!==void 0&&_this$ts4$lastValidat2.length){props.push({name:"Errors",value:this.ts.lastValidateResult.errors.length+""});}if((_this$ts5=this.ts)!==null&&_this$ts5!==void 0&&(_this$ts5$lastValidat=_this$ts5.lastValidateResult)!==null&&_this$ts5$lastValidat!==void 0&&(_this$ts5$lastValidat2=_this$ts5$lastValidat.warnings)!==null&&_this$ts5$lastValidat2!==void 0&&_this$ts5$lastValidat2.length){props.push({name:"Warnings",value:this.ts.lastValidateResult.warnings.length+""});}if((_this$ts6=this.ts)!==null&&_this$ts6!==void 0&&(_this$ts6$lastValidat=_this$ts6.lastValidateResult)!==null&&_this$ts6$lastValidat!==void 0&&(_this$ts6$lastValidat2=_this$ts6$lastValidat.drift)!==null&&_this$ts6$lastValidat2!==void 0&&_this$ts6$lastValidat2.length){props.push({name:"Drifted Objects",value:this.ts.lastValidateResult.drift.length+""});}return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}},{key:"getTargetName",value:function getTargetName(){if(!this.ts){return"";}var name="";if(this.ts.target.targetName){name=this.ts.target.targetName;}return name;}},{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return this.getTargetName();}}]);return MyProvider;}();var TargetDetailsDrawer=/*#__PURE__*/react.memo(function(props){return/*#__PURE__*/(0,jsx_runtime.jsx)(styles_ThemeProvider_ThemeProvider,{theme:theme_dark,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Drawer_Drawer,{sx:{zIndex:1300},anchor:"right",open:props.ts!==undefined,onClose:props.onClose,ModalProps:{BackdropProps:{invisible:true}},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"600px",height:"100%",children:/*#__PURE__*/(0,jsx_runtime.jsx)(SidePanel,{provider:new MyProvider(props.ts),onClose:props.onClose})})})});}); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Category.js + + +/* harmony default export */ var Category = (createSvgIcon([/*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "m12 2-5.5 9h11z" +}, "0"), /*#__PURE__*/(0,jsx_runtime.jsx)("circle", { + cx: "17.5", + cy: "17.5", + r: "4.5" +}, "1"), /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M3 13.5h8v8H3z" +}, "2")], 'Category')); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Settings.js + + +/* harmony default export */ var Settings = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z" +}), 'Settings')); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Lock.js + + +/* harmony default export */ var Lock = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" +}), 'Lock')); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Dvr.js + + +/* harmony default export */ var Dvr = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-.9 1.99-2L23 5c0-1.1-.9-2-2-2zm0 14H3V5h18v12zm-2-9H8v2h11V8zm0 4H8v2h11v-2zM7 8H5v2h2V8zm0 4H5v2h2v-2z" +}), 'Dvr')); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Http.js + + +/* harmony default export */ var Http = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M4.5 11h-2V9H1v6h1.5v-2.5h2V15H6V9H4.5v2zm2.5-.5h1.5V15H10v-4.5h1.5V9H7v1.5zm5.5 0H14V15h1.5v-4.5H17V9h-4.5v1.5zm9-1.5H18v6h1.5v-2h2c.8 0 1.5-.7 1.5-1.5v-1c0-.8-.7-1.5-1.5-1.5zm0 2.5h-2v-1h2v1z" +}), 'Http')); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Cloud.js + + +/* harmony default export */ var Cloud = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z" +}), 'Cloud')); +;// CONCATENATED MODULE: ./src/components/result-view/nodes/VarsSourceNode.tsx +var VarsSourceNodeData=/*#__PURE__*/function(_NodeData){_inherits(VarsSourceNodeData,_NodeData);var _super=_createSuper(VarsSourceNodeData);function VarsSourceNodeData(props,id,varsSource){var _this$varsSource$clus;var _this;classCallCheck_classCallCheck(this,VarsSourceNodeData);_this=_super.call(this,props,id,false,false);_this.varsSource=void 0;_this.labelsYaml=void 0;_this.renderedVarsYaml=void 0;_this.varsSource=varsSource;var labels=(_this$varsSource$clus=_this.varsSource.clusterConfigMap)===null||_this$varsSource$clus===void 0?void 0:_this$varsSource$clus.labels;if(!labels){var _this$varsSource$clus2;labels=(_this$varsSource$clus2=_this.varsSource.clusterSecret)===null||_this$varsSource$clus2===void 0?void 0:_this$varsSource$clus2.labels;}if(labels){_this.labelsYaml=dump(labels);}_this.renderedVarsYaml=dump(_this.varsSource.renderedVars);return _this;}createClass_createClass(VarsSourceNodeData,[{key:"getVarsSourceHandler",value:function getVarsSourceHandler(){var _this2=this;if(this.varsSource.values){return{type:"values",label:function label(){if(_this2.varsSource.renderedVars){return"values: "+Object.keys(_this2.varsSource.renderedVars).length;}else{return"empty values";}},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Category,{fontSize:"large"});},sourceProps:function sourceProps(){return[];}};}else if(this.varsSource.file){return{type:"file",label:function label(){return _this2.varsSource.file;},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(FileIcon,{});},sourceProps:function sourceProps(){return[{name:"File",value:_this2.varsSource.file}];}};}else if(this.varsSource.git){return{type:"git",label:function label(){var s=_this2.varsSource.git.url.split("/");var name=s[s.length-1];return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[name,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),_this2.varsSource.git.path]});},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(GitIcon,{});},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"Url",value:_this2.varsSource.git.url});sourceProps.push({name:"Path",value:_this2.varsSource.git.path});var ref="HEAD";if(_this2.varsSource.git.ref){ref=_this2.varsSource.git.ref;}sourceProps.push({name:"Ref",value:ref});return sourceProps;}};}else if(this.varsSource.clusterConfigMap||this.varsSource.clusterSecret){var vs=this.varsSource.clusterConfigMap?this.varsSource.clusterConfigMap:this.varsSource.clusterSecret;var type=this.varsSource.clusterConfigMap?"cm":"secret";var _icon=this.varsSource.clusterConfigMap?/*#__PURE__*/(0,jsx_runtime.jsx)(Settings,{fontSize:"large"}):/*#__PURE__*/(0,jsx_runtime.jsx)(Lock,{fontSize:"large"});return{type:type,label:function label(){var _this2$varsSource$clu;return(_this2$varsSource$clu=_this2.varsSource.clusterConfigMap)===null||_this2$varsSource$clu===void 0?void 0:_this2$varsSource$clu.name;},icon:function icon(){return _icon;},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"Name",value:vs.name});sourceProps.push({name:"Namespace",value:vs.namespace});sourceProps.push({name:"Key",value:vs.key});if(vs.labels){sourceProps.push({name:"Labels",value:/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:_this2.labelsYaml,language:"yaml"})});}return sourceProps;}};}else if(this.varsSource.systemEnvVars){return{type:"systemEnvVar",label:function label(){return"systemEnvVars";},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Dvr,{fontSize:"large"});},sourceProps:function sourceProps(){// TODO +return[];}};}else if(this.varsSource.http){return{type:"http",label:function label(){return _this2.varsSource.http.url;},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Http,{fontSize:"large"});},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"Url",value:_this2.varsSource.http.url});sourceProps.push({name:"Method",value:_this2.varsSource.http.method||"GET"});if(_this2.varsSource.http.jsonPath){sourceProps.push({name:"JsonPath",value:_this2.varsSource.http.jsonPath});}return sourceProps;}};}else if(this.varsSource.awsSecretsManager){return{type:"awsSecretsManager",label:function label(){return _this2.varsSource.awsSecretsManager.secretName;},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Cloud,{fontSize:"large"});},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"SecretName",value:_this2.varsSource.awsSecretsManager.secretName});if(_this2.varsSource.awsSecretsManager.region){sourceProps.push({name:"Region",value:_this2.varsSource.awsSecretsManager.region});}if(_this2.varsSource.awsSecretsManager.profile){sourceProps.push({name:"Profile",value:_this2.varsSource.awsSecretsManager.profile});}return sourceProps;}};}else if(this.varsSource.vault){return{type:"vault",label:function label(){return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[_this2.varsSource.vault.address,/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),_this2.varsSource.vault.path]});},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Cloud,{fontSize:"large"});},sourceProps:function sourceProps(){var sourceProps=[];sourceProps.push({name:"Address",value:_this2.varsSource.vault.address});sourceProps.push({name:"Path",value:_this2.varsSource.vault.path});return sourceProps;}};}else{return{type:"unknown",label:function label(){return"values: "+Object.keys(_this2.varsSource.renderedVars).length;},icon:function icon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(Category,{fontSize:"large"});},sourceProps:function sourceProps(){return[];}};}}},{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return this.getVarsSourceHandler().label();}},{key:"buildIcon",value:function buildIcon(){var h=this.getVarsSourceHandler();return[h.icon(),h.type];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()},{label:"Vars",content:this.buildVarsPage()}];this.buildDiffAndHealthPages(tabs);return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var h=this.getVarsSourceHandler();var props=[{name:"Type",value:h.type}].concat(toConsumableArray_toConsumableArray(h.sourceProps()),[{name:"IgnoreMissing",value:!!this.varsSource.ignoreMissing+""},{name:"NoOverride",value:!!this.varsSource.noOverride+""}]);if(this.varsSource.when){props.push({name:"When",value:this.varsSource.when});}return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}},{key:"buildVarsPage",value:function buildVarsPage(){return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{sx:{overflowY:'scroll',// Enable vertical scrolling +//maxHeight: '400px', // Set a fixed height +border:'1px solid #ccc',// Optional border +padding:'10px'// Optional padding +},children:/*#__PURE__*/(0,jsx_runtime.jsx)("pre",{children:/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:this.renderedVarsYaml,language:"yaml"})})});}}]);return VarsSourceNodeData;}(NodeData); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Source.js + + +/* harmony default export */ var Source = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-6 10H6v-2h8v2zm4-4H6v-2h12v2z" +}), 'Source')); +;// CONCATENATED MODULE: ./src/components/result-view/nodes/DeploymentItemNode.tsx +var DeploymentItemNodeData=/*#__PURE__*/function(_NodeData){_inherits(DeploymentItemNodeData,_NodeData);var _super=_createSuper(DeploymentItemNodeData);function DeploymentItemNodeData(props,id,deploymentItem){var _this;classCallCheck_classCallCheck(this,DeploymentItemNodeData);_this=_super.call(this,props,id,true,true);_this.deploymentItem=void 0;_this.deploymentItem=deploymentItem;return _this;}createClass_createClass(DeploymentItemNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return this.deploymentItem.path;}},{key:"buildIcon",value:function buildIcon(){var iconText="di";return[/*#__PURE__*/(0,jsx_runtime.jsx)(Source,{fontSize:"large"}),iconText];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()}];this.buildDiffAndHealthPages(tabs);return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var props=[];props.push({name:"Path",value:this.deploymentItem.path});buildDeploymentItemSummaryProps(this.deploymentItem,props);return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}}]);return DeploymentItemNodeData;}(NodeData);function buildDeploymentItemSummaryProps(di,props){if(di.barrier!==undefined){props.push({name:"Barrier",value:di.barrier+""});}if(di.waitReadiness!==undefined){props.push({name:"WaitReadiness",value:di.waitReadiness+""});}if(di.skipDeleteIfTags!==undefined){props.push({name:"SkipDeleteIfTags",value:di.skipDeleteIfTags+""});}if(di.onlyRender!==undefined){props.push({name:"OnlyRender",value:di.onlyRender+""});}if(di.alwaysDeploy!==undefined){props.push({name:"AlwaysDeploy",value:di.alwaysDeploy+""});}if(di.deleteObjects){// TODO this is ugly +props.push({name:"DeleteObjects",value:JSON.stringify(di.deleteObjects)});}if(di.when){props.push({name:"When",value:di.when});}} +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/SettingsEthernet.js + + +/* harmony default export */ var SettingsEthernet = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M7.77 6.76 6.23 5.48.82 12l5.41 6.52 1.54-1.28L3.42 12l4.35-5.24zM7 13h2v-2H7v2zm10-2h-2v2h2v-2zm-6 2h2v-2h-2v2zm6.77-7.52-1.54 1.28L20.58 12l-4.35 5.24 1.54 1.28L23.18 12l-5.41-6.52z" +}), 'SettingsEthernet')); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/SmartToy.js + + +/* harmony default export */ var SmartToy = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M20 9V7c0-1.1-.9-2-2-2h-3c0-1.66-1.34-3-3-3S9 3.34 9 5H6c-1.1 0-2 .9-2 2v2c-1.66 0-3 1.34-3 3s1.34 3 3 3v4c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-4c1.66 0 3-1.34 3-3s-1.34-3-3-3zM7.5 11.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5S9.83 13 9 13s-1.5-.67-1.5-1.5zM16 17H8v-2h8v2zm-1-4c-.83 0-1.5-.67-1.5-1.5S14.17 10 15 10s1.5.67 1.5 1.5S15.83 13 15 13z" +}), 'SmartToy')); +;// CONCATENATED MODULE: ./src/components/result-view/nodes/ObjectNode.tsx +var kindMapping={"/ConfigMap":{icon:Settings},"apps/Deployment":{icon:PublishedWithChanges},"Service":{icon:SettingsEthernet},"ServiceAccount":{icon:SmartToy}};var ObjectNodeData=/*#__PURE__*/function(_NodeData){_inherits(ObjectNodeData,_NodeData);var _super=_createSuper(ObjectNodeData);function ObjectNodeData(props,id,objectRef){var _this;classCallCheck_classCallCheck(this,ObjectNodeData);_this=_super.call(this,props,id,true,true);_this.objectRef=void 0;_this.objectRef=objectRef;return _this;}createClass_createClass(ObjectNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return this.objectRef.name;}},{key:"buildIcon",value:function buildIcon(){var _this2=this;var sn=this.props.shortNames.find(function(sn){return sn.group===_this2.objectRef.group&&sn.kind===_this2.objectRef.kind;});var snStr=(sn===null||sn===void 0?void 0:sn.shortName)||"";var m=kindMapping[this.objectRef.group+"/"+this.objectRef.kind];if(m!==undefined){return[/*#__PURE__*/react.createElement(m.icon,{fontSize:"large"}),snStr];}return[/*#__PURE__*/(0,jsx_runtime.jsx)(BracketsCurlyIcon,{}),snStr];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var _findObjectByRef,_findObjectByRef2,_findObjectByRef3;var tabs=[{label:"Summary",content:this.buildSummaryPage()}];this.buildDiffAndHealthPages(tabs);if((_findObjectByRef=findObjectByRef(this.props.commandResult.objects,this.objectRef))!==null&&_findObjectByRef!==void 0&&_findObjectByRef.rendered){tabs.push({label:"Rendered",content:this.buildObjectPage(this.objectRef,ObjectType.Rendered)});}if((_findObjectByRef2=findObjectByRef(this.props.commandResult.objects,this.objectRef))!==null&&_findObjectByRef2!==void 0&&_findObjectByRef2.remote){tabs.push({label:"Remote",content:this.buildObjectPage(this.objectRef,ObjectType.Remote)});}if((_findObjectByRef3=findObjectByRef(this.props.commandResult.objects,this.objectRef))!==null&&_findObjectByRef3!==void 0&&_findObjectByRef3.applied){tabs.push({label:"Applied",content:this.buildObjectPage(this.objectRef,ObjectType.Applied)});}return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var _o$rendered;var props=[];var apiVersion=this.objectRef.version;if(this.objectRef.group){apiVersion=this.objectRef.group+"/"+this.objectRef.version;}props.push({name:"ApiVersion",value:apiVersion});props.push({name:"Kind",value:this.objectRef.kind});props.push({name:"Name",value:this.objectRef.name});if(this.objectRef.namespace){props.push({name:"Namespace",value:this.objectRef.namespace});}var o=findObjectByRef(this.props.commandResult.objects,this.objectRef);var annotations=o===null||o===void 0?void 0:(_o$rendered=o.rendered)===null||_o$rendered===void 0?void 0:_o$rendered.metadata.annotations;if(annotations){Object.keys(annotations).forEach(function(k){if(k.indexOf("kluctl.io/")!==-1){props.push({name:k,value:annotations[k]});}});}return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}}]);return ObjectNodeData;}(NodeData); +;// CONCATENATED MODULE: ./src/components/result-view/nodes/CommandResultNode.tsx +var CommandResultNodeData=/*#__PURE__*/function(_NodeData){_inherits(CommandResultNodeData,_NodeData);var _super=_createSuper(CommandResultNodeData);function CommandResultNodeData(props,id){var _this$props$commandRe;var _this;classCallCheck_classCallCheck(this,CommandResultNodeData);_this=_super.call(this,props,id,true,true);_this.dumpedTargetYaml=void 0;if((_this$props$commandRe=_this.props.commandResult.command)!==null&&_this$props$commandRe!==void 0&&_this$props$commandRe.target){_this.dumpedTargetYaml=dump(_this.props.commandResult.command.target);}return _this;}createClass_createClass(CommandResultNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return"CommandResult";}},{key:"buildIcon",value:function buildIcon(){return[/*#__PURE__*/(0,jsx_runtime.jsx)(DeployIcon,{}),"result"];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()}];if(this.dumpedTargetYaml){var page=this.buildTargetPage();tabs.push({label:"Target",content:page});}this.buildDiffAndHealthPages(tabs);return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var _this$props$commandRe2,_this$props$commandRe3;var props=[{name:"Initiator",value:(_this$props$commandRe2=this.props.commandResult.command)===null||_this$props$commandRe2===void 0?void 0:_this$props$commandRe2.initiator},{name:"Command",value:(_this$props$commandRe3=this.props.commandResult.command)===null||_this$props$commandRe3===void 0?void 0:_this$props$commandRe3.command}];return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}},{key:"buildTargetPage",value:function buildTargetPage(){return/*#__PURE__*/(0,jsx_runtime.jsx)(CodeViewer,{code:this.dumpedTargetYaml,language:"yaml"});}}]);return CommandResultNodeData;}(NodeData); +;// CONCATENATED MODULE: ./src/components/result-view/nodes/VarsSourceCollectionNode.tsx +var VarsSourceCollectionNodeData=/*#__PURE__*/function(_NodeData){_inherits(VarsSourceCollectionNodeData,_NodeData);var _super=_createSuper(VarsSourceCollectionNodeData);function VarsSourceCollectionNodeData(props,id){var _this;classCallCheck_classCallCheck(this,VarsSourceCollectionNodeData);_this=_super.call(this,props,id,false,false);_this.varsSources=[];return _this;}createClass_createClass(VarsSourceCollectionNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){return"vars: "+this.varsSources.length;}},{key:"buildIcon",value:function buildIcon(){return[/*#__PURE__*/(0,jsx_runtime.jsx)(BracketsSquareIcon,{}),"vars"];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){return[];}}]);return VarsSourceCollectionNodeData;}(NodeData); +;// CONCATENATED MODULE: ./src/components/result-view/nodes/DeploymentItemIncludeNode.tsx +var DeploymentItemIncludeNodeData=/*#__PURE__*/function(_NodeData){_inherits(DeploymentItemIncludeNodeData,_NodeData);var _super=_createSuper(DeploymentItemIncludeNodeData);function DeploymentItemIncludeNodeData(props,id,deploymentItem,includedDeployment){var _this;classCallCheck_classCallCheck(this,DeploymentItemIncludeNodeData);_this=_super.call(this,props,id,true,true);_this.deploymentItem=void 0;_this.includedDeployment=void 0;_this.deploymentItem=deploymentItem;_this.includedDeployment=includedDeployment;return _this;}createClass_createClass(DeploymentItemIncludeNodeData,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){if(this.deploymentItem.include){return this.deploymentItem.include;}else if(this.deploymentItem.git){var s=this.deploymentItem.git.url.split("/");var name=s[s.length-1];return/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[name,this.deploymentItem.git.subDir&&/*#__PURE__*/(0,jsx_runtime.jsxs)(jsx_runtime.Fragment,{children:[/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),this.deploymentItem.git.subDir]})]});}else{return"unknown include";}}},{key:"buildIcon",value:function buildIcon(){if(this.deploymentItem.git){return[/*#__PURE__*/(0,jsx_runtime.jsx)(GitIcon,{}),"git"];}return[/*#__PURE__*/(0,jsx_runtime.jsx)(IncludeIcon,{}),"include"];}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()}];this.buildDiffAndHealthPages(tabs);return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var props=[];if(this.deploymentItem.include){props.push({name:"Type",value:"LocalInclude"});props.push({name:"Path",value:this.deploymentItem.include});}else if(this.deploymentItem.git){props.push({name:"Type",value:"GitInclude"});props.push({name:"Url",value:this.deploymentItem.git.url});props.push({name:"SubDir",value:this.deploymentItem.git.subDir});var ref="HEAD";if(this.deploymentItem.git.ref){ref=this.deploymentItem.git.ref;}props.push({name:"Ref",value:ref});}else{props.push({name:"Type",value:"Unknown"});}buildDeploymentItemSummaryProps(this.deploymentItem,props);return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}}]);return DeploymentItemIncludeNodeData;}(NodeData); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/Delete.js + + +/* harmony default export */ var Delete = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" +}), 'Delete')); +;// CONCATENATED MODULE: ./node_modules/@mui/icons-material/esm/LinkOff.js + + +/* harmony default export */ var LinkOff = (createSvgIcon( /*#__PURE__*/(0,jsx_runtime.jsx)("path", { + d: "M17 7h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1 0 1.43-.98 2.63-2.31 2.98l1.46 1.46C20.88 15.61 22 13.95 22 12c0-2.76-2.24-5-5-5zm-1 4h-2.19l2 2H16zM2 4.27l3.11 3.11C3.29 8.12 2 9.91 2 12c0 2.76 2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1 0-1.59 1.21-2.9 2.76-3.07L8.73 11H8v2h2.73L13 15.27V17h1.73l4.01 4L20 19.74 3.27 3 2 4.27z" +}), 'LinkOff')); +;// CONCATENATED MODULE: ./src/components/result-view/nodes/DeletedOrOrphanObjectsCollectionNode.tsx +var DeletedOrOrphanObjectsCollectionNode=/*#__PURE__*/function(_NodeData){_inherits(DeletedOrOrphanObjectsCollectionNode,_NodeData);var _super=_createSuper(DeletedOrOrphanObjectsCollectionNode);function DeletedOrOrphanObjectsCollectionNode(props,id,deleted){var _this;classCallCheck_classCallCheck(this,DeletedOrOrphanObjectsCollectionNode);_this=_super.call(this,props,id,false,true);_this.deleted=void 0;_this.deleted=deleted;return _this;}createClass_createClass(DeletedOrOrphanObjectsCollectionNode,[{key:"buildSidePanelTitle",value:function buildSidePanelTitle(){if(this.deleted){var _this$diffStatus;return"".concat((_this$diffStatus=this.diffStatus)===null||_this$diffStatus===void 0?void 0:_this$diffStatus.deletedObjects.length," objects deleted");}else{var _this$diffStatus2;return"".concat((_this$diffStatus2=this.diffStatus)===null||_this$diffStatus2===void 0?void 0:_this$diffStatus2.orphanObjects.length," objects orphaned");}}},{key:"buildIcon",value:function buildIcon(){if(this.deleted){return[/*#__PURE__*/(0,jsx_runtime.jsx)(Delete,{fontSize:"large"}),"deleted"];}else{return[/*#__PURE__*/(0,jsx_runtime.jsx)(LinkOff,{fontSize:"large"}),"orphans"];}}},{key:"buildSidePanelTabs",value:function buildSidePanelTabs(){var tabs=[{label:"Summary",content:this.buildSummaryPage()}];return tabs;}},{key:"buildSummaryPage",value:function buildSummaryPage(){var _this$diffStatus3,_this$diffStatus5;var props=[];if((_this$diffStatus3=this.diffStatus)!==null&&_this$diffStatus3!==void 0&&_this$diffStatus3.deletedObjects.length){var _this$diffStatus4;props.push({name:"Deleted",value:(_this$diffStatus4=this.diffStatus)===null||_this$diffStatus4===void 0?void 0:_this$diffStatus4.deletedObjects.length});}if((_this$diffStatus5=this.diffStatus)!==null&&_this$diffStatus5!==void 0&&_this$diffStatus5.orphanObjects.length){var _this$diffStatus6;props.push({name:"Orphaned",value:(_this$diffStatus6=this.diffStatus)===null||_this$diffStatus6===void 0?void 0:_this$diffStatus6.orphanObjects.length});}return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:/*#__PURE__*/(0,jsx_runtime.jsx)(PropertiesTable,{properties:props})});}}]);return DeletedOrOrphanObjectsCollectionNode;}(NodeData); +;// CONCATENATED MODULE: ./src/components/result-view/nodes/NodeBuilder.ts +var NodeBuilder=/*#__PURE__*/function(){function NodeBuilder(props){var _props$commandResult$,_this=this,_props$commandResult$2,_props$commandResult$3;classCallCheck_classCallCheck(this,NodeBuilder);this.props=void 0;this.changedObjectsMap=new Map();this.newObjectsMap=new Map();this.orphanObjectsMap=new Map();this.deletedObjectsMap=new Map();this.errorsMap=new Map();this.warningsMap=new Map();this.props=props;(_props$commandResult$=props.commandResult.objects)===null||_props$commandResult$===void 0?void 0:_props$commandResult$.forEach(function(o){var _o$changes;var key=buildObjectRefKey(o.ref);if((_o$changes=o.changes)!==null&&_o$changes!==void 0&&_o$changes.length){_this.changedObjectsMap.set(key,o);}if(o.new){_this.newObjectsMap.set(key,o.ref);}if(o.orphan){_this.orphanObjectsMap.set(key,o.ref);}if(o.deleted){_this.deletedObjectsMap.set(key,o.ref);}});(_props$commandResult$2=props.commandResult.errors)===null||_props$commandResult$2===void 0?void 0:_props$commandResult$2.forEach(function(e){var key=buildObjectRefKey(e.ref);var l=_this.errorsMap.get(key);if(!l){l=[e];_this.errorsMap.set(key,l);}else{l.push(e);}});(_props$commandResult$3=props.commandResult.warnings)===null||_props$commandResult$3===void 0?void 0:_props$commandResult$3.forEach(function(e){var key=buildObjectRefKey(e.ref);var l=_this.warningsMap.get(key);if(!l){l=[e];_this.warningsMap.set(key,l);}else{l.push(e);}});}createClass_createClass(NodeBuilder,[{key:"buildRoot",value:function buildRoot(){var rootNode=new CommandResultNodeData(this.props,"root");if(this.props.commandResult.deployment){this.buildDeploymentProjectChildren(rootNode,this.props.commandResult.deployment);}if(this.deletedObjectsMap.size){this.buildDeletedOrOrphanNode(rootNode,true,Array.from(this.deletedObjectsMap.values()));}if(this.orphanObjectsMap.size){this.buildDeletedOrOrphanNode(rootNode,false,Array.from(this.orphanObjectsMap.values()));}var nodeMap=new Map();function collect(n){nodeMap.set(n.id,n);n.children.forEach(function(c){collect(c);});}collect(rootNode);return[rootNode,nodeMap];}},{key:"buildDeploymentProjectChildren",value:function buildDeploymentProjectChildren(node,deploymentProject){var _deploymentProject$de,_this2=this;if(deploymentProject.vars){this.buildVarsSourceCollectionNode(node,deploymentProject.vars);}(_deploymentProject$de=deploymentProject.deployments)===null||_deploymentProject$de===void 0?void 0:_deploymentProject$de.forEach(function(deploymentItem,i){_this2.buildDeploymentItemNode(node,deploymentItem,i);});}},{key:"buildVarsSourceCollectionNode",value:function buildVarsSourceCollectionNode(parentNode,varsSources){var _node$varsSources,_this3=this;if(varsSources===undefined){return;}var newId="".concat(parentNode.id,"/(vars)");var node=new VarsSourceCollectionNodeData(this.props,newId);(_node$varsSources=node.varsSources).push.apply(_node$varsSources,toConsumableArray_toConsumableArray(varsSources));varsSources.forEach(function(vs,i){_this3.buildVarsSourceNode(node,vs,i);});parentNode.pushChild(node);return node;}},{key:"buildVarsSourceNode",value:function buildVarsSourceNode(parentNode,varsSource,index){var newId="".concat(parentNode.id,"/").concat(index,"}");var node=new VarsSourceNodeData(this.props,newId,varsSource);parentNode.pushChild(node);return node;}},{key:"buildDeploymentItemNode",value:function buildDeploymentItemNode(parentNode,deploymentItem,index){var _this4=this;var node;var newId="".concat(parentNode.id,"/(dis)/").concat(index);if(deploymentItem.path){var _deploymentItem$rende;node=new DeploymentItemNodeData(this.props,newId,deploymentItem);this.buildVarsSourceCollectionNode(node,deploymentItem.vars);(_deploymentItem$rende=deploymentItem.renderedObjects)===null||_deploymentItem$rende===void 0?void 0:_deploymentItem$rende.forEach(function(renderedObject){_this4.buildObjectNode(node,renderedObject);});}else if(deploymentItem.include||deploymentItem.git){node=new DeploymentItemIncludeNodeData(this.props,newId,deploymentItem,deploymentItem.renderedInclude);this.buildVarsSourceCollectionNode(node,deploymentItem.vars);if(deploymentItem.renderedInclude){this.buildDeploymentProjectChildren(node,deploymentItem.renderedInclude);}}else{return node;}parentNode.pushChild(node);return node;}},{key:"buildObjectNode",value:function buildObjectNode(parentNode,objectRef){var _this$errorsMap$get,_this$warningsMap$get;var newId="".concat(parentNode.id,"/(obj)/").concat(buildObjectRefKey(objectRef));var node=new ObjectNodeData(this.props,newId,objectRef);var key=buildObjectRefKey(objectRef);if(this.changedObjectsMap.has(key)){var _node$diffStatus;(_node$diffStatus=node.diffStatus)===null||_node$diffStatus===void 0?void 0:_node$diffStatus.addChangedObject(this.changedObjectsMap.get(key));}if(this.newObjectsMap.has(key)){var _node$diffStatus2;(_node$diffStatus2=node.diffStatus)===null||_node$diffStatus2===void 0?void 0:_node$diffStatus2.newObjects.push(objectRef);}if(this.deletedObjectsMap.has(key)){var _node$diffStatus3;(_node$diffStatus3=node.diffStatus)===null||_node$diffStatus3===void 0?void 0:_node$diffStatus3.deletedObjects.push(objectRef);}if(this.orphanObjectsMap.has(key)){var _node$diffStatus4;(_node$diffStatus4=node.diffStatus)===null||_node$diffStatus4===void 0?void 0:_node$diffStatus4.orphanObjects.push(objectRef);}(_this$errorsMap$get=this.errorsMap.get(key))===null||_this$errorsMap$get===void 0?void 0:_this$errorsMap$get.forEach(function(e){var _node$healthStatus;(_node$healthStatus=node.healthStatus)===null||_node$healthStatus===void 0?void 0:_node$healthStatus.errors.push(e);});(_this$warningsMap$get=this.warningsMap.get(key))===null||_this$warningsMap$get===void 0?void 0:_this$warningsMap$get.forEach(function(e){var _node$healthStatus2;(_node$healthStatus2=node.healthStatus)===null||_node$healthStatus2===void 0?void 0:_node$healthStatus2.warnings.push(e);});parentNode.pushChild(node);return node;}},{key:"buildDeletedOrOrphanNode",value:function buildDeletedOrOrphanNode(parentNode,deleted,refs){var _this5=this;var idType=deleted?"deleted":"orphaned";var newId="".concat(parentNode.id,"/(").concat(idType,")");var node=new DeletedOrOrphanObjectsCollectionNode(this.props,newId,deleted);refs.forEach(function(ref){_this5.buildObjectNode(node,ref);});parentNode.pushChild(node,true);return node;}}]);return NodeBuilder;}();function buildObjectRefKey(ref){return[ref.group,ref.version,ref.kind,ref.namespace,ref.name].join("+");} +;// CONCATENATED MODULE: ./src/components/targets-view/HistoryCards.tsx +function doGetRootNode(_x,_x2){return _doGetRootNode.apply(this,arguments);}function _doGetRootNode(){_doGetRootNode=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(api,rs){var shortNames,r,builder,_builder$buildRoot,_builder$buildRoot2,node;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return api.getShortNames();case 2:shortNames=_context.sent;_context.next=5;return api.getResult(rs.id);case 5:r=_context.sent;builder=new NodeBuilder({shortNames:shortNames,summary:rs,commandResult:r});_builder$buildRoot=builder.buildRoot(),_builder$buildRoot2=slicedToArray_slicedToArray(_builder$buildRoot,1),node=_builder$buildRoot2[0];return _context.abrupt("return",node);case 9:case"end":return _context.stop();}},_callee);}));return _doGetRootNode.apply(this,arguments);}var CardContent=/*#__PURE__*/react.memo(function(props){var _useSidePanelTabs=useSidePanelTabs(props.provider),tabs=_useSidePanelTabs.tabs,selectedTab=_useSidePanelTabs.selectedTab,handleTabChange=_useSidePanelTabs.handleTabChange;if(!props.provider||!selectedTab||!tabs.find(function(x){return x.label===selectedTab;})){return null;}return/*#__PURE__*/(0,jsx_runtime.jsxs)(TabContext,{value:selectedTab,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"36px",flex:"0 0 auto",p:"0 30px",mt:"12px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabList_TabList,{onChange:handleTabChange,children:tabs.map(function(tab,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Tab_Tab,{label:tab.label,value:tab.label},tab.label);})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:0}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{overflow:"auto",p:"30px",children:tabs.map(function(tab){return/*#__PURE__*/(0,jsx_runtime.jsx)(TabPanel_TabPanel,{value:tab.label,sx:{padding:0},children:tab.content},tab.label);})})]});});var ArrowButton=/*#__PURE__*/react.memo(function(props){var Icon={left:TriangleLeftLightIcon,right:TriangleRightLightIcon}[props.direction];return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",height:"100%",width:"80px",display:"flex",justifyContent:"center",alignItems:"center",children:!props.hidden&&/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:props.onClick,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Icon,{})})});});var HistoryCard=/*#__PURE__*/react.memo(function(props){var navigate=dist_useNavigate();var api=(0,react.useContext)(ApiContext);var _useLoadingHelper=useLoadingHelper(function(){return doGetRootNode(api,props.rs);},[api,props.rs]),_useLoadingHelper2=slicedToArray_slicedToArray(_useLoadingHelper,3),loading=_useLoadingHelper2[0],loadingError=_useLoadingHelper2[1],node=_useLoadingHelper2[2];if(loadingError){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:"Error"});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(CardPaper,{sx:_objectSpread2({position:'relative'},props.sx),children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{position:"absolute",right:"10px",top:"10px",children:props.transitionFinished&&/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(CloseLightIcon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",height:"100%",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{p:"0 16px",flex:"0 0 auto",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultItemHeader,{rs:props.rs})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",flex:"1 1 auto",overflow:"hidden",display:"flex",flexDirection:"column",children:props.transitionFinished&&(loading?/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{}):/*#__PURE__*/(0,jsx_runtime.jsx)(CardContent,{provider:node}))}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",height:"39px",display:"flex",alignItems:"center",justifyContent:"end",p:"0 30px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:function onClick(e){e.stopPropagation();navigate("/results/".concat(props.rs.id));},sx:{padding:0,width:32,height:32},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Open Result Tree",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TreeViewIcon,{})})})})})]})]});});var HistoryCards=/*#__PURE__*/react.memo(function(props){var theme=styles_useTheme_useTheme();var containerElem=(0,react.useRef)();var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),cardRect=_useState2[0],setCardRect=_useState2[1];var _useState3=(0,react.useState)('not-started'),_useState4=slicedToArray_slicedToArray(_useState3,2),transitionStatus=_useState4[0],setTransitionStatus=_useState4[1];var _useState5=(0,react.useState)(props.rs),_useState6=slicedToArray_slicedToArray(_useState5,2),currentRS=_useState6[0],setCurrentRS=_useState6[1];(0,react.useEffect)(function(){var _containerElem$curren;var rect=(_containerElem$curren=containerElem.current)===null||_containerElem$curren===void 0?void 0:_containerElem$curren.getBoundingClientRect();if(!rect){setCardRect(undefined);return;}var initialRect={left:props.initialCardRect.left-rect.left,top:props.initialCardRect.top-rect.top,width:cardWidth,height:cardHeight};setCardRect(initialRect);},[props.initialCardRect]);(0,react.useEffect)(function(){if(!cardRect){return;}var targetRect={left:0,top:0,width:'100%',height:'100%'};if(cardRect.left===targetRect.left&&cardRect.top===targetRect.top&&cardRect.width===targetRect.width&&cardRect.height===targetRect.height){return;}setTimeout(function(){setCardRect(targetRect);setTransitionStatus('running');setTimeout(function(){setTransitionStatus('finished');},theme.transitions.duration.enteringScreen);},10);},[cardRect,theme.transitions.duration.enteringScreen]);var currentRSIndex=(0,react.useMemo)(function(){return props.ts.commandResults.indexOf(currentRS);},[currentRS,props.ts.commandResults]);var onLeftArrowClick=(0,react.useCallback)(function(){if(currentRSIndex>0){setCurrentRS(props.ts.commandResults[currentRSIndex-1]);}},[currentRSIndex,props.ts.commandResults]);var onRightArrowClick=(0,react.useCallback)(function(){if(currentRSIndex + + diff --git a/pkg/webui/ui/build/static/media/triangle-left-light.svg b/pkg/webui/ui/build/static/media/triangle-left-light.svg new file mode 100644 index 000000000..6731d308d --- /dev/null +++ b/pkg/webui/ui/build/static/media/triangle-left-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/ui/build/static/media/triangle-right-light.svg b/pkg/webui/ui/build/static/media/triangle-right-light.svg new file mode 100644 index 000000000..500b3f1e8 --- /dev/null +++ b/pkg/webui/ui/build/static/media/triangle-right-light.svg @@ -0,0 +1,4 @@ + + + + From 1b14e82beac14795a65aa4d58ada0543a84e0150 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 09:12:35 +0200 Subject: [PATCH 1194/2268] feat: Enable writing of command results by default (#580) * feat: Enable writing of command results by default * tests: Don't pass --write-command-result --- cmd/kluctl/args/project.go | 2 +- docs/reference/commands/common-arguments.md | 3 ++- e2e/gitops_test.go | 1 - e2e/results_test.go | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/kluctl/args/project.go b/cmd/kluctl/args/project.go index 880426843..b09afefbb 100644 --- a/cmd/kluctl/args/project.go +++ b/cmd/kluctl/args/project.go @@ -44,7 +44,7 @@ type TargetFlags struct { } type CommandResultFlags struct { - WriteCommandResult bool `group:"results" help:"Enable writing of command results into the cluster."` + WriteCommandResult bool `group:"results" help:"Enable writing of command results into the cluster. This is enabled by default." default:"true"` ForceWriteCommandResult bool `group:"results" help:"Force writing of command results, even if the command is run in dry-run mode."` CommandResultNamespace string `group:"results" help:"Override the namespace to be used when writing command results." default:"kluctl-results"` KeepCommandResultsCount int `group:"results" help:"Configure how many old command results to keep." default:"10"` diff --git a/docs/reference/commands/common-arguments.md b/docs/reference/commands/common-arguments.md index 192cf6433..8a25add90 100644 --- a/docs/reference/commands/common-arguments.md +++ b/docs/reference/commands/common-arguments.md @@ -138,7 +138,8 @@ Command Results: "kluctl-results") --force-write-command-result Force writing of command results, even if the command is run in dry-run mode. --keep-command-results-count int Configure how many old command results to keep. (default 10) - --write-command-result Enable writing of command results into the cluster. + --write-command-result Enable writing of command results into the cluster. This is enabled by + default. (default true) ``` diff --git a/e2e/gitops_test.go b/e2e/gitops_test.go index 72d4dc89f..a9c5d1c10 100644 --- a/e2e/gitops_test.go +++ b/e2e/gitops_test.go @@ -101,7 +101,6 @@ func (suite *GitopsTestSuite) startController() { tmpKubeconfig, "--context", "context1", - "--write-command-result", } done := make(chan struct{}) go func() { diff --git a/e2e/results_test.go b/e2e/results_test.go index ae859d7c8..2bcd243fb 100644 --- a/e2e/results_test.go +++ b/e2e/results_test.go @@ -39,7 +39,7 @@ func TestWriteResult(t *testing.T) { name: "cm", namespace: p.TestSlug(), }) - p.KluctlMust("deploy", "--yes", "-t", "test", "--write-command-result") + p.KluctlMust("deploy", "--yes", "-t", "test") assertConfigMapExists(t, k, p.TestSlug(), "cm") rs, err := results.NewResultStoreSecrets(context.Background(), k.Client, "kluctl-results", 0) @@ -67,7 +67,7 @@ func TestWriteResult(t *testing.T) { _ = o.SetNestedField("v2", "data", "d1") return nil }, "") - p.KluctlMust("deploy", "--yes", "-t", "test", "--write-command-result") + p.KluctlMust("deploy", "--yes", "-t", "test") assertConfigMapExists(t, k, p.TestSlug(), "cm2") summaries, err = rs.ListCommandResultSummaries(opts) @@ -84,7 +84,7 @@ func TestWriteResult(t *testing.T) { _ = o.RemoveNestedField("deployments", 1) return nil }) - p.KluctlMust("deploy", "--yes", "-t", "test", "--write-command-result") + p.KluctlMust("deploy", "--yes", "-t", "test") assertConfigMapExists(t, k, p.TestSlug(), "cm2") summaries, err = rs.ListCommandResultSummaries(opts) @@ -95,7 +95,7 @@ func TestWriteResult(t *testing.T) { OrphanObjects: 1, }, summaries[0]) - p.KluctlMust("prune", "--yes", "-t", "test", "--write-command-result") + p.KluctlMust("prune", "--yes", "-t", "test") assertConfigMapNotExists(t, k, p.TestSlug(), "cm2") summaries, err = rs.ListCommandResultSummaries(opts) From b31683b6b0609840a32251dad2355d0fa04264dc Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 11:18:58 +0200 Subject: [PATCH 1195/2268] fix: Add missing service to webui deployment --- install/webui/webui/deployment.yaml | 14 +++++++++----- install/webui/webui/service.yaml | 13 +++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 install/webui/webui/service.yaml diff --git a/install/webui/webui/deployment.yaml b/install/webui/webui/deployment.yaml index 109a6eea4..dedf908b5 100644 --- a/install/webui/webui/deployment.yaml +++ b/install/webui/webui/deployment.yaml @@ -5,10 +5,10 @@ apiVersion: apps/v1 kind: Deployment metadata: labels: - app.kubernetes.io/component: kluctl-webui - app.kubernetes.io/instance: kluctl-controller - app.kubernetes.io/managed-by: kluctl + app.kubernetes.io/component: deployment + app.kubernetes.io/instance: kluctl-webui app.kubernetes.io/name: kluctl-webui + app.kubernetes.io/managed-by: kluctl control-plane: kluctl-webui name: kluctl-webui namespace: kluctl-system @@ -16,11 +16,15 @@ spec: replicas: 1 selector: matchLabels: - control-plane: kluctl-controller + app.kubernetes.io/component: deployment + app.kubernetes.io/instance: kluctl-webui + app.kubernetes.io/name: kluctl-webui template: metadata: labels: - control-plane: kluctl-controller + app.kubernetes.io/component: deployment + app.kubernetes.io/instance: kluctl-webui + app.kubernetes.io/name: kluctl-webui spec: containers: - name: webui diff --git a/install/webui/webui/service.yaml b/install/webui/webui/service.yaml new file mode 100644 index 000000000..4c28ef2c4 --- /dev/null +++ b/install/webui/webui/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: kluctl-webui + namespace: kluctl-system +spec: + ports: + - port: 8080 + targetPort: 8080 + selector: + app.kubernetes.io/component: deployment + app.kubernetes.io/instance: kluctl-webui + app.kubernetes.io/name: kluctl-webui From fc33f588ac83e8329a6febca97699796c2342a0d Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 11:50:24 +0200 Subject: [PATCH 1196/2268] fix: Smaller initialDelaySeconds in webui deployment --- install/webui/webui/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/webui/webui/deployment.yaml b/install/webui/webui/deployment.yaml index dedf908b5..f50c31b72 100644 --- a/install/webui/webui/deployment.yaml +++ b/install/webui/webui/deployment.yaml @@ -43,7 +43,7 @@ spec: httpGet: path: / port: 8080 - initialDelaySeconds: 15 + initialDelaySeconds: 5 periodSeconds: 20 readinessProbe: httpGet: From ac4d71d3c289ef1078b8c49a3c4929b156958234 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 11:25:25 +0200 Subject: [PATCH 1197/2268] fix: Use wss protocol when needed --- pkg/webui/ui/build/index.html | 2 +- pkg/webui/ui/build/static/js/main.js | 4 ++-- pkg/webui/ui/src/api.tsx | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/webui/ui/build/index.html b/pkg/webui/ui/build/index.html index 95134f6bf..31e223a49 100644 --- a/pkg/webui/ui/build/index.html +++ b/pkg/webui/ui/build/index.html @@ -1 +1 @@ -React App
    \ No newline at end of file +React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js index 361e5411f..882a0ead2 100644 --- a/pkg/webui/ui/build/static/js/main.js +++ b/pkg/webui/ui/build/static/js/main.js @@ -52128,7 +52128,7 @@ var loadedScripts=new Map();var loadScript=function loadScript(fileUrl){var asyn ;// CONCATENATED MODULE: ./src/utils/misc.ts function getLastPathElement(url){if(url===undefined){return undefined;}if(!url){return"";}var s=url.split("/");return s[s.length-1];}function sleep(ms){return new Promise(function(resolve){return setTimeout(resolve,ms);});} ;// CONCATENATED MODULE: ./src/api.tsx -var staticPath="./staticdata";var ObjectType=/*#__PURE__*/function(ObjectType){ObjectType["Rendered"]="rendered";ObjectType["Remote"]="remote";ObjectType["Applied"]="applied";return ObjectType;}({});function checkStaticBuild(){return _checkStaticBuild.apply(this,arguments);}function _checkStaticBuild(){_checkStaticBuild=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee19(){var p;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee19$(_context19){while(1)switch(_context19.prev=_context19.next){case 0:p=loadScript(staticPath+"/summaries.js");_context19.prev=1;_context19.next=4;return p;case 4:return _context19.abrupt("return",true);case 7:_context19.prev=7;_context19.t0=_context19["catch"](1);return _context19.abrupt("return",false);case 10:case"end":return _context19.stop();}},_callee19,null,[[1,7]]);}));return _checkStaticBuild.apply(this,arguments);}var RealApi=/*#__PURE__*/function(){function RealApi(getToken,onUnauthorized,onTokenRefresh){classCallCheck_classCallCheck(this,RealApi);this.getToken=void 0;this.onUnauthorized=void 0;this.onTokenRefresh=void 0;this.getToken=getToken;this.onUnauthorized=onUnauthorized;this.onTokenRefresh=onTokenRefresh;}createClass_createClass(RealApi,[{key:"setAuthorizationHeader",value:function setAuthorizationHeader(headers){if(!this.getToken){return;}headers['Authorization']="Bearer "+this.getToken();}},{key:"refreshToken",value:function(){var _refreshToken=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var headers,resp,j;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:headers={'Accept':'application/json'};this.setAuthorizationHeader(headers);_context.next=4;return fetch("/auth/refresh",{method:"POST",headers:headers});case 4:resp=_context.sent;if(!(resp.status===401)){_context.next=8;break;}if(this.onUnauthorized){this.onUnauthorized();}throw Error(resp.statusText);case 8:_context.next=10;return resp.json();case 10:j=_context.sent;if(this.onTokenRefresh){this.onTokenRefresh(j.token);}case 12:case"end":return _context.stop();}},_callee,this);}));function refreshToken(){return _refreshToken.apply(this,arguments);}return refreshToken;}()},{key:"handleErrors",value:function(){var _handleErrors=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee2(response,retry){var newResp;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee2$(_context2){while(1)switch(_context2.prev=_context2.next){case 0:if(response.ok){_context2.next=16;break;}if(!(response.status===401)){_context2.next=15;break;}if(!(this.getToken&&retry)){_context2.next=14;break;}console.log("retrying with token refresh");_context2.next=6;return this.refreshToken();case 6:_context2.next=8;return retry();case 8:newResp=_context2.sent;_context2.next=11;return this.handleErrors(newResp);case 11:return _context2.abrupt("return",_context2.sent);case 14:if(this.onUnauthorized){this.onUnauthorized();}case 15:throw Error(response.statusText);case 16:return _context2.abrupt("return",response);case 17:case"end":return _context2.stop();}},_callee2,this);}));function handleErrors(_x,_x2){return _handleErrors.apply(this,arguments);}return handleErrors;}()},{key:"doGet",value:function(){var _doGet=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee3(path,params){var _this=this;var url,doFetch,resp;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee3$(_context3){while(1)switch(_context3.prev=_context3.next){case 0:url=path;if(params){url+="?"+params.toString();}doFetch=function doFetch(){var headers={'Accept':'application/json'};_this.setAuthorizationHeader(headers);return fetch(url,{method:"GET",headers:headers});};_context3.next=5;return doFetch();case 5:resp=_context3.sent;_context3.next=8;return this.handleErrors(resp,doFetch);case 8:resp=_context3.sent;return _context3.abrupt("return",resp.json());case 10:case"end":return _context3.stop();}},_callee3,this);}));function doGet(_x3,_x4){return _doGet.apply(this,arguments);}return doGet;}()},{key:"doPost",value:function(){var _doPost=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee4(path,body){var _this2=this;var url,doFetch,resp;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee4$(_context4){while(1)switch(_context4.prev=_context4.next){case 0:url=path;doFetch=function doFetch(){var headers={'Accept':'application/json','Content-Type':'application/json'};_this2.setAuthorizationHeader(headers);return fetch(url,{method:"POST",body:JSON.stringify(body),headers:headers});};_context4.next=4;return doFetch();case 4:resp=_context4.sent;_context4.next=7;return this.handleErrors(resp,doFetch);case 7:resp=_context4.sent;return _context4.abrupt("return",resp);case 9:case"end":return _context4.stop();}},_callee4,this);}));function doPost(_x5,_x6){return _doPost.apply(this,arguments);}return doPost;}()},{key:"getShortNames",value:function(){var _getShortNames=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee5(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee5$(_context5){while(1)switch(_context5.prev=_context5.next){case 0:return _context5.abrupt("return",this.doGet("/api/getShortNames"));case 1:case"end":return _context5.stop();}},_callee5,this);}));function getShortNames(){return _getShortNames.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee7(filterProject,filterSubDir,handle){var host,url,params,getToken,ws,cancelled,connect;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee7$(_context7){while(1)switch(_context7.prev=_context7.next){case 0:host=window.location.host;if(false){}url="ws://".concat(host,"/api/ws");params=new URLSearchParams();if(filterProject){params.set("filterProject",filterProject);}if(filterSubDir){params.set("filterSubDir",filterSubDir);}url+="?"+params.toString();getToken=this.getToken;cancelled=false;connect=/*#__PURE__*/function(){var _ref=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee6(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee6$(_context6){while(1)switch(_context6.prev=_context6.next){case 0:if(!cancelled){_context6.next=2;break;}return _context6.abrupt("return");case 2:console.log("ws connect: "+url);ws=new WebSocket(url);ws.onopen=function(){console.log("ws connected");if(getToken){ws.send(JSON.stringify({"type":"auth","token":getToken()}));}};ws.onclose=function(event){console.log("ws close");if(!cancelled){sleep(5000).then(connect);}};ws.onerror=function(event){console.log("ws error",event);};ws.onmessage=function(event){if(cancelled){return;}var msg=JSON.parse(event.data);handle(msg);};case 8:case"end":return _context6.stop();}},_callee6);}));return function connect(){return _ref.apply(this,arguments);};}();_context7.next=12;return connect();case 12:return _context7.abrupt("return",function(){console.log("ws cancel");cancelled=true;if(ws){ws.close();}});case 13:case"end":return _context7.stop();}},_callee7,this);}));function listenUpdates(_x7,_x8,_x9){return _listenUpdates.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee8(resultId){var params,json;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee8$(_context8){while(1)switch(_context8.prev=_context8.next){case 0:params=new URLSearchParams();params.set("resultId",resultId);_context8.next=4;return this.doGet("/api/getResult",params);case 4:json=_context8.sent;return _context8.abrupt("return",new CommandResult(json));case 6:case"end":return _context8.stop();}},_callee8,this);}));function getResult(_x10){return _getResult.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee9(resultId){var params,json;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee9$(_context9){while(1)switch(_context9.prev=_context9.next){case 0:params=new URLSearchParams();params.set("resultId",resultId);_context9.next=4;return this.doGet("/api/getResultSummary",params);case 4:json=_context9.sent;return _context9.abrupt("return",new CommandResultSummary(json));case 6:case"end":return _context9.stop();}},_callee9,this);}));function getResultSummary(_x11){return _getResultSummary.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee10(resultId,ref,objectType){var params;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee10$(_context10){while(1)switch(_context10.prev=_context10.next){case 0:params=new URLSearchParams();params.set("resultId",resultId);params.set("objectType",objectType);buildRefParams(ref,params);_context10.next=6;return this.doGet("/api/getResultObject",params);case 6:return _context10.abrupt("return",_context10.sent);case 7:case"end":return _context10.stop();}},_callee10,this);}));function getResultObject(_x12,_x13,_x14){return _getResultObject.apply(this,arguments);}return getResultObject;}()},{key:"validateNow",value:function(){var _validateNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee11(project,target){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee11$(_context11){while(1)switch(_context11.prev=_context11.next){case 0:return _context11.abrupt("return",this.doPost("/api/validateNow",{"project":project,"target":target}));case 1:case"end":return _context11.stop();}},_callee11,this);}));function validateNow(_x15,_x16){return _validateNow.apply(this,arguments);}return validateNow;}()},{key:"deployNow",value:function(){var _deployNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee12(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee12$(_context12){while(1)switch(_context12.prev=_context12.next){case 0:return _context12.abrupt("return",this.doPost("/api/deployNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context12.stop();}},_callee12,this);}));function deployNow(_x17,_x18,_x19){return _deployNow.apply(this,arguments);}return deployNow;}()},{key:"reconcileNow",value:function(){var _reconcileNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee13(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee13$(_context13){while(1)switch(_context13.prev=_context13.next){case 0:return _context13.abrupt("return",this.doPost("/api/reconcileNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context13.stop();}},_callee13,this);}));function reconcileNow(_x20,_x21,_x22){return _reconcileNow.apply(this,arguments);}return reconcileNow;}()}]);return RealApi;}();var StaticApi=/*#__PURE__*/function(){function StaticApi(){classCallCheck_classCallCheck(this,StaticApi);}createClass_createClass(StaticApi,[{key:"getShortNames",value:function(){var _getShortNames2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee14(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee14$(_context14){while(1)switch(_context14.prev=_context14.next){case 0:_context14.next=2;return loadScript(staticPath+"/shortnames.js");case 2:return _context14.abrupt("return",staticShortNames);case 3:case"end":return _context14.stop();}},_callee14);}));function getShortNames(){return _getShortNames2.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee15(filterProject,filterSubDir,handle){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee15$(_context15){while(1)switch(_context15.prev=_context15.next){case 0:_context15.next=2;return loadScript(staticPath+"/summaries.js");case 2:staticSummaries.forEach(function(rs){if(filterProject&&filterProject!==rs.project.normalizedGitUrl){return;}if(filterSubDir&&filterSubDir!==rs.project.subDir){return;}handle({"type":"update_summary","summary":rs});});return _context15.abrupt("return",function(){});case 4:case"end":return _context15.stop();}},_callee15);}));function listenUpdates(_x23,_x24,_x25){return _listenUpdates2.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee16(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee16$(_context16){while(1)switch(_context16.prev=_context16.next){case 0:_context16.next=2;return loadScript(staticPath+"/result-".concat(resultId,".js"));case 2:return _context16.abrupt("return",staticResults.get(resultId));case 3:case"end":return _context16.stop();}},_callee16);}));function getResult(_x26){return _getResult2.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee17(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee17$(_context17){while(1)switch(_context17.prev=_context17.next){case 0:_context17.next=2;return loadScript(staticPath+"/summaries.js");case 2:return _context17.abrupt("return",staticSummaries.filter(function(s){return s.id===resultId;}).at(0));case 3:case"end":return _context17.stop();}},_callee17);}));function getResultSummary(_x27){return _getResultSummary2.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee18(resultId,ref,objectType){var _result$objects;var result,object;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee18$(_context18){while(1)switch(_context18.prev=_context18.next){case 0:_context18.next=2;return this.getResult(resultId);case 2:result=_context18.sent;object=(_result$objects=result.objects)===null||_result$objects===void 0?void 0:_result$objects.find(function(x){return lodash_default().isEqual(x.ref,ref);});if(object){_context18.next=6;break;}throw new Error("object not found");case 6:_context18.t0=objectType;_context18.next=_context18.t0===ObjectType.Rendered?9:_context18.t0===ObjectType.Remote?10:_context18.t0===ObjectType.Applied?11:12;break;case 9:return _context18.abrupt("return",object.rendered);case 10:return _context18.abrupt("return",object.remote);case 11:return _context18.abrupt("return",object.applied);case 12:throw new Error("unknown object type "+objectType);case 13:case"end":return _context18.stop();}},_callee18,this);}));function getResultObject(_x28,_x29,_x30){return _getResultObject2.apply(this,arguments);}return getResultObject;}()},{key:"validateNow",value:function validateNow(project,target){throw new Error("not implemented");}},{key:"reconcileNow",value:function reconcileNow(cluster,name,namespace){throw new Error("not implemented");}},{key:"deployNow",value:function deployNow(cluster,name,namespace){throw new Error("not implemented");}}]);return StaticApi;}();function buildRefParams(ref,params){params.set("kind",ref.kind);params.set("name",ref.name);if(ref.group){params.set("group",ref.group);}if(ref.version){params.set("version",ref.version);}if(ref.namespace){params.set("namespace",ref.namespace);}}function buildRefString(ref){if(ref.namespace){return"".concat(ref.namespace,"/").concat(ref.kind,"/").concat(ref.name);}else{if(ref.name){return"".concat(ref.kind,"/").concat(ref.name);}else{return ref.kind;}}}function buildRefKindElement(ref,element){var tooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{zIndex:1000,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["ApiVersion: ",[ref.group,ref.version].filter(function(x){return x;}).join("/")]}),/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["Kind: ",ref.kind]})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip,children:element?element:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.kind})});}function buildObjectRefFromObject(obj){var apiVersion=obj.apiVersion;var s=apiVersion.split("/",2);var ref=new ObjectRef();if(s.length===1){ref.version=s[0];}else{ref.group=s[0];ref.version=s[1];}ref.kind=obj.kind;ref.namespace=obj.metadata.namespace;ref.name=obj.metadata.name;return ref;}function findObjectByRef(l,ref,filter){return l===null||l===void 0?void 0:l.find(function(x){if(filter&&!filter(x)){return false;}return lodash_default().isEqual(x.ref,ref);});} +var staticPath="./staticdata";var ObjectType=/*#__PURE__*/function(ObjectType){ObjectType["Rendered"]="rendered";ObjectType["Remote"]="remote";ObjectType["Applied"]="applied";return ObjectType;}({});function checkStaticBuild(){return _checkStaticBuild.apply(this,arguments);}function _checkStaticBuild(){_checkStaticBuild=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee19(){var p;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee19$(_context19){while(1)switch(_context19.prev=_context19.next){case 0:p=loadScript(staticPath+"/summaries.js");_context19.prev=1;_context19.next=4;return p;case 4:return _context19.abrupt("return",true);case 7:_context19.prev=7;_context19.t0=_context19["catch"](1);return _context19.abrupt("return",false);case 10:case"end":return _context19.stop();}},_callee19,null,[[1,7]]);}));return _checkStaticBuild.apply(this,arguments);}var RealApi=/*#__PURE__*/function(){function RealApi(getToken,onUnauthorized,onTokenRefresh){classCallCheck_classCallCheck(this,RealApi);this.getToken=void 0;this.onUnauthorized=void 0;this.onTokenRefresh=void 0;this.getToken=getToken;this.onUnauthorized=onUnauthorized;this.onTokenRefresh=onTokenRefresh;}createClass_createClass(RealApi,[{key:"setAuthorizationHeader",value:function setAuthorizationHeader(headers){if(!this.getToken){return;}headers['Authorization']="Bearer "+this.getToken();}},{key:"refreshToken",value:function(){var _refreshToken=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(){var headers,resp,j;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:headers={'Accept':'application/json'};this.setAuthorizationHeader(headers);_context.next=4;return fetch("/auth/refresh",{method:"POST",headers:headers});case 4:resp=_context.sent;if(!(resp.status===401)){_context.next=8;break;}if(this.onUnauthorized){this.onUnauthorized();}throw Error(resp.statusText);case 8:_context.next=10;return resp.json();case 10:j=_context.sent;if(this.onTokenRefresh){this.onTokenRefresh(j.token);}case 12:case"end":return _context.stop();}},_callee,this);}));function refreshToken(){return _refreshToken.apply(this,arguments);}return refreshToken;}()},{key:"handleErrors",value:function(){var _handleErrors=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee2(response,retry){var newResp;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee2$(_context2){while(1)switch(_context2.prev=_context2.next){case 0:if(response.ok){_context2.next=16;break;}if(!(response.status===401)){_context2.next=15;break;}if(!(this.getToken&&retry)){_context2.next=14;break;}console.log("retrying with token refresh");_context2.next=6;return this.refreshToken();case 6:_context2.next=8;return retry();case 8:newResp=_context2.sent;_context2.next=11;return this.handleErrors(newResp);case 11:return _context2.abrupt("return",_context2.sent);case 14:if(this.onUnauthorized){this.onUnauthorized();}case 15:throw Error(response.statusText);case 16:return _context2.abrupt("return",response);case 17:case"end":return _context2.stop();}},_callee2,this);}));function handleErrors(_x,_x2){return _handleErrors.apply(this,arguments);}return handleErrors;}()},{key:"doGet",value:function(){var _doGet=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee3(path,params){var _this=this;var url,doFetch,resp;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee3$(_context3){while(1)switch(_context3.prev=_context3.next){case 0:url=path;if(params){url+="?"+params.toString();}doFetch=function doFetch(){var headers={'Accept':'application/json'};_this.setAuthorizationHeader(headers);return fetch(url,{method:"GET",headers:headers});};_context3.next=5;return doFetch();case 5:resp=_context3.sent;_context3.next=8;return this.handleErrors(resp,doFetch);case 8:resp=_context3.sent;return _context3.abrupt("return",resp.json());case 10:case"end":return _context3.stop();}},_callee3,this);}));function doGet(_x3,_x4){return _doGet.apply(this,arguments);}return doGet;}()},{key:"doPost",value:function(){var _doPost=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee4(path,body){var _this2=this;var url,doFetch,resp;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee4$(_context4){while(1)switch(_context4.prev=_context4.next){case 0:url=path;doFetch=function doFetch(){var headers={'Accept':'application/json','Content-Type':'application/json'};_this2.setAuthorizationHeader(headers);return fetch(url,{method:"POST",body:JSON.stringify(body),headers:headers});};_context4.next=4;return doFetch();case 4:resp=_context4.sent;_context4.next=7;return this.handleErrors(resp,doFetch);case 7:resp=_context4.sent;return _context4.abrupt("return",resp);case 9:case"end":return _context4.stop();}},_callee4,this);}));function doPost(_x5,_x6){return _doPost.apply(this,arguments);}return doPost;}()},{key:"getShortNames",value:function(){var _getShortNames=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee5(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee5$(_context5){while(1)switch(_context5.prev=_context5.next){case 0:return _context5.abrupt("return",this.doGet("/api/getShortNames"));case 1:case"end":return _context5.stop();}},_callee5,this);}));function getShortNames(){return _getShortNames.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee7(filterProject,filterSubDir,handle){var host,proto,url,params,getToken,ws,cancelled,connect;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee7$(_context7){while(1)switch(_context7.prev=_context7.next){case 0:host=window.location.host;proto="wss";if(false){}if(window.location.protocol!=="https:"){proto="ws";}url="".concat(proto,"://").concat(host,"/api/ws");params=new URLSearchParams();if(filterProject){params.set("filterProject",filterProject);}if(filterSubDir){params.set("filterSubDir",filterSubDir);}url+="?"+params.toString();getToken=this.getToken;cancelled=false;connect=/*#__PURE__*/function(){var _ref=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee6(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee6$(_context6){while(1)switch(_context6.prev=_context6.next){case 0:if(!cancelled){_context6.next=2;break;}return _context6.abrupt("return");case 2:console.log("ws connect: "+url);ws=new WebSocket(url);ws.onopen=function(){console.log("ws connected");if(getToken){ws.send(JSON.stringify({"type":"auth","token":getToken()}));}};ws.onclose=function(event){console.log("ws close");if(!cancelled){sleep(5000).then(connect);}};ws.onerror=function(event){console.log("ws error",event);};ws.onmessage=function(event){if(cancelled){return;}var msg=JSON.parse(event.data);handle(msg);};case 8:case"end":return _context6.stop();}},_callee6);}));return function connect(){return _ref.apply(this,arguments);};}();_context7.next=14;return connect();case 14:return _context7.abrupt("return",function(){console.log("ws cancel");cancelled=true;if(ws){ws.close();}});case 15:case"end":return _context7.stop();}},_callee7,this);}));function listenUpdates(_x7,_x8,_x9){return _listenUpdates.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee8(resultId){var params,json;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee8$(_context8){while(1)switch(_context8.prev=_context8.next){case 0:params=new URLSearchParams();params.set("resultId",resultId);_context8.next=4;return this.doGet("/api/getResult",params);case 4:json=_context8.sent;return _context8.abrupt("return",new CommandResult(json));case 6:case"end":return _context8.stop();}},_callee8,this);}));function getResult(_x10){return _getResult.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee9(resultId){var params,json;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee9$(_context9){while(1)switch(_context9.prev=_context9.next){case 0:params=new URLSearchParams();params.set("resultId",resultId);_context9.next=4;return this.doGet("/api/getResultSummary",params);case 4:json=_context9.sent;return _context9.abrupt("return",new CommandResultSummary(json));case 6:case"end":return _context9.stop();}},_callee9,this);}));function getResultSummary(_x11){return _getResultSummary.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee10(resultId,ref,objectType){var params;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee10$(_context10){while(1)switch(_context10.prev=_context10.next){case 0:params=new URLSearchParams();params.set("resultId",resultId);params.set("objectType",objectType);buildRefParams(ref,params);_context10.next=6;return this.doGet("/api/getResultObject",params);case 6:return _context10.abrupt("return",_context10.sent);case 7:case"end":return _context10.stop();}},_callee10,this);}));function getResultObject(_x12,_x13,_x14){return _getResultObject.apply(this,arguments);}return getResultObject;}()},{key:"validateNow",value:function(){var _validateNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee11(project,target){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee11$(_context11){while(1)switch(_context11.prev=_context11.next){case 0:return _context11.abrupt("return",this.doPost("/api/validateNow",{"project":project,"target":target}));case 1:case"end":return _context11.stop();}},_callee11,this);}));function validateNow(_x15,_x16){return _validateNow.apply(this,arguments);}return validateNow;}()},{key:"deployNow",value:function(){var _deployNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee12(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee12$(_context12){while(1)switch(_context12.prev=_context12.next){case 0:return _context12.abrupt("return",this.doPost("/api/deployNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context12.stop();}},_callee12,this);}));function deployNow(_x17,_x18,_x19){return _deployNow.apply(this,arguments);}return deployNow;}()},{key:"reconcileNow",value:function(){var _reconcileNow=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee13(cluster,name,namespace){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee13$(_context13){while(1)switch(_context13.prev=_context13.next){case 0:return _context13.abrupt("return",this.doPost("/api/reconcileNow",{"cluster":cluster,"name":name,"namespace":namespace}));case 1:case"end":return _context13.stop();}},_callee13,this);}));function reconcileNow(_x20,_x21,_x22){return _reconcileNow.apply(this,arguments);}return reconcileNow;}()}]);return RealApi;}();var StaticApi=/*#__PURE__*/function(){function StaticApi(){classCallCheck_classCallCheck(this,StaticApi);}createClass_createClass(StaticApi,[{key:"getShortNames",value:function(){var _getShortNames2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee14(){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee14$(_context14){while(1)switch(_context14.prev=_context14.next){case 0:_context14.next=2;return loadScript(staticPath+"/shortnames.js");case 2:return _context14.abrupt("return",staticShortNames);case 3:case"end":return _context14.stop();}},_callee14);}));function getShortNames(){return _getShortNames2.apply(this,arguments);}return getShortNames;}()},{key:"listenUpdates",value:function(){var _listenUpdates2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee15(filterProject,filterSubDir,handle){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee15$(_context15){while(1)switch(_context15.prev=_context15.next){case 0:_context15.next=2;return loadScript(staticPath+"/summaries.js");case 2:staticSummaries.forEach(function(rs){if(filterProject&&filterProject!==rs.project.normalizedGitUrl){return;}if(filterSubDir&&filterSubDir!==rs.project.subDir){return;}handle({"type":"update_summary","summary":rs});});return _context15.abrupt("return",function(){});case 4:case"end":return _context15.stop();}},_callee15);}));function listenUpdates(_x23,_x24,_x25){return _listenUpdates2.apply(this,arguments);}return listenUpdates;}()},{key:"getResult",value:function(){var _getResult2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee16(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee16$(_context16){while(1)switch(_context16.prev=_context16.next){case 0:_context16.next=2;return loadScript(staticPath+"/result-".concat(resultId,".js"));case 2:return _context16.abrupt("return",staticResults.get(resultId));case 3:case"end":return _context16.stop();}},_callee16);}));function getResult(_x26){return _getResult2.apply(this,arguments);}return getResult;}()},{key:"getResultSummary",value:function(){var _getResultSummary2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee17(resultId){return regeneratorRuntime_regeneratorRuntime().wrap(function _callee17$(_context17){while(1)switch(_context17.prev=_context17.next){case 0:_context17.next=2;return loadScript(staticPath+"/summaries.js");case 2:return _context17.abrupt("return",staticSummaries.filter(function(s){return s.id===resultId;}).at(0));case 3:case"end":return _context17.stop();}},_callee17);}));function getResultSummary(_x27){return _getResultSummary2.apply(this,arguments);}return getResultSummary;}()},{key:"getResultObject",value:function(){var _getResultObject2=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee18(resultId,ref,objectType){var _result$objects;var result,object;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee18$(_context18){while(1)switch(_context18.prev=_context18.next){case 0:_context18.next=2;return this.getResult(resultId);case 2:result=_context18.sent;object=(_result$objects=result.objects)===null||_result$objects===void 0?void 0:_result$objects.find(function(x){return lodash_default().isEqual(x.ref,ref);});if(object){_context18.next=6;break;}throw new Error("object not found");case 6:_context18.t0=objectType;_context18.next=_context18.t0===ObjectType.Rendered?9:_context18.t0===ObjectType.Remote?10:_context18.t0===ObjectType.Applied?11:12;break;case 9:return _context18.abrupt("return",object.rendered);case 10:return _context18.abrupt("return",object.remote);case 11:return _context18.abrupt("return",object.applied);case 12:throw new Error("unknown object type "+objectType);case 13:case"end":return _context18.stop();}},_callee18,this);}));function getResultObject(_x28,_x29,_x30){return _getResultObject2.apply(this,arguments);}return getResultObject;}()},{key:"validateNow",value:function validateNow(project,target){throw new Error("not implemented");}},{key:"reconcileNow",value:function reconcileNow(cluster,name,namespace){throw new Error("not implemented");}},{key:"deployNow",value:function deployNow(cluster,name,namespace){throw new Error("not implemented");}}]);return StaticApi;}();function buildRefParams(ref,params){params.set("kind",ref.kind);params.set("name",ref.name);if(ref.group){params.set("group",ref.group);}if(ref.version){params.set("version",ref.version);}if(ref.namespace){params.set("namespace",ref.namespace);}}function buildRefString(ref){if(ref.namespace){return"".concat(ref.namespace,"/").concat(ref.kind,"/").concat(ref.name);}else{if(ref.name){return"".concat(ref.kind,"/").concat(ref.name);}else{return ref.kind;}}}function buildRefKindElement(ref,element){var tooltip=/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{zIndex:1000,children:[/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["ApiVersion: ",[ref.group,ref.version].filter(function(x){return x;}).join("/")]}),/*#__PURE__*/(0,jsx_runtime.jsx)("br",{}),/*#__PURE__*/(0,jsx_runtime.jsxs)(Typography_Typography,{children:["Kind: ",ref.kind]})]});return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:tooltip,children:element?element:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{children:ref.kind})});}function buildObjectRefFromObject(obj){var apiVersion=obj.apiVersion;var s=apiVersion.split("/",2);var ref=new ObjectRef();if(s.length===1){ref.version=s[0];}else{ref.group=s[0];ref.version=s[1];}ref.kind=obj.kind;ref.namespace=obj.metadata.namespace;ref.name=obj.metadata.name;return ref;}function findObjectByRef(l,ref,filter){return l===null||l===void 0?void 0:l.find(function(x){if(filter&&!filter(x)){return false;}return lodash_default().isEqual(x.ref,ref);});} ;// CONCATENATED MODULE: ./src/project-summaries.ts function compareSummaries(a,b){return b.commandInfo.startTime.localeCompare(a.commandInfo.startTime)||b.commandInfo.endTime.localeCompare(b.commandInfo.endTime)||(b.commandInfo.command||"").localeCompare(a.commandInfo.command||"");}function buildProjectSummaries(summaries,validateResults){var sorted=Array.from(summaries.values());sorted.sort(compareSummaries);var m=new Map();sorted.forEach(function(rs){var projectKey=JSON.stringify(rs.projectKey);var p=m.get(projectKey);if(!p){p={project:rs.projectKey,targets:[]};m.set(projectKey,p);}var ptKey=new ProjectTargetKey();ptKey.project=rs.projectKey;ptKey.target=rs.targetKey;var vr=validateResults.get(JSON.stringify(ptKey));var target=p.targets.find(function(t){return lodash_default().isEqual(t.target,rs.targetKey);});if(!target){target={target:rs.targetKey,lastValidateResult:vr,commandResults:[]};p.targets.push(target);}target.commandResults.push(rs);});var ret=[];m.forEach(function(p){p.targets.sort(function(a,b){var _ref;return(a.target.targetName||"").localeCompare(b.target.targetName||"")||a.target.clusterId.localeCompare(b.target.clusterId)||((_ref=a.target.discriminator||"")===null||_ref===void 0?void 0:_ref.localeCompare(b.target.discriminator||""));});ret.push(p);});ret.sort(function(a,b){return(a.project.gitRepoKey||"").localeCompare(b.project.gitRepoKey||"")||(a.project.subDir||"").localeCompare(b.project.subDir||"");});return ret;} ;// CONCATENATED MODULE: ./src/components/Login.tsx @@ -61549,4 +61549,4 @@ src_reportWebVitals(); }(); /******/ })() ; -//# sourceMappingURL=main.4093ded8.js.map \ No newline at end of file +//# sourceMappingURL=main.7c75d535.js.map \ No newline at end of file diff --git a/pkg/webui/ui/src/api.tsx b/pkg/webui/ui/src/api.tsx index 2bd092be2..9c325ec86 100644 --- a/pkg/webui/ui/src/api.tsx +++ b/pkg/webui/ui/src/api.tsx @@ -152,10 +152,14 @@ export class RealApi implements Api { async listenUpdates(filterProject: string | undefined, filterSubDir: string | undefined, handle: (msg: any) => void): Promise<() => void> { let host = window.location.host + let proto = "wss" if (process.env.NODE_ENV === 'development') { host = "localhost:9090" } - let url = `ws://${host}/api/ws` + if (window.location.protocol !== "https:") { + proto = "ws" + } + let url = `${proto}://${host}/api/ws` const params = new URLSearchParams() if (filterProject) { params.set("filterProject", filterProject) From 0add46776f2f5ba5f897a6cc67af9ac03638474a Mon Sep 17 00:00:00 2001 From: nett_hier <66856670+netthier@users.noreply.github.com> Date: Thu, 15 Jun 2023 11:52:01 +0200 Subject: [PATCH 1198/2268] fix: Set seccompProfile for controller deployment (#571) Signed-off-by: netthier --- config/manager/manager.yaml | 9 ++------- install/controller/controller/manager.yaml | 2 ++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 4700ace09..5115bc02c 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -58,13 +58,8 @@ spec: # - linux securityContext: runAsNonRoot: true - # TODO(user): For common cases that do not require escalating privileges - # it is recommended to ensure that all your Pods/Containers are restrictive. - # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted - # Please uncomment the following code if your project does NOT have to work on old Kubernetes - # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). - # seccompProfile: - # type: RuntimeDefault + seccompProfile: + type: RuntimeDefault containers: - name: controller image: ghcr.io/kluctl/kluctl:latest diff --git a/install/controller/controller/manager.yaml b/install/controller/controller/manager.yaml index 8ef9d3e81..6fe0ebe3a 100644 --- a/install/controller/controller/manager.yaml +++ b/install/controller/controller/manager.yaml @@ -73,5 +73,7 @@ spec: - ALL securityContext: runAsNonRoot: true + seccompProfile: + type: RuntimeDefault serviceAccountName: kluctl-controller terminationGracePeriodSeconds: 10 From d4fba8fe720de3aa9b08f57ede2df2211efd75e0 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 14:25:20 +0200 Subject: [PATCH 1199/2268] ci: Add warning header to generated manifests (#572) --- Makefile | 9 ++++++--- install/controller/controller/crd.yaml | 1 + install/controller/controller/manager.yaml | 1 + install/controller/controller/rbac.yaml | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index c7bea97f0..4023149a4 100644 --- a/Makefile +++ b/Makefile @@ -60,9 +60,12 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen kustomize ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. $(CONTROLLER_GEN) rbac:roleName=kluctl-controller-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases - $(KUSTOMIZE) build config/crd > install/controller/controller/crd.yaml - $(KUSTOMIZE) build config/rbac > install/controller/controller/rbac.yaml - $(KUSTOMIZE) build config/manager > install/controller/controller/manager.yaml + @echo "# Warning, this file is generated via \"make manifests\", don't edit it directly but instead change the files in config/crd" > install/controller/controller/crd.yaml + $(KUSTOMIZE) build config/crd >> install/controller/controller/crd.yaml + @echo "# Warning, this file is generated via \"make manifests\", don't edit it directly but instead change the files in config/rbac" > install/controller/controller/rbac.yaml + $(KUSTOMIZE) build config/rbac >> install/controller/controller/rbac.yaml + @echo "# Warning, this file is generated via \"make manifests\", don't edit it directly but instead change the files in config/manager" > install/controller/controller/manager.yaml + $(KUSTOMIZE) build config/manager >> install/controller/controller/manager.yaml # Generate API reference documentation api-docs: gen-crd-api-reference-docs diff --git a/install/controller/controller/crd.yaml b/install/controller/controller/crd.yaml index dd1e01c15..0159cc23b 100644 --- a/install/controller/controller/crd.yaml +++ b/install/controller/controller/crd.yaml @@ -1,3 +1,4 @@ +# Warning, this file is generated via "make manifests", don't edit it directly but instead change the files in config/crd apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/install/controller/controller/manager.yaml b/install/controller/controller/manager.yaml index 6fe0ebe3a..acec1201a 100644 --- a/install/controller/controller/manager.yaml +++ b/install/controller/controller/manager.yaml @@ -1,3 +1,4 @@ +# Warning, this file is generated via "make manifests", don't edit it directly but instead change the files in config/manager apiVersion: v1 kind: Namespace metadata: diff --git a/install/controller/controller/rbac.yaml b/install/controller/controller/rbac.yaml index aa7d7bccb..9072415fc 100644 --- a/install/controller/controller/rbac.yaml +++ b/install/controller/controller/rbac.yaml @@ -1,3 +1,4 @@ +# Warning, this file is generated via "make manifests", don't edit it directly but instead change the files in config/rbac apiVersion: v1 kind: ServiceAccount metadata: From 87110b16c399ca4df37d900b84c6d009b78a68da Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 14:51:19 +0200 Subject: [PATCH 1200/2268] fix: Don't treat deleted objects still as orphan (#582) --- pkg/deployment/commands/utils.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pkg/deployment/commands/utils.go b/pkg/deployment/commands/utils.go index ebb2b08b2..44ca0359c 100644 --- a/pkg/deployment/commands/utils.go +++ b/pkg/deployment/commands/utils.go @@ -86,13 +86,22 @@ func collectObjects(c *deployment.DeploymentCollection, ru *utils.RemoteObjectUt o.New = true } } - for _, x := range orphans { - o := getOrCreate(x) - o.Orphan = true - } + + deletedMap := map[k8s.ObjectRef]bool{} for _, x := range deleted { o := getOrCreate(x) o.Deleted = true + deletedMap[o.Ref] = true + } + for _, x := range orphans { + if _, ok := deletedMap[x]; ok { + // orphan object also got deleted? This can only mean that deletion did not wait for the object to disappear, + // so we should treat this object not as orphan anymore + continue + } + o := getOrCreate(x) + o.Orphan = true + } for ref, o := range m { From acc6d97ee650512078bbf214aac1cf4a895ae241 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 16:50:14 +0200 Subject: [PATCH 1201/2268] fix: Fix snapshot detection and version replacement via prepare-release.sh (#583) --- hack/prepare-release.sh | 1 + install/webui/webui/deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hack/prepare-release.sh b/hack/prepare-release.sh index b306fb781..380832889 100755 --- a/hack/prepare-release.sh +++ b/hack/prepare-release.sh @@ -28,6 +28,7 @@ FILES="" FILES="$FILES install/controller/.kluctl.yaml" FILES="$FILES install/controller/controller/kustomization.yaml" FILES="$FILES install/webui/.kluctl.yaml" +FILES="$FILES install/webui/webui/deployment.yaml" FILES="$FILES docs/installation.md" for f in $FILES; do diff --git a/install/webui/webui/deployment.yaml b/install/webui/webui/deployment.yaml index f50c31b72..d832b2ef4 100644 --- a/install/webui/webui/deployment.yaml +++ b/install/webui/webui/deployment.yaml @@ -1,5 +1,5 @@ {% set kluctl_version = get_var("args.kluctl_version", "v2.20.4") %} -{% set pull_policy = "Always" if "-devel" in kluctl_version or "-snapshot" in kluctl_version else "IfNotPresent" %} +{% set pull_policy = "Always" if ("-devel" in kluctl_version or "-snapshot" in kluctl_version) else "IfNotPresent" %} apiVersion: apps/v1 kind: Deployment From 3d8a94c9c1f1890f810fe568a7a3e87c6de3808f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 16:59:11 +0200 Subject: [PATCH 1202/2268] fix: Rename controller_version to kluctl_version and fix snapshot detection (#585) --- cmd/kluctl/commands/cmd_controller_install.go | 8 ++++---- install/controller/.kluctl.yaml | 2 +- install/controller/controller/kustomization.yaml | 13 +++++-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/cmd/kluctl/commands/cmd_controller_install.go b/cmd/kluctl/commands/cmd_controller_install.go index d5e3db490..1e70e16a8 100644 --- a/cmd/kluctl/commands/cmd_controller_install.go +++ b/cmd/kluctl/commands/cmd_controller_install.go @@ -14,8 +14,8 @@ type controllerInstallCmd struct { args.DryRunFlags args.CommandResultFlags - Context string `group:"misc" help:"Override the context to use."` - ControllerVersion string `group:"misc" help:"Specify the controller version to install."` + Context string `group:"misc" help:"Override the context to use."` + KluctlVersion string `group:"misc" help:"Specify the controller version to install."` } func (cmd *controllerInstallCmd) Help() string { @@ -29,8 +29,8 @@ func (cmd *controllerInstallCmd) Run(ctx context.Context) error { } var deployArgs []string - if cmd.ControllerVersion != "" { - deployArgs = append(deployArgs, fmt.Sprintf("controller_version=%s", cmd.ControllerVersion)) + if cmd.KluctlVersion != "" { + deployArgs = append(deployArgs, fmt.Sprintf("kluctl_version=%s", cmd.KluctlVersion)) } cmd2 := deployCmd{ diff --git a/install/controller/.kluctl.yaml b/install/controller/.kluctl.yaml index 61be49c62..f85272bec 100644 --- a/install/controller/.kluctl.yaml +++ b/install/controller/.kluctl.yaml @@ -1,7 +1,7 @@ discriminator: kluctl.io-controller args: - - name: controller_version + - name: kluctl_version default: v2.20.4 - name: controller_args default: [] diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml index be62c808a..a7551b296 100644 --- a/install/controller/controller/kustomization.yaml +++ b/install/controller/controller/kustomization.yaml @@ -1,4 +1,6 @@ -{% set controller_version = get_var("args.controller_version", "v2.20.4") %} +# TODO remove controller_version +{% set kluctl_version = get_var(["args.kluctl_version", "args.controller_version"], "v2.20.4") %} +{% set pull_policy = "Always" if ("-devel" in kluctl_version or "-snapshot" in kluctl_version) else "IfNotPresent" %} resources: - crd.yaml @@ -12,19 +14,14 @@ patches: patch: |- - op: add path: /spec/template/spec/containers/0/image - value: ghcr.io/kluctl/kluctl:{{ controller_version }} + value: ghcr.io/kluctl/kluctl:{{ kluctl_version }} - target: kind: Deployment name: kluctl-controller patch: |- - - op: test - path: /kind - value: Deployment # this is just a dummy test to avoid empty patches -{% if "-devel" in controller_version %} - op: add path: /spec/template/spec/containers/0/imagePullPolicy - value: Always -{% endif %} + value: {{ pull_policy }} {% for a in get_var("args.controller_args", []) %} - op: add path: /spec/template/spec/containers/0/args/- From d0fb711aefaf89a0226b86b85fd605fb765e47a5 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 17:11:10 +0200 Subject: [PATCH 1203/2268] chore: Run make replace-commands-help --- docs/reference/commands/controller-install.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/reference/commands/controller-install.md b/docs/reference/commands/controller-install.md index 516e9ee91..fcaabfd83 100644 --- a/docs/reference/commands/controller-install.md +++ b/docs/reference/commands/controller-install.md @@ -28,10 +28,10 @@ In addition, the following arguments are available: Misc arguments: Command specific arguments. - --context string Override the context to use. - --controller-version string Specify the controller version to install. - --dry-run Performs all kubernetes API calls in dry-run mode. - -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. + --context string Override the context to use. + --dry-run Performs all kubernetes API calls in dry-run mode. + --kluctl-version string Specify the controller version to install. + -y, --yes Suppresses 'Are you sure?' questions and proceeds as if you would answer 'yes'. ``` From 2087c139f9d9cb7de70f5a04c35ffe9ddf51d142 Mon Sep 17 00:00:00 2001 From: Aleksei Vagarenko Date: Thu, 15 Jun 2023 21:12:45 +0600 Subject: [PATCH 1204/2268] fix: Fix cropping of sliding history card animations (#584) * Update sliding animations. * Update build. --- pkg/webui/ui/build/index.html | 2 +- pkg/webui/ui/build/static/css/main.css | 2 +- pkg/webui/ui/build/static/js/main.js | 78 +++++++++---------- .../components/targets-view/HistoryCards.tsx | 28 +++++-- 4 files changed, 63 insertions(+), 47 deletions(-) diff --git a/pkg/webui/ui/build/index.html b/pkg/webui/ui/build/index.html index 31e223a49..d31444bc3 100644 --- a/pkg/webui/ui/build/index.html +++ b/pkg/webui/ui/build/index.html @@ -1 +1 @@ -React App
    \ No newline at end of file +React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/static/css/main.css b/pkg/webui/ui/build/static/css/main.css index a8874843f..38a0dc2e1 100644 --- a/pkg/webui/ui/build/static/css/main.css +++ b/pkg/webui/ui/build/static/css/main.css @@ -84,4 +84,4 @@ body, unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } -/*# sourceMappingURL=main.9c2b54a7.css.map*/ \ No newline at end of file +/*# sourceMappingURL=main.94bdb1a0.css.map*/ \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js index 882a0ead2..bb1335e47 100644 --- a/pkg/webui/ui/build/static/js/main.js +++ b/pkg/webui/ui/build/static/js/main.js @@ -47615,7 +47615,7 @@ function SvgKluctlText(_ref, svgRef) { }))); } var ForwardRef = /*#__PURE__*/react.forwardRef(SvgKluctlText); -/* harmony default export */ var kluctl_text = (__webpack_require__.p + "static/media/kluctl-text.svg?f=kluctl-text.f97d4b1ffe2002c0802e2e1ad2044bab.svg"); +/* harmony default export */ var kluctl_text = (__webpack_require__.p + "static/media/kluctl-text.svg?f=kluctl-text.1392c9ce908d475e5086961a5b3a3072.svg"); ;// CONCATENATED MODULE: ./src/icons/git.svg var _defs; @@ -47683,7 +47683,7 @@ function SvgKluctlLogo(_ref, svgRef) { }))); } var kluctl_logo_ForwardRef = /*#__PURE__*/react.forwardRef(SvgKluctlLogo); -/* harmony default export */ var kluctl_logo = (__webpack_require__.p + "static/media/kluctl-logo.svg?f=kluctl-logo.252d0acc30f14f58acd1b5faac05c17b.svg"); +/* harmony default export */ var kluctl_logo = (__webpack_require__.p + "static/media/kluctl-logo.svg?f=kluctl-logo.928b5d24c522acfce937ce451da78cfd.svg"); ;// CONCATENATED MODULE: ./src/icons/search.svg var search_path, search_path2; @@ -47716,7 +47716,7 @@ function SvgSearch(_ref, svgRef) { }))); } var search_ForwardRef = /*#__PURE__*/react.forwardRef(SvgSearch); -/* harmony default export */ var search = (__webpack_require__.p + "static/media/search.svg?f=search.10cfa92f61b9621ea7fe3d64cac12eee.svg"); +/* harmony default export */ var search = (__webpack_require__.p + "static/media/search.svg?f=search.301f22f9e2228136d62b0edb7d97c86e.svg"); ;// CONCATENATED MODULE: ./src/icons/targets.svg var _circle, targets_path, targets_path2, targets_path3; @@ -47763,7 +47763,7 @@ function SvgTargets(_ref, svgRef) { }))); } var targets_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTargets); -/* harmony default export */ var targets = (__webpack_require__.p + "static/media/targets.svg?f=targets.2b39a3a176679d8e9ed00f0b3bfd3ba5.svg"); +/* harmony default export */ var targets = (__webpack_require__.p + "static/media/targets.svg?f=targets.ddda17cc51371fc22f6fc66cb7986073.svg"); ;// CONCATENATED MODULE: ./src/icons/target.svg var _ellipse, target_path, target_path2; @@ -47808,7 +47808,7 @@ function SvgTarget(_ref, svgRef) { }))); } var target_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTarget); -/* harmony default export */ var target = (__webpack_require__.p + "static/media/target.svg?f=target.1dd672dc23cacf59e90fbb277c9bdd9d.svg"); +/* harmony default export */ var target = (__webpack_require__.p + "static/media/target.svg?f=target.0750f7a6c362d82cc7fb678f9260c46d.svg"); ;// CONCATENATED MODULE: ./src/icons/relation-hline.svg var relation_hline_path, relation_hline_circle, _circle2; @@ -47852,7 +47852,7 @@ function SvgRelationHline(_ref, svgRef) { }))); } var relation_hline_ForwardRef = /*#__PURE__*/(/* unused pure expression or super */ null && (React.forwardRef(SvgRelationHline))); -/* harmony default export */ var relation_hline = (__webpack_require__.p + "static/media/relation-hline.svg?f=relation-hline.91c9012405859f512c983b2985999ad7.svg"); +/* harmony default export */ var relation_hline = (__webpack_require__.p + "static/media/relation-hline.svg?f=relation-hline.871cfae94c944beb7d05364a2cee37f1.svg"); ;// CONCATENATED MODULE: ./src/icons/project.svg var project_ellipse, project_path, project_path2; @@ -47899,7 +47899,7 @@ function SvgProject(_ref, svgRef) { }))); } var project_ForwardRef = /*#__PURE__*/react.forwardRef(SvgProject); -/* harmony default export */ var project = (__webpack_require__.p + "static/media/project.svg?f=project.66e77c7ad83b85f0325c2e0ffed64b8c.svg"); +/* harmony default export */ var project = (__webpack_require__.p + "static/media/project.svg?f=project.637cf85e6cadcedc900412158dfa7372.svg"); ;// CONCATENATED MODULE: ./src/icons/cpu.svg var cpu_path, cpu_path2, cpu_path3, cpu_path4, cpu_path5, _path6, _path7, _path8, _path9, _path10, _path11, _path12, _path13, _path14; @@ -47968,7 +47968,7 @@ function SvgCpu(_ref, svgRef) { }))); } var cpu_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCpu); -/* harmony default export */ var cpu = (__webpack_require__.p + "static/media/cpu.svg?f=cpu.a3fa0b9152cb81606a7da57c1b07cc85.svg"); +/* harmony default export */ var cpu = (__webpack_require__.p + "static/media/cpu.svg?f=cpu.b47d73a3f55d2fa8fa37d8ed73d178c4.svg"); ;// CONCATENATED MODULE: ./src/icons/finger-scan.svg var finger_scan_path, finger_scan_path2, finger_scan_path3, finger_scan_path4, finger_scan_path5, finger_scan_path6; @@ -48034,7 +48034,7 @@ function SvgFingerScan(_ref, svgRef) { }))); } var finger_scan_ForwardRef = /*#__PURE__*/react.forwardRef(SvgFingerScan); -/* harmony default export */ var finger_scan = (__webpack_require__.p + "static/media/finger-scan.svg?f=finger-scan.ab3c987f3fd693b908ef65df4c2cd433.svg"); +/* harmony default export */ var finger_scan = (__webpack_require__.p + "static/media/finger-scan.svg?f=finger-scan.2c034cf5598ca4927432b008dc2304c4.svg"); ;// CONCATENATED MODULE: ./src/icons/tree-view.svg var tree_view_path, tree_view_path2, tree_view_path3, tree_view_path4, tree_view_path5; @@ -48076,7 +48076,7 @@ function SvgTreeView(_ref, svgRef) { }))); } var tree_view_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTreeView); -/* harmony default export */ var tree_view = (__webpack_require__.p + "static/media/tree-view.svg?f=tree-view.a2f07b1cebd3235db357cc2b6e4c8c0d.svg"); +/* harmony default export */ var tree_view = (__webpack_require__.p + "static/media/tree-view.svg?f=tree-view.3c49a19936c3ecef1167c9ae1aad9102.svg"); ;// CONCATENATED MODULE: ./src/icons/close.svg var close_path; @@ -48105,7 +48105,7 @@ function SvgClose(_ref, svgRef) { }))); } var close_ForwardRef = /*#__PURE__*/react.forwardRef(SvgClose); -/* harmony default export */ var icons_close = (__webpack_require__.p + "static/media/close.svg?f=close.76fdca7f91598011401cb97d4b490d52.svg"); +/* harmony default export */ var icons_close = (__webpack_require__.p + "static/media/close.svg?f=close.06c69ef83805e9ea03cc487136b57b43.svg"); ;// CONCATENATED MODULE: ./src/icons/close-light.svg var close_light_path; @@ -48134,7 +48134,7 @@ function SvgCloseLight(_ref, svgRef) { }))); } var close_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCloseLight); -/* harmony default export */ var close_light = (__webpack_require__.p + "static/media/close-light.svg?f=close-light.eb8a7d21cff89473a30809cdaacf6446.svg"); +/* harmony default export */ var close_light = (__webpack_require__.p + "static/media/close-light.svg?f=close-light.3f339905970f9885aae8cc76977a1abb.svg"); ;// CONCATENATED MODULE: ./src/icons/deploy.svg var deploy_circle, deploy_path, _g; @@ -48186,7 +48186,7 @@ function SvgDeploy(_ref, svgRef) { })))); } var deploy_ForwardRef = /*#__PURE__*/react.forwardRef(SvgDeploy); -/* harmony default export */ var deploy = (__webpack_require__.p + "static/media/deploy.svg?f=deploy.2d28f597e81eb12910b9816b567b11ce.svg"); +/* harmony default export */ var deploy = (__webpack_require__.p + "static/media/deploy.svg?f=deploy.62f738f9b93ba10869f740e158192270.svg"); ;// CONCATENATED MODULE: ./src/icons/prune.svg var prune_circle, prune_path, prune_path2, prune_path3, prune_path4, prune_path5; @@ -48250,7 +48250,7 @@ function SvgPrune(_ref, svgRef) { }))); } var prune_ForwardRef = /*#__PURE__*/react.forwardRef(SvgPrune); -/* harmony default export */ var prune = (__webpack_require__.p + "static/media/prune.svg?f=prune.a5f592f5851c37276e764af73b2a8a56.svg"); +/* harmony default export */ var prune = (__webpack_require__.p + "static/media/prune.svg?f=prune.ac5564f5a55a590b9d82c5e6aac239fd.svg"); ;// CONCATENATED MODULE: ./src/icons/diff.svg var diff_circle, diff_g, diff_path; @@ -48301,7 +48301,7 @@ function SvgDiff(_ref, svgRef) { }))); } var diff_ForwardRef = /*#__PURE__*/react.forwardRef(SvgDiff); -/* harmony default export */ var diff = (__webpack_require__.p + "static/media/diff.svg?f=diff.37d81f4e01925715c51f42684bf21554.svg"); +/* harmony default export */ var diff = (__webpack_require__.p + "static/media/diff.svg?f=diff.e79db65b5d7c3fb43104133da0dc8368.svg"); ;// CONCATENATED MODULE: ./src/icons/warning.svg var warning_path, warning_path2, warning_path3; @@ -48336,7 +48336,7 @@ function SvgWarning(_ref, svgRef) { }))); } var warning_ForwardRef = /*#__PURE__*/react.forwardRef(SvgWarning); -/* harmony default export */ var icons_warning = (__webpack_require__.p + "static/media/warning.svg?f=warning.dd1c59340347be553c83108e04d6be04.svg"); +/* harmony default export */ var icons_warning = (__webpack_require__.p + "static/media/warning.svg?f=warning.ddf48b11eb0bb3fbca783743bfd0ae08.svg"); ;// CONCATENATED MODULE: ./src/icons/error.svg var error_path, error_path2; @@ -48368,7 +48368,7 @@ function SvgError(_ref, svgRef) { }))); } var error_ForwardRef = /*#__PURE__*/react.forwardRef(SvgError); -/* harmony default export */ var error = (__webpack_require__.p + "static/media/error.svg?f=error.c59bc8c7d7f86a588a16bc6ec27d448a.svg"); +/* harmony default export */ var error = (__webpack_require__.p + "static/media/error.svg?f=error.ee4b3e05eeb403dd3e8a8f65cc782fd4.svg"); ;// CONCATENATED MODULE: ./src/icons/trash.svg var trash_path, trash_path2, trash_path3, trash_path4; @@ -48411,7 +48411,7 @@ function SvgTrash(_ref, svgRef) { }))); } var trash_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTrash); -/* harmony default export */ var trash = (__webpack_require__.p + "static/media/trash.svg?f=trash.b782f36f610aae639750393e0793c538.svg"); +/* harmony default export */ var trash = (__webpack_require__.p + "static/media/trash.svg?f=trash.025b2d3120e905c307efe6236bb4d3a2.svg"); ;// CONCATENATED MODULE: ./src/icons/orphan.svg var orphan_path, orphan_path2, orphan_path3, orphan_path4; @@ -48453,7 +48453,7 @@ function SvgOrphan(_ref, svgRef) { }))); } var orphan_ForwardRef = /*#__PURE__*/react.forwardRef(SvgOrphan); -/* harmony default export */ var orphan = (__webpack_require__.p + "static/media/orphan.svg?f=orphan.6fde4d55f3fb9bbd2738b0b61fcee146.svg"); +/* harmony default export */ var orphan = (__webpack_require__.p + "static/media/orphan.svg?f=orphan.05f9bb90537f7ff1ab68bc2c4806352c.svg"); ;// CONCATENATED MODULE: ./src/icons/added.svg var added_path, added_path2; @@ -48486,7 +48486,7 @@ function SvgAdded(_ref, svgRef) { }))); } var added_ForwardRef = /*#__PURE__*/react.forwardRef(SvgAdded); -/* harmony default export */ var added = (__webpack_require__.p + "static/media/added.svg?f=added.a633ace445f83e4ab52d3c80cc788e1e.svg"); +/* harmony default export */ var added = (__webpack_require__.p + "static/media/added.svg?f=added.54530b6cceb257eddeb8f0016f3c9821.svg"); ;// CONCATENATED MODULE: ./src/icons/changed.svg var changed_path, changed_path2, changed_path3; @@ -48522,7 +48522,7 @@ function SvgChanged(_ref, svgRef) { }))); } var changed_ForwardRef = /*#__PURE__*/react.forwardRef(SvgChanged); -/* harmony default export */ var changed = (__webpack_require__.p + "static/media/changed.svg?f=changed.a9c091de612ab63c8d38d83dbb73e505.svg"); +/* harmony default export */ var changed = (__webpack_require__.p + "static/media/changed.svg?f=changed.27c5207a7397f2557ac197a3636da39d.svg"); ;// CONCATENATED MODULE: ./src/icons/checkbox.svg var _rect; @@ -48557,7 +48557,7 @@ function SvgCheckbox(_ref, svgRef) { }))); } var checkbox_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCheckbox); -/* harmony default export */ var icons_checkbox = (__webpack_require__.p + "static/media/checkbox.svg?f=checkbox.90148614b6ab0451aac6f71db92028fd.svg"); +/* harmony default export */ var icons_checkbox = (__webpack_require__.p + "static/media/checkbox.svg?f=checkbox.bcc364182a1a03dc753e7402a023eee2.svg"); ;// CONCATENATED MODULE: ./src/icons/checkbox-checked.svg var checkbox_checked_rect, checkbox_checked_path, _rect2; @@ -48601,7 +48601,7 @@ function SvgCheckboxChecked(_ref, svgRef) { }))); } var checkbox_checked_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCheckboxChecked); -/* harmony default export */ var checkbox_checked = (__webpack_require__.p + "static/media/checkbox-checked.svg?f=checkbox-checked.1c552519ffded79bb8ad25822c2573bb.svg"); +/* harmony default export */ var checkbox_checked = (__webpack_require__.p + "static/media/checkbox-checked.svg?f=checkbox-checked.57f61c3bde08a33c0bd9136714a3d3b3.svg"); ;// CONCATENATED MODULE: ./src/icons/checkbox-disabled.svg var checkbox_disabled_rect, checkbox_disabled_rect2; @@ -48644,7 +48644,7 @@ function SvgCheckboxDisabled(_ref, svgRef) { }))); } var checkbox_disabled_ForwardRef = /*#__PURE__*/(/* unused pure expression or super */ null && (React.forwardRef(SvgCheckboxDisabled))); -/* harmony default export */ var checkbox_disabled = (__webpack_require__.p + "static/media/checkbox-disabled.svg?f=checkbox-disabled.f7de3b059a1f9fc60f809e7d3fd601e2.svg"); +/* harmony default export */ var checkbox_disabled = (__webpack_require__.p + "static/media/checkbox-disabled.svg?f=checkbox-disabled.83ae46d3015ca4b4114b8a785857ef00.svg"); ;// CONCATENATED MODULE: ./src/icons/arrow-left.svg var arrow_left_path, arrow_left_path2; @@ -48677,7 +48677,7 @@ function SvgArrowLeft(_ref, svgRef) { }))); } var arrow_left_ForwardRef = /*#__PURE__*/react.forwardRef(SvgArrowLeft); -/* harmony default export */ var arrow_left = (__webpack_require__.p + "static/media/arrow-left.svg?f=arrow-left.0e69d1eb25254a957b660d86b58b7594.svg"); +/* harmony default export */ var arrow_left = (__webpack_require__.p + "static/media/arrow-left.svg?f=arrow-left.55965a5b737ce5b390f6ce9053ef61f4.svg"); ;// CONCATENATED MODULE: ./src/icons/warning-sign.svg var warning_sign_path, warning_sign_path2, warning_sign_path3; @@ -48713,7 +48713,7 @@ function SvgWarningSign(_ref, svgRef) { }))); } var warning_sign_ForwardRef = /*#__PURE__*/react.forwardRef(SvgWarningSign); -/* harmony default export */ var warning_sign = (__webpack_require__.p + "static/media/warning-sign.svg?f=warning-sign.fb9ebdb743ce95ba858869fcc9772754.svg"); +/* harmony default export */ var warning_sign = (__webpack_require__.p + "static/media/warning-sign.svg?f=warning-sign.a4dd71daaeea7e2f3212daa2159c2c55.svg"); ;// CONCATENATED MODULE: ./src/icons/changes.svg var changes_path, changes_path2, changes_path3; @@ -48750,7 +48750,7 @@ function SvgChanges(_ref, svgRef) { }))); } var changes_ForwardRef = /*#__PURE__*/react.forwardRef(SvgChanges); -/* harmony default export */ var changes = (__webpack_require__.p + "static/media/changes.svg?f=changes.b1e048d4a195559cfe9376069cd93bea.svg"); +/* harmony default export */ var changes = (__webpack_require__.p + "static/media/changes.svg?f=changes.ceb3f50f60cb8242ab5c9a5bfecb20f8.svg"); ;// CONCATENATED MODULE: ./src/icons/star.svg var star_path, star_path2; @@ -48783,7 +48783,7 @@ function SvgStar(_ref, svgRef) { }))); } var star_ForwardRef = /*#__PURE__*/react.forwardRef(SvgStar); -/* harmony default export */ var star = (__webpack_require__.p + "static/media/star.svg?f=star.0c433d1c6ccb36781955621fc40f0540.svg"); +/* harmony default export */ var star = (__webpack_require__.p + "static/media/star.svg?f=star.231e6e122fdf32a9487349a189a2a100.svg"); ;// CONCATENATED MODULE: ./src/icons/triangle-down.svg var triangle_down_path, triangle_down_path2; @@ -48816,7 +48816,7 @@ function SvgTriangleDown(_ref, svgRef) { }))); } var triangle_down_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleDown); -/* harmony default export */ var triangle_down = (__webpack_require__.p + "static/media/triangle-down.svg?f=triangle-down.cb859bf44378ddfd76730eb4a9f90a89.svg"); +/* harmony default export */ var triangle_down = (__webpack_require__.p + "static/media/triangle-down.svg?f=triangle-down.60eb89e2ef7264593e9b4daf2377cc6c.svg"); ;// CONCATENATED MODULE: ./src/icons/triangle-left-light.svg var triangle_left_light_path, triangle_left_light_path2; @@ -48850,7 +48850,7 @@ function SvgTriangleLeftLight(_ref, svgRef) { }))); } var triangle_left_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleLeftLight); -/* harmony default export */ var triangle_left_light = (__webpack_require__.p + "static/media/triangle-left-light.svg?f=triangle-left-light.723aa352c7195ed6c2c94748badb018c.svg"); +/* harmony default export */ var triangle_left_light = (__webpack_require__.p + "static/media/triangle-left-light.svg?f=triangle-left-light.65fea1fe6bee135dd5ccdcfd6ee5ba02.svg"); ;// CONCATENATED MODULE: ./src/icons/triangle-right-light.svg var triangle_right_light_path, triangle_right_light_path2; @@ -48883,7 +48883,7 @@ function SvgTriangleRightLight(_ref, svgRef) { }))); } var triangle_right_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleRightLight); -/* harmony default export */ var triangle_right_light = (__webpack_require__.p + "static/media/triangle-right-light.svg?f=triangle-right-light.22d8903e553c49bf9b0100b61fe7254f.svg"); +/* harmony default export */ var triangle_right_light = (__webpack_require__.p + "static/media/triangle-right-light.svg?f=triangle-right-light.0436dfced580e6b8bf6a64dbbb6b6ebd.svg"); ;// CONCATENATED MODULE: ./src/icons/triangle-right.svg var triangle_right_path, triangle_right_path2; @@ -48917,7 +48917,7 @@ function SvgTriangleRight(_ref, svgRef) { }))); } var triangle_right_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleRight); -/* harmony default export */ var triangle_right = (__webpack_require__.p + "static/media/triangle-right.svg?f=triangle-right.795bcf3f22906edc57cecb48328c9f89.svg"); +/* harmony default export */ var triangle_right = (__webpack_require__.p + "static/media/triangle-right.svg?f=triangle-right.4ec8dee9a1ea90ac8936f504d81720ca.svg"); ;// CONCATENATED MODULE: ./src/icons/brackets-curly.svg var brackets_curly_path; @@ -48946,7 +48946,7 @@ function SvgBracketsCurly(_ref, svgRef) { }))); } var brackets_curly_ForwardRef = /*#__PURE__*/react.forwardRef(SvgBracketsCurly); -/* harmony default export */ var brackets_curly = (__webpack_require__.p + "static/media/brackets-curly.svg?f=brackets-curly.99e04241d2e10a7cfb60d2c695401adc.svg"); +/* harmony default export */ var brackets_curly = (__webpack_require__.p + "static/media/brackets-curly.svg?f=brackets-curly.d831d285bb85a307a76da8c13f76fa9b.svg"); ;// CONCATENATED MODULE: ./src/icons/brackets-square.svg var brackets_square_path; @@ -48975,7 +48975,7 @@ function SvgBracketsSquare(_ref, svgRef) { }))); } var brackets_square_ForwardRef = /*#__PURE__*/react.forwardRef(SvgBracketsSquare); -/* harmony default export */ var brackets_square = (__webpack_require__.p + "static/media/brackets-square.svg?f=brackets-square.3daecb55d1cd657d5682c02cfe8563d5.svg"); +/* harmony default export */ var brackets_square = (__webpack_require__.p + "static/media/brackets-square.svg?f=brackets-square.c743c4f98f9a2cdddff2e1ec7843d74c.svg"); ;// CONCATENATED MODULE: ./src/icons/file.svg var file_path, file_path2, file_path3; @@ -49011,7 +49011,7 @@ function SvgFile(_ref, svgRef) { }))); } var file_ForwardRef = /*#__PURE__*/react.forwardRef(SvgFile); -/* harmony default export */ var file = (__webpack_require__.p + "static/media/file.svg?f=file.f2670ef3fe4fae6122bb957ea2390db0.svg"); +/* harmony default export */ var file = (__webpack_require__.p + "static/media/file.svg?f=file.82db4ab2b22fca5dc01ef7cc944e4cae.svg"); ;// CONCATENATED MODULE: ./src/icons/result.svg var result_circle, result_path, result_g; @@ -49063,7 +49063,7 @@ function SvgResult(_ref, svgRef) { })))); } var result_ForwardRef = /*#__PURE__*/(/* unused pure expression or super */ null && (React.forwardRef(SvgResult))); -/* harmony default export */ var result = (__webpack_require__.p + "static/media/result.svg?f=result.cc7a9a9173a072950aee17843ff145ba.svg"); +/* harmony default export */ var result = (__webpack_require__.p + "static/media/result.svg?f=result.f37cb0485efb4bdd26bde5626dcc236b.svg"); ;// CONCATENATED MODULE: ./src/icons/include.svg var include_circle, include_path, include_path2, include_path3, include_path4; @@ -49125,7 +49125,7 @@ function SvgInclude(_ref, svgRef) { }))); } var include_ForwardRef = /*#__PURE__*/react.forwardRef(SvgInclude); -/* harmony default export */ var include = (__webpack_require__.p + "static/media/include.svg?f=include.6bf4795a2f5623b297789116effd0bec.svg"); +/* harmony default export */ var include = (__webpack_require__.p + "static/media/include.svg?f=include.758427058cde3728cc49a2a93848150c.svg"); ;// CONCATENATED MODULE: ./src/icons/Icons.tsx var KluctlText=function KluctlText(){return/*#__PURE__*/(0,jsx_runtime.jsx)(ForwardRef,{width:"115px",height:"33px"});};var GitIcon=function GitIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(git_ForwardRef,{width:"35px",height:"35px"});};var KluctlLogo=function KluctlLogo(){return/*#__PURE__*/(0,jsx_runtime.jsx)(kluctl_logo_ForwardRef,{width:"50px",height:"50px"});};var SearchIcon=function SearchIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(search_ForwardRef,{width:"27px",height:"27px"});};var TargetsIcon=function TargetsIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(targets_ForwardRef,{width:"48px",height:"48px"});};var TargetIcon=function TargetIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(target_ForwardRef,{width:"45px",height:"45px"});};var RelationHLine=function RelationHLine(){return/*#__PURE__*/_jsx(RelationHLineSvg,{width:"169px",height:"12px"});};var ProjectIcon=function ProjectIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(project_ForwardRef,{width:"45px",height:"45px"});};var DeployIcon=function DeployIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(deploy_ForwardRef,{width:"45px",height:"45px"});};var PruneIcon=function PruneIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(prune_ForwardRef,{width:"45px",height:"45px"});};var DiffIcon=function DiffIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(diff_ForwardRef,{width:"45px",height:"45px"});};var CpuIcon=function CpuIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(cpu_ForwardRef,{width:"24px",height:"24px"});};var FingerScanIcon=function FingerScanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(finger_scan_ForwardRef,{width:"24px",height:"24px"});};var MessageQuestionIcon=function MessageQuestionIcon(props){return/*#__PURE__*/(0,jsx_runtime.jsx)("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:/*#__PURE__*/(0,jsx_runtime.jsx)("path",{d:"M17 2.42999H7C4 2.42999 2 4.42999 2 7.42999V13.43C2 16.43 4 18.43 7 18.43V20.56C7 21.36 7.89 21.84 8.55 21.39L13 18.43H17C20 18.43 22 16.43 22 13.43V7.42999C22 4.42999 20 2.42999 17 2.42999ZM12 14.6C11.58 14.6 11.25 14.26 11.25 13.85C11.25 13.44 11.58 13.1 12 13.1C12.42 13.1 12.75 13.44 12.75 13.85C12.75 14.26 12.42 14.6 12 14.6ZM13.26 10.45C12.87 10.71 12.75 10.88 12.75 11.16V11.37C12.75 11.78 12.41 12.12 12 12.12C11.59 12.12 11.25 11.78 11.25 11.37V11.16C11.25 9.99999 12.1 9.42999 12.42 9.20999C12.79 8.95999 12.91 8.78999 12.91 8.52999C12.91 8.02999 12.5 7.61999 12 7.61999C11.5 7.61999 11.09 8.02999 11.09 8.52999C11.09 8.93999 10.75 9.27999 10.34 9.27999C9.93 9.27999 9.59 8.93999 9.59 8.52999C9.59 7.19999 10.67 6.11999 12 6.11999C13.33 6.11999 14.41 7.19999 14.41 8.52999C14.41 9.66999 13.57 10.24 13.26 10.45Z",fill:props.color})});};var TreeViewIcon=function TreeViewIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(tree_view_ForwardRef,{width:"26px",height:"26px"});};var CloseIcon=function CloseIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(close_ForwardRef,{width:"24px",height:"24px"});};var CloseLightIcon=function CloseLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(close_light_ForwardRef,{width:"24px",height:"24px"});};var WarningIcon=function WarningIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_ForwardRef,{width:"24px",height:"24px"});};var ErrorIcon=function ErrorIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(error_ForwardRef,{width:"24px",height:"24px"});};var TrashIcon=function TrashIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(trash_ForwardRef,{width:"24px",height:"24px"});};var OrphanIcon=function OrphanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(orphan_ForwardRef,{width:"24px",height:"24px"});};var AddedIcon=function AddedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(added_ForwardRef,{width:"24px",height:"24px"});};var ChangedIcon=function ChangedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changed_ForwardRef,{width:"24px",height:"24px"});};var CheckboxIcon=function CheckboxIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_ForwardRef,{width:"24px",height:"24px"});};var CheckboxCheckedIcon=function CheckboxCheckedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_checked_ForwardRef,{width:"24px",height:"24px"});};var CheckboxDisabledIcon=function CheckboxDisabledIcon(){return/*#__PURE__*/_jsx(CheckboxDisabledIconSvg,{width:"24px",height:"24px"});};var ArrowLeftIcon=function ArrowLeftIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(arrow_left_ForwardRef,{width:"40px",height:"40px"});};var WarningSignIcon=function WarningSignIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_sign_ForwardRef,{width:"21px",height:"21px"});};var ChangesIcon=function ChangesIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changes_ForwardRef,{width:"21px",height:"21px"});};var StarIcon=function StarIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(star_ForwardRef,{width:"21px",height:"21px"});};var TriangleDownIcon=function TriangleDownIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_down_ForwardRef,{width:"50px",height:"50px"});};var TriangleLeftLightIcon=function TriangleLeftLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_left_light_ForwardRef,{width:"50px",height:"50px"});};var TriangleRightLightIcon=function TriangleRightLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_right_light_ForwardRef,{width:"50px",height:"50px"});};var TriangleRightIcon=function TriangleRightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_right_ForwardRef,{width:"50px",height:"50px"});};var BracketsCurlyIcon=function BracketsCurlyIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_curly_ForwardRef,{width:"22px",height:"18px"});};var BracketsSquareIcon=function BracketsSquareIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_square_ForwardRef,{width:"22px",height:"18px"});};var FileIcon=function FileIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(file_ForwardRef,{width:"40px",height:"40px"});};var ResultIcon=function ResultIcon(){return/*#__PURE__*/_jsx(ResultIconSvg,{width:"30px",height:"30px"});};var IncludeIcon=function IncludeIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(include_ForwardRef,{width:"30px",height:"30px"});}; @@ -59259,7 +59259,7 @@ var DeletedOrOrphanObjectsCollectionNode=/*#__PURE__*/function(_NodeData){_inher ;// CONCATENATED MODULE: ./src/components/result-view/nodes/NodeBuilder.ts var NodeBuilder=/*#__PURE__*/function(){function NodeBuilder(props){var _props$commandResult$,_this=this,_props$commandResult$2,_props$commandResult$3;classCallCheck_classCallCheck(this,NodeBuilder);this.props=void 0;this.changedObjectsMap=new Map();this.newObjectsMap=new Map();this.orphanObjectsMap=new Map();this.deletedObjectsMap=new Map();this.errorsMap=new Map();this.warningsMap=new Map();this.props=props;(_props$commandResult$=props.commandResult.objects)===null||_props$commandResult$===void 0?void 0:_props$commandResult$.forEach(function(o){var _o$changes;var key=buildObjectRefKey(o.ref);if((_o$changes=o.changes)!==null&&_o$changes!==void 0&&_o$changes.length){_this.changedObjectsMap.set(key,o);}if(o.new){_this.newObjectsMap.set(key,o.ref);}if(o.orphan){_this.orphanObjectsMap.set(key,o.ref);}if(o.deleted){_this.deletedObjectsMap.set(key,o.ref);}});(_props$commandResult$2=props.commandResult.errors)===null||_props$commandResult$2===void 0?void 0:_props$commandResult$2.forEach(function(e){var key=buildObjectRefKey(e.ref);var l=_this.errorsMap.get(key);if(!l){l=[e];_this.errorsMap.set(key,l);}else{l.push(e);}});(_props$commandResult$3=props.commandResult.warnings)===null||_props$commandResult$3===void 0?void 0:_props$commandResult$3.forEach(function(e){var key=buildObjectRefKey(e.ref);var l=_this.warningsMap.get(key);if(!l){l=[e];_this.warningsMap.set(key,l);}else{l.push(e);}});}createClass_createClass(NodeBuilder,[{key:"buildRoot",value:function buildRoot(){var rootNode=new CommandResultNodeData(this.props,"root");if(this.props.commandResult.deployment){this.buildDeploymentProjectChildren(rootNode,this.props.commandResult.deployment);}if(this.deletedObjectsMap.size){this.buildDeletedOrOrphanNode(rootNode,true,Array.from(this.deletedObjectsMap.values()));}if(this.orphanObjectsMap.size){this.buildDeletedOrOrphanNode(rootNode,false,Array.from(this.orphanObjectsMap.values()));}var nodeMap=new Map();function collect(n){nodeMap.set(n.id,n);n.children.forEach(function(c){collect(c);});}collect(rootNode);return[rootNode,nodeMap];}},{key:"buildDeploymentProjectChildren",value:function buildDeploymentProjectChildren(node,deploymentProject){var _deploymentProject$de,_this2=this;if(deploymentProject.vars){this.buildVarsSourceCollectionNode(node,deploymentProject.vars);}(_deploymentProject$de=deploymentProject.deployments)===null||_deploymentProject$de===void 0?void 0:_deploymentProject$de.forEach(function(deploymentItem,i){_this2.buildDeploymentItemNode(node,deploymentItem,i);});}},{key:"buildVarsSourceCollectionNode",value:function buildVarsSourceCollectionNode(parentNode,varsSources){var _node$varsSources,_this3=this;if(varsSources===undefined){return;}var newId="".concat(parentNode.id,"/(vars)");var node=new VarsSourceCollectionNodeData(this.props,newId);(_node$varsSources=node.varsSources).push.apply(_node$varsSources,toConsumableArray_toConsumableArray(varsSources));varsSources.forEach(function(vs,i){_this3.buildVarsSourceNode(node,vs,i);});parentNode.pushChild(node);return node;}},{key:"buildVarsSourceNode",value:function buildVarsSourceNode(parentNode,varsSource,index){var newId="".concat(parentNode.id,"/").concat(index,"}");var node=new VarsSourceNodeData(this.props,newId,varsSource);parentNode.pushChild(node);return node;}},{key:"buildDeploymentItemNode",value:function buildDeploymentItemNode(parentNode,deploymentItem,index){var _this4=this;var node;var newId="".concat(parentNode.id,"/(dis)/").concat(index);if(deploymentItem.path){var _deploymentItem$rende;node=new DeploymentItemNodeData(this.props,newId,deploymentItem);this.buildVarsSourceCollectionNode(node,deploymentItem.vars);(_deploymentItem$rende=deploymentItem.renderedObjects)===null||_deploymentItem$rende===void 0?void 0:_deploymentItem$rende.forEach(function(renderedObject){_this4.buildObjectNode(node,renderedObject);});}else if(deploymentItem.include||deploymentItem.git){node=new DeploymentItemIncludeNodeData(this.props,newId,deploymentItem,deploymentItem.renderedInclude);this.buildVarsSourceCollectionNode(node,deploymentItem.vars);if(deploymentItem.renderedInclude){this.buildDeploymentProjectChildren(node,deploymentItem.renderedInclude);}}else{return node;}parentNode.pushChild(node);return node;}},{key:"buildObjectNode",value:function buildObjectNode(parentNode,objectRef){var _this$errorsMap$get,_this$warningsMap$get;var newId="".concat(parentNode.id,"/(obj)/").concat(buildObjectRefKey(objectRef));var node=new ObjectNodeData(this.props,newId,objectRef);var key=buildObjectRefKey(objectRef);if(this.changedObjectsMap.has(key)){var _node$diffStatus;(_node$diffStatus=node.diffStatus)===null||_node$diffStatus===void 0?void 0:_node$diffStatus.addChangedObject(this.changedObjectsMap.get(key));}if(this.newObjectsMap.has(key)){var _node$diffStatus2;(_node$diffStatus2=node.diffStatus)===null||_node$diffStatus2===void 0?void 0:_node$diffStatus2.newObjects.push(objectRef);}if(this.deletedObjectsMap.has(key)){var _node$diffStatus3;(_node$diffStatus3=node.diffStatus)===null||_node$diffStatus3===void 0?void 0:_node$diffStatus3.deletedObjects.push(objectRef);}if(this.orphanObjectsMap.has(key)){var _node$diffStatus4;(_node$diffStatus4=node.diffStatus)===null||_node$diffStatus4===void 0?void 0:_node$diffStatus4.orphanObjects.push(objectRef);}(_this$errorsMap$get=this.errorsMap.get(key))===null||_this$errorsMap$get===void 0?void 0:_this$errorsMap$get.forEach(function(e){var _node$healthStatus;(_node$healthStatus=node.healthStatus)===null||_node$healthStatus===void 0?void 0:_node$healthStatus.errors.push(e);});(_this$warningsMap$get=this.warningsMap.get(key))===null||_this$warningsMap$get===void 0?void 0:_this$warningsMap$get.forEach(function(e){var _node$healthStatus2;(_node$healthStatus2=node.healthStatus)===null||_node$healthStatus2===void 0?void 0:_node$healthStatus2.warnings.push(e);});parentNode.pushChild(node);return node;}},{key:"buildDeletedOrOrphanNode",value:function buildDeletedOrOrphanNode(parentNode,deleted,refs){var _this5=this;var idType=deleted?"deleted":"orphaned";var newId="".concat(parentNode.id,"/(").concat(idType,")");var node=new DeletedOrOrphanObjectsCollectionNode(this.props,newId,deleted);refs.forEach(function(ref){_this5.buildObjectNode(node,ref);});parentNode.pushChild(node,true);return node;}}]);return NodeBuilder;}();function buildObjectRefKey(ref){return[ref.group,ref.version,ref.kind,ref.namespace,ref.name].join("+");} ;// CONCATENATED MODULE: ./src/components/targets-view/HistoryCards.tsx -function doGetRootNode(_x,_x2){return _doGetRootNode.apply(this,arguments);}function _doGetRootNode(){_doGetRootNode=asyncToGenerator_asyncToGenerator(/*#__PURE__*/regeneratorRuntime_regeneratorRuntime().mark(function _callee(api,rs){var shortNames,r,builder,_builder$buildRoot,_builder$buildRoot2,node;return regeneratorRuntime_regeneratorRuntime().wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:_context.next=2;return api.getShortNames();case 2:shortNames=_context.sent;_context.next=5;return api.getResult(rs.id);case 5:r=_context.sent;builder=new NodeBuilder({shortNames:shortNames,summary:rs,commandResult:r});_builder$buildRoot=builder.buildRoot(),_builder$buildRoot2=slicedToArray_slicedToArray(_builder$buildRoot,1),node=_builder$buildRoot2[0];return _context.abrupt("return",node);case 9:case"end":return _context.stop();}},_callee);}));return _doGetRootNode.apply(this,arguments);}var CardContent=/*#__PURE__*/react.memo(function(props){var _useSidePanelTabs=useSidePanelTabs(props.provider),tabs=_useSidePanelTabs.tabs,selectedTab=_useSidePanelTabs.selectedTab,handleTabChange=_useSidePanelTabs.handleTabChange;if(!props.provider||!selectedTab||!tabs.find(function(x){return x.label===selectedTab;})){return null;}return/*#__PURE__*/(0,jsx_runtime.jsxs)(TabContext,{value:selectedTab,children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{height:"36px",flex:"0 0 auto",p:"0 30px",mt:"12px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TabList_TabList,{onChange:handleTabChange,children:tabs.map(function(tab,i){return/*#__PURE__*/(0,jsx_runtime.jsx)(Tab_Tab,{label:tab.label,value:tab.label},tab.label);})})}),/*#__PURE__*/(0,jsx_runtime.jsx)(Divider_Divider,{sx:{margin:0}}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{overflow:"auto",p:"30px",children:tabs.map(function(tab){return/*#__PURE__*/(0,jsx_runtime.jsx)(TabPanel_TabPanel,{value:tab.label,sx:{padding:0},children:tab.content},tab.label);})})]});});var ArrowButton=/*#__PURE__*/react.memo(function(props){var Icon={left:TriangleLeftLightIcon,right:TriangleRightLightIcon}[props.direction];return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",height:"100%",width:"80px",display:"flex",justifyContent:"center",alignItems:"center",children:!props.hidden&&/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:props.onClick,children:/*#__PURE__*/(0,jsx_runtime.jsx)(Icon,{})})});});var HistoryCard=/*#__PURE__*/react.memo(function(props){var navigate=dist_useNavigate();var api=(0,react.useContext)(ApiContext);var _useLoadingHelper=useLoadingHelper(function(){return doGetRootNode(api,props.rs);},[api,props.rs]),_useLoadingHelper2=slicedToArray_slicedToArray(_useLoadingHelper,3),loading=_useLoadingHelper2[0],loadingError=_useLoadingHelper2[1],node=_useLoadingHelper2[2];if(loadingError){return/*#__PURE__*/(0,jsx_runtime.jsx)(jsx_runtime.Fragment,{children:"Error"});}return/*#__PURE__*/(0,jsx_runtime.jsxs)(CardPaper,{sx:_objectSpread2({position:'relative'},props.sx),children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{position:"absolute",right:"10px",top:"10px",children:props.transitionFinished&&/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:props.onClose,children:/*#__PURE__*/(0,jsx_runtime.jsx)(CloseLightIcon,{})})}),/*#__PURE__*/(0,jsx_runtime.jsxs)(material_Box_Box,{display:"flex",flexDirection:"column",height:"100%",justifyContent:"space-between",children:[/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{p:"0 16px",flex:"0 0 auto",children:/*#__PURE__*/(0,jsx_runtime.jsx)(CommandResultItemHeader,{rs:props.rs})}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{width:"100%",flex:"1 1 auto",overflow:"hidden",display:"flex",flexDirection:"column",children:props.transitionFinished&&(loading?/*#__PURE__*/(0,jsx_runtime.jsx)(Loading,{}):/*#__PURE__*/(0,jsx_runtime.jsx)(CardContent,{provider:node}))}),/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{flex:"0 0 auto",height:"39px",display:"flex",alignItems:"center",justifyContent:"end",p:"0 30px",children:/*#__PURE__*/(0,jsx_runtime.jsx)(IconButton_IconButton,{onClick:function onClick(e){e.stopPropagation();navigate("/results/".concat(props.rs.id));},sx:{padding:0,width:32,height:32},children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Tooltip_Tooltip,{title:"Open Result Tree",children:/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{display:"flex",children:/*#__PURE__*/(0,jsx_runtime.jsx)(TreeViewIcon,{})})})})})]})]});});var HistoryCards=/*#__PURE__*/react.memo(function(props){var theme=styles_useTheme_useTheme();var containerElem=(0,react.useRef)();var _useState=(0,react.useState)(),_useState2=slicedToArray_slicedToArray(_useState,2),cardRect=_useState2[0],setCardRect=_useState2[1];var _useState3=(0,react.useState)('not-started'),_useState4=slicedToArray_slicedToArray(_useState3,2),transitionStatus=_useState4[0],setTransitionStatus=_useState4[1];var _useState5=(0,react.useState)(props.rs),_useState6=slicedToArray_slicedToArray(_useState5,2),currentRS=_useState6[0],setCurrentRS=_useState6[1];(0,react.useEffect)(function(){var _containerElem$curren;var rect=(_containerElem$curren=containerElem.current)===null||_containerElem$curren===void 0?void 0:_containerElem$curren.getBoundingClientRect();if(!rect){setCardRect(undefined);return;}var initialRect={left:props.initialCardRect.left-rect.left,top:props.initialCardRect.top-rect.top,width:cardWidth,height:cardHeight};setCardRect(initialRect);},[props.initialCardRect]);(0,react.useEffect)(function(){if(!cardRect){return;}var targetRect={left:0,top:0,width:'100%',height:'100%'};if(cardRect.left===targetRect.left&&cardRect.top===targetRect.top&&cardRect.width===targetRect.width&&cardRect.height===targetRect.height){return;}setTimeout(function(){setCardRect(targetRect);setTransitionStatus('running');setTimeout(function(){setTransitionStatus('finished');},theme.transitions.duration.enteringScreen);},10);},[cardRect,theme.transitions.duration.enteringScreen]);var currentRSIndex=(0,react.useMemo)(function(){return props.ts.commandResults.indexOf(currentRS);},[currentRS,props.ts.commandResults]);var onLeftArrowClick=(0,react.useCallback)(function(){if(currentRSIndex>0){setCurrentRS(props.ts.commandResults[currentRSIndex-1]);}},[currentRSIndex,props.ts.commandResults]);var onRightArrowClick=(0,react.useCallback)(function(){if(currentRSIndex0){setCurrentRS(props.ts.commandResults[currentRSIndex-1]);}},[currentRSIndex,props.ts.commandResults]);var onRightArrowClick=(0,react.useCallback)(function(){if(currentRSIndex { }); +const arrowButtonWidth = 80; + const ArrowButton = React.memo((props: { direction: 'left' | 'right', onClick: () => void, @@ -80,7 +82,16 @@ const ArrowButton = React.memo((props: { right: TriangleRightLightIcon }[props.direction]; - return + return {!props.hidden && @@ -236,11 +247,16 @@ export const HistoryCards = React.memo((props: HistoryCardsProps) => { } }, [currentRSIndex, props.ts.commandResults]); + const paddingX = 40; + const gap = 2 * (paddingX + arrowButtonWidth); + return { hidden={currentRSIndex === 0 || transitionStatus !== 'finished'} /> {transitionStatus !== 'finished' && cardRect && { width='100%' height='100%' display='flex' - gap='20px' + gap={`${gap}px`} sx={{ - translate: `calc((-100% - 20px) * ${currentRSIndex})`, + translate: `calc((-100% - ${gap}px) * ${currentRSIndex})`, transition: theme.transitions.create(['translate'], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, From ca1908e0f47d2c2865dc0debda39e20a8ef12851 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 17:21:31 +0200 Subject: [PATCH 1205/2268] chore: Re-run npm build --- pkg/webui/ui/build/index.html | 2 +- pkg/webui/ui/build/static/css/main.css | 2 +- pkg/webui/ui/build/static/js/main.js | 76 +++++++++++++------------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/pkg/webui/ui/build/index.html b/pkg/webui/ui/build/index.html index d31444bc3..7b9e36bcf 100644 --- a/pkg/webui/ui/build/index.html +++ b/pkg/webui/ui/build/index.html @@ -1 +1 @@ -React App
    \ No newline at end of file +React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/static/css/main.css b/pkg/webui/ui/build/static/css/main.css index 38a0dc2e1..a8874843f 100644 --- a/pkg/webui/ui/build/static/css/main.css +++ b/pkg/webui/ui/build/static/css/main.css @@ -84,4 +84,4 @@ body, unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } -/*# sourceMappingURL=main.94bdb1a0.css.map*/ \ No newline at end of file +/*# sourceMappingURL=main.9c2b54a7.css.map*/ \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js index bb1335e47..7fc426284 100644 --- a/pkg/webui/ui/build/static/js/main.js +++ b/pkg/webui/ui/build/static/js/main.js @@ -47615,7 +47615,7 @@ function SvgKluctlText(_ref, svgRef) { }))); } var ForwardRef = /*#__PURE__*/react.forwardRef(SvgKluctlText); -/* harmony default export */ var kluctl_text = (__webpack_require__.p + "static/media/kluctl-text.svg?f=kluctl-text.1392c9ce908d475e5086961a5b3a3072.svg"); +/* harmony default export */ var kluctl_text = (__webpack_require__.p + "static/media/kluctl-text.svg?f=kluctl-text.f97d4b1ffe2002c0802e2e1ad2044bab.svg"); ;// CONCATENATED MODULE: ./src/icons/git.svg var _defs; @@ -47683,7 +47683,7 @@ function SvgKluctlLogo(_ref, svgRef) { }))); } var kluctl_logo_ForwardRef = /*#__PURE__*/react.forwardRef(SvgKluctlLogo); -/* harmony default export */ var kluctl_logo = (__webpack_require__.p + "static/media/kluctl-logo.svg?f=kluctl-logo.928b5d24c522acfce937ce451da78cfd.svg"); +/* harmony default export */ var kluctl_logo = (__webpack_require__.p + "static/media/kluctl-logo.svg?f=kluctl-logo.252d0acc30f14f58acd1b5faac05c17b.svg"); ;// CONCATENATED MODULE: ./src/icons/search.svg var search_path, search_path2; @@ -47716,7 +47716,7 @@ function SvgSearch(_ref, svgRef) { }))); } var search_ForwardRef = /*#__PURE__*/react.forwardRef(SvgSearch); -/* harmony default export */ var search = (__webpack_require__.p + "static/media/search.svg?f=search.301f22f9e2228136d62b0edb7d97c86e.svg"); +/* harmony default export */ var search = (__webpack_require__.p + "static/media/search.svg?f=search.10cfa92f61b9621ea7fe3d64cac12eee.svg"); ;// CONCATENATED MODULE: ./src/icons/targets.svg var _circle, targets_path, targets_path2, targets_path3; @@ -47763,7 +47763,7 @@ function SvgTargets(_ref, svgRef) { }))); } var targets_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTargets); -/* harmony default export */ var targets = (__webpack_require__.p + "static/media/targets.svg?f=targets.ddda17cc51371fc22f6fc66cb7986073.svg"); +/* harmony default export */ var targets = (__webpack_require__.p + "static/media/targets.svg?f=targets.2b39a3a176679d8e9ed00f0b3bfd3ba5.svg"); ;// CONCATENATED MODULE: ./src/icons/target.svg var _ellipse, target_path, target_path2; @@ -47808,7 +47808,7 @@ function SvgTarget(_ref, svgRef) { }))); } var target_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTarget); -/* harmony default export */ var target = (__webpack_require__.p + "static/media/target.svg?f=target.0750f7a6c362d82cc7fb678f9260c46d.svg"); +/* harmony default export */ var target = (__webpack_require__.p + "static/media/target.svg?f=target.1dd672dc23cacf59e90fbb277c9bdd9d.svg"); ;// CONCATENATED MODULE: ./src/icons/relation-hline.svg var relation_hline_path, relation_hline_circle, _circle2; @@ -47852,7 +47852,7 @@ function SvgRelationHline(_ref, svgRef) { }))); } var relation_hline_ForwardRef = /*#__PURE__*/(/* unused pure expression or super */ null && (React.forwardRef(SvgRelationHline))); -/* harmony default export */ var relation_hline = (__webpack_require__.p + "static/media/relation-hline.svg?f=relation-hline.871cfae94c944beb7d05364a2cee37f1.svg"); +/* harmony default export */ var relation_hline = (__webpack_require__.p + "static/media/relation-hline.svg?f=relation-hline.91c9012405859f512c983b2985999ad7.svg"); ;// CONCATENATED MODULE: ./src/icons/project.svg var project_ellipse, project_path, project_path2; @@ -47899,7 +47899,7 @@ function SvgProject(_ref, svgRef) { }))); } var project_ForwardRef = /*#__PURE__*/react.forwardRef(SvgProject); -/* harmony default export */ var project = (__webpack_require__.p + "static/media/project.svg?f=project.637cf85e6cadcedc900412158dfa7372.svg"); +/* harmony default export */ var project = (__webpack_require__.p + "static/media/project.svg?f=project.66e77c7ad83b85f0325c2e0ffed64b8c.svg"); ;// CONCATENATED MODULE: ./src/icons/cpu.svg var cpu_path, cpu_path2, cpu_path3, cpu_path4, cpu_path5, _path6, _path7, _path8, _path9, _path10, _path11, _path12, _path13, _path14; @@ -47968,7 +47968,7 @@ function SvgCpu(_ref, svgRef) { }))); } var cpu_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCpu); -/* harmony default export */ var cpu = (__webpack_require__.p + "static/media/cpu.svg?f=cpu.b47d73a3f55d2fa8fa37d8ed73d178c4.svg"); +/* harmony default export */ var cpu = (__webpack_require__.p + "static/media/cpu.svg?f=cpu.a3fa0b9152cb81606a7da57c1b07cc85.svg"); ;// CONCATENATED MODULE: ./src/icons/finger-scan.svg var finger_scan_path, finger_scan_path2, finger_scan_path3, finger_scan_path4, finger_scan_path5, finger_scan_path6; @@ -48034,7 +48034,7 @@ function SvgFingerScan(_ref, svgRef) { }))); } var finger_scan_ForwardRef = /*#__PURE__*/react.forwardRef(SvgFingerScan); -/* harmony default export */ var finger_scan = (__webpack_require__.p + "static/media/finger-scan.svg?f=finger-scan.2c034cf5598ca4927432b008dc2304c4.svg"); +/* harmony default export */ var finger_scan = (__webpack_require__.p + "static/media/finger-scan.svg?f=finger-scan.ab3c987f3fd693b908ef65df4c2cd433.svg"); ;// CONCATENATED MODULE: ./src/icons/tree-view.svg var tree_view_path, tree_view_path2, tree_view_path3, tree_view_path4, tree_view_path5; @@ -48076,7 +48076,7 @@ function SvgTreeView(_ref, svgRef) { }))); } var tree_view_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTreeView); -/* harmony default export */ var tree_view = (__webpack_require__.p + "static/media/tree-view.svg?f=tree-view.3c49a19936c3ecef1167c9ae1aad9102.svg"); +/* harmony default export */ var tree_view = (__webpack_require__.p + "static/media/tree-view.svg?f=tree-view.a2f07b1cebd3235db357cc2b6e4c8c0d.svg"); ;// CONCATENATED MODULE: ./src/icons/close.svg var close_path; @@ -48105,7 +48105,7 @@ function SvgClose(_ref, svgRef) { }))); } var close_ForwardRef = /*#__PURE__*/react.forwardRef(SvgClose); -/* harmony default export */ var icons_close = (__webpack_require__.p + "static/media/close.svg?f=close.06c69ef83805e9ea03cc487136b57b43.svg"); +/* harmony default export */ var icons_close = (__webpack_require__.p + "static/media/close.svg?f=close.76fdca7f91598011401cb97d4b490d52.svg"); ;// CONCATENATED MODULE: ./src/icons/close-light.svg var close_light_path; @@ -48134,7 +48134,7 @@ function SvgCloseLight(_ref, svgRef) { }))); } var close_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCloseLight); -/* harmony default export */ var close_light = (__webpack_require__.p + "static/media/close-light.svg?f=close-light.3f339905970f9885aae8cc76977a1abb.svg"); +/* harmony default export */ var close_light = (__webpack_require__.p + "static/media/close-light.svg?f=close-light.eb8a7d21cff89473a30809cdaacf6446.svg"); ;// CONCATENATED MODULE: ./src/icons/deploy.svg var deploy_circle, deploy_path, _g; @@ -48186,7 +48186,7 @@ function SvgDeploy(_ref, svgRef) { })))); } var deploy_ForwardRef = /*#__PURE__*/react.forwardRef(SvgDeploy); -/* harmony default export */ var deploy = (__webpack_require__.p + "static/media/deploy.svg?f=deploy.62f738f9b93ba10869f740e158192270.svg"); +/* harmony default export */ var deploy = (__webpack_require__.p + "static/media/deploy.svg?f=deploy.2d28f597e81eb12910b9816b567b11ce.svg"); ;// CONCATENATED MODULE: ./src/icons/prune.svg var prune_circle, prune_path, prune_path2, prune_path3, prune_path4, prune_path5; @@ -48250,7 +48250,7 @@ function SvgPrune(_ref, svgRef) { }))); } var prune_ForwardRef = /*#__PURE__*/react.forwardRef(SvgPrune); -/* harmony default export */ var prune = (__webpack_require__.p + "static/media/prune.svg?f=prune.ac5564f5a55a590b9d82c5e6aac239fd.svg"); +/* harmony default export */ var prune = (__webpack_require__.p + "static/media/prune.svg?f=prune.a5f592f5851c37276e764af73b2a8a56.svg"); ;// CONCATENATED MODULE: ./src/icons/diff.svg var diff_circle, diff_g, diff_path; @@ -48301,7 +48301,7 @@ function SvgDiff(_ref, svgRef) { }))); } var diff_ForwardRef = /*#__PURE__*/react.forwardRef(SvgDiff); -/* harmony default export */ var diff = (__webpack_require__.p + "static/media/diff.svg?f=diff.e79db65b5d7c3fb43104133da0dc8368.svg"); +/* harmony default export */ var diff = (__webpack_require__.p + "static/media/diff.svg?f=diff.37d81f4e01925715c51f42684bf21554.svg"); ;// CONCATENATED MODULE: ./src/icons/warning.svg var warning_path, warning_path2, warning_path3; @@ -48336,7 +48336,7 @@ function SvgWarning(_ref, svgRef) { }))); } var warning_ForwardRef = /*#__PURE__*/react.forwardRef(SvgWarning); -/* harmony default export */ var icons_warning = (__webpack_require__.p + "static/media/warning.svg?f=warning.ddf48b11eb0bb3fbca783743bfd0ae08.svg"); +/* harmony default export */ var icons_warning = (__webpack_require__.p + "static/media/warning.svg?f=warning.dd1c59340347be553c83108e04d6be04.svg"); ;// CONCATENATED MODULE: ./src/icons/error.svg var error_path, error_path2; @@ -48368,7 +48368,7 @@ function SvgError(_ref, svgRef) { }))); } var error_ForwardRef = /*#__PURE__*/react.forwardRef(SvgError); -/* harmony default export */ var error = (__webpack_require__.p + "static/media/error.svg?f=error.ee4b3e05eeb403dd3e8a8f65cc782fd4.svg"); +/* harmony default export */ var error = (__webpack_require__.p + "static/media/error.svg?f=error.c59bc8c7d7f86a588a16bc6ec27d448a.svg"); ;// CONCATENATED MODULE: ./src/icons/trash.svg var trash_path, trash_path2, trash_path3, trash_path4; @@ -48411,7 +48411,7 @@ function SvgTrash(_ref, svgRef) { }))); } var trash_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTrash); -/* harmony default export */ var trash = (__webpack_require__.p + "static/media/trash.svg?f=trash.025b2d3120e905c307efe6236bb4d3a2.svg"); +/* harmony default export */ var trash = (__webpack_require__.p + "static/media/trash.svg?f=trash.b782f36f610aae639750393e0793c538.svg"); ;// CONCATENATED MODULE: ./src/icons/orphan.svg var orphan_path, orphan_path2, orphan_path3, orphan_path4; @@ -48453,7 +48453,7 @@ function SvgOrphan(_ref, svgRef) { }))); } var orphan_ForwardRef = /*#__PURE__*/react.forwardRef(SvgOrphan); -/* harmony default export */ var orphan = (__webpack_require__.p + "static/media/orphan.svg?f=orphan.05f9bb90537f7ff1ab68bc2c4806352c.svg"); +/* harmony default export */ var orphan = (__webpack_require__.p + "static/media/orphan.svg?f=orphan.6fde4d55f3fb9bbd2738b0b61fcee146.svg"); ;// CONCATENATED MODULE: ./src/icons/added.svg var added_path, added_path2; @@ -48486,7 +48486,7 @@ function SvgAdded(_ref, svgRef) { }))); } var added_ForwardRef = /*#__PURE__*/react.forwardRef(SvgAdded); -/* harmony default export */ var added = (__webpack_require__.p + "static/media/added.svg?f=added.54530b6cceb257eddeb8f0016f3c9821.svg"); +/* harmony default export */ var added = (__webpack_require__.p + "static/media/added.svg?f=added.a633ace445f83e4ab52d3c80cc788e1e.svg"); ;// CONCATENATED MODULE: ./src/icons/changed.svg var changed_path, changed_path2, changed_path3; @@ -48522,7 +48522,7 @@ function SvgChanged(_ref, svgRef) { }))); } var changed_ForwardRef = /*#__PURE__*/react.forwardRef(SvgChanged); -/* harmony default export */ var changed = (__webpack_require__.p + "static/media/changed.svg?f=changed.27c5207a7397f2557ac197a3636da39d.svg"); +/* harmony default export */ var changed = (__webpack_require__.p + "static/media/changed.svg?f=changed.a9c091de612ab63c8d38d83dbb73e505.svg"); ;// CONCATENATED MODULE: ./src/icons/checkbox.svg var _rect; @@ -48557,7 +48557,7 @@ function SvgCheckbox(_ref, svgRef) { }))); } var checkbox_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCheckbox); -/* harmony default export */ var icons_checkbox = (__webpack_require__.p + "static/media/checkbox.svg?f=checkbox.bcc364182a1a03dc753e7402a023eee2.svg"); +/* harmony default export */ var icons_checkbox = (__webpack_require__.p + "static/media/checkbox.svg?f=checkbox.90148614b6ab0451aac6f71db92028fd.svg"); ;// CONCATENATED MODULE: ./src/icons/checkbox-checked.svg var checkbox_checked_rect, checkbox_checked_path, _rect2; @@ -48601,7 +48601,7 @@ function SvgCheckboxChecked(_ref, svgRef) { }))); } var checkbox_checked_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCheckboxChecked); -/* harmony default export */ var checkbox_checked = (__webpack_require__.p + "static/media/checkbox-checked.svg?f=checkbox-checked.57f61c3bde08a33c0bd9136714a3d3b3.svg"); +/* harmony default export */ var checkbox_checked = (__webpack_require__.p + "static/media/checkbox-checked.svg?f=checkbox-checked.1c552519ffded79bb8ad25822c2573bb.svg"); ;// CONCATENATED MODULE: ./src/icons/checkbox-disabled.svg var checkbox_disabled_rect, checkbox_disabled_rect2; @@ -48644,7 +48644,7 @@ function SvgCheckboxDisabled(_ref, svgRef) { }))); } var checkbox_disabled_ForwardRef = /*#__PURE__*/(/* unused pure expression or super */ null && (React.forwardRef(SvgCheckboxDisabled))); -/* harmony default export */ var checkbox_disabled = (__webpack_require__.p + "static/media/checkbox-disabled.svg?f=checkbox-disabled.83ae46d3015ca4b4114b8a785857ef00.svg"); +/* harmony default export */ var checkbox_disabled = (__webpack_require__.p + "static/media/checkbox-disabled.svg?f=checkbox-disabled.f7de3b059a1f9fc60f809e7d3fd601e2.svg"); ;// CONCATENATED MODULE: ./src/icons/arrow-left.svg var arrow_left_path, arrow_left_path2; @@ -48677,7 +48677,7 @@ function SvgArrowLeft(_ref, svgRef) { }))); } var arrow_left_ForwardRef = /*#__PURE__*/react.forwardRef(SvgArrowLeft); -/* harmony default export */ var arrow_left = (__webpack_require__.p + "static/media/arrow-left.svg?f=arrow-left.55965a5b737ce5b390f6ce9053ef61f4.svg"); +/* harmony default export */ var arrow_left = (__webpack_require__.p + "static/media/arrow-left.svg?f=arrow-left.0e69d1eb25254a957b660d86b58b7594.svg"); ;// CONCATENATED MODULE: ./src/icons/warning-sign.svg var warning_sign_path, warning_sign_path2, warning_sign_path3; @@ -48713,7 +48713,7 @@ function SvgWarningSign(_ref, svgRef) { }))); } var warning_sign_ForwardRef = /*#__PURE__*/react.forwardRef(SvgWarningSign); -/* harmony default export */ var warning_sign = (__webpack_require__.p + "static/media/warning-sign.svg?f=warning-sign.a4dd71daaeea7e2f3212daa2159c2c55.svg"); +/* harmony default export */ var warning_sign = (__webpack_require__.p + "static/media/warning-sign.svg?f=warning-sign.fb9ebdb743ce95ba858869fcc9772754.svg"); ;// CONCATENATED MODULE: ./src/icons/changes.svg var changes_path, changes_path2, changes_path3; @@ -48750,7 +48750,7 @@ function SvgChanges(_ref, svgRef) { }))); } var changes_ForwardRef = /*#__PURE__*/react.forwardRef(SvgChanges); -/* harmony default export */ var changes = (__webpack_require__.p + "static/media/changes.svg?f=changes.ceb3f50f60cb8242ab5c9a5bfecb20f8.svg"); +/* harmony default export */ var changes = (__webpack_require__.p + "static/media/changes.svg?f=changes.b1e048d4a195559cfe9376069cd93bea.svg"); ;// CONCATENATED MODULE: ./src/icons/star.svg var star_path, star_path2; @@ -48783,7 +48783,7 @@ function SvgStar(_ref, svgRef) { }))); } var star_ForwardRef = /*#__PURE__*/react.forwardRef(SvgStar); -/* harmony default export */ var star = (__webpack_require__.p + "static/media/star.svg?f=star.231e6e122fdf32a9487349a189a2a100.svg"); +/* harmony default export */ var star = (__webpack_require__.p + "static/media/star.svg?f=star.0c433d1c6ccb36781955621fc40f0540.svg"); ;// CONCATENATED MODULE: ./src/icons/triangle-down.svg var triangle_down_path, triangle_down_path2; @@ -48816,7 +48816,7 @@ function SvgTriangleDown(_ref, svgRef) { }))); } var triangle_down_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleDown); -/* harmony default export */ var triangle_down = (__webpack_require__.p + "static/media/triangle-down.svg?f=triangle-down.60eb89e2ef7264593e9b4daf2377cc6c.svg"); +/* harmony default export */ var triangle_down = (__webpack_require__.p + "static/media/triangle-down.svg?f=triangle-down.cb859bf44378ddfd76730eb4a9f90a89.svg"); ;// CONCATENATED MODULE: ./src/icons/triangle-left-light.svg var triangle_left_light_path, triangle_left_light_path2; @@ -48850,7 +48850,7 @@ function SvgTriangleLeftLight(_ref, svgRef) { }))); } var triangle_left_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleLeftLight); -/* harmony default export */ var triangle_left_light = (__webpack_require__.p + "static/media/triangle-left-light.svg?f=triangle-left-light.65fea1fe6bee135dd5ccdcfd6ee5ba02.svg"); +/* harmony default export */ var triangle_left_light = (__webpack_require__.p + "static/media/triangle-left-light.svg?f=triangle-left-light.723aa352c7195ed6c2c94748badb018c.svg"); ;// CONCATENATED MODULE: ./src/icons/triangle-right-light.svg var triangle_right_light_path, triangle_right_light_path2; @@ -48883,7 +48883,7 @@ function SvgTriangleRightLight(_ref, svgRef) { }))); } var triangle_right_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleRightLight); -/* harmony default export */ var triangle_right_light = (__webpack_require__.p + "static/media/triangle-right-light.svg?f=triangle-right-light.0436dfced580e6b8bf6a64dbbb6b6ebd.svg"); +/* harmony default export */ var triangle_right_light = (__webpack_require__.p + "static/media/triangle-right-light.svg?f=triangle-right-light.22d8903e553c49bf9b0100b61fe7254f.svg"); ;// CONCATENATED MODULE: ./src/icons/triangle-right.svg var triangle_right_path, triangle_right_path2; @@ -48917,7 +48917,7 @@ function SvgTriangleRight(_ref, svgRef) { }))); } var triangle_right_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleRight); -/* harmony default export */ var triangle_right = (__webpack_require__.p + "static/media/triangle-right.svg?f=triangle-right.4ec8dee9a1ea90ac8936f504d81720ca.svg"); +/* harmony default export */ var triangle_right = (__webpack_require__.p + "static/media/triangle-right.svg?f=triangle-right.795bcf3f22906edc57cecb48328c9f89.svg"); ;// CONCATENATED MODULE: ./src/icons/brackets-curly.svg var brackets_curly_path; @@ -48946,7 +48946,7 @@ function SvgBracketsCurly(_ref, svgRef) { }))); } var brackets_curly_ForwardRef = /*#__PURE__*/react.forwardRef(SvgBracketsCurly); -/* harmony default export */ var brackets_curly = (__webpack_require__.p + "static/media/brackets-curly.svg?f=brackets-curly.d831d285bb85a307a76da8c13f76fa9b.svg"); +/* harmony default export */ var brackets_curly = (__webpack_require__.p + "static/media/brackets-curly.svg?f=brackets-curly.99e04241d2e10a7cfb60d2c695401adc.svg"); ;// CONCATENATED MODULE: ./src/icons/brackets-square.svg var brackets_square_path; @@ -48975,7 +48975,7 @@ function SvgBracketsSquare(_ref, svgRef) { }))); } var brackets_square_ForwardRef = /*#__PURE__*/react.forwardRef(SvgBracketsSquare); -/* harmony default export */ var brackets_square = (__webpack_require__.p + "static/media/brackets-square.svg?f=brackets-square.c743c4f98f9a2cdddff2e1ec7843d74c.svg"); +/* harmony default export */ var brackets_square = (__webpack_require__.p + "static/media/brackets-square.svg?f=brackets-square.3daecb55d1cd657d5682c02cfe8563d5.svg"); ;// CONCATENATED MODULE: ./src/icons/file.svg var file_path, file_path2, file_path3; @@ -49011,7 +49011,7 @@ function SvgFile(_ref, svgRef) { }))); } var file_ForwardRef = /*#__PURE__*/react.forwardRef(SvgFile); -/* harmony default export */ var file = (__webpack_require__.p + "static/media/file.svg?f=file.82db4ab2b22fca5dc01ef7cc944e4cae.svg"); +/* harmony default export */ var file = (__webpack_require__.p + "static/media/file.svg?f=file.f2670ef3fe4fae6122bb957ea2390db0.svg"); ;// CONCATENATED MODULE: ./src/icons/result.svg var result_circle, result_path, result_g; @@ -49063,7 +49063,7 @@ function SvgResult(_ref, svgRef) { })))); } var result_ForwardRef = /*#__PURE__*/(/* unused pure expression or super */ null && (React.forwardRef(SvgResult))); -/* harmony default export */ var result = (__webpack_require__.p + "static/media/result.svg?f=result.f37cb0485efb4bdd26bde5626dcc236b.svg"); +/* harmony default export */ var result = (__webpack_require__.p + "static/media/result.svg?f=result.cc7a9a9173a072950aee17843ff145ba.svg"); ;// CONCATENATED MODULE: ./src/icons/include.svg var include_circle, include_path, include_path2, include_path3, include_path4; @@ -49125,7 +49125,7 @@ function SvgInclude(_ref, svgRef) { }))); } var include_ForwardRef = /*#__PURE__*/react.forwardRef(SvgInclude); -/* harmony default export */ var include = (__webpack_require__.p + "static/media/include.svg?f=include.758427058cde3728cc49a2a93848150c.svg"); +/* harmony default export */ var include = (__webpack_require__.p + "static/media/include.svg?f=include.6bf4795a2f5623b297789116effd0bec.svg"); ;// CONCATENATED MODULE: ./src/icons/Icons.tsx var KluctlText=function KluctlText(){return/*#__PURE__*/(0,jsx_runtime.jsx)(ForwardRef,{width:"115px",height:"33px"});};var GitIcon=function GitIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(git_ForwardRef,{width:"35px",height:"35px"});};var KluctlLogo=function KluctlLogo(){return/*#__PURE__*/(0,jsx_runtime.jsx)(kluctl_logo_ForwardRef,{width:"50px",height:"50px"});};var SearchIcon=function SearchIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(search_ForwardRef,{width:"27px",height:"27px"});};var TargetsIcon=function TargetsIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(targets_ForwardRef,{width:"48px",height:"48px"});};var TargetIcon=function TargetIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(target_ForwardRef,{width:"45px",height:"45px"});};var RelationHLine=function RelationHLine(){return/*#__PURE__*/_jsx(RelationHLineSvg,{width:"169px",height:"12px"});};var ProjectIcon=function ProjectIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(project_ForwardRef,{width:"45px",height:"45px"});};var DeployIcon=function DeployIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(deploy_ForwardRef,{width:"45px",height:"45px"});};var PruneIcon=function PruneIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(prune_ForwardRef,{width:"45px",height:"45px"});};var DiffIcon=function DiffIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(diff_ForwardRef,{width:"45px",height:"45px"});};var CpuIcon=function CpuIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(cpu_ForwardRef,{width:"24px",height:"24px"});};var FingerScanIcon=function FingerScanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(finger_scan_ForwardRef,{width:"24px",height:"24px"});};var MessageQuestionIcon=function MessageQuestionIcon(props){return/*#__PURE__*/(0,jsx_runtime.jsx)("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:/*#__PURE__*/(0,jsx_runtime.jsx)("path",{d:"M17 2.42999H7C4 2.42999 2 4.42999 2 7.42999V13.43C2 16.43 4 18.43 7 18.43V20.56C7 21.36 7.89 21.84 8.55 21.39L13 18.43H17C20 18.43 22 16.43 22 13.43V7.42999C22 4.42999 20 2.42999 17 2.42999ZM12 14.6C11.58 14.6 11.25 14.26 11.25 13.85C11.25 13.44 11.58 13.1 12 13.1C12.42 13.1 12.75 13.44 12.75 13.85C12.75 14.26 12.42 14.6 12 14.6ZM13.26 10.45C12.87 10.71 12.75 10.88 12.75 11.16V11.37C12.75 11.78 12.41 12.12 12 12.12C11.59 12.12 11.25 11.78 11.25 11.37V11.16C11.25 9.99999 12.1 9.42999 12.42 9.20999C12.79 8.95999 12.91 8.78999 12.91 8.52999C12.91 8.02999 12.5 7.61999 12 7.61999C11.5 7.61999 11.09 8.02999 11.09 8.52999C11.09 8.93999 10.75 9.27999 10.34 9.27999C9.93 9.27999 9.59 8.93999 9.59 8.52999C9.59 7.19999 10.67 6.11999 12 6.11999C13.33 6.11999 14.41 7.19999 14.41 8.52999C14.41 9.66999 13.57 10.24 13.26 10.45Z",fill:props.color})});};var TreeViewIcon=function TreeViewIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(tree_view_ForwardRef,{width:"26px",height:"26px"});};var CloseIcon=function CloseIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(close_ForwardRef,{width:"24px",height:"24px"});};var CloseLightIcon=function CloseLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(close_light_ForwardRef,{width:"24px",height:"24px"});};var WarningIcon=function WarningIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_ForwardRef,{width:"24px",height:"24px"});};var ErrorIcon=function ErrorIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(error_ForwardRef,{width:"24px",height:"24px"});};var TrashIcon=function TrashIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(trash_ForwardRef,{width:"24px",height:"24px"});};var OrphanIcon=function OrphanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(orphan_ForwardRef,{width:"24px",height:"24px"});};var AddedIcon=function AddedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(added_ForwardRef,{width:"24px",height:"24px"});};var ChangedIcon=function ChangedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changed_ForwardRef,{width:"24px",height:"24px"});};var CheckboxIcon=function CheckboxIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_ForwardRef,{width:"24px",height:"24px"});};var CheckboxCheckedIcon=function CheckboxCheckedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_checked_ForwardRef,{width:"24px",height:"24px"});};var CheckboxDisabledIcon=function CheckboxDisabledIcon(){return/*#__PURE__*/_jsx(CheckboxDisabledIconSvg,{width:"24px",height:"24px"});};var ArrowLeftIcon=function ArrowLeftIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(arrow_left_ForwardRef,{width:"40px",height:"40px"});};var WarningSignIcon=function WarningSignIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_sign_ForwardRef,{width:"21px",height:"21px"});};var ChangesIcon=function ChangesIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changes_ForwardRef,{width:"21px",height:"21px"});};var StarIcon=function StarIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(star_ForwardRef,{width:"21px",height:"21px"});};var TriangleDownIcon=function TriangleDownIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_down_ForwardRef,{width:"50px",height:"50px"});};var TriangleLeftLightIcon=function TriangleLeftLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_left_light_ForwardRef,{width:"50px",height:"50px"});};var TriangleRightLightIcon=function TriangleRightLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_right_light_ForwardRef,{width:"50px",height:"50px"});};var TriangleRightIcon=function TriangleRightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_right_ForwardRef,{width:"50px",height:"50px"});};var BracketsCurlyIcon=function BracketsCurlyIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_curly_ForwardRef,{width:"22px",height:"18px"});};var BracketsSquareIcon=function BracketsSquareIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_square_ForwardRef,{width:"22px",height:"18px"});};var FileIcon=function FileIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(file_ForwardRef,{width:"40px",height:"40px"});};var ResultIcon=function ResultIcon(){return/*#__PURE__*/_jsx(ResultIconSvg,{width:"30px",height:"30px"});};var IncludeIcon=function IncludeIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(include_ForwardRef,{width:"30px",height:"30px"});}; @@ -61549,4 +61549,4 @@ src_reportWebVitals(); }(); /******/ })() ; -//# sourceMappingURL=main.5e199973.js.map \ No newline at end of file +//# sourceMappingURL=main.45a7295d.js.map \ No newline at end of file From fdbc61129ae175a3abf4b77e233c6bb86eebcf84 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 22:06:57 +0200 Subject: [PATCH 1206/2268] fix: Enforce LF line-ending for .svg files (#586) --- pkg/webui/ui/.gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 pkg/webui/ui/.gitattributes diff --git a/pkg/webui/ui/.gitattributes b/pkg/webui/ui/.gitattributes new file mode 100644 index 000000000..0c34d559f --- /dev/null +++ b/pkg/webui/ui/.gitattributes @@ -0,0 +1 @@ +*.svg text eol=lf \ No newline at end of file From eda6ac8f62107e3949c4d1a303d8c050100d1fec Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 15 Jun 2023 22:12:07 +0200 Subject: [PATCH 1207/2268] fix: Also treat *.map as eol=lf (#588) --- pkg/webui/ui/.gitattributes | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/webui/ui/.gitattributes b/pkg/webui/ui/.gitattributes index 0c34d559f..447692d7d 100644 --- a/pkg/webui/ui/.gitattributes +++ b/pkg/webui/ui/.gitattributes @@ -1 +1,2 @@ -*.svg text eol=lf \ No newline at end of file +*.svg text eol=lf +*.map text eol=lf From 121660189e0fac13a89b026060c0290fd3d331f7 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 16 Jun 2023 00:34:09 +0200 Subject: [PATCH 1208/2268] fix: Instead of using .gitattributes to fix CRLF issues, implement it via fix-assets (#589) --- pkg/webui/ui/.gitattributes | 2 - pkg/webui/ui/build/index.html | 2 +- pkg/webui/ui/build/static/css/main.css | 12 ++-- pkg/webui/ui/build/static/js/787.chunk.js | 2 +- pkg/webui/ui/build/static/js/main.js | 78 +++++++++++------------ pkg/webui/ui/fix-assets/main.go | 63 ++++++++++++++++-- 6 files changed, 105 insertions(+), 54 deletions(-) delete mode 100644 pkg/webui/ui/.gitattributes diff --git a/pkg/webui/ui/.gitattributes b/pkg/webui/ui/.gitattributes deleted file mode 100644 index 447692d7d..000000000 --- a/pkg/webui/ui/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -*.svg text eol=lf -*.map text eol=lf diff --git a/pkg/webui/ui/build/index.html b/pkg/webui/ui/build/index.html index 7b9e36bcf..044a50d48 100644 --- a/pkg/webui/ui/build/index.html +++ b/pkg/webui/ui/build/index.html @@ -1 +1 @@ -React App
    \ No newline at end of file +React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/static/css/main.css b/pkg/webui/ui/build/static/css/main.css index a8874843f..86c1ca612 100644 --- a/pkg/webui/ui/build/static/css/main.css +++ b/pkg/webui/ui/build/static/css/main.css @@ -36,7 +36,7 @@ body, font-display: swap; font-display: var(--fontsource-display, swap); font-weight: 200 1000; - src: url(../../static/media/nunito-cyrillic-ext-wght-normal.woff2?f=nunito-cyrillic-ext-wght-normal.c18a3502d6473272bdc3.woff2) format('woff2-variations'); + src: url(../../static/media/nunito-cyrillic-ext-wght-normal.woff2?h=b2611fa3f916d23df9b0735ba668944500ff23b73f9da4fbb10818c875d482a0) format('woff2-variations'); unicode-range: U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F; } @@ -47,7 +47,7 @@ body, font-display: swap; font-display: var(--fontsource-display, swap); font-weight: 200 1000; - src: url(../../static/media/nunito-cyrillic-wght-normal.woff2?f=nunito-cyrillic-wght-normal.ab3faa41df1758ee5b88.woff2) format('woff2-variations'); + src: url(../../static/media/nunito-cyrillic-wght-normal.woff2?h=7ca4b4bb8be6840990cc92b2dee938f142df99c93ce85063b391a09369b63b17) format('woff2-variations'); unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; } @@ -58,7 +58,7 @@ body, font-display: swap; font-display: var(--fontsource-display, swap); font-weight: 200 1000; - src: url(../../static/media/nunito-vietnamese-wght-normal.woff2?f=nunito-vietnamese-wght-normal.864aa311404227008fae.woff2) format('woff2-variations'); + src: url(../../static/media/nunito-vietnamese-wght-normal.woff2?h=0ef9726dbc36b5871efa4b0cfdc43fd1bfed5dd48aeb70dc8210e8cb9bc9247b) format('woff2-variations'); unicode-range: U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB; } @@ -69,7 +69,7 @@ body, font-display: swap; font-display: var(--fontsource-display, swap); font-weight: 200 1000; - src: url(../../static/media/nunito-latin-ext-wght-normal.woff2?f=nunito-latin-ext-wght-normal.b2120f41c4a82a277229.woff2) format('woff2-variations'); + src: url(../../static/media/nunito-latin-ext-wght-normal.woff2?h=89def7428656f40331c1430ee1dc1846ed1e30d7001707b548f9f816d27264a5) format('woff2-variations'); unicode-range: U+0100-02AF,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF; } @@ -80,8 +80,8 @@ body, font-display: swap; font-display: var(--fontsource-display, swap); font-weight: 200 1000; - src: url(../../static/media/nunito-latin-wght-normal.woff2?f=nunito-latin-wght-normal.07adf0b2f5cb89c62ba7.woff2) format('woff2-variations'); + src: url(../../static/media/nunito-latin-wght-normal.woff2?h=96217f1d27fb909f92b4a6b35a0d3d6775f2f0b4d136d27aee88547d3ed87357) format('woff2-variations'); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } -/*# sourceMappingURL=main.9c2b54a7.css.map*/ \ No newline at end of file +/*# sourceMappingURL=main.css.map?h=9212af710a168fca4ce287f416924bf456fbc497b2cc289b74211c435224c397*/ \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/787.chunk.js b/pkg/webui/ui/build/static/js/787.chunk.js index 748939666..15b544b3a 100644 --- a/pkg/webui/ui/build/static/js/787.chunk.js +++ b/pkg/webui/ui/build/static/js/787.chunk.js @@ -236,4 +236,4 @@ var e, /***/ }) }]); -//# sourceMappingURL=787.98a1313e.chunk.js.map \ No newline at end of file +//# sourceMappingURL=787.chunk.js.map?h=47820fdbf26705ec6b47ae95042786f8669103d43ced5b19492db0ef8a1a30eb \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js index 7fc426284..7f430b626 100644 --- a/pkg/webui/ui/build/static/js/main.js +++ b/pkg/webui/ui/build/static/js/main.js @@ -47615,7 +47615,7 @@ function SvgKluctlText(_ref, svgRef) { }))); } var ForwardRef = /*#__PURE__*/react.forwardRef(SvgKluctlText); -/* harmony default export */ var kluctl_text = (__webpack_require__.p + "static/media/kluctl-text.svg?f=kluctl-text.f97d4b1ffe2002c0802e2e1ad2044bab.svg"); +/* harmony default export */ var kluctl_text = (__webpack_require__.p + "static/media/kluctl-text.svg?h=5599a99d8e965c1475a0712854d4c20b8f920a28267ed65213cbdcba720a1a33"); ;// CONCATENATED MODULE: ./src/icons/git.svg var _defs; @@ -47654,7 +47654,7 @@ function SvgGit(_ref, svgRef) { }))); } var git_ForwardRef = /*#__PURE__*/react.forwardRef(SvgGit); -/* harmony default export */ var git = (__webpack_require__.p + "static/media/git.svg?f=git.53474401b92b10a2c44571348999e714.svg"); +/* harmony default export */ var git = (__webpack_require__.p + "static/media/git.svg?h=2989537d0badbe5ef04e33d0c26fed9dd7f949310e0eb0f85dcbf9ff43e1e231"); ;// CONCATENATED MODULE: ./src/icons/kluctl-logo.svg var kluctl_logo_path; @@ -47683,7 +47683,7 @@ function SvgKluctlLogo(_ref, svgRef) { }))); } var kluctl_logo_ForwardRef = /*#__PURE__*/react.forwardRef(SvgKluctlLogo); -/* harmony default export */ var kluctl_logo = (__webpack_require__.p + "static/media/kluctl-logo.svg?f=kluctl-logo.252d0acc30f14f58acd1b5faac05c17b.svg"); +/* harmony default export */ var kluctl_logo = (__webpack_require__.p + "static/media/kluctl-logo.svg?h=7d059c061a18c3766a797697efc167ad38bfd4871a68abd35614a49684ea83cb"); ;// CONCATENATED MODULE: ./src/icons/search.svg var search_path, search_path2; @@ -47716,7 +47716,7 @@ function SvgSearch(_ref, svgRef) { }))); } var search_ForwardRef = /*#__PURE__*/react.forwardRef(SvgSearch); -/* harmony default export */ var search = (__webpack_require__.p + "static/media/search.svg?f=search.10cfa92f61b9621ea7fe3d64cac12eee.svg"); +/* harmony default export */ var search = (__webpack_require__.p + "static/media/search.svg?h=7e9ab6fee66c4ecdfb04a958152201a6c415cea8e98368f513f7e38bbcb59265"); ;// CONCATENATED MODULE: ./src/icons/targets.svg var _circle, targets_path, targets_path2, targets_path3; @@ -47763,7 +47763,7 @@ function SvgTargets(_ref, svgRef) { }))); } var targets_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTargets); -/* harmony default export */ var targets = (__webpack_require__.p + "static/media/targets.svg?f=targets.2b39a3a176679d8e9ed00f0b3bfd3ba5.svg"); +/* harmony default export */ var targets = (__webpack_require__.p + "static/media/targets.svg?h=a44bb94e204eb47bd105d1a987b6d514bb4b84d4e57c6597d7339a9dbc926224"); ;// CONCATENATED MODULE: ./src/icons/target.svg var _ellipse, target_path, target_path2; @@ -47808,7 +47808,7 @@ function SvgTarget(_ref, svgRef) { }))); } var target_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTarget); -/* harmony default export */ var target = (__webpack_require__.p + "static/media/target.svg?f=target.1dd672dc23cacf59e90fbb277c9bdd9d.svg"); +/* harmony default export */ var target = (__webpack_require__.p + "static/media/target.svg?h=49b4146149b8d455ad856589b7c88f795a8a43c6ddba7d0014be1550fb552723"); ;// CONCATENATED MODULE: ./src/icons/relation-hline.svg var relation_hline_path, relation_hline_circle, _circle2; @@ -47852,7 +47852,7 @@ function SvgRelationHline(_ref, svgRef) { }))); } var relation_hline_ForwardRef = /*#__PURE__*/(/* unused pure expression or super */ null && (React.forwardRef(SvgRelationHline))); -/* harmony default export */ var relation_hline = (__webpack_require__.p + "static/media/relation-hline.svg?f=relation-hline.91c9012405859f512c983b2985999ad7.svg"); +/* harmony default export */ var relation_hline = (__webpack_require__.p + "static/media/relation-hline.svg?h=d8e3ff7b79ea251ab37f8117527bd452a5ca962eb1ca21c6e7b8e285ee57468c"); ;// CONCATENATED MODULE: ./src/icons/project.svg var project_ellipse, project_path, project_path2; @@ -47899,7 +47899,7 @@ function SvgProject(_ref, svgRef) { }))); } var project_ForwardRef = /*#__PURE__*/react.forwardRef(SvgProject); -/* harmony default export */ var project = (__webpack_require__.p + "static/media/project.svg?f=project.66e77c7ad83b85f0325c2e0ffed64b8c.svg"); +/* harmony default export */ var project = (__webpack_require__.p + "static/media/project.svg?h=44199fa94e808397355a59b736af8b3c2bf87f2f6a5205c31b415a873a4d4d8f"); ;// CONCATENATED MODULE: ./src/icons/cpu.svg var cpu_path, cpu_path2, cpu_path3, cpu_path4, cpu_path5, _path6, _path7, _path8, _path9, _path10, _path11, _path12, _path13, _path14; @@ -47968,7 +47968,7 @@ function SvgCpu(_ref, svgRef) { }))); } var cpu_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCpu); -/* harmony default export */ var cpu = (__webpack_require__.p + "static/media/cpu.svg?f=cpu.a3fa0b9152cb81606a7da57c1b07cc85.svg"); +/* harmony default export */ var cpu = (__webpack_require__.p + "static/media/cpu.svg?h=4f20d8f5d919bccd9f172faa8762929f37ddd85a6e458fe84b53e349a1d1a125"); ;// CONCATENATED MODULE: ./src/icons/finger-scan.svg var finger_scan_path, finger_scan_path2, finger_scan_path3, finger_scan_path4, finger_scan_path5, finger_scan_path6; @@ -48034,7 +48034,7 @@ function SvgFingerScan(_ref, svgRef) { }))); } var finger_scan_ForwardRef = /*#__PURE__*/react.forwardRef(SvgFingerScan); -/* harmony default export */ var finger_scan = (__webpack_require__.p + "static/media/finger-scan.svg?f=finger-scan.ab3c987f3fd693b908ef65df4c2cd433.svg"); +/* harmony default export */ var finger_scan = (__webpack_require__.p + "static/media/finger-scan.svg?h=415c47592edb926869c3c59c6ef803d1faf7ab06b4d0110031222a21a61abc7f"); ;// CONCATENATED MODULE: ./src/icons/tree-view.svg var tree_view_path, tree_view_path2, tree_view_path3, tree_view_path4, tree_view_path5; @@ -48076,7 +48076,7 @@ function SvgTreeView(_ref, svgRef) { }))); } var tree_view_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTreeView); -/* harmony default export */ var tree_view = (__webpack_require__.p + "static/media/tree-view.svg?f=tree-view.a2f07b1cebd3235db357cc2b6e4c8c0d.svg"); +/* harmony default export */ var tree_view = (__webpack_require__.p + "static/media/tree-view.svg?h=9ef6fdcafc96d72a91f43a39e2cd00344b439cd3f3ff289363ad0a8baada77a6"); ;// CONCATENATED MODULE: ./src/icons/close.svg var close_path; @@ -48105,7 +48105,7 @@ function SvgClose(_ref, svgRef) { }))); } var close_ForwardRef = /*#__PURE__*/react.forwardRef(SvgClose); -/* harmony default export */ var icons_close = (__webpack_require__.p + "static/media/close.svg?f=close.76fdca7f91598011401cb97d4b490d52.svg"); +/* harmony default export */ var icons_close = (__webpack_require__.p + "static/media/close.svg?h=f8d9dcdf085e62ba04b182f66ef408c71c13691bd83ed431aac09c981b3b3a8d"); ;// CONCATENATED MODULE: ./src/icons/close-light.svg var close_light_path; @@ -48134,7 +48134,7 @@ function SvgCloseLight(_ref, svgRef) { }))); } var close_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCloseLight); -/* harmony default export */ var close_light = (__webpack_require__.p + "static/media/close-light.svg?f=close-light.eb8a7d21cff89473a30809cdaacf6446.svg"); +/* harmony default export */ var close_light = (__webpack_require__.p + "static/media/close-light.svg?h=19c3aa7cdc6bf35102d6f9437c6a0d4412e6e349c926b591122af8a8df11ee7e"); ;// CONCATENATED MODULE: ./src/icons/deploy.svg var deploy_circle, deploy_path, _g; @@ -48186,7 +48186,7 @@ function SvgDeploy(_ref, svgRef) { })))); } var deploy_ForwardRef = /*#__PURE__*/react.forwardRef(SvgDeploy); -/* harmony default export */ var deploy = (__webpack_require__.p + "static/media/deploy.svg?f=deploy.2d28f597e81eb12910b9816b567b11ce.svg"); +/* harmony default export */ var deploy = (__webpack_require__.p + "static/media/deploy.svg?h=5ef5e7631c6b158f7ed8e25d00e145bb139937a3def2af691a45ee930a906bcc"); ;// CONCATENATED MODULE: ./src/icons/prune.svg var prune_circle, prune_path, prune_path2, prune_path3, prune_path4, prune_path5; @@ -48250,7 +48250,7 @@ function SvgPrune(_ref, svgRef) { }))); } var prune_ForwardRef = /*#__PURE__*/react.forwardRef(SvgPrune); -/* harmony default export */ var prune = (__webpack_require__.p + "static/media/prune.svg?f=prune.a5f592f5851c37276e764af73b2a8a56.svg"); +/* harmony default export */ var prune = (__webpack_require__.p + "static/media/prune.svg?h=782022127a83a36b114977736d9e36c4101fce39ad822abee1a474eff352a604"); ;// CONCATENATED MODULE: ./src/icons/diff.svg var diff_circle, diff_g, diff_path; @@ -48301,7 +48301,7 @@ function SvgDiff(_ref, svgRef) { }))); } var diff_ForwardRef = /*#__PURE__*/react.forwardRef(SvgDiff); -/* harmony default export */ var diff = (__webpack_require__.p + "static/media/diff.svg?f=diff.37d81f4e01925715c51f42684bf21554.svg"); +/* harmony default export */ var diff = (__webpack_require__.p + "static/media/diff.svg?h=e5b9cc64086c22cbd40424e67cf80f9696e32fe5640c5cedce8774233059b146"); ;// CONCATENATED MODULE: ./src/icons/warning.svg var warning_path, warning_path2, warning_path3; @@ -48336,7 +48336,7 @@ function SvgWarning(_ref, svgRef) { }))); } var warning_ForwardRef = /*#__PURE__*/react.forwardRef(SvgWarning); -/* harmony default export */ var icons_warning = (__webpack_require__.p + "static/media/warning.svg?f=warning.dd1c59340347be553c83108e04d6be04.svg"); +/* harmony default export */ var icons_warning = (__webpack_require__.p + "static/media/warning.svg?h=3f478d508e7172505f66b7dadbd81d0e156360ff8bc0de0d12ec851c42ac864f"); ;// CONCATENATED MODULE: ./src/icons/error.svg var error_path, error_path2; @@ -48368,7 +48368,7 @@ function SvgError(_ref, svgRef) { }))); } var error_ForwardRef = /*#__PURE__*/react.forwardRef(SvgError); -/* harmony default export */ var error = (__webpack_require__.p + "static/media/error.svg?f=error.c59bc8c7d7f86a588a16bc6ec27d448a.svg"); +/* harmony default export */ var error = (__webpack_require__.p + "static/media/error.svg?h=9a026f75493e261450266b437a9e4a5c1cb049ba2ee56efa107230a3d057c468"); ;// CONCATENATED MODULE: ./src/icons/trash.svg var trash_path, trash_path2, trash_path3, trash_path4; @@ -48411,7 +48411,7 @@ function SvgTrash(_ref, svgRef) { }))); } var trash_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTrash); -/* harmony default export */ var trash = (__webpack_require__.p + "static/media/trash.svg?f=trash.b782f36f610aae639750393e0793c538.svg"); +/* harmony default export */ var trash = (__webpack_require__.p + "static/media/trash.svg?h=414c070724e5b79d4382fb146751a613df58166e88df2baa42fffa71aee32f8a"); ;// CONCATENATED MODULE: ./src/icons/orphan.svg var orphan_path, orphan_path2, orphan_path3, orphan_path4; @@ -48453,7 +48453,7 @@ function SvgOrphan(_ref, svgRef) { }))); } var orphan_ForwardRef = /*#__PURE__*/react.forwardRef(SvgOrphan); -/* harmony default export */ var orphan = (__webpack_require__.p + "static/media/orphan.svg?f=orphan.6fde4d55f3fb9bbd2738b0b61fcee146.svg"); +/* harmony default export */ var orphan = (__webpack_require__.p + "static/media/orphan.svg?h=75637a27d6cf40bee4022eff91c852ecca4f0bcc7c8c78bf3cd02cd76a5fba66"); ;// CONCATENATED MODULE: ./src/icons/added.svg var added_path, added_path2; @@ -48486,7 +48486,7 @@ function SvgAdded(_ref, svgRef) { }))); } var added_ForwardRef = /*#__PURE__*/react.forwardRef(SvgAdded); -/* harmony default export */ var added = (__webpack_require__.p + "static/media/added.svg?f=added.a633ace445f83e4ab52d3c80cc788e1e.svg"); +/* harmony default export */ var added = (__webpack_require__.p + "static/media/added.svg?h=af4dd83591778905da5dcbc1833893ff89a96495158cab04e1b7b415284b0388"); ;// CONCATENATED MODULE: ./src/icons/changed.svg var changed_path, changed_path2, changed_path3; @@ -48522,7 +48522,7 @@ function SvgChanged(_ref, svgRef) { }))); } var changed_ForwardRef = /*#__PURE__*/react.forwardRef(SvgChanged); -/* harmony default export */ var changed = (__webpack_require__.p + "static/media/changed.svg?f=changed.a9c091de612ab63c8d38d83dbb73e505.svg"); +/* harmony default export */ var changed = (__webpack_require__.p + "static/media/changed.svg?h=a30883ccb8a4cd3e6416232dd4032ffd8e66e0ffee0111be7c7a98268a4a1605"); ;// CONCATENATED MODULE: ./src/icons/checkbox.svg var _rect; @@ -48557,7 +48557,7 @@ function SvgCheckbox(_ref, svgRef) { }))); } var checkbox_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCheckbox); -/* harmony default export */ var icons_checkbox = (__webpack_require__.p + "static/media/checkbox.svg?f=checkbox.90148614b6ab0451aac6f71db92028fd.svg"); +/* harmony default export */ var icons_checkbox = (__webpack_require__.p + "static/media/checkbox.svg?h=0303869c42d1b682ecbb742cc0854c5638754d1330c39b80b6be07d82fd4fcea"); ;// CONCATENATED MODULE: ./src/icons/checkbox-checked.svg var checkbox_checked_rect, checkbox_checked_path, _rect2; @@ -48601,7 +48601,7 @@ function SvgCheckboxChecked(_ref, svgRef) { }))); } var checkbox_checked_ForwardRef = /*#__PURE__*/react.forwardRef(SvgCheckboxChecked); -/* harmony default export */ var checkbox_checked = (__webpack_require__.p + "static/media/checkbox-checked.svg?f=checkbox-checked.1c552519ffded79bb8ad25822c2573bb.svg"); +/* harmony default export */ var checkbox_checked = (__webpack_require__.p + "static/media/checkbox-checked.svg?h=92ab4f2e6bfa34dbb548604d6ad1ae00f19076060aacd9c2a2c6ca31f469079c"); ;// CONCATENATED MODULE: ./src/icons/checkbox-disabled.svg var checkbox_disabled_rect, checkbox_disabled_rect2; @@ -48644,7 +48644,7 @@ function SvgCheckboxDisabled(_ref, svgRef) { }))); } var checkbox_disabled_ForwardRef = /*#__PURE__*/(/* unused pure expression or super */ null && (React.forwardRef(SvgCheckboxDisabled))); -/* harmony default export */ var checkbox_disabled = (__webpack_require__.p + "static/media/checkbox-disabled.svg?f=checkbox-disabled.f7de3b059a1f9fc60f809e7d3fd601e2.svg"); +/* harmony default export */ var checkbox_disabled = (__webpack_require__.p + "static/media/checkbox-disabled.svg?h=94fbeaf8068db2fddb43810b7f56cf4271d719f30f0ea327108e57af9d275856"); ;// CONCATENATED MODULE: ./src/icons/arrow-left.svg var arrow_left_path, arrow_left_path2; @@ -48677,7 +48677,7 @@ function SvgArrowLeft(_ref, svgRef) { }))); } var arrow_left_ForwardRef = /*#__PURE__*/react.forwardRef(SvgArrowLeft); -/* harmony default export */ var arrow_left = (__webpack_require__.p + "static/media/arrow-left.svg?f=arrow-left.0e69d1eb25254a957b660d86b58b7594.svg"); +/* harmony default export */ var arrow_left = (__webpack_require__.p + "static/media/arrow-left.svg?h=183e112934d3d7e04d2e2fe78ee2f47a7f04210313c736747ee3370f4656f22f"); ;// CONCATENATED MODULE: ./src/icons/warning-sign.svg var warning_sign_path, warning_sign_path2, warning_sign_path3; @@ -48713,7 +48713,7 @@ function SvgWarningSign(_ref, svgRef) { }))); } var warning_sign_ForwardRef = /*#__PURE__*/react.forwardRef(SvgWarningSign); -/* harmony default export */ var warning_sign = (__webpack_require__.p + "static/media/warning-sign.svg?f=warning-sign.fb9ebdb743ce95ba858869fcc9772754.svg"); +/* harmony default export */ var warning_sign = (__webpack_require__.p + "static/media/warning-sign.svg?h=6512b04406700d29462a623eb41ae95760073332a6f28960317702dd61d21144"); ;// CONCATENATED MODULE: ./src/icons/changes.svg var changes_path, changes_path2, changes_path3; @@ -48750,7 +48750,7 @@ function SvgChanges(_ref, svgRef) { }))); } var changes_ForwardRef = /*#__PURE__*/react.forwardRef(SvgChanges); -/* harmony default export */ var changes = (__webpack_require__.p + "static/media/changes.svg?f=changes.b1e048d4a195559cfe9376069cd93bea.svg"); +/* harmony default export */ var changes = (__webpack_require__.p + "static/media/changes.svg?h=8a3b67f711bfd0e1b818331d3204b053b1d8225655238c5e585e5d8fbfbb70d0"); ;// CONCATENATED MODULE: ./src/icons/star.svg var star_path, star_path2; @@ -48783,7 +48783,7 @@ function SvgStar(_ref, svgRef) { }))); } var star_ForwardRef = /*#__PURE__*/react.forwardRef(SvgStar); -/* harmony default export */ var star = (__webpack_require__.p + "static/media/star.svg?f=star.0c433d1c6ccb36781955621fc40f0540.svg"); +/* harmony default export */ var star = (__webpack_require__.p + "static/media/star.svg?h=2301326ebc4d30ade3aee8dc4d89c3cdeb89fa6fe5a86f088d6437b193a02f7e"); ;// CONCATENATED MODULE: ./src/icons/triangle-down.svg var triangle_down_path, triangle_down_path2; @@ -48816,7 +48816,7 @@ function SvgTriangleDown(_ref, svgRef) { }))); } var triangle_down_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleDown); -/* harmony default export */ var triangle_down = (__webpack_require__.p + "static/media/triangle-down.svg?f=triangle-down.cb859bf44378ddfd76730eb4a9f90a89.svg"); +/* harmony default export */ var triangle_down = (__webpack_require__.p + "static/media/triangle-down.svg?h=b3825504f7a87e2008f015a505eae34a4d4b3de056523b32aef0f3ee97dddc48"); ;// CONCATENATED MODULE: ./src/icons/triangle-left-light.svg var triangle_left_light_path, triangle_left_light_path2; @@ -48850,7 +48850,7 @@ function SvgTriangleLeftLight(_ref, svgRef) { }))); } var triangle_left_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleLeftLight); -/* harmony default export */ var triangle_left_light = (__webpack_require__.p + "static/media/triangle-left-light.svg?f=triangle-left-light.723aa352c7195ed6c2c94748badb018c.svg"); +/* harmony default export */ var triangle_left_light = (__webpack_require__.p + "static/media/triangle-left-light.svg?h=df35ecb761287084bb4d0cbc4ffbfe1fc2a293d207e6290abd825463f189aba0"); ;// CONCATENATED MODULE: ./src/icons/triangle-right-light.svg var triangle_right_light_path, triangle_right_light_path2; @@ -48883,7 +48883,7 @@ function SvgTriangleRightLight(_ref, svgRef) { }))); } var triangle_right_light_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleRightLight); -/* harmony default export */ var triangle_right_light = (__webpack_require__.p + "static/media/triangle-right-light.svg?f=triangle-right-light.22d8903e553c49bf9b0100b61fe7254f.svg"); +/* harmony default export */ var triangle_right_light = (__webpack_require__.p + "static/media/triangle-right-light.svg?h=669c6e13b5b122615e60e89ab2b01dc4073f13c891c0affdc26c7c92c7e187d9"); ;// CONCATENATED MODULE: ./src/icons/triangle-right.svg var triangle_right_path, triangle_right_path2; @@ -48917,7 +48917,7 @@ function SvgTriangleRight(_ref, svgRef) { }))); } var triangle_right_ForwardRef = /*#__PURE__*/react.forwardRef(SvgTriangleRight); -/* harmony default export */ var triangle_right = (__webpack_require__.p + "static/media/triangle-right.svg?f=triangle-right.795bcf3f22906edc57cecb48328c9f89.svg"); +/* harmony default export */ var triangle_right = (__webpack_require__.p + "static/media/triangle-right.svg?h=4e2adfe1227483c7faec85cc2dd2c47b720b45484b561af3f1dc9cb71a0c76e3"); ;// CONCATENATED MODULE: ./src/icons/brackets-curly.svg var brackets_curly_path; @@ -48946,7 +48946,7 @@ function SvgBracketsCurly(_ref, svgRef) { }))); } var brackets_curly_ForwardRef = /*#__PURE__*/react.forwardRef(SvgBracketsCurly); -/* harmony default export */ var brackets_curly = (__webpack_require__.p + "static/media/brackets-curly.svg?f=brackets-curly.99e04241d2e10a7cfb60d2c695401adc.svg"); +/* harmony default export */ var brackets_curly = (__webpack_require__.p + "static/media/brackets-curly.svg?h=52ca1e6781ac563ec8694c3e48173bed7f18c48f39bdcf5c35418cb4f0db5231"); ;// CONCATENATED MODULE: ./src/icons/brackets-square.svg var brackets_square_path; @@ -48975,7 +48975,7 @@ function SvgBracketsSquare(_ref, svgRef) { }))); } var brackets_square_ForwardRef = /*#__PURE__*/react.forwardRef(SvgBracketsSquare); -/* harmony default export */ var brackets_square = (__webpack_require__.p + "static/media/brackets-square.svg?f=brackets-square.3daecb55d1cd657d5682c02cfe8563d5.svg"); +/* harmony default export */ var brackets_square = (__webpack_require__.p + "static/media/brackets-square.svg?h=2cb41787d8487f94387e48d5d2df0c2d1476a426e7db05bfd18c9ca83b1c3203"); ;// CONCATENATED MODULE: ./src/icons/file.svg var file_path, file_path2, file_path3; @@ -49011,7 +49011,7 @@ function SvgFile(_ref, svgRef) { }))); } var file_ForwardRef = /*#__PURE__*/react.forwardRef(SvgFile); -/* harmony default export */ var file = (__webpack_require__.p + "static/media/file.svg?f=file.f2670ef3fe4fae6122bb957ea2390db0.svg"); +/* harmony default export */ var file = (__webpack_require__.p + "static/media/file.svg?h=a5cdec22db8a9870f965606ebe636175c096705f5230dc3ef5fb8c69e8481e52"); ;// CONCATENATED MODULE: ./src/icons/result.svg var result_circle, result_path, result_g; @@ -49063,7 +49063,7 @@ function SvgResult(_ref, svgRef) { })))); } var result_ForwardRef = /*#__PURE__*/(/* unused pure expression or super */ null && (React.forwardRef(SvgResult))); -/* harmony default export */ var result = (__webpack_require__.p + "static/media/result.svg?f=result.cc7a9a9173a072950aee17843ff145ba.svg"); +/* harmony default export */ var result = (__webpack_require__.p + "static/media/result.svg?h=109429f6ac0c3258b03a2931285d539bbe05c832b2ffc74bfc4e0186ed415c4e"); ;// CONCATENATED MODULE: ./src/icons/include.svg var include_circle, include_path, include_path2, include_path3, include_path4; @@ -49125,7 +49125,7 @@ function SvgInclude(_ref, svgRef) { }))); } var include_ForwardRef = /*#__PURE__*/react.forwardRef(SvgInclude); -/* harmony default export */ var include = (__webpack_require__.p + "static/media/include.svg?f=include.6bf4795a2f5623b297789116effd0bec.svg"); +/* harmony default export */ var include = (__webpack_require__.p + "static/media/include.svg?h=09e12f819cefe6871ba002f11807c529f4a7c11f4ec84c1a15945fe9a267d26d"); ;// CONCATENATED MODULE: ./src/icons/Icons.tsx var KluctlText=function KluctlText(){return/*#__PURE__*/(0,jsx_runtime.jsx)(ForwardRef,{width:"115px",height:"33px"});};var GitIcon=function GitIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(git_ForwardRef,{width:"35px",height:"35px"});};var KluctlLogo=function KluctlLogo(){return/*#__PURE__*/(0,jsx_runtime.jsx)(kluctl_logo_ForwardRef,{width:"50px",height:"50px"});};var SearchIcon=function SearchIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(search_ForwardRef,{width:"27px",height:"27px"});};var TargetsIcon=function TargetsIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(targets_ForwardRef,{width:"48px",height:"48px"});};var TargetIcon=function TargetIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(target_ForwardRef,{width:"45px",height:"45px"});};var RelationHLine=function RelationHLine(){return/*#__PURE__*/_jsx(RelationHLineSvg,{width:"169px",height:"12px"});};var ProjectIcon=function ProjectIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(project_ForwardRef,{width:"45px",height:"45px"});};var DeployIcon=function DeployIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(deploy_ForwardRef,{width:"45px",height:"45px"});};var PruneIcon=function PruneIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(prune_ForwardRef,{width:"45px",height:"45px"});};var DiffIcon=function DiffIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(diff_ForwardRef,{width:"45px",height:"45px"});};var CpuIcon=function CpuIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(cpu_ForwardRef,{width:"24px",height:"24px"});};var FingerScanIcon=function FingerScanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(finger_scan_ForwardRef,{width:"24px",height:"24px"});};var MessageQuestionIcon=function MessageQuestionIcon(props){return/*#__PURE__*/(0,jsx_runtime.jsx)("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:/*#__PURE__*/(0,jsx_runtime.jsx)("path",{d:"M17 2.42999H7C4 2.42999 2 4.42999 2 7.42999V13.43C2 16.43 4 18.43 7 18.43V20.56C7 21.36 7.89 21.84 8.55 21.39L13 18.43H17C20 18.43 22 16.43 22 13.43V7.42999C22 4.42999 20 2.42999 17 2.42999ZM12 14.6C11.58 14.6 11.25 14.26 11.25 13.85C11.25 13.44 11.58 13.1 12 13.1C12.42 13.1 12.75 13.44 12.75 13.85C12.75 14.26 12.42 14.6 12 14.6ZM13.26 10.45C12.87 10.71 12.75 10.88 12.75 11.16V11.37C12.75 11.78 12.41 12.12 12 12.12C11.59 12.12 11.25 11.78 11.25 11.37V11.16C11.25 9.99999 12.1 9.42999 12.42 9.20999C12.79 8.95999 12.91 8.78999 12.91 8.52999C12.91 8.02999 12.5 7.61999 12 7.61999C11.5 7.61999 11.09 8.02999 11.09 8.52999C11.09 8.93999 10.75 9.27999 10.34 9.27999C9.93 9.27999 9.59 8.93999 9.59 8.52999C9.59 7.19999 10.67 6.11999 12 6.11999C13.33 6.11999 14.41 7.19999 14.41 8.52999C14.41 9.66999 13.57 10.24 13.26 10.45Z",fill:props.color})});};var TreeViewIcon=function TreeViewIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(tree_view_ForwardRef,{width:"26px",height:"26px"});};var CloseIcon=function CloseIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(close_ForwardRef,{width:"24px",height:"24px"});};var CloseLightIcon=function CloseLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(close_light_ForwardRef,{width:"24px",height:"24px"});};var WarningIcon=function WarningIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_ForwardRef,{width:"24px",height:"24px"});};var ErrorIcon=function ErrorIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(error_ForwardRef,{width:"24px",height:"24px"});};var TrashIcon=function TrashIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(trash_ForwardRef,{width:"24px",height:"24px"});};var OrphanIcon=function OrphanIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(orphan_ForwardRef,{width:"24px",height:"24px"});};var AddedIcon=function AddedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(added_ForwardRef,{width:"24px",height:"24px"});};var ChangedIcon=function ChangedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changed_ForwardRef,{width:"24px",height:"24px"});};var CheckboxIcon=function CheckboxIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_ForwardRef,{width:"24px",height:"24px"});};var CheckboxCheckedIcon=function CheckboxCheckedIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(checkbox_checked_ForwardRef,{width:"24px",height:"24px"});};var CheckboxDisabledIcon=function CheckboxDisabledIcon(){return/*#__PURE__*/_jsx(CheckboxDisabledIconSvg,{width:"24px",height:"24px"});};var ArrowLeftIcon=function ArrowLeftIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(arrow_left_ForwardRef,{width:"40px",height:"40px"});};var WarningSignIcon=function WarningSignIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(warning_sign_ForwardRef,{width:"21px",height:"21px"});};var ChangesIcon=function ChangesIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(changes_ForwardRef,{width:"21px",height:"21px"});};var StarIcon=function StarIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(star_ForwardRef,{width:"21px",height:"21px"});};var TriangleDownIcon=function TriangleDownIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_down_ForwardRef,{width:"50px",height:"50px"});};var TriangleLeftLightIcon=function TriangleLeftLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_left_light_ForwardRef,{width:"50px",height:"50px"});};var TriangleRightLightIcon=function TriangleRightLightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_right_light_ForwardRef,{width:"50px",height:"50px"});};var TriangleRightIcon=function TriangleRightIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(triangle_right_ForwardRef,{width:"50px",height:"50px"});};var BracketsCurlyIcon=function BracketsCurlyIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_curly_ForwardRef,{width:"22px",height:"18px"});};var BracketsSquareIcon=function BracketsSquareIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(brackets_square_ForwardRef,{width:"22px",height:"18px"});};var FileIcon=function FileIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(file_ForwardRef,{width:"40px",height:"40px"});};var ResultIcon=function ResultIcon(){return/*#__PURE__*/_jsx(ResultIconSvg,{width:"30px",height:"30px"});};var IncludeIcon=function IncludeIcon(){return/*#__PURE__*/(0,jsx_runtime.jsx)(include_ForwardRef,{width:"30px",height:"30px"});}; @@ -61549,4 +61549,4 @@ src_reportWebVitals(); }(); /******/ })() ; -//# sourceMappingURL=main.45a7295d.js.map \ No newline at end of file +//# sourceMappingURL=main.js.map?h=1b699a9c9a029bfaa14bdc9c1ea688196c3b803b93226977d03fa0803d4391b3 \ No newline at end of file diff --git a/pkg/webui/ui/fix-assets/main.go b/pkg/webui/ui/fix-assets/main.go index 76132b6a9..722e6e416 100644 --- a/pkg/webui/ui/fix-assets/main.go +++ b/pkg/webui/ui/fix-assets/main.go @@ -1,13 +1,19 @@ package main import ( + "bytes" + "crypto/sha256" + "encoding/hex" "encoding/json" + "fmt" "io/fs" "k8s.io/apimachinery/pkg/util/rand" + "mime" "os" "path" "path/filepath" "regexp" + "sort" "strings" ) @@ -37,8 +43,22 @@ func main() { hashRegex := regexp.MustCompile(`([a-z-0-9-_]*)(\.[a-f0-9]*)(\.chunk)?\.(js|css)(\.map)?`) + var sortedByLen []string + for p1 := range manifest.Files { + sortedByLen = append(sortedByLen, p1) + } + sort.Slice(sortedByLen, func(i, j int) bool { + l1 := len(sortedByLen[i]) + l2 := len(sortedByLen[j]) + if l1 != l2 { + return l2 < l1 + } + return sortedByLen[i] < sortedByLen[j] + }) + newEntries := map[string]string{} - for p1, p2 := range manifest.Files { + for _, p1 := range sortedByLen { + p2 := manifest.Files[p1] oldName := path.Base(p2) newName := path.Base(p1) oldPath := path.Join(path.Dir(p2), oldName) @@ -59,10 +79,11 @@ func main() { } // we assume that .map files are implicitly replaced by the non-.map versions - if !strings.HasSuffix(oldName, ".map") { - err = replaceInFiles(".", oldName, newName+"?f="+oldName) - doError(err) - } + //if !strings.HasSuffix(oldName, ".map") { + contentHash := calcContentHash(oldPath) + err = replaceInFiles(".", oldName, newName+"?h="+contentHash) + doError(err) + //} err = os.Rename(oldPath, newPath) doError(err) @@ -83,6 +104,38 @@ func main() { doError(err) } +func calcContentHash(p string) string { + b, err := os.ReadFile(p) + if err != nil { + panic(err) + } + + if isText(p) { + // replace CR LF \r\n (windows) with LF \n (unix) + b = bytes.ReplaceAll(b, []byte{13, 10}, []byte{10}) + // replace CF \r (mac) with LF \n (unix) + b = bytes.ReplaceAll(b, []byte{13}, []byte{10}) + } + + s := sha256.Sum256(b) + ret := hex.EncodeToString(s[:]) + + os.Stderr.WriteString(fmt.Sprintf("%s: %s\n", p, ret)) + + return ret +} + +func isText(p string) bool { + mt := mime.TypeByExtension(path.Ext(p)) + if strings.HasPrefix(mt, "text/") { + return true + } + if strings.Index(mt, "+xml") != -1 { + return true + } + return false +} + func replaceInFiles(dir string, s string, r string) error { return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { if d.IsDir() { From 45bfeb66e2483c400696bbdb84cc4958aece98d6 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 17 Jun 2023 00:34:18 +0200 Subject: [PATCH 1209/2268] ci: Add workflow that creates PR comments for preview-runs --- .github/workflows/preview-comment.yml | 73 +++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/workflows/preview-comment.yml diff --git a/.github/workflows/preview-comment.yml b/.github/workflows/preview-comment.yml new file mode 100644 index 000000000..1fd73b736 --- /dev/null +++ b/.github/workflows/preview-comment.yml @@ -0,0 +1,73 @@ +name: preview-comment + +on: + workflow_run: + workflows: + - preview-run + types: + - requested + +permissions: + contents: read + pull-requests: write + +jobs: + preview-comment: + runs-on: ubuntu-latest + steps: + - name: 'Download artifact' + uses: actions/github-script@v6 + with: + script: | + let matchArtifact = undefined + while (true) { + console.log("do listWorkflowRunArtifacts") + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + matchArtifact = allArtifacts.data.artifacts.find((artifact) => { + return artifact.name == "preview-run-info" + }); + if (matchArtifact) { + console.log("found artifact " + matchArtifact.id) + break + } + console.log("artifact not found, retrying") + await new Promise(r => setTimeout(r, 5000)); + } + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-run-info.zip`, Buffer.from(download.data)); + - name: 'Unzip artifact' + run: unzip preview-run-info.zip + - run: | + ls -lah + find + - name: Build comment text + run: | + cat < comment.md + # 🤖 Preview Environment is starting up and will be reachable soon. + ipfs-id: `$(cat ipfs_id)` + + # ipfs forward commands + http: `ipfs p2p forward /x/kluctl-webui /ip4/127.0.0.1/tcp/9090 /p2p/$(cat ipfs_id)` + ssh: `ipfs p2p forward /x/ssh /ip4/127.0.0.1/tcp/9090 /p2p/$(cat ipfs_id)` + k8s: `ipfs p2p forward /x/k /ip4/127.0.0.1/tcp/9090 /p2p/$(cat ipfs_id)` + + # kubeconfig + ``` + $(cat kubeconfig) + ``` + EOF + - uses: mshick/add-pr-comment@v2 + with: + message-id: preview-run-info + message-path: | + comment.md From 6cb0e6cd650f51c7c91a8b662682683aa7a69b52 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 16 Jun 2023 10:13:33 +0200 Subject: [PATCH 1210/2268] chore: Cleanup commented out code --- pkg/webui/ui/fix-assets/main.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/webui/ui/fix-assets/main.go b/pkg/webui/ui/fix-assets/main.go index 722e6e416..6941a3a26 100644 --- a/pkg/webui/ui/fix-assets/main.go +++ b/pkg/webui/ui/fix-assets/main.go @@ -78,12 +78,9 @@ func main() { manifest.Files[p1] = newPath } - // we assume that .map files are implicitly replaced by the non-.map versions - //if !strings.HasSuffix(oldName, ".map") { contentHash := calcContentHash(oldPath) err = replaceInFiles(".", oldName, newName+"?h="+contentHash) doError(err) - //} err = os.Rename(oldPath, newPath) doError(err) From a0652f6c666f0dfff08bd7e0eea7e84c0f53f3f3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 16 Jun 2023 14:45:45 +0200 Subject: [PATCH 1211/2268] feat: Allow to pass Kluctl image to deployments --- install/controller/.kluctl.yaml | 2 ++ install/controller/controller/kustomization.yaml | 3 ++- install/webui/.kluctl.yaml | 2 ++ install/webui/webui/deployment.yaml | 3 ++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/controller/.kluctl.yaml b/install/controller/.kluctl.yaml index f85272bec..778f36af8 100644 --- a/install/controller/.kluctl.yaml +++ b/install/controller/.kluctl.yaml @@ -1,6 +1,8 @@ discriminator: kluctl.io-controller args: + - name: kluctl_image + default: ghcr.io/kluctl/kluctl - name: kluctl_version default: v2.20.4 - name: controller_args diff --git a/install/controller/controller/kustomization.yaml b/install/controller/controller/kustomization.yaml index a7551b296..b18e9fed5 100644 --- a/install/controller/controller/kustomization.yaml +++ b/install/controller/controller/kustomization.yaml @@ -1,3 +1,4 @@ +{% set kluctl_image = get_var("args.kluctl_image", "ghcr.io/kluctl/kluctl") %} # TODO remove controller_version {% set kluctl_version = get_var(["args.kluctl_version", "args.controller_version"], "v2.20.4") %} {% set pull_policy = "Always" if ("-devel" in kluctl_version or "-snapshot" in kluctl_version) else "IfNotPresent" %} @@ -14,7 +15,7 @@ patches: patch: |- - op: add path: /spec/template/spec/containers/0/image - value: ghcr.io/kluctl/kluctl:{{ kluctl_version }} + value: {{ kluctl_image }}:{{ kluctl_version }} - target: kind: Deployment name: kluctl-controller diff --git a/install/webui/.kluctl.yaml b/install/webui/.kluctl.yaml index 961fb60c4..b52dcb538 100644 --- a/install/webui/.kluctl.yaml +++ b/install/webui/.kluctl.yaml @@ -1,6 +1,8 @@ discriminator: kluctl.io-webui args: + - name: kluctl_image + default: ghcr.io/kluctl/kluctl - name: kluctl_version default: v2.20.4 - name: webui_args diff --git a/install/webui/webui/deployment.yaml b/install/webui/webui/deployment.yaml index d832b2ef4..9cff9cff5 100644 --- a/install/webui/webui/deployment.yaml +++ b/install/webui/webui/deployment.yaml @@ -1,3 +1,4 @@ +{% set kluctl_image = get_var("args.kluctl_image", "ghcr.io/kluctl/kluctl") %} {% set kluctl_version = get_var("args.kluctl_version", "v2.20.4") %} {% set pull_policy = "Always" if ("-devel" in kluctl_version or "-snapshot" in kluctl_version) else "IfNotPresent" %} @@ -28,7 +29,7 @@ spec: spec: containers: - name: webui - image: ghcr.io/kluctl/kluctl:{{ kluctl_version }} + image: {{ kluctl_image }}:{{ kluctl_version }} imagePullPolicy: {{ pull_policy }} command: - kluctl From 553893a7500751e70b1b45273e7786f984baf96f Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 16 Jun 2023 10:14:03 +0200 Subject: [PATCH 1212/2268] fix: Make goreleaser, Dockerfile and Makefile all use the same binary name --- .goreleaser.yaml | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index c8ebae4c9..3f829010a 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -3,7 +3,7 @@ before: - go mod tidy builds: - <<: &build_defaults - binary: kluctl + binary: bin/kluctl env: - CGO_ENABLED=0 main: ./cmd diff --git a/Dockerfile b/Dockerfile index 91818c999..3515958cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN apk add git # Ensure helm is not trying to access / ENV HELM_CACHE_HOME=/tmp/helm-cache -ARG BIN_PATH=kluctl +ARG BIN_PATH=bin/kluctl COPY $BIN_PATH /usr/bin/ USER 65532:65532 From 234fd06ad7468b6d87dc4ba570fa87252e0e943c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 20 Jun 2023 15:36:49 +0200 Subject: [PATCH 1213/2268] ci: Implement IPFS based webui previews --- .github/workflows/preview-comment.yml | 154 ++++++--- .github/workflows/preview-run.yml | 182 ++++++++++ hack/ipfs-exchange-info/go.mod | 64 ++++ hack/ipfs-exchange-info/go.sum | 285 ++++++++++++++++ hack/ipfs-exchange-info/main.go | 465 ++++++++++++++++++++++++++ 5 files changed, 1101 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/preview-run.yml create mode 100644 hack/ipfs-exchange-info/go.mod create mode 100644 hack/ipfs-exchange-info/go.sum create mode 100644 hack/ipfs-exchange-info/main.go diff --git a/.github/workflows/preview-comment.yml b/.github/workflows/preview-comment.yml index 1fd73b736..3128a5f24 100644 --- a/.github/workflows/preview-comment.yml +++ b/.github/workflows/preview-comment.yml @@ -1,73 +1,129 @@ name: preview-comment on: - workflow_run: - workflows: - - preview-run - types: - - requested + pull_request_target: + types: [labeled, synchronize] + branches: + - main permissions: contents: read + issues: write pull-requests: write +concurrency: preview-comment + jobs: preview-comment: + if: ${{ github.event.label.name == 'preview' || contains(github.event.pull_request.labels.*.name, 'preview') }} runs-on: ubuntu-latest steps: - - name: 'Download artifact' - uses: actions/github-script@v6 + - name: Reset PR comment + uses: mshick/add-pr-comment@v2 + with: + issue: ${{ github.pull_request.number }} + message-id: preview-run-info + message: | + # 🤖 Preview Environment + The preview environment is starting up and will be available soon. + - uses: actions/checkout@v2 with: - script: | - let matchArtifact = undefined - while (true) { - console.log("do listWorkflowRunArtifacts") - let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id, - }); - matchArtifact = allArtifacts.data.artifacts.find((artifact) => { - return artifact.name == "preview-run-info" - }); - if (matchArtifact) { - console.log("found artifact " + matchArtifact.id) - break - } - console.log("artifact not found, retrying") - await new Promise(r => setTimeout(r, 5000)); - } - let download = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - let fs = require('fs'); - fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-run-info.zip`, Buffer.from(download.data)); - - name: 'Unzip artifact' - run: unzip preview-run-info.zip - - run: | - ls -lah - find + # Warning, do not try to checkout the base branch here as it would introduce enormous security risks! + # Also don't introduce a cache in this workflow! + ref: main + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.19 + - name: Install some tools + run: | + sudo apt update + sudo apt install -y ncat + wget https://github.com/FiloSottile/age/releases/download/v1.1.1/age-v1.1.1-linux-amd64.tar.gz + tar xzf age-v1.1.1-linux-amd64.tar.gz + sudo mv age/age* /usr/bin/ + - name: Install ipfs + run: | + wget -q https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz + tar -xvzf kubo_v0.20.0_linux-amd64.tar.gz + ./kubo/install.sh + - name: ipfs init + run: | + ipfs init + ipfs config --json Experimental.Libp2pStreamMounting true + ipfs config --json Ipns.UsePubsub true + ipfs daemon & + sleep 1 + while ! ipfs id &> /dev/null; do + echo "waiting for the daemon" + sleep 1 + done + - name: Import ipfs key + run: | + echo "${{ secrets.KLUCTL_PREVIEW_IPNS_KEY }}" | base64 -d > kluctl-preview-comment.pem + ipfs key import kluctl-preview-comment kluctl-preview-comment.pem -f pem-pkcs8-cleartext + - name: Build ipfs-exchange-info + run: | + (cd ./hack/ipfs-exchange-info && go build ./) + - name: Publish own ID + run: | + ./hack/ipfs-exchange-info/ipfs-exchange-info -mode publish-ipns --ipns-key kluctl-preview-comment -pr-number ${{ github.event.pull_request.number }} + - name: Receive info + id: info + run: | + echo "${{ secrets.KLUCTL_PREVIEW_AGE_KEY }}" > age.key + while ! ./hack/ipfs-exchange-info/ipfs-exchange-info -mode receive-info \ + --age-key-file age.key --repo-name "${{ github.repository }}" -pr-number ${{ github.event.pull_request.number }} > info.json; do + sleep 1 + done + + cat info.json | jq ".ipfsId" -r > ipfs_id + cat info.json | jq ".staticIpnsName" -r > static_ipns_name + + echo "ipfs_id=$(cat ipfs_id)" + echo "static_ipns_name=$(cat static_ipns_name)" - name: Build comment text run: | cat < comment.md - # 🤖 Preview Environment is starting up and will be reachable soon. - ipfs-id: `$(cat ipfs_id)` + # 🤖 Preview Environment + The preview environment is running. + + # Accessing the static preview UI + This version of the UI is static but updated regularly. The updates will lag quite a lot, as IPFS needs + some time to propagate changes. You'd also need to refresh the page to get an update. This version of the UI + is not able to interact with the cluster, due to missing WebSockets support in IPFS. Use IPFS p2p forwarding + if you need this. + + The public gateway url is: https://ipfs.io/ipns/$(cat static_ipns_name)/index.html - # ipfs forward commands - http: `ipfs p2p forward /x/kluctl-webui /ip4/127.0.0.1/tcp/9090 /p2p/$(cat ipfs_id)` - ssh: `ipfs p2p forward /x/ssh /ip4/127.0.0.1/tcp/9090 /p2p/$(cat ipfs_id)` - k8s: `ipfs p2p forward /x/k /ip4/127.0.0.1/tcp/9090 /p2p/$(cat ipfs_id)` + For much better perforamance and update-speed, install and start [IPFS Kubo](https://github.com/ipfs/kubo/) + locally and use this URL: http://$(cat static_ipns_name).ipns.localhost:8080/index.html - # kubeconfig - ``` - $(cat kubeconfig) - ``` + If you configured a different gateway port then the default (8080), fix the url accordingly. + + # Accessing the dynamic preview UI + First, install and start [IPFS Kubo](https://github.com/ipfs/kubo/) locally. + Then run the \`http\` forward command from below. + After that, the UI will be accessible through http://localhost:9090. + + IPFS forward commands: + http: \`ipfs p2p forward /x/kluctl-webui /ip4/127.0.0.1/tcp/9090 /p2p/$(cat ipfs_id)\` + ssh: \`ipfs p2p forward /x/ssh /ip4/127.0.0.1/tcp/2222 /p2p/$(cat ipfs_id)\` + k8s: \`ipfs p2p forward /x/k /ip4/127.0.0.1/tcp/6443 /p2p/$(cat ipfs_id)\` + + When asked for credentials, use admin:admin. + + # Shutting it down + Either cancel the pipeline in the PR or simply wait for 30 minutes until it auto-terminates. EOF - uses: mshick/add-pr-comment@v2 with: + issue: ${{ github.pull_request.number }} message-id: preview-run-info message-path: | comment.md + - name: Sleep a bit + run: | + # Sleep a few seconds to give IPFS p2p streams enough time to flush + sleep 10 \ No newline at end of file diff --git a/.github/workflows/preview-run.yml b/.github/workflows/preview-run.yml new file mode 100644 index 000000000..627328776 --- /dev/null +++ b/.github/workflows/preview-run.yml @@ -0,0 +1,182 @@ +name: preview-run + +on: + pull_request: + types: [labeled, synchronize] + branches: + - main + +concurrency: + group: preview-run + cancel-in-progress: true + +jobs: + preview-run: + if: ${{ github.event.label.name == 'preview' || contains(github.event.pull_request.labels.*.name, 'preview') }} + runs-on: ubuntu-latest + steps: + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.19 + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-preview-build-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-preview-build- + - name: Install some tools + run: | + sudo apt update + sudo apt install -y ncat + wget https://github.com/FiloSottile/age/releases/download/v1.1.1/age-v1.1.1-linux-amd64.tar.gz + tar xzf age-v1.1.1-linux-amd64.tar.gz + sudo mv age/age* /usr/bin/ + # SETUP IPFS + - name: Install ipfs + run: | + wget -q https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz + tar -xvzf kubo_v0.20.0_linux-amd64.tar.gz + ./kubo/install.sh + - name: Setup ipfs + run: | + ipfs init + ipfs config --json Experimental.Libp2pStreamMounting true + ipfs config --json Ipns.UsePubsub true + ipfs daemon --enable-pubsub-experiment & + sleep 1 + while ! ipfs id &> /dev/null; do + echo "waiting for the daemon" + sleep 1 + done + # kick of ipns resolving in the background to warm it up + ipfs name resolve k51qzi5uqu5diql7mejg4dfn35igqqf22202vgv3sepjrsobtya2xihqqtshbe & + - name: Setup ssh + run: | + mkdir -p ~/.ssh + echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCtWJls6XlpszR5zMjiK7cdUnSBI/p7tWaEHykJZrlwHRepNIckPk4ftOsOfiLb+2K/TntPGa0NMWM0uccRNXJ1/hgT5uiA8MpR8d1SGV5QtVwzJkDTXN8iwcTu1zcIUoL2FvQUm/P4hHI4BdcS9GyokOnqh296RRtajnzWZlGtBHRMPt9S7eil9kl5sOuHQIsZHjYkqLb7PSyVWeeMzEEeI28L2ZDrfBBgBNiE4ibVBlRbUeArRO5coV2Vn9uafzOIXT13apo0bhacv5FEmmsEcDGelZWKVInoUQHDnsr7UQPDHS2OsdtZRCluvRYH5ZC4SvrDeuZe4AKjc8iDeNuZLlzn7cgwGZDNHJ1PwAWwEz4/yF0vshA7mfrXLhjJ4+vN4enlQqDYqvudJ3x4uKO67panc+Gmaq76mxh81bJHNnlothEs9K9WfGcXAlBBjuk/0kmIf6I1ICA/dxCKa0sAMbolZHoBuoVUszQdlVrDkwPmzNCenBX/MPDJl08FJFc= ablock@Alexanders-MacBook-Pro.local" >> ~/.ssh/authorized_keys + sudo systemctl enable ssh + sudo systemctl start ssh + ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 + + ssh-keygen -f scp_key -P "" + cat scp_key.pub >> ~/.ssh/authorized_keys + # SETUP KIND + - name: Setup Kind cluster + run: | + cat < kind-config.yaml + kind: Cluster + apiVersion: kind.x-k8s.io/v1alpha4 + networking: + apiServerAddress: "127.0.0.1" + apiServerPort: 6443 + nodes: + - role: control-plane + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP + EOF + curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64 + kind create cluster --config kind-config.yaml + - name: Kind info + run: | + kubectl cluster-info + # BUILD + - name: Setup Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v2 + - name: Build binary + run: | + go build -o bin/kluctl cmd/main.go + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + push: false + load: true + tags: kluctl:preview + # LOAD IMAGE + - name: Load image to Kind + run: | + kind load docker-image kluctl:preview + # INSTALL + - name: Install Kluctl Controller + run: | + cd install/controller + ../../bin/kluctl deploy --yes -a kluctl_image=kluctl -a kluctl_version=preview + - name: Install Kluctl Webui + run: | + cd install/webui + ../../bin/kluctl deploy --yes -a kluctl_image=kluctl -a kluctl_version=preview + kubectl -n kluctl-system create secret generic admin-secret --from-literal adminPassword=admin --from-literal secret=dummy + - name: Run kubectl port-forward + run: | + kubectl -n kluctl-system port-forward svc/kluctl-webui 9090:8080 & + - name: Listen ipfs/p2p + run: | + ipfs p2p listen /x/k /ip4/127.0.0.1/tcp/6443 + ipfs p2p listen /x/kluctl-webui /ip4/127.0.0.1/tcp/9090 + # SEND INFO + - name: Build ipfs-exchange-info + run: | + (cd ./hack/ipfs-exchange-info && go build ./) + - name: send info + run: | + static_ipns_name=$(ipfs key gen static-webui) + while true; do + if ! ./hack/ipfs-exchange-info/ipfs-exchange-info -mode resolve-ipns -pr-number ${{ github.event.pull_request.number }} -ipns-name k51qzi5uqu5diql7mejg4dfn35igqqf22202vgv3sepjrsobtya2xihqqtshbe > ipfs_id; then + echo "Resolving IPNS failed...retrying..." + sleep 1 + continue + fi + echo "ipfs_id=$(cat ipfs_id)" + if ! ./hack/ipfs-exchange-info/ipfs-exchange-info -mode send-info -pr-number ${{ github.event.pull_request.number }} -age-pub-key age1dhueesr5qj8e8uy298k7z8x3ntv620rde89phumg0kvjx2t32elsm759z7 --ipfs-id $(cat ipfs_id) --static-ipns-name $static_ipns_name; then + echo "Resolving info failed...retrying..." + sleep 1 + continue + fi + break + done + env: + # we send the GITHUB_TOKEN to the preview-comment workflow. This token is then verified to be from a workflow + # that runs inside this repo, which can only be a workflow that got manually approved. The token is encrypted + # via age + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # GENERATE STATIC WEBUI + - name: Publish static webui and sleep + run: | + ( + old_cid="" + while true; do + rm -rf kluctl-webui-static + if ./bin/kluctl webui --static-path=kluctl-webui-static; then + cid=$(ipfs add -r -Q kluctl-webui-static) + if [ "$cid" != "$old_cid" ]; then + echo "Publishing $cid..." + ipfs name publish --key static-webui /ipfs/$cid || true + old_cid="$cid" + fi + else + echo "Building static webui failed" + fi + sleep 1 + done + ) & + echo "Sleeping..." + sleep 1800 diff --git a/hack/ipfs-exchange-info/go.mod b/hack/ipfs-exchange-info/go.mod new file mode 100644 index 000000000..1a78cc690 --- /dev/null +++ b/hack/ipfs-exchange-info/go.mod @@ -0,0 +1,64 @@ +module github.com/kluctl/kluctl/hack/ipfs-exchange-info + +go 1.20 + +require ( + filippo.io/age v1.1.1 + github.com/ipfs/boxo v0.10.1 + github.com/ipfs/kubo v0.20.1-0.20230619130733-8bba03d8bf46 + github.com/sirupsen/logrus v1.9.3 +) + +require ( + github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/ipfs/bbloom v0.0.4 // indirect + github.com/ipfs/go-block-format v0.1.2 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect + github.com/ipfs/go-datastore v0.6.0 // indirect + github.com/ipfs/go-ipfs-cmds v0.9.0 // indirect + github.com/ipfs/go-ipfs-util v0.0.3 // indirect + github.com/ipfs/go-ipld-format v0.5.0 // indirect + github.com/ipfs/go-ipld-legacy v0.2.1 // indirect + github.com/ipfs/go-log v1.0.5 // indirect + github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/ipfs/go-metrics-interface v0.0.1 // indirect + github.com/ipld/go-codec-dagpb v1.6.0 // indirect + github.com/ipld/go-ipld-prime v0.20.0 // indirect + github.com/jbenet/goprocess v0.1.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-libp2p v0.27.6 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.9.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-multistream v0.4.1 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/polydawn/refmt v0.89.0 // indirect + github.com/rs/cors v1.7.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.24.0 // indirect + golang.org/x/crypto v0.10.0 // indirect + golang.org/x/sys v0.9.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + lukechampine.com/blake3 v1.2.1 // indirect +) diff --git a/hack/ipfs-exchange-info/go.sum b/hack/ipfs-exchange-info/go.sum new file mode 100644 index 000000000..0dfefaac6 --- /dev/null +++ b/hack/ipfs-exchange-info/go.sum @@ -0,0 +1,285 @@ +filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= +filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= +github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= +github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= +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/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A= +github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/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-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= +github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= +github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= +github.com/ipfs/boxo v0.10.1 h1:q0ZhbyN6iNZLipd6txt1xotCiP/icfvdAQ4YpUi+cL4= +github.com/ipfs/boxo v0.10.1/go.mod h1:1qgKq45mPRCxf4ZPoJV2lnXxyxucigILMJOrQrVivv8= +github.com/ipfs/go-block-format v0.1.2 h1:GAjkfhVx1f4YTODS6Esrj1wt2HhrtwTnhEr+DyPUaJo= +github.com/ipfs/go-block-format v0.1.2/go.mod h1:mACVcrxarQKstUU3Yf/RdwbC4DzPV6++rO2a3d+a/KE= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= +github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= +github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= +github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= +github.com/ipfs/go-ipfs-cmds v0.9.0 h1:K0VcXg1l1k6aY6sHnoxYcyimyJQbcV1ueXuWgThmK9Q= +github.com/ipfs/go-ipfs-cmds v0.9.0/go.mod h1:SBFHK8WNwC416QWH9Vz1Ql42SSMAOqKpaHUMBu3jpLo= +github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= +github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= +github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= +github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= +github.com/ipfs/go-ipld-cbor v0.0.6 h1:pYuWHyvSpIsOOLw4Jy7NbBkCyzLDcl64Bf/LZW7eBQ0= +github.com/ipfs/go-ipld-format v0.5.0 h1:WyEle9K96MSrvr47zZHKKcDxJ/vlpET6PSiQsAFO+Ds= +github.com/ipfs/go-ipld-format v0.5.0/go.mod h1:ImdZqJQaEouMjCvqCe0ORUS+uoBmf7Hf+EO/jh+nk3M= +github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk= +github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM= +github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= +github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= +github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= +github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= +github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= +github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= +github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= +github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg= +github.com/ipfs/kubo v0.20.1-0.20230619130733-8bba03d8bf46 h1:slTRRjlz5fOCtMXRt6HXhEgxksV0XzWjgpgp4Z8iChU= +github.com/ipfs/kubo v0.20.1-0.20230619130733-8bba03d8bf46/go.mod h1:dxRoDwePol9YV+P7Q96LcN21jiOF3nF8xRC4ij72BIQ= +github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= +github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= +github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= +github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= +github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +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/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= +github.com/libp2p/go-libp2p v0.27.6 h1:KmGU5kskCaaerm53heqzfGOlrW2z8icZ+fnyqgrZs38= +github.com/libp2p/go-libp2p v0.27.6/go.mod h1:oMfQGTb9CHnrOuSM6yMmyK2lXz3qIhnkn2+oK3B1Y2g= +github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= +github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= +github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +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/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.9.0 h1:3h4V1LHIk5w4hJHekMKWALPXErDfz/sggzwC/NcqbDQ= +github.com/multiformats/go-multiaddr v0.9.0/go.mod h1:mI67Lb1EeTOYb8GQfL/7wpIZwc46ElrvzhYnoJOmTT0= +github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= +github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= +github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= +github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= +github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= +github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/warpfork/go-testmark v0.11.0 h1:J6LnV8KpceDvo7spaNU4+DauH2n1x+6RaO2rJrmpQ9U= +github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= +github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa h1:EyA027ZAkuaCLoxVX4r1TZMPy1d31fM6hbfQ4OU4I5o= +github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= +github.com/yuin/goldmark v1.1.27/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.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +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-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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +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.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +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-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +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-20201020160332-67f06af15bc9/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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/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-20210330210617-4fbd30eecc44/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +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= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +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/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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= +lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= diff --git a/hack/ipfs-exchange-info/main.go b/hack/ipfs-exchange-info/main.go new file mode 100644 index 000000000..f188087bc --- /dev/null +++ b/hack/ipfs-exchange-info/main.go @@ -0,0 +1,465 @@ +package main + +import ( + "bytes" + "context" + "encoding/gob" + "encoding/json" + "filippo.io/age" + "flag" + "fmt" + "github.com/ipfs/boxo/coreiface/options" + "github.com/ipfs/boxo/files" + log "github.com/sirupsen/logrus" + "io" + "net" + "net/http" + "os" + "strings" + "time" + + "github.com/ipfs/kubo/client/rpc" +) + +var modeFlag string +var ipnsKey string +var ipnsName string +var ipfsId string +var staticIpnsName string +var prNumber int +var ageKeyFile string +var agePubKey string +var repoName string + +func ParseFlags() error { + flag.StringVar(&modeFlag, "mode", "", "Mode") + flag.StringVar(&ipnsKey, "ipns-key", "", "IPNS key name") + flag.StringVar(&ipnsName, "ipns-name", "", "IPNS name") + flag.StringVar(&staticIpnsName, "static-ipns-name", "", "Static Webui IPNS name") + flag.IntVar(&prNumber, "pr-number", 0, "PR number") + flag.StringVar(&ipfsId, "ipfs-id", "", "IPFS id") + flag.StringVar(&ageKeyFile, "age-key-file", "", "AGE key file") + flag.StringVar(&agePubKey, "age-pub-key", "", "AGE pubkey") + flag.StringVar(&repoName, "repo-name", "", "Repo name") + flag.Parse() + + return nil +} + +func main() { + err := ParseFlags() + if err != nil { + panic(err) + } + + // "Connect" to local node + node, err := rpc.NewLocalApi() + if err != nil { + log.Error(err) + os.Exit(1) + } + + switch modeFlag { + case "publish-ipns": + err = doPublishIpns(node) + case "resolve-ipns": + err = doResolve(node) + case "send-info": + err = doSend(node) + case "receive-info": + err = doReceive(node) + default: + err = fmt.Errorf("unknown mode %s", modeFlag) + } + + if err != nil { + log.Error(err) + log.Exit(1) + } else { + log.Exit(0) + } +} + +type ipnsInfo struct { + PrNumber int `json:"prNumber"` + IpfsId string `json:"ipfsId"` +} + +type workflowInfo struct { + PrNumber int `json:"prNumber"` + IpfsId string `json:"ipfsId"` + StaticIpnsName string `json:"staticIpnsName"` + GithubToken string `json:"githubToken"` +} + +func doPublishIpns(node *rpc.HttpApi) error { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + + selfKey, err := node.Key().Self(ctx) + if err != nil { + return err + } + + info := ipnsInfo{ + PrNumber: prNumber, + IpfsId: selfKey.ID().String(), + } + b, err := json.Marshal(&info) + if err != nil { + return err + } + log.Info("publishing: ", string(b)) + + f := files.NewBytesFile(b) + + pth, err := node.Unixfs().Add(ctx, f) + if err != nil { + return err + } + + log.Info("path: ", pth.String()) + + ipnsEntry, err := node.Name().Publish(ctx, pth, options.Name.Key(ipnsKey), options.Name.TTL(10*time.Second)) + if err != nil { + return err + } + + log.Info("published as ", ipnsEntry.Name()) + + return nil +} + +func doResolve(node *rpc.HttpApi) error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + log.Info("Resolving: ", ipnsName) + + resolved, err := node.Name().Resolve(ctx, ipnsName, options.Name.Cache(false)) + if err != nil { + return err + } + + log.Info("Resolved to: ", resolved.String()) + + nd, err := node.Unixfs().Get(ctx, resolved) + if err != nil { + return err + } + defer nd.Close() + + f, ok := nd.(files.File) + if !ok { + return fmt.Errorf("%s is not a file", resolved.String()) + } + + b, err := io.ReadAll(f) + if err != nil { + return err + } + + var info ipnsInfo + err = json.Unmarshal(b, &info) + if err != nil { + return err + } + + log.Info("IPNS Info: ", string(b)) + + if info.PrNumber != prNumber { + return fmt.Errorf("IPNS entry not up-to-date") + } + + _, err = os.Stdout.WriteString(info.IpfsId + "\n") + if err != nil { + return err + } + return nil +} + +func doSend(node *rpc.HttpApi) error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + selfKey, err := node.Key().Self(ctx) + if err != nil { + return err + } + + info := workflowInfo{ + PrNumber: prNumber, + GithubToken: os.Getenv("GITHUB_TOKEN"), + IpfsId: selfKey.ID().String(), + StaticIpnsName: staticIpnsName, + } + + b, err := json.Marshal(&info) + if err != nil { + return err + } + + ageRecipient, err := age.ParseX25519Recipient(agePubKey) + if err != nil { + return err + } + + w := bytes.NewBuffer(nil) + e, err := age.Encrypt(w, ageRecipient) + if err != nil { + return err + } + _, err = e.Write(b) + if err != nil { + return err + } + err = e.Close() + if err != nil { + return err + } + b = w.Bytes() + + log.Info("Sending info...") + + err = p2pSendFile(ctx, node, ipfsId, b) + if err != nil { + return err + } + + log.Info("Done sending info.") + + return nil + +} + +func doReceive(node *rpc.HttpApi) error { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) + defer cancel() + + log.Info("Receiving info...") + + b, err := p2pReceiveFile(ctx, node) + if err != nil { + return err + } + + log.Info("Done receiving info.") + + idsBytes, err := os.ReadFile(ageKeyFile) + if err != nil { + return err + } + ageIds, err := age.ParseIdentities(bytes.NewReader(idsBytes)) + if err != nil { + return err + } + d, err := age.Decrypt(bytes.NewReader(b), ageIds...) + if err != nil { + return err + } + + w := bytes.NewBuffer(nil) + _, err = io.Copy(w, d) + if err != nil { + return err + } + b = w.Bytes() + + var info workflowInfo + err = json.Unmarshal(b, &info) + if err != nil { + return err + } + + if info.PrNumber != prNumber { + return fmt.Errorf("%d is not the expected (%d) PR number", info.PrNumber, prNumber) + } + + log.Info("Checking Github token...") + + err = checkGithubToken(ctx, info.GithubToken) + if err != nil { + return err + } + + log.Info("Done checking Github token...") + + info.GithubToken = "" + + b, err = json.Marshal(&info) + if err != nil { + return err + } + _, _ = os.Stdout.WriteString(string(b) + "\n") + + return nil +} + +func doGithubRequest(ctx context.Context, method string, url string, body string, token string) ([]byte, error) { + log.Info("request: ", method, url) + + req, err := http.NewRequest(method, url, strings.NewReader(body)) + if err != nil { + log.Error("NewRequest failed: ", err) + return nil, err + } + req = req.WithContext(ctx) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("token %s", token)) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Error("Request failed: ", err) + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + log.Error(fmt.Sprintf("Request failed: %d - %v", resp.StatusCode, resp.Status)) + return nil, fmt.Errorf("http error: %s", resp.Status) + } + + b, err := io.ReadAll(resp.Body) + if err != nil { + log.Error("Failed to read body: ", err) + return nil, err + } + + return b, nil +} + +func checkGithubToken(ctx context.Context, token string) error { + body := fmt.Sprintf(`{"query": "query UserCurrent{viewer{login}}"}`) + b, err := doGithubRequest(ctx, "POST", "https://api.github.com/graphql", body, token) + if err != nil { + return err + } + log.Info("body=", string(b)) + + var r struct { + Data struct { + Viewer struct { + Login string `json:"login"` + } `json:"viewer"` + } `json:"data"` + } + err = json.Unmarshal(b, &r) + if err != nil { + log.Error("Unmarshal failed: ", err) + return err + } + if r.Data.Viewer.Login != "github-actions[bot]" { + log.Error("unexpected response from github") + return fmt.Errorf("unexpected response from github") + } + + log.Info("Querying repositories...") + + b, err = doGithubRequest(ctx, "GET", "https://api.github.com/installation/repositories", "", token) + if err != nil { + return err + } + log.Info("body=", string(b)) + + var r2 struct { + Repositories []struct { + FullName string `json:"full_name"` + } `json:"repositories"` + } + err = json.Unmarshal(b, &r2) + if err != nil { + return err + } + if len(r2.Repositories) != 1 { + return fmt.Errorf("unexpected repositories count %d", len(r2.Repositories)) + } + if r2.Repositories[0].FullName != repoName { + return fmt.Errorf("%s is not the expected repo name", r2.Repositories[0].FullName) + } + + return nil +} + +func p2pSendFile(ctx context.Context, node *rpc.HttpApi, ipfsId string, data []byte) error { + // close the old one + _, _ = node.Request("p2p/close"). + Option("protocol", "/x/kluctl-preview-info"). + Option("listen-address", "/ip4/127.0.0.1/tcp/10001"). + Send(ctx) + + _, err := node.Request("p2p/forward", "/x/kluctl-preview-info", "/ip4/127.0.0.1/tcp/10001", fmt.Sprintf("/p2p/%s", ipfsId)). + Send(ctx) + if err != nil { + return err + } + + c, err := net.Dial("tcp", "127.0.0.1:10001") + if err != nil { + return err + } + defer c.Close() + + e := gob.NewEncoder(c) + d := gob.NewDecoder(c) + + err = e.Encode(data) + if err != nil { + return err + } + + var ok string + err = d.Decode(&ok) + if err != nil { + return err + } + if ok != "ok" { + return fmt.Errorf("did not receive ok") + } + + return nil +} + +func p2pReceiveFile(ctx context.Context, node *rpc.HttpApi) ([]byte, error) { + l, err := net.Listen("tcp", "127.0.0.1:10002") + if err != nil { + return nil, err + } + defer l.Close() + addr := l.Addr().(*net.TCPAddr) + + targetAddr := fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", addr.Port) + + // close the old one + _, _ = node.Request("p2p/close"). + Option("protocol", "/x/kluctl-preview-info"). + Option("target-address", targetAddr). + Send(ctx) + _, err = node.Request("p2p/listen", "/x/kluctl-preview-info", targetAddr). + Send(ctx) + if err != nil { + return nil, err + } + + c, err := l.Accept() + if err != nil { + return nil, err + } + defer c.Close() + + d := gob.NewDecoder(c) + e := gob.NewEncoder(c) + + var data []byte + err = d.Decode(&data) + if err != nil { + return nil, err + } + + ok := "ok" + err = e.Encode(&ok) + if err != nil { + return nil, err + } + + return data, nil +} From 02a0977795f011fd9b95d2ef689b1a09b7afc2b3 Mon Sep 17 00:00:00 2001 From: Aleksei Vagarenko Date: Wed, 21 Jun 2023 03:59:16 +0600 Subject: [PATCH 1214/2268] Fix visual bug on the Targets page in Firefox browser. (#593) --- pkg/webui/ui/build/index.html | 2 +- pkg/webui/ui/build/static/css/main.css | 2 +- pkg/webui/ui/build/static/js/main.js | 4 ++-- .../components/targets-view/TargetsView.tsx | 20 +++++++++---------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/webui/ui/build/index.html b/pkg/webui/ui/build/index.html index 044a50d48..0ef404b56 100644 --- a/pkg/webui/ui/build/index.html +++ b/pkg/webui/ui/build/index.html @@ -1 +1 @@ -React App
    \ No newline at end of file +React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/static/css/main.css b/pkg/webui/ui/build/static/css/main.css index 86c1ca612..70ec57003 100644 --- a/pkg/webui/ui/build/static/css/main.css +++ b/pkg/webui/ui/build/static/css/main.css @@ -84,4 +84,4 @@ body, unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; } -/*# sourceMappingURL=main.css.map?h=9212af710a168fca4ce287f416924bf456fbc497b2cc289b74211c435224c397*/ \ No newline at end of file +/*# sourceMappingURL=main.css.map?h=b04a21858ecfe46c93b33bc95788b6861c50ee3a7852bdb7721e314a3ee95b80*/ \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js index 7f430b626..a7625bb71 100644 --- a/pkg/webui/ui/build/static/js/main.js +++ b/pkg/webui/ui/build/static/js/main.js @@ -59264,7 +59264,7 @@ function doGetRootNode(_x,_x2){return _doGetRootNode.apply(this,arguments);}func var colWidth=416;var curveRadius=12;var circleRadius=5;var strokeWidth=2;function ColHeader(_ref){var children=_ref.children;return/*#__PURE__*/(0,jsx_runtime.jsx)(material_Box_Box,{minWidth:colWidth,width:colWidth,height:"42px",display:"flex",alignItems:"center",sx:{borderLeft:'0.8px solid rgba(0,0,0,0.5)',paddingLeft:'15px','&:first-of-type':{borderLeft:'none',paddingLeft:0}},children:/*#__PURE__*/(0,jsx_runtime.jsx)(Typography_Typography,{variant:"h2",textAlign:"left",children:children})});}var Circle=/*#__PURE__*/react.memo(function(props){var theme=styles_useTheme_useTheme();return/*#__PURE__*/(0,jsx_runtime.jsx)("circle",_objectSpread2({r:circleRadius,fill:theme.palette.background.default,stroke:theme.palette.secondary.main,strokeWidth:strokeWidth},props));});var RelationTree=/*#__PURE__*/react.memo(function(_ref2){var targetCount=_ref2.targetCount;var theme=styles_useTheme_useTheme();var height=targetCount*cardHeight+(targetCount-1)*cardGap;var width=152;if(targetCount<=0){return null;}var targetsCenterYs=Array(targetCount).fill(0).map(function(_,i){return cardHeight/2+i*(cardHeight+cardGap);});var rootCenterY=height/2;return/*#__PURE__*/(0,jsx_runtime.jsxs)("svg",{width:width,height:height,viewBox:"0 0 ".concat(width," ").concat(height),fill:"none",children:[targetsCenterYs.map(function(cy,i){var d;if(targetCount%2===1&&i===Math.floor(targetCount/2)){// target is in the middle. d="\n M ".concat(circleRadius,",").concat(rootCenterY,"\n h ").concat(width-circleRadius,"\n ");}else if(i { { /> - + - +
    From 636469345cf6c08b8f8ccb49dc997858aee76267 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Wed, 21 Jun 2023 09:10:49 +0200 Subject: [PATCH 1215/2268] ci: Remove npm build folder from git --- .github/workflows/tests.yml | 11 - pkg/webui/ui/.gitignore | 3 + pkg/webui/ui/build.js | 7 - pkg/webui/ui/build/.gitignore | 1 - pkg/webui/ui/build/.gitkeep | 0 pkg/webui/ui/build/asset-manifest.json | 58 - pkg/webui/ui/build/favicon.ico | Bin 15406 -> 0 bytes pkg/webui/ui/build/index.html | 1 - pkg/webui/ui/build/logo192.png | Bin 13703 -> 0 bytes pkg/webui/ui/build/logo512.png | Bin 26403 -> 0 bytes pkg/webui/ui/build/manifest.json | 25 - pkg/webui/ui/build/robots.txt | 3 - pkg/webui/ui/build/static/css/main.css | 87 - pkg/webui/ui/build/static/js/787.chunk.js | 239 - pkg/webui/ui/build/static/js/main.js | 61552 ---------------- pkg/webui/ui/build/static/media/added.svg | 4 - .../ui/build/static/media/arrow-left.svg | 4 - .../ui/build/static/media/brackets-curly.svg | 3 - .../ui/build/static/media/brackets-square.svg | 3 - pkg/webui/ui/build/static/media/changed.svg | 5 - pkg/webui/ui/build/static/media/changes.svg | 5 - .../build/static/media/checkbox-checked.svg | 5 - .../build/static/media/checkbox-disabled.svg | 4 - pkg/webui/ui/build/static/media/checkbox.svg | 3 - .../ui/build/static/media/close-light.svg | 3 - pkg/webui/ui/build/static/media/close.svg | 3 - pkg/webui/ui/build/static/media/cpu.svg | 16 - pkg/webui/ui/build/static/media/deploy.svg | 8 - pkg/webui/ui/build/static/media/diff.svg | 8 - pkg/webui/ui/build/static/media/error.svg | 4 - pkg/webui/ui/build/static/media/file.svg | 5 - .../ui/build/static/media/finger-scan.svg | 8 - pkg/webui/ui/build/static/media/git.svg | 1 - pkg/webui/ui/build/static/media/include.svg | 7 - .../ui/build/static/media/kluctl-logo.svg | 3 - .../ui/build/static/media/kluctl-text.svg | 7 - .../nunito-cyrillic-ext-wght-normal.woff2 | Bin 28960 -> 0 bytes .../media/nunito-cyrillic-wght-normal.woff2 | Bin 20824 -> 0 bytes .../media/nunito-latin-ext-wght-normal.woff2 | Bin 32720 -> 0 bytes .../media/nunito-latin-wght-normal.woff2 | Bin 35904 -> 0 bytes .../media/nunito-vietnamese-wght-normal.woff2 | Bin 10632 -> 0 bytes pkg/webui/ui/build/static/media/orphan.svg | 6 - pkg/webui/ui/build/static/media/project.svg | 5 - pkg/webui/ui/build/static/media/prune.svg | 8 - .../ui/build/static/media/relation-hline.svg | 5 - pkg/webui/ui/build/static/media/result.svg | 8 - pkg/webui/ui/build/static/media/search.svg | 4 - pkg/webui/ui/build/static/media/star.svg | 4 - pkg/webui/ui/build/static/media/target.svg | 5 - pkg/webui/ui/build/static/media/targets.svg | 6 - pkg/webui/ui/build/static/media/trash.svg | 6 - pkg/webui/ui/build/static/media/tree-view.svg | 7 - .../ui/build/static/media/triangle-down.svg | 4 - .../static/media/triangle-left-light.svg | 4 - .../static/media/triangle-right-light.svg | 4 - .../ui/build/static/media/triangle-right.svg | 4 - .../ui/build/static/media/warning-sign.svg | 5 - pkg/webui/ui/build/static/media/warning.svg | 5 - pkg/webui/ui/build/staticbuild.js | 1 - pkg/webui/ui/fix-assets/main.go | 169 - pkg/webui/ui/package-lock.json | 396 +- pkg/webui/ui/package.json | 8 +- 62 files changed, 7 insertions(+), 62753 deletions(-) delete mode 100644 pkg/webui/ui/build.js delete mode 100644 pkg/webui/ui/build/.gitignore create mode 100644 pkg/webui/ui/build/.gitkeep delete mode 100644 pkg/webui/ui/build/asset-manifest.json delete mode 100644 pkg/webui/ui/build/favicon.ico delete mode 100644 pkg/webui/ui/build/index.html delete mode 100644 pkg/webui/ui/build/logo192.png delete mode 100644 pkg/webui/ui/build/logo512.png delete mode 100644 pkg/webui/ui/build/manifest.json delete mode 100644 pkg/webui/ui/build/robots.txt delete mode 100644 pkg/webui/ui/build/static/css/main.css delete mode 100644 pkg/webui/ui/build/static/js/787.chunk.js delete mode 100644 pkg/webui/ui/build/static/js/main.js delete mode 100644 pkg/webui/ui/build/static/media/added.svg delete mode 100644 pkg/webui/ui/build/static/media/arrow-left.svg delete mode 100644 pkg/webui/ui/build/static/media/brackets-curly.svg delete mode 100644 pkg/webui/ui/build/static/media/brackets-square.svg delete mode 100644 pkg/webui/ui/build/static/media/changed.svg delete mode 100644 pkg/webui/ui/build/static/media/changes.svg delete mode 100644 pkg/webui/ui/build/static/media/checkbox-checked.svg delete mode 100644 pkg/webui/ui/build/static/media/checkbox-disabled.svg delete mode 100644 pkg/webui/ui/build/static/media/checkbox.svg delete mode 100644 pkg/webui/ui/build/static/media/close-light.svg delete mode 100644 pkg/webui/ui/build/static/media/close.svg delete mode 100644 pkg/webui/ui/build/static/media/cpu.svg delete mode 100644 pkg/webui/ui/build/static/media/deploy.svg delete mode 100644 pkg/webui/ui/build/static/media/diff.svg delete mode 100644 pkg/webui/ui/build/static/media/error.svg delete mode 100644 pkg/webui/ui/build/static/media/file.svg delete mode 100644 pkg/webui/ui/build/static/media/finger-scan.svg delete mode 100644 pkg/webui/ui/build/static/media/git.svg delete mode 100644 pkg/webui/ui/build/static/media/include.svg delete mode 100644 pkg/webui/ui/build/static/media/kluctl-logo.svg delete mode 100644 pkg/webui/ui/build/static/media/kluctl-text.svg delete mode 100644 pkg/webui/ui/build/static/media/nunito-cyrillic-ext-wght-normal.woff2 delete mode 100644 pkg/webui/ui/build/static/media/nunito-cyrillic-wght-normal.woff2 delete mode 100644 pkg/webui/ui/build/static/media/nunito-latin-ext-wght-normal.woff2 delete mode 100644 pkg/webui/ui/build/static/media/nunito-latin-wght-normal.woff2 delete mode 100644 pkg/webui/ui/build/static/media/nunito-vietnamese-wght-normal.woff2 delete mode 100644 pkg/webui/ui/build/static/media/orphan.svg delete mode 100644 pkg/webui/ui/build/static/media/project.svg delete mode 100644 pkg/webui/ui/build/static/media/prune.svg delete mode 100644 pkg/webui/ui/build/static/media/relation-hline.svg delete mode 100644 pkg/webui/ui/build/static/media/result.svg delete mode 100644 pkg/webui/ui/build/static/media/search.svg delete mode 100644 pkg/webui/ui/build/static/media/star.svg delete mode 100644 pkg/webui/ui/build/static/media/target.svg delete mode 100644 pkg/webui/ui/build/static/media/targets.svg delete mode 100644 pkg/webui/ui/build/static/media/trash.svg delete mode 100644 pkg/webui/ui/build/static/media/tree-view.svg delete mode 100644 pkg/webui/ui/build/static/media/triangle-down.svg delete mode 100644 pkg/webui/ui/build/static/media/triangle-left-light.svg delete mode 100644 pkg/webui/ui/build/static/media/triangle-right-light.svg delete mode 100644 pkg/webui/ui/build/static/media/triangle-right.svg delete mode 100644 pkg/webui/ui/build/static/media/warning-sign.svg delete mode 100644 pkg/webui/ui/build/static/media/warning.svg delete mode 100644 pkg/webui/ui/build/staticbuild.js delete mode 100644 pkg/webui/ui/fix-assets/main.go diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 74553224f..c2674e099 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -72,17 +72,6 @@ jobs: git diff exit 1 fi - - name: Verify webui build is up-to-date - run: | - cd pkg/webui/ui - npm install - npm run build - if [ ! -z "$(git status --porcelain)" ]; then - echo "npm run build must be invoked and the result committed" - git status - git diff - exit 1 - fi tests: strategy: diff --git a/pkg/webui/ui/.gitignore b/pkg/webui/ui/.gitignore index f471c1a59..3b5e29e26 100644 --- a/pkg/webui/ui/.gitignore +++ b/pkg/webui/ui/.gitignore @@ -23,3 +23,6 @@ yarn-error.log* *.js.map /public/staticdata + +/build/* +!/build/.gitkeep diff --git a/pkg/webui/ui/build.js b/pkg/webui/ui/build.js deleted file mode 100644 index 17db0e576..000000000 --- a/pkg/webui/ui/build.js +++ /dev/null @@ -1,7 +0,0 @@ -const rewire = require('rewire'); -const defaults = rewire('react-scripts/scripts/build.js'); -const config = defaults.__get__('config'); - -// we disable minimize as we're actually committing the build folder into git, so that people who don't want to deal -// with the UI while working on Go code don't have to use npm and friends -config.optimization.minimize = false diff --git a/pkg/webui/ui/build/.gitignore b/pkg/webui/ui/build/.gitignore deleted file mode 100644 index c9e9f1beb..000000000 --- a/pkg/webui/ui/build/.gitignore +++ /dev/null @@ -1 +0,0 @@ -staticdata \ No newline at end of file diff --git a/pkg/webui/ui/build/.gitkeep b/pkg/webui/ui/build/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/webui/ui/build/asset-manifest.json b/pkg/webui/ui/build/asset-manifest.json deleted file mode 100644 index dfdfa5fab..000000000 --- a/pkg/webui/ui/build/asset-manifest.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "files": { - "787.chunk.js": "static/js/787.chunk.js", - "787.chunk.js.map": "static/js/787.chunk.js.map", - "index.html": "index.html", - "main.css": "static/css/main.css", - "main.css.map": "static/css/main.css.map", - "main.js": "static/js/main.js", - "main.js.map": "static/js/main.js.map", - "static/media/added.svg": "static/media/added.svg", - "static/media/arrow-left.svg": "static/media/arrow-left.svg", - "static/media/brackets-curly.svg": "static/media/brackets-curly.svg", - "static/media/brackets-square.svg": "static/media/brackets-square.svg", - "static/media/changed.svg": "static/media/changed.svg", - "static/media/changes.svg": "static/media/changes.svg", - "static/media/checkbox-checked.svg": "static/media/checkbox-checked.svg", - "static/media/checkbox-disabled.svg": "static/media/checkbox-disabled.svg", - "static/media/checkbox.svg": "static/media/checkbox.svg", - "static/media/close-light.svg": "static/media/close-light.svg", - "static/media/close.svg": "static/media/close.svg", - "static/media/cpu.svg": "static/media/cpu.svg", - "static/media/deploy.svg": "static/media/deploy.svg", - "static/media/diff.svg": "static/media/diff.svg", - "static/media/error.svg": "static/media/error.svg", - "static/media/file.svg": "static/media/file.svg", - "static/media/finger-scan.svg": "static/media/finger-scan.svg", - "static/media/git.svg": "static/media/git.svg", - "static/media/include.svg": "static/media/include.svg", - "static/media/kluctl-logo.svg": "static/media/kluctl-logo.svg", - "static/media/kluctl-text.svg": "static/media/kluctl-text.svg", - "static/media/nunito-cyrillic-ext-wght-normal.woff2": "static/media/nunito-cyrillic-ext-wght-normal.woff2", - "static/media/nunito-cyrillic-wght-normal.woff2": "static/media/nunito-cyrillic-wght-normal.woff2", - "static/media/nunito-latin-ext-wght-normal.woff2": "static/media/nunito-latin-ext-wght-normal.woff2", - "static/media/nunito-latin-wght-normal.woff2": "static/media/nunito-latin-wght-normal.woff2", - "static/media/nunito-vietnamese-wght-normal.woff2": "static/media/nunito-vietnamese-wght-normal.woff2", - "static/media/orphan.svg": "static/media/orphan.svg", - "static/media/project.svg": "static/media/project.svg", - "static/media/prune.svg": "static/media/prune.svg", - "static/media/relation-hline.svg": "static/media/relation-hline.svg", - "static/media/result.svg": "static/media/result.svg", - "static/media/search.svg": "static/media/search.svg", - "static/media/star.svg": "static/media/star.svg", - "static/media/target.svg": "static/media/target.svg", - "static/media/targets.svg": "static/media/targets.svg", - "static/media/trash.svg": "static/media/trash.svg", - "static/media/tree-view.svg": "static/media/tree-view.svg", - "static/media/triangle-down.svg": "static/media/triangle-down.svg", - "static/media/triangle-left-light.svg": "static/media/triangle-left-light.svg", - "static/media/triangle-right-light.svg": "static/media/triangle-right-light.svg", - "static/media/triangle-right.svg": "static/media/triangle-right.svg", - "static/media/warning-sign.svg": "static/media/warning-sign.svg", - "static/media/warning.svg": "static/media/warning.svg" - }, - "entrypoints": [ - "static/css/main.css", - "static/js/main.js" - ] -} \ No newline at end of file diff --git a/pkg/webui/ui/build/favicon.ico b/pkg/webui/ui/build/favicon.ico deleted file mode 100644 index 496bbc69cabfce158be2fbc7e709e8a81a5bc95e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 zcmeHO3v^V)8D6vxTWwWZwYJ*gQCn+UYisN2sUD@R)x%@&y$Qy*0*Z(SJr#k_~K1sI8X6j^_Pc$c$$5q)hSqBj@C_1{Q@LcIGU`Zm8@bQIo zzk~u!KkI6i3>TnrqpY`Mdm0cH{5ED!1ok3XvY zW7L#BgJX;QCBv(4O7+QDKo8fdFm~@AkLM(R#N7+HyD*LmAlGiZ?+HY- zc^H?EeNm&YXF+FJYug{k6#Xs)?1m)Yk%WMrMqw<+B;NJnM4Pr5_Tcl>8Z>Y72i#J& zjhWh4JU^xP3;{i}0X$=T_|7`en?|_eNjHn}vj>i2J%7HiG2+>pyTts}6$ge+?Ece4 zE$|~+BX~8)Z}jQ>XKU^}Fnh&|A~b)<2w7h;v&*-^=MFrV$+|$5erZBpT?C?en3ahi zVF*hcS!Y)UqQ?Eiub4X+_{RNqFq!H+5u?-db-fzv>Yv->jk_<%1bZxP^fR6CbjCyf z)ZU*zYCMSkHp6BPKtGj5@#{`^qQy4AhA=*#(N_`I2{xB{)o;l$gA5TTT-Z@K1&1-h z_zKui4=4C+^_Z8{D!t_U=MR>o+X(#Bx6poAg0adXs{m4@{jy8b(8wMcrh^hUe=(gyZ2+0Js#n=gsj!on8Pu)-eA zcOb0w1id3F?LbJ&x6*(QxLLxCs=G(TUh}pD!uowyxb}SeBJMjuyHyWieYlkl+DA(M z$+Eu{=2*UA+ujuYu(sMt3+v0dJUo7jbWnpFa-(pUU z16(C}vK>78NZHLY%sH$Lby^3wCMDtaVQs(5w2iR7FG0fySb6fBw7enB0~k~G1T5kJ zlQ-5)^e6q%8hsJtPOB~pqwYVZ!zS>95$$&>f3E425&dbj#fj?V-Nt97!)co_kDj9L zO&bX5qtjs${1H!#B1~eOw4s1==$mfFNoYR=9n5l~XH$RBrp~H6TNuWvm9IW7%I6Fc zQ|q4-4O{9N8%_&iJqArPR!q_+KILXiw+YY3c&t2f`%c(J6Y!gc$8Xv?{*?iTYF;lE zQ{EUUiYMQ^YFJhNc^ogsK=i5JsBxjk<2p$lL%t8I)4o?&rCkMGt;{A1#$XlW% z+B(g#ArJDh<74G({l?_jg$Fj?*{e#PQkq!!MM%l5ys7;_LE6v>$`-^lZFS z?O|<>FO>hYW95&1%y+Oaco_ZOD`}&DlNr;XTqx^ME`krtV{DYFGvhVgmu+8WC!;@_ z_iOr8JRhd$nR7rJD`R-tkZj{#EJ>#WY_vqyn`M-~Q5#LaWyedc!y526;Bd~R%`dEP zRqD9X&r3MQ^a`C!cCyhMcK=m@O}#>Q=YZZPlBO)jPP-4XX!VTmQowkDkGodFSZS7) zUhz$|+3ocN)}MWzioCN`T6_omZIYfWm#mG8aWfv+>kD;Fvn4NL8wm%ReXR5_cITsg zPYMP)9Y1S;Sg^VhYp45GD?InVD(u2F5mCRsmg6wM3d?WU-%Apne%~I({g8xVIrpuX z#p1Ow5eS?6@ir&}XzLMuBjIXae@1NDzEX^=zJ0w_?-B`iCikTD#Ya=|?wFw`~omgRFE7H>B#5KGoh65fdBIUclgp}}?B7uAN1p4w;k=&ASaX1s5g zzsZHx|2howB;0WH!gDNn5}>#Fc`Mu(aExH5+hDJUkq=qmuhhDE%6pyC1~9`1VPRJq zXmH>4IqYsbXbmUr3~0yPl#Jh($vLK!pJI(3;^dQ6#tGkR48hv@nSw8U)>3b!_6rsM zc)iEDPYr8zHatzK-yW z?wc_FU*LHHbUI|9)d4x@#~@>yyb1dZg9c`w6gXTbN@!6G*dP%7pl5F7usE{1{)u2fU`r|IFlD#mbGdaaQGT zDT?-(X?N!2M6W&d>;qy{?#I4Rb${21o?6d50_JmTJ~OH}$Q&f$=o3Mk zc~8^!;b%XjA5&>Ky>m~EHT#hy{+5J{TBTg$$OrV)oRyqk^zk(uk6iXJIIDgD@Y1&& z#NPU84{66KyP0E8=6?;u+{Qgl^2&PhByXJM@~kgyy`jvM9p@6v-)CA=kz+T8Fuy8u zWRizkQr5P4T%46g^%dZ=0`w7ICv*?lm%+!{<{wCpJc4oUBH%p*zTlnaucUqraK;gMm+7#AP(Fcj^)yCP-GR<6NbXDD$=13qPEuDHYE1!k@=w$RI* zCG%nZbH#OlLmXt-#X?#7VB%!Ngq>|4b98N&OKY0KLxIKH*Fvp zk6HJloYV9f`_a#RfZ5N7dc^8DyHsHG%WSQ{MGQ!0C^%tU_6XwwE}!z2?{$y@JT=qW(7c;hrWO@>Iw< zjzXVm@%=vh40*RhX7ip?9enK=(*|)@34D8z4a;@cJ;0*|yeMOTtcT}N z_S6r4i+d0DiEBnCU&(ww?^>8^Vj|z(e6;dI&&^naI@53Q95xd_&ULP(dV`FuGH~v~mvWE7_#C-e?r9F-k-GnscaDZt z=by*e4D}(UjW8|1`v`ur+8^$bx|8T6N3QkMb)Y8i2k^C*fqpTb)OoSOpX*sUV;M=8 zevUd#!G!gxs*MyBAeS07&-tutCi`*_{31mW{m=CAr7QY6=JO;!Yf0bEJHF6a5HimO zuiYtGT6IcZhhRr9QeKtDAjGI>E0;LPSyk{3unJRDT5vkrN8@4tNz3vuKdmi>9)@GQ z-MQKp?__{hIdCX*TN|2c77G`7$%tjeBLm>9?IpJnN4(XO3G^F6=xRj{ zF$nGIpQ@dv@r5oS{{km{RJ{6_n7M2$`f$re*w>$|`10G_GvY2?D;;e$>twsN3?hdD zTlse@d>j3X=WBb3@B)v(S^7NQ*|*}$Z{7h${M2Yir)vI_$Zt1@QB&{X9oj*e_Qc*$ zkBgJ`m$VCQN3c@6Nb<3k)G6Y%f#`$Xd^UCV!R{u3xU(_S{T1deA-g3XrHj&;4~VV1 z-W4;KzW{lRWr(F5bF`H0=_+>@s}6X_-72^G?TO#e5p&g=-!kv|u56qmcJAFGo~`Yf zF~7=r2OEp2yyLhdK-stKSSOyV>4|ko>!$Lxd&k|Jwz(e8gT&d49eApaYZ%~;aE@`G z6^!NMuB=b&JFrX4Uo{alv{$&}t=>H0czpS5GpX58;66T>Inw2QjS6lY7K@Ev)k3}O7Q z1^g)N9eWg`q$G!Y8Sf}rW#>2i%aKfTSn?(h@&XR>Xa;@wPV{LVWs)$tl*Ki9XM3aF zuje|(`M-@rS4#uGAGvn zzGDpD{;FijRb~|q|Ci7O`bQj=u6yQeQ2&vzn5;E5tX&w~m> k-LAl1ECQML7Eiu)AL7+G?;m^__cBhDe*a&N|4t432jt`J!vFvP diff --git a/pkg/webui/ui/build/index.html b/pkg/webui/ui/build/index.html deleted file mode 100644 index 0ef404b56..000000000 --- a/pkg/webui/ui/build/index.html +++ /dev/null @@ -1 +0,0 @@ -React App
    \ No newline at end of file diff --git a/pkg/webui/ui/build/logo192.png b/pkg/webui/ui/build/logo192.png deleted file mode 100644 index 11f22322c00fe673d7aa1530ecfd53fbc82b2373..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13703 zcmXY&V|3tLw1!h#Q@c}RYE3(}+o|oTt*LE$YTLGL+qP}@_Ph7~NY2X2TFFkQfeI4yI5rx8rLi^(Sp1sbHxjepRrplYlV8kRo&iEf>^m3#fc#ghbythAIJiJ0* z>$!qgq+BcBVwf31A&eY@uAqnJq`?n}A(sV(yTRNw=zPe@nKFXYR?bm7(X~u8FWMib zG=Z4yoU_+I_inoKbhj9P%0gHeZ+!#W{aBvhn0Zu!F-&Mn4idJIq3x$MWdH_-Vr* z!Ei6Y#99^_k!g1t{Pzr;-3$RGyO^)gYQSyDgR9F7CMq1#>oDS9#iNoG+T=x_3E%$kma_H-6hmO+$xl*)UM* z2hrhMjH05Jr{OmL5C~;rwYj1Z`dQu^B6jJ8($aXmS8F7P#!^e}jgy37vD^s9-0=*I zvVVqyWsLA4je@w^_ij8)dT@BUw^K4^iA#wH;{lNN_3PzO-_?Z3O?vrEjZr1(<%FtAqKfWUifaF~P@;i|3&Key|ro#mw}>!428ftwh~ z!R73g@Wsa{K0H7-GI$PD1qanAs2Gxa)m~tNOn;iE*brZd_j1jDkS8r-J^o`AByX#G zr#iAT(7csubPxntAR5st<2de7{8rHPNp#wcf9k} z`r>u#r@IakGd6wXM!=j74Ar5;MAqVDf7eERF!tn)$7`O# z`8E{^?2~7_fX-=5JT({lTvTBS6Uuu+n?QZfBC$x#7-&*242A5U;M_Lgax^tc-(%QHcgDK;(K**8=E`ZVj8crXt0mXmcN5r`9bjsC^g+$#J!=QoJL<>P>6}t#(i-bW zC!GB0j|k4sUvQ(1FHsKN>Yp`ZqMgiB?y+*{Um;dV(>cG{2h7ED_D4}A7*uaR_^52h zWEk^ZkT1rCe7b;bUOuhM@F05Y2iA7O{soT0)0cs0t9z4mlql_($cORBGX%|FuQ1MP zeIQx2o1pz)abORL0emeB?Mk9~nSW8F&E`SuR6Kn9I~U$(LOMb09rfOR)_rsd!vd@X z)I`uW?TCXQS)GKt)R$J2mPy@WOf&V+Nm9rVOPtu-4qx8 zzmwW;ugCO|>P+if0gl#=I(lZ)R$y90i~p`b2Xr5ZP8VNQt5zwHX5?LxrG&hsmG)2J zamZu}X9s;3U@(DSg1Z^;`?-}?!#{GWAXr37N!5Rhy2&~%5i5)D1%@>>o(p>`jV|C0 zo@2IwdOIo~WhUi8bDlkZkjmLX%K{6;jHZo$8?_t<6KwrMS9oUD>E;@Kz5a9o=|;=U z&q55^D~!xw*o8B>D_uDIsi+Aa9;DxMY)?(&y0W4klQp-XuwB06!K?GAS6I6caA@-b zf*zL+m`31Y#w7R;Ywl~^il+rrF4q!;LhPr3- z_YD4{Kc>uWo*`acNoK3Fk~JHMQ=ooG$N|+9!AV`=tZOY#m=p-#&FF-Y+3!tv2fyG=y9LZnD(>9IJ_#aN+eYtGqFi4nrXe3`c%7BF4P1f+H;UW-;`A0`x1 zUONOTjo|H>2bT{ewv8vD?*#zSe7Z(gmH|Ku55phvZNN%7t~0JcCXOpjPNiZZb7eFq zm{7|8EGPzkkkA{F4l>E)C)PYwfI_WUuoqqgj`qpFysq)G`Q>axSFSC6c4JgRSG z;)RJj>JpqLF8>jZ#H(KgOKtw~)hwzV zEnbr@{b1z)!L_=M`o+GTdtX}hlo;RP4pR@uOdY&cSDaydn8#7-8?iclf(_}7_BdAy zv*Q7L6wCNt-T2(b{JkD_;KknW^7<@9ANqU{RCInH&)tM3)I!b^OE2BKtR-Vw#r@~A z196g8OEw4}Tk`pd?_iCVjep+qBS;0&VK3v!zGe&9Zapiuv9OIOrG{>N(50+g?Z(g(*5 z_-u5|*cqpPva1J-iq+E(W13Puj{xZJ<9?A=iCEyq;;s#EZQ&^~F_$fIXm9LO*$Xh^ zQ^hX0WvkDxjD?Lc5Y{@0+E^>E7VsAfh0?3amahn2?{!$eKsUgJ+@1cqMoJ1{DZ}6b zB%5RB1uJn*0FPEB1%_}x#8=>r2raO*mJT!K&vO!?S8vuBxz6{8AEl7tWNcx^!E&4a zuB$exk;(F~T2ZKb->Mh`pnpN^zRbW63h1*0RtkH*W-iqX1`(wZcCck>$-{*cvcMl^ zIG1E{MnFiY&_fe1cssy;=vz${iH?~9@zM4*ku{AAyImIW(dgONWo zoUZw}91TqVm4fbyzGlNVhTMAq)}5Oe-al;lc1to;*lAadTk{#kWl|;MJ(y^C&?H!| z(CVoQL?=&Kx;+fTlQ{+K3Ry0L&xg{uWd?+QU@mSCCEja)zHrBt!6fN_>oino+datR zc#k#3-d7BEJ*!Q7&r*+Qr5;vQiut$Bs}6WNe9SeJ_J0 zmp|iuzPxaMb~1O$&KJysg^8>wP{eLABH))GV*oL585dcyhTpwM{aCRixtr-3n#>8g zY`0k*N=D&#V3flSST#8A1Q5LFwi7ddi$nqJi zSr|v{WU^^5!A-Y6CuI7Cx!(j;j_--=E@r2eF3CPSbYxuB3ThWhzl)KG07UsFCscW+ zzX7IwVCk9SzQ6sDoMd_@Q=!q|-q_i$9}2nNm)}2Lx!>+1jir!FY5l1G!f_ZT<-4`W zcZQB1qm1p-xDZ{9JB9dfcv&At&$EplDw6Fxki7Cbrsgk+ms{oao>2Z?kFfhWUkk}= z=Ja=xHDHGwvwtXS^ciO3uxUp`F~TZbfzbM);!o)GTsWXtU@XC0`zd415FYpY>zgI& zpDc2OcE(ZuW43t(T5J16%%PQ>c?(`;a(eFY0mYZPq$*9l`EyI5A@{`NsLKoIYtMF+;<6cygab=K${n{PBIQMwTWwE@u=~WeeL(QvY4qGTujk$QpQUy(Ll&VmWlMLcX@nS%Rt;z0 zuh#pqIRf21{de@&DGE*<0QSIKurc~Lg99w4MCie&J>ND(;qAo;%?FDi!enHFwPZnt z6P8cfz&}pb{q5GH=#M1*r`_0am5Va)7#IMtI0_y9`9cXdxh4ZF_X=LK9ufQCLD>rn zLqaQBPk+RC65B}n;kT_b8%Tn#V=`gjZ`W_7jS!q8_S3nCvnj*&V4t8Eu7(ZLk+Ja)9c*gwYbb(LI^&+bi9-7)4gGaoF}k5wFcdE-zV%q`Zg8J!899@yx4Sb!-(oBK^zVc} zX=xi(t(CnCyMM+M4x0$xADO2vfuerIg?i$*>U$|c9C&?VA9!AV78q}#GE`8F+dt7m z1#bOO1%mhbD66EHVq54#)`s6bjD<3^c59++dG;YpU7a7&$B6&cmv8(O-k~5Z1W=k!TTSx7MK#; z2oPHVLXhiVOE6X0E8f&HsZ%Fm5m?BZ9+kfTk=hOKsZe~iq2eCPoI})PHlQ$`sz&oU zoP~Sm@NK+ero`%xeL8c_?-oIAC-MgADoIGWIjv4EJkgwc0UeW{o1gM3OTg7SlvXQA zW|#h*Lk*+6?Rk^TDxFE{93J6k1E-E3e0-mcDy_!{hN*hIV&mv=mjQRVfsK`E%kH!o zp>J*LZ3TpC1$)E7u9J{K(ye6zI=lZUE*cwPpf5UZbC0P!L}NBc%o8eRal$UpS?gOj z16>m-&MUuOAr9{fzZI=d8-*8V^phaWU<$EZbC}(AQfvibdO=&6*6W9qo3m7~twTxB z8WjSzB$bU6sQ}&9a8`nBy*DP^9Iu(Y^QWS&O2 znD}3r(~_=NYOr1sJ=shY7ml}&82F$I+~E3g97w=LWGyFju}f@zu_9QUcDlq_vWY#! z_nvK@+c4N$K2IOj6ceo@+rT2y9Vboos500f^A*Z8S-FN@xFk=Gz!ahVuS&B z;t42WANW9EI>U2!&Nkyq;j=0&Su<5cjyl*IN>&Ig!fwv}A<2?K|0ABq?xawWy^t0r8KNE^^_fIcG-jb~c=hj{y&0 za;0J}%m590D8p7cfImyOWyqi!w#RaGY3JD-7#N~)#c!uGet7Sh3Ne`4oTvNno;4` z=WO7FIT;p4&q~kXx1Qd}WWNBv0w`glOvr8K1`G@y&kiyrDWyQMJ-T;J(CK+Mf{eg- zE6w&aKRC0z7Jy_8m`HTT>B7o!`iWq7<7G1^iK4D{b>SPyM;?Iz%30|t+gX{y8QCIo2ea^ zASx%^I}q=VTSopQ*@_IteU>n#e`V!6&@X(;f0~znTrD~>5ChQR z!S3 wqli$aKQ2SbKZFKg{E9g@WEz#e7APR8O^xSmtxq&tn!vCgCj&k^n$_Wq;zA zAM`Dv;VH~GnZEgN+f{tP6GI?QJ%CAnC)TK!g(l-IRitx%djDsAT3SP8(raAI-V-XJPO#E7uu)q zC5$n<^Wi}vP<#<2(vH+LMoTN9q}D1A)V(1`*&-=i){4-*;cZeF2-e!U<;2#`V;balnGP<`|Sh9?U$#nvTrMq z2ZvdG(bpSzWSX=I^9<1%KE*Fq^dfkU9l%0~;KTQi(2=GY9xApw<-xM+u)7awFaN$5>r(HwEOb^^?O96vocq-^A~aW7ganJsxg>my^}3eei%8 zyRcD;3X-a|zRxQ5k>SmA{5kCO)4khDq6fAfq+9E22pk~A(|!L<{M-Wi!yUC)@1i*f2chw_?%TD5bN zOzu;LoAxIo4*TOPVjnwdRe;6}{8+QQ$A!4o$~Fj;p&j~dsOJ(z)R(`Qm7=T_@|~jk z5`=4&X3i;>%1#Qp9Qsp4m2zOLh$ho9^HGo@wMrANpx9y~Il-2JS7p8+{?^2PEe5;A z*Gp0%DHEub!<9;f9=fP&6#S^}C{$Rh7zQ5_S~(vJe*B(3nne_Z=$at%l2SZv8G6jl zr^Ddx^bPGAek=6{ZhRVX7d&mi;x8Xs7+&(C6ys0NEMd4Tyq*G+BLPXoXj3QRy*}Ge z*=lh=`GAs=5utG-2&LjOb!j7E7hr4Jn~FBgzR|P=iceE;# zWQp4yeu~XCS9UAPWKQDu_zkTa8P;zF#U@4EukzvPioqG3@f`eGnW0`+fEdKqZnW$p zu8zsN#22ntJHfK---fqVE$G70I3CaGvof2*<-SfEcn*nXXvBq@B!?_wPbr4fn}?Or zM2py;i727GNDW{Pe9~!tQu&!FNhIk}4K1W1G!5a8fLXvIHK)953L3E>LudT@@oaZFbR$=kpCmi^sIyRT5P)U2grZ$FJrs(Gq_r<-BImTq<%Y9Do)7J5mJ976Nn{_OJnA z1QRk=jM_Pc>KvnyR+Dv{<1q^kR9AKv{)VuT0e&M(^K@%?rCBu7!eU&hg&VI*M`YsOvIU%mlhOy zPqT>jJ-Tmcu1Jh@N>?pUKxmKU0_!KP(L{!$Gx6d(8dK1l?q2a1N4 zi@i0ryR@muM9prolt>*JqW8-NaIqGPbw?NA`foj$fRP^x43{-qvyH(#N4IQ*ylI(| zdKW07IMWZk;ZzQYU#pONxuF!nrC-*r6DFR@{0Zy;!4HgPC0<`ofx00V;8F;CFqz9K zu4&Rsd=`F^og9|F@^uQzn|Lu^ptrSpHe&EW5m_WI{}%>+tI=!;3KU=aXjXHj_tC#gi7&1t=g#kJ2RBkgZ*@lO4|Q!(^KX;i zw_;3i+eDsvy9P1FV;L_w40Fn6I!<*p{u zDRy4^94Jzbsayb}ai#7xu@7BX!35(dCTM~)*G__?m?rP-3k=j)4Jw?g14vd%wHk-u zt-QPr3A&ELag(@iVHG=#^Ub$#!@~>a|22b9#Rq{!40 z90vwj$rMoEfG&aMNa$&I`*uzRBl`9S!QuFzn#{Eoa{hJMmyMXp2&N<6Qf#L0ElM3q z?MlJ36MFJj9m~w*G3BrN{*xRKX-{6%CcV-5HU!7q?JJpdl|6IhiQwI#;4CP3aVIyZjp`&Z18iJg6N#@t@0jvSjwi;#x$ldZ8n8 zO0H><4Eir>;Y1*%;s-Rn?~$ZQir z?j&`+5jT3B$5lfB5Hg(E+Rn?@{aN}!nM9yM0SBDVEc?_EV+ib7H8ASAAUHm&@7+Qk z1sSk3?n%!_$TKNnt{Oz+o?gjy`5ej8NKq`>iHI11F3QVFrUk)}pJw`wqTKIGN4t`x zP^-M8)<3TC@wPW58x1O^+GXq2D5Z_o zeWIr-+llKMr=^WSe4mTOGOyV6YL{<7H0Q5J9E>}oG`6cIJbk&vB8HI^;g&8~z} zOtyJn@Gzx`=jbAJk58m}i;Ye=IL{~0%a10!Z&)CPi6X=v{4aUEQ20n-I!`|iLtsko zMIkM+aUvF@MER`2Asvn*{u(3jzZ#WK^`so(zWE4y&M7*V$dX0En?U#VQKrge^qU2K*a1K=TB+>@jgv&B00@0uC&s92(-|`t`InVCjikxiAFu* zMIwj<(Q@t1!Q;rPvOTSIK8bx^QgE4)p-160RgxYrP<)(=#op{&abA%n5fCKf$LnqguuA+WF&kHo%0R5ymcI7K{=Md0wyZ|!l==nO*D*h5p1|YO~;X&O-oQJ;IJo7sTFytPlWlRe}#XfKj_;{kzsS^7#NaJ#$r%DPk9M== zte+1BLBz~ut1eVMuIY#ePzXXAH8N=b4_LyBZ7hV2*Re3lS;nY*d30Ii#zQGVYF#B$ zBl&<8S~p#O?`x|4Im<}%jl|Yq8g-pk`!7A}A2_~^t;)P7z}NBI_+tHzrVD1GO*q2;rM zph_6QHE=rQhQ3l@+_^`ROgd943qz{lZ1Sj{RXc1eT|N(12N>9(i+V?x2(MQs8GaGS ze?}vvB`BxHau$bTk^3ir$xezbAuK||WeDZM7N(J_su9#zR-GWEkHfcRD^J_qS`Q__ zLq;g)U;tg0INzJAQMF%fX`K7PIy_KC4kRfFBh>#Ke+ zyE)`Wyr>5%+}I69`>KGLp(A|EJ@Iw1W$RcRs}4dD}M-VL>B?p z61HQJN!tucMRW0_H?afZ*z3rh?6QFm$+B5KBZ>6)zIb>>gzygFgaI}MGtQOub3&(8 z636-JBYB;BABNB6XHVoDTVo0|J+x2GjO1W~$Xzb~>@Y<0BbCr(;6AQCAu5joRw+2# zW<6Jt1e_iZSU3n#iQqe?Z@aB`tpt!77#<1Cf0hE5rA_+BmL_y}Jp7AJosDTlZ|M^DPB zW}0PZ;HO4Ejb+K>8xDb7C+-lH@a#*>L3}LbZQ~1e4D~g|_owcfGQ8(!^m^f%Jxi#-XB-{^lL+Z1#$1@NG$Pa}`qL9ZF=SAS*oWLB zl_9N_L+Dd8d5&LG9A82B9PbhqR+)*e@P-r{42BgxV+J-7K_|D_je=MVGzc&;z{PR0 z4sQ*Rm^Jg0BcmVG+!g`ln2cjw?dy4cy6<)`s)e3LeWb=aO{K0)5?N zmS(%TJ>H&rSwb9sQd<3asByS$DIe||>6B7Geu}_uBvp;!$%uX6+j4l%TKP6Car5>& zzKRR!pQ|4k|wI4Aea7dw*}sny}2&>^XS9D!Mp_jQex z8;li1sLOaEfwQGo4A~1+Jo+IOz7F0}{FAv=HOlh!j7o)%(h9)0ANr4592EZAYa=5c z8Oj-6SE{`KL-Z2MNF$u*^Q;lk_YTKIPFq0_ElSpYZ)pU*FO4h$c2vL{`o_)JzNG2A zF@|(4UbJcS6w@1a!@1Jd2JO$^QpcDen{O;sgsEU=F>Id9HZN}*e8FviiA6~@ZZoty z%BWBhTd8X88`t z>2UgORu$8DMlGOc1d0T;ciodhquxal7;&MUG;pf9O zd`mcy>*y}Psu;?241vZ8_}D^vNl7wWDo;X9*M#BP14v4ev?-jytaK2nn+HVtPT~c* zmPL-IrPfY|mQbojwbKfoeiFSBGd225_obS|R@Kw4AthI;(Y7GW6evv)qv1Hwic_Rf z{iE&i4r6cpIr+<@*<-YVpnT^?uL~88q!}02&hF`Dl%)RiH=|oh=(uUpg&7v#T{#!g zur~{X^gOVI^g$LdNeYI^l9!`!w{;|Z%t2A^=*(RK*XlL>akkT(XcS9$2`J`>jQB-0 zKgUo=`TZmXrPGFIeL{5vVy!~RI|nP-jRC9t6EJHQu=~AS*cv6<N=f{{5!2ag-gR9wzPO=x(ao@jiTUvf_62EYbOO ze)3jqsT5$0>}*iQiKX_Yh%?yZ zvNp)f$7s4R>Y3eMTh+-DE4~U}Y^63daLW-^!2{}DiOG1`!_Zu*Bv-m@S#xHO|2}w-;=v``}5K_lHe`w*Oe1+l?wxwy~)0$FSwpLxk~4f zUzZAynS>cu>h!AE(^x7Ly|=4)$4|I`x2BW!#-j4jR#oyQ+E&6dIk&FDTC)@KkuX?b zk3j!!v&+Z*gzJ|;n#F6R*arf1X>W}{=;~p$Mh0=FH7WfvA59jd06*AgG~mm7Ggtp@ zc7Sj!Dd!i6?L|LqKZG|W)xu&53{iY07LvouWi|J<12mhfLpKM+pLy13`pk)fo zjU)Bg`%G_dIf+-@Y=oiM_@V|Rp=s&mDGqdiU&pdb-Lm=lSE|Q`mE-@Ju1zvJwuus= z5ndqA($gq*Y%&9UiHHYGI_#7v15qVgqlbq3Z)Qe^WRpoV3f0s%2Kr8ZXJrk=;$VUt zJUY?qMY6MBgyWLS;$3(3U6N(INLxoBBxdStAmuJqW`jKFc6cWD6%JtWk6nhi05<=DX}|U0T~9eHM`}(IxS^3J5LC$HEctYWcjdLC=d3c z!XP_zq&e_ZLqM)Z&NM4nfFRw|5@Rn)ID1DZT)lo?!oZ<0emK@NUKGY1fs(7g%w+{) zvwJ$xrv=k6Oyo(F#(XyjOO*77c6hA$Jn3GtDEh>4+=yr#e4drsFz}hIBxrC97jv0C zEyVIh!9x?YYtSIup-B{EkG-bY(g0#tVo-#fUS%umYj)gVbqKoaCdR5ec)vYuRyA6E zpg{7-p<3jU)Q_M$>I=x~oNPgNBlVWNSUHfXk$;QLV`gmiAPDGF6WvW`EYrQG#8HcO z>X3LVMq#TsbHEVEbmYR4@%UMa|p&VRWxv6Jv#guMf0wSm@-qKb>3=2OLLkb zbU$&#TMJ6;bZ_n8@FC^VC{VgSQuRXZV*%u0H7Ss{xltz`z?n0MQdbqVf@0@HSz3X~~CQHd+=<2E>cOim{~vQpeU)fQM{sf8z5x@teQZXx{D=fB%)iWBHeA z%XLr-dD4agmsnIwpF2M!iSk#))OgAR$RxE4JIoQy81bJJ)!YT~!Z9rrDE@Yk!6wc^ zLz$YU1cOC^bUa2F6xJuAGBI3M>L8@2b!+88b`GX8?m|+M8QfXlNdoVhf1c3Bs7dgd znk3pirBjs^@K^H&6qEkiWJ29D)_+A?dwsNnboyT=TdPV`(IN;o4B^K$7uN z4_8|Cyfp`5u0$VGp&v?7oP_lCiD2bdk_7GgvToQ{+#GKP2XuUC4s0*l{6ps)Lh;1S zk_a-t%;eBC*K&ESu$>`NG@`t9ZlEBZXmtjYe5mrVBSi(R_(jtul7|L_&KXn?|EXBw-KfsGVbA3I>Y6B ze)@Pd(X!uhD_Ac}B<@4zV?7p^1m^uE-$8Hb${}xyN-*ICR!jKLW}r2ivn9Jxqa{9a z&9V~~QT=9r^FBjIG}Zv>qGii=F-Z`YB{)G$JL|Ff39s93RxK`k`S14B2FVc1l{ zAZu=v|MJB##q8vFEIC?DL%JkJ3AW~)Ggze8S9MJ|5f8GURs!YYpFj=IK-Ls2?n_@4xVeafvB`EGtJ+6Y^D`h@3#!H}ufpNP!jW>o{SEv{7B8XunL&=)!wi}6O%t{TRw zBy<=GKow?_6~r@nmpDq4qIdUCz+2e7vv2gzi`H)*u`aEC>`3?EN9I7f3rxlFBSq$mlE&h0u}oNX>CVvX<|CNC zRP!PmWdhU8AR1s0uCU(R56`p*n|0CM^CtAA-G8?yZSjCcmZY_jG7!j+djY-wGP|Mm zzQXsEly~kNljV2BX8Qy{bSkFA+ZC$uBxSQ70MVLT5(?0@iFNZ2hybbHx{XpyjVx% z3?L=qgeD{UbJK2w%;w_$gI3=ZS4&0mT<4dI48JNlJNpr~6xA~jK2J#WJ?-B4*ln>y zKlqkP7LT1$q7n~yrWwz<+b4*8j05e#E;yh*xK0qqrr#GnXtTj4&tpoTo*d*W;Iph~ zGQupHh!%JPgR2Dj0Atl_sp%Io(MY$P_Bng)->rm{qS;Q(yPaz$?T&e(%B>^LDb zrBHET=!p!oC6ESh+KxXJz(J=q|3rw${y)1&Jczr&;K&@v;3`aId#>hZDW&2S%Vs&qyqp!r2tp$v(? zn_DEh4-&j1CL~i~q+KERzO^&khghwKqdNt`cPuIkhcgTMmFP*$%F3gqT8**CQe_N` z50a1Pnrp+4B&les3r`0(xxXAHA)RnS+^vCYjgLLBE&O-DqfUvy9h$cl6(#nK{aoE?2*tU&}2< z=l+HlFyHC(7gCHOo>P|{E3=4g4dd9|#B~)veC;m;u&lEg7_HYDjt&nNMgyFnj~Qfs zsU%fNXmoVqqiO)`&qH)cmfR-kdyN{V_-ri%VsQ}sv2sqMfk~ksi;tMgilbhKdE7M= zp{dw4=7acv88O42gR$PP)O~RJqQsmjlfa+x{E5iXHbs$=;94W$_p+vu{WNUO4{nX| z5l#f~9HG@Dd|!U(@lkFwOt|S}c+G(~fqly^; diff --git a/pkg/webui/ui/build/logo512.png b/pkg/webui/ui/build/logo512.png deleted file mode 100644 index 2bc3b0b03aa840a3beb5879dafeaa47c65dd1ce7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26403 zcmb@tbySpV8#nsQFvt+XP?EwBItWTBEi#0pgft={NOw0gC@~-UG=g*qBB3JP zUDDkR=V9;te&0F&pS4)RTHencSI6%PRa23>PDD=x0Kjzxc^P#8fP#NQ0r)lW*Pdtp z3HS@*qAn)|6m>E#0{{|Gkdf5%G+cX6=*ehy-iFz~at5Z`v?_OuL~udNT{`B}ZzZ>fR!~15M%|Isaeo4hYkDnOKLs54Xux3l<%4D)+XpVYbHMm`#Vy<`U6?O1LIc zEv@UOp2=C4Gj?`p%)7nv?@xR^EC3h21wf|bs0IVDFj!f|ILYi80mY|Y9iq*V8b0;j z@#q_HY@;j;3Gh-N(}94R3OyQU!hb{Q%U7 z6@VbJ?K2W|5tf#hozX?Jf+Z(Se`Pk^W-qY1e^6mKG6)=dv_}!&jK!KKeIf6$=i|ug zqOhH4IDr#=V4qs zsgcZmUk6MuSwBNNm--`4Tg(Vm!JEc|BBIF}h#`P{hsjK>PrXf&4R5;S=<=L)_jdnf z^1{`+cjCu_fcfih3&^`h!cfQnVIZbo?LF-d0fZU$p#3hz+vvVMuj^BpE3WZTw$1Mb z%jcVgSy>;V(6oxe(10+Ishq4$Py|96UHXg}X@WZ4KPbnG%ttw3*_M7#OOv)R95r4_*O#yW<3hoC?*i_o1q=`OL>1rqyGIhZURtw z>9HbfF#U@afU~CpFmw|EFdrg~-gCo9=#eStJZ^2!*EMjAuQ6OM1Njq*+NewdhYZ*3{tU(olg*lgd5Kb%T$ z$6%s((yFMYiJ8MC(^)||3&RxyusX_|TDDqz=I8azwIN$~Etn90gbq@WLR7MpD2WfMu{E`vw~{t_>zlWw8vrPUU8Hoam))UmovdczD{x zjkR#)g@J@C##19Fzk$UHLEY?1RTk>@utKYrg~3__-4hlD-0YzZ=Zhlz%&U7VtN8jt zvOWUAH{n?HB}i}Hn@9u#Lfv&CB>VK|1LBYO=Y%TzN}0WUED`RIz%oDuZA=LYIxP_b z{J`Lw)?)ZPp5x7$b{~dsa#~O(Vb_ImJB~J;{R^9QKbT_3fsgh_$&qF=Wi?a;?71c52R@!AOk_Dj_2WoIeg!HI01#Wkw;inR$!T&!Hs=gI`k-y*!!(Y4#s9u|QE;93SsEYSD_iv^(N+N-tU!}5@#IG64AQHS0e(jh*- zPdnQ%k8gf?v_5)62wEe14G%FK2*7|~C=7;>pWVNl9Io4do~_njb{{f1Uk<*60}yFq zn*Sp?t60RxGc!I_ZZgL>i?L~{sKH3hXGk#PVw1l~eup#ZwH;)>nH z^~+m%r%j2BQQw0OuJ2v^o{t>*VuZU14E;0)pu>9j2vUhoP8c5|2jj#@-K_r3ijWrs zEr#-`M1vD&mPW2WLFPw4LAS*5`s;{9Am&^|df(*AW6Tpe5P~=B_LvJ9Ao*C^hOvmn zoL#*3=(*4QZQkpdBVprxQQB9H7FWVaf<$6Ln`ed_yG|#or1K?xAj@RFg<2a89Ld57 zkXR3h@ijF1iVWmb?@zISVhP?@3&Wx{v^3&fTz~B|S z0F|SK4O3T4`r6UHMc~0^pMa-yPCT~zNYQA9b#yd6m0;||QDDH+w>SAXNkYLF2&c0E zO*7#f+o174Try=gMT5c^h(`}a{(qNZ&F42*XDvdgxIex)(x{kpR+D-oH*&>O7*;LS zG2taN+Ys|BJ=spKNRj__x{Ye$kv;M4iaI7&#v_6NcF>*5OC>%vwS+m+A$9wQFUS&j z>QHaM3w%9^u*SQcuD4*qI{Iju_m^u|?wCElb&TG_ylaqckTA{ngW6z}!ly+xg8JGM z`wFkfVLIM#{l0(bKkJ%?Hr|J`|8E;T>OKe? zP(+`~)R;9FyqX-3`#Jmc0UQRJ6C|?OF~t@-jE@+@q@KS4WdNym8zj645CDLbfCv7z ztbWfIOuPrz0p$v`!4L#I80|N@vHGRHgF|=n1JC16)hw`_~p4^dP4SW0+iQosN zG&+Fo`B%6xO`}nR)4^sGbAjalG5uH8(4#8+C<4D8Um|-i5|M!9MP6AeI2%+OKUTyWa?t)hQ|NXyqoqs6r#Ge$(?aCz2tVz7Jlj~sttq+_|xan3b;0{SWd{5p` zo?iLH-98{5B#t3SFVBzH7!ZyM0JKqrH}zc1A}Ag?9Rl8)*}E7Y5&|nb7`NoyS-i=u zy2lU4%2YD9gvf(DkQs)@U-2LZG&(#?Zbp6K4Vs3b)Zm`}pkf}~g8LvG!FB2U9I59z z=7H2mWEi+7N6Z!;lo}qM)>mW0*K&cUc7A0WU9o6Skn<0}4i=2-$@tzTUtjIP=gS}y z?h&uBf4(q4u-4BCI=PEet+iRP2@m`BF>5!f5${`SL-tHR-?)mSMB~V>A51SA$+*@R zGDz$j6H{jFcpxrP5jkCpk@vDZ1BC5nUe*r@)HUmip(>snT9b)Y6Iv!z$Kit8-o`v?=}6+^aiu8 z%T2fEf;mjs%z0tfg=>qnT61?jH0m5iYf6-O`)33=q5F1)&q7P3sJ7{PGG5uI-gs=Q zWg-Ra_2kxwEVzEi;L5UMUEGX1*6AW0v8`ZNR*`SM4|#rVn94xe`^}48`iAXLiyYp# zFzn#q$z7+4hPw(`y=ys$MnFEfMDz#dfHCduP|hQe18J0maK2HTWmeU$1(f{5H- zL0&K)(4l;KUHCi-sJI+R3nq4`&N``|VRrxZoKOi&0Rk7~-PvFMg+kCW<=PuI^KDVT zhtn^dn;;rl_IB@V-V4|WsbmfH7ot5EzKqqBq&R+Yq`d+8toK>HHuhHMqlV(#hlir!uyPO+OfswwypRCj_(KN=^rTmQY0+<|fFq)sDpBeM92=-#=%$(|sJiZh15 z6iz}lP4KY^LTN}@0s>x*kF`%u5Fw<`(P%hedr3^2DR0nh9xQ_Cwzn3!qyCN@U-c7! z>zF>Er*BK$-Bkv$@s$o}45o-8JD?ZnIH@N*+u43}58wv3nJB8!{Z!YrFXMc$P zR@i?s(bi~jrnm*H>{hcWg)QoGR8{Is z{fcaeo@mPq0%n1hk*7D6e;t>jO`FypIjK9(0U)-+0?Z?0ITojNlbSjD+qxVik<^$gpF z6~Immq@Me{Pj*}@^C=on>s_{Z9i|USAM9C*4QlP_voLy1s=q?adk zQD*lvp}uEJqh1@$-b(KF7jSbaAscM^K4`HE<^NK@p7+Amvg<2$ynQucn``|^`{BGY z??_VUWK!PL93XmF+$SCITO_-5(MTz65AJ8;eb-2vS`4C=nC3-jk-I9L^ngY()%Vz8 zZDSRTa5jF=b7zp$QL%!_*~ds3X4|0KG|{DeJZ{PG$q`J(MkI$d@nPaX( z^SRRtz`v}pviUd^*W?*8FR#5#|K!}4M&H5fKJC=jN)Q$&r;)h1;^2@vd&}2SaH56D z`khDll;>l8A(QQPtS%f2Rya^DhTe@1I@D1dk33c*D>^!|$ud)jPwJa+JRs0USLzhRtOmTcTxkAnE|4`VIw!mv{_*T04) zGI~Pa7jMoqNuR9*N%R$VJzQ(sRM!`(*qQ?>X5p3z$3*1ocRDlcJ|Ka=D-q^N^QEEd z_2ya)hb*oe-%*D)7d>I~4>ofNta?&-sz{xFNuqkTPZ_1|V&w+=D?3ygMP@>e zyJLFWjO182RWEXd{2Uk}v@tt4Sy>1vqV=o5*7B9io|75$O=l(d}wcGUwP;N#M{F z&+6AcR86griIX_mmTT<#V|E}O&QI{6>UMyOsJ~3Rc8H71+1s&A6^+QgbLz?V(I~ju z`B82euywz#>2q5^CK4Yla>i`r7hL3b=1-7hTf^S+M$YX)#}>w>AbW{-MHwCMbseB`>7v;8C7X-f(n3j8s*b9SCc14m40 zze&}bEhWvL%?CZwe&PkzM}Eyk1aOiSlfCRsvQF?~BlnZ8X@=bn)T4y=W#e9(IGq#LTTCyV+Z$%6YVAyj_&H`3hdLJnj2x8z1i z`{?kalp4iJ`w%s)Va_|i*Ad&5+@}MNy;{4xZo72Vq+Zh+5EgPQ`Krm;bL8%zTBq)Y zHJE;Cl55>Gc)dT_)%%TCgvM)Mx0DoDih99FmqET4qsc=Ar^mbA6k4&9wbq8Fna)!R z7na8?>puiZe{e~a90YCcJeJ7TN$?(tFp(Sf-ZkL7RU`! z9+QyK)WP;UEk&Wi5$GnK7>OoDL2DJ!c7Gugu#yD1ho{q9IYT;owN2Y{vxM6imBEvMR7oP!Js#51-eAU^?gy=_w}21?{E$Z82`FnR_Aq}FZr%la#63wdgD@_ z@Z%e8PjyKHCpX6k>lr2V!fsRDrdG7A#{5SF1u9iOFt(+As{YRW>)E}j6DGb21NQIu1X|1C@{Y^uRc~V7?Lqf&HxEAL zwyA8HD00>6#6G$dO|1CGCG=%IWp9rs2Nal1Bv_~0FsZ6!4f`?KkRRX2Cm#Ve<+!${}N1E~#W;acOheW0V}7OY5ECB^qkwxV`*8#$eTAP2AU} zX5qo6P;l+d*{YUOVjOi0abqKVnf zlDyzRPg)Idt}K#~5IJrqp-R5tzap7@**o{aR*TMDwMD^}iU87xGpBMj{oMS{H02rN zf}Exczmae<|0(n8uL(PhfjqI%X$Y6TR%8-bEE%OgpRFxZvO0?H5WhA1_*Vj+Q^u|L zH6`)c>AX8nkfa2TT2nC#)mAaI)?T}Z%}c@xy>D5se@TQqdR7w7y;RqubsgNrQOl4q z7 z{1IhJ>&2RWlEetZW>@$K(kFJ=$C?Z^)CT{Ah365LTl&9}7FgRBwjpz0MYxT6 zyEg6YdI#p1P)Bf;#zloqEG7K#A>EIk)L@6_kXq%8rYM|UkK`k*A=}r7Dc*U7qhl3@ zo>sE-x%T}pTqoaAiq8X(X~Gm*wsAbQB-8WBknU=NwC7$gzEInT(ANS*TSWQ=E8Jjm zTTGs_BtJlxtNX4Izt*+_-#}u+M3rfRiAyWV?-n=YLD{9FypGd6k?MP;=%;;4nK|2U zOmFJc`R)^m*3Pv>4mp$+w+%*Ee`zgtQ9%#9uH__a_wQ9d z00EF6J{@>%JUm<^?8|tw{`4a9#|M|q^0I=qu{Wbfs0UwnZNRcSZADv3TE23{)5JW-U z<(_R>lhlZ%Co(^ZjC87^+}a^<=%sF`9W%fh=CeQ_X1cu0(W@3@-B|bg9Lb6^3QR(D zxtd0md+idnF0D+nwV+iAK*WV`jNRJ{BK8Bx0&rF#fR*t^?XuUg~lM{WTdIY~};l-Pa zvLk`YBqx2LI8Ullelg(Rc1MpLs`$fF^xImEcHMf`FpH^Up$^ZN_APFH+{WQ!N#?hL zC-nn*p5?kXh5>Lon=?h&&bh{uozjAp~vgQ+S%|8YAv>@|sgkQx@o-ik~RbG>8c4 z@=VQpFUTw@^kF+9Z1BT1|MxnwuKtoj{(5}y8&$W{-vJz>HBaYjgCd#P)TjXm4&_$c>}I7jMOLZ9S?bt;_gd!EuB2jG&|@ye0gAC zJ7DdtVRyg+^rzmR(L3}|NMD|`u?lMQz>ka7T*qT!EI@~VHR2bXvx=pEcDl}gOPu~l zJA=jfNG5wjrw<0-Tfu##FZ9%SejJo?XD8<+D^nc0+`-Bh+L-WrD3|^jkSViz{a3N( z<*FxPG%jHpg<<6gUNl|)xv>%w)qW62(}Q83hXLGpM-C@wrV6Gyx(V&W!s`i+ow@!S zce?`UvfO!=`Yz^1AS6QwwZAWP)4*Dk$g_nsET`C`3$BO|0%{L27GRdorzyY6bX>@? zy+hV_;jjlIp*(cHuJd6o=V)f@xh*=le6WBoToFU|v!0_;qTFpRB_s9LK$LkB7tT^% zeO0H>UiLv|I>UYHKlT=cm4-1f-WqA>poA!9y;|_3$hqFvv zvk3ZMc*1I}oEHT)*PBAQ4$Wfv{o_})_hE9sZesZzN5}%5PI$vd%q>nXLM^`B`4t5> zmg%B)HN6xrA*{VIvS>Jf_hQ&@jmL1}=E%LHX={h5^w=z1;QaT{Nw7t0pmkSY4+4-Q zE~?fm=jE-%=b=WdSPp^7!ECp{G{1+eQb2QN)VUUm4`xnzO5ct1jbRo(`n}l=5%Nj& zqMbckX`TW`*wt_@N?SGo+Z14S`AY5Bn08G}Nd}MS=j!IG% zUKBFIgtJRQf9Lb}r|iYu&Tp&99SK?$4U5S>OD?|M-PWL9cm*(?|Rzi1Q zl;RzxVA7483*=V%K^r-1qu%99YG5g?@1opLvDtstRzUlg4ATBSlNk6SFfz#hIhAN- z8U4l-S{3>S!U+cmqS+1jxiRa4cPdWs6#Ar};UREiiJ0pCVM451Euew36#T5G0975X zamtBF3W<9FL(8W>4w<=1#p`~^JQ(6V%UWtbfXxN)N(IreB|wb};g$yI=9M3Tp}Mc& zj#nrFjp)kZ>bLtZ&`uAfDMTKAB@%io(Ar0{S-hXH?No5D0-8Qv@T}c8nB0#qGUAPo z@*QpZiML4?Qsb+8>S=@K+-xd8>V~gb;X%434DLDH2%RaJm+Uk`d=uF|728j^|7z>r z>n>eltQ%{3Bklp6F&lvgY%8c&h^K>p>UKs~btc`J^ z@$1dL%BOuj4yy-rQBZDUnaW?+n2$AYV|n70=>{t2KPL#MX(bs}adqb_d)Xv-F_XV% zvPU60cp%ytyTp_cMko7hwSKHHbTy%y-TlMA5BI~?u1or-P&CrC2R~tdZ+7;|G_z-5 zXF(^lr)xV&I|7($8PuQDyl2C6d|MVM2zkxmR2QzWCy0PB9H47N%*)0*X@6v3~?I&!73-lXz{je?#V0+?M3k*f1jGQB6}{kQHl=kYqA>bD)j9!zU(7NQu)~r>Mw_& z9=dDRU1M2pM*)`K-t%=*sHgr`xr%S{YI2+j723tEEY9msQ* zr|2Yiwj5hUu-Ve2*FI|z0WuCRY-lWcQ33OVHHLd${mHVDAWoQ zDIALp`5oW`YW#F}lpkpQ7!KQzK$P%3 z8gJAD0?NSd=-^sG`!Sktka3Y$6rSkfKI3@1cEGHzk+A! zzO^;ksn1n5PU@{u?2WJl!~K}I4~O1Uwmh0TVKudlD`O7yL}{DI7~NUg#;9Ou?E(RK z^jGuwNw(QBd|GIc5C{d}Vg1+4Mwh(c`Q!*Do5G2Tm32m(2N z@!Upx!rg@pA_LuOZ7g|bBxnWo;JKdB( z^CU8g9zu$&PrjT^)R1Je1kBWPHl^3I?bZ7znKgAi;kMgkDp?Oy*XxaE%XCaNVqXQJ z0r}yw(Yk7JQ%!%086;t!26vXQzsQUQ_IuQe_h2eP=_h_9uMpNqqVLdoE7v5+CJ2bG z7MnK+5Ztw%bq)G!rI=JLXs?{-L;(Rciq*S+Fhchsi$>{_6r8nIio?i`SvdE-m*EUA+li0Qu7F^# z`qXU7FJj2WbiVhqsfv)YrgvJ`2T-1x|K$}@M3UOt>=t(MwSGe~=m+gAe|5u4ue{%J$0S^i;yGH4uDZ~58=q|EAAPucj`bx(NA zQ|MLq2Ps9LgNCBy59-$WrzjP!P1!r!C^6M)9FtvR-{POqubf&Z9HVz%MsyYx?=RSx{ZT3BxvS>^`Cs9b z&gZU@O1NSh_1-dBdkonQJQ9>3N879e-%06_?@BjxzQl?Cg2$N3G~8Qi{Y4(QA@m6> z{qPjTJrNQ4^;nR+cw>lvLd98iHaXdKpA>9=Rtkrfqsq)jEPoByI zE$a_U5M9yLyd+QOyd8~K=_XAyqmI#WuT^*3_hGWe)hpz^vFmfP-^G|3aS)CFgQYRK z9&ms0&v-2={)EQSR~EU0O182yCveKss79ss<#m3xY|&g#9rsL_-PKy4mDyOPn6mrc zoUo(Rww0&`LI?=$d@*KGHZ}w5snl4rdjUBWB?6%1$9!P&VhrIGwrdNr^u@QZaG#)ggk+k~2$tjiBdw2)E_PYPLa*YlYyFfPsMJ#e z4YfuRP9>^Nf)$y$$7D{q1a3#**VAb)3dAk#s;4LxQLQ!dsFrwj)Cgs^_|nVVApy4p zFD6vYkNkQH_f-3ml<+sNIt7YYe-l?yEiSq}C?8*Lxgod>!Xglx7ex6grGHdIEpvE7 z!bFKoxM~NJ1%jV0Z-%jMzY{!_(0B0f&va@qPc~CwVLfwyj}zWb^H{k@6J}0=*=7NE zNHYvpC+sZvO`!cukfZ)kdzYTU@BD-B^G6=GVcRm7lQqpF!$HuBtkqHT4}ZK?0@NPC zb_l!%2P5$Qs#4O)DuY_MLdrpFRErWy?mhFu*P!{pk4hHdINu{%5a@Jazt(B#Fu`Z= z(t%Kw{TiuF@AAC`0Ts71Z_pR6E#$u$VCK#E&c|USLkMOx0Yb0BkVyz;kSA8I2s6+b zF4AT8NFHeqqB5CgHE-a}Uw#@AZ_Iu+;+>&X7lNz7Jw4|Co@q0Q{pod+uY!&|U{ThO z@bp^yQY-ZBV-Q$@P`Ll`o2H+-fz(*gSJTJXO_SAo4y}3YRFw1}wxe6EO-${-cKEEp ztvo!40-RT{qw~R>xtzl2M{9Znkms(_Teo_5*Iwm;Lhl|3?&VOy);p|rjBy#ldJ{qM z!?H)<^yxu9f0;@65->*wL!*HJr2i{UWCHJKk?ldR&T;A-1doz2Gd5` z5*H%x-H9EGFcK{`0FwT!jp~mMXlm*qwRhf$s$UD>AgnW4ZJm628$0|Tn-#TgGO@C2 ziA9Gu!-^+(oLo%e=wayaQLo|Ubp^C2NNLNj1-HPGrniY}Xu&ZH4+8!g9ZGYk zKl}hMYLFC9(8LxEqQmjX?||)>f9jDts@Jv&pbBhT4xrCaeAbhr!vreP&K_$|l4XJy zB{}}Zha(wj+3jA0yxWlNlv5}5rcx||LV+EmxJ-q?WX84<#LMUPpFo6X(_n;jkmoB( z#MTu;1%-b129>T?EkU`>*5VT^hrmUmm1}&u>soDfT_0#LHtf}E+Ne^(&y zkimp|+QAZS6oJM&EIJ*uXwVkoMSsHS7_8B&;;0z(5bnimn)mK^{CDWhC=sCB~Bm^=-7#G&^ z9IbDXi|;|2nQe7IHJziD{gc<>I)+M(8(mSME?$+46gRurbVXXcZ!jTI$i z(?AS^*e~ESt02r#0V!A*SA!T|Lb>@wrxJr_avQ(MJ%b^ke>dO$rkiayn%`hKjJSgO z-Zqf~Sds4c;xmxG$4Wid*jcV%P3U)yx5ywkz{{IF-hEBV!1WC{qU)zJlG44zvXHGgXfpu&}2qKlhyWkA-mTVGQr3-ed}9o$m4lBT8b z>X}dFLBt?drc6)wEdpp#YkJ#!b&x%&I;C9tAit5hj8Et;T$S&(+E+KWn{y{>^zZ`E zq+%uIVF9I^HRh#e0|sgA8M;){LvBl@9nJ&TtNwhymot;BC9T6*tk9&n~9lu_2 zX*y{fcSr|bhD6!~3=P;n4}2pE`Ulub1j1O=a`fZ}uJG)bvlx@>kmxv-OcfB^j{g5) zsaQZw9SDwat@j`+enJ9iT^vhFD5cQgAyU2zzP-8PkzSBZf?ksgPt1S|^MJFykou#w z7er7+5Y48Rp)B9a1F^k4z4h*{&+}{Y+6}SK?!nONVrj)SEC9uzW7@nFa0G+9+ybA( z(Q})9IRq{3<5QD=#BWHjMqtuptF)XfenZf3`&rX(*=jca7pioswCi*<#3hW!&%W9Ya-r(8*fc zug}<(IVn(AxZgkVym@_HCu)t;y&=VsghK)bt&?fqv&Sm2D3Tjb<{D%Z zTvJ#2+AI*ZQZU|^dITZ=jO|8l(FI%qWlBfxHglLu-2o+9v7Bq<*Z(qORY81Bp8El= zcgh!Eh4vZ7gTS?G(A6u0bK}!(5a^>yp3Ot?RB=V-EhF4k(O6(E>&ZteZ-^rrEelmA zx+<*Egy#g{t{+0yB?@HrgF;*T9{DytV28T7JZt!+VarLrPOB~-2npbTyOQ_xg~ulo zB`se@vO=0&RY28LwU7}a@9FF;6ny?mq|hY%LOa&~q{=H|H_Y%H_5u|I!M6nG|f^F}Ml!?i2^SL`TAKFxv=@ z%KH{TK0w)X5Flr0j* z2ZSP6bT6Uf>=(Wag3#d~Ev_d~hXN{E%feg*es1ffq}=;)krv@9EE$x6BBZ~b{tssH z$&o4y$Vut*rd2Iz2-ebk=OnzCuYSiJQ_{ScOK{^6rk* zd1C7|UYSWm3Z(|-HU&L6w0p!PX+G9qRL*jpuisHT zuRohg4YRrM$dZs<=Cc{QF0Z-=I zvmZeu;QSB>rTv5|{xcXsF8T7#^DAm*$*iFy*@@Hs{SX-~15M3yU+d3?PdugJ1Xk{r zzM<4Ym5AifU40AwKyPm`gOw>7YH1uGystAvmZw+a8=v!On&7_$=yV5Yg+2Uu<6!+0 zx5O?*r}|Ll)Bkn{pfiXZ1rM_1p?CKr^m=#GcpxC^c#1M}?x9==3mgRTUj1NE&^y`! zHEj3i@l{)8Ce!dh6t;~_S;|mjF&+c~+64d-4=2gb`#4cC90{VziAQaQ2g`PGc8`=U zJ3s^xSNo;XrtcZ#e0A=yi7P+*2y-xUpJ|_>yN?tsgnu)-Sg%gR7DN8qDhZ4fGczSB zh5QbcEC>~Li%|Z`H@}?KpZ!TNz6fjK8I3hR`%&mJ1O=1eM4TmaWk500e@}hM#%t28 zR%6^?x-?u*MtG}S&d@)`|L8AXld0Q(Gh_>`C0ti;H0m3*xO){A|3kz$mg1oW{hqiI zO3Sg1$~e}=`TR#&rNk!8NplB7kVt%VYT6=C%L63gzpw3rxsUlOiQ4QvExRQ3>0Njo ztS%bAv&Qx02hG$enX(@T&)rW9a&RaVeiq0gA}o)ps30Hc-0|-Hv~L2d%**E zB6Mm%c)LZVLWwoRhvM4`mhV3v7qB=EY*SkGyVtgUXuZE#_hbt5^1~NVt_d>Ubf9st zf#-nt+CTh(;~Mv~pQCGSMfj1M&Q<4IvA6aHPVINzl3>0M>dPK3rW=7M(&{$c(aa;( zA5{O*7m9M+CycfHV(0#O2haEH%#Fs!@^~eFZ`h`*xA5}lwW-dc-wcPS+f=5dnp=b^ zLpE`m@9wSS}K`3`;jG{pO(#RQg{YfavjQoZZl5>%lOALsi*cg|Gjd z=0^$Xvq3+hp4GIw+X*C3e+@>s8nyZn0Ui98N;3Lh5!b+Z%YU9BFB;HJCLR$vvWsS+ zT^V)$aK2Yb+?@tAjx<>GU(JABEep6L{~6T~9z1yD6Z~CFNh19X&G|T&w*6}BZB^M( z8KD2!!YK*Q$u<$4`+xdWOc+o|a6M~mrD6R|_>uuu83t^e9S$(lzW!H}z=@>=A(fA= zd0k37)VcL1QV3lT^kR5p5sN;6SI9 zmPITd4BE(NfA@bBoraG){>CpW{o%m?PN2;b4H{k3EyYWpcZm(%PI=9B{evVp-5EaY zYWNBN;L`%;AX4+H$kLmh_aj$c2w}R73DPZoM>+HUK4$}Dzt>f{V^gfvXxo1?qa2(V z(EhZYdh|!85&iZf#Xl&Q;oDuSBkTkvAg<8|_j8PlQo7}}F|zmr&@efu|7|`Lqo6 zspMZ+LNUdq^9z&qxeR`~DRU;xzk8oJFk6|(0L>M4pQbW`$7V%()jZ_eK{5}d;#7|m zm;w}88@~V?Q!ris2fQ%!j=54+*roMqE4d`9KyM*JW-lUruCYNAwo_sMghVt`wB0v` z@+K8H7!ySkaL?vi-_1Fm)k+Qs)VSjckg=W2u%AD(hn;xoD-tGEOnC!;f6J6~&wmeD z+OxBe7TWVxvK6~|gb=p<%hdl+uv9^k*{>#!TlYv~DyWDYNFI2m+I-{a;2HsPIv&Ut zto_wppZun*Z^xX5FYm@O<+o2P-J3o=KRVUgQyNgRZ9z9_w)k z2q`#8MHmRoofSxFw4b3&T$}TA{=w^knN&71Q!fjR**rQCKoiYmKIf6$tQdZbEAH4b6nJkc?q3~zI_+vRN;3b%Y zAob5Z*>hW>hI=Hn(CHC1xYB$ZqXh{}-aPiB-isoK2RH1$U>U;2Z9_|WX z{|YSE=PwEf-iX!QYb`gSK}T$$me(4-L_R39u&D_fm|v`&CWBqOn&pHnCjzE>u~Mx^ zf{4Of>GS%VEUi$C|3gn7RJ4ihbGgsQk2aZR&Gc-NFfV1~+VVLy+5Vl%;oFIwJyW`= zd=C@;RG~@nJ2#d$EUO7GWDv~#&^whFEN zws{Q%iZMKU@n!yvT|K^P4+G${ezF1`{2vxzqV&K-D=YEG2Fa{~0``7khwoeAh!}ox zcItJ{{o|KR4LuS1}@A$*1^bBgf zkUYH_{&kJo;?VG=P5cRA-TFV7U9`18&tdu(lH&8tLLRWx#+*==FJz;etB8#cTP$_n z5l+t?l_K+U@ou)~kK8vVjCF%;dA)U^LIP*|k4yhxD0&vpy#mTPVS{y~v`}=s`|+RH zl2`AoiVm0Y7Z87XJ3(f=8|>FxkPzvynrT|QNUap6C*A4GiOrG8Q$W6IHA_h>9 z0@nkHJm+Gsxiud+bgrK6gL554gA zspx)!leNxA*@L5kpF^lMAaIDX!}?r|oPp9%MutM!`23t70c89NUXSYQbB_%mGsTUw z08)YLyi}%Z4&bRTNE1zy0O+$x-w@gvI##G|@OlOhWDA_wqH<@&EhaUFRM^|Dj?f2x z0P{FcS(+Rce|!YTr2l&xFBdvK`WwowGH%M)o;LT^;jgxbBc$kK%yi=dy&2UtaMDXt zj+~Znh@x>(rc(h-go}9ogCZMzOJc;tP%fQ|1OU->di8zz3S@MmU$(!-6J;^hY6`U5 zvSx^saNWIzFA2y@HQyiA_jp1xqTgfjisXci7YkU{hvE!iu3yPHF479SJT)r{V%~vp zmVDgSnJb-78n^a(opCRf@3DM0W8poaB;f|VSDMjH!7wHe`*JyWX~eJw82e~^)q8P9 zF4p&|?Le{rhV7+Nf4H)S*U(@@m%{rOO)-51gBS|FhlaW_-yG_cDslnK`-$Xj*Ka;O zZ11GKW73@2iMM?zhfXp5Aa&@ipgAQCgR9TW=! zLlt*O#u|cLf1uuM=4`F4$2?%kj~on$&Ro9JHGI0?=bU+#bel)I!+IhKSO2!|_k2zj zkJES>a1@9WR3mbZLrmQjUepjxc~%tZ5M-?r{5nze{0BF7ygsk|4zs;S70}TJ2{Z%dqpn91k{wo@ol6>4QZ8Kg}NUAv$o*`5cS0n*)Iv z$1L4x98B`S^mnvBPOP|4LBc@bixX2(gorWqe9Zmb(uVFIVO@`t@Jwpium*rfUB=7TE09H5l>MZ(#Hb(8!H;%>0fszu{^s>qtJW#~z@YdhT-JPn{L{eyaa$ z7HvyES}I`Bva(}iOg8!dGAlScb}O#@60*p7ke!Igf%H-c8%LmI4=Z&%Yua{ zsO%LJ8S;V|wRSwh!Xf*LiI3iKD3PZSJtGLkXKkUm1_`C7XW6k}FHVvYoZ~fJ#gc#~ zD6&~k#_q_dPBQoBKnU8v6TS|E%ipM#ExE17+K+o~f-fFMgKdUf1_Ho@i#To4Dyt-8 zWU&0eDLp<)N6dXyYkKD|&Jqi``p&wf@w*Av)})^%lwqsGKA*nY{rIIUcS<-(osYZD zKHn42B@KPR>aVSQ(d9;z&b~s&{SO}#cUMaTdLN}D4nGay=$CFDHEn-|&S!?(=$1x> zs=?bl5D)dkRk>7bi1!n2PE6`^Xcq4OX5J-f<8a3a%)Iet0I*KtNH-W!RmXBJf@g2A zT4*;aJrt=U%DPB2$|+0MOUKWJmyLF8%Qa>tIAPp)Gr4RH#Y_+R=qCEKi^}JaW$vq; z>C%1ha~$Q_g@)2`dxaNzC)R(DU8T_#KTKZr?)&Qry4-|DHekHWS*J?GJ6p)q}M#aqrS^w2n0;boVsKMkjhl!+!Ev+ z-X*KfJw7+H$d>tSS@3~#Wc+X1uR9)9^9<+%O51q-X)F5e&cKzTw|93hsvT2FZE@gy z!N9--Fx1}VTj9Cz4TkYP)p}(v1F(UyFP*&AvDp2l+NIC-O1wG~8FhTa=Hu;O&Zb=6 z@1Ob%J+352XFRiS(TK;aXb&n>dA8<<+iY+cY?&fT+23nn&VClzot}X&M14c{t)##a zSQTSph4YkEnxP)-sWFZ!b~g1{ENzj3?GHL%#XfWOV7{JXMdLg9nzuI@fR2!>235Qg z3TS=(nIB~|&Y0}FVJZ(4*BCi)NcQj)hpV@idgLzBNzP@_%uWnVj3B*HLkwD*Yvy4)08qKwp;u<4cl|DwLED1F-se1>$q2569}~)>vLP zVYl2MjTXLAiXZt|BM@K^@W*q)Di5Aq0q`_I`t+Y*XWfT%%iUEs+7q1mU(8W1uX63EN)rN zjUFYu(tlk)*qCH)B1ABLnLPbP@J-23I0OfqQypVa8D6l{3!$z)r$jmg1`;(t2b0(< zL+;LeN&b^d{vbK;%7o7w)B5M&bowTD6a7R_*q2Wls*k-&$ldMmhV`ui$Zff|;pX>c zNg>Oz{DGv;mO4e+O#-s@lBj(HfO@bPKULsVhbg7za(f<@C2lVR;VX>cu1LZgbZ^@36HbF{XhHSWeqAiRcMe0+eu>U&! z>5*y#yJZ6QyBPm6O#k-Bd?Y}elzp6mzb0tG33vnl>h!LPE2i_r(#$Xriekr9= z8YLo|?v`3-e8;QMnKGp6Cs_`&k0D_oQ}`FLl@OIHKFpOx7CTJQ(038r{*&zGX*#%2 znsXmObvV~vE+W<2^a2>z&F69c+iJP-*&K23xfy9@aR3X-%nd>gX69>5Ff2JhyPZ^hHzH(36|`DO<)t60fowE}CMuS{ zGC(N2;f_`b(>FarFjRnkPL358?swAVcjkW)nkh;4@^Q3M7D!BQ)?e)KG&!Qyo``o` z7Cp686z$R0fpTavMq+G}O8(fhSR+<|K?StLs*9z4@tg(a;QsDIR}t+6g_iZ_>9#QA zf~O_QrmLlXA}Jdh0v-#5|5?1WhoC>1UJ7__l>KIrDVrqw>Wx5v0#Hg^%QN!YUrQ@g zVSHme$Ns9=kEvzie4MU&`imWw`J3qf^W>ny4yYIh+0kK1~t-fkM0Z!eKht8TZ}7KYgx0pKwxwRV$nb zu$V*?G_NIZDE%_ts;h``a7bQ>#IAQ~w)c7!qxQ1t;1iko=qpm50-Dqn;M63-%z3p< z0FwsT6+D*D7gtH@pycYN(^c1^pj6$Pbj_pmBemY98!P2sMd6?Y30m+4&a?o(73>kd z(7|TnYJsOJxxb4cz7x1#I?w!vc_D{M=;qkJSoU))?W^vYD|97}p;MDy9S&UOv0YCe zYkxkz8*LKLBng7n8|x5Y!YynfiE^@L>kQ+qY&d#lk?i$j1;=fVdxZEJAmn~(3Ef#l z8kr|1@MQrFye_JAV0V+vAJKk!?lfN!s-;Fd6ox%@Yz$?SBrr1rUn~IfI@3`6D~rg? zJb(RHxMRhKfp$Lmj`{Z+#Uq0LUqex`_atk)9R71%&^>C^b|mQZwghX<`MciYmdU7< z$v4p{vV@|Fi{>XB^Hl`urGytzJskI@Ur0gi4b2q=j_b1HMWhjwe#1^o#x2yyFW3-L zikDcTf*%HBKD@2l+)|syKvv=7&{!vZ(H~m;UyI#^F?ZXoxrEMIAqFoN3U~-+WX22d zGJPv*QXS>AoSp^e!)C7Xe|50lpOxSL;J3E*^ZZb;=<=6bE-(zV38KGM7i(0-3*UbF zUW3hWSs$2E^l|%%N!YC+o74R7mkSzQzmvO&f11iX@11Q5J6&q5CB#F~ zgPrgIBLKKMV-P5RWkZZswkEqBWqL&c+8@!m15&DInVo!IxQmhyDP<&9>}ZUDRuB2A zle!Lin0~j$$=~m9v__VJ+<)LZML7(s?V4&tY>jkLxTe%ancX8!D6akp6j6_-20807 zT&N&R-BcwZv4g)tNY@}q#5MppTL|urVaa6n3og56?F5J5$2VFV!Q zw?!o!glWy`XBEz4k-cB*`D7{B{U+J4!K}Fau++Y{HnB@stbFP0(aoBV!y8^Q^zWt- ztJzgL&~whxNvjt?UMQ*mO^msXuhmOBDXCJW46=*j~r?byZWN#R5!?Q3yk!?9=Q|r85d?? zxJLMA9hXss^4=3yhelENt-J6~v<4~Ts;NbRkt``rD3!J06fsLc)@)AzGxPP~v9z3! zs65Jd+tKHGN5ILIdeMMeyl7}mYOXb9SmZF&Smg8CG%dsUqdVm{6G{*^MQ+^eUhEEb zHIw(@kki+ccKy~bo+*@%y+F@wHj3_}IHCn(3bP6;<>m_$W*C|HK1%{1)Cz9rgeY&e zrWym<)Qr-znTz4H$s~PcA~AWtJltv&h;fk*!S|ZrH&3MA1l1l56@Ko`5XdU?Im&Fh zwIqMR_}vI#61`Jl(vFIkyIuOJR~V?%bF+~10fhMebqDSzDGdhwmx`!0kJ>x3b~7yY z!nC%{yiZ8}JGZf9K*TY!DjiBkXr)Y9tOP))P%Sum;6~&3PPQO&!GpOtJPZWmT062W z;c7y6{xnQsaSF+BS`LAlXe2!_10oe_}(fixD`#Z~%l5{9#=+ zFn!CIw~4P^pFY+3vSMoZlO?^9G|>>Ch=w-dR&;}}e;W$N&40nrL5xlu>a;|wiZnT_ zb)ufGv?<44AK-OoFgs8rS+?6IN+pCh`~iB99=~vE7P(Ud^s;)+wAwK;HcMJTMj!0K z_i9vn`{(5*9j!2%CTq6MhrGQv)20oNur6sOp2BT3pN3NIJV-LJ{9{(D-k+NiY&$|sWQwaTq+@^?ndQC|NaGezX`c% zD4hAUvV=}vUKau50W3eMzlU-uzG;^3E?KKKmC-^EO0Oa|l#Tc>tLSyqUa#+-+;dj? zf}CHD{~Wc|JTqP&V5)T7nJA8nfV$V}qQikum6UWTpD7su$#kv@ak!8|ql}VahZBwo z1FPKe;cixEIxw>xYWqgZDoEZrh8ZS;<|r7?Qi|$neN6H8;rXP~S|=^Wsaonn^^5FB!EDE& zz#*pCOc{6wy!IuzPYf+BeS;oH!sAetgyDf z8wSWp$o9049m$^8FQ11$gC2DaX7PL*Hh(aLAarB%a7q37ug1H{pK7e(TxZJoeUg6( zq9sv_g(l7ehLPz@a6yge3M%sF-jukLHACe#Z*de~?ySUITDiKQDU~#|n$DN(%#TF> z^lGgSxPEzkW&U_eMdg)VUkBetvr|WBd4IiMWH$yQ`VQbLE=H zd+Y^_WdAN1cf3u&W#X!b&pcSre&ARp@P-`00u$Mh1-d2{Tm7|-YBpqEV z*yIc~CBk4%w>4#acc*zY&6#+kZUgluAYIghin6ZR^zexv1K{Y(z|mt>KlE7uVO|L- zn^w}vEsW(1y^!rWn*jEKf@$Z+TZ4niEN`Uaw(vM-=w_Emgw|iDdsl% zWu|^f0$D|dh+&kDBVG>9FZ&sR%kZPAi6@Od*S@F^*d{s}bL**4OcYg3GLm8#kt=zD zjz%AW9Afj@fKN*^yi9pQUu$fmO<&8ieQkH?t!TE>D{7553QxkJ-b#82k_QBEbS9qi ztZ7e8KWitB-$^Z&S5YdAIC1VpS-+s#ERrOIS|eTINf?CMe+`K7JZg*I09h3QNvp^8 za`ayYIe~^^D@;>5RHM3>Pcm(GvUt1!|`D)Zc&pNyE&a|N3-QY0#P}8W{AS}{pj6yzM1>q zdm|XI(A3YS9bzAjQZqTzr=6d$F<|a{YcVfP2t2(-Edz0m-(ObbxRCmr`o1|%ME!Jr zdE|zx>cWy{%Z!PCdT;ZXqX4;D!r_K}>Se5*EDfhLxJttAdns#HK6fH>Ssoxipu*8j zWdtO5a}3t&f)VLx!eX=;*=39doh6;Q7gRBAAg@KRZ$z8VG1HPc_gnu~z5P=rJmd~C zc-%w`$91(?HHGN;p~Y65aj}Bww(RI$(A4eGZ|?;FLorWhlT6~^aI*TItO%pZJaJ0? zuhd-j7gl+FI~Ig&YFN*1@4b9fRl+f!NLyD&F>6W9{EszQpMMzb{r9p+z&2#S1f&K< zWX4-tFU`1)0-vM6^ozIJiXGy#w`aU11>6mKd)&0s=`gnEIFwh4kERyBIhD!rI7~TL z_sxz7jmQJ(7PIDgD|Q!^t+T!GVdU)}+mFlMYKvK0#pi0ykVt;=B?T0#`kJAWvV$+{ zFtg6ZwT940W(cYNeh^TLJXKp;1;j_z*na5?bv;*-pzyO0Wvd4g_IP3A`ZD`AFe+q|gO)T&!!U3poUfLNj z#4qi1nCQAC1`RY`2!0lSFhJ?qTgPLhbUoE9)y>%UfFTC{=3*DKedWOviKWQ~h|{pX zkq|9sJ=zq$T@y? zkVHTI@$JAeveL@$mRzA}`$yiwo&YE`PvW1U=}(tmplq6ik{#KeT^pc+F8v2JP~#4q zeBo@PAfpE6oXPRBP%tkP`YEk`C&;Egsz4g7A3X3`@WZu_M(O0EY$_}+A6TF)TTv{j zS{ORi&vvczWXO$9BM7V_0_zw2l@a94Xk zR+2wZZyWc*4cGO?>*vlt2RSyHzx+=GWG~exxW$~c7Z?TGY6N#0Qt0|K*e&|5)>&GQqY`4FF>|+Y)1^4peQLAA>mwwF?`%DjxAWmI0#v$`(Zq{(V0D}W z2(!!%c3(I`N;J0CCk_AgP~Y4zn@f-a-g&-ZdnbMu?XA;&L}=9`6ImYbdgFwsG``Vq zIl{n?vq^4~JQ#8w=q<+PiAQm%Bwx~%9hUFOLzPYO8FU!4mS@fMn6Q?akKb*;C1tQd;oPnEM<~&iNmWd?i#QyvYB7(vhfAe$x}Esa7vHe( z)`M2wsrdipF8L$kDx>(_AT7yfpz%oJNc{5Q)*kq$-x6P*9v2I+FgFf{S|-xX7MwK* z^9-Z8jVOW&@=9@h(hB1EAsmT|d17?EL=PSw2KpUcc2#8=MStEr`Iqu4IbYH>BnI}vt(&EG?aR}nT8Wd>1Bh_`Z zxu8$775f&dSyTX5bkg1J57#Ej{d;Hp!wC?LBXNtf@h& z3T)7xu~YSYnwJsT#^Y&;Wkiv!UGu{J-I2GRJjaGX)oGSZ_Kd%xA@R!}^=Q4q785;g z4xT$$kgj{dE%Uf3t(!JQ2HKg%>JYV@$yv(+gvceA`4O))?REbENGX0x)_dbB%pi66<;TVwIA7Z*s=b#EJrWKsZ2qj*_eXk|VYw1Z z3nx0(>#{&At431O@ykt0$N_9#;fnQOC$XQZ^hQTXw+AFtkd-eo*!V63SC918sY%*@ zk-Uu61ILgT$y00e@9aM1H{=?71};Wnjng?iv8Xez{NnCkkK@UJZMWuAi(>QHwz0ff%9C?i5^>k+G?fvHXk!O_`(+N$ew?7keD8UB|G(EYZx37_f<%JrQ`lKHZz5cBbFJ%_n3pzFS10H1Vd&}FOG3}ljfE@PafE` zJNMY}3`WWu-rkf*MQ-Kz{P`+ri~DE8$q^oX=G$toFek zN?zu9wnHF(A{9(fX1{UtXS0BG_fT>Y7}iTTCuKck=3-S+av@A?#Jb3AJ3JtE4712# z-NO-HJmZgLpAvssCh`3@QF(+Rc)@BZM+;1o)f7IvK3_WPA!xRi14*tgmm{OMXqZ5L(DFT3#ktOVJO%XT=?TFexCNb0M}f zpe>f?dw!fO+2=;8C0q;v#eVU=a9w-I;&U{cGPq_TC*|vyA(w{qN^GU;Z zVc&*r?Z*_uDIXvhn)V@2nr40%?11Z6w+}T%R~$ZchmE2fM_IX0mC#Or*RM~FAf+}2 z3C%8rKJw)qkdQ%oIOjwGcA~N_VzsJ%kRDfVcH_DykS-nikd3XwXi&Ul6|%R}nCoU@l2%*%CaYe+jRjeI5J z;7r*#M(a09DrV9FnfMDKN^};F_DMAi)g$rNZvM)R-ijQ1)%E@lj1d341Z+pO%tw5S z#vj})?L9a7LjrklFr~HQ0{dfgjGH#Z8F&=$2EkY;zj<#uO7Fu-jg^s6rIoQ*X)KCN kC)Jgl7IyDt1M(=32|CI*DI7{v>LBo^D2GCp%9sWI59Y;SdH?_b diff --git a/pkg/webui/ui/build/manifest.json b/pkg/webui/ui/build/manifest.json deleted file mode 100644 index 080d6c77a..000000000 --- a/pkg/webui/ui/build/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/pkg/webui/ui/build/robots.txt b/pkg/webui/ui/build/robots.txt deleted file mode 100644 index e9e57dc4d..000000000 --- a/pkg/webui/ui/build/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/pkg/webui/ui/build/static/css/main.css b/pkg/webui/ui/build/static/css/main.css deleted file mode 100644 index 70ec57003..000000000 --- a/pkg/webui/ui/build/static/css/main.css +++ /dev/null @@ -1,87 +0,0 @@ -body { - margin: 0; - background: linear-gradient(180deg, #59A588 0%, #404846 100%); - background-attachment: fixed; -} - -body, input { - font-family: 'Nunito Variable', 'Segoe UI', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', -apple-system, BlinkMacSystemFont, - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} - -* { - box-sizing: border-box; -} - -html, -body, -#root { - width: 100%; - height: 100%; - margin: 0; - padding: 0; -} -/* nunito-cyrillic-ext-wght-normal */ -@font-face { - font-family: 'Nunito Variable'; - font-style: normal; - font-display: swap; - font-display: var(--fontsource-display, swap); - font-weight: 200 1000; - src: url(../../static/media/nunito-cyrillic-ext-wght-normal.woff2?h=b2611fa3f916d23df9b0735ba668944500ff23b73f9da4fbb10818c875d482a0) format('woff2-variations'); - unicode-range: U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F; -} - -/* nunito-cyrillic-wght-normal */ -@font-face { - font-family: 'Nunito Variable'; - font-style: normal; - font-display: swap; - font-display: var(--fontsource-display, swap); - font-weight: 200 1000; - src: url(../../static/media/nunito-cyrillic-wght-normal.woff2?h=7ca4b4bb8be6840990cc92b2dee938f142df99c93ce85063b391a09369b63b17) format('woff2-variations'); - unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; -} - -/* nunito-vietnamese-wght-normal */ -@font-face { - font-family: 'Nunito Variable'; - font-style: normal; - font-display: swap; - font-display: var(--fontsource-display, swap); - font-weight: 200 1000; - src: url(../../static/media/nunito-vietnamese-wght-normal.woff2?h=0ef9726dbc36b5871efa4b0cfdc43fd1bfed5dd48aeb70dc8210e8cb9bc9247b) format('woff2-variations'); - unicode-range: U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB; -} - -/* nunito-latin-ext-wght-normal */ -@font-face { - font-family: 'Nunito Variable'; - font-style: normal; - font-display: swap; - font-display: var(--fontsource-display, swap); - font-weight: 200 1000; - src: url(../../static/media/nunito-latin-ext-wght-normal.woff2?h=89def7428656f40331c1430ee1dc1846ed1e30d7001707b548f9f816d27264a5) format('woff2-variations'); - unicode-range: U+0100-02AF,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF; -} - -/* nunito-latin-wght-normal */ -@font-face { - font-family: 'Nunito Variable'; - font-style: normal; - font-display: swap; - font-display: var(--fontsource-display, swap); - font-weight: 200 1000; - src: url(../../static/media/nunito-latin-wght-normal.woff2?h=96217f1d27fb909f92b4a6b35a0d3d6775f2f0b4d136d27aee88547d3ed87357) format('woff2-variations'); - unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; -} - -/*# sourceMappingURL=main.css.map?h=b04a21858ecfe46c93b33bc95788b6861c50ee3a7852bdb7721e314a3ee95b80*/ \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/787.chunk.js b/pkg/webui/ui/build/static/js/787.chunk.js deleted file mode 100644 index 15b544b3a..000000000 --- a/pkg/webui/ui/build/static/js/787.chunk.js +++ /dev/null @@ -1,239 +0,0 @@ -"use strict"; -(self["webpackChunkkluctl_webui"] = self["webpackChunkkluctl_webui"] || []).push([[787],{ - -/***/ 787: -/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ getCLS: function() { return /* binding */ h; }, -/* harmony export */ getFCP: function() { return /* binding */ d; }, -/* harmony export */ getFID: function() { return /* binding */ L; }, -/* harmony export */ getLCP: function() { return /* binding */ F; }, -/* harmony export */ getTTFB: function() { return /* binding */ P; } -/* harmony export */ }); -var e, - t, - n, - i, - r = function r(e, t) { - return { - name: e, - value: void 0 === t ? -1 : t, - delta: 0, - entries: [], - id: "v2-".concat(Date.now(), "-").concat(Math.floor(8999999999999 * Math.random()) + 1e12) - }; - }, - a = function a(e, t) { - try { - if (PerformanceObserver.supportedEntryTypes.includes(e)) { - if ("first-input" === e && !("PerformanceEventTiming" in self)) return; - var n = new PerformanceObserver(function (e) { - return e.getEntries().map(t); - }); - return n.observe({ - type: e, - buffered: !0 - }), n; - } - } catch (e) {} - }, - o = function o(e, t) { - var n = function n(i) { - "pagehide" !== i.type && "hidden" !== document.visibilityState || (e(i), t && (removeEventListener("visibilitychange", n, !0), removeEventListener("pagehide", n, !0))); - }; - addEventListener("visibilitychange", n, !0), addEventListener("pagehide", n, !0); - }, - u = function u(e) { - addEventListener("pageshow", function (t) { - t.persisted && e(t); - }, !0); - }, - c = function c(e, t, n) { - var i; - return function (r) { - t.value >= 0 && (r || n) && (t.delta = t.value - (i || 0), (t.delta || void 0 === i) && (i = t.value, e(t))); - }; - }, - f = -1, - s = function s() { - return "hidden" === document.visibilityState ? 0 : 1 / 0; - }, - m = function m() { - o(function (e) { - var t = e.timeStamp; - f = t; - }, !0); - }, - v = function v() { - return f < 0 && (f = s(), m(), u(function () { - setTimeout(function () { - f = s(), m(); - }, 0); - })), { - get firstHiddenTime() { - return f; - } - }; - }, - d = function d(e, t) { - var n, - i = v(), - o = r("FCP"), - f = function f(e) { - "first-contentful-paint" === e.name && (m && m.disconnect(), e.startTime < i.firstHiddenTime && (o.value = e.startTime, o.entries.push(e), n(!0))); - }, - s = window.performance && performance.getEntriesByName && performance.getEntriesByName("first-contentful-paint")[0], - m = s ? null : a("paint", f); - (s || m) && (n = c(e, o, t), s && f(s), u(function (i) { - o = r("FCP"), n = c(e, o, t), requestAnimationFrame(function () { - requestAnimationFrame(function () { - o.value = performance.now() - i.timeStamp, n(!0); - }); - }); - })); - }, - p = !1, - l = -1, - h = function h(e, t) { - p || (d(function (e) { - l = e.value; - }), p = !0); - var n, - i = function i(t) { - l > -1 && e(t); - }, - f = r("CLS", 0), - s = 0, - m = [], - v = function v(e) { - if (!e.hadRecentInput) { - var t = m[0], - i = m[m.length - 1]; - s && e.startTime - i.startTime < 1e3 && e.startTime - t.startTime < 5e3 ? (s += e.value, m.push(e)) : (s = e.value, m = [e]), s > f.value && (f.value = s, f.entries = m, n()); - } - }, - h = a("layout-shift", v); - h && (n = c(i, f, t), o(function () { - h.takeRecords().map(v), n(!0); - }), u(function () { - s = 0, l = -1, f = r("CLS", 0), n = c(i, f, t); - })); - }, - T = { - passive: !0, - capture: !0 - }, - y = new Date(), - g = function g(i, r) { - e || (e = r, t = i, n = new Date(), w(removeEventListener), E()); - }, - E = function E() { - if (t >= 0 && t < n - y) { - var r = { - entryType: "first-input", - name: e.type, - target: e.target, - cancelable: e.cancelable, - startTime: e.timeStamp, - processingStart: e.timeStamp + t - }; - i.forEach(function (e) { - e(r); - }), i = []; - } - }, - S = function S(e) { - if (e.cancelable) { - var t = (e.timeStamp > 1e12 ? new Date() : performance.now()) - e.timeStamp; - "pointerdown" == e.type ? function (e, t) { - var n = function n() { - g(e, t), r(); - }, - i = function i() { - r(); - }, - r = function r() { - removeEventListener("pointerup", n, T), removeEventListener("pointercancel", i, T); - }; - addEventListener("pointerup", n, T), addEventListener("pointercancel", i, T); - }(t, e) : g(t, e); - } - }, - w = function w(e) { - ["mousedown", "keydown", "touchstart", "pointerdown"].forEach(function (t) { - return e(t, S, T); - }); - }, - L = function L(n, f) { - var s, - m = v(), - d = r("FID"), - p = function p(e) { - e.startTime < m.firstHiddenTime && (d.value = e.processingStart - e.startTime, d.entries.push(e), s(!0)); - }, - l = a("first-input", p); - s = c(n, d, f), l && o(function () { - l.takeRecords().map(p), l.disconnect(); - }, !0), l && u(function () { - var a; - d = r("FID"), s = c(n, d, f), i = [], t = -1, e = null, w(addEventListener), a = p, i.push(a), E(); - }); - }, - b = {}, - F = function F(e, t) { - var n, - i = v(), - f = r("LCP"), - s = function s(e) { - var t = e.startTime; - t < i.firstHiddenTime && (f.value = t, f.entries.push(e), n()); - }, - m = a("largest-contentful-paint", s); - if (m) { - n = c(e, f, t); - var d = function d() { - b[f.id] || (m.takeRecords().map(s), m.disconnect(), b[f.id] = !0, n(!0)); - }; - ["keydown", "click"].forEach(function (e) { - addEventListener(e, d, { - once: !0, - capture: !0 - }); - }), o(d, !0), u(function (i) { - f = r("LCP"), n = c(e, f, t), requestAnimationFrame(function () { - requestAnimationFrame(function () { - f.value = performance.now() - i.timeStamp, b[f.id] = !0, n(!0); - }); - }); - }); - } - }, - P = function P(e) { - var t, - n = r("TTFB"); - t = function t() { - try { - var t = performance.getEntriesByType("navigation")[0] || function () { - var e = performance.timing, - t = { - entryType: "navigation", - startTime: 0 - }; - for (var n in e) "navigationStart" !== n && "toJSON" !== n && (t[n] = Math.max(e[n] - e.navigationStart, 0)); - return t; - }(); - if (n.value = n.delta = t.responseStart, n.value < 0 || n.value > performance.now()) return; - n.entries = [t], e(n); - } catch (e) {} - }, "complete" === document.readyState ? setTimeout(t, 0) : addEventListener("load", function () { - return setTimeout(t, 0); - }); - }; - - -/***/ }) - -}]); -//# sourceMappingURL=787.chunk.js.map?h=47820fdbf26705ec6b47ae95042786f8669103d43ced5b19492db0ef8a1a30eb \ No newline at end of file diff --git a/pkg/webui/ui/build/static/js/main.js b/pkg/webui/ui/build/static/js/main.js deleted file mode 100644 index a7625bb71..000000000 --- a/pkg/webui/ui/build/static/js/main.js +++ /dev/null @@ -1,61552 +0,0 @@ -/******/ (function() { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ 867: -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -"use strict"; - - -var formatter = __webpack_require__(707); -var fault = create(Error); -module.exports = fault; -fault.eval = create(EvalError); -fault.range = create(RangeError); -fault.reference = create(ReferenceError); -fault.syntax = create(SyntaxError); -fault.type = create(TypeError); -fault.uri = create(URIError); -fault.create = create; - -// Create a new `EConstructor`, with the formatted `format` as a first argument. -function create(EConstructor) { - FormattedError.displayName = EConstructor.displayName || EConstructor.name; - return FormattedError; - function FormattedError(format) { - if (format) { - format = formatter.apply(null, arguments); - } - return new EConstructor(format); - } -} - -/***/ }), - -/***/ 707: -/***/ (function(module) { - -// -// format - printf-like string formatting for JavaScript -// github.com/samsonjs/format -// @_sjs -// -// Copyright 2010 - 2013 Sami Samhuri -// -// MIT License -// http://sjs.mit-license.org -// - -; -(function () { - //// Export the API - var namespace; - - // CommonJS / Node module - if (true) { - namespace = module.exports = format; - } - - // Browsers and other environments - else {} - namespace.format = format; - namespace.vsprintf = vsprintf; - if (typeof console !== 'undefined' && typeof console.log === 'function') { - namespace.printf = printf; - } - function printf( /* ... */ - ) { - console.log(format.apply(null, arguments)); - } - function vsprintf(fmt, replacements) { - return format.apply(null, [fmt].concat(replacements)); - } - function format(fmt) { - var argIndex = 1 // skip initial format argument - , - args = [].slice.call(arguments), - i = 0, - n = fmt.length, - result = '', - c, - escaped = false, - arg, - tmp, - leadingZero = false, - precision, - nextArg = function nextArg() { - return args[argIndex++]; - }, - slurpNumber = function slurpNumber() { - var digits = ''; - while (/\d/.test(fmt[i])) { - digits += fmt[i++]; - c = fmt[i]; - } - return digits.length > 0 ? parseInt(digits) : null; - }; - for (; i < n; ++i) { - c = fmt[i]; - if (escaped) { - escaped = false; - if (c == '.') { - leadingZero = false; - c = fmt[++i]; - } else if (c == '0' && fmt[i + 1] == '.') { - leadingZero = true; - i += 2; - c = fmt[i]; - } else { - leadingZero = true; - } - precision = slurpNumber(); - switch (c) { - case 'b': - // number in binary - result += parseInt(nextArg(), 10).toString(2); - break; - case 'c': - // character - arg = nextArg(); - if (typeof arg === 'string' || arg instanceof String) result += arg;else result += String.fromCharCode(parseInt(arg, 10)); - break; - case 'd': - // number in decimal - result += parseInt(nextArg(), 10); - break; - case 'f': - // floating point number - tmp = String(parseFloat(nextArg()).toFixed(precision || 6)); - result += leadingZero ? tmp : tmp.replace(/^0/, ''); - break; - case 'j': - // JSON - result += JSON.stringify(nextArg()); - break; - case 'o': - // number in octal - result += '0' + parseInt(nextArg(), 10).toString(8); - break; - case 's': - // string - result += nextArg(); - break; - case 'x': - // lowercase hexadecimal - result += '0x' + parseInt(nextArg(), 10).toString(16); - break; - case 'X': - // uppercase hexadecimal - result += '0x' + parseInt(nextArg(), 10).toString(16).toUpperCase(); - break; - default: - result += c; - break; - } - } else if (c === '%') { - escaped = true; - } else { - result += c; - } - } - return result; - } -})(); - -/***/ }), - -/***/ 478: -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -var _slicedToArray = (__webpack_require__(424)["default"]); -var _toConsumableArray = (__webpack_require__(861)["default"]); -var _inherits = (__webpack_require__(655)["default"]); -var _createSuper = (__webpack_require__(389)["default"]); -var _classCallCheck = (__webpack_require__(690)["default"]); -var _createClass = (__webpack_require__(728)["default"]); -function deepFreeze(obj) { - if (obj instanceof Map) { - obj.clear = obj.delete = obj.set = function () { - throw new Error('map is read-only'); - }; - } else if (obj instanceof Set) { - obj.add = obj.clear = obj.delete = function () { - throw new Error('set is read-only'); - }; - } - - // Freeze self - Object.freeze(obj); - Object.getOwnPropertyNames(obj).forEach(function (name) { - var prop = obj[name]; - - // Freeze prop if it is an object - if (typeof prop == 'object' && !Object.isFrozen(prop)) { - deepFreeze(prop); - } - }); - return obj; -} -var deepFreezeEs6 = deepFreeze; -var _default = deepFreeze; -deepFreezeEs6.default = _default; - -/** @implements CallbackResponse */ -var Response = /*#__PURE__*/function () { - "use strict"; - - /** - * @param {CompiledMode} mode - */ - function Response(mode) { - _classCallCheck(this, Response); - // eslint-disable-next-line no-undefined - if (mode.data === undefined) mode.data = {}; - this.data = mode.data; - this.isMatchIgnored = false; - } - _createClass(Response, [{ - key: "ignoreMatch", - value: function ignoreMatch() { - this.isMatchIgnored = true; - } - }]); - return Response; -}(); -/** - * @param {string} value - * @returns {string} - */ -function escapeHTML(value) { - return value.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); -} - -/** - * performs a shallow merge of multiple objects into one - * - * @template T - * @param {T} original - * @param {Record[]} objects - * @returns {T} a single new object - */ -function inherit(original) { - /** @type Record */ - var result = Object.create(null); - for (var key in original) { - result[key] = original[key]; - } - for (var _len = arguments.length, objects = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - objects[_key - 1] = arguments[_key]; - } - objects.forEach(function (obj) { - for (var _key2 in obj) { - result[_key2] = obj[_key2]; - } - }); - return (/** @type {T} */result - ); -} - -/** - * @typedef {object} Renderer - * @property {(text: string) => void} addText - * @property {(node: Node) => void} openNode - * @property {(node: Node) => void} closeNode - * @property {() => string} value - */ - -/** @typedef {{kind?: string, sublanguage?: boolean}} Node */ -/** @typedef {{walk: (r: Renderer) => void}} Tree */ -/** */ - -var SPAN_CLOSE = ''; - -/** - * Determines if a node needs to be wrapped in - * - * @param {Node} node */ -var emitsWrappingTags = function emitsWrappingTags(node) { - return !!node.kind; -}; - -/** @type {Renderer} */ -var HTMLRenderer = /*#__PURE__*/function () { - "use strict"; - - /** - * Creates a new HTMLRenderer - * - * @param {Tree} parseTree - the parse tree (must support `walk` API) - * @param {{classPrefix: string}} options - */ - function HTMLRenderer(parseTree, options) { - _classCallCheck(this, HTMLRenderer); - this.buffer = ""; - this.classPrefix = options.classPrefix; - parseTree.walk(this); - } - - /** - * Adds texts to the output stream - * - * @param {string} text */ - _createClass(HTMLRenderer, [{ - key: "addText", - value: function addText(text) { - this.buffer += escapeHTML(text); - } - - /** - * Adds a node open to the output stream (if needed) - * - * @param {Node} node */ - }, { - key: "openNode", - value: function openNode(node) { - if (!emitsWrappingTags(node)) return; - var className = node.kind; - if (!node.sublanguage) { - className = "".concat(this.classPrefix).concat(className); - } - this.span(className); - } - - /** - * Adds a node close to the output stream (if needed) - * - * @param {Node} node */ - }, { - key: "closeNode", - value: function closeNode(node) { - if (!emitsWrappingTags(node)) return; - this.buffer += SPAN_CLOSE; - } - - /** - * returns the accumulated buffer - */ - }, { - key: "value", - value: function value() { - return this.buffer; - } - - // helpers - - /** - * Builds a span element - * - * @param {string} className */ - }, { - key: "span", - value: function span(className) { - this.buffer += ""); - } - }]); - return HTMLRenderer; -}(); -/** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} | string} Node */ -/** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} } DataNode */ -/** */ -var TokenTree = /*#__PURE__*/function () { - "use strict"; - - function TokenTree() { - _classCallCheck(this, TokenTree); - /** @type DataNode */ - this.rootNode = { - children: [] - }; - this.stack = [this.rootNode]; - } - _createClass(TokenTree, [{ - key: "top", - get: function get() { - return this.stack[this.stack.length - 1]; - } - }, { - key: "root", - get: function get() { - return this.rootNode; - } - - /** @param {Node} node */ - }, { - key: "add", - value: function add(node) { - this.top.children.push(node); - } - - /** @param {string} kind */ - }, { - key: "openNode", - value: function openNode(kind) { - /** @type Node */ - var node = { - kind: kind, - children: [] - }; - this.add(node); - this.stack.push(node); - } - }, { - key: "closeNode", - value: function closeNode() { - if (this.stack.length > 1) { - return this.stack.pop(); - } - // eslint-disable-next-line no-undefined - return undefined; - } - }, { - key: "closeAllNodes", - value: function closeAllNodes() { - while (this.closeNode()); - } - }, { - key: "toJSON", - value: function toJSON() { - return JSON.stringify(this.rootNode, null, 4); - } - - /** - * @typedef { import("./html_renderer").Renderer } Renderer - * @param {Renderer} builder - */ - }, { - key: "walk", - value: function walk(builder) { - // this does not - return this.constructor._walk(builder, this.rootNode); - // this works - // return TokenTree._walk(builder, this.rootNode); - } - - /** - * @param {Renderer} builder - * @param {Node} node - */ - }], [{ - key: "_walk", - value: function _walk(builder, node) { - var _this = this; - if (typeof node === "string") { - builder.addText(node); - } else if (node.children) { - builder.openNode(node); - node.children.forEach(function (child) { - return _this._walk(builder, child); - }); - builder.closeNode(node); - } - return builder; - } - - /** - * @param {Node} node - */ - }, { - key: "_collapse", - value: function _collapse(node) { - if (typeof node === "string") return; - if (!node.children) return; - if (node.children.every(function (el) { - return typeof el === "string"; - })) { - // node.text = node.children.join(""); - // delete node.children; - node.children = [node.children.join("")]; - } else { - node.children.forEach(function (child) { - TokenTree._collapse(child); - }); - } - } - }]); - return TokenTree; -}(); -/** - Currently this is all private API, but this is the minimal API necessary - that an Emitter must implement to fully support the parser. - - Minimal interface: - - - addKeyword(text, kind) - - addText(text) - - addSublanguage(emitter, subLanguageName) - - finalize() - - openNode(kind) - - closeNode() - - closeAllNodes() - - toHTML() - -*/ -/** - * @implements {Emitter} - */ -var TokenTreeEmitter = /*#__PURE__*/function (_TokenTree) { - "use strict"; - - _inherits(TokenTreeEmitter, _TokenTree); - var _super = _createSuper(TokenTreeEmitter); - /** - * @param {*} options - */ - function TokenTreeEmitter(options) { - var _this2; - _classCallCheck(this, TokenTreeEmitter); - _this2 = _super.call(this); - _this2.options = options; - return _this2; - } - - /** - * @param {string} text - * @param {string} kind - */ - _createClass(TokenTreeEmitter, [{ - key: "addKeyword", - value: function addKeyword(text, kind) { - if (text === "") { - return; - } - this.openNode(kind); - this.addText(text); - this.closeNode(); - } - - /** - * @param {string} text - */ - }, { - key: "addText", - value: function addText(text) { - if (text === "") { - return; - } - this.add(text); - } - - /** - * @param {Emitter & {root: DataNode}} emitter - * @param {string} name - */ - }, { - key: "addSublanguage", - value: function addSublanguage(emitter, name) { - /** @type DataNode */ - var node = emitter.root; - node.kind = name; - node.sublanguage = true; - this.add(node); - } - }, { - key: "toHTML", - value: function toHTML() { - var renderer = new HTMLRenderer(this, this.options); - return renderer.value(); - } - }, { - key: "finalize", - value: function finalize() { - return true; - } - }]); - return TokenTreeEmitter; -}(TokenTree); -/** - * @param {string} value - * @returns {RegExp} - * */ -function escape(value) { - return new RegExp(value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm'); -} - -/** - * @param {RegExp | string } re - * @returns {string} - */ -function source(re) { - if (!re) return null; - if (typeof re === "string") return re; - return re.source; -} - -/** - * @param {...(RegExp | string) } args - * @returns {string} - */ -function concat() { - for (var _len2 = arguments.length, args = new Array(_len2), _key3 = 0; _key3 < _len2; _key3++) { - args[_key3] = arguments[_key3]; - } - var joined = args.map(function (x) { - return source(x); - }).join(""); - return joined; -} - -/** - * Any of the passed expresssions may match - * - * Creates a huge this | this | that | that match - * @param {(RegExp | string)[] } args - * @returns {string} - */ -function either() { - for (var _len3 = arguments.length, args = new Array(_len3), _key4 = 0; _key4 < _len3; _key4++) { - args[_key4] = arguments[_key4]; - } - var joined = '(' + args.map(function (x) { - return source(x); - }).join("|") + ")"; - return joined; -} - -/** - * @param {RegExp} re - * @returns {number} - */ -function countMatchGroups(re) { - return new RegExp(re.toString() + '|').exec('').length - 1; -} - -/** - * Does lexeme start with a regular expression match at the beginning - * @param {RegExp} re - * @param {string} lexeme - */ -function startsWith(re, lexeme) { - var match = re && re.exec(lexeme); - return match && match.index === 0; -} - -// BACKREF_RE matches an open parenthesis or backreference. To avoid -// an incorrect parse, it additionally matches the following: -// - [...] elements, where the meaning of parentheses and escapes change -// - other escape sequences, so we do not misparse escape sequences as -// interesting elements -// - non-matching or lookahead parentheses, which do not capture. These -// follow the '(' with a '?'. -var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./; - -// join logically computes regexps.join(separator), but fixes the -// backreferences so they continue to match. -// it also places each individual regular expression into it's own -// match group, keeping track of the sequencing of those match groups -// is currently an exercise for the caller. :-) -/** - * @param {(string | RegExp)[]} regexps - * @param {string} separator - * @returns {string} - */ -function join(regexps) { - var separator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "|"; - var numCaptures = 0; - return regexps.map(function (regex) { - numCaptures += 1; - var offset = numCaptures; - var re = source(regex); - var out = ''; - while (re.length > 0) { - var match = BACKREF_RE.exec(re); - if (!match) { - out += re; - break; - } - out += re.substring(0, match.index); - re = re.substring(match.index + match[0].length); - if (match[0][0] === '\\' && match[1]) { - // Adjust the backreference. - out += '\\' + String(Number(match[1]) + offset); - } else { - out += match[0]; - if (match[0] === '(') { - numCaptures++; - } - } - } - return out; - }).map(function (re) { - return "(".concat(re, ")"); - }).join(separator); -} - -// Common regexps -var MATCH_NOTHING_RE = /\b\B/; -var IDENT_RE = '[a-zA-Z]\\w*'; -var UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*'; -var NUMBER_RE = '\\b\\d+(\\.\\d+)?'; -var C_NUMBER_RE = '(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float -var BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b... -var RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~'; - -/** -* @param { Partial & {binary?: string | RegExp} } opts -*/ -var SHEBANG = function SHEBANG() { - var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - var beginShebang = /^#![ ]*\//; - if (opts.binary) { - opts.begin = concat(beginShebang, /.*\b/, opts.binary, /\b.*/); - } - return inherit({ - className: 'meta', - begin: beginShebang, - end: /$/, - relevance: 0, - /** @type {ModeCallback} */ - "on:begin": function onBegin(m, resp) { - if (m.index !== 0) resp.ignoreMatch(); - } - }, opts); -}; - -// Common modes -var BACKSLASH_ESCAPE = { - begin: '\\\\[\\s\\S]', - relevance: 0 -}; -var APOS_STRING_MODE = { - className: 'string', - begin: '\'', - end: '\'', - illegal: '\\n', - contains: [BACKSLASH_ESCAPE] -}; -var QUOTE_STRING_MODE = { - className: 'string', - begin: '"', - end: '"', - illegal: '\\n', - contains: [BACKSLASH_ESCAPE] -}; -var PHRASAL_WORDS_MODE = { - begin: /\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ -}; -/** - * Creates a comment mode - * - * @param {string | RegExp} begin - * @param {string | RegExp} end - * @param {Mode | {}} [modeOptions] - * @returns {Partial} - */ -var COMMENT = function COMMENT(begin, end) { - var modeOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - var mode = inherit({ - className: 'comment', - begin: begin, - end: end, - contains: [] - }, modeOptions); - mode.contains.push(PHRASAL_WORDS_MODE); - mode.contains.push({ - className: 'doctag', - begin: '(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):', - relevance: 0 - }); - return mode; -}; -var C_LINE_COMMENT_MODE = COMMENT('//', '$'); -var C_BLOCK_COMMENT_MODE = COMMENT('/\\*', '\\*/'); -var HASH_COMMENT_MODE = COMMENT('#', '$'); -var NUMBER_MODE = { - className: 'number', - begin: NUMBER_RE, - relevance: 0 -}; -var C_NUMBER_MODE = { - className: 'number', - begin: C_NUMBER_RE, - relevance: 0 -}; -var BINARY_NUMBER_MODE = { - className: 'number', - begin: BINARY_NUMBER_RE, - relevance: 0 -}; -var CSS_NUMBER_MODE = { - className: 'number', - begin: NUMBER_RE + '(' + '%|em|ex|ch|rem' + '|vw|vh|vmin|vmax' + '|cm|mm|in|pt|pc|px' + '|deg|grad|rad|turn' + '|s|ms' + '|Hz|kHz' + '|dpi|dpcm|dppx' + ')?', - relevance: 0 -}; -var REGEXP_MODE = { - // this outer rule makes sure we actually have a WHOLE regex and not simply - // an expression such as: - // - // 3 / something - // - // (which will then blow up when regex's `illegal` sees the newline) - begin: /(?=\/[^/\n]*\/)/, - contains: [{ - className: 'regexp', - begin: /\//, - end: /\/[gimuy]*/, - illegal: /\n/, - contains: [BACKSLASH_ESCAPE, { - begin: /\[/, - end: /\]/, - relevance: 0, - contains: [BACKSLASH_ESCAPE] - }] - }] -}; -var TITLE_MODE = { - className: 'title', - begin: IDENT_RE, - relevance: 0 -}; -var UNDERSCORE_TITLE_MODE = { - className: 'title', - begin: UNDERSCORE_IDENT_RE, - relevance: 0 -}; -var METHOD_GUARD = { - // excludes method names from keyword processing - begin: '\\.\\s*' + UNDERSCORE_IDENT_RE, - relevance: 0 -}; - -/** - * Adds end same as begin mechanics to a mode - * - * Your mode must include at least a single () match group as that first match - * group is what is used for comparison - * @param {Partial} mode - */ -var END_SAME_AS_BEGIN = function END_SAME_AS_BEGIN(mode) { - return Object.assign(mode, { - /** @type {ModeCallback} */ - 'on:begin': function onBegin(m, resp) { - resp.data._beginMatch = m[1]; - }, - /** @type {ModeCallback} */ - 'on:end': function onEnd(m, resp) { - if (resp.data._beginMatch !== m[1]) resp.ignoreMatch(); - } - }); -}; -var MODES = /*#__PURE__*/Object.freeze({ - __proto__: null, - MATCH_NOTHING_RE: MATCH_NOTHING_RE, - IDENT_RE: IDENT_RE, - UNDERSCORE_IDENT_RE: UNDERSCORE_IDENT_RE, - NUMBER_RE: NUMBER_RE, - C_NUMBER_RE: C_NUMBER_RE, - BINARY_NUMBER_RE: BINARY_NUMBER_RE, - RE_STARTERS_RE: RE_STARTERS_RE, - SHEBANG: SHEBANG, - BACKSLASH_ESCAPE: BACKSLASH_ESCAPE, - APOS_STRING_MODE: APOS_STRING_MODE, - QUOTE_STRING_MODE: QUOTE_STRING_MODE, - PHRASAL_WORDS_MODE: PHRASAL_WORDS_MODE, - COMMENT: COMMENT, - C_LINE_COMMENT_MODE: C_LINE_COMMENT_MODE, - C_BLOCK_COMMENT_MODE: C_BLOCK_COMMENT_MODE, - HASH_COMMENT_MODE: HASH_COMMENT_MODE, - NUMBER_MODE: NUMBER_MODE, - C_NUMBER_MODE: C_NUMBER_MODE, - BINARY_NUMBER_MODE: BINARY_NUMBER_MODE, - CSS_NUMBER_MODE: CSS_NUMBER_MODE, - REGEXP_MODE: REGEXP_MODE, - TITLE_MODE: TITLE_MODE, - UNDERSCORE_TITLE_MODE: UNDERSCORE_TITLE_MODE, - METHOD_GUARD: METHOD_GUARD, - END_SAME_AS_BEGIN: END_SAME_AS_BEGIN -}); - -// Grammar extensions / plugins -// See: https://github.com/highlightjs/highlight.js/issues/2833 - -// Grammar extensions allow "syntactic sugar" to be added to the grammar modes -// without requiring any underlying changes to the compiler internals. - -// `compileMatch` being the perfect small example of now allowing a grammar -// author to write `match` when they desire to match a single expression rather -// than being forced to use `begin`. The extension then just moves `match` into -// `begin` when it runs. Ie, no features have been added, but we've just made -// the experience of writing (and reading grammars) a little bit nicer. - -// ------ - -// TODO: We need negative look-behind support to do this properly -/** - * Skip a match if it has a preceding dot - * - * This is used for `beginKeywords` to prevent matching expressions such as - * `bob.keyword.do()`. The mode compiler automatically wires this up as a - * special _internal_ 'on:begin' callback for modes with `beginKeywords` - * @param {RegExpMatchArray} match - * @param {CallbackResponse} response - */ -function skipIfhasPrecedingDot(match, response) { - var before = match.input[match.index - 1]; - if (before === ".") { - response.ignoreMatch(); - } -} - -/** - * `beginKeywords` syntactic sugar - * @type {CompilerExt} - */ -function beginKeywords(mode, parent) { - if (!parent) return; - if (!mode.beginKeywords) return; - - // for languages with keywords that include non-word characters checking for - // a word boundary is not sufficient, so instead we check for a word boundary - // or whitespace - this does no harm in any case since our keyword engine - // doesn't allow spaces in keywords anyways and we still check for the boundary - // first - mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?!\\.)(?=\\b|\\s)'; - mode.__beforeBegin = skipIfhasPrecedingDot; - mode.keywords = mode.keywords || mode.beginKeywords; - delete mode.beginKeywords; - - // prevents double relevance, the keywords themselves provide - // relevance, the mode doesn't need to double it - // eslint-disable-next-line no-undefined - if (mode.relevance === undefined) mode.relevance = 0; -} - -/** - * Allow `illegal` to contain an array of illegal values - * @type {CompilerExt} - */ -function compileIllegal(mode, _parent) { - if (!Array.isArray(mode.illegal)) return; - mode.illegal = either.apply(void 0, _toConsumableArray(mode.illegal)); -} - -/** - * `match` to match a single expression for readability - * @type {CompilerExt} - */ -function compileMatch(mode, _parent) { - if (!mode.match) return; - if (mode.begin || mode.end) throw new Error("begin & end are not supported with match"); - mode.begin = mode.match; - delete mode.match; -} - -/** - * provides the default 1 relevance to all modes - * @type {CompilerExt} - */ -function compileRelevance(mode, _parent) { - // eslint-disable-next-line no-undefined - if (mode.relevance === undefined) mode.relevance = 1; -} - -// keywords that should have no default relevance value -var COMMON_KEYWORDS = ['of', 'and', 'for', 'in', 'not', 'or', 'if', 'then', 'parent', -// common variable name -'list', -// common variable name -'value' // common variable name -]; - -var DEFAULT_KEYWORD_CLASSNAME = "keyword"; - -/** - * Given raw keywords from a language definition, compile them. - * - * @param {string | Record | Array} rawKeywords - * @param {boolean} caseInsensitive - */ -function compileKeywords(rawKeywords, caseInsensitive) { - var className = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_KEYWORD_CLASSNAME; - /** @type KeywordDict */ - var compiledKeywords = {}; - - // input can be a string of keywords, an array of keywords, or a object with - // named keys representing className (which can then point to a string or array) - if (typeof rawKeywords === 'string') { - compileList(className, rawKeywords.split(" ")); - } else if (Array.isArray(rawKeywords)) { - compileList(className, rawKeywords); - } else { - Object.keys(rawKeywords).forEach(function (className) { - // collapse all our objects back into the parent object - Object.assign(compiledKeywords, compileKeywords(rawKeywords[className], caseInsensitive, className)); - }); - } - return compiledKeywords; - - // --- - - /** - * Compiles an individual list of keywords - * - * Ex: "for if when while|5" - * - * @param {string} className - * @param {Array} keywordList - */ - function compileList(className, keywordList) { - if (caseInsensitive) { - keywordList = keywordList.map(function (x) { - return x.toLowerCase(); - }); - } - keywordList.forEach(function (keyword) { - var pair = keyword.split('|'); - compiledKeywords[pair[0]] = [className, scoreForKeyword(pair[0], pair[1])]; - }); - } -} - -/** - * Returns the proper score for a given keyword - * - * Also takes into account comment keywords, which will be scored 0 UNLESS - * another score has been manually assigned. - * @param {string} keyword - * @param {string} [providedScore] - */ -function scoreForKeyword(keyword, providedScore) { - // manual scores always win over common keywords - // so you can force a score of 1 if you really insist - if (providedScore) { - return Number(providedScore); - } - return commonKeyword(keyword) ? 0 : 1; -} - -/** - * Determines if a given keyword is common or not - * - * @param {string} keyword */ -function commonKeyword(keyword) { - return COMMON_KEYWORDS.includes(keyword.toLowerCase()); -} - -// compilation - -/** - * Compiles a language definition result - * - * Given the raw result of a language definition (Language), compiles this so - * that it is ready for highlighting code. - * @param {Language} language - * @param {{plugins: HLJSPlugin[]}} opts - * @returns {CompiledLanguage} - */ -function compileLanguage(language, _ref) { - var plugins = _ref.plugins; - /** - * Builds a regex with the case sensativility of the current language - * - * @param {RegExp | string} value - * @param {boolean} [global] - */ - function langRe(value, global) { - return new RegExp(source(value), 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '')); - } - - /** - Stores multiple regular expressions and allows you to quickly search for - them all in a string simultaneously - returning the first match. It does - this by creating a huge (a|b|c) regex - each individual item wrapped with () - and joined by `|` - using match groups to track position. When a match is - found checking which position in the array has content allows us to figure - out which of the original regexes / match groups triggered the match. - The match object itself (the result of `Regex.exec`) is returned but also - enhanced by merging in any meta-data that was registered with the regex. - This is how we keep track of which mode matched, and what type of rule - (`illegal`, `begin`, end, etc). - */ - var MultiRegex = /*#__PURE__*/function () { - "use strict"; - - function MultiRegex() { - _classCallCheck(this, MultiRegex); - this.matchIndexes = {}; - // @ts-ignore - this.regexes = []; - this.matchAt = 1; - this.position = 0; - } - - // @ts-ignore - _createClass(MultiRegex, [{ - key: "addRule", - value: function addRule(re, opts) { - opts.position = this.position++; - // @ts-ignore - this.matchIndexes[this.matchAt] = opts; - this.regexes.push([opts, re]); - this.matchAt += countMatchGroups(re) + 1; - } - }, { - key: "compile", - value: function compile() { - if (this.regexes.length === 0) { - // avoids the need to check length every time exec is called - // @ts-ignore - this.exec = function () { - return null; - }; - } - var terminators = this.regexes.map(function (el) { - return el[1]; - }); - this.matcherRe = langRe(join(terminators), true); - this.lastIndex = 0; - } - - /** @param {string} s */ - }, { - key: "exec", - value: function exec(s) { - this.matcherRe.lastIndex = this.lastIndex; - var match = this.matcherRe.exec(s); - if (!match) { - return null; - } - - // eslint-disable-next-line no-undefined - var i = match.findIndex(function (el, i) { - return i > 0 && el !== undefined; - }); - // @ts-ignore - var matchData = this.matchIndexes[i]; - // trim off any earlier non-relevant match groups (ie, the other regex - // match groups that make up the multi-matcher) - match.splice(0, i); - return Object.assign(match, matchData); - } - }]); - return MultiRegex; - }(); - /* - Created to solve the key deficiently with MultiRegex - there is no way to - test for multiple matches at a single location. Why would we need to do - that? In the future a more dynamic engine will allow certain matches to be - ignored. An example: if we matched say the 3rd regex in a large group but - decided to ignore it - we'd need to started testing again at the 4th - regex... but MultiRegex itself gives us no real way to do that. - So what this class creates MultiRegexs on the fly for whatever search - position they are needed. - NOTE: These additional MultiRegex objects are created dynamically. For most - grammars most of the time we will never actually need anything more than the - first MultiRegex - so this shouldn't have too much overhead. - Say this is our search group, and we match regex3, but wish to ignore it. - regex1 | regex2 | regex3 | regex4 | regex5 ' ie, startAt = 0 - What we need is a new MultiRegex that only includes the remaining - possibilities: - regex4 | regex5 ' ie, startAt = 3 - This class wraps all that complexity up in a simple API... `startAt` decides - where in the array of expressions to start doing the matching. It - auto-increments, so if a match is found at position 2, then startAt will be - set to 3. If the end is reached startAt will return to 0. - MOST of the time the parser will be setting startAt manually to 0. - */ - var ResumableMultiRegex = /*#__PURE__*/function () { - "use strict"; - - function ResumableMultiRegex() { - _classCallCheck(this, ResumableMultiRegex); - // @ts-ignore - this.rules = []; - // @ts-ignore - this.multiRegexes = []; - this.count = 0; - this.lastIndex = 0; - this.regexIndex = 0; - } - - // @ts-ignore - _createClass(ResumableMultiRegex, [{ - key: "getMatcher", - value: function getMatcher(index) { - if (this.multiRegexes[index]) return this.multiRegexes[index]; - var matcher = new MultiRegex(); - this.rules.slice(index).forEach(function (_ref2) { - var _ref3 = _slicedToArray(_ref2, 2), - re = _ref3[0], - opts = _ref3[1]; - return matcher.addRule(re, opts); - }); - matcher.compile(); - this.multiRegexes[index] = matcher; - return matcher; - } - }, { - key: "resumingScanAtSamePosition", - value: function resumingScanAtSamePosition() { - return this.regexIndex !== 0; - } - }, { - key: "considerAll", - value: function considerAll() { - this.regexIndex = 0; - } - - // @ts-ignore - }, { - key: "addRule", - value: function addRule(re, opts) { - this.rules.push([re, opts]); - if (opts.type === "begin") this.count++; - } - - /** @param {string} s */ - }, { - key: "exec", - value: function exec(s) { - var m = this.getMatcher(this.regexIndex); - m.lastIndex = this.lastIndex; - var result = m.exec(s); - - // The following is because we have no easy way to say "resume scanning at the - // existing position but also skip the current rule ONLY". What happens is - // all prior rules are also skipped which can result in matching the wrong - // thing. Example of matching "booger": - - // our matcher is [string, "booger", number] - // - // ....booger.... - - // if "booger" is ignored then we'd really need a regex to scan from the - // SAME position for only: [string, number] but ignoring "booger" (if it - // was the first match), a simple resume would scan ahead who knows how - // far looking only for "number", ignoring potential string matches (or - // future "booger" matches that might be valid.) - - // So what we do: We execute two matchers, one resuming at the same - // position, but the second full matcher starting at the position after: - - // /--- resume first regex match here (for [number]) - // |/---- full match here for [string, "booger", number] - // vv - // ....booger.... - - // Which ever results in a match first is then used. So this 3-4 step - // process essentially allows us to say "match at this position, excluding - // a prior rule that was ignored". - // - // 1. Match "booger" first, ignore. Also proves that [string] does non match. - // 2. Resume matching for [number] - // 3. Match at index + 1 for [string, "booger", number] - // 4. If #2 and #3 result in matches, which came first? - if (this.resumingScanAtSamePosition()) { - if (result && result.index === this.lastIndex) ;else { - // use the second matcher result - var m2 = this.getMatcher(0); - m2.lastIndex = this.lastIndex + 1; - result = m2.exec(s); - } - } - if (result) { - this.regexIndex += result.position + 1; - if (this.regexIndex === this.count) { - // wrap-around to considering all matches again - this.considerAll(); - } - } - return result; - } - }]); - return ResumableMultiRegex; - }(); - /** - * Given a mode, builds a huge ResumableMultiRegex that can be used to walk - * the content and find matches. - * - * @param {CompiledMode} mode - * @returns {ResumableMultiRegex} - */ - function buildModeRegex(mode) { - var mm = new ResumableMultiRegex(); - mode.contains.forEach(function (term) { - return mm.addRule(term.begin, { - rule: term, - type: "begin" - }); - }); - if (mode.terminatorEnd) { - mm.addRule(mode.terminatorEnd, { - type: "end" - }); - } - if (mode.illegal) { - mm.addRule(mode.illegal, { - type: "illegal" - }); - } - return mm; - } - - /** skip vs abort vs ignore - * - * @skip - The mode is still entered and exited normally (and contains rules apply), - * but all content is held and added to the parent buffer rather than being - * output when the mode ends. Mostly used with `sublanguage` to build up - * a single large buffer than can be parsed by sublanguage. - * - * - The mode begin ands ends normally. - * - Content matched is added to the parent mode buffer. - * - The parser cursor is moved forward normally. - * - * @abort - A hack placeholder until we have ignore. Aborts the mode (as if it - * never matched) but DOES NOT continue to match subsequent `contains` - * modes. Abort is bad/suboptimal because it can result in modes - * farther down not getting applied because an earlier rule eats the - * content but then aborts. - * - * - The mode does not begin. - * - Content matched by `begin` is added to the mode buffer. - * - The parser cursor is moved forward accordingly. - * - * @ignore - Ignores the mode (as if it never matched) and continues to match any - * subsequent `contains` modes. Ignore isn't technically possible with - * the current parser implementation. - * - * - The mode does not begin. - * - Content matched by `begin` is ignored. - * - The parser cursor is not moved forward. - */ - - /** - * Compiles an individual mode - * - * This can raise an error if the mode contains certain detectable known logic - * issues. - * @param {Mode} mode - * @param {CompiledMode | null} [parent] - * @returns {CompiledMode | never} - */ - function compileMode(mode, parent) { - var _ref4; - var cmode = /** @type CompiledMode */mode; - if (mode.isCompiled) return cmode; - [ - // do this early so compiler extensions generally don't have to worry about - // the distinction between match/begin - compileMatch].forEach(function (ext) { - return ext(mode, parent); - }); - language.compilerExtensions.forEach(function (ext) { - return ext(mode, parent); - }); - - // __beforeBegin is considered private API, internal use only - mode.__beforeBegin = null; - [beginKeywords, - // do this later so compiler extensions that come earlier have access to the - // raw array if they wanted to perhaps manipulate it, etc. - compileIllegal, - // default to 1 relevance if not specified - compileRelevance].forEach(function (ext) { - return ext(mode, parent); - }); - mode.isCompiled = true; - var keywordPattern = null; - if (typeof mode.keywords === "object") { - keywordPattern = mode.keywords.$pattern; - delete mode.keywords.$pattern; - } - if (mode.keywords) { - mode.keywords = compileKeywords(mode.keywords, language.case_insensitive); - } - - // both are not allowed - if (mode.lexemes && keywordPattern) { - throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) "); - } - - // `mode.lexemes` was the old standard before we added and now recommend - // using `keywords.$pattern` to pass the keyword pattern - keywordPattern = keywordPattern || mode.lexemes || /\w+/; - cmode.keywordPatternRe = langRe(keywordPattern, true); - if (parent) { - if (!mode.begin) mode.begin = /\B|\b/; - cmode.beginRe = langRe(mode.begin); - if (mode.endSameAsBegin) mode.end = mode.begin; - if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/; - if (mode.end) cmode.endRe = langRe(mode.end); - cmode.terminatorEnd = source(mode.end) || ''; - if (mode.endsWithParent && parent.terminatorEnd) { - cmode.terminatorEnd += (mode.end ? '|' : '') + parent.terminatorEnd; - } - } - if (mode.illegal) cmode.illegalRe = langRe( /** @type {RegExp | string} */mode.illegal); - if (!mode.contains) mode.contains = []; - mode.contains = (_ref4 = []).concat.apply(_ref4, _toConsumableArray(mode.contains.map(function (c) { - return expandOrCloneMode(c === 'self' ? mode : c); - }))); - mode.contains.forEach(function (c) { - compileMode( /** @type Mode */c, cmode); - }); - if (mode.starts) { - compileMode(mode.starts, parent); - } - cmode.matcher = buildModeRegex(cmode); - return cmode; - } - if (!language.compilerExtensions) language.compilerExtensions = []; - - // self is not valid at the top-level - if (language.contains && language.contains.includes('self')) { - throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation."); - } - - // we need a null object, which inherit will guarantee - language.classNameAliases = inherit(language.classNameAliases || {}); - return compileMode( /** @type Mode */language); -} - -/** - * Determines if a mode has a dependency on it's parent or not - * - * If a mode does have a parent dependency then often we need to clone it if - * it's used in multiple places so that each copy points to the correct parent, - * where-as modes without a parent can often safely be re-used at the bottom of - * a mode chain. - * - * @param {Mode | null} mode - * @returns {boolean} - is there a dependency on the parent? - * */ -function dependencyOnParent(mode) { - if (!mode) return false; - return mode.endsWithParent || dependencyOnParent(mode.starts); -} - -/** - * Expands a mode or clones it if necessary - * - * This is necessary for modes with parental dependenceis (see notes on - * `dependencyOnParent`) and for nodes that have `variants` - which must then be - * exploded into their own individual modes at compile time. - * - * @param {Mode} mode - * @returns {Mode | Mode[]} - * */ -function expandOrCloneMode(mode) { - if (mode.variants && !mode.cachedVariants) { - mode.cachedVariants = mode.variants.map(function (variant) { - return inherit(mode, { - variants: null - }, variant); - }); - } - - // EXPAND - // if we have variants then essentially "replace" the mode with the variants - // this happens in compileMode, where this function is called from - if (mode.cachedVariants) { - return mode.cachedVariants; - } - - // CLONE - // if we have dependencies on parents then we need a unique - // instance of ourselves, so we can be reused with many - // different parents without issue - if (dependencyOnParent(mode)) { - return inherit(mode, { - starts: mode.starts ? inherit(mode.starts) : null - }); - } - if (Object.isFrozen(mode)) { - return inherit(mode); - } - - // no special dependency issues, just return ourselves - return mode; -} -var version = "10.7.3"; - -// @ts-nocheck - -function hasValueOrEmptyAttribute(value) { - return Boolean(value || value === ""); -} -function BuildVuePlugin(hljs) { - var Component = { - props: ["language", "code", "autodetect"], - data: function data() { - return { - detectedLanguage: "", - unknownLanguage: false - }; - }, - computed: { - className: function className() { - if (this.unknownLanguage) return ""; - return "hljs " + this.detectedLanguage; - }, - highlighted: function highlighted() { - // no idea what language to use, return raw code - if (!this.autoDetect && !hljs.getLanguage(this.language)) { - console.warn("The language \"".concat(this.language, "\" you specified could not be found.")); - this.unknownLanguage = true; - return escapeHTML(this.code); - } - var result = {}; - if (this.autoDetect) { - result = hljs.highlightAuto(this.code); - this.detectedLanguage = result.language; - } else { - result = hljs.highlight(this.language, this.code, this.ignoreIllegals); - this.detectedLanguage = this.language; - } - return result.value; - }, - autoDetect: function autoDetect() { - return !this.language || hasValueOrEmptyAttribute(this.autodetect); - }, - ignoreIllegals: function ignoreIllegals() { - return true; - } - }, - // this avoids needing to use a whole Vue compilation pipeline just - // to build Highlight.js - render: function render(createElement) { - return createElement("pre", {}, [createElement("code", { - class: this.className, - domProps: { - innerHTML: this.highlighted - } - })]); - } // template: `
    ` - }; - var VuePlugin = { - install: function install(Vue) { - Vue.component('highlightjs', Component); - } - }; - return { - Component: Component, - VuePlugin: VuePlugin - }; -} - -/* plugin itself */ - -/** @type {HLJSPlugin} */ -var mergeHTMLPlugin = { - "after:highlightElement": function afterHighlightElement(_ref5) { - var el = _ref5.el, - result = _ref5.result, - text = _ref5.text; - var originalStream = nodeStream(el); - if (!originalStream.length) return; - var resultNode = document.createElement('div'); - resultNode.innerHTML = result.value; - result.value = mergeStreams(originalStream, nodeStream(resultNode), text); - } -}; - -/* Stream merging support functions */ - -/** - * @typedef Event - * @property {'start'|'stop'} event - * @property {number} offset - * @property {Node} node - */ - -/** - * @param {Node} node - */ -function tag(node) { - return node.nodeName.toLowerCase(); -} - -/** - * @param {Node} node - */ -function nodeStream(node) { - /** @type Event[] */ - var result = []; - (function _nodeStream(node, offset) { - for (var child = node.firstChild; child; child = child.nextSibling) { - if (child.nodeType === 3) { - offset += child.nodeValue.length; - } else if (child.nodeType === 1) { - result.push({ - event: 'start', - offset: offset, - node: child - }); - offset = _nodeStream(child, offset); - // Prevent void elements from having an end tag that would actually - // double them in the output. There are more void elements in HTML - // but we list only those realistically expected in code display. - if (!tag(child).match(/br|hr|img|input/)) { - result.push({ - event: 'stop', - offset: offset, - node: child - }); - } - } - } - return offset; - })(node, 0); - return result; -} - -/** - * @param {any} original - the original stream - * @param {any} highlighted - stream of the highlighted source - * @param {string} value - the original source itself - */ -function mergeStreams(original, highlighted, value) { - var processed = 0; - var result = ''; - var nodeStack = []; - function selectStream() { - if (!original.length || !highlighted.length) { - return original.length ? original : highlighted; - } - if (original[0].offset !== highlighted[0].offset) { - return original[0].offset < highlighted[0].offset ? original : highlighted; - } - - /* - To avoid starting the stream just before it should stop the order is - ensured that original always starts first and closes last: - if (event1 == 'start' && event2 == 'start') - return original; - if (event1 == 'start' && event2 == 'stop') - return highlighted; - if (event1 == 'stop' && event2 == 'start') - return original; - if (event1 == 'stop' && event2 == 'stop') - return highlighted; - ... which is collapsed to: - */ - return highlighted[0].event === 'start' ? original : highlighted; - } - - /** - * @param {Node} node - */ - function open(node) { - /** @param {Attr} attr */ - function attributeString(attr) { - return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"'; - } - // @ts-ignore - result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('') + '>'; - } - - /** - * @param {Node} node - */ - function close(node) { - result += ''; - } - - /** - * @param {Event} event - */ - function render(event) { - (event.event === 'start' ? open : close)(event.node); - } - while (original.length || highlighted.length) { - var stream = selectStream(); - result += escapeHTML(value.substring(processed, stream[0].offset)); - processed = stream[0].offset; - if (stream === original) { - /* - On any opening or closing tag of the original markup we first close - the entire highlighted node stack, then render the original tag along - with all the following original tags at the same offset and then - reopen all the tags on the highlighted stack. - */ - nodeStack.reverse().forEach(close); - do { - render(stream.splice(0, 1)[0]); - stream = selectStream(); - } while (stream === original && stream.length && stream[0].offset === processed); - nodeStack.reverse().forEach(open); - } else { - if (stream[0].event === 'start') { - nodeStack.push(stream[0].node); - } else { - nodeStack.pop(); - } - render(stream.splice(0, 1)[0]); - } - } - return result + escapeHTML(value.substr(processed)); -} - -/* - -For the reasoning behind this please see: -https://github.com/highlightjs/highlight.js/issues/2880#issuecomment-747275419 - -*/ - -/** - * @type {Record} - */ -var seenDeprecations = {}; - -/** - * @param {string} message - */ -var error = function error(message) { - console.error(message); -}; - -/** - * @param {string} message - * @param {any} args - */ -var warn = function warn(message) { - var _console; - for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key5 = 1; _key5 < _len4; _key5++) { - args[_key5 - 1] = arguments[_key5]; - } - (_console = console).log.apply(_console, ["WARN: ".concat(message)].concat(args)); -}; - -/** - * @param {string} version - * @param {string} message - */ -var deprecated = function deprecated(version, message) { - if (seenDeprecations["".concat(version, "/").concat(message)]) return; - console.log("Deprecated as of ".concat(version, ". ").concat(message)); - seenDeprecations["".concat(version, "/").concat(message)] = true; -}; - -/* -Syntax highlighting with language autodetection. -https://highlightjs.org/ -*/ - -var escape$1 = escapeHTML; -var inherit$1 = inherit; -var NO_MATCH = Symbol("nomatch"); - -/** - * @param {any} hljs - object that is extended (legacy) - * @returns {HLJSApi} - */ -var HLJS = function HLJS(hljs) { - // Global internal variables used within the highlight.js library. - /** @type {Record} */ - var languages = Object.create(null); - /** @type {Record} */ - var aliases = Object.create(null); - /** @type {HLJSPlugin[]} */ - var plugins = []; - - // safe/production mode - swallows more errors, tries to keep running - // even if a single syntax or parse hits a fatal error - var SAFE_MODE = true; - var fixMarkupRe = /(^(<[^>]+>|\t|)+|\n)/gm; - var LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?"; - /** @type {Language} */ - var PLAINTEXT_LANGUAGE = { - disableAutodetect: true, - name: 'Plain text', - contains: [] - }; - - // Global options used when within external APIs. This is modified when - // calling the `hljs.configure` function. - /** @type HLJSOptions */ - var options = { - noHighlightRe: /^(no-?highlight)$/i, - languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i, - classPrefix: 'hljs-', - tabReplace: null, - useBR: false, - languages: null, - // beta configuration options, subject to change, welcome to discuss - // https://github.com/highlightjs/highlight.js/issues/1086 - __emitter: TokenTreeEmitter - }; - - /* Utility functions */ - - /** - * Tests a language name to see if highlighting should be skipped - * @param {string} languageName - */ - function shouldNotHighlight(languageName) { - return options.noHighlightRe.test(languageName); - } - - /** - * @param {HighlightedHTMLElement} block - the HTML element to determine language for - */ - function blockLanguage(block) { - var classes = block.className + ' '; - classes += block.parentNode ? block.parentNode.className : ''; - - // language-* takes precedence over non-prefixed class names. - var match = options.languageDetectRe.exec(classes); - if (match) { - var language = getLanguage(match[1]); - if (!language) { - warn(LANGUAGE_NOT_FOUND.replace("{}", match[1])); - warn("Falling back to no-highlight mode for this block.", block); - } - return language ? match[1] : 'no-highlight'; - } - return classes.split(/\s+/).find(function (_class) { - return shouldNotHighlight(_class) || getLanguage(_class); - }); - } - - /** - * Core highlighting function. - * - * OLD API - * highlight(lang, code, ignoreIllegals, continuation) - * - * NEW API - * highlight(code, {lang, ignoreIllegals}) - * - * @param {string} codeOrlanguageName - the language to use for highlighting - * @param {string | HighlightOptions} optionsOrCode - the code to highlight - * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail - * @param {CompiledMode} [continuation] - current continuation mode, if any - * - * @returns {HighlightResult} Result - an object that represents the result - * @property {string} language - the language name - * @property {number} relevance - the relevance score - * @property {string} value - the highlighted HTML code - * @property {string} code - the original raw code - * @property {CompiledMode} top - top of the current mode stack - * @property {boolean} illegal - indicates whether any illegal matches were found - */ - function highlight(codeOrlanguageName, optionsOrCode, ignoreIllegals, continuation) { - var code = ""; - var languageName = ""; - if (typeof optionsOrCode === "object") { - code = codeOrlanguageName; - ignoreIllegals = optionsOrCode.ignoreIllegals; - languageName = optionsOrCode.language; - // continuation not supported at all via the new API - // eslint-disable-next-line no-undefined - continuation = undefined; - } else { - // old API - deprecated("10.7.0", "highlight(lang, code, ...args) has been deprecated."); - deprecated("10.7.0", "Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"); - languageName = codeOrlanguageName; - code = optionsOrCode; - } - - /** @type {BeforeHighlightContext} */ - var context = { - code: code, - language: languageName - }; - // the plugin can change the desired language or the code to be highlighted - // just be changing the object it was passed - fire("before:highlight", context); - - // a before plugin can usurp the result completely by providing it's own - // in which case we don't even need to call highlight - var result = context.result ? context.result : _highlight(context.language, context.code, ignoreIllegals, continuation); - result.code = context.code; - // the plugin can change anything in result to suite it - fire("after:highlight", result); - return result; - } - - /** - * private highlight that's used internally and does not fire callbacks - * - * @param {string} languageName - the language to use for highlighting - * @param {string} codeToHighlight - the code to highlight - * @param {boolean?} [ignoreIllegals] - whether to ignore illegal matches, default is to bail - * @param {CompiledMode?} [continuation] - current continuation mode, if any - * @returns {HighlightResult} - result of the highlight operation - */ - function _highlight(languageName, codeToHighlight, ignoreIllegals, continuation) { - /** - * Return keyword data if a match is a keyword - * @param {CompiledMode} mode - current mode - * @param {RegExpMatchArray} match - regexp match data - * @returns {KeywordData | false} - */ - function keywordData(mode, match) { - var matchText = language.case_insensitive ? match[0].toLowerCase() : match[0]; - return Object.prototype.hasOwnProperty.call(mode.keywords, matchText) && mode.keywords[matchText]; - } - function processKeywords() { - if (!top.keywords) { - emitter.addText(modeBuffer); - return; - } - var lastIndex = 0; - top.keywordPatternRe.lastIndex = 0; - var match = top.keywordPatternRe.exec(modeBuffer); - var buf = ""; - while (match) { - buf += modeBuffer.substring(lastIndex, match.index); - var data = keywordData(top, match); - if (data) { - var _data = _slicedToArray(data, 2), - kind = _data[0], - keywordRelevance = _data[1]; - emitter.addText(buf); - buf = ""; - relevance += keywordRelevance; - if (kind.startsWith("_")) { - // _ implied for relevance only, do not highlight - // by applying a class name - buf += match[0]; - } else { - var cssClass = language.classNameAliases[kind] || kind; - emitter.addKeyword(match[0], cssClass); - } - } else { - buf += match[0]; - } - lastIndex = top.keywordPatternRe.lastIndex; - match = top.keywordPatternRe.exec(modeBuffer); - } - buf += modeBuffer.substr(lastIndex); - emitter.addText(buf); - } - function processSubLanguage() { - if (modeBuffer === "") return; - /** @type HighlightResult */ - var result = null; - if (typeof top.subLanguage === 'string') { - if (!languages[top.subLanguage]) { - emitter.addText(modeBuffer); - return; - } - result = _highlight(top.subLanguage, modeBuffer, true, continuations[top.subLanguage]); - continuations[top.subLanguage] = /** @type {CompiledMode} */result.top; - } else { - result = highlightAuto(modeBuffer, top.subLanguage.length ? top.subLanguage : null); - } - - // Counting embedded language score towards the host language may be disabled - // with zeroing the containing mode relevance. Use case in point is Markdown that - // allows XML everywhere and makes every XML snippet to have a much larger Markdown - // score. - if (top.relevance > 0) { - relevance += result.relevance; - } - emitter.addSublanguage(result.emitter, result.language); - } - function processBuffer() { - if (top.subLanguage != null) { - processSubLanguage(); - } else { - processKeywords(); - } - modeBuffer = ''; - } - - /** - * @param {Mode} mode - new mode to start - */ - function startNewMode(mode) { - if (mode.className) { - emitter.openNode(language.classNameAliases[mode.className] || mode.className); - } - top = Object.create(mode, { - parent: { - value: top - } - }); - return top; - } - - /** - * @param {CompiledMode } mode - the mode to potentially end - * @param {RegExpMatchArray} match - the latest match - * @param {string} matchPlusRemainder - match plus remainder of content - * @returns {CompiledMode | void} - the next mode, or if void continue on in current mode - */ - function endOfMode(mode, match, matchPlusRemainder) { - var matched = startsWith(mode.endRe, matchPlusRemainder); - if (matched) { - if (mode["on:end"]) { - var resp = new Response(mode); - mode["on:end"](match, resp); - if (resp.isMatchIgnored) matched = false; - } - if (matched) { - while (mode.endsParent && mode.parent) { - mode = mode.parent; - } - return mode; - } - } - // even if on:end fires an `ignore` it's still possible - // that we might trigger the end node because of a parent mode - if (mode.endsWithParent) { - return endOfMode(mode.parent, match, matchPlusRemainder); - } - } - - /** - * Handle matching but then ignoring a sequence of text - * - * @param {string} lexeme - string containing full match text - */ - function doIgnore(lexeme) { - if (top.matcher.regexIndex === 0) { - // no more regexs to potentially match here, so we move the cursor forward one - // space - modeBuffer += lexeme[0]; - return 1; - } else { - // no need to move the cursor, we still have additional regexes to try and - // match at this very spot - resumeScanAtSamePosition = true; - return 0; - } - } - - /** - * Handle the start of a new potential mode match - * - * @param {EnhancedMatch} match - the current match - * @returns {number} how far to advance the parse cursor - */ - function doBeginMatch(match) { - var lexeme = match[0]; - var newMode = match.rule; - var resp = new Response(newMode); - // first internal before callbacks, then the public ones - var beforeCallbacks = [newMode.__beforeBegin, newMode["on:begin"]]; - for (var _i = 0, _beforeCallbacks = beforeCallbacks; _i < _beforeCallbacks.length; _i++) { - var cb = _beforeCallbacks[_i]; - if (!cb) continue; - cb(match, resp); - if (resp.isMatchIgnored) return doIgnore(lexeme); - } - if (newMode && newMode.endSameAsBegin) { - newMode.endRe = escape(lexeme); - } - if (newMode.skip) { - modeBuffer += lexeme; - } else { - if (newMode.excludeBegin) { - modeBuffer += lexeme; - } - processBuffer(); - if (!newMode.returnBegin && !newMode.excludeBegin) { - modeBuffer = lexeme; - } - } - startNewMode(newMode); - // if (mode["after:begin"]) { - // let resp = new Response(mode); - // mode["after:begin"](match, resp); - // } - return newMode.returnBegin ? 0 : lexeme.length; - } - - /** - * Handle the potential end of mode - * - * @param {RegExpMatchArray} match - the current match - */ - function doEndMatch(match) { - var lexeme = match[0]; - var matchPlusRemainder = codeToHighlight.substr(match.index); - var endMode = endOfMode(top, match, matchPlusRemainder); - if (!endMode) { - return NO_MATCH; - } - var origin = top; - if (origin.skip) { - modeBuffer += lexeme; - } else { - if (!(origin.returnEnd || origin.excludeEnd)) { - modeBuffer += lexeme; - } - processBuffer(); - if (origin.excludeEnd) { - modeBuffer = lexeme; - } - } - do { - if (top.className) { - emitter.closeNode(); - } - if (!top.skip && !top.subLanguage) { - relevance += top.relevance; - } - top = top.parent; - } while (top !== endMode.parent); - if (endMode.starts) { - if (endMode.endSameAsBegin) { - endMode.starts.endRe = endMode.endRe; - } - startNewMode(endMode.starts); - } - return origin.returnEnd ? 0 : lexeme.length; - } - function processContinuations() { - var list = []; - for (var current = top; current !== language; current = current.parent) { - if (current.className) { - list.unshift(current.className); - } - } - list.forEach(function (item) { - return emitter.openNode(item); - }); - } - - /** @type {{type?: MatchType, index?: number, rule?: Mode}}} */ - var lastMatch = {}; - - /** - * Process an individual match - * - * @param {string} textBeforeMatch - text preceeding the match (since the last match) - * @param {EnhancedMatch} [match] - the match itself - */ - function processLexeme(textBeforeMatch, match) { - var lexeme = match && match[0]; - - // add non-matched text to the current mode buffer - modeBuffer += textBeforeMatch; - if (lexeme == null) { - processBuffer(); - return 0; - } - - // we've found a 0 width match and we're stuck, so we need to advance - // this happens when we have badly behaved rules that have optional matchers to the degree that - // sometimes they can end up matching nothing at all - // Ref: https://github.com/highlightjs/highlight.js/issues/2140 - if (lastMatch.type === "begin" && match.type === "end" && lastMatch.index === match.index && lexeme === "") { - // spit the "skipped" character that our regex choked on back into the output sequence - modeBuffer += codeToHighlight.slice(match.index, match.index + 1); - if (!SAFE_MODE) { - /** @type {AnnotatedError} */ - var err = new Error('0 width match regex'); - err.languageName = languageName; - err.badRule = lastMatch.rule; - throw err; - } - return 1; - } - lastMatch = match; - if (match.type === "begin") { - return doBeginMatch(match); - } else if (match.type === "illegal" && !ignoreIllegals) { - // illegal match, we do not continue processing - /** @type {AnnotatedError} */ - var _err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '') + '"'); - _err.mode = top; - throw _err; - } else if (match.type === "end") { - var processed = doEndMatch(match); - if (processed !== NO_MATCH) { - return processed; - } - } - - // edge case for when illegal matches $ (end of line) which is technically - // a 0 width match but not a begin/end match so it's not caught by the - // first handler (when ignoreIllegals is true) - if (match.type === "illegal" && lexeme === "") { - // advance so we aren't stuck in an infinite loop - return 1; - } - - // infinite loops are BAD, this is a last ditch catch all. if we have a - // decent number of iterations yet our index (cursor position in our - // parsing) still 3x behind our index then something is very wrong - // so we bail - if (iterations > 100000 && iterations > match.index * 3) { - var _err2 = new Error('potential infinite loop, way more iterations than matches'); - throw _err2; - } - - /* - Why might be find ourselves here? Only one occasion now. An end match that was - triggered but could not be completed. When might this happen? When an `endSameasBegin` - rule sets the end rule to a specific match. Since the overall mode termination rule that's - being used to scan the text isn't recompiled that means that any match that LOOKS like - the end (but is not, because it is not an exact match to the beginning) will - end up here. A definite end match, but when `doEndMatch` tries to "reapply" - the end rule and fails to match, we wind up here, and just silently ignore the end. - This causes no real harm other than stopping a few times too many. - */ - - modeBuffer += lexeme; - return lexeme.length; - } - var language = getLanguage(languageName); - if (!language) { - error(LANGUAGE_NOT_FOUND.replace("{}", languageName)); - throw new Error('Unknown language: "' + languageName + '"'); - } - var md = compileLanguage(language, { - plugins: plugins - }); - var result = ''; - /** @type {CompiledMode} */ - var top = continuation || md; - /** @type Record */ - var continuations = {}; // keep continuations for sub-languages - var emitter = new options.__emitter(options); - processContinuations(); - var modeBuffer = ''; - var relevance = 0; - var index = 0; - var iterations = 0; - var resumeScanAtSamePosition = false; - try { - top.matcher.considerAll(); - for (;;) { - iterations++; - if (resumeScanAtSamePosition) { - // only regexes not matched previously will now be - // considered for a potential match - resumeScanAtSamePosition = false; - } else { - top.matcher.considerAll(); - } - top.matcher.lastIndex = index; - var match = top.matcher.exec(codeToHighlight); - // console.log("match", match[0], match.rule && match.rule.begin) - - if (!match) break; - var beforeMatch = codeToHighlight.substring(index, match.index); - var processedCount = processLexeme(beforeMatch, match); - index = match.index + processedCount; - } - processLexeme(codeToHighlight.substr(index)); - emitter.closeAllNodes(); - emitter.finalize(); - result = emitter.toHTML(); - return { - // avoid possible breakage with v10 clients expecting - // this to always be an integer - relevance: Math.floor(relevance), - value: result, - language: languageName, - illegal: false, - emitter: emitter, - top: top - }; - } catch (err) { - if (err.message && err.message.includes('Illegal')) { - return { - illegal: true, - illegalBy: { - msg: err.message, - context: codeToHighlight.slice(index - 100, index + 100), - mode: err.mode - }, - sofar: result, - relevance: 0, - value: escape$1(codeToHighlight), - emitter: emitter - }; - } else if (SAFE_MODE) { - return { - illegal: false, - relevance: 0, - value: escape$1(codeToHighlight), - emitter: emitter, - language: languageName, - top: top, - errorRaised: err - }; - } else { - throw err; - } - } - } - - /** - * returns a valid highlight result, without actually doing any actual work, - * auto highlight starts with this and it's possible for small snippets that - * auto-detection may not find a better match - * @param {string} code - * @returns {HighlightResult} - */ - function justTextHighlightResult(code) { - var result = { - relevance: 0, - emitter: new options.__emitter(options), - value: escape$1(code), - illegal: false, - top: PLAINTEXT_LANGUAGE - }; - result.emitter.addText(code); - return result; - } - - /** - Highlighting with language detection. Accepts a string with the code to - highlight. Returns an object with the following properties: - - language (detected language) - - relevance (int) - - value (an HTML string with highlighting markup) - - second_best (object with the same structure for second-best heuristically - detected language, may be absent) - @param {string} code - @param {Array} [languageSubset] - @returns {AutoHighlightResult} - */ - function highlightAuto(code, languageSubset) { - languageSubset = languageSubset || options.languages || Object.keys(languages); - var plaintext = justTextHighlightResult(code); - var results = languageSubset.filter(getLanguage).filter(autoDetection).map(function (name) { - return _highlight(name, code, false); - }); - results.unshift(plaintext); // plaintext is always an option - - var sorted = results.sort(function (a, b) { - // sort base on relevance - if (a.relevance !== b.relevance) return b.relevance - a.relevance; - - // always award the tie to the base language - // ie if C++ and Arduino are tied, it's more likely to be C++ - if (a.language && b.language) { - if (getLanguage(a.language).supersetOf === b.language) { - return 1; - } else if (getLanguage(b.language).supersetOf === a.language) { - return -1; - } - } - - // otherwise say they are equal, which has the effect of sorting on - // relevance while preserving the original ordering - which is how ties - // have historically been settled, ie the language that comes first always - // wins in the case of a tie - return 0; - }); - var _sorted = _slicedToArray(sorted, 2), - best = _sorted[0], - secondBest = _sorted[1]; - - /** @type {AutoHighlightResult} */ - var result = best; - result.second_best = secondBest; - return result; - } - - /** - Post-processing of the highlighted markup: - - replace TABs with something more useful - - replace real line-breaks with '
    ' for non-pre containers - @param {string} html - @returns {string} - */ - function fixMarkup(html) { - if (!(options.tabReplace || options.useBR)) { - return html; - } - return html.replace(fixMarkupRe, function (match) { - if (match === '\n') { - return options.useBR ? '
    ' : match; - } else if (options.tabReplace) { - return match.replace(/\t/g, options.tabReplace); - } - return match; - }); - } - - /** - * Builds new class name for block given the language name - * - * @param {HTMLElement} element - * @param {string} [currentLang] - * @param {string} [resultLang] - */ - function updateClassName(element, currentLang, resultLang) { - var language = currentLang ? aliases[currentLang] : resultLang; - element.classList.add("hljs"); - if (language) element.classList.add(language); - } - - /** @type {HLJSPlugin} */ - var brPlugin = { - "before:highlightElement": function beforeHighlightElement(_ref6) { - var el = _ref6.el; - if (options.useBR) { - el.innerHTML = el.innerHTML.replace(/\n/g, '').replace(//g, '\n'); - } - }, - "after:highlightElement": function afterHighlightElement(_ref7) { - var result = _ref7.result; - if (options.useBR) { - result.value = result.value.replace(/\n/g, "
    "); - } - } - }; - var TAB_REPLACE_RE = /^(<[^>]+>|\t)+/gm; - /** @type {HLJSPlugin} */ - var tabReplacePlugin = { - "after:highlightElement": function afterHighlightElement(_ref8) { - var result = _ref8.result; - if (options.tabReplace) { - result.value = result.value.replace(TAB_REPLACE_RE, function (m) { - return m.replace(/\t/g, options.tabReplace); - }); - } - } - }; - - /** - * Applies highlighting to a DOM node containing code. Accepts a DOM node and - * two optional parameters for fixMarkup. - * - * @param {HighlightedHTMLElement} element - the HTML element to highlight - */ - function highlightElement(element) { - /** @type HTMLElement */ - var node = null; - var language = blockLanguage(element); - if (shouldNotHighlight(language)) return; - - // support for v10 API - fire("before:highlightElement", { - el: element, - language: language - }); - node = element; - var text = node.textContent; - var result = language ? highlight(text, { - language: language, - ignoreIllegals: true - }) : highlightAuto(text); - - // support for v10 API - fire("after:highlightElement", { - el: element, - result: result, - text: text - }); - element.innerHTML = result.value; - updateClassName(element, language, result.language); - element.result = { - language: result.language, - // TODO: remove with version 11.0 - re: result.relevance, - relavance: result.relevance - }; - if (result.second_best) { - element.second_best = { - language: result.second_best.language, - // TODO: remove with version 11.0 - re: result.second_best.relevance, - relavance: result.second_best.relevance - }; - } - } - - /** - * Updates highlight.js global options with the passed options - * - * @param {Partial} userOptions - */ - function configure(userOptions) { - if (userOptions.useBR) { - deprecated("10.3.0", "'useBR' will be removed entirely in v11.0"); - deprecated("10.3.0", "Please see https://github.com/highlightjs/highlight.js/issues/2559"); - } - options = inherit$1(options, userOptions); - } - - /** - * Highlights to all
     blocks on a page
    -   *
    -   * @type {Function & {called?: boolean}}
    -   */
    -  // TODO: remove v12, deprecated
    -  var initHighlighting = function initHighlighting() {
    -    if (initHighlighting.called) return;
    -    initHighlighting.called = true;
    -    deprecated("10.6.0", "initHighlighting() is deprecated.  Use highlightAll() instead.");
    -    var blocks = document.querySelectorAll('pre code');
    -    blocks.forEach(highlightElement);
    -  };
    -
    -  // Higlights all when DOMContentLoaded fires
    -  // TODO: remove v12, deprecated
    -  function initHighlightingOnLoad() {
    -    deprecated("10.6.0", "initHighlightingOnLoad() is deprecated.  Use highlightAll() instead.");
    -    wantsHighlight = true;
    -  }
    -  var wantsHighlight = false;
    -
    -  /**
    -   * auto-highlights all pre>code elements on the page
    -   */
    -  function highlightAll() {
    -    // if we are called too early in the loading process
    -    if (document.readyState === "loading") {
    -      wantsHighlight = true;
    -      return;
    -    }
    -    var blocks = document.querySelectorAll('pre code');
    -    blocks.forEach(highlightElement);
    -  }
    -  function boot() {
    -    // if a highlight was requested before DOM was loaded, do now
    -    if (wantsHighlight) highlightAll();
    -  }
    -
    -  // make sure we are in the browser environment
    -  if (typeof window !== 'undefined' && window.addEventListener) {
    -    window.addEventListener('DOMContentLoaded', boot, false);
    -  }
    -
    -  /**
    -   * Register a language grammar module
    -   *
    -   * @param {string} languageName
    -   * @param {LanguageFn} languageDefinition
    -   */
    -  function registerLanguage(languageName, languageDefinition) {
    -    var lang = null;
    -    try {
    -      lang = languageDefinition(hljs);
    -    } catch (error$1) {
    -      error("Language definition for '{}' could not be registered.".replace("{}", languageName));
    -      // hard or soft error
    -      if (!SAFE_MODE) {
    -        throw error$1;
    -      } else {
    -        error(error$1);
    -      }
    -      // languages that have serious errors are replaced with essentially a
    -      // "plaintext" stand-in so that the code blocks will still get normal
    -      // css classes applied to them - and one bad language won't break the
    -      // entire highlighter
    -      lang = PLAINTEXT_LANGUAGE;
    -    }
    -    // give it a temporary name if it doesn't have one in the meta-data
    -    if (!lang.name) lang.name = languageName;
    -    languages[languageName] = lang;
    -    lang.rawDefinition = languageDefinition.bind(null, hljs);
    -    if (lang.aliases) {
    -      registerAliases(lang.aliases, {
    -        languageName: languageName
    -      });
    -    }
    -  }
    -
    -  /**
    -   * Remove a language grammar module
    -   *
    -   * @param {string} languageName
    -   */
    -  function unregisterLanguage(languageName) {
    -    delete languages[languageName];
    -    for (var _i2 = 0, _Object$keys = Object.keys(aliases); _i2 < _Object$keys.length; _i2++) {
    -      var alias = _Object$keys[_i2];
    -      if (aliases[alias] === languageName) {
    -        delete aliases[alias];
    -      }
    -    }
    -  }
    -
    -  /**
    -   * @returns {string[]} List of language internal names
    -   */
    -  function listLanguages() {
    -    return Object.keys(languages);
    -  }
    -
    -  /**
    -    intended usage: When one language truly requires another
    -     Unlike `getLanguage`, this will throw when the requested language
    -    is not available.
    -     @param {string} name - name of the language to fetch/require
    -    @returns {Language | never}
    -  */
    -  function requireLanguage(name) {
    -    deprecated("10.4.0", "requireLanguage will be removed entirely in v11.");
    -    deprecated("10.4.0", "Please see https://github.com/highlightjs/highlight.js/pull/2844");
    -    var lang = getLanguage(name);
    -    if (lang) {
    -      return lang;
    -    }
    -    var err = new Error('The \'{}\' language is required, but not loaded.'.replace('{}', name));
    -    throw err;
    -  }
    -
    -  /**
    -   * @param {string} name - name of the language to retrieve
    -   * @returns {Language | undefined}
    -   */
    -  function getLanguage(name) {
    -    name = (name || '').toLowerCase();
    -    return languages[name] || languages[aliases[name]];
    -  }
    -
    -  /**
    -   *
    -   * @param {string|string[]} aliasList - single alias or list of aliases
    -   * @param {{languageName: string}} opts
    -   */
    -  function registerAliases(aliasList, _ref9) {
    -    var languageName = _ref9.languageName;
    -    if (typeof aliasList === 'string') {
    -      aliasList = [aliasList];
    -    }
    -    aliasList.forEach(function (alias) {
    -      aliases[alias.toLowerCase()] = languageName;
    -    });
    -  }
    -
    -  /**
    -   * Determines if a given language has auto-detection enabled
    -   * @param {string} name - name of the language
    -   */
    -  function autoDetection(name) {
    -    var lang = getLanguage(name);
    -    return lang && !lang.disableAutodetect;
    -  }
    -
    -  /**
    -   * Upgrades the old highlightBlock plugins to the new
    -   * highlightElement API
    -   * @param {HLJSPlugin} plugin
    -   */
    -  function upgradePluginAPI(plugin) {
    -    // TODO: remove with v12
    -    if (plugin["before:highlightBlock"] && !plugin["before:highlightElement"]) {
    -      plugin["before:highlightElement"] = function (data) {
    -        plugin["before:highlightBlock"](Object.assign({
    -          block: data.el
    -        }, data));
    -      };
    -    }
    -    if (plugin["after:highlightBlock"] && !plugin["after:highlightElement"]) {
    -      plugin["after:highlightElement"] = function (data) {
    -        plugin["after:highlightBlock"](Object.assign({
    -          block: data.el
    -        }, data));
    -      };
    -    }
    -  }
    -
    -  /**
    -   * @param {HLJSPlugin} plugin
    -   */
    -  function addPlugin(plugin) {
    -    upgradePluginAPI(plugin);
    -    plugins.push(plugin);
    -  }
    -
    -  /**
    -   *
    -   * @param {PluginEvent} event
    -   * @param {any} args
    -   */
    -  function fire(event, args) {
    -    var cb = event;
    -    plugins.forEach(function (plugin) {
    -      if (plugin[cb]) {
    -        plugin[cb](args);
    -      }
    -    });
    -  }
    -
    -  /**
    -  Note: fixMarkup is deprecated and will be removed entirely in v11
    -   @param {string} arg
    -  @returns {string}
    -  */
    -  function deprecateFixMarkup(arg) {
    -    deprecated("10.2.0", "fixMarkup will be removed entirely in v11.0");
    -    deprecated("10.2.0", "Please see https://github.com/highlightjs/highlight.js/issues/2534");
    -    return fixMarkup(arg);
    -  }
    -
    -  /**
    -   *
    -   * @param {HighlightedHTMLElement} el
    -   */
    -  function deprecateHighlightBlock(el) {
    -    deprecated("10.7.0", "highlightBlock will be removed entirely in v12.0");
    -    deprecated("10.7.0", "Please use highlightElement now.");
    -    return highlightElement(el);
    -  }
    -
    -  /* Interface definition */
    -  Object.assign(hljs, {
    -    highlight: highlight,
    -    highlightAuto: highlightAuto,
    -    highlightAll: highlightAll,
    -    fixMarkup: deprecateFixMarkup,
    -    highlightElement: highlightElement,
    -    // TODO: Remove with v12 API
    -    highlightBlock: deprecateHighlightBlock,
    -    configure: configure,
    -    initHighlighting: initHighlighting,
    -    initHighlightingOnLoad: initHighlightingOnLoad,
    -    registerLanguage: registerLanguage,
    -    unregisterLanguage: unregisterLanguage,
    -    listLanguages: listLanguages,
    -    getLanguage: getLanguage,
    -    registerAliases: registerAliases,
    -    requireLanguage: requireLanguage,
    -    autoDetection: autoDetection,
    -    inherit: inherit$1,
    -    addPlugin: addPlugin,
    -    // plugins for frameworks
    -    vuePlugin: BuildVuePlugin(hljs).VuePlugin
    -  });
    -  hljs.debugMode = function () {
    -    SAFE_MODE = false;
    -  };
    -  hljs.safeMode = function () {
    -    SAFE_MODE = true;
    -  };
    -  hljs.versionString = version;
    -  for (var key in MODES) {
    -    // @ts-ignore
    -    if (typeof MODES[key] === "object") {
    -      // @ts-ignore
    -      deepFreezeEs6(MODES[key]);
    -    }
    -  }
    -
    -  // merge all the modes/regexs into our main object
    -  Object.assign(hljs, MODES);
    -
    -  // built-in plugins, likely to be moved out of core in the future
    -  hljs.addPlugin(brPlugin); // slated to be removed in v11
    -  hljs.addPlugin(mergeHTMLPlugin);
    -  hljs.addPlugin(tabReplacePlugin);
    -  return hljs;
    -};
    -
    -// export an "instance" of the highlighter
    -var highlight = HLJS({});
    -module.exports = highlight;
    -
    -/***/ }),
    -
    -/***/ 682:
    -/***/ (function(module) {
    -
    -/*
    -Language: Diff
    -Description: Unified and context diff
    -Author: Vasily Polovnyov 
    -Website: https://www.gnu.org/software/diffutils/
    -Category: common
    -*/
    -
    -/** @type LanguageFn */
    -function diff(hljs) {
    -  return {
    -    name: 'Diff',
    -    aliases: ['patch'],
    -    contains: [{
    -      className: 'meta',
    -      relevance: 10,
    -      variants: [{
    -        begin: /^@@ +-\d+,\d+ +\+\d+,\d+ +@@/
    -      }, {
    -        begin: /^\*\*\* +\d+,\d+ +\*\*\*\*$/
    -      }, {
    -        begin: /^--- +\d+,\d+ +----$/
    -      }]
    -    }, {
    -      className: 'comment',
    -      variants: [{
    -        begin: /Index: /,
    -        end: /$/
    -      }, {
    -        begin: /^index/,
    -        end: /$/
    -      }, {
    -        begin: /={3,}/,
    -        end: /$/
    -      }, {
    -        begin: /^-{3}/,
    -        end: /$/
    -      }, {
    -        begin: /^\*{3} /,
    -        end: /$/
    -      }, {
    -        begin: /^\+{3}/,
    -        end: /$/
    -      }, {
    -        begin: /^\*{15}$/
    -      }, {
    -        begin: /^diff --git/,
    -        end: /$/
    -      }]
    -    }, {
    -      className: 'addition',
    -      begin: /^\+/,
    -      end: /$/
    -    }, {
    -      className: 'deletion',
    -      begin: /^-/,
    -      end: /$/
    -    }, {
    -      className: 'addition',
    -      begin: /^!/,
    -      end: /$/
    -    }]
    -  };
    -}
    -module.exports = diff;
    -
    -/***/ }),
    -
    -/***/ 712:
    -/***/ (function(module) {
    -
    -/*
    -Language: YAML
    -Description: Yet Another Markdown Language
    -Author: Stefan Wienert 
    -Contributors: Carl Baxter 
    -Requires: ruby.js
    -Website: https://yaml.org
    -Category: common, config
    -*/
    -function yaml(hljs) {
    -  var LITERALS = 'true false yes no null';
    -
    -  // YAML spec allows non-reserved URI characters in tags.
    -  var URI_CHARACTERS = '[\\w#;/?:@&=+$,.~*\'()[\\]]+';
    -
    -  // Define keys as starting with a word character
    -  // ...containing word chars, spaces, colons, forward-slashes, hyphens and periods
    -  // ...and ending with a colon followed immediately by a space, tab or newline.
    -  // The YAML spec allows for much more than this, but this covers most use-cases.
    -  var KEY = {
    -    className: 'attr',
    -    variants: [{
    -      begin: '\\w[\\w :\\/.-]*:(?=[ \t]|$)'
    -    }, {
    -      begin: '"\\w[\\w :\\/.-]*":(?=[ \t]|$)'
    -    },
    -    // double quoted keys
    -    {
    -      begin: '\'\\w[\\w :\\/.-]*\':(?=[ \t]|$)'
    -    } // single quoted keys
    -    ]
    -  };
    -
    -  var TEMPLATE_VARIABLES = {
    -    className: 'template-variable',
    -    variants: [{
    -      begin: /\{\{/,
    -      end: /\}\}/
    -    },
    -    // jinja templates Ansible
    -    {
    -      begin: /%\{/,
    -      end: /\}/
    -    } // Ruby i18n
    -    ]
    -  };
    -
    -  var STRING = {
    -    className: 'string',
    -    relevance: 0,
    -    variants: [{
    -      begin: /'/,
    -      end: /'/
    -    }, {
    -      begin: /"/,
    -      end: /"/
    -    }, {
    -      begin: /\S+/
    -    }],
    -    contains: [hljs.BACKSLASH_ESCAPE, TEMPLATE_VARIABLES]
    -  };
    -
    -  // Strings inside of value containers (objects) can't contain braces,
    -  // brackets, or commas
    -  var CONTAINER_STRING = hljs.inherit(STRING, {
    -    variants: [{
    -      begin: /'/,
    -      end: /'/
    -    }, {
    -      begin: /"/,
    -      end: /"/
    -    }, {
    -      begin: /[^\s,{}[\]]+/
    -    }]
    -  });
    -  var DATE_RE = '[0-9]{4}(-[0-9][0-9]){0,2}';
    -  var TIME_RE = '([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?';
    -  var FRACTION_RE = '(\\.[0-9]*)?';
    -  var ZONE_RE = '([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?';
    -  var TIMESTAMP = {
    -    className: 'number',
    -    begin: '\\b' + DATE_RE + TIME_RE + FRACTION_RE + ZONE_RE + '\\b'
    -  };
    -  var VALUE_CONTAINER = {
    -    end: ',',
    -    endsWithParent: true,
    -    excludeEnd: true,
    -    keywords: LITERALS,
    -    relevance: 0
    -  };
    -  var OBJECT = {
    -    begin: /\{/,
    -    end: /\}/,
    -    contains: [VALUE_CONTAINER],
    -    illegal: '\\n',
    -    relevance: 0
    -  };
    -  var ARRAY = {
    -    begin: '\\[',
    -    end: '\\]',
    -    contains: [VALUE_CONTAINER],
    -    illegal: '\\n',
    -    relevance: 0
    -  };
    -  var MODES = [KEY, {
    -    className: 'meta',
    -    begin: '^---\\s*$',
    -    relevance: 10
    -  }, {
    -    // multi line string
    -    // Blocks start with a | or > followed by a newline
    -    //
    -    // Indentation of subsequent lines must be the same to
    -    // be considered part of the block
    -    className: 'string',
    -    begin: '[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*'
    -  }, {
    -    // Ruby/Rails erb
    -    begin: '<%[%=-]?',
    -    end: '[%-]?%>',
    -    subLanguage: 'ruby',
    -    excludeBegin: true,
    -    excludeEnd: true,
    -    relevance: 0
    -  }, {
    -    // named tags
    -    className: 'type',
    -    begin: '!\\w+!' + URI_CHARACTERS
    -  },
    -  // https://yaml.org/spec/1.2/spec.html#id2784064
    -  {
    -    // verbatim tags
    -    className: 'type',
    -    begin: '!<' + URI_CHARACTERS + ">"
    -  }, {
    -    // primary tags
    -    className: 'type',
    -    begin: '!' + URI_CHARACTERS
    -  }, {
    -    // secondary tags
    -    className: 'type',
    -    begin: '!!' + URI_CHARACTERS
    -  }, {
    -    // fragment id &ref
    -    className: 'meta',
    -    begin: '&' + hljs.UNDERSCORE_IDENT_RE + '$'
    -  }, {
    -    // fragment reference *ref
    -    className: 'meta',
    -    begin: '\\*' + hljs.UNDERSCORE_IDENT_RE + '$'
    -  }, {
    -    // array listing
    -    className: 'bullet',
    -    // TODO: remove |$ hack when we have proper look-ahead support
    -    begin: '-(?=[ ]|$)',
    -    relevance: 0
    -  }, hljs.HASH_COMMENT_MODE, {
    -    beginKeywords: LITERALS,
    -    keywords: {
    -      literal: LITERALS
    -    }
    -  }, TIMESTAMP,
    -  // numbers are any valid C-style number that
    -  // sit isolated from other words
    -  {
    -    className: 'number',
    -    begin: hljs.C_NUMBER_RE + '\\b',
    -    relevance: 0
    -  }, OBJECT, ARRAY, STRING];
    -  var VALUE_MODES = [].concat(MODES);
    -  VALUE_MODES.pop();
    -  VALUE_MODES.push(CONTAINER_STRING);
    -  VALUE_CONTAINER.contains = VALUE_MODES;
    -  return {
    -    name: 'YAML',
    -    case_insensitive: true,
    -    aliases: ['yml'],
    -    contains: MODES
    -  };
    -}
    -module.exports = yaml;
    -
    -/***/ }),
    -
    -/***/ 110:
    -/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
    -
    -"use strict";
    -
    -
    -var reactIs = __webpack_require__(309);
    -
    -/**
    - * Copyright 2015, Yahoo! Inc.
    - * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
    - */
    -var REACT_STATICS = {
    -  childContextTypes: true,
    -  contextType: true,
    -  contextTypes: true,
    -  defaultProps: true,
    -  displayName: true,
    -  getDefaultProps: true,
    -  getDerivedStateFromError: true,
    -  getDerivedStateFromProps: true,
    -  mixins: true,
    -  propTypes: true,
    -  type: true
    -};
    -var KNOWN_STATICS = {
    -  name: true,
    -  length: true,
    -  prototype: true,
    -  caller: true,
    -  callee: true,
    -  arguments: true,
    -  arity: true
    -};
    -var FORWARD_REF_STATICS = {
    -  '$$typeof': true,
    -  render: true,
    -  defaultProps: true,
    -  displayName: true,
    -  propTypes: true
    -};
    -var MEMO_STATICS = {
    -  '$$typeof': true,
    -  compare: true,
    -  defaultProps: true,
    -  displayName: true,
    -  propTypes: true,
    -  type: true
    -};
    -var TYPE_STATICS = {};
    -TYPE_STATICS[reactIs.ForwardRef] = FORWARD_REF_STATICS;
    -TYPE_STATICS[reactIs.Memo] = MEMO_STATICS;
    -function getStatics(component) {
    -  // React v16.11 and below
    -  if (reactIs.isMemo(component)) {
    -    return MEMO_STATICS;
    -  } // React v16.12 and above
    -
    -  return TYPE_STATICS[component['$$typeof']] || REACT_STATICS;
    -}
    -var defineProperty = Object.defineProperty;
    -var getOwnPropertyNames = Object.getOwnPropertyNames;
    -var getOwnPropertySymbols = Object.getOwnPropertySymbols;
    -var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
    -var getPrototypeOf = Object.getPrototypeOf;
    -var objectPrototype = Object.prototype;
    -function hoistNonReactStatics(targetComponent, sourceComponent, blacklist) {
    -  if (typeof sourceComponent !== 'string') {
    -    // don't hoist over string (html) components
    -    if (objectPrototype) {
    -      var inheritedComponent = getPrototypeOf(sourceComponent);
    -      if (inheritedComponent && inheritedComponent !== objectPrototype) {
    -        hoistNonReactStatics(targetComponent, inheritedComponent, blacklist);
    -      }
    -    }
    -    var keys = getOwnPropertyNames(sourceComponent);
    -    if (getOwnPropertySymbols) {
    -      keys = keys.concat(getOwnPropertySymbols(sourceComponent));
    -    }
    -    var targetStatics = getStatics(targetComponent);
    -    var sourceStatics = getStatics(sourceComponent);
    -    for (var i = 0; i < keys.length; ++i) {
    -      var key = keys[i];
    -      if (!KNOWN_STATICS[key] && !(blacklist && blacklist[key]) && !(sourceStatics && sourceStatics[key]) && !(targetStatics && targetStatics[key])) {
    -        var descriptor = getOwnPropertyDescriptor(sourceComponent, key);
    -        try {
    -          // Avoid failures from read-only properties
    -          defineProperty(targetComponent, key, descriptor);
    -        } catch (e) {}
    -      }
    -    }
    -  }
    -  return targetComponent;
    -}
    -module.exports = hoistNonReactStatics;
    -
    -/***/ }),
    -
    -/***/ 746:
    -/***/ (function(__unused_webpack_module, exports) {
    -
    -"use strict";
    -/** @license React v16.13.1
    - * react-is.production.min.js
    - *
    - * Copyright (c) Facebook, Inc. and its affiliates.
    - *
    - * This source code is licensed under the MIT license found in the
    - * LICENSE file in the root directory of this source tree.
    - */
    -
    -
    -
    -var b = "function" === typeof Symbol && Symbol.for,
    -  c = b ? Symbol.for("react.element") : 60103,
    -  d = b ? Symbol.for("react.portal") : 60106,
    -  e = b ? Symbol.for("react.fragment") : 60107,
    -  f = b ? Symbol.for("react.strict_mode") : 60108,
    -  g = b ? Symbol.for("react.profiler") : 60114,
    -  h = b ? Symbol.for("react.provider") : 60109,
    -  k = b ? Symbol.for("react.context") : 60110,
    -  l = b ? Symbol.for("react.async_mode") : 60111,
    -  m = b ? Symbol.for("react.concurrent_mode") : 60111,
    -  n = b ? Symbol.for("react.forward_ref") : 60112,
    -  p = b ? Symbol.for("react.suspense") : 60113,
    -  q = b ? Symbol.for("react.suspense_list") : 60120,
    -  r = b ? Symbol.for("react.memo") : 60115,
    -  t = b ? Symbol.for("react.lazy") : 60116,
    -  v = b ? Symbol.for("react.block") : 60121,
    -  w = b ? Symbol.for("react.fundamental") : 60117,
    -  x = b ? Symbol.for("react.responder") : 60118,
    -  y = b ? Symbol.for("react.scope") : 60119;
    -function z(a) {
    -  if ("object" === typeof a && null !== a) {
    -    var u = a.$$typeof;
    -    switch (u) {
    -      case c:
    -        switch (a = a.type, a) {
    -          case l:
    -          case m:
    -          case e:
    -          case g:
    -          case f:
    -          case p:
    -            return a;
    -          default:
    -            switch (a = a && a.$$typeof, a) {
    -              case k:
    -              case n:
    -              case t:
    -              case r:
    -              case h:
    -                return a;
    -              default:
    -                return u;
    -            }
    -        }
    -      case d:
    -        return u;
    -    }
    -  }
    -}
    -function A(a) {
    -  return z(a) === m;
    -}
    -exports.AsyncMode = l;
    -exports.ConcurrentMode = m;
    -exports.ContextConsumer = k;
    -exports.ContextProvider = h;
    -exports.Element = c;
    -exports.ForwardRef = n;
    -exports.Fragment = e;
    -exports.Lazy = t;
    -exports.Memo = r;
    -exports.Portal = d;
    -exports.Profiler = g;
    -exports.StrictMode = f;
    -exports.Suspense = p;
    -exports.isAsyncMode = function (a) {
    -  return A(a) || z(a) === l;
    -};
    -exports.isConcurrentMode = A;
    -exports.isContextConsumer = function (a) {
    -  return z(a) === k;
    -};
    -exports.isContextProvider = function (a) {
    -  return z(a) === h;
    -};
    -exports.isElement = function (a) {
    -  return "object" === typeof a && null !== a && a.$$typeof === c;
    -};
    -exports.isForwardRef = function (a) {
    -  return z(a) === n;
    -};
    -exports.isFragment = function (a) {
    -  return z(a) === e;
    -};
    -exports.isLazy = function (a) {
    -  return z(a) === t;
    -};
    -exports.isMemo = function (a) {
    -  return z(a) === r;
    -};
    -exports.isPortal = function (a) {
    -  return z(a) === d;
    -};
    -exports.isProfiler = function (a) {
    -  return z(a) === g;
    -};
    -exports.isStrictMode = function (a) {
    -  return z(a) === f;
    -};
    -exports.isSuspense = function (a) {
    -  return z(a) === p;
    -};
    -exports.isValidElementType = function (a) {
    -  return "string" === typeof a || "function" === typeof a || a === e || a === m || a === g || a === f || a === p || a === q || "object" === typeof a && null !== a && (a.$$typeof === t || a.$$typeof === r || a.$$typeof === h || a.$$typeof === k || a.$$typeof === n || a.$$typeof === w || a.$$typeof === x || a.$$typeof === y || a.$$typeof === v);
    -};
    -exports.typeOf = z;
    -
    -/***/ }),
    -
    -/***/ 309:
    -/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
    -
    -"use strict";
    -
    -
    -if (true) {
    -  module.exports = __webpack_require__(746);
    -} else {}
    -
    -/***/ }),
    -
    -/***/ 981:
    -/***/ (function(module, exports, __webpack_require__) {
    -
    -var __WEBPACK_AMD_DEFINE_RESULT__;/**
    - * [js-sha256]{@link https://github.com/emn178/js-sha256}
    - *
    - * @version 0.9.0
    - * @author Chen, Yi-Cyuan [emn178@gmail.com]
    - * @copyright Chen, Yi-Cyuan 2014-2017
    - * @license MIT
    - */
    -/*jslint bitwise: true */
    -(function () {
    -  'use strict';
    -
    -  var ERROR = 'input is invalid type';
    -  var WINDOW = typeof window === 'object';
    -  var root = WINDOW ? window : {};
    -  if (root.JS_SHA256_NO_WINDOW) {
    -    WINDOW = false;
    -  }
    -  var WEB_WORKER = !WINDOW && typeof self === 'object';
    -  var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
    -  if (NODE_JS) {
    -    root = __webpack_require__.g;
    -  } else if (WEB_WORKER) {
    -    root = self;
    -  }
    -  var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && "object" === 'object' && module.exports;
    -  var AMD =  true && __webpack_require__.amdO;
    -  var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
    -  var HEX_CHARS = '0123456789abcdef'.split('');
    -  var EXTRA = [-2147483648, 8388608, 32768, 128];
    -  var SHIFT = [24, 16, 8, 0];
    -  var K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
    -  var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer'];
    -  var blocks = [];
    -  if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) {
    -    Array.isArray = function (obj) {
    -      return Object.prototype.toString.call(obj) === '[object Array]';
    -    };
    -  }
    -  if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
    -    ArrayBuffer.isView = function (obj) {
    -      return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
    -    };
    -  }
    -  var createOutputMethod = function createOutputMethod(outputType, is224) {
    -    return function (message) {
    -      return new Sha256(is224, true).update(message)[outputType]();
    -    };
    -  };
    -  var createMethod = function createMethod(is224) {
    -    var method = createOutputMethod('hex', is224);
    -    if (NODE_JS) {
    -      method = nodeWrap(method, is224);
    -    }
    -    method.create = function () {
    -      return new Sha256(is224);
    -    };
    -    method.update = function (message) {
    -      return method.create().update(message);
    -    };
    -    for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
    -      var type = OUTPUT_TYPES[i];
    -      method[type] = createOutputMethod(type, is224);
    -    }
    -    return method;
    -  };
    -  var nodeWrap = function nodeWrap(method, is224) {
    -    var crypto = eval("require('crypto')");
    -    var Buffer = eval("require('buffer').Buffer");
    -    var algorithm = is224 ? 'sha224' : 'sha256';
    -    var nodeMethod = function nodeMethod(message) {
    -      if (typeof message === 'string') {
    -        return crypto.createHash(algorithm).update(message, 'utf8').digest('hex');
    -      } else {
    -        if (message === null || message === undefined) {
    -          throw new Error(ERROR);
    -        } else if (message.constructor === ArrayBuffer) {
    -          message = new Uint8Array(message);
    -        }
    -      }
    -      if (Array.isArray(message) || ArrayBuffer.isView(message) || message.constructor === Buffer) {
    -        return crypto.createHash(algorithm).update(new Buffer(message)).digest('hex');
    -      } else {
    -        return method(message);
    -      }
    -    };
    -    return nodeMethod;
    -  };
    -  var createHmacOutputMethod = function createHmacOutputMethod(outputType, is224) {
    -    return function (key, message) {
    -      return new HmacSha256(key, is224, true).update(message)[outputType]();
    -    };
    -  };
    -  var createHmacMethod = function createHmacMethod(is224) {
    -    var method = createHmacOutputMethod('hex', is224);
    -    method.create = function (key) {
    -      return new HmacSha256(key, is224);
    -    };
    -    method.update = function (key, message) {
    -      return method.create(key).update(message);
    -    };
    -    for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
    -      var type = OUTPUT_TYPES[i];
    -      method[type] = createHmacOutputMethod(type, is224);
    -    }
    -    return method;
    -  };
    -  function Sha256(is224, sharedMemory) {
    -    if (sharedMemory) {
    -      blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
    -      this.blocks = blocks;
    -    } else {
    -      this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    -    }
    -    if (is224) {
    -      this.h0 = 0xc1059ed8;
    -      this.h1 = 0x367cd507;
    -      this.h2 = 0x3070dd17;
    -      this.h3 = 0xf70e5939;
    -      this.h4 = 0xffc00b31;
    -      this.h5 = 0x68581511;
    -      this.h6 = 0x64f98fa7;
    -      this.h7 = 0xbefa4fa4;
    -    } else {
    -      // 256
    -      this.h0 = 0x6a09e667;
    -      this.h1 = 0xbb67ae85;
    -      this.h2 = 0x3c6ef372;
    -      this.h3 = 0xa54ff53a;
    -      this.h4 = 0x510e527f;
    -      this.h5 = 0x9b05688c;
    -      this.h6 = 0x1f83d9ab;
    -      this.h7 = 0x5be0cd19;
    -    }
    -    this.block = this.start = this.bytes = this.hBytes = 0;
    -    this.finalized = this.hashed = false;
    -    this.first = true;
    -    this.is224 = is224;
    -  }
    -  Sha256.prototype.update = function (message) {
    -    if (this.finalized) {
    -      return;
    -    }
    -    var notString,
    -      type = typeof message;
    -    if (type !== 'string') {
    -      if (type === 'object') {
    -        if (message === null) {
    -          throw new Error(ERROR);
    -        } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
    -          message = new Uint8Array(message);
    -        } else if (!Array.isArray(message)) {
    -          if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
    -            throw new Error(ERROR);
    -          }
    -        }
    -      } else {
    -        throw new Error(ERROR);
    -      }
    -      notString = true;
    -    }
    -    var code,
    -      index = 0,
    -      i,
    -      length = message.length,
    -      blocks = this.blocks;
    -    while (index < length) {
    -      if (this.hashed) {
    -        this.hashed = false;
    -        blocks[0] = this.block;
    -        blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
    -      }
    -      if (notString) {
    -        for (i = this.start; index < length && i < 64; ++index) {
    -          blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
    -        }
    -      } else {
    -        for (i = this.start; index < length && i < 64; ++index) {
    -          code = message.charCodeAt(index);
    -          if (code < 0x80) {
    -            blocks[i >> 2] |= code << SHIFT[i++ & 3];
    -          } else if (code < 0x800) {
    -            blocks[i >> 2] |= (0xc0 | code >> 6) << SHIFT[i++ & 3];
    -            blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
    -          } else if (code < 0xd800 || code >= 0xe000) {
    -            blocks[i >> 2] |= (0xe0 | code >> 12) << SHIFT[i++ & 3];
    -            blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
    -            blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
    -          } else {
    -            code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++index) & 0x3ff);
    -            blocks[i >> 2] |= (0xf0 | code >> 18) << SHIFT[i++ & 3];
    -            blocks[i >> 2] |= (0x80 | code >> 12 & 0x3f) << SHIFT[i++ & 3];
    -            blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
    -            blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
    -          }
    -        }
    -      }
    -      this.lastByteIndex = i;
    -      this.bytes += i - this.start;
    -      if (i >= 64) {
    -        this.block = blocks[16];
    -        this.start = i - 64;
    -        this.hash();
    -        this.hashed = true;
    -      } else {
    -        this.start = i;
    -      }
    -    }
    -    if (this.bytes > 4294967295) {
    -      this.hBytes += this.bytes / 4294967296 << 0;
    -      this.bytes = this.bytes % 4294967296;
    -    }
    -    return this;
    -  };
    -  Sha256.prototype.finalize = function () {
    -    if (this.finalized) {
    -      return;
    -    }
    -    this.finalized = true;
    -    var blocks = this.blocks,
    -      i = this.lastByteIndex;
    -    blocks[16] = this.block;
    -    blocks[i >> 2] |= EXTRA[i & 3];
    -    this.block = blocks[16];
    -    if (i >= 56) {
    -      if (!this.hashed) {
    -        this.hash();
    -      }
    -      blocks[0] = this.block;
    -      blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
    -    }
    -    blocks[14] = this.hBytes << 3 | this.bytes >>> 29;
    -    blocks[15] = this.bytes << 3;
    -    this.hash();
    -  };
    -  Sha256.prototype.hash = function () {
    -    var a = this.h0,
    -      b = this.h1,
    -      c = this.h2,
    -      d = this.h3,
    -      e = this.h4,
    -      f = this.h5,
    -      g = this.h6,
    -      h = this.h7,
    -      blocks = this.blocks,
    -      j,
    -      s0,
    -      s1,
    -      maj,
    -      t1,
    -      t2,
    -      ch,
    -      ab,
    -      da,
    -      cd,
    -      bc;
    -    for (j = 16; j < 64; ++j) {
    -      // rightrotate
    -      t1 = blocks[j - 15];
    -      s0 = (t1 >>> 7 | t1 << 25) ^ (t1 >>> 18 | t1 << 14) ^ t1 >>> 3;
    -      t1 = blocks[j - 2];
    -      s1 = (t1 >>> 17 | t1 << 15) ^ (t1 >>> 19 | t1 << 13) ^ t1 >>> 10;
    -      blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0;
    -    }
    -    bc = b & c;
    -    for (j = 0; j < 64; j += 4) {
    -      if (this.first) {
    -        if (this.is224) {
    -          ab = 300032;
    -          t1 = blocks[0] - 1413257819;
    -          h = t1 - 150054599 << 0;
    -          d = t1 + 24177077 << 0;
    -        } else {
    -          ab = 704751109;
    -          t1 = blocks[0] - 210244248;
    -          h = t1 - 1521486534 << 0;
    -          d = t1 + 143694565 << 0;
    -        }
    -        this.first = false;
    -      } else {
    -        s0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
    -        s1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
    -        ab = a & b;
    -        maj = ab ^ a & c ^ bc;
    -        ch = e & f ^ ~e & g;
    -        t1 = h + s1 + ch + K[j] + blocks[j];
    -        t2 = s0 + maj;
    -        h = d + t1 << 0;
    -        d = t1 + t2 << 0;
    -      }
    -      s0 = (d >>> 2 | d << 30) ^ (d >>> 13 | d << 19) ^ (d >>> 22 | d << 10);
    -      s1 = (h >>> 6 | h << 26) ^ (h >>> 11 | h << 21) ^ (h >>> 25 | h << 7);
    -      da = d & a;
    -      maj = da ^ d & b ^ ab;
    -      ch = h & e ^ ~h & f;
    -      t1 = g + s1 + ch + K[j + 1] + blocks[j + 1];
    -      t2 = s0 + maj;
    -      g = c + t1 << 0;
    -      c = t1 + t2 << 0;
    -      s0 = (c >>> 2 | c << 30) ^ (c >>> 13 | c << 19) ^ (c >>> 22 | c << 10);
    -      s1 = (g >>> 6 | g << 26) ^ (g >>> 11 | g << 21) ^ (g >>> 25 | g << 7);
    -      cd = c & d;
    -      maj = cd ^ c & a ^ da;
    -      ch = g & h ^ ~g & e;
    -      t1 = f + s1 + ch + K[j + 2] + blocks[j + 2];
    -      t2 = s0 + maj;
    -      f = b + t1 << 0;
    -      b = t1 + t2 << 0;
    -      s0 = (b >>> 2 | b << 30) ^ (b >>> 13 | b << 19) ^ (b >>> 22 | b << 10);
    -      s1 = (f >>> 6 | f << 26) ^ (f >>> 11 | f << 21) ^ (f >>> 25 | f << 7);
    -      bc = b & c;
    -      maj = bc ^ b & d ^ cd;
    -      ch = f & g ^ ~f & h;
    -      t1 = e + s1 + ch + K[j + 3] + blocks[j + 3];
    -      t2 = s0 + maj;
    -      e = a + t1 << 0;
    -      a = t1 + t2 << 0;
    -    }
    -    this.h0 = this.h0 + a << 0;
    -    this.h1 = this.h1 + b << 0;
    -    this.h2 = this.h2 + c << 0;
    -    this.h3 = this.h3 + d << 0;
    -    this.h4 = this.h4 + e << 0;
    -    this.h5 = this.h5 + f << 0;
    -    this.h6 = this.h6 + g << 0;
    -    this.h7 = this.h7 + h << 0;
    -  };
    -  Sha256.prototype.hex = function () {
    -    this.finalize();
    -    var h0 = this.h0,
    -      h1 = this.h1,
    -      h2 = this.h2,
    -      h3 = this.h3,
    -      h4 = this.h4,
    -      h5 = this.h5,
    -      h6 = this.h6,
    -      h7 = this.h7;
    -    var hex = HEX_CHARS[h0 >> 28 & 0x0F] + HEX_CHARS[h0 >> 24 & 0x0F] + HEX_CHARS[h0 >> 20 & 0x0F] + HEX_CHARS[h0 >> 16 & 0x0F] + HEX_CHARS[h0 >> 12 & 0x0F] + HEX_CHARS[h0 >> 8 & 0x0F] + HEX_CHARS[h0 >> 4 & 0x0F] + HEX_CHARS[h0 & 0x0F] + HEX_CHARS[h1 >> 28 & 0x0F] + HEX_CHARS[h1 >> 24 & 0x0F] + HEX_CHARS[h1 >> 20 & 0x0F] + HEX_CHARS[h1 >> 16 & 0x0F] + HEX_CHARS[h1 >> 12 & 0x0F] + HEX_CHARS[h1 >> 8 & 0x0F] + HEX_CHARS[h1 >> 4 & 0x0F] + HEX_CHARS[h1 & 0x0F] + HEX_CHARS[h2 >> 28 & 0x0F] + HEX_CHARS[h2 >> 24 & 0x0F] + HEX_CHARS[h2 >> 20 & 0x0F] + HEX_CHARS[h2 >> 16 & 0x0F] + HEX_CHARS[h2 >> 12 & 0x0F] + HEX_CHARS[h2 >> 8 & 0x0F] + HEX_CHARS[h2 >> 4 & 0x0F] + HEX_CHARS[h2 & 0x0F] + HEX_CHARS[h3 >> 28 & 0x0F] + HEX_CHARS[h3 >> 24 & 0x0F] + HEX_CHARS[h3 >> 20 & 0x0F] + HEX_CHARS[h3 >> 16 & 0x0F] + HEX_CHARS[h3 >> 12 & 0x0F] + HEX_CHARS[h3 >> 8 & 0x0F] + HEX_CHARS[h3 >> 4 & 0x0F] + HEX_CHARS[h3 & 0x0F] + HEX_CHARS[h4 >> 28 & 0x0F] + HEX_CHARS[h4 >> 24 & 0x0F] + HEX_CHARS[h4 >> 20 & 0x0F] + HEX_CHARS[h4 >> 16 & 0x0F] + HEX_CHARS[h4 >> 12 & 0x0F] + HEX_CHARS[h4 >> 8 & 0x0F] + HEX_CHARS[h4 >> 4 & 0x0F] + HEX_CHARS[h4 & 0x0F] + HEX_CHARS[h5 >> 28 & 0x0F] + HEX_CHARS[h5 >> 24 & 0x0F] + HEX_CHARS[h5 >> 20 & 0x0F] + HEX_CHARS[h5 >> 16 & 0x0F] + HEX_CHARS[h5 >> 12 & 0x0F] + HEX_CHARS[h5 >> 8 & 0x0F] + HEX_CHARS[h5 >> 4 & 0x0F] + HEX_CHARS[h5 & 0x0F] + HEX_CHARS[h6 >> 28 & 0x0F] + HEX_CHARS[h6 >> 24 & 0x0F] + HEX_CHARS[h6 >> 20 & 0x0F] + HEX_CHARS[h6 >> 16 & 0x0F] + HEX_CHARS[h6 >> 12 & 0x0F] + HEX_CHARS[h6 >> 8 & 0x0F] + HEX_CHARS[h6 >> 4 & 0x0F] + HEX_CHARS[h6 & 0x0F];
    -    if (!this.is224) {
    -      hex += HEX_CHARS[h7 >> 28 & 0x0F] + HEX_CHARS[h7 >> 24 & 0x0F] + HEX_CHARS[h7 >> 20 & 0x0F] + HEX_CHARS[h7 >> 16 & 0x0F] + HEX_CHARS[h7 >> 12 & 0x0F] + HEX_CHARS[h7 >> 8 & 0x0F] + HEX_CHARS[h7 >> 4 & 0x0F] + HEX_CHARS[h7 & 0x0F];
    -    }
    -    return hex;
    -  };
    -  Sha256.prototype.toString = Sha256.prototype.hex;
    -  Sha256.prototype.digest = function () {
    -    this.finalize();
    -    var h0 = this.h0,
    -      h1 = this.h1,
    -      h2 = this.h2,
    -      h3 = this.h3,
    -      h4 = this.h4,
    -      h5 = this.h5,
    -      h6 = this.h6,
    -      h7 = this.h7;
    -    var arr = [h0 >> 24 & 0xFF, h0 >> 16 & 0xFF, h0 >> 8 & 0xFF, h0 & 0xFF, h1 >> 24 & 0xFF, h1 >> 16 & 0xFF, h1 >> 8 & 0xFF, h1 & 0xFF, h2 >> 24 & 0xFF, h2 >> 16 & 0xFF, h2 >> 8 & 0xFF, h2 & 0xFF, h3 >> 24 & 0xFF, h3 >> 16 & 0xFF, h3 >> 8 & 0xFF, h3 & 0xFF, h4 >> 24 & 0xFF, h4 >> 16 & 0xFF, h4 >> 8 & 0xFF, h4 & 0xFF, h5 >> 24 & 0xFF, h5 >> 16 & 0xFF, h5 >> 8 & 0xFF, h5 & 0xFF, h6 >> 24 & 0xFF, h6 >> 16 & 0xFF, h6 >> 8 & 0xFF, h6 & 0xFF];
    -    if (!this.is224) {
    -      arr.push(h7 >> 24 & 0xFF, h7 >> 16 & 0xFF, h7 >> 8 & 0xFF, h7 & 0xFF);
    -    }
    -    return arr;
    -  };
    -  Sha256.prototype.array = Sha256.prototype.digest;
    -  Sha256.prototype.arrayBuffer = function () {
    -    this.finalize();
    -    var buffer = new ArrayBuffer(this.is224 ? 28 : 32);
    -    var dataView = new DataView(buffer);
    -    dataView.setUint32(0, this.h0);
    -    dataView.setUint32(4, this.h1);
    -    dataView.setUint32(8, this.h2);
    -    dataView.setUint32(12, this.h3);
    -    dataView.setUint32(16, this.h4);
    -    dataView.setUint32(20, this.h5);
    -    dataView.setUint32(24, this.h6);
    -    if (!this.is224) {
    -      dataView.setUint32(28, this.h7);
    -    }
    -    return buffer;
    -  };
    -  function HmacSha256(key, is224, sharedMemory) {
    -    var i,
    -      type = typeof key;
    -    if (type === 'string') {
    -      var bytes = [],
    -        length = key.length,
    -        index = 0,
    -        code;
    -      for (i = 0; i < length; ++i) {
    -        code = key.charCodeAt(i);
    -        if (code < 0x80) {
    -          bytes[index++] = code;
    -        } else if (code < 0x800) {
    -          bytes[index++] = 0xc0 | code >> 6;
    -          bytes[index++] = 0x80 | code & 0x3f;
    -        } else if (code < 0xd800 || code >= 0xe000) {
    -          bytes[index++] = 0xe0 | code >> 12;
    -          bytes[index++] = 0x80 | code >> 6 & 0x3f;
    -          bytes[index++] = 0x80 | code & 0x3f;
    -        } else {
    -          code = 0x10000 + ((code & 0x3ff) << 10 | key.charCodeAt(++i) & 0x3ff);
    -          bytes[index++] = 0xf0 | code >> 18;
    -          bytes[index++] = 0x80 | code >> 12 & 0x3f;
    -          bytes[index++] = 0x80 | code >> 6 & 0x3f;
    -          bytes[index++] = 0x80 | code & 0x3f;
    -        }
    -      }
    -      key = bytes;
    -    } else {
    -      if (type === 'object') {
    -        if (key === null) {
    -          throw new Error(ERROR);
    -        } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) {
    -          key = new Uint8Array(key);
    -        } else if (!Array.isArray(key)) {
    -          if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) {
    -            throw new Error(ERROR);
    -          }
    -        }
    -      } else {
    -        throw new Error(ERROR);
    -      }
    -    }
    -    if (key.length > 64) {
    -      key = new Sha256(is224, true).update(key).array();
    -    }
    -    var oKeyPad = [],
    -      iKeyPad = [];
    -    for (i = 0; i < 64; ++i) {
    -      var b = key[i] || 0;
    -      oKeyPad[i] = 0x5c ^ b;
    -      iKeyPad[i] = 0x36 ^ b;
    -    }
    -    Sha256.call(this, is224, sharedMemory);
    -    this.update(iKeyPad);
    -    this.oKeyPad = oKeyPad;
    -    this.inner = true;
    -    this.sharedMemory = sharedMemory;
    -  }
    -  HmacSha256.prototype = new Sha256();
    -  HmacSha256.prototype.finalize = function () {
    -    Sha256.prototype.finalize.call(this);
    -    if (this.inner) {
    -      this.inner = false;
    -      var innerHash = this.array();
    -      Sha256.call(this, this.is224, this.sharedMemory);
    -      this.update(this.oKeyPad);
    -      this.update(innerHash);
    -      Sha256.prototype.finalize.call(this);
    -    }
    -  };
    -  var exports = createMethod();
    -  exports.sha256 = exports;
    -  exports.sha224 = createMethod(true);
    -  exports.sha256.hmac = createHmacMethod();
    -  exports.sha224.hmac = createHmacMethod(true);
    -  if (COMMON_JS) {
    -    module.exports = exports;
    -  } else {
    -    root.sha256 = exports.sha256;
    -    root.sha224 = exports.sha224;
    -    if (AMD) {
    -      !(__WEBPACK_AMD_DEFINE_RESULT__ = (function () {
    -        return exports;
    -      }).call(exports, __webpack_require__, exports, module),
    -		__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
    -    }
    -  }
    -})();
    -
    -/***/ }),
    -
    -/***/ 763:
    -/***/ (function(module, exports, __webpack_require__) {
    -
    -/* module decorator */ module = __webpack_require__.nmd(module);
    -var __WEBPACK_AMD_DEFINE_RESULT__;/**
    - * @license
    - * Lodash 
    - * Copyright OpenJS Foundation and other contributors 
    - * Released under MIT license 
    - * Based on Underscore.js 1.8.3 
    - * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
    - */
    -;
    -(function () {
    -  /** Used as a safe reference for `undefined` in pre-ES5 environments. */
    -  var undefined;
    -
    -  /** Used as the semantic version number. */
    -  var VERSION = '4.17.21';
    -
    -  /** Used as the size to enable large array optimizations. */
    -  var LARGE_ARRAY_SIZE = 200;
    -
    -  /** Error message constants. */
    -  var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',
    -    FUNC_ERROR_TEXT = 'Expected a function',
    -    INVALID_TEMPL_VAR_ERROR_TEXT = 'Invalid `variable` option passed into `_.template`';
    -
    -  /** Used to stand-in for `undefined` hash values. */
    -  var HASH_UNDEFINED = '__lodash_hash_undefined__';
    -
    -  /** Used as the maximum memoize cache size. */
    -  var MAX_MEMOIZE_SIZE = 500;
    -
    -  /** Used as the internal argument placeholder. */
    -  var PLACEHOLDER = '__lodash_placeholder__';
    -
    -  /** Used to compose bitmasks for cloning. */
    -  var CLONE_DEEP_FLAG = 1,
    -    CLONE_FLAT_FLAG = 2,
    -    CLONE_SYMBOLS_FLAG = 4;
    -
    -  /** Used to compose bitmasks for value comparisons. */
    -  var COMPARE_PARTIAL_FLAG = 1,
    -    COMPARE_UNORDERED_FLAG = 2;
    -
    -  /** Used to compose bitmasks for function metadata. */
    -  var WRAP_BIND_FLAG = 1,
    -    WRAP_BIND_KEY_FLAG = 2,
    -    WRAP_CURRY_BOUND_FLAG = 4,
    -    WRAP_CURRY_FLAG = 8,
    -    WRAP_CURRY_RIGHT_FLAG = 16,
    -    WRAP_PARTIAL_FLAG = 32,
    -    WRAP_PARTIAL_RIGHT_FLAG = 64,
    -    WRAP_ARY_FLAG = 128,
    -    WRAP_REARG_FLAG = 256,
    -    WRAP_FLIP_FLAG = 512;
    -
    -  /** Used as default options for `_.truncate`. */
    -  var DEFAULT_TRUNC_LENGTH = 30,
    -    DEFAULT_TRUNC_OMISSION = '...';
    -
    -  /** Used to detect hot functions by number of calls within a span of milliseconds. */
    -  var HOT_COUNT = 800,
    -    HOT_SPAN = 16;
    -
    -  /** Used to indicate the type of lazy iteratees. */
    -  var LAZY_FILTER_FLAG = 1,
    -    LAZY_MAP_FLAG = 2,
    -    LAZY_WHILE_FLAG = 3;
    -
    -  /** Used as references for various `Number` constants. */
    -  var INFINITY = 1 / 0,
    -    MAX_SAFE_INTEGER = 9007199254740991,
    -    MAX_INTEGER = 1.7976931348623157e+308,
    -    NAN = 0 / 0;
    -
    -  /** Used as references for the maximum length and index of an array. */
    -  var MAX_ARRAY_LENGTH = 4294967295,
    -    MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
    -    HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
    -
    -  /** Used to associate wrap methods with their bit flags. */
    -  var wrapFlags = [['ary', WRAP_ARY_FLAG], ['bind', WRAP_BIND_FLAG], ['bindKey', WRAP_BIND_KEY_FLAG], ['curry', WRAP_CURRY_FLAG], ['curryRight', WRAP_CURRY_RIGHT_FLAG], ['flip', WRAP_FLIP_FLAG], ['partial', WRAP_PARTIAL_FLAG], ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], ['rearg', WRAP_REARG_FLAG]];
    -
    -  /** `Object#toString` result references. */
    -  var argsTag = '[object Arguments]',
    -    arrayTag = '[object Array]',
    -    asyncTag = '[object AsyncFunction]',
    -    boolTag = '[object Boolean]',
    -    dateTag = '[object Date]',
    -    domExcTag = '[object DOMException]',
    -    errorTag = '[object Error]',
    -    funcTag = '[object Function]',
    -    genTag = '[object GeneratorFunction]',
    -    mapTag = '[object Map]',
    -    numberTag = '[object Number]',
    -    nullTag = '[object Null]',
    -    objectTag = '[object Object]',
    -    promiseTag = '[object Promise]',
    -    proxyTag = '[object Proxy]',
    -    regexpTag = '[object RegExp]',
    -    setTag = '[object Set]',
    -    stringTag = '[object String]',
    -    symbolTag = '[object Symbol]',
    -    undefinedTag = '[object Undefined]',
    -    weakMapTag = '[object WeakMap]',
    -    weakSetTag = '[object WeakSet]';
    -  var arrayBufferTag = '[object ArrayBuffer]',
    -    dataViewTag = '[object DataView]',
    -    float32Tag = '[object Float32Array]',
    -    float64Tag = '[object Float64Array]',
    -    int8Tag = '[object Int8Array]',
    -    int16Tag = '[object Int16Array]',
    -    int32Tag = '[object Int32Array]',
    -    uint8Tag = '[object Uint8Array]',
    -    uint8ClampedTag = '[object Uint8ClampedArray]',
    -    uint16Tag = '[object Uint16Array]',
    -    uint32Tag = '[object Uint32Array]';
    -
    -  /** Used to match empty string literals in compiled template source. */
    -  var reEmptyStringLeading = /\b__p \+= '';/g,
    -    reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
    -    reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
    -
    -  /** Used to match HTML entities and HTML characters. */
    -  var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,
    -    reUnescapedHtml = /[&<>"']/g,
    -    reHasEscapedHtml = RegExp(reEscapedHtml.source),
    -    reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
    -
    -  /** Used to match template delimiters. */
    -  var reEscape = /<%-([\s\S]+?)%>/g,
    -    reEvaluate = /<%([\s\S]+?)%>/g,
    -    reInterpolate = /<%=([\s\S]+?)%>/g;
    -
    -  /** Used to match property names within property paths. */
    -  var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
    -    reIsPlainProp = /^\w*$/,
    -    rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
    -
    -  /**
    -   * Used to match `RegExp`
    -   * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
    -   */
    -  var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
    -    reHasRegExpChar = RegExp(reRegExpChar.source);
    -
    -  /** Used to match leading whitespace. */
    -  var reTrimStart = /^\s+/;
    -
    -  /** Used to match a single whitespace character. */
    -  var reWhitespace = /\s/;
    -
    -  /** Used to match wrap detail comments. */
    -  var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,
    -    reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/,
    -    reSplitDetails = /,? & /;
    -
    -  /** Used to match words composed of alphanumeric characters. */
    -  var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
    -
    -  /**
    -   * Used to validate the `validate` option in `_.template` variable.
    -   *
    -   * Forbids characters which could potentially change the meaning of the function argument definition:
    -   * - "()," (modification of function parameters)
    -   * - "=" (default value)
    -   * - "[]{}" (destructuring of function parameters)
    -   * - "/" (beginning of a comment)
    -   * - whitespace
    -   */
    -  var reForbiddenIdentifierChars = /[()=,{}\[\]\/\s]/;
    -
    -  /** Used to match backslashes in property paths. */
    -  var reEscapeChar = /\\(\\)?/g;
    -
    -  /**
    -   * Used to match
    -   * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components).
    -   */
    -  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
    -
    -  /** Used to match `RegExp` flags from their coerced string values. */
    -  var reFlags = /\w*$/;
    -
    -  /** Used to detect bad signed hexadecimal string values. */
    -  var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
    -
    -  /** Used to detect binary string values. */
    -  var reIsBinary = /^0b[01]+$/i;
    -
    -  /** Used to detect host constructors (Safari). */
    -  var reIsHostCtor = /^\[object .+?Constructor\]$/;
    -
    -  /** Used to detect octal string values. */
    -  var reIsOctal = /^0o[0-7]+$/i;
    -
    -  /** Used to detect unsigned integer values. */
    -  var reIsUint = /^(?:0|[1-9]\d*)$/;
    -
    -  /** Used to match Latin Unicode letters (excluding mathematical operators). */
    -  var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;
    -
    -  /** Used to ensure capturing order of template delimiters. */
    -  var reNoMatch = /($^)/;
    -
    -  /** Used to match unescaped characters in compiled string literals. */
    -  var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
    -
    -  /** Used to compose unicode character classes. */
    -  var rsAstralRange = "\\ud800-\\udfff",
    -    rsComboMarksRange = "\\u0300-\\u036f",
    -    reComboHalfMarksRange = "\\ufe20-\\ufe2f",
    -    rsComboSymbolsRange = "\\u20d0-\\u20ff",
    -    rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
    -    rsDingbatRange = "\\u2700-\\u27bf",
    -    rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff',
    -    rsMathOpRange = '\\xac\\xb1\\xd7\\xf7',
    -    rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf',
    -    rsPunctuationRange = "\\u2000-\\u206f",
    -    rsSpaceRange = " \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",
    -    rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde',
    -    rsVarRange = "\\ufe0e\\ufe0f",
    -    rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;
    -
    -  /** Used to compose unicode capture groups. */
    -  var rsApos = "['\u2019]",
    -    rsAstral = '[' + rsAstralRange + ']',
    -    rsBreak = '[' + rsBreakRange + ']',
    -    rsCombo = '[' + rsComboRange + ']',
    -    rsDigits = '\\d+',
    -    rsDingbat = '[' + rsDingbatRange + ']',
    -    rsLower = '[' + rsLowerRange + ']',
    -    rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',
    -    rsFitz = "\\ud83c[\\udffb-\\udfff]",
    -    rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
    -    rsNonAstral = '[^' + rsAstralRange + ']',
    -    rsRegional = "(?:\\ud83c[\\udde6-\\uddff]){2}",
    -    rsSurrPair = "[\\ud800-\\udbff][\\udc00-\\udfff]",
    -    rsUpper = '[' + rsUpperRange + ']',
    -    rsZWJ = "\\u200d";
    -
    -  /** Used to compose unicode regexes. */
    -  var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')',
    -    rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')',
    -    rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',
    -    rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',
    -    reOptMod = rsModifier + '?',
    -    rsOptVar = '[' + rsVarRange + ']?',
    -    rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
    -    rsOrdLower = '\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])',
    -    rsOrdUpper = '\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])',
    -    rsSeq = rsOptVar + reOptMod + rsOptJoin,
    -    rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,
    -    rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
    -
    -  /** Used to match apostrophes. */
    -  var reApos = RegExp(rsApos, 'g');
    -
    -  /**
    -   * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
    -   * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
    -   */
    -  var reComboMark = RegExp(rsCombo, 'g');
    -
    -  /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
    -  var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
    -
    -  /** Used to match complex or compound words. */
    -  var reUnicodeWord = RegExp([rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, rsUpper + '+' + rsOptContrUpper, rsOrdUpper, rsOrdLower, rsDigits, rsEmoji].join('|'), 'g');
    -
    -  /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
    -  var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');
    -
    -  /** Used to detect strings that need a more robust regexp to match words. */
    -  var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;
    -
    -  /** Used to assign default `context` object properties. */
    -  var contextProps = ['Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'];
    -
    -  /** Used to make template sourceURLs easier to identify. */
    -  var templateCounter = -1;
    -
    -  /** Used to identify `toStringTag` values of typed arrays. */
    -  var typedArrayTags = {};
    -  typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true;
    -  typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
    -
    -  /** Used to identify `toStringTag` values supported by `_.clone`. */
    -  var cloneableTags = {};
    -  cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[mapTag] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[setTag] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
    -  cloneableTags[errorTag] = cloneableTags[funcTag] = cloneableTags[weakMapTag] = false;
    -
    -  /** Used to map Latin Unicode letters to basic Latin letters. */
    -  var deburredLetters = {
    -    // Latin-1 Supplement block.
    -    '\xc0': 'A',
    -    '\xc1': 'A',
    -    '\xc2': 'A',
    -    '\xc3': 'A',
    -    '\xc4': 'A',
    -    '\xc5': 'A',
    -    '\xe0': 'a',
    -    '\xe1': 'a',
    -    '\xe2': 'a',
    -    '\xe3': 'a',
    -    '\xe4': 'a',
    -    '\xe5': 'a',
    -    '\xc7': 'C',
    -    '\xe7': 'c',
    -    '\xd0': 'D',
    -    '\xf0': 'd',
    -    '\xc8': 'E',
    -    '\xc9': 'E',
    -    '\xca': 'E',
    -    '\xcb': 'E',
    -    '\xe8': 'e',
    -    '\xe9': 'e',
    -    '\xea': 'e',
    -    '\xeb': 'e',
    -    '\xcc': 'I',
    -    '\xcd': 'I',
    -    '\xce': 'I',
    -    '\xcf': 'I',
    -    '\xec': 'i',
    -    '\xed': 'i',
    -    '\xee': 'i',
    -    '\xef': 'i',
    -    '\xd1': 'N',
    -    '\xf1': 'n',
    -    '\xd2': 'O',
    -    '\xd3': 'O',
    -    '\xd4': 'O',
    -    '\xd5': 'O',
    -    '\xd6': 'O',
    -    '\xd8': 'O',
    -    '\xf2': 'o',
    -    '\xf3': 'o',
    -    '\xf4': 'o',
    -    '\xf5': 'o',
    -    '\xf6': 'o',
    -    '\xf8': 'o',
    -    '\xd9': 'U',
    -    '\xda': 'U',
    -    '\xdb': 'U',
    -    '\xdc': 'U',
    -    '\xf9': 'u',
    -    '\xfa': 'u',
    -    '\xfb': 'u',
    -    '\xfc': 'u',
    -    '\xdd': 'Y',
    -    '\xfd': 'y',
    -    '\xff': 'y',
    -    '\xc6': 'Ae',
    -    '\xe6': 'ae',
    -    '\xde': 'Th',
    -    '\xfe': 'th',
    -    '\xdf': 'ss',
    -    // Latin Extended-A block.
    -    "\u0100": 'A',
    -    "\u0102": 'A',
    -    "\u0104": 'A',
    -    "\u0101": 'a',
    -    "\u0103": 'a',
    -    "\u0105": 'a',
    -    "\u0106": 'C',
    -    "\u0108": 'C',
    -    "\u010A": 'C',
    -    "\u010C": 'C',
    -    "\u0107": 'c',
    -    "\u0109": 'c',
    -    "\u010B": 'c',
    -    "\u010D": 'c',
    -    "\u010E": 'D',
    -    "\u0110": 'D',
    -    "\u010F": 'd',
    -    "\u0111": 'd',
    -    "\u0112": 'E',
    -    "\u0114": 'E',
    -    "\u0116": 'E',
    -    "\u0118": 'E',
    -    "\u011A": 'E',
    -    "\u0113": 'e',
    -    "\u0115": 'e',
    -    "\u0117": 'e',
    -    "\u0119": 'e',
    -    "\u011B": 'e',
    -    "\u011C": 'G',
    -    "\u011E": 'G',
    -    "\u0120": 'G',
    -    "\u0122": 'G',
    -    "\u011D": 'g',
    -    "\u011F": 'g',
    -    "\u0121": 'g',
    -    "\u0123": 'g',
    -    "\u0124": 'H',
    -    "\u0126": 'H',
    -    "\u0125": 'h',
    -    "\u0127": 'h',
    -    "\u0128": 'I',
    -    "\u012A": 'I',
    -    "\u012C": 'I',
    -    "\u012E": 'I',
    -    "\u0130": 'I',
    -    "\u0129": 'i',
    -    "\u012B": 'i',
    -    "\u012D": 'i',
    -    "\u012F": 'i',
    -    "\u0131": 'i',
    -    "\u0134": 'J',
    -    "\u0135": 'j',
    -    "\u0136": 'K',
    -    "\u0137": 'k',
    -    "\u0138": 'k',
    -    "\u0139": 'L',
    -    "\u013B": 'L',
    -    "\u013D": 'L',
    -    "\u013F": 'L',
    -    "\u0141": 'L',
    -    "\u013A": 'l',
    -    "\u013C": 'l',
    -    "\u013E": 'l',
    -    "\u0140": 'l',
    -    "\u0142": 'l',
    -    "\u0143": 'N',
    -    "\u0145": 'N',
    -    "\u0147": 'N',
    -    "\u014A": 'N',
    -    "\u0144": 'n',
    -    "\u0146": 'n',
    -    "\u0148": 'n',
    -    "\u014B": 'n',
    -    "\u014C": 'O',
    -    "\u014E": 'O',
    -    "\u0150": 'O',
    -    "\u014D": 'o',
    -    "\u014F": 'o',
    -    "\u0151": 'o',
    -    "\u0154": 'R',
    -    "\u0156": 'R',
    -    "\u0158": 'R',
    -    "\u0155": 'r',
    -    "\u0157": 'r',
    -    "\u0159": 'r',
    -    "\u015A": 'S',
    -    "\u015C": 'S',
    -    "\u015E": 'S',
    -    "\u0160": 'S',
    -    "\u015B": 's',
    -    "\u015D": 's',
    -    "\u015F": 's',
    -    "\u0161": 's',
    -    "\u0162": 'T',
    -    "\u0164": 'T',
    -    "\u0166": 'T',
    -    "\u0163": 't',
    -    "\u0165": 't',
    -    "\u0167": 't',
    -    "\u0168": 'U',
    -    "\u016A": 'U',
    -    "\u016C": 'U',
    -    "\u016E": 'U',
    -    "\u0170": 'U',
    -    "\u0172": 'U',
    -    "\u0169": 'u',
    -    "\u016B": 'u',
    -    "\u016D": 'u',
    -    "\u016F": 'u',
    -    "\u0171": 'u',
    -    "\u0173": 'u',
    -    "\u0174": 'W',
    -    "\u0175": 'w',
    -    "\u0176": 'Y',
    -    "\u0177": 'y',
    -    "\u0178": 'Y',
    -    "\u0179": 'Z',
    -    "\u017B": 'Z',
    -    "\u017D": 'Z',
    -    "\u017A": 'z',
    -    "\u017C": 'z',
    -    "\u017E": 'z',
    -    "\u0132": 'IJ',
    -    "\u0133": 'ij',
    -    "\u0152": 'Oe',
    -    "\u0153": 'oe',
    -    "\u0149": "'n",
    -    "\u017F": 's'
    -  };
    -
    -  /** Used to map characters to HTML entities. */
    -  var htmlEscapes = {
    -    '&': '&',
    -    '<': '<',
    -    '>': '>',
    -    '"': '"',
    -    "'": '''
    -  };
    -
    -  /** Used to map HTML entities to characters. */
    -  var htmlUnescapes = {
    -    '&': '&',
    -    '<': '<',
    -    '>': '>',
    -    '"': '"',
    -    ''': "'"
    -  };
    -
    -  /** Used to escape characters for inclusion in compiled string literals. */
    -  var stringEscapes = {
    -    '\\': '\\',
    -    "'": "'",
    -    '\n': 'n',
    -    '\r': 'r',
    -    "\u2028": 'u2028',
    -    "\u2029": 'u2029'
    -  };
    -
    -  /** Built-in method references without a dependency on `root`. */
    -  var freeParseFloat = parseFloat,
    -    freeParseInt = parseInt;
    -
    -  /** Detect free variable `global` from Node.js. */
    -  var freeGlobal = typeof __webpack_require__.g == 'object' && __webpack_require__.g && __webpack_require__.g.Object === Object && __webpack_require__.g;
    -
    -  /** Detect free variable `self`. */
    -  var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
    -
    -  /** Used as a reference to the global object. */
    -  var root = freeGlobal || freeSelf || Function('return this')();
    -
    -  /** Detect free variable `exports`. */
    -  var freeExports =  true && exports && !exports.nodeType && exports;
    -
    -  /** Detect free variable `module`. */
    -  var freeModule = freeExports && "object" == 'object' && module && !module.nodeType && module;
    -
    -  /** Detect the popular CommonJS extension `module.exports`. */
    -  var moduleExports = freeModule && freeModule.exports === freeExports;
    -
    -  /** Detect free variable `process` from Node.js. */
    -  var freeProcess = moduleExports && freeGlobal.process;
    -
    -  /** Used to access faster Node.js helpers. */
    -  var nodeUtil = function () {
    -    try {
    -      // Use `util.types` for Node.js 10+.
    -      var types = freeModule && freeModule.require && freeModule.require('util').types;
    -      if (types) {
    -        return types;
    -      }
    -
    -      // Legacy `process.binding('util')` for Node.js < 10.
    -      return freeProcess && freeProcess.binding && freeProcess.binding('util');
    -    } catch (e) {}
    -  }();
    -
    -  /* Node.js helper references. */
    -  var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer,
    -    nodeIsDate = nodeUtil && nodeUtil.isDate,
    -    nodeIsMap = nodeUtil && nodeUtil.isMap,
    -    nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,
    -    nodeIsSet = nodeUtil && nodeUtil.isSet,
    -    nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
    -
    -  /*--------------------------------------------------------------------------*/
    -
    -  /**
    -   * A faster alternative to `Function#apply`, this function invokes `func`
    -   * with the `this` binding of `thisArg` and the arguments of `args`.
    -   *
    -   * @private
    -   * @param {Function} func The function to invoke.
    -   * @param {*} thisArg The `this` binding of `func`.
    -   * @param {Array} args The arguments to invoke `func` with.
    -   * @returns {*} Returns the result of `func`.
    -   */
    -  function apply(func, thisArg, args) {
    -    switch (args.length) {
    -      case 0:
    -        return func.call(thisArg);
    -      case 1:
    -        return func.call(thisArg, args[0]);
    -      case 2:
    -        return func.call(thisArg, args[0], args[1]);
    -      case 3:
    -        return func.call(thisArg, args[0], args[1], args[2]);
    -    }
    -    return func.apply(thisArg, args);
    -  }
    -
    -  /**
    -   * A specialized version of `baseAggregator` for arrays.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to iterate over.
    -   * @param {Function} setter The function to set `accumulator` values.
    -   * @param {Function} iteratee The iteratee to transform keys.
    -   * @param {Object} accumulator The initial aggregated object.
    -   * @returns {Function} Returns `accumulator`.
    -   */
    -  function arrayAggregator(array, setter, iteratee, accumulator) {
    -    var index = -1,
    -      length = array == null ? 0 : array.length;
    -    while (++index < length) {
    -      var value = array[index];
    -      setter(accumulator, value, iteratee(value), array);
    -    }
    -    return accumulator;
    -  }
    -
    -  /**
    -   * A specialized version of `_.forEach` for arrays without support for
    -   * iteratee shorthands.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to iterate over.
    -   * @param {Function} iteratee The function invoked per iteration.
    -   * @returns {Array} Returns `array`.
    -   */
    -  function arrayEach(array, iteratee) {
    -    var index = -1,
    -      length = array == null ? 0 : array.length;
    -    while (++index < length) {
    -      if (iteratee(array[index], index, array) === false) {
    -        break;
    -      }
    -    }
    -    return array;
    -  }
    -
    -  /**
    -   * A specialized version of `_.forEachRight` for arrays without support for
    -   * iteratee shorthands.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to iterate over.
    -   * @param {Function} iteratee The function invoked per iteration.
    -   * @returns {Array} Returns `array`.
    -   */
    -  function arrayEachRight(array, iteratee) {
    -    var length = array == null ? 0 : array.length;
    -    while (length--) {
    -      if (iteratee(array[length], length, array) === false) {
    -        break;
    -      }
    -    }
    -    return array;
    -  }
    -
    -  /**
    -   * A specialized version of `_.every` for arrays without support for
    -   * iteratee shorthands.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to iterate over.
    -   * @param {Function} predicate The function invoked per iteration.
    -   * @returns {boolean} Returns `true` if all elements pass the predicate check,
    -   *  else `false`.
    -   */
    -  function arrayEvery(array, predicate) {
    -    var index = -1,
    -      length = array == null ? 0 : array.length;
    -    while (++index < length) {
    -      if (!predicate(array[index], index, array)) {
    -        return false;
    -      }
    -    }
    -    return true;
    -  }
    -
    -  /**
    -   * A specialized version of `_.filter` for arrays without support for
    -   * iteratee shorthands.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to iterate over.
    -   * @param {Function} predicate The function invoked per iteration.
    -   * @returns {Array} Returns the new filtered array.
    -   */
    -  function arrayFilter(array, predicate) {
    -    var index = -1,
    -      length = array == null ? 0 : array.length,
    -      resIndex = 0,
    -      result = [];
    -    while (++index < length) {
    -      var value = array[index];
    -      if (predicate(value, index, array)) {
    -        result[resIndex++] = value;
    -      }
    -    }
    -    return result;
    -  }
    -
    -  /**
    -   * A specialized version of `_.includes` for arrays without support for
    -   * specifying an index to search from.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to inspect.
    -   * @param {*} target The value to search for.
    -   * @returns {boolean} Returns `true` if `target` is found, else `false`.
    -   */
    -  function arrayIncludes(array, value) {
    -    var length = array == null ? 0 : array.length;
    -    return !!length && baseIndexOf(array, value, 0) > -1;
    -  }
    -
    -  /**
    -   * This function is like `arrayIncludes` except that it accepts a comparator.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to inspect.
    -   * @param {*} target The value to search for.
    -   * @param {Function} comparator The comparator invoked per element.
    -   * @returns {boolean} Returns `true` if `target` is found, else `false`.
    -   */
    -  function arrayIncludesWith(array, value, comparator) {
    -    var index = -1,
    -      length = array == null ? 0 : array.length;
    -    while (++index < length) {
    -      if (comparator(value, array[index])) {
    -        return true;
    -      }
    -    }
    -    return false;
    -  }
    -
    -  /**
    -   * A specialized version of `_.map` for arrays without support for iteratee
    -   * shorthands.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to iterate over.
    -   * @param {Function} iteratee The function invoked per iteration.
    -   * @returns {Array} Returns the new mapped array.
    -   */
    -  function arrayMap(array, iteratee) {
    -    var index = -1,
    -      length = array == null ? 0 : array.length,
    -      result = Array(length);
    -    while (++index < length) {
    -      result[index] = iteratee(array[index], index, array);
    -    }
    -    return result;
    -  }
    -
    -  /**
    -   * Appends the elements of `values` to `array`.
    -   *
    -   * @private
    -   * @param {Array} array The array to modify.
    -   * @param {Array} values The values to append.
    -   * @returns {Array} Returns `array`.
    -   */
    -  function arrayPush(array, values) {
    -    var index = -1,
    -      length = values.length,
    -      offset = array.length;
    -    while (++index < length) {
    -      array[offset + index] = values[index];
    -    }
    -    return array;
    -  }
    -
    -  /**
    -   * A specialized version of `_.reduce` for arrays without support for
    -   * iteratee shorthands.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to iterate over.
    -   * @param {Function} iteratee The function invoked per iteration.
    -   * @param {*} [accumulator] The initial value.
    -   * @param {boolean} [initAccum] Specify using the first element of `array` as
    -   *  the initial value.
    -   * @returns {*} Returns the accumulated value.
    -   */
    -  function arrayReduce(array, iteratee, accumulator, initAccum) {
    -    var index = -1,
    -      length = array == null ? 0 : array.length;
    -    if (initAccum && length) {
    -      accumulator = array[++index];
    -    }
    -    while (++index < length) {
    -      accumulator = iteratee(accumulator, array[index], index, array);
    -    }
    -    return accumulator;
    -  }
    -
    -  /**
    -   * A specialized version of `_.reduceRight` for arrays without support for
    -   * iteratee shorthands.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to iterate over.
    -   * @param {Function} iteratee The function invoked per iteration.
    -   * @param {*} [accumulator] The initial value.
    -   * @param {boolean} [initAccum] Specify using the last element of `array` as
    -   *  the initial value.
    -   * @returns {*} Returns the accumulated value.
    -   */
    -  function arrayReduceRight(array, iteratee, accumulator, initAccum) {
    -    var length = array == null ? 0 : array.length;
    -    if (initAccum && length) {
    -      accumulator = array[--length];
    -    }
    -    while (length--) {
    -      accumulator = iteratee(accumulator, array[length], length, array);
    -    }
    -    return accumulator;
    -  }
    -
    -  /**
    -   * A specialized version of `_.some` for arrays without support for iteratee
    -   * shorthands.
    -   *
    -   * @private
    -   * @param {Array} [array] The array to iterate over.
    -   * @param {Function} predicate The function invoked per iteration.
    -   * @returns {boolean} Returns `true` if any element passes the predicate check,
    -   *  else `false`.
    -   */
    -  function arraySome(array, predicate) {
    -    var index = -1,
    -      length = array == null ? 0 : array.length;
    -    while (++index < length) {
    -      if (predicate(array[index], index, array)) {
    -        return true;
    -      }
    -    }
    -    return false;
    -  }
    -
    -  /**
    -   * Gets the size of an ASCII `string`.
    -   *
    -   * @private
    -   * @param {string} string The string inspect.
    -   * @returns {number} Returns the string size.
    -   */
    -  var asciiSize = baseProperty('length');
    -
    -  /**
    -   * Converts an ASCII `string` to an array.
    -   *
    -   * @private
    -   * @param {string} string The string to convert.
    -   * @returns {Array} Returns the converted array.
    -   */
    -  function asciiToArray(string) {
    -    return string.split('');
    -  }
    -
    -  /**
    -   * Splits an ASCII `string` into an array of its words.
    -   *
    -   * @private
    -   * @param {string} The string to inspect.
    -   * @returns {Array} Returns the words of `string`.
    -   */
    -  function asciiWords(string) {
    -    return string.match(reAsciiWord) || [];
    -  }
    -
    -  /**
    -   * The base implementation of methods like `_.findKey` and `_.findLastKey`,
    -   * without support for iteratee shorthands, which iterates over `collection`
    -   * using `eachFunc`.
    -   *
    -   * @private
    -   * @param {Array|Object} collection The collection to inspect.
    -   * @param {Function} predicate The function invoked per iteration.
    -   * @param {Function} eachFunc The function to iterate over `collection`.
    -   * @returns {*} Returns the found element or its key, else `undefined`.
    -   */
    -  function baseFindKey(collection, predicate, eachFunc) {
    -    var result;
    -    eachFunc(collection, function (value, key, collection) {
    -      if (predicate(value, key, collection)) {
    -        result = key;
    -        return false;
    -      }
    -    });
    -    return result;
    -  }
    -
    -  /**
    -   * The base implementation of `_.findIndex` and `_.findLastIndex` without
    -   * support for iteratee shorthands.
    -   *
    -   * @private
    -   * @param {Array} array The array to inspect.
    -   * @param {Function} predicate The function invoked per iteration.
    -   * @param {number} fromIndex The index to search from.
    -   * @param {boolean} [fromRight] Specify iterating from right to left.
    -   * @returns {number} Returns the index of the matched value, else `-1`.
    -   */
    -  function baseFindIndex(array, predicate, fromIndex, fromRight) {
    -    var length = array.length,
    -      index = fromIndex + (fromRight ? 1 : -1);
    -    while (fromRight ? index-- : ++index < length) {
    -      if (predicate(array[index], index, array)) {
    -        return index;
    -      }
    -    }
    -    return -1;
    -  }
    -
    -  /**
    -   * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
    -   *
    -   * @private
    -   * @param {Array} array The array to inspect.
    -   * @param {*} value The value to search for.
    -   * @param {number} fromIndex The index to search from.
    -   * @returns {number} Returns the index of the matched value, else `-1`.
    -   */
    -  function baseIndexOf(array, value, fromIndex) {
    -    return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex);
    -  }
    -
    -  /**
    -   * This function is like `baseIndexOf` except that it accepts a comparator.
    -   *
    -   * @private
    -   * @param {Array} array The array to inspect.
    -   * @param {*} value The value to search for.
    -   * @param {number} fromIndex The index to search from.
    -   * @param {Function} comparator The comparator invoked per element.
    -   * @returns {number} Returns the index of the matched value, else `-1`.
    -   */
    -  function baseIndexOfWith(array, value, fromIndex, comparator) {
    -    var index = fromIndex - 1,
    -      length = array.length;
    -    while (++index < length) {
    -      if (comparator(array[index], value)) {
    -        return index;
    -      }
    -    }
    -    return -1;
    -  }
    -
    -  /**
    -   * The base implementation of `_.isNaN` without support for number objects.
    -   *
    -   * @private
    -   * @param {*} value The value to check.
    -   * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
    -   */
    -  function baseIsNaN(value) {
    -    return value !== value;
    -  }
    -
    -  /**
    -   * The base implementation of `_.mean` and `_.meanBy` without support for
    -   * iteratee shorthands.
    -   *
    -   * @private
    -   * @param {Array} array The array to iterate over.
    -   * @param {Function} iteratee The function invoked per iteration.
    -   * @returns {number} Returns the mean.
    -   */
    -  function baseMean(array, iteratee) {
    -    var length = array == null ? 0 : array.length;
    -    return length ? baseSum(array, iteratee) / length : NAN;
    -  }
    -
    -  /**
    -   * The base implementation of `_.property` without support for deep paths.
    -   *
    -   * @private
    -   * @param {string} key The key of the property to get.
    -   * @returns {Function} Returns the new accessor function.
    -   */
    -  function baseProperty(key) {
    -    return function (object) {
    -      return object == null ? undefined : object[key];
    -    };
    -  }
    -
    -  /**
    -   * The base implementation of `_.propertyOf` without support for deep paths.
    -   *
    -   * @private
    -   * @param {Object} object The object to query.
    -   * @returns {Function} Returns the new accessor function.
    -   */
    -  function basePropertyOf(object) {
    -    return function (key) {
    -      return object == null ? undefined : object[key];
    -    };
    -  }
    -
    -  /**
    -   * The base implementation of `_.reduce` and `_.reduceRight`, without support
    -   * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
    -   *
    -   * @private
    -   * @param {Array|Object} collection The collection to iterate over.
    -   * @param {Function} iteratee The function invoked per iteration.
    -   * @param {*} accumulator The initial value.
    -   * @param {boolean} initAccum Specify using the first or last element of
    -   *  `collection` as the initial value.
    -   * @param {Function} eachFunc The function to iterate over `collection`.
    -   * @returns {*} Returns the accumulated value.
    -   */
    -  function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
    -    eachFunc(collection, function (value, index, collection) {
    -      accumulator = initAccum ? (initAccum = false, value) : iteratee(accumulator, value, index, collection);
    -    });
    -    return accumulator;
    -  }
    -
    -  /**
    -   * The base implementation of `_.sortBy` which uses `comparer` to define the
    -   * sort order of `array` and replaces criteria objects with their corresponding
    -   * values.
    -   *
    -   * @private
    -   * @param {Array} array The array to sort.
    -   * @param {Function} comparer The function to define sort order.
    -   * @returns {Array} Returns `array`.
    -   */
    -  function baseSortBy(array, comparer) {
    -    var length = array.length;
    -    array.sort(comparer);
    -    while (length--) {
    -      array[length] = array[length].value;
    -    }
    -    return array;
    -  }
    -
    -  /**
    -   * The base implementation of `_.sum` and `_.sumBy` without support for
    -   * iteratee shorthands.
    -   *
    -   * @private
    -   * @param {Array} array The array to iterate over.
    -   * @param {Function} iteratee The function invoked per iteration.
    -   * @returns {number} Returns the sum.
    -   */
    -  function baseSum(array, iteratee) {
    -    var result,
    -      index = -1,
    -      length = array.length;
    -    while (++index < length) {
    -      var current = iteratee(array[index]);
    -      if (current !== undefined) {
    -        result = result === undefined ? current : result + current;
    -      }
    -    }
    -    return result;
    -  }
    -
    -  /**
    -   * The base implementation of `_.times` without support for iteratee shorthands
    -   * or max array length checks.
    -   *
    -   * @private
    -   * @param {number} n The number of times to invoke `iteratee`.
    -   * @param {Function} iteratee The function invoked per iteration.
    -   * @returns {Array} Returns the array of results.
    -   */
    -  function baseTimes(n, iteratee) {
    -    var index = -1,
    -      result = Array(n);
    -    while (++index < n) {
    -      result[index] = iteratee(index);
    -    }
    -    return result;
    -  }
    -
    -  /**
    -   * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
    -   * of key-value pairs for `object` corresponding to the property names of `props`.
    -   *
    -   * @private
    -   * @param {Object} object The object to query.
    -   * @param {Array} props The property names to get values for.
    -   * @returns {Object} Returns the key-value pairs.
    -   */
    -  function baseToPairs(object, props) {
    -    return arrayMap(props, function (key) {
    -      return [key, object[key]];
    -    });
    -  }
    -
    -  /**
    -   * The base implementation of `_.trim`.
    -   *
    -   * @private
    -   * @param {string} string The string to trim.
    -   * @returns {string} Returns the trimmed string.
    -   */
    -  function baseTrim(string) {
    -    return string ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') : string;
    -  }
    -
    -  /**
    -   * The base implementation of `_.unary` without support for storing metadata.
    -   *
    -   * @private
    -   * @param {Function} func The function to cap arguments for.
    -   * @returns {Function} Returns the new capped function.
    -   */
    -  function baseUnary(func) {
    -    return function (value) {
    -      return func(value);
    -    };
    -  }
    -
    -  /**
    -   * The base implementation of `_.values` and `_.valuesIn` which creates an
    -   * array of `object` property values corresponding to the property names
    -   * of `props`.
    -   *
    -   * @private
    -   * @param {Object} object The object to query.
    -   * @param {Array} props The property names to get values for.
    -   * @returns {Object} Returns the array of property values.
    -   */
    -  function baseValues(object, props) {
    -    return arrayMap(props, function (key) {
    -      return object[key];
    -    });
    -  }
    -
    -  /**
    -   * Checks if a `cache` value for `key` exists.
    -   *
    -   * @private
    -   * @param {Object} cache The cache to query.
    -   * @param {string} key The key of the entry to check.
    -   * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    -   */
    -  function cacheHas(cache, key) {
    -    return cache.has(key);
    -  }
    -
    -  /**
    -   * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
    -   * that is not found in the character symbols.
    -   *
    -   * @private
    -   * @param {Array} strSymbols The string symbols to inspect.
    -   * @param {Array} chrSymbols The character symbols to find.
    -   * @returns {number} Returns the index of the first unmatched string symbol.
    -   */
    -  function charsStartIndex(strSymbols, chrSymbols) {
    -    var index = -1,
    -      length = strSymbols.length;
    -    while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
    -    return index;
    -  }
    -
    -  /**
    -   * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
    -   * that is not found in the character symbols.
    -   *
    -   * @private
    -   * @param {Array} strSymbols The string symbols to inspect.
    -   * @param {Array} chrSymbols The character symbols to find.
    -   * @returns {number} Returns the index of the last unmatched string symbol.
    -   */
    -  function charsEndIndex(strSymbols, chrSymbols) {
    -    var index = strSymbols.length;
    -    while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
    -    return index;
    -  }
    -
    -  /**
    -   * Gets the number of `placeholder` occurrences in `array`.
    -   *
    -   * @private
    -   * @param {Array} array The array to inspect.
    -   * @param {*} placeholder The placeholder to search for.
    -   * @returns {number} Returns the placeholder count.
    -   */
    -  function countHolders(array, placeholder) {
    -    var length = array.length,
    -      result = 0;
    -    while (length--) {
    -      if (array[length] === placeholder) {
    -        ++result;
    -      }
    -    }
    -    return result;
    -  }
    -
    -  /**
    -   * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A
    -   * letters to basic Latin letters.
    -   *
    -   * @private
    -   * @param {string} letter The matched letter to deburr.
    -   * @returns {string} Returns the deburred letter.
    -   */
    -  var deburrLetter = basePropertyOf(deburredLetters);
    -
    -  /**
    -   * Used by `_.escape` to convert characters to HTML entities.
    -   *
    -   * @private
    -   * @param {string} chr The matched character to escape.
    -   * @returns {string} Returns the escaped character.
    -   */
    -  var escapeHtmlChar = basePropertyOf(htmlEscapes);
    -
    -  /**
    -   * Used by `_.template` to escape characters for inclusion in compiled string literals.
    -   *
    -   * @private
    -   * @param {string} chr The matched character to escape.
    -   * @returns {string} Returns the escaped character.
    -   */
    -  function escapeStringChar(chr) {
    -    return '\\' + stringEscapes[chr];
    -  }
    -
    -  /**
    -   * Gets the value at `key` of `object`.
    -   *
    -   * @private
    -   * @param {Object} [object] The object to query.
    -   * @param {string} key The key of the property to get.
    -   * @returns {*} Returns the property value.
    -   */
    -  function getValue(object, key) {
    -    return object == null ? undefined : object[key];
    -  }
    -
    -  /**
    -   * Checks if `string` contains Unicode symbols.
    -   *
    -   * @private
    -   * @param {string} string The string to inspect.
    -   * @returns {boolean} Returns `true` if a symbol is found, else `false`.
    -   */
    -  function hasUnicode(string) {
    -    return reHasUnicode.test(string);
    -  }
    -
    -  /**
    -   * Checks if `string` contains a word composed of Unicode symbols.
    -   *
    -   * @private
    -   * @param {string} string The string to inspect.
    -   * @returns {boolean} Returns `true` if a word is found, else `false`.
    -   */
    -  function hasUnicodeWord(string) {
    -    return reHasUnicodeWord.test(string);
    -  }
    -
    -  /**
    -   * Converts `iterator` to an array.
    -   *
    -   * @private
    -   * @param {Object} iterator The iterator to convert.
    -   * @returns {Array} Returns the converted array.
    -   */
    -  function iteratorToArray(iterator) {
    -    var data,
    -      result = [];
    -    while (!(data = iterator.next()).done) {
    -      result.push(data.value);
    -    }
    -    return result;
    -  }
    -
    -  /**
    -   * Converts `map` to its key-value pairs.
    -   *
    -   * @private
    -   * @param {Object} map The map to convert.
    -   * @returns {Array} Returns the key-value pairs.
    -   */
    -  function mapToArray(map) {
    -    var index = -1,
    -      result = Array(map.size);
    -    map.forEach(function (value, key) {
    -      result[++index] = [key, value];
    -    });
    -    return result;
    -  }
    -
    -  /**
    -   * Creates a unary function that invokes `func` with its argument transformed.
    -   *
    -   * @private
    -   * @param {Function} func The function to wrap.
    -   * @param {Function} transform The argument transform.
    -   * @returns {Function} Returns the new function.
    -   */
    -  function overArg(func, transform) {
    -    return function (arg) {
    -      return func(transform(arg));
    -    };
    -  }
    -
    -  /**
    -   * Replaces all `placeholder` elements in `array` with an internal placeholder
    -   * and returns an array of their indexes.
    -   *
    -   * @private
    -   * @param {Array} array The array to modify.
    -   * @param {*} placeholder The placeholder to replace.
    -   * @returns {Array} Returns the new array of placeholder indexes.
    -   */
    -  function replaceHolders(array, placeholder) {
    -    var index = -1,
    -      length = array.length,
    -      resIndex = 0,
    -      result = [];
    -    while (++index < length) {
    -      var value = array[index];
    -      if (value === placeholder || value === PLACEHOLDER) {
    -        array[index] = PLACEHOLDER;
    -        result[resIndex++] = index;
    -      }
    -    }
    -    return result;
    -  }
    -
    -  /**
    -   * Converts `set` to an array of its values.
    -   *
    -   * @private
    -   * @param {Object} set The set to convert.
    -   * @returns {Array} Returns the values.
    -   */
    -  function setToArray(set) {
    -    var index = -1,
    -      result = Array(set.size);
    -    set.forEach(function (value) {
    -      result[++index] = value;
    -    });
    -    return result;
    -  }
    -
    -  /**
    -   * Converts `set` to its value-value pairs.
    -   *
    -   * @private
    -   * @param {Object} set The set to convert.
    -   * @returns {Array} Returns the value-value pairs.
    -   */
    -  function setToPairs(set) {
    -    var index = -1,
    -      result = Array(set.size);
    -    set.forEach(function (value) {
    -      result[++index] = [value, value];
    -    });
    -    return result;
    -  }
    -
    -  /**
    -   * A specialized version of `_.indexOf` which performs strict equality
    -   * comparisons of values, i.e. `===`.
    -   *
    -   * @private
    -   * @param {Array} array The array to inspect.
    -   * @param {*} value The value to search for.
    -   * @param {number} fromIndex The index to search from.
    -   * @returns {number} Returns the index of the matched value, else `-1`.
    -   */
    -  function strictIndexOf(array, value, fromIndex) {
    -    var index = fromIndex - 1,
    -      length = array.length;
    -    while (++index < length) {
    -      if (array[index] === value) {
    -        return index;
    -      }
    -    }
    -    return -1;
    -  }
    -
    -  /**
    -   * A specialized version of `_.lastIndexOf` which performs strict equality
    -   * comparisons of values, i.e. `===`.
    -   *
    -   * @private
    -   * @param {Array} array The array to inspect.
    -   * @param {*} value The value to search for.
    -   * @param {number} fromIndex The index to search from.
    -   * @returns {number} Returns the index of the matched value, else `-1`.
    -   */
    -  function strictLastIndexOf(array, value, fromIndex) {
    -    var index = fromIndex + 1;
    -    while (index--) {
    -      if (array[index] === value) {
    -        return index;
    -      }
    -    }
    -    return index;
    -  }
    -
    -  /**
    -   * Gets the number of symbols in `string`.
    -   *
    -   * @private
    -   * @param {string} string The string to inspect.
    -   * @returns {number} Returns the string size.
    -   */
    -  function stringSize(string) {
    -    return hasUnicode(string) ? unicodeSize(string) : asciiSize(string);
    -  }
    -
    -  /**
    -   * Converts `string` to an array.
    -   *
    -   * @private
    -   * @param {string} string The string to convert.
    -   * @returns {Array} Returns the converted array.
    -   */
    -  function stringToArray(string) {
    -    return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string);
    -  }
    -
    -  /**
    -   * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
    -   * character of `string`.
    -   *
    -   * @private
    -   * @param {string} string The string to inspect.
    -   * @returns {number} Returns the index of the last non-whitespace character.
    -   */
    -  function trimmedEndIndex(string) {
    -    var index = string.length;
    -    while (index-- && reWhitespace.test(string.charAt(index))) {}
    -    return index;
    -  }
    -
    -  /**
    -   * Used by `_.unescape` to convert HTML entities to characters.
    -   *
    -   * @private
    -   * @param {string} chr The matched character to unescape.
    -   * @returns {string} Returns the unescaped character.
    -   */
    -  var unescapeHtmlChar = basePropertyOf(htmlUnescapes);
    -
    -  /**
    -   * Gets the size of a Unicode `string`.
    -   *
    -   * @private
    -   * @param {string} string The string inspect.
    -   * @returns {number} Returns the string size.
    -   */
    -  function unicodeSize(string) {
    -    var result = reUnicode.lastIndex = 0;
    -    while (reUnicode.test(string)) {
    -      ++result;
    -    }
    -    return result;
    -  }
    -
    -  /**
    -   * Converts a Unicode `string` to an array.
    -   *
    -   * @private
    -   * @param {string} string The string to convert.
    -   * @returns {Array} Returns the converted array.
    -   */
    -  function unicodeToArray(string) {
    -    return string.match(reUnicode) || [];
    -  }
    -
    -  /**
    -   * Splits a Unicode `string` into an array of its words.
    -   *
    -   * @private
    -   * @param {string} The string to inspect.
    -   * @returns {Array} Returns the words of `string`.
    -   */
    -  function unicodeWords(string) {
    -    return string.match(reUnicodeWord) || [];
    -  }
    -
    -  /*--------------------------------------------------------------------------*/
    -
    -  /**
    -   * Create a new pristine `lodash` function using the `context` object.
    -   *
    -   * @static
    -   * @memberOf _
    -   * @since 1.1.0
    -   * @category Util
    -   * @param {Object} [context=root] The context object.
    -   * @returns {Function} Returns a new `lodash` function.
    -   * @example
    -   *
    -   * _.mixin({ 'foo': _.constant('foo') });
    -   *
    -   * var lodash = _.runInContext();
    -   * lodash.mixin({ 'bar': lodash.constant('bar') });
    -   *
    -   * _.isFunction(_.foo);
    -   * // => true
    -   * _.isFunction(_.bar);
    -   * // => false
    -   *
    -   * lodash.isFunction(lodash.foo);
    -   * // => false
    -   * lodash.isFunction(lodash.bar);
    -   * // => true
    -   *
    -   * // Create a suped-up `defer` in Node.js.
    -   * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;
    -   */
    -  var runInContext = function runInContext(context) {
    -    context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps));
    -
    -    /** Built-in constructor references. */
    -    var Array = context.Array,
    -      Date = context.Date,
    -      Error = context.Error,
    -      Function = context.Function,
    -      Math = context.Math,
    -      Object = context.Object,
    -      RegExp = context.RegExp,
    -      String = context.String,
    -      TypeError = context.TypeError;
    -
    -    /** Used for built-in method references. */
    -    var arrayProto = Array.prototype,
    -      funcProto = Function.prototype,
    -      objectProto = Object.prototype;
    -
    -    /** Used to detect overreaching core-js shims. */
    -    var coreJsData = context['__core-js_shared__'];
    -
    -    /** Used to resolve the decompiled source of functions. */
    -    var funcToString = funcProto.toString;
    -
    -    /** Used to check objects for own properties. */
    -    var hasOwnProperty = objectProto.hasOwnProperty;
    -
    -    /** Used to generate unique IDs. */
    -    var idCounter = 0;
    -
    -    /** Used to detect methods masquerading as native. */
    -    var maskSrcKey = function () {
    -      var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
    -      return uid ? 'Symbol(src)_1.' + uid : '';
    -    }();
    -
    -    /**
    -     * Used to resolve the
    -     * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
    -     * of values.
    -     */
    -    var nativeObjectToString = objectProto.toString;
    -
    -    /** Used to infer the `Object` constructor. */
    -    var objectCtorString = funcToString.call(Object);
    -
    -    /** Used to restore the original `_` reference in `_.noConflict`. */
    -    var oldDash = root._;
    -
    -    /** Used to detect if a method is native. */
    -    var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&').replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$');
    -
    -    /** Built-in value references. */
    -    var Buffer = moduleExports ? context.Buffer : undefined,
    -      Symbol = context.Symbol,
    -      Uint8Array = context.Uint8Array,
    -      allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined,
    -      getPrototype = overArg(Object.getPrototypeOf, Object),
    -      objectCreate = Object.create,
    -      propertyIsEnumerable = objectProto.propertyIsEnumerable,
    -      splice = arrayProto.splice,
    -      spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined,
    -      symIterator = Symbol ? Symbol.iterator : undefined,
    -      symToStringTag = Symbol ? Symbol.toStringTag : undefined;
    -    var defineProperty = function () {
    -      try {
    -        var func = getNative(Object, 'defineProperty');
    -        func({}, '', {});
    -        return func;
    -      } catch (e) {}
    -    }();
    -
    -    /** Mocked built-ins. */
    -    var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout,
    -      ctxNow = Date && Date.now !== root.Date.now && Date.now,
    -      ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout;
    -
    -    /* Built-in method references for those with the same name as other `lodash` methods. */
    -    var nativeCeil = Math.ceil,
    -      nativeFloor = Math.floor,
    -      nativeGetSymbols = Object.getOwnPropertySymbols,
    -      nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,
    -      nativeIsFinite = context.isFinite,
    -      nativeJoin = arrayProto.join,
    -      nativeKeys = overArg(Object.keys, Object),
    -      nativeMax = Math.max,
    -      nativeMin = Math.min,
    -      nativeNow = Date.now,
    -      nativeParseInt = context.parseInt,
    -      nativeRandom = Math.random,
    -      nativeReverse = arrayProto.reverse;
    -
    -    /* Built-in method references that are verified to be native. */
    -    var DataView = getNative(context, 'DataView'),
    -      Map = getNative(context, 'Map'),
    -      Promise = getNative(context, 'Promise'),
    -      Set = getNative(context, 'Set'),
    -      WeakMap = getNative(context, 'WeakMap'),
    -      nativeCreate = getNative(Object, 'create');
    -
    -    /** Used to store function metadata. */
    -    var metaMap = WeakMap && new WeakMap();
    -
    -    /** Used to lookup unminified function names. */
    -    var realNames = {};
    -
    -    /** Used to detect maps, sets, and weakmaps. */
    -    var dataViewCtorString = toSource(DataView),
    -      mapCtorString = toSource(Map),
    -      promiseCtorString = toSource(Promise),
    -      setCtorString = toSource(Set),
    -      weakMapCtorString = toSource(WeakMap);
    -
    -    /** Used to convert symbols to primitives and strings. */
    -    var symbolProto = Symbol ? Symbol.prototype : undefined,
    -      symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,
    -      symbolToString = symbolProto ? symbolProto.toString : undefined;
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates a `lodash` object which wraps `value` to enable implicit method
    -     * chain sequences. Methods that operate on and return arrays, collections,
    -     * and functions can be chained together. Methods that retrieve a single value
    -     * or may return a primitive value will automatically end the chain sequence
    -     * and return the unwrapped value. Otherwise, the value must be unwrapped
    -     * with `_#value`.
    -     *
    -     * Explicit chain sequences, which must be unwrapped with `_#value`, may be
    -     * enabled using `_.chain`.
    -     *
    -     * The execution of chained methods is lazy, that is, it's deferred until
    -     * `_#value` is implicitly or explicitly called.
    -     *
    -     * Lazy evaluation allows several methods to support shortcut fusion.
    -     * Shortcut fusion is an optimization to merge iteratee calls; this avoids
    -     * the creation of intermediate arrays and can greatly reduce the number of
    -     * iteratee executions. Sections of a chain sequence qualify for shortcut
    -     * fusion if the section is applied to an array and iteratees accept only
    -     * one argument. The heuristic for whether a section qualifies for shortcut
    -     * fusion is subject to change.
    -     *
    -     * Chaining is supported in custom builds as long as the `_#value` method is
    -     * directly or indirectly included in the build.
    -     *
    -     * In addition to lodash methods, wrappers have `Array` and `String` methods.
    -     *
    -     * The wrapper `Array` methods are:
    -     * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
    -     *
    -     * The wrapper `String` methods are:
    -     * `replace` and `split`
    -     *
    -     * The wrapper methods that support shortcut fusion are:
    -     * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
    -     * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
    -     * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
    -     *
    -     * The chainable wrapper methods are:
    -     * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
    -     * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
    -     * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
    -     * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
    -     * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
    -     * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
    -     * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
    -     * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
    -     * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
    -     * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
    -     * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
    -     * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
    -     * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
    -     * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
    -     * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
    -     * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
    -     * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
    -     * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
    -     * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
    -     * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
    -     * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
    -     * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
    -     * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
    -     * `zipObject`, `zipObjectDeep`, and `zipWith`
    -     *
    -     * The wrapper methods that are **not** chainable by default are:
    -     * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
    -     * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,
    -     * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,
    -     * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,
    -     * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,
    -     * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,
    -     * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,
    -     * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,
    -     * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,
    -     * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,
    -     * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,
    -     * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,
    -     * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,
    -     * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,
    -     * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,
    -     * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,
    -     * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,
    -     * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,
    -     * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,
    -     * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,
    -     * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,
    -     * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,
    -     * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,
    -     * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,
    -     * `upperFirst`, `value`, and `words`
    -     *
    -     * @name _
    -     * @constructor
    -     * @category Seq
    -     * @param {*} value The value to wrap in a `lodash` instance.
    -     * @returns {Object} Returns the new `lodash` wrapper instance.
    -     * @example
    -     *
    -     * function square(n) {
    -     *   return n * n;
    -     * }
    -     *
    -     * var wrapped = _([1, 2, 3]);
    -     *
    -     * // Returns an unwrapped value.
    -     * wrapped.reduce(_.add);
    -     * // => 6
    -     *
    -     * // Returns a wrapped value.
    -     * var squares = wrapped.map(square);
    -     *
    -     * _.isArray(squares);
    -     * // => false
    -     *
    -     * _.isArray(squares.value());
    -     * // => true
    -     */
    -    function lodash(value) {
    -      if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
    -        if (value instanceof LodashWrapper) {
    -          return value;
    -        }
    -        if (hasOwnProperty.call(value, '__wrapped__')) {
    -          return wrapperClone(value);
    -        }
    -      }
    -      return new LodashWrapper(value);
    -    }
    -
    -    /**
    -     * The base implementation of `_.create` without support for assigning
    -     * properties to the created object.
    -     *
    -     * @private
    -     * @param {Object} proto The object to inherit from.
    -     * @returns {Object} Returns the new object.
    -     */
    -    var baseCreate = function () {
    -      function object() {}
    -      return function (proto) {
    -        if (!isObject(proto)) {
    -          return {};
    -        }
    -        if (objectCreate) {
    -          return objectCreate(proto);
    -        }
    -        object.prototype = proto;
    -        var result = new object();
    -        object.prototype = undefined;
    -        return result;
    -      };
    -    }();
    -
    -    /**
    -     * The function whose prototype chain sequence wrappers inherit from.
    -     *
    -     * @private
    -     */
    -    function baseLodash() {
    -      // No operation performed.
    -    }
    -
    -    /**
    -     * The base constructor for creating `lodash` wrapper objects.
    -     *
    -     * @private
    -     * @param {*} value The value to wrap.
    -     * @param {boolean} [chainAll] Enable explicit method chain sequences.
    -     */
    -    function LodashWrapper(value, chainAll) {
    -      this.__wrapped__ = value;
    -      this.__actions__ = [];
    -      this.__chain__ = !!chainAll;
    -      this.__index__ = 0;
    -      this.__values__ = undefined;
    -    }
    -
    -    /**
    -     * By default, the template delimiters used by lodash are like those in
    -     * embedded Ruby (ERB) as well as ES2015 template strings. Change the
    -     * following template settings to use alternative delimiters.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @type {Object}
    -     */
    -    lodash.templateSettings = {
    -      /**
    -       * Used to detect `data` property values to be HTML-escaped.
    -       *
    -       * @memberOf _.templateSettings
    -       * @type {RegExp}
    -       */
    -      'escape': reEscape,
    -      /**
    -       * Used to detect code to be evaluated.
    -       *
    -       * @memberOf _.templateSettings
    -       * @type {RegExp}
    -       */
    -      'evaluate': reEvaluate,
    -      /**
    -       * Used to detect `data` property values to inject.
    -       *
    -       * @memberOf _.templateSettings
    -       * @type {RegExp}
    -       */
    -      'interpolate': reInterpolate,
    -      /**
    -       * Used to reference the data object in the template text.
    -       *
    -       * @memberOf _.templateSettings
    -       * @type {string}
    -       */
    -      'variable': '',
    -      /**
    -       * Used to import variables into the compiled template.
    -       *
    -       * @memberOf _.templateSettings
    -       * @type {Object}
    -       */
    -      'imports': {
    -        /**
    -         * A reference to the `lodash` function.
    -         *
    -         * @memberOf _.templateSettings.imports
    -         * @type {Function}
    -         */
    -        '_': lodash
    -      }
    -    };
    -
    -    // Ensure wrappers are instances of `baseLodash`.
    -    lodash.prototype = baseLodash.prototype;
    -    lodash.prototype.constructor = lodash;
    -    LodashWrapper.prototype = baseCreate(baseLodash.prototype);
    -    LodashWrapper.prototype.constructor = LodashWrapper;
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
    -     *
    -     * @private
    -     * @constructor
    -     * @param {*} value The value to wrap.
    -     */
    -    function LazyWrapper(value) {
    -      this.__wrapped__ = value;
    -      this.__actions__ = [];
    -      this.__dir__ = 1;
    -      this.__filtered__ = false;
    -      this.__iteratees__ = [];
    -      this.__takeCount__ = MAX_ARRAY_LENGTH;
    -      this.__views__ = [];
    -    }
    -
    -    /**
    -     * Creates a clone of the lazy wrapper object.
    -     *
    -     * @private
    -     * @name clone
    -     * @memberOf LazyWrapper
    -     * @returns {Object} Returns the cloned `LazyWrapper` object.
    -     */
    -    function lazyClone() {
    -      var result = new LazyWrapper(this.__wrapped__);
    -      result.__actions__ = copyArray(this.__actions__);
    -      result.__dir__ = this.__dir__;
    -      result.__filtered__ = this.__filtered__;
    -      result.__iteratees__ = copyArray(this.__iteratees__);
    -      result.__takeCount__ = this.__takeCount__;
    -      result.__views__ = copyArray(this.__views__);
    -      return result;
    -    }
    -
    -    /**
    -     * Reverses the direction of lazy iteration.
    -     *
    -     * @private
    -     * @name reverse
    -     * @memberOf LazyWrapper
    -     * @returns {Object} Returns the new reversed `LazyWrapper` object.
    -     */
    -    function lazyReverse() {
    -      if (this.__filtered__) {
    -        var result = new LazyWrapper(this);
    -        result.__dir__ = -1;
    -        result.__filtered__ = true;
    -      } else {
    -        result = this.clone();
    -        result.__dir__ *= -1;
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Extracts the unwrapped value from its lazy wrapper.
    -     *
    -     * @private
    -     * @name value
    -     * @memberOf LazyWrapper
    -     * @returns {*} Returns the unwrapped value.
    -     */
    -    function lazyValue() {
    -      var array = this.__wrapped__.value(),
    -        dir = this.__dir__,
    -        isArr = isArray(array),
    -        isRight = dir < 0,
    -        arrLength = isArr ? array.length : 0,
    -        view = getView(0, arrLength, this.__views__),
    -        start = view.start,
    -        end = view.end,
    -        length = end - start,
    -        index = isRight ? end : start - 1,
    -        iteratees = this.__iteratees__,
    -        iterLength = iteratees.length,
    -        resIndex = 0,
    -        takeCount = nativeMin(length, this.__takeCount__);
    -      if (!isArr || !isRight && arrLength == length && takeCount == length) {
    -        return baseWrapperValue(array, this.__actions__);
    -      }
    -      var result = [];
    -      outer: while (length-- && resIndex < takeCount) {
    -        index += dir;
    -        var iterIndex = -1,
    -          value = array[index];
    -        while (++iterIndex < iterLength) {
    -          var data = iteratees[iterIndex],
    -            iteratee = data.iteratee,
    -            type = data.type,
    -            computed = iteratee(value);
    -          if (type == LAZY_MAP_FLAG) {
    -            value = computed;
    -          } else if (!computed) {
    -            if (type == LAZY_FILTER_FLAG) {
    -              continue outer;
    -            } else {
    -              break outer;
    -            }
    -          }
    -        }
    -        result[resIndex++] = value;
    -      }
    -      return result;
    -    }
    -
    -    // Ensure `LazyWrapper` is an instance of `baseLodash`.
    -    LazyWrapper.prototype = baseCreate(baseLodash.prototype);
    -    LazyWrapper.prototype.constructor = LazyWrapper;
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates a hash object.
    -     *
    -     * @private
    -     * @constructor
    -     * @param {Array} [entries] The key-value pairs to cache.
    -     */
    -    function Hash(entries) {
    -      var index = -1,
    -        length = entries == null ? 0 : entries.length;
    -      this.clear();
    -      while (++index < length) {
    -        var entry = entries[index];
    -        this.set(entry[0], entry[1]);
    -      }
    -    }
    -
    -    /**
    -     * Removes all key-value entries from the hash.
    -     *
    -     * @private
    -     * @name clear
    -     * @memberOf Hash
    -     */
    -    function hashClear() {
    -      this.__data__ = nativeCreate ? nativeCreate(null) : {};
    -      this.size = 0;
    -    }
    -
    -    /**
    -     * Removes `key` and its value from the hash.
    -     *
    -     * @private
    -     * @name delete
    -     * @memberOf Hash
    -     * @param {Object} hash The hash to modify.
    -     * @param {string} key The key of the value to remove.
    -     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
    -     */
    -    function hashDelete(key) {
    -      var result = this.has(key) && delete this.__data__[key];
    -      this.size -= result ? 1 : 0;
    -      return result;
    -    }
    -
    -    /**
    -     * Gets the hash value for `key`.
    -     *
    -     * @private
    -     * @name get
    -     * @memberOf Hash
    -     * @param {string} key The key of the value to get.
    -     * @returns {*} Returns the entry value.
    -     */
    -    function hashGet(key) {
    -      var data = this.__data__;
    -      if (nativeCreate) {
    -        var result = data[key];
    -        return result === HASH_UNDEFINED ? undefined : result;
    -      }
    -      return hasOwnProperty.call(data, key) ? data[key] : undefined;
    -    }
    -
    -    /**
    -     * Checks if a hash value for `key` exists.
    -     *
    -     * @private
    -     * @name has
    -     * @memberOf Hash
    -     * @param {string} key The key of the entry to check.
    -     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    -     */
    -    function hashHas(key) {
    -      var data = this.__data__;
    -      return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
    -    }
    -
    -    /**
    -     * Sets the hash `key` to `value`.
    -     *
    -     * @private
    -     * @name set
    -     * @memberOf Hash
    -     * @param {string} key The key of the value to set.
    -     * @param {*} value The value to set.
    -     * @returns {Object} Returns the hash instance.
    -     */
    -    function hashSet(key, value) {
    -      var data = this.__data__;
    -      this.size += this.has(key) ? 0 : 1;
    -      data[key] = nativeCreate && value === undefined ? HASH_UNDEFINED : value;
    -      return this;
    -    }
    -
    -    // Add methods to `Hash`.
    -    Hash.prototype.clear = hashClear;
    -    Hash.prototype['delete'] = hashDelete;
    -    Hash.prototype.get = hashGet;
    -    Hash.prototype.has = hashHas;
    -    Hash.prototype.set = hashSet;
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates an list cache object.
    -     *
    -     * @private
    -     * @constructor
    -     * @param {Array} [entries] The key-value pairs to cache.
    -     */
    -    function ListCache(entries) {
    -      var index = -1,
    -        length = entries == null ? 0 : entries.length;
    -      this.clear();
    -      while (++index < length) {
    -        var entry = entries[index];
    -        this.set(entry[0], entry[1]);
    -      }
    -    }
    -
    -    /**
    -     * Removes all key-value entries from the list cache.
    -     *
    -     * @private
    -     * @name clear
    -     * @memberOf ListCache
    -     */
    -    function listCacheClear() {
    -      this.__data__ = [];
    -      this.size = 0;
    -    }
    -
    -    /**
    -     * Removes `key` and its value from the list cache.
    -     *
    -     * @private
    -     * @name delete
    -     * @memberOf ListCache
    -     * @param {string} key The key of the value to remove.
    -     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
    -     */
    -    function listCacheDelete(key) {
    -      var data = this.__data__,
    -        index = assocIndexOf(data, key);
    -      if (index < 0) {
    -        return false;
    -      }
    -      var lastIndex = data.length - 1;
    -      if (index == lastIndex) {
    -        data.pop();
    -      } else {
    -        splice.call(data, index, 1);
    -      }
    -      --this.size;
    -      return true;
    -    }
    -
    -    /**
    -     * Gets the list cache value for `key`.
    -     *
    -     * @private
    -     * @name get
    -     * @memberOf ListCache
    -     * @param {string} key The key of the value to get.
    -     * @returns {*} Returns the entry value.
    -     */
    -    function listCacheGet(key) {
    -      var data = this.__data__,
    -        index = assocIndexOf(data, key);
    -      return index < 0 ? undefined : data[index][1];
    -    }
    -
    -    /**
    -     * Checks if a list cache value for `key` exists.
    -     *
    -     * @private
    -     * @name has
    -     * @memberOf ListCache
    -     * @param {string} key The key of the entry to check.
    -     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    -     */
    -    function listCacheHas(key) {
    -      return assocIndexOf(this.__data__, key) > -1;
    -    }
    -
    -    /**
    -     * Sets the list cache `key` to `value`.
    -     *
    -     * @private
    -     * @name set
    -     * @memberOf ListCache
    -     * @param {string} key The key of the value to set.
    -     * @param {*} value The value to set.
    -     * @returns {Object} Returns the list cache instance.
    -     */
    -    function listCacheSet(key, value) {
    -      var data = this.__data__,
    -        index = assocIndexOf(data, key);
    -      if (index < 0) {
    -        ++this.size;
    -        data.push([key, value]);
    -      } else {
    -        data[index][1] = value;
    -      }
    -      return this;
    -    }
    -
    -    // Add methods to `ListCache`.
    -    ListCache.prototype.clear = listCacheClear;
    -    ListCache.prototype['delete'] = listCacheDelete;
    -    ListCache.prototype.get = listCacheGet;
    -    ListCache.prototype.has = listCacheHas;
    -    ListCache.prototype.set = listCacheSet;
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates a map cache object to store key-value pairs.
    -     *
    -     * @private
    -     * @constructor
    -     * @param {Array} [entries] The key-value pairs to cache.
    -     */
    -    function MapCache(entries) {
    -      var index = -1,
    -        length = entries == null ? 0 : entries.length;
    -      this.clear();
    -      while (++index < length) {
    -        var entry = entries[index];
    -        this.set(entry[0], entry[1]);
    -      }
    -    }
    -
    -    /**
    -     * Removes all key-value entries from the map.
    -     *
    -     * @private
    -     * @name clear
    -     * @memberOf MapCache
    -     */
    -    function mapCacheClear() {
    -      this.size = 0;
    -      this.__data__ = {
    -        'hash': new Hash(),
    -        'map': new (Map || ListCache)(),
    -        'string': new Hash()
    -      };
    -    }
    -
    -    /**
    -     * Removes `key` and its value from the map.
    -     *
    -     * @private
    -     * @name delete
    -     * @memberOf MapCache
    -     * @param {string} key The key of the value to remove.
    -     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
    -     */
    -    function mapCacheDelete(key) {
    -      var result = getMapData(this, key)['delete'](key);
    -      this.size -= result ? 1 : 0;
    -      return result;
    -    }
    -
    -    /**
    -     * Gets the map value for `key`.
    -     *
    -     * @private
    -     * @name get
    -     * @memberOf MapCache
    -     * @param {string} key The key of the value to get.
    -     * @returns {*} Returns the entry value.
    -     */
    -    function mapCacheGet(key) {
    -      return getMapData(this, key).get(key);
    -    }
    -
    -    /**
    -     * Checks if a map value for `key` exists.
    -     *
    -     * @private
    -     * @name has
    -     * @memberOf MapCache
    -     * @param {string} key The key of the entry to check.
    -     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    -     */
    -    function mapCacheHas(key) {
    -      return getMapData(this, key).has(key);
    -    }
    -
    -    /**
    -     * Sets the map `key` to `value`.
    -     *
    -     * @private
    -     * @name set
    -     * @memberOf MapCache
    -     * @param {string} key The key of the value to set.
    -     * @param {*} value The value to set.
    -     * @returns {Object} Returns the map cache instance.
    -     */
    -    function mapCacheSet(key, value) {
    -      var data = getMapData(this, key),
    -        size = data.size;
    -      data.set(key, value);
    -      this.size += data.size == size ? 0 : 1;
    -      return this;
    -    }
    -
    -    // Add methods to `MapCache`.
    -    MapCache.prototype.clear = mapCacheClear;
    -    MapCache.prototype['delete'] = mapCacheDelete;
    -    MapCache.prototype.get = mapCacheGet;
    -    MapCache.prototype.has = mapCacheHas;
    -    MapCache.prototype.set = mapCacheSet;
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     *
    -     * Creates an array cache object to store unique values.
    -     *
    -     * @private
    -     * @constructor
    -     * @param {Array} [values] The values to cache.
    -     */
    -    function SetCache(values) {
    -      var index = -1,
    -        length = values == null ? 0 : values.length;
    -      this.__data__ = new MapCache();
    -      while (++index < length) {
    -        this.add(values[index]);
    -      }
    -    }
    -
    -    /**
    -     * Adds `value` to the array cache.
    -     *
    -     * @private
    -     * @name add
    -     * @memberOf SetCache
    -     * @alias push
    -     * @param {*} value The value to cache.
    -     * @returns {Object} Returns the cache instance.
    -     */
    -    function setCacheAdd(value) {
    -      this.__data__.set(value, HASH_UNDEFINED);
    -      return this;
    -    }
    -
    -    /**
    -     * Checks if `value` is in the array cache.
    -     *
    -     * @private
    -     * @name has
    -     * @memberOf SetCache
    -     * @param {*} value The value to search for.
    -     * @returns {number} Returns `true` if `value` is found, else `false`.
    -     */
    -    function setCacheHas(value) {
    -      return this.__data__.has(value);
    -    }
    -
    -    // Add methods to `SetCache`.
    -    SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
    -    SetCache.prototype.has = setCacheHas;
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates a stack cache object to store key-value pairs.
    -     *
    -     * @private
    -     * @constructor
    -     * @param {Array} [entries] The key-value pairs to cache.
    -     */
    -    function Stack(entries) {
    -      var data = this.__data__ = new ListCache(entries);
    -      this.size = data.size;
    -    }
    -
    -    /**
    -     * Removes all key-value entries from the stack.
    -     *
    -     * @private
    -     * @name clear
    -     * @memberOf Stack
    -     */
    -    function stackClear() {
    -      this.__data__ = new ListCache();
    -      this.size = 0;
    -    }
    -
    -    /**
    -     * Removes `key` and its value from the stack.
    -     *
    -     * @private
    -     * @name delete
    -     * @memberOf Stack
    -     * @param {string} key The key of the value to remove.
    -     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
    -     */
    -    function stackDelete(key) {
    -      var data = this.__data__,
    -        result = data['delete'](key);
    -      this.size = data.size;
    -      return result;
    -    }
    -
    -    /**
    -     * Gets the stack value for `key`.
    -     *
    -     * @private
    -     * @name get
    -     * @memberOf Stack
    -     * @param {string} key The key of the value to get.
    -     * @returns {*} Returns the entry value.
    -     */
    -    function stackGet(key) {
    -      return this.__data__.get(key);
    -    }
    -
    -    /**
    -     * Checks if a stack value for `key` exists.
    -     *
    -     * @private
    -     * @name has
    -     * @memberOf Stack
    -     * @param {string} key The key of the entry to check.
    -     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
    -     */
    -    function stackHas(key) {
    -      return this.__data__.has(key);
    -    }
    -
    -    /**
    -     * Sets the stack `key` to `value`.
    -     *
    -     * @private
    -     * @name set
    -     * @memberOf Stack
    -     * @param {string} key The key of the value to set.
    -     * @param {*} value The value to set.
    -     * @returns {Object} Returns the stack cache instance.
    -     */
    -    function stackSet(key, value) {
    -      var data = this.__data__;
    -      if (data instanceof ListCache) {
    -        var pairs = data.__data__;
    -        if (!Map || pairs.length < LARGE_ARRAY_SIZE - 1) {
    -          pairs.push([key, value]);
    -          this.size = ++data.size;
    -          return this;
    -        }
    -        data = this.__data__ = new MapCache(pairs);
    -      }
    -      data.set(key, value);
    -      this.size = data.size;
    -      return this;
    -    }
    -
    -    // Add methods to `Stack`.
    -    Stack.prototype.clear = stackClear;
    -    Stack.prototype['delete'] = stackDelete;
    -    Stack.prototype.get = stackGet;
    -    Stack.prototype.has = stackHas;
    -    Stack.prototype.set = stackSet;
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates an array of the enumerable property names of the array-like `value`.
    -     *
    -     * @private
    -     * @param {*} value The value to query.
    -     * @param {boolean} inherited Specify returning inherited property names.
    -     * @returns {Array} Returns the array of property names.
    -     */
    -    function arrayLikeKeys(value, inherited) {
    -      var isArr = isArray(value),
    -        isArg = !isArr && isArguments(value),
    -        isBuff = !isArr && !isArg && isBuffer(value),
    -        isType = !isArr && !isArg && !isBuff && isTypedArray(value),
    -        skipIndexes = isArr || isArg || isBuff || isType,
    -        result = skipIndexes ? baseTimes(value.length, String) : [],
    -        length = result.length;
    -      for (var key in value) {
    -        if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (
    -        // Safari 9 has enumerable `arguments.length` in strict mode.
    -        key == 'length' ||
    -        // Node.js 0.10 has enumerable non-index properties on buffers.
    -        isBuff && (key == 'offset' || key == 'parent') ||
    -        // PhantomJS 2 has enumerable non-index properties on typed arrays.
    -        isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset') ||
    -        // Skip index properties.
    -        isIndex(key, length)))) {
    -          result.push(key);
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * A specialized version of `_.sample` for arrays.
    -     *
    -     * @private
    -     * @param {Array} array The array to sample.
    -     * @returns {*} Returns the random element.
    -     */
    -    function arraySample(array) {
    -      var length = array.length;
    -      return length ? array[baseRandom(0, length - 1)] : undefined;
    -    }
    -
    -    /**
    -     * A specialized version of `_.sampleSize` for arrays.
    -     *
    -     * @private
    -     * @param {Array} array The array to sample.
    -     * @param {number} n The number of elements to sample.
    -     * @returns {Array} Returns the random elements.
    -     */
    -    function arraySampleSize(array, n) {
    -      return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length));
    -    }
    -
    -    /**
    -     * A specialized version of `_.shuffle` for arrays.
    -     *
    -     * @private
    -     * @param {Array} array The array to shuffle.
    -     * @returns {Array} Returns the new shuffled array.
    -     */
    -    function arrayShuffle(array) {
    -      return shuffleSelf(copyArray(array));
    -    }
    -
    -    /**
    -     * This function is like `assignValue` except that it doesn't assign
    -     * `undefined` values.
    -     *
    -     * @private
    -     * @param {Object} object The object to modify.
    -     * @param {string} key The key of the property to assign.
    -     * @param {*} value The value to assign.
    -     */
    -    function assignMergeValue(object, key, value) {
    -      if (value !== undefined && !eq(object[key], value) || value === undefined && !(key in object)) {
    -        baseAssignValue(object, key, value);
    -      }
    -    }
    -
    -    /**
    -     * Assigns `value` to `key` of `object` if the existing value is not equivalent
    -     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    -     * for equality comparisons.
    -     *
    -     * @private
    -     * @param {Object} object The object to modify.
    -     * @param {string} key The key of the property to assign.
    -     * @param {*} value The value to assign.
    -     */
    -    function assignValue(object, key, value) {
    -      var objValue = object[key];
    -      if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || value === undefined && !(key in object)) {
    -        baseAssignValue(object, key, value);
    -      }
    -    }
    -
    -    /**
    -     * Gets the index at which the `key` is found in `array` of key-value pairs.
    -     *
    -     * @private
    -     * @param {Array} array The array to inspect.
    -     * @param {*} key The key to search for.
    -     * @returns {number} Returns the index of the matched value, else `-1`.
    -     */
    -    function assocIndexOf(array, key) {
    -      var length = array.length;
    -      while (length--) {
    -        if (eq(array[length][0], key)) {
    -          return length;
    -        }
    -      }
    -      return -1;
    -    }
    -
    -    /**
    -     * Aggregates elements of `collection` on `accumulator` with keys transformed
    -     * by `iteratee` and values set by `setter`.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} setter The function to set `accumulator` values.
    -     * @param {Function} iteratee The iteratee to transform keys.
    -     * @param {Object} accumulator The initial aggregated object.
    -     * @returns {Function} Returns `accumulator`.
    -     */
    -    function baseAggregator(collection, setter, iteratee, accumulator) {
    -      baseEach(collection, function (value, key, collection) {
    -        setter(accumulator, value, iteratee(value), collection);
    -      });
    -      return accumulator;
    -    }
    -
    -    /**
    -     * The base implementation of `_.assign` without support for multiple sources
    -     * or `customizer` functions.
    -     *
    -     * @private
    -     * @param {Object} object The destination object.
    -     * @param {Object} source The source object.
    -     * @returns {Object} Returns `object`.
    -     */
    -    function baseAssign(object, source) {
    -      return object && copyObject(source, keys(source), object);
    -    }
    -
    -    /**
    -     * The base implementation of `_.assignIn` without support for multiple sources
    -     * or `customizer` functions.
    -     *
    -     * @private
    -     * @param {Object} object The destination object.
    -     * @param {Object} source The source object.
    -     * @returns {Object} Returns `object`.
    -     */
    -    function baseAssignIn(object, source) {
    -      return object && copyObject(source, keysIn(source), object);
    -    }
    -
    -    /**
    -     * The base implementation of `assignValue` and `assignMergeValue` without
    -     * value checks.
    -     *
    -     * @private
    -     * @param {Object} object The object to modify.
    -     * @param {string} key The key of the property to assign.
    -     * @param {*} value The value to assign.
    -     */
    -    function baseAssignValue(object, key, value) {
    -      if (key == '__proto__' && defineProperty) {
    -        defineProperty(object, key, {
    -          'configurable': true,
    -          'enumerable': true,
    -          'value': value,
    -          'writable': true
    -        });
    -      } else {
    -        object[key] = value;
    -      }
    -    }
    -
    -    /**
    -     * The base implementation of `_.at` without support for individual paths.
    -     *
    -     * @private
    -     * @param {Object} object The object to iterate over.
    -     * @param {string[]} paths The property paths to pick.
    -     * @returns {Array} Returns the picked elements.
    -     */
    -    function baseAt(object, paths) {
    -      var index = -1,
    -        length = paths.length,
    -        result = Array(length),
    -        skip = object == null;
    -      while (++index < length) {
    -        result[index] = skip ? undefined : get(object, paths[index]);
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.clamp` which doesn't coerce arguments.
    -     *
    -     * @private
    -     * @param {number} number The number to clamp.
    -     * @param {number} [lower] The lower bound.
    -     * @param {number} upper The upper bound.
    -     * @returns {number} Returns the clamped number.
    -     */
    -    function baseClamp(number, lower, upper) {
    -      if (number === number) {
    -        if (upper !== undefined) {
    -          number = number <= upper ? number : upper;
    -        }
    -        if (lower !== undefined) {
    -          number = number >= lower ? number : lower;
    -        }
    -      }
    -      return number;
    -    }
    -
    -    /**
    -     * The base implementation of `_.clone` and `_.cloneDeep` which tracks
    -     * traversed objects.
    -     *
    -     * @private
    -     * @param {*} value The value to clone.
    -     * @param {boolean} bitmask The bitmask flags.
    -     *  1 - Deep clone
    -     *  2 - Flatten inherited properties
    -     *  4 - Clone symbols
    -     * @param {Function} [customizer] The function to customize cloning.
    -     * @param {string} [key] The key of `value`.
    -     * @param {Object} [object] The parent object of `value`.
    -     * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
    -     * @returns {*} Returns the cloned value.
    -     */
    -    function baseClone(value, bitmask, customizer, key, object, stack) {
    -      var result,
    -        isDeep = bitmask & CLONE_DEEP_FLAG,
    -        isFlat = bitmask & CLONE_FLAT_FLAG,
    -        isFull = bitmask & CLONE_SYMBOLS_FLAG;
    -      if (customizer) {
    -        result = object ? customizer(value, key, object, stack) : customizer(value);
    -      }
    -      if (result !== undefined) {
    -        return result;
    -      }
    -      if (!isObject(value)) {
    -        return value;
    -      }
    -      var isArr = isArray(value);
    -      if (isArr) {
    -        result = initCloneArray(value);
    -        if (!isDeep) {
    -          return copyArray(value, result);
    -        }
    -      } else {
    -        var tag = getTag(value),
    -          isFunc = tag == funcTag || tag == genTag;
    -        if (isBuffer(value)) {
    -          return cloneBuffer(value, isDeep);
    -        }
    -        if (tag == objectTag || tag == argsTag || isFunc && !object) {
    -          result = isFlat || isFunc ? {} : initCloneObject(value);
    -          if (!isDeep) {
    -            return isFlat ? copySymbolsIn(value, baseAssignIn(result, value)) : copySymbols(value, baseAssign(result, value));
    -          }
    -        } else {
    -          if (!cloneableTags[tag]) {
    -            return object ? value : {};
    -          }
    -          result = initCloneByTag(value, tag, isDeep);
    -        }
    -      }
    -      // Check for circular references and return its corresponding clone.
    -      stack || (stack = new Stack());
    -      var stacked = stack.get(value);
    -      if (stacked) {
    -        return stacked;
    -      }
    -      stack.set(value, result);
    -      if (isSet(value)) {
    -        value.forEach(function (subValue) {
    -          result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
    -        });
    -      } else if (isMap(value)) {
    -        value.forEach(function (subValue, key) {
    -          result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
    -        });
    -      }
    -      var keysFunc = isFull ? isFlat ? getAllKeysIn : getAllKeys : isFlat ? keysIn : keys;
    -      var props = isArr ? undefined : keysFunc(value);
    -      arrayEach(props || value, function (subValue, key) {
    -        if (props) {
    -          key = subValue;
    -          subValue = value[key];
    -        }
    -        // Recursively populate clone (susceptible to call stack limits).
    -        assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
    -      });
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.conforms` which doesn't clone `source`.
    -     *
    -     * @private
    -     * @param {Object} source The object of property predicates to conform to.
    -     * @returns {Function} Returns the new spec function.
    -     */
    -    function baseConforms(source) {
    -      var props = keys(source);
    -      return function (object) {
    -        return baseConformsTo(object, source, props);
    -      };
    -    }
    -
    -    /**
    -     * The base implementation of `_.conformsTo` which accepts `props` to check.
    -     *
    -     * @private
    -     * @param {Object} object The object to inspect.
    -     * @param {Object} source The object of property predicates to conform to.
    -     * @returns {boolean} Returns `true` if `object` conforms, else `false`.
    -     */
    -    function baseConformsTo(object, source, props) {
    -      var length = props.length;
    -      if (object == null) {
    -        return !length;
    -      }
    -      object = Object(object);
    -      while (length--) {
    -        var key = props[length],
    -          predicate = source[key],
    -          value = object[key];
    -        if (value === undefined && !(key in object) || !predicate(value)) {
    -          return false;
    -        }
    -      }
    -      return true;
    -    }
    -
    -    /**
    -     * The base implementation of `_.delay` and `_.defer` which accepts `args`
    -     * to provide to `func`.
    -     *
    -     * @private
    -     * @param {Function} func The function to delay.
    -     * @param {number} wait The number of milliseconds to delay invocation.
    -     * @param {Array} args The arguments to provide to `func`.
    -     * @returns {number|Object} Returns the timer id or timeout object.
    -     */
    -    function baseDelay(func, wait, args) {
    -      if (typeof func != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      return setTimeout(function () {
    -        func.apply(undefined, args);
    -      }, wait);
    -    }
    -
    -    /**
    -     * The base implementation of methods like `_.difference` without support
    -     * for excluding multiple arrays or iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array} array The array to inspect.
    -     * @param {Array} values The values to exclude.
    -     * @param {Function} [iteratee] The iteratee invoked per element.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns the new array of filtered values.
    -     */
    -    function baseDifference(array, values, iteratee, comparator) {
    -      var index = -1,
    -        includes = arrayIncludes,
    -        isCommon = true,
    -        length = array.length,
    -        result = [],
    -        valuesLength = values.length;
    -      if (!length) {
    -        return result;
    -      }
    -      if (iteratee) {
    -        values = arrayMap(values, baseUnary(iteratee));
    -      }
    -      if (comparator) {
    -        includes = arrayIncludesWith;
    -        isCommon = false;
    -      } else if (values.length >= LARGE_ARRAY_SIZE) {
    -        includes = cacheHas;
    -        isCommon = false;
    -        values = new SetCache(values);
    -      }
    -      outer: while (++index < length) {
    -        var value = array[index],
    -          computed = iteratee == null ? value : iteratee(value);
    -        value = comparator || value !== 0 ? value : 0;
    -        if (isCommon && computed === computed) {
    -          var valuesIndex = valuesLength;
    -          while (valuesIndex--) {
    -            if (values[valuesIndex] === computed) {
    -              continue outer;
    -            }
    -          }
    -          result.push(value);
    -        } else if (!includes(values, computed, comparator)) {
    -          result.push(value);
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.forEach` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} iteratee The function invoked per iteration.
    -     * @returns {Array|Object} Returns `collection`.
    -     */
    -    var baseEach = createBaseEach(baseForOwn);
    -
    -    /**
    -     * The base implementation of `_.forEachRight` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} iteratee The function invoked per iteration.
    -     * @returns {Array|Object} Returns `collection`.
    -     */
    -    var baseEachRight = createBaseEach(baseForOwnRight, true);
    -
    -    /**
    -     * The base implementation of `_.every` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} predicate The function invoked per iteration.
    -     * @returns {boolean} Returns `true` if all elements pass the predicate check,
    -     *  else `false`
    -     */
    -    function baseEvery(collection, predicate) {
    -      var result = true;
    -      baseEach(collection, function (value, index, collection) {
    -        result = !!predicate(value, index, collection);
    -        return result;
    -      });
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of methods like `_.max` and `_.min` which accepts a
    -     * `comparator` to determine the extremum value.
    -     *
    -     * @private
    -     * @param {Array} array The array to iterate over.
    -     * @param {Function} iteratee The iteratee invoked per iteration.
    -     * @param {Function} comparator The comparator used to compare values.
    -     * @returns {*} Returns the extremum value.
    -     */
    -    function baseExtremum(array, iteratee, comparator) {
    -      var index = -1,
    -        length = array.length;
    -      while (++index < length) {
    -        var value = array[index],
    -          current = iteratee(value);
    -        if (current != null && (computed === undefined ? current === current && !isSymbol(current) : comparator(current, computed))) {
    -          var computed = current,
    -            result = value;
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.fill` without an iteratee call guard.
    -     *
    -     * @private
    -     * @param {Array} array The array to fill.
    -     * @param {*} value The value to fill `array` with.
    -     * @param {number} [start=0] The start position.
    -     * @param {number} [end=array.length] The end position.
    -     * @returns {Array} Returns `array`.
    -     */
    -    function baseFill(array, value, start, end) {
    -      var length = array.length;
    -      start = toInteger(start);
    -      if (start < 0) {
    -        start = -start > length ? 0 : length + start;
    -      }
    -      end = end === undefined || end > length ? length : toInteger(end);
    -      if (end < 0) {
    -        end += length;
    -      }
    -      end = start > end ? 0 : toLength(end);
    -      while (start < end) {
    -        array[start++] = value;
    -      }
    -      return array;
    -    }
    -
    -    /**
    -     * The base implementation of `_.filter` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} predicate The function invoked per iteration.
    -     * @returns {Array} Returns the new filtered array.
    -     */
    -    function baseFilter(collection, predicate) {
    -      var result = [];
    -      baseEach(collection, function (value, index, collection) {
    -        if (predicate(value, index, collection)) {
    -          result.push(value);
    -        }
    -      });
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.flatten` with support for restricting flattening.
    -     *
    -     * @private
    -     * @param {Array} array The array to flatten.
    -     * @param {number} depth The maximum recursion depth.
    -     * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
    -     * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
    -     * @param {Array} [result=[]] The initial result value.
    -     * @returns {Array} Returns the new flattened array.
    -     */
    -    function baseFlatten(array, depth, predicate, isStrict, result) {
    -      var index = -1,
    -        length = array.length;
    -      predicate || (predicate = isFlattenable);
    -      result || (result = []);
    -      while (++index < length) {
    -        var value = array[index];
    -        if (depth > 0 && predicate(value)) {
    -          if (depth > 1) {
    -            // Recursively flatten arrays (susceptible to call stack limits).
    -            baseFlatten(value, depth - 1, predicate, isStrict, result);
    -          } else {
    -            arrayPush(result, value);
    -          }
    -        } else if (!isStrict) {
    -          result[result.length] = value;
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `baseForOwn` which iterates over `object`
    -     * properties returned by `keysFunc` and invokes `iteratee` for each property.
    -     * Iteratee functions may exit iteration early by explicitly returning `false`.
    -     *
    -     * @private
    -     * @param {Object} object The object to iterate over.
    -     * @param {Function} iteratee The function invoked per iteration.
    -     * @param {Function} keysFunc The function to get the keys of `object`.
    -     * @returns {Object} Returns `object`.
    -     */
    -    var baseFor = createBaseFor();
    -
    -    /**
    -     * This function is like `baseFor` except that it iterates over properties
    -     * in the opposite order.
    -     *
    -     * @private
    -     * @param {Object} object The object to iterate over.
    -     * @param {Function} iteratee The function invoked per iteration.
    -     * @param {Function} keysFunc The function to get the keys of `object`.
    -     * @returns {Object} Returns `object`.
    -     */
    -    var baseForRight = createBaseFor(true);
    -
    -    /**
    -     * The base implementation of `_.forOwn` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Object} object The object to iterate over.
    -     * @param {Function} iteratee The function invoked per iteration.
    -     * @returns {Object} Returns `object`.
    -     */
    -    function baseForOwn(object, iteratee) {
    -      return object && baseFor(object, iteratee, keys);
    -    }
    -
    -    /**
    -     * The base implementation of `_.forOwnRight` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Object} object The object to iterate over.
    -     * @param {Function} iteratee The function invoked per iteration.
    -     * @returns {Object} Returns `object`.
    -     */
    -    function baseForOwnRight(object, iteratee) {
    -      return object && baseForRight(object, iteratee, keys);
    -    }
    -
    -    /**
    -     * The base implementation of `_.functions` which creates an array of
    -     * `object` function property names filtered from `props`.
    -     *
    -     * @private
    -     * @param {Object} object The object to inspect.
    -     * @param {Array} props The property names to filter.
    -     * @returns {Array} Returns the function names.
    -     */
    -    function baseFunctions(object, props) {
    -      return arrayFilter(props, function (key) {
    -        return isFunction(object[key]);
    -      });
    -    }
    -
    -    /**
    -     * The base implementation of `_.get` without support for default values.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @param {Array|string} path The path of the property to get.
    -     * @returns {*} Returns the resolved value.
    -     */
    -    function baseGet(object, path) {
    -      path = castPath(path, object);
    -      var index = 0,
    -        length = path.length;
    -      while (object != null && index < length) {
    -        object = object[toKey(path[index++])];
    -      }
    -      return index && index == length ? object : undefined;
    -    }
    -
    -    /**
    -     * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
    -     * `keysFunc` and `symbolsFunc` to get the enumerable property names and
    -     * symbols of `object`.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @param {Function} keysFunc The function to get the keys of `object`.
    -     * @param {Function} symbolsFunc The function to get the symbols of `object`.
    -     * @returns {Array} Returns the array of property names and symbols.
    -     */
    -    function baseGetAllKeys(object, keysFunc, symbolsFunc) {
    -      var result = keysFunc(object);
    -      return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
    -    }
    -
    -    /**
    -     * The base implementation of `getTag` without fallbacks for buggy environments.
    -     *
    -     * @private
    -     * @param {*} value The value to query.
    -     * @returns {string} Returns the `toStringTag`.
    -     */
    -    function baseGetTag(value) {
    -      if (value == null) {
    -        return value === undefined ? undefinedTag : nullTag;
    -      }
    -      return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);
    -    }
    -
    -    /**
    -     * The base implementation of `_.gt` which doesn't coerce arguments.
    -     *
    -     * @private
    -     * @param {*} value The value to compare.
    -     * @param {*} other The other value to compare.
    -     * @returns {boolean} Returns `true` if `value` is greater than `other`,
    -     *  else `false`.
    -     */
    -    function baseGt(value, other) {
    -      return value > other;
    -    }
    -
    -    /**
    -     * The base implementation of `_.has` without support for deep paths.
    -     *
    -     * @private
    -     * @param {Object} [object] The object to query.
    -     * @param {Array|string} key The key to check.
    -     * @returns {boolean} Returns `true` if `key` exists, else `false`.
    -     */
    -    function baseHas(object, key) {
    -      return object != null && hasOwnProperty.call(object, key);
    -    }
    -
    -    /**
    -     * The base implementation of `_.hasIn` without support for deep paths.
    -     *
    -     * @private
    -     * @param {Object} [object] The object to query.
    -     * @param {Array|string} key The key to check.
    -     * @returns {boolean} Returns `true` if `key` exists, else `false`.
    -     */
    -    function baseHasIn(object, key) {
    -      return object != null && key in Object(object);
    -    }
    -
    -    /**
    -     * The base implementation of `_.inRange` which doesn't coerce arguments.
    -     *
    -     * @private
    -     * @param {number} number The number to check.
    -     * @param {number} start The start of the range.
    -     * @param {number} end The end of the range.
    -     * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
    -     */
    -    function baseInRange(number, start, end) {
    -      return number >= nativeMin(start, end) && number < nativeMax(start, end);
    -    }
    -
    -    /**
    -     * The base implementation of methods like `_.intersection`, without support
    -     * for iteratee shorthands, that accepts an array of arrays to inspect.
    -     *
    -     * @private
    -     * @param {Array} arrays The arrays to inspect.
    -     * @param {Function} [iteratee] The iteratee invoked per element.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns the new array of shared values.
    -     */
    -    function baseIntersection(arrays, iteratee, comparator) {
    -      var includes = comparator ? arrayIncludesWith : arrayIncludes,
    -        length = arrays[0].length,
    -        othLength = arrays.length,
    -        othIndex = othLength,
    -        caches = Array(othLength),
    -        maxLength = Infinity,
    -        result = [];
    -      while (othIndex--) {
    -        var array = arrays[othIndex];
    -        if (othIndex && iteratee) {
    -          array = arrayMap(array, baseUnary(iteratee));
    -        }
    -        maxLength = nativeMin(array.length, maxLength);
    -        caches[othIndex] = !comparator && (iteratee || length >= 120 && array.length >= 120) ? new SetCache(othIndex && array) : undefined;
    -      }
    -      array = arrays[0];
    -      var index = -1,
    -        seen = caches[0];
    -      outer: while (++index < length && result.length < maxLength) {
    -        var value = array[index],
    -          computed = iteratee ? iteratee(value) : value;
    -        value = comparator || value !== 0 ? value : 0;
    -        if (!(seen ? cacheHas(seen, computed) : includes(result, computed, comparator))) {
    -          othIndex = othLength;
    -          while (--othIndex) {
    -            var cache = caches[othIndex];
    -            if (!(cache ? cacheHas(cache, computed) : includes(arrays[othIndex], computed, comparator))) {
    -              continue outer;
    -            }
    -          }
    -          if (seen) {
    -            seen.push(computed);
    -          }
    -          result.push(value);
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.invert` and `_.invertBy` which inverts
    -     * `object` with values transformed by `iteratee` and set by `setter`.
    -     *
    -     * @private
    -     * @param {Object} object The object to iterate over.
    -     * @param {Function} setter The function to set `accumulator` values.
    -     * @param {Function} iteratee The iteratee to transform values.
    -     * @param {Object} accumulator The initial inverted object.
    -     * @returns {Function} Returns `accumulator`.
    -     */
    -    function baseInverter(object, setter, iteratee, accumulator) {
    -      baseForOwn(object, function (value, key, object) {
    -        setter(accumulator, iteratee(value), key, object);
    -      });
    -      return accumulator;
    -    }
    -
    -    /**
    -     * The base implementation of `_.invoke` without support for individual
    -     * method arguments.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @param {Array|string} path The path of the method to invoke.
    -     * @param {Array} args The arguments to invoke the method with.
    -     * @returns {*} Returns the result of the invoked method.
    -     */
    -    function baseInvoke(object, path, args) {
    -      path = castPath(path, object);
    -      object = parent(object, path);
    -      var func = object == null ? object : object[toKey(last(path))];
    -      return func == null ? undefined : apply(func, object, args);
    -    }
    -
    -    /**
    -     * The base implementation of `_.isArguments`.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is an `arguments` object,
    -     */
    -    function baseIsArguments(value) {
    -      return isObjectLike(value) && baseGetTag(value) == argsTag;
    -    }
    -
    -    /**
    -     * The base implementation of `_.isArrayBuffer` without Node.js optimizations.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
    -     */
    -    function baseIsArrayBuffer(value) {
    -      return isObjectLike(value) && baseGetTag(value) == arrayBufferTag;
    -    }
    -
    -    /**
    -     * The base implementation of `_.isDate` without Node.js optimizations.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
    -     */
    -    function baseIsDate(value) {
    -      return isObjectLike(value) && baseGetTag(value) == dateTag;
    -    }
    -
    -    /**
    -     * The base implementation of `_.isEqual` which supports partial comparisons
    -     * and tracks traversed objects.
    -     *
    -     * @private
    -     * @param {*} value The value to compare.
    -     * @param {*} other The other value to compare.
    -     * @param {boolean} bitmask The bitmask flags.
    -     *  1 - Unordered comparison
    -     *  2 - Partial comparison
    -     * @param {Function} [customizer] The function to customize comparisons.
    -     * @param {Object} [stack] Tracks traversed `value` and `other` objects.
    -     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
    -     */
    -    function baseIsEqual(value, other, bitmask, customizer, stack) {
    -      if (value === other) {
    -        return true;
    -      }
    -      if (value == null || other == null || !isObjectLike(value) && !isObjectLike(other)) {
    -        return value !== value && other !== other;
    -      }
    -      return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
    -    }
    -
    -    /**
    -     * A specialized version of `baseIsEqual` for arrays and objects which performs
    -     * deep comparisons and tracks traversed objects enabling objects with circular
    -     * references to be compared.
    -     *
    -     * @private
    -     * @param {Object} object The object to compare.
    -     * @param {Object} other The other object to compare.
    -     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
    -     * @param {Function} customizer The function to customize comparisons.
    -     * @param {Function} equalFunc The function to determine equivalents of values.
    -     * @param {Object} [stack] Tracks traversed `object` and `other` objects.
    -     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
    -     */
    -    function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
    -      var objIsArr = isArray(object),
    -        othIsArr = isArray(other),
    -        objTag = objIsArr ? arrayTag : getTag(object),
    -        othTag = othIsArr ? arrayTag : getTag(other);
    -      objTag = objTag == argsTag ? objectTag : objTag;
    -      othTag = othTag == argsTag ? objectTag : othTag;
    -      var objIsObj = objTag == objectTag,
    -        othIsObj = othTag == objectTag,
    -        isSameTag = objTag == othTag;
    -      if (isSameTag && isBuffer(object)) {
    -        if (!isBuffer(other)) {
    -          return false;
    -        }
    -        objIsArr = true;
    -        objIsObj = false;
    -      }
    -      if (isSameTag && !objIsObj) {
    -        stack || (stack = new Stack());
    -        return objIsArr || isTypedArray(object) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
    -      }
    -      if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
    -        var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
    -          othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
    -        if (objIsWrapped || othIsWrapped) {
    -          var objUnwrapped = objIsWrapped ? object.value() : object,
    -            othUnwrapped = othIsWrapped ? other.value() : other;
    -          stack || (stack = new Stack());
    -          return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
    -        }
    -      }
    -      if (!isSameTag) {
    -        return false;
    -      }
    -      stack || (stack = new Stack());
    -      return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
    -    }
    -
    -    /**
    -     * The base implementation of `_.isMap` without Node.js optimizations.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is a map, else `false`.
    -     */
    -    function baseIsMap(value) {
    -      return isObjectLike(value) && getTag(value) == mapTag;
    -    }
    -
    -    /**
    -     * The base implementation of `_.isMatch` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Object} object The object to inspect.
    -     * @param {Object} source The object of property values to match.
    -     * @param {Array} matchData The property names, values, and compare flags to match.
    -     * @param {Function} [customizer] The function to customize comparisons.
    -     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
    -     */
    -    function baseIsMatch(object, source, matchData, customizer) {
    -      var index = matchData.length,
    -        length = index,
    -        noCustomizer = !customizer;
    -      if (object == null) {
    -        return !length;
    -      }
    -      object = Object(object);
    -      while (index--) {
    -        var data = matchData[index];
    -        if (noCustomizer && data[2] ? data[1] !== object[data[0]] : !(data[0] in object)) {
    -          return false;
    -        }
    -      }
    -      while (++index < length) {
    -        data = matchData[index];
    -        var key = data[0],
    -          objValue = object[key],
    -          srcValue = data[1];
    -        if (noCustomizer && data[2]) {
    -          if (objValue === undefined && !(key in object)) {
    -            return false;
    -          }
    -        } else {
    -          var stack = new Stack();
    -          if (customizer) {
    -            var result = customizer(objValue, srcValue, key, object, source, stack);
    -          }
    -          if (!(result === undefined ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) : result)) {
    -            return false;
    -          }
    -        }
    -      }
    -      return true;
    -    }
    -
    -    /**
    -     * The base implementation of `_.isNative` without bad shim checks.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is a native function,
    -     *  else `false`.
    -     */
    -    function baseIsNative(value) {
    -      if (!isObject(value) || isMasked(value)) {
    -        return false;
    -      }
    -      var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
    -      return pattern.test(toSource(value));
    -    }
    -
    -    /**
    -     * The base implementation of `_.isRegExp` without Node.js optimizations.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
    -     */
    -    function baseIsRegExp(value) {
    -      return isObjectLike(value) && baseGetTag(value) == regexpTag;
    -    }
    -
    -    /**
    -     * The base implementation of `_.isSet` without Node.js optimizations.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is a set, else `false`.
    -     */
    -    function baseIsSet(value) {
    -      return isObjectLike(value) && getTag(value) == setTag;
    -    }
    -
    -    /**
    -     * The base implementation of `_.isTypedArray` without Node.js optimizations.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
    -     */
    -    function baseIsTypedArray(value) {
    -      return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
    -    }
    -
    -    /**
    -     * The base implementation of `_.iteratee`.
    -     *
    -     * @private
    -     * @param {*} [value=_.identity] The value to convert to an iteratee.
    -     * @returns {Function} Returns the iteratee.
    -     */
    -    function baseIteratee(value) {
    -      // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
    -      // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
    -      if (typeof value == 'function') {
    -        return value;
    -      }
    -      if (value == null) {
    -        return identity;
    -      }
    -      if (typeof value == 'object') {
    -        return isArray(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value);
    -      }
    -      return property(value);
    -    }
    -
    -    /**
    -     * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @returns {Array} Returns the array of property names.
    -     */
    -    function baseKeys(object) {
    -      if (!isPrototype(object)) {
    -        return nativeKeys(object);
    -      }
    -      var result = [];
    -      for (var key in Object(object)) {
    -        if (hasOwnProperty.call(object, key) && key != 'constructor') {
    -          result.push(key);
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @returns {Array} Returns the array of property names.
    -     */
    -    function baseKeysIn(object) {
    -      if (!isObject(object)) {
    -        return nativeKeysIn(object);
    -      }
    -      var isProto = isPrototype(object),
    -        result = [];
    -      for (var key in object) {
    -        if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
    -          result.push(key);
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.lt` which doesn't coerce arguments.
    -     *
    -     * @private
    -     * @param {*} value The value to compare.
    -     * @param {*} other The other value to compare.
    -     * @returns {boolean} Returns `true` if `value` is less than `other`,
    -     *  else `false`.
    -     */
    -    function baseLt(value, other) {
    -      return value < other;
    -    }
    -
    -    /**
    -     * The base implementation of `_.map` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} iteratee The function invoked per iteration.
    -     * @returns {Array} Returns the new mapped array.
    -     */
    -    function baseMap(collection, iteratee) {
    -      var index = -1,
    -        result = isArrayLike(collection) ? Array(collection.length) : [];
    -      baseEach(collection, function (value, key, collection) {
    -        result[++index] = iteratee(value, key, collection);
    -      });
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.matches` which doesn't clone `source`.
    -     *
    -     * @private
    -     * @param {Object} source The object of property values to match.
    -     * @returns {Function} Returns the new spec function.
    -     */
    -    function baseMatches(source) {
    -      var matchData = getMatchData(source);
    -      if (matchData.length == 1 && matchData[0][2]) {
    -        return matchesStrictComparable(matchData[0][0], matchData[0][1]);
    -      }
    -      return function (object) {
    -        return object === source || baseIsMatch(object, source, matchData);
    -      };
    -    }
    -
    -    /**
    -     * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
    -     *
    -     * @private
    -     * @param {string} path The path of the property to get.
    -     * @param {*} srcValue The value to match.
    -     * @returns {Function} Returns the new spec function.
    -     */
    -    function baseMatchesProperty(path, srcValue) {
    -      if (isKey(path) && isStrictComparable(srcValue)) {
    -        return matchesStrictComparable(toKey(path), srcValue);
    -      }
    -      return function (object) {
    -        var objValue = get(object, path);
    -        return objValue === undefined && objValue === srcValue ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
    -      };
    -    }
    -
    -    /**
    -     * The base implementation of `_.merge` without support for multiple sources.
    -     *
    -     * @private
    -     * @param {Object} object The destination object.
    -     * @param {Object} source The source object.
    -     * @param {number} srcIndex The index of `source`.
    -     * @param {Function} [customizer] The function to customize merged values.
    -     * @param {Object} [stack] Tracks traversed source values and their merged
    -     *  counterparts.
    -     */
    -    function baseMerge(object, source, srcIndex, customizer, stack) {
    -      if (object === source) {
    -        return;
    -      }
    -      baseFor(source, function (srcValue, key) {
    -        stack || (stack = new Stack());
    -        if (isObject(srcValue)) {
    -          baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
    -        } else {
    -          var newValue = customizer ? customizer(safeGet(object, key), srcValue, key + '', object, source, stack) : undefined;
    -          if (newValue === undefined) {
    -            newValue = srcValue;
    -          }
    -          assignMergeValue(object, key, newValue);
    -        }
    -      }, keysIn);
    -    }
    -
    -    /**
    -     * A specialized version of `baseMerge` for arrays and objects which performs
    -     * deep merges and tracks traversed objects enabling objects with circular
    -     * references to be merged.
    -     *
    -     * @private
    -     * @param {Object} object The destination object.
    -     * @param {Object} source The source object.
    -     * @param {string} key The key of the value to merge.
    -     * @param {number} srcIndex The index of `source`.
    -     * @param {Function} mergeFunc The function to merge values.
    -     * @param {Function} [customizer] The function to customize assigned values.
    -     * @param {Object} [stack] Tracks traversed source values and their merged
    -     *  counterparts.
    -     */
    -    function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
    -      var objValue = safeGet(object, key),
    -        srcValue = safeGet(source, key),
    -        stacked = stack.get(srcValue);
    -      if (stacked) {
    -        assignMergeValue(object, key, stacked);
    -        return;
    -      }
    -      var newValue = customizer ? customizer(objValue, srcValue, key + '', object, source, stack) : undefined;
    -      var isCommon = newValue === undefined;
    -      if (isCommon) {
    -        var isArr = isArray(srcValue),
    -          isBuff = !isArr && isBuffer(srcValue),
    -          isTyped = !isArr && !isBuff && isTypedArray(srcValue);
    -        newValue = srcValue;
    -        if (isArr || isBuff || isTyped) {
    -          if (isArray(objValue)) {
    -            newValue = objValue;
    -          } else if (isArrayLikeObject(objValue)) {
    -            newValue = copyArray(objValue);
    -          } else if (isBuff) {
    -            isCommon = false;
    -            newValue = cloneBuffer(srcValue, true);
    -          } else if (isTyped) {
    -            isCommon = false;
    -            newValue = cloneTypedArray(srcValue, true);
    -          } else {
    -            newValue = [];
    -          }
    -        } else if (isPlainObject(srcValue) || isArguments(srcValue)) {
    -          newValue = objValue;
    -          if (isArguments(objValue)) {
    -            newValue = toPlainObject(objValue);
    -          } else if (!isObject(objValue) || isFunction(objValue)) {
    -            newValue = initCloneObject(srcValue);
    -          }
    -        } else {
    -          isCommon = false;
    -        }
    -      }
    -      if (isCommon) {
    -        // Recursively merge objects and arrays (susceptible to call stack limits).
    -        stack.set(srcValue, newValue);
    -        mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
    -        stack['delete'](srcValue);
    -      }
    -      assignMergeValue(object, key, newValue);
    -    }
    -
    -    /**
    -     * The base implementation of `_.nth` which doesn't coerce arguments.
    -     *
    -     * @private
    -     * @param {Array} array The array to query.
    -     * @param {number} n The index of the element to return.
    -     * @returns {*} Returns the nth element of `array`.
    -     */
    -    function baseNth(array, n) {
    -      var length = array.length;
    -      if (!length) {
    -        return;
    -      }
    -      n += n < 0 ? length : 0;
    -      return isIndex(n, length) ? array[n] : undefined;
    -    }
    -
    -    /**
    -     * The base implementation of `_.orderBy` without param guards.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
    -     * @param {string[]} orders The sort orders of `iteratees`.
    -     * @returns {Array} Returns the new sorted array.
    -     */
    -    function baseOrderBy(collection, iteratees, orders) {
    -      if (iteratees.length) {
    -        iteratees = arrayMap(iteratees, function (iteratee) {
    -          if (isArray(iteratee)) {
    -            return function (value) {
    -              return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee);
    -            };
    -          }
    -          return iteratee;
    -        });
    -      } else {
    -        iteratees = [identity];
    -      }
    -      var index = -1;
    -      iteratees = arrayMap(iteratees, baseUnary(getIteratee()));
    -      var result = baseMap(collection, function (value, key, collection) {
    -        var criteria = arrayMap(iteratees, function (iteratee) {
    -          return iteratee(value);
    -        });
    -        return {
    -          'criteria': criteria,
    -          'index': ++index,
    -          'value': value
    -        };
    -      });
    -      return baseSortBy(result, function (object, other) {
    -        return compareMultiple(object, other, orders);
    -      });
    -    }
    -
    -    /**
    -     * The base implementation of `_.pick` without support for individual
    -     * property identifiers.
    -     *
    -     * @private
    -     * @param {Object} object The source object.
    -     * @param {string[]} paths The property paths to pick.
    -     * @returns {Object} Returns the new object.
    -     */
    -    function basePick(object, paths) {
    -      return basePickBy(object, paths, function (value, path) {
    -        return hasIn(object, path);
    -      });
    -    }
    -
    -    /**
    -     * The base implementation of  `_.pickBy` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Object} object The source object.
    -     * @param {string[]} paths The property paths to pick.
    -     * @param {Function} predicate The function invoked per property.
    -     * @returns {Object} Returns the new object.
    -     */
    -    function basePickBy(object, paths, predicate) {
    -      var index = -1,
    -        length = paths.length,
    -        result = {};
    -      while (++index < length) {
    -        var path = paths[index],
    -          value = baseGet(object, path);
    -        if (predicate(value, path)) {
    -          baseSet(result, castPath(path, object), value);
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * A specialized version of `baseProperty` which supports deep paths.
    -     *
    -     * @private
    -     * @param {Array|string} path The path of the property to get.
    -     * @returns {Function} Returns the new accessor function.
    -     */
    -    function basePropertyDeep(path) {
    -      return function (object) {
    -        return baseGet(object, path);
    -      };
    -    }
    -
    -    /**
    -     * The base implementation of `_.pullAllBy` without support for iteratee
    -     * shorthands.
    -     *
    -     * @private
    -     * @param {Array} array The array to modify.
    -     * @param {Array} values The values to remove.
    -     * @param {Function} [iteratee] The iteratee invoked per element.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns `array`.
    -     */
    -    function basePullAll(array, values, iteratee, comparator) {
    -      var indexOf = comparator ? baseIndexOfWith : baseIndexOf,
    -        index = -1,
    -        length = values.length,
    -        seen = array;
    -      if (array === values) {
    -        values = copyArray(values);
    -      }
    -      if (iteratee) {
    -        seen = arrayMap(array, baseUnary(iteratee));
    -      }
    -      while (++index < length) {
    -        var fromIndex = 0,
    -          value = values[index],
    -          computed = iteratee ? iteratee(value) : value;
    -        while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
    -          if (seen !== array) {
    -            splice.call(seen, fromIndex, 1);
    -          }
    -          splice.call(array, fromIndex, 1);
    -        }
    -      }
    -      return array;
    -    }
    -
    -    /**
    -     * The base implementation of `_.pullAt` without support for individual
    -     * indexes or capturing the removed elements.
    -     *
    -     * @private
    -     * @param {Array} array The array to modify.
    -     * @param {number[]} indexes The indexes of elements to remove.
    -     * @returns {Array} Returns `array`.
    -     */
    -    function basePullAt(array, indexes) {
    -      var length = array ? indexes.length : 0,
    -        lastIndex = length - 1;
    -      while (length--) {
    -        var index = indexes[length];
    -        if (length == lastIndex || index !== previous) {
    -          var previous = index;
    -          if (isIndex(index)) {
    -            splice.call(array, index, 1);
    -          } else {
    -            baseUnset(array, index);
    -          }
    -        }
    -      }
    -      return array;
    -    }
    -
    -    /**
    -     * The base implementation of `_.random` without support for returning
    -     * floating-point numbers.
    -     *
    -     * @private
    -     * @param {number} lower The lower bound.
    -     * @param {number} upper The upper bound.
    -     * @returns {number} Returns the random number.
    -     */
    -    function baseRandom(lower, upper) {
    -      return lower + nativeFloor(nativeRandom() * (upper - lower + 1));
    -    }
    -
    -    /**
    -     * The base implementation of `_.range` and `_.rangeRight` which doesn't
    -     * coerce arguments.
    -     *
    -     * @private
    -     * @param {number} start The start of the range.
    -     * @param {number} end The end of the range.
    -     * @param {number} step The value to increment or decrement by.
    -     * @param {boolean} [fromRight] Specify iterating from right to left.
    -     * @returns {Array} Returns the range of numbers.
    -     */
    -    function baseRange(start, end, step, fromRight) {
    -      var index = -1,
    -        length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
    -        result = Array(length);
    -      while (length--) {
    -        result[fromRight ? length : ++index] = start;
    -        start += step;
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.repeat` which doesn't coerce arguments.
    -     *
    -     * @private
    -     * @param {string} string The string to repeat.
    -     * @param {number} n The number of times to repeat the string.
    -     * @returns {string} Returns the repeated string.
    -     */
    -    function baseRepeat(string, n) {
    -      var result = '';
    -      if (!string || n < 1 || n > MAX_SAFE_INTEGER) {
    -        return result;
    -      }
    -      // Leverage the exponentiation by squaring algorithm for a faster repeat.
    -      // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
    -      do {
    -        if (n % 2) {
    -          result += string;
    -        }
    -        n = nativeFloor(n / 2);
    -        if (n) {
    -          string += string;
    -        }
    -      } while (n);
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.rest` which doesn't validate or coerce arguments.
    -     *
    -     * @private
    -     * @param {Function} func The function to apply a rest parameter to.
    -     * @param {number} [start=func.length-1] The start position of the rest parameter.
    -     * @returns {Function} Returns the new function.
    -     */
    -    function baseRest(func, start) {
    -      return setToString(overRest(func, start, identity), func + '');
    -    }
    -
    -    /**
    -     * The base implementation of `_.sample`.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to sample.
    -     * @returns {*} Returns the random element.
    -     */
    -    function baseSample(collection) {
    -      return arraySample(values(collection));
    -    }
    -
    -    /**
    -     * The base implementation of `_.sampleSize` without param guards.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to sample.
    -     * @param {number} n The number of elements to sample.
    -     * @returns {Array} Returns the random elements.
    -     */
    -    function baseSampleSize(collection, n) {
    -      var array = values(collection);
    -      return shuffleSelf(array, baseClamp(n, 0, array.length));
    -    }
    -
    -    /**
    -     * The base implementation of `_.set`.
    -     *
    -     * @private
    -     * @param {Object} object The object to modify.
    -     * @param {Array|string} path The path of the property to set.
    -     * @param {*} value The value to set.
    -     * @param {Function} [customizer] The function to customize path creation.
    -     * @returns {Object} Returns `object`.
    -     */
    -    function baseSet(object, path, value, customizer) {
    -      if (!isObject(object)) {
    -        return object;
    -      }
    -      path = castPath(path, object);
    -      var index = -1,
    -        length = path.length,
    -        lastIndex = length - 1,
    -        nested = object;
    -      while (nested != null && ++index < length) {
    -        var key = toKey(path[index]),
    -          newValue = value;
    -        if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
    -          return object;
    -        }
    -        if (index != lastIndex) {
    -          var objValue = nested[key];
    -          newValue = customizer ? customizer(objValue, key, nested) : undefined;
    -          if (newValue === undefined) {
    -            newValue = isObject(objValue) ? objValue : isIndex(path[index + 1]) ? [] : {};
    -          }
    -        }
    -        assignValue(nested, key, newValue);
    -        nested = nested[key];
    -      }
    -      return object;
    -    }
    -
    -    /**
    -     * The base implementation of `setData` without support for hot loop shorting.
    -     *
    -     * @private
    -     * @param {Function} func The function to associate metadata with.
    -     * @param {*} data The metadata.
    -     * @returns {Function} Returns `func`.
    -     */
    -    var baseSetData = !metaMap ? identity : function (func, data) {
    -      metaMap.set(func, data);
    -      return func;
    -    };
    -
    -    /**
    -     * The base implementation of `setToString` without support for hot loop shorting.
    -     *
    -     * @private
    -     * @param {Function} func The function to modify.
    -     * @param {Function} string The `toString` result.
    -     * @returns {Function} Returns `func`.
    -     */
    -    var baseSetToString = !defineProperty ? identity : function (func, string) {
    -      return defineProperty(func, 'toString', {
    -        'configurable': true,
    -        'enumerable': false,
    -        'value': constant(string),
    -        'writable': true
    -      });
    -    };
    -
    -    /**
    -     * The base implementation of `_.shuffle`.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to shuffle.
    -     * @returns {Array} Returns the new shuffled array.
    -     */
    -    function baseShuffle(collection) {
    -      return shuffleSelf(values(collection));
    -    }
    -
    -    /**
    -     * The base implementation of `_.slice` without an iteratee call guard.
    -     *
    -     * @private
    -     * @param {Array} array The array to slice.
    -     * @param {number} [start=0] The start position.
    -     * @param {number} [end=array.length] The end position.
    -     * @returns {Array} Returns the slice of `array`.
    -     */
    -    function baseSlice(array, start, end) {
    -      var index = -1,
    -        length = array.length;
    -      if (start < 0) {
    -        start = -start > length ? 0 : length + start;
    -      }
    -      end = end > length ? length : end;
    -      if (end < 0) {
    -        end += length;
    -      }
    -      length = start > end ? 0 : end - start >>> 0;
    -      start >>>= 0;
    -      var result = Array(length);
    -      while (++index < length) {
    -        result[index] = array[index + start];
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.some` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} predicate The function invoked per iteration.
    -     * @returns {boolean} Returns `true` if any element passes the predicate check,
    -     *  else `false`.
    -     */
    -    function baseSome(collection, predicate) {
    -      var result;
    -      baseEach(collection, function (value, index, collection) {
    -        result = predicate(value, index, collection);
    -        return !result;
    -      });
    -      return !!result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which
    -     * performs a binary search of `array` to determine the index at which `value`
    -     * should be inserted into `array` in order to maintain its sort order.
    -     *
    -     * @private
    -     * @param {Array} array The sorted array to inspect.
    -     * @param {*} value The value to evaluate.
    -     * @param {boolean} [retHighest] Specify returning the highest qualified index.
    -     * @returns {number} Returns the index at which `value` should be inserted
    -     *  into `array`.
    -     */
    -    function baseSortedIndex(array, value, retHighest) {
    -      var low = 0,
    -        high = array == null ? low : array.length;
    -      if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
    -        while (low < high) {
    -          var mid = low + high >>> 1,
    -            computed = array[mid];
    -          if (computed !== null && !isSymbol(computed) && (retHighest ? computed <= value : computed < value)) {
    -            low = mid + 1;
    -          } else {
    -            high = mid;
    -          }
    -        }
    -        return high;
    -      }
    -      return baseSortedIndexBy(array, value, identity, retHighest);
    -    }
    -
    -    /**
    -     * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`
    -     * which invokes `iteratee` for `value` and each element of `array` to compute
    -     * their sort ranking. The iteratee is invoked with one argument; (value).
    -     *
    -     * @private
    -     * @param {Array} array The sorted array to inspect.
    -     * @param {*} value The value to evaluate.
    -     * @param {Function} iteratee The iteratee invoked per element.
    -     * @param {boolean} [retHighest] Specify returning the highest qualified index.
    -     * @returns {number} Returns the index at which `value` should be inserted
    -     *  into `array`.
    -     */
    -    function baseSortedIndexBy(array, value, iteratee, retHighest) {
    -      var low = 0,
    -        high = array == null ? 0 : array.length;
    -      if (high === 0) {
    -        return 0;
    -      }
    -      value = iteratee(value);
    -      var valIsNaN = value !== value,
    -        valIsNull = value === null,
    -        valIsSymbol = isSymbol(value),
    -        valIsUndefined = value === undefined;
    -      while (low < high) {
    -        var mid = nativeFloor((low + high) / 2),
    -          computed = iteratee(array[mid]),
    -          othIsDefined = computed !== undefined,
    -          othIsNull = computed === null,
    -          othIsReflexive = computed === computed,
    -          othIsSymbol = isSymbol(computed);
    -        if (valIsNaN) {
    -          var setLow = retHighest || othIsReflexive;
    -        } else if (valIsUndefined) {
    -          setLow = othIsReflexive && (retHighest || othIsDefined);
    -        } else if (valIsNull) {
    -          setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);
    -        } else if (valIsSymbol) {
    -          setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);
    -        } else if (othIsNull || othIsSymbol) {
    -          setLow = false;
    -        } else {
    -          setLow = retHighest ? computed <= value : computed < value;
    -        }
    -        if (setLow) {
    -          low = mid + 1;
    -        } else {
    -          high = mid;
    -        }
    -      }
    -      return nativeMin(high, MAX_ARRAY_INDEX);
    -    }
    -
    -    /**
    -     * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without
    -     * support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array} array The array to inspect.
    -     * @param {Function} [iteratee] The iteratee invoked per element.
    -     * @returns {Array} Returns the new duplicate free array.
    -     */
    -    function baseSortedUniq(array, iteratee) {
    -      var index = -1,
    -        length = array.length,
    -        resIndex = 0,
    -        result = [];
    -      while (++index < length) {
    -        var value = array[index],
    -          computed = iteratee ? iteratee(value) : value;
    -        if (!index || !eq(computed, seen)) {
    -          var seen = computed;
    -          result[resIndex++] = value === 0 ? 0 : value;
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.toNumber` which doesn't ensure correct
    -     * conversions of binary, hexadecimal, or octal string values.
    -     *
    -     * @private
    -     * @param {*} value The value to process.
    -     * @returns {number} Returns the number.
    -     */
    -    function baseToNumber(value) {
    -      if (typeof value == 'number') {
    -        return value;
    -      }
    -      if (isSymbol(value)) {
    -        return NAN;
    -      }
    -      return +value;
    -    }
    -
    -    /**
    -     * The base implementation of `_.toString` which doesn't convert nullish
    -     * values to empty strings.
    -     *
    -     * @private
    -     * @param {*} value The value to process.
    -     * @returns {string} Returns the string.
    -     */
    -    function baseToString(value) {
    -      // Exit early for strings to avoid a performance hit in some environments.
    -      if (typeof value == 'string') {
    -        return value;
    -      }
    -      if (isArray(value)) {
    -        // Recursively convert values (susceptible to call stack limits).
    -        return arrayMap(value, baseToString) + '';
    -      }
    -      if (isSymbol(value)) {
    -        return symbolToString ? symbolToString.call(value) : '';
    -      }
    -      var result = value + '';
    -      return result == '0' && 1 / value == -INFINITY ? '-0' : result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.uniqBy` without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array} array The array to inspect.
    -     * @param {Function} [iteratee] The iteratee invoked per element.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns the new duplicate free array.
    -     */
    -    function baseUniq(array, iteratee, comparator) {
    -      var index = -1,
    -        includes = arrayIncludes,
    -        length = array.length,
    -        isCommon = true,
    -        result = [],
    -        seen = result;
    -      if (comparator) {
    -        isCommon = false;
    -        includes = arrayIncludesWith;
    -      } else if (length >= LARGE_ARRAY_SIZE) {
    -        var set = iteratee ? null : createSet(array);
    -        if (set) {
    -          return setToArray(set);
    -        }
    -        isCommon = false;
    -        includes = cacheHas;
    -        seen = new SetCache();
    -      } else {
    -        seen = iteratee ? [] : result;
    -      }
    -      outer: while (++index < length) {
    -        var value = array[index],
    -          computed = iteratee ? iteratee(value) : value;
    -        value = comparator || value !== 0 ? value : 0;
    -        if (isCommon && computed === computed) {
    -          var seenIndex = seen.length;
    -          while (seenIndex--) {
    -            if (seen[seenIndex] === computed) {
    -              continue outer;
    -            }
    -          }
    -          if (iteratee) {
    -            seen.push(computed);
    -          }
    -          result.push(value);
    -        } else if (!includes(seen, computed, comparator)) {
    -          if (seen !== result) {
    -            seen.push(computed);
    -          }
    -          result.push(value);
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * The base implementation of `_.unset`.
    -     *
    -     * @private
    -     * @param {Object} object The object to modify.
    -     * @param {Array|string} path The property path to unset.
    -     * @returns {boolean} Returns `true` if the property is deleted, else `false`.
    -     */
    -    function baseUnset(object, path) {
    -      path = castPath(path, object);
    -      object = parent(object, path);
    -      return object == null || delete object[toKey(last(path))];
    -    }
    -
    -    /**
    -     * The base implementation of `_.update`.
    -     *
    -     * @private
    -     * @param {Object} object The object to modify.
    -     * @param {Array|string} path The path of the property to update.
    -     * @param {Function} updater The function to produce the updated value.
    -     * @param {Function} [customizer] The function to customize path creation.
    -     * @returns {Object} Returns `object`.
    -     */
    -    function baseUpdate(object, path, updater, customizer) {
    -      return baseSet(object, path, updater(baseGet(object, path)), customizer);
    -    }
    -
    -    /**
    -     * The base implementation of methods like `_.dropWhile` and `_.takeWhile`
    -     * without support for iteratee shorthands.
    -     *
    -     * @private
    -     * @param {Array} array The array to query.
    -     * @param {Function} predicate The function invoked per iteration.
    -     * @param {boolean} [isDrop] Specify dropping elements instead of taking them.
    -     * @param {boolean} [fromRight] Specify iterating from right to left.
    -     * @returns {Array} Returns the slice of `array`.
    -     */
    -    function baseWhile(array, predicate, isDrop, fromRight) {
    -      var length = array.length,
    -        index = fromRight ? length : -1;
    -      while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {}
    -      return isDrop ? baseSlice(array, fromRight ? 0 : index, fromRight ? index + 1 : length) : baseSlice(array, fromRight ? index + 1 : 0, fromRight ? length : index);
    -    }
    -
    -    /**
    -     * The base implementation of `wrapperValue` which returns the result of
    -     * performing a sequence of actions on the unwrapped `value`, where each
    -     * successive action is supplied the return value of the previous.
    -     *
    -     * @private
    -     * @param {*} value The unwrapped value.
    -     * @param {Array} actions Actions to perform to resolve the unwrapped value.
    -     * @returns {*} Returns the resolved value.
    -     */
    -    function baseWrapperValue(value, actions) {
    -      var result = value;
    -      if (result instanceof LazyWrapper) {
    -        result = result.value();
    -      }
    -      return arrayReduce(actions, function (result, action) {
    -        return action.func.apply(action.thisArg, arrayPush([result], action.args));
    -      }, result);
    -    }
    -
    -    /**
    -     * The base implementation of methods like `_.xor`, without support for
    -     * iteratee shorthands, that accepts an array of arrays to inspect.
    -     *
    -     * @private
    -     * @param {Array} arrays The arrays to inspect.
    -     * @param {Function} [iteratee] The iteratee invoked per element.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns the new array of values.
    -     */
    -    function baseXor(arrays, iteratee, comparator) {
    -      var length = arrays.length;
    -      if (length < 2) {
    -        return length ? baseUniq(arrays[0]) : [];
    -      }
    -      var index = -1,
    -        result = Array(length);
    -      while (++index < length) {
    -        var array = arrays[index],
    -          othIndex = -1;
    -        while (++othIndex < length) {
    -          if (othIndex != index) {
    -            result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator);
    -          }
    -        }
    -      }
    -      return baseUniq(baseFlatten(result, 1), iteratee, comparator);
    -    }
    -
    -    /**
    -     * This base implementation of `_.zipObject` which assigns values using `assignFunc`.
    -     *
    -     * @private
    -     * @param {Array} props The property identifiers.
    -     * @param {Array} values The property values.
    -     * @param {Function} assignFunc The function to assign values.
    -     * @returns {Object} Returns the new object.
    -     */
    -    function baseZipObject(props, values, assignFunc) {
    -      var index = -1,
    -        length = props.length,
    -        valsLength = values.length,
    -        result = {};
    -      while (++index < length) {
    -        var value = index < valsLength ? values[index] : undefined;
    -        assignFunc(result, props[index], value);
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Casts `value` to an empty array if it's not an array like object.
    -     *
    -     * @private
    -     * @param {*} value The value to inspect.
    -     * @returns {Array|Object} Returns the cast array-like object.
    -     */
    -    function castArrayLikeObject(value) {
    -      return isArrayLikeObject(value) ? value : [];
    -    }
    -
    -    /**
    -     * Casts `value` to `identity` if it's not a function.
    -     *
    -     * @private
    -     * @param {*} value The value to inspect.
    -     * @returns {Function} Returns cast function.
    -     */
    -    function castFunction(value) {
    -      return typeof value == 'function' ? value : identity;
    -    }
    -
    -    /**
    -     * Casts `value` to a path array if it's not one.
    -     *
    -     * @private
    -     * @param {*} value The value to inspect.
    -     * @param {Object} [object] The object to query keys on.
    -     * @returns {Array} Returns the cast property path array.
    -     */
    -    function castPath(value, object) {
    -      if (isArray(value)) {
    -        return value;
    -      }
    -      return isKey(value, object) ? [value] : stringToPath(toString(value));
    -    }
    -
    -    /**
    -     * A `baseRest` alias which can be replaced with `identity` by module
    -     * replacement plugins.
    -     *
    -     * @private
    -     * @type {Function}
    -     * @param {Function} func The function to apply a rest parameter to.
    -     * @returns {Function} Returns the new function.
    -     */
    -    var castRest = baseRest;
    -
    -    /**
    -     * Casts `array` to a slice if it's needed.
    -     *
    -     * @private
    -     * @param {Array} array The array to inspect.
    -     * @param {number} start The start position.
    -     * @param {number} [end=array.length] The end position.
    -     * @returns {Array} Returns the cast slice.
    -     */
    -    function castSlice(array, start, end) {
    -      var length = array.length;
    -      end = end === undefined ? length : end;
    -      return !start && end >= length ? array : baseSlice(array, start, end);
    -    }
    -
    -    /**
    -     * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout).
    -     *
    -     * @private
    -     * @param {number|Object} id The timer id or timeout object of the timer to clear.
    -     */
    -    var clearTimeout = ctxClearTimeout || function (id) {
    -      return root.clearTimeout(id);
    -    };
    -
    -    /**
    -     * Creates a clone of  `buffer`.
    -     *
    -     * @private
    -     * @param {Buffer} buffer The buffer to clone.
    -     * @param {boolean} [isDeep] Specify a deep clone.
    -     * @returns {Buffer} Returns the cloned buffer.
    -     */
    -    function cloneBuffer(buffer, isDeep) {
    -      if (isDeep) {
    -        return buffer.slice();
    -      }
    -      var length = buffer.length,
    -        result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);
    -      buffer.copy(result);
    -      return result;
    -    }
    -
    -    /**
    -     * Creates a clone of `arrayBuffer`.
    -     *
    -     * @private
    -     * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
    -     * @returns {ArrayBuffer} Returns the cloned array buffer.
    -     */
    -    function cloneArrayBuffer(arrayBuffer) {
    -      var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
    -      new Uint8Array(result).set(new Uint8Array(arrayBuffer));
    -      return result;
    -    }
    -
    -    /**
    -     * Creates a clone of `dataView`.
    -     *
    -     * @private
    -     * @param {Object} dataView The data view to clone.
    -     * @param {boolean} [isDeep] Specify a deep clone.
    -     * @returns {Object} Returns the cloned data view.
    -     */
    -    function cloneDataView(dataView, isDeep) {
    -      var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
    -      return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
    -    }
    -
    -    /**
    -     * Creates a clone of `regexp`.
    -     *
    -     * @private
    -     * @param {Object} regexp The regexp to clone.
    -     * @returns {Object} Returns the cloned regexp.
    -     */
    -    function cloneRegExp(regexp) {
    -      var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
    -      result.lastIndex = regexp.lastIndex;
    -      return result;
    -    }
    -
    -    /**
    -     * Creates a clone of the `symbol` object.
    -     *
    -     * @private
    -     * @param {Object} symbol The symbol object to clone.
    -     * @returns {Object} Returns the cloned symbol object.
    -     */
    -    function cloneSymbol(symbol) {
    -      return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
    -    }
    -
    -    /**
    -     * Creates a clone of `typedArray`.
    -     *
    -     * @private
    -     * @param {Object} typedArray The typed array to clone.
    -     * @param {boolean} [isDeep] Specify a deep clone.
    -     * @returns {Object} Returns the cloned typed array.
    -     */
    -    function cloneTypedArray(typedArray, isDeep) {
    -      var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
    -      return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
    -    }
    -
    -    /**
    -     * Compares values to sort them in ascending order.
    -     *
    -     * @private
    -     * @param {*} value The value to compare.
    -     * @param {*} other The other value to compare.
    -     * @returns {number} Returns the sort order indicator for `value`.
    -     */
    -    function compareAscending(value, other) {
    -      if (value !== other) {
    -        var valIsDefined = value !== undefined,
    -          valIsNull = value === null,
    -          valIsReflexive = value === value,
    -          valIsSymbol = isSymbol(value);
    -        var othIsDefined = other !== undefined,
    -          othIsNull = other === null,
    -          othIsReflexive = other === other,
    -          othIsSymbol = isSymbol(other);
    -        if (!othIsNull && !othIsSymbol && !valIsSymbol && value > other || valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol || valIsNull && othIsDefined && othIsReflexive || !valIsDefined && othIsReflexive || !valIsReflexive) {
    -          return 1;
    -        }
    -        if (!valIsNull && !valIsSymbol && !othIsSymbol && value < other || othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol || othIsNull && valIsDefined && valIsReflexive || !othIsDefined && valIsReflexive || !othIsReflexive) {
    -          return -1;
    -        }
    -      }
    -      return 0;
    -    }
    -
    -    /**
    -     * Used by `_.orderBy` to compare multiple properties of a value to another
    -     * and stable sort them.
    -     *
    -     * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,
    -     * specify an order of "desc" for descending or "asc" for ascending sort order
    -     * of corresponding values.
    -     *
    -     * @private
    -     * @param {Object} object The object to compare.
    -     * @param {Object} other The other object to compare.
    -     * @param {boolean[]|string[]} orders The order to sort by for each property.
    -     * @returns {number} Returns the sort order indicator for `object`.
    -     */
    -    function compareMultiple(object, other, orders) {
    -      var index = -1,
    -        objCriteria = object.criteria,
    -        othCriteria = other.criteria,
    -        length = objCriteria.length,
    -        ordersLength = orders.length;
    -      while (++index < length) {
    -        var result = compareAscending(objCriteria[index], othCriteria[index]);
    -        if (result) {
    -          if (index >= ordersLength) {
    -            return result;
    -          }
    -          var order = orders[index];
    -          return result * (order == 'desc' ? -1 : 1);
    -        }
    -      }
    -      // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
    -      // that causes it, under certain circumstances, to provide the same value for
    -      // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
    -      // for more details.
    -      //
    -      // This also ensures a stable sort in V8 and other engines.
    -      // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
    -      return object.index - other.index;
    -    }
    -
    -    /**
    -     * Creates an array that is the composition of partially applied arguments,
    -     * placeholders, and provided arguments into a single array of arguments.
    -     *
    -     * @private
    -     * @param {Array} args The provided arguments.
    -     * @param {Array} partials The arguments to prepend to those provided.
    -     * @param {Array} holders The `partials` placeholder indexes.
    -     * @params {boolean} [isCurried] Specify composing for a curried function.
    -     * @returns {Array} Returns the new array of composed arguments.
    -     */
    -    function composeArgs(args, partials, holders, isCurried) {
    -      var argsIndex = -1,
    -        argsLength = args.length,
    -        holdersLength = holders.length,
    -        leftIndex = -1,
    -        leftLength = partials.length,
    -        rangeLength = nativeMax(argsLength - holdersLength, 0),
    -        result = Array(leftLength + rangeLength),
    -        isUncurried = !isCurried;
    -      while (++leftIndex < leftLength) {
    -        result[leftIndex] = partials[leftIndex];
    -      }
    -      while (++argsIndex < holdersLength) {
    -        if (isUncurried || argsIndex < argsLength) {
    -          result[holders[argsIndex]] = args[argsIndex];
    -        }
    -      }
    -      while (rangeLength--) {
    -        result[leftIndex++] = args[argsIndex++];
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * This function is like `composeArgs` except that the arguments composition
    -     * is tailored for `_.partialRight`.
    -     *
    -     * @private
    -     * @param {Array} args The provided arguments.
    -     * @param {Array} partials The arguments to append to those provided.
    -     * @param {Array} holders The `partials` placeholder indexes.
    -     * @params {boolean} [isCurried] Specify composing for a curried function.
    -     * @returns {Array} Returns the new array of composed arguments.
    -     */
    -    function composeArgsRight(args, partials, holders, isCurried) {
    -      var argsIndex = -1,
    -        argsLength = args.length,
    -        holdersIndex = -1,
    -        holdersLength = holders.length,
    -        rightIndex = -1,
    -        rightLength = partials.length,
    -        rangeLength = nativeMax(argsLength - holdersLength, 0),
    -        result = Array(rangeLength + rightLength),
    -        isUncurried = !isCurried;
    -      while (++argsIndex < rangeLength) {
    -        result[argsIndex] = args[argsIndex];
    -      }
    -      var offset = argsIndex;
    -      while (++rightIndex < rightLength) {
    -        result[offset + rightIndex] = partials[rightIndex];
    -      }
    -      while (++holdersIndex < holdersLength) {
    -        if (isUncurried || argsIndex < argsLength) {
    -          result[offset + holders[holdersIndex]] = args[argsIndex++];
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Copies the values of `source` to `array`.
    -     *
    -     * @private
    -     * @param {Array} source The array to copy values from.
    -     * @param {Array} [array=[]] The array to copy values to.
    -     * @returns {Array} Returns `array`.
    -     */
    -    function copyArray(source, array) {
    -      var index = -1,
    -        length = source.length;
    -      array || (array = Array(length));
    -      while (++index < length) {
    -        array[index] = source[index];
    -      }
    -      return array;
    -    }
    -
    -    /**
    -     * Copies properties of `source` to `object`.
    -     *
    -     * @private
    -     * @param {Object} source The object to copy properties from.
    -     * @param {Array} props The property identifiers to copy.
    -     * @param {Object} [object={}] The object to copy properties to.
    -     * @param {Function} [customizer] The function to customize copied values.
    -     * @returns {Object} Returns `object`.
    -     */
    -    function copyObject(source, props, object, customizer) {
    -      var isNew = !object;
    -      object || (object = {});
    -      var index = -1,
    -        length = props.length;
    -      while (++index < length) {
    -        var key = props[index];
    -        var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined;
    -        if (newValue === undefined) {
    -          newValue = source[key];
    -        }
    -        if (isNew) {
    -          baseAssignValue(object, key, newValue);
    -        } else {
    -          assignValue(object, key, newValue);
    -        }
    -      }
    -      return object;
    -    }
    -
    -    /**
    -     * Copies own symbols of `source` to `object`.
    -     *
    -     * @private
    -     * @param {Object} source The object to copy symbols from.
    -     * @param {Object} [object={}] The object to copy symbols to.
    -     * @returns {Object} Returns `object`.
    -     */
    -    function copySymbols(source, object) {
    -      return copyObject(source, getSymbols(source), object);
    -    }
    -
    -    /**
    -     * Copies own and inherited symbols of `source` to `object`.
    -     *
    -     * @private
    -     * @param {Object} source The object to copy symbols from.
    -     * @param {Object} [object={}] The object to copy symbols to.
    -     * @returns {Object} Returns `object`.
    -     */
    -    function copySymbolsIn(source, object) {
    -      return copyObject(source, getSymbolsIn(source), object);
    -    }
    -
    -    /**
    -     * Creates a function like `_.groupBy`.
    -     *
    -     * @private
    -     * @param {Function} setter The function to set accumulator values.
    -     * @param {Function} [initializer] The accumulator object initializer.
    -     * @returns {Function} Returns the new aggregator function.
    -     */
    -    function createAggregator(setter, initializer) {
    -      return function (collection, iteratee) {
    -        var func = isArray(collection) ? arrayAggregator : baseAggregator,
    -          accumulator = initializer ? initializer() : {};
    -        return func(collection, setter, getIteratee(iteratee, 2), accumulator);
    -      };
    -    }
    -
    -    /**
    -     * Creates a function like `_.assign`.
    -     *
    -     * @private
    -     * @param {Function} assigner The function to assign values.
    -     * @returns {Function} Returns the new assigner function.
    -     */
    -    function createAssigner(assigner) {
    -      return baseRest(function (object, sources) {
    -        var index = -1,
    -          length = sources.length,
    -          customizer = length > 1 ? sources[length - 1] : undefined,
    -          guard = length > 2 ? sources[2] : undefined;
    -        customizer = assigner.length > 3 && typeof customizer == 'function' ? (length--, customizer) : undefined;
    -        if (guard && isIterateeCall(sources[0], sources[1], guard)) {
    -          customizer = length < 3 ? undefined : customizer;
    -          length = 1;
    -        }
    -        object = Object(object);
    -        while (++index < length) {
    -          var source = sources[index];
    -          if (source) {
    -            assigner(object, source, index, customizer);
    -          }
    -        }
    -        return object;
    -      });
    -    }
    -
    -    /**
    -     * Creates a `baseEach` or `baseEachRight` function.
    -     *
    -     * @private
    -     * @param {Function} eachFunc The function to iterate over a collection.
    -     * @param {boolean} [fromRight] Specify iterating from right to left.
    -     * @returns {Function} Returns the new base function.
    -     */
    -    function createBaseEach(eachFunc, fromRight) {
    -      return function (collection, iteratee) {
    -        if (collection == null) {
    -          return collection;
    -        }
    -        if (!isArrayLike(collection)) {
    -          return eachFunc(collection, iteratee);
    -        }
    -        var length = collection.length,
    -          index = fromRight ? length : -1,
    -          iterable = Object(collection);
    -        while (fromRight ? index-- : ++index < length) {
    -          if (iteratee(iterable[index], index, iterable) === false) {
    -            break;
    -          }
    -        }
    -        return collection;
    -      };
    -    }
    -
    -    /**
    -     * Creates a base function for methods like `_.forIn` and `_.forOwn`.
    -     *
    -     * @private
    -     * @param {boolean} [fromRight] Specify iterating from right to left.
    -     * @returns {Function} Returns the new base function.
    -     */
    -    function createBaseFor(fromRight) {
    -      return function (object, iteratee, keysFunc) {
    -        var index = -1,
    -          iterable = Object(object),
    -          props = keysFunc(object),
    -          length = props.length;
    -        while (length--) {
    -          var key = props[fromRight ? length : ++index];
    -          if (iteratee(iterable[key], key, iterable) === false) {
    -            break;
    -          }
    -        }
    -        return object;
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that wraps `func` to invoke it with the optional `this`
    -     * binding of `thisArg`.
    -     *
    -     * @private
    -     * @param {Function} func The function to wrap.
    -     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    -     * @param {*} [thisArg] The `this` binding of `func`.
    -     * @returns {Function} Returns the new wrapped function.
    -     */
    -    function createBind(func, bitmask, thisArg) {
    -      var isBind = bitmask & WRAP_BIND_FLAG,
    -        Ctor = createCtor(func);
    -      function wrapper() {
    -        var fn = this && this !== root && this instanceof wrapper ? Ctor : func;
    -        return fn.apply(isBind ? thisArg : this, arguments);
    -      }
    -      return wrapper;
    -    }
    -
    -    /**
    -     * Creates a function like `_.lowerFirst`.
    -     *
    -     * @private
    -     * @param {string} methodName The name of the `String` case method to use.
    -     * @returns {Function} Returns the new case function.
    -     */
    -    function createCaseFirst(methodName) {
    -      return function (string) {
    -        string = toString(string);
    -        var strSymbols = hasUnicode(string) ? stringToArray(string) : undefined;
    -        var chr = strSymbols ? strSymbols[0] : string.charAt(0);
    -        var trailing = strSymbols ? castSlice(strSymbols, 1).join('') : string.slice(1);
    -        return chr[methodName]() + trailing;
    -      };
    -    }
    -
    -    /**
    -     * Creates a function like `_.camelCase`.
    -     *
    -     * @private
    -     * @param {Function} callback The function to combine each word.
    -     * @returns {Function} Returns the new compounder function.
    -     */
    -    function createCompounder(callback) {
    -      return function (string) {
    -        return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that produces an instance of `Ctor` regardless of
    -     * whether it was invoked as part of a `new` expression or by `call` or `apply`.
    -     *
    -     * @private
    -     * @param {Function} Ctor The constructor to wrap.
    -     * @returns {Function} Returns the new wrapped function.
    -     */
    -    function createCtor(Ctor) {
    -      return function () {
    -        // Use a `switch` statement to work with class constructors. See
    -        // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
    -        // for more details.
    -        var args = arguments;
    -        switch (args.length) {
    -          case 0:
    -            return new Ctor();
    -          case 1:
    -            return new Ctor(args[0]);
    -          case 2:
    -            return new Ctor(args[0], args[1]);
    -          case 3:
    -            return new Ctor(args[0], args[1], args[2]);
    -          case 4:
    -            return new Ctor(args[0], args[1], args[2], args[3]);
    -          case 5:
    -            return new Ctor(args[0], args[1], args[2], args[3], args[4]);
    -          case 6:
    -            return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
    -          case 7:
    -            return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
    -        }
    -        var thisBinding = baseCreate(Ctor.prototype),
    -          result = Ctor.apply(thisBinding, args);
    -
    -        // Mimic the constructor's `return` behavior.
    -        // See https://es5.github.io/#x13.2.2 for more details.
    -        return isObject(result) ? result : thisBinding;
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that wraps `func` to enable currying.
    -     *
    -     * @private
    -     * @param {Function} func The function to wrap.
    -     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    -     * @param {number} arity The arity of `func`.
    -     * @returns {Function} Returns the new wrapped function.
    -     */
    -    function createCurry(func, bitmask, arity) {
    -      var Ctor = createCtor(func);
    -      function wrapper() {
    -        var length = arguments.length,
    -          args = Array(length),
    -          index = length,
    -          placeholder = getHolder(wrapper);
    -        while (index--) {
    -          args[index] = arguments[index];
    -        }
    -        var holders = length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder ? [] : replaceHolders(args, placeholder);
    -        length -= holders.length;
    -        if (length < arity) {
    -          return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, undefined, args, holders, undefined, undefined, arity - length);
    -        }
    -        var fn = this && this !== root && this instanceof wrapper ? Ctor : func;
    -        return apply(fn, this, args);
    -      }
    -      return wrapper;
    -    }
    -
    -    /**
    -     * Creates a `_.find` or `_.findLast` function.
    -     *
    -     * @private
    -     * @param {Function} findIndexFunc The function to find the collection index.
    -     * @returns {Function} Returns the new find function.
    -     */
    -    function createFind(findIndexFunc) {
    -      return function (collection, predicate, fromIndex) {
    -        var iterable = Object(collection);
    -        if (!isArrayLike(collection)) {
    -          var iteratee = getIteratee(predicate, 3);
    -          collection = keys(collection);
    -          predicate = function predicate(key) {
    -            return iteratee(iterable[key], key, iterable);
    -          };
    -        }
    -        var index = findIndexFunc(collection, predicate, fromIndex);
    -        return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
    -      };
    -    }
    -
    -    /**
    -     * Creates a `_.flow` or `_.flowRight` function.
    -     *
    -     * @private
    -     * @param {boolean} [fromRight] Specify iterating from right to left.
    -     * @returns {Function} Returns the new flow function.
    -     */
    -    function createFlow(fromRight) {
    -      return flatRest(function (funcs) {
    -        var length = funcs.length,
    -          index = length,
    -          prereq = LodashWrapper.prototype.thru;
    -        if (fromRight) {
    -          funcs.reverse();
    -        }
    -        while (index--) {
    -          var func = funcs[index];
    -          if (typeof func != 'function') {
    -            throw new TypeError(FUNC_ERROR_TEXT);
    -          }
    -          if (prereq && !wrapper && getFuncName(func) == 'wrapper') {
    -            var wrapper = new LodashWrapper([], true);
    -          }
    -        }
    -        index = wrapper ? index : length;
    -        while (++index < length) {
    -          func = funcs[index];
    -          var funcName = getFuncName(func),
    -            data = funcName == 'wrapper' ? getData(func) : undefined;
    -          if (data && isLaziable(data[0]) && data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && !data[4].length && data[9] == 1) {
    -            wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
    -          } else {
    -            wrapper = func.length == 1 && isLaziable(func) ? wrapper[funcName]() : wrapper.thru(func);
    -          }
    -        }
    -        return function () {
    -          var args = arguments,
    -            value = args[0];
    -          if (wrapper && args.length == 1 && isArray(value)) {
    -            return wrapper.plant(value).value();
    -          }
    -          var index = 0,
    -            result = length ? funcs[index].apply(this, args) : value;
    -          while (++index < length) {
    -            result = funcs[index].call(this, result);
    -          }
    -          return result;
    -        };
    -      });
    -    }
    -
    -    /**
    -     * Creates a function that wraps `func` to invoke it with optional `this`
    -     * binding of `thisArg`, partial application, and currying.
    -     *
    -     * @private
    -     * @param {Function|string} func The function or method name to wrap.
    -     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    -     * @param {*} [thisArg] The `this` binding of `func`.
    -     * @param {Array} [partials] The arguments to prepend to those provided to
    -     *  the new function.
    -     * @param {Array} [holders] The `partials` placeholder indexes.
    -     * @param {Array} [partialsRight] The arguments to append to those provided
    -     *  to the new function.
    -     * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
    -     * @param {Array} [argPos] The argument positions of the new function.
    -     * @param {number} [ary] The arity cap of `func`.
    -     * @param {number} [arity] The arity of `func`.
    -     * @returns {Function} Returns the new wrapped function.
    -     */
    -    function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
    -      var isAry = bitmask & WRAP_ARY_FLAG,
    -        isBind = bitmask & WRAP_BIND_FLAG,
    -        isBindKey = bitmask & WRAP_BIND_KEY_FLAG,
    -        isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),
    -        isFlip = bitmask & WRAP_FLIP_FLAG,
    -        Ctor = isBindKey ? undefined : createCtor(func);
    -      function wrapper() {
    -        var length = arguments.length,
    -          args = Array(length),
    -          index = length;
    -        while (index--) {
    -          args[index] = arguments[index];
    -        }
    -        if (isCurried) {
    -          var placeholder = getHolder(wrapper),
    -            holdersCount = countHolders(args, placeholder);
    -        }
    -        if (partials) {
    -          args = composeArgs(args, partials, holders, isCurried);
    -        }
    -        if (partialsRight) {
    -          args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
    -        }
    -        length -= holdersCount;
    -        if (isCurried && length < arity) {
    -          var newHolders = replaceHolders(args, placeholder);
    -          return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, thisArg, args, newHolders, argPos, ary, arity - length);
    -        }
    -        var thisBinding = isBind ? thisArg : this,
    -          fn = isBindKey ? thisBinding[func] : func;
    -        length = args.length;
    -        if (argPos) {
    -          args = reorder(args, argPos);
    -        } else if (isFlip && length > 1) {
    -          args.reverse();
    -        }
    -        if (isAry && ary < length) {
    -          args.length = ary;
    -        }
    -        if (this && this !== root && this instanceof wrapper) {
    -          fn = Ctor || createCtor(fn);
    -        }
    -        return fn.apply(thisBinding, args);
    -      }
    -      return wrapper;
    -    }
    -
    -    /**
    -     * Creates a function like `_.invertBy`.
    -     *
    -     * @private
    -     * @param {Function} setter The function to set accumulator values.
    -     * @param {Function} toIteratee The function to resolve iteratees.
    -     * @returns {Function} Returns the new inverter function.
    -     */
    -    function createInverter(setter, toIteratee) {
    -      return function (object, iteratee) {
    -        return baseInverter(object, setter, toIteratee(iteratee), {});
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that performs a mathematical operation on two values.
    -     *
    -     * @private
    -     * @param {Function} operator The function to perform the operation.
    -     * @param {number} [defaultValue] The value used for `undefined` arguments.
    -     * @returns {Function} Returns the new mathematical operation function.
    -     */
    -    function createMathOperation(operator, defaultValue) {
    -      return function (value, other) {
    -        var result;
    -        if (value === undefined && other === undefined) {
    -          return defaultValue;
    -        }
    -        if (value !== undefined) {
    -          result = value;
    -        }
    -        if (other !== undefined) {
    -          if (result === undefined) {
    -            return other;
    -          }
    -          if (typeof value == 'string' || typeof other == 'string') {
    -            value = baseToString(value);
    -            other = baseToString(other);
    -          } else {
    -            value = baseToNumber(value);
    -            other = baseToNumber(other);
    -          }
    -          result = operator(value, other);
    -        }
    -        return result;
    -      };
    -    }
    -
    -    /**
    -     * Creates a function like `_.over`.
    -     *
    -     * @private
    -     * @param {Function} arrayFunc The function to iterate over iteratees.
    -     * @returns {Function} Returns the new over function.
    -     */
    -    function createOver(arrayFunc) {
    -      return flatRest(function (iteratees) {
    -        iteratees = arrayMap(iteratees, baseUnary(getIteratee()));
    -        return baseRest(function (args) {
    -          var thisArg = this;
    -          return arrayFunc(iteratees, function (iteratee) {
    -            return apply(iteratee, thisArg, args);
    -          });
    -        });
    -      });
    -    }
    -
    -    /**
    -     * Creates the padding for `string` based on `length`. The `chars` string
    -     * is truncated if the number of characters exceeds `length`.
    -     *
    -     * @private
    -     * @param {number} length The padding length.
    -     * @param {string} [chars=' '] The string used as padding.
    -     * @returns {string} Returns the padding for `string`.
    -     */
    -    function createPadding(length, chars) {
    -      chars = chars === undefined ? ' ' : baseToString(chars);
    -      var charsLength = chars.length;
    -      if (charsLength < 2) {
    -        return charsLength ? baseRepeat(chars, length) : chars;
    -      }
    -      var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));
    -      return hasUnicode(chars) ? castSlice(stringToArray(result), 0, length).join('') : result.slice(0, length);
    -    }
    -
    -    /**
    -     * Creates a function that wraps `func` to invoke it with the `this` binding
    -     * of `thisArg` and `partials` prepended to the arguments it receives.
    -     *
    -     * @private
    -     * @param {Function} func The function to wrap.
    -     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    -     * @param {*} thisArg The `this` binding of `func`.
    -     * @param {Array} partials The arguments to prepend to those provided to
    -     *  the new function.
    -     * @returns {Function} Returns the new wrapped function.
    -     */
    -    function createPartial(func, bitmask, thisArg, partials) {
    -      var isBind = bitmask & WRAP_BIND_FLAG,
    -        Ctor = createCtor(func);
    -      function wrapper() {
    -        var argsIndex = -1,
    -          argsLength = arguments.length,
    -          leftIndex = -1,
    -          leftLength = partials.length,
    -          args = Array(leftLength + argsLength),
    -          fn = this && this !== root && this instanceof wrapper ? Ctor : func;
    -        while (++leftIndex < leftLength) {
    -          args[leftIndex] = partials[leftIndex];
    -        }
    -        while (argsLength--) {
    -          args[leftIndex++] = arguments[++argsIndex];
    -        }
    -        return apply(fn, isBind ? thisArg : this, args);
    -      }
    -      return wrapper;
    -    }
    -
    -    /**
    -     * Creates a `_.range` or `_.rangeRight` function.
    -     *
    -     * @private
    -     * @param {boolean} [fromRight] Specify iterating from right to left.
    -     * @returns {Function} Returns the new range function.
    -     */
    -    function createRange(fromRight) {
    -      return function (start, end, step) {
    -        if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {
    -          end = step = undefined;
    -        }
    -        // Ensure the sign of `-0` is preserved.
    -        start = toFinite(start);
    -        if (end === undefined) {
    -          end = start;
    -          start = 0;
    -        } else {
    -          end = toFinite(end);
    -        }
    -        step = step === undefined ? start < end ? 1 : -1 : toFinite(step);
    -        return baseRange(start, end, step, fromRight);
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that performs a relational operation on two values.
    -     *
    -     * @private
    -     * @param {Function} operator The function to perform the operation.
    -     * @returns {Function} Returns the new relational operation function.
    -     */
    -    function createRelationalOperation(operator) {
    -      return function (value, other) {
    -        if (!(typeof value == 'string' && typeof other == 'string')) {
    -          value = toNumber(value);
    -          other = toNumber(other);
    -        }
    -        return operator(value, other);
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that wraps `func` to continue currying.
    -     *
    -     * @private
    -     * @param {Function} func The function to wrap.
    -     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    -     * @param {Function} wrapFunc The function to create the `func` wrapper.
    -     * @param {*} placeholder The placeholder value.
    -     * @param {*} [thisArg] The `this` binding of `func`.
    -     * @param {Array} [partials] The arguments to prepend to those provided to
    -     *  the new function.
    -     * @param {Array} [holders] The `partials` placeholder indexes.
    -     * @param {Array} [argPos] The argument positions of the new function.
    -     * @param {number} [ary] The arity cap of `func`.
    -     * @param {number} [arity] The arity of `func`.
    -     * @returns {Function} Returns the new wrapped function.
    -     */
    -    function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
    -      var isCurry = bitmask & WRAP_CURRY_FLAG,
    -        newHolders = isCurry ? holders : undefined,
    -        newHoldersRight = isCurry ? undefined : holders,
    -        newPartials = isCurry ? partials : undefined,
    -        newPartialsRight = isCurry ? undefined : partials;
    -      bitmask |= isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG;
    -      bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);
    -      if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {
    -        bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);
    -      }
    -      var newData = [func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, newHoldersRight, argPos, ary, arity];
    -      var result = wrapFunc.apply(undefined, newData);
    -      if (isLaziable(func)) {
    -        setData(result, newData);
    -      }
    -      result.placeholder = placeholder;
    -      return setWrapToString(result, func, bitmask);
    -    }
    -
    -    /**
    -     * Creates a function like `_.round`.
    -     *
    -     * @private
    -     * @param {string} methodName The name of the `Math` method to use when rounding.
    -     * @returns {Function} Returns the new round function.
    -     */
    -    function createRound(methodName) {
    -      var func = Math[methodName];
    -      return function (number, precision) {
    -        number = toNumber(number);
    -        precision = precision == null ? 0 : nativeMin(toInteger(precision), 292);
    -        if (precision && nativeIsFinite(number)) {
    -          // Shift with exponential notation to avoid floating-point issues.
    -          // See [MDN](https://mdn.io/round#Examples) for more details.
    -          var pair = (toString(number) + 'e').split('e'),
    -            value = func(pair[0] + 'e' + (+pair[1] + precision));
    -          pair = (toString(value) + 'e').split('e');
    -          return +(pair[0] + 'e' + (+pair[1] - precision));
    -        }
    -        return func(number);
    -      };
    -    }
    -
    -    /**
    -     * Creates a set object of `values`.
    -     *
    -     * @private
    -     * @param {Array} values The values to add to the set.
    -     * @returns {Object} Returns the new set.
    -     */
    -    var createSet = !(Set && 1 / setToArray(new Set([, -0]))[1] == INFINITY) ? noop : function (values) {
    -      return new Set(values);
    -    };
    -
    -    /**
    -     * Creates a `_.toPairs` or `_.toPairsIn` function.
    -     *
    -     * @private
    -     * @param {Function} keysFunc The function to get the keys of a given object.
    -     * @returns {Function} Returns the new pairs function.
    -     */
    -    function createToPairs(keysFunc) {
    -      return function (object) {
    -        var tag = getTag(object);
    -        if (tag == mapTag) {
    -          return mapToArray(object);
    -        }
    -        if (tag == setTag) {
    -          return setToPairs(object);
    -        }
    -        return baseToPairs(object, keysFunc(object));
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that either curries or invokes `func` with optional
    -     * `this` binding and partially applied arguments.
    -     *
    -     * @private
    -     * @param {Function|string} func The function or method name to wrap.
    -     * @param {number} bitmask The bitmask flags.
    -     *    1 - `_.bind`
    -     *    2 - `_.bindKey`
    -     *    4 - `_.curry` or `_.curryRight` of a bound function
    -     *    8 - `_.curry`
    -     *   16 - `_.curryRight`
    -     *   32 - `_.partial`
    -     *   64 - `_.partialRight`
    -     *  128 - `_.rearg`
    -     *  256 - `_.ary`
    -     *  512 - `_.flip`
    -     * @param {*} [thisArg] The `this` binding of `func`.
    -     * @param {Array} [partials] The arguments to be partially applied.
    -     * @param {Array} [holders] The `partials` placeholder indexes.
    -     * @param {Array} [argPos] The argument positions of the new function.
    -     * @param {number} [ary] The arity cap of `func`.
    -     * @param {number} [arity] The arity of `func`.
    -     * @returns {Function} Returns the new wrapped function.
    -     */
    -    function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
    -      var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;
    -      if (!isBindKey && typeof func != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      var length = partials ? partials.length : 0;
    -      if (!length) {
    -        bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);
    -        partials = holders = undefined;
    -      }
    -      ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
    -      arity = arity === undefined ? arity : toInteger(arity);
    -      length -= holders ? holders.length : 0;
    -      if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {
    -        var partialsRight = partials,
    -          holdersRight = holders;
    -        partials = holders = undefined;
    -      }
    -      var data = isBindKey ? undefined : getData(func);
    -      var newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
    -      if (data) {
    -        mergeData(newData, data);
    -      }
    -      func = newData[0];
    -      bitmask = newData[1];
    -      thisArg = newData[2];
    -      partials = newData[3];
    -      holders = newData[4];
    -      arity = newData[9] = newData[9] === undefined ? isBindKey ? 0 : func.length : nativeMax(newData[9] - length, 0);
    -      if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {
    -        bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);
    -      }
    -      if (!bitmask || bitmask == WRAP_BIND_FLAG) {
    -        var result = createBind(func, bitmask, thisArg);
    -      } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {
    -        result = createCurry(func, bitmask, arity);
    -      } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {
    -        result = createPartial(func, bitmask, thisArg, partials);
    -      } else {
    -        result = createHybrid.apply(undefined, newData);
    -      }
    -      var setter = data ? baseSetData : setData;
    -      return setWrapToString(setter(result, newData), func, bitmask);
    -    }
    -
    -    /**
    -     * Used by `_.defaults` to customize its `_.assignIn` use to assign properties
    -     * of source objects to the destination object for all destination properties
    -     * that resolve to `undefined`.
    -     *
    -     * @private
    -     * @param {*} objValue The destination value.
    -     * @param {*} srcValue The source value.
    -     * @param {string} key The key of the property to assign.
    -     * @param {Object} object The parent object of `objValue`.
    -     * @returns {*} Returns the value to assign.
    -     */
    -    function customDefaultsAssignIn(objValue, srcValue, key, object) {
    -      if (objValue === undefined || eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key)) {
    -        return srcValue;
    -      }
    -      return objValue;
    -    }
    -
    -    /**
    -     * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source
    -     * objects into destination objects that are passed thru.
    -     *
    -     * @private
    -     * @param {*} objValue The destination value.
    -     * @param {*} srcValue The source value.
    -     * @param {string} key The key of the property to merge.
    -     * @param {Object} object The parent object of `objValue`.
    -     * @param {Object} source The parent object of `srcValue`.
    -     * @param {Object} [stack] Tracks traversed source values and their merged
    -     *  counterparts.
    -     * @returns {*} Returns the value to assign.
    -     */
    -    function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {
    -      if (isObject(objValue) && isObject(srcValue)) {
    -        // Recursively merge objects and arrays (susceptible to call stack limits).
    -        stack.set(srcValue, objValue);
    -        baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack);
    -        stack['delete'](srcValue);
    -      }
    -      return objValue;
    -    }
    -
    -    /**
    -     * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain
    -     * objects.
    -     *
    -     * @private
    -     * @param {*} value The value to inspect.
    -     * @param {string} key The key of the property to inspect.
    -     * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.
    -     */
    -    function customOmitClone(value) {
    -      return isPlainObject(value) ? undefined : value;
    -    }
    -
    -    /**
    -     * A specialized version of `baseIsEqualDeep` for arrays with support for
    -     * partial deep comparisons.
    -     *
    -     * @private
    -     * @param {Array} array The array to compare.
    -     * @param {Array} other The other array to compare.
    -     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
    -     * @param {Function} customizer The function to customize comparisons.
    -     * @param {Function} equalFunc The function to determine equivalents of values.
    -     * @param {Object} stack Tracks traversed `array` and `other` objects.
    -     * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
    -     */
    -    function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
    -      var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
    -        arrLength = array.length,
    -        othLength = other.length;
    -      if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
    -        return false;
    -      }
    -      // Check that cyclic values are equal.
    -      var arrStacked = stack.get(array);
    -      var othStacked = stack.get(other);
    -      if (arrStacked && othStacked) {
    -        return arrStacked == other && othStacked == array;
    -      }
    -      var index = -1,
    -        result = true,
    -        seen = bitmask & COMPARE_UNORDERED_FLAG ? new SetCache() : undefined;
    -      stack.set(array, other);
    -      stack.set(other, array);
    -
    -      // Ignore non-index properties.
    -      while (++index < arrLength) {
    -        var arrValue = array[index],
    -          othValue = other[index];
    -        if (customizer) {
    -          var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack);
    -        }
    -        if (compared !== undefined) {
    -          if (compared) {
    -            continue;
    -          }
    -          result = false;
    -          break;
    -        }
    -        // Recursively compare arrays (susceptible to call stack limits).
    -        if (seen) {
    -          if (!arraySome(other, function (othValue, othIndex) {
    -            if (!cacheHas(seen, othIndex) && (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
    -              return seen.push(othIndex);
    -            }
    -          })) {
    -            result = false;
    -            break;
    -          }
    -        } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
    -          result = false;
    -          break;
    -        }
    -      }
    -      stack['delete'](array);
    -      stack['delete'](other);
    -      return result;
    -    }
    -
    -    /**
    -     * A specialized version of `baseIsEqualDeep` for comparing objects of
    -     * the same `toStringTag`.
    -     *
    -     * **Note:** This function only supports comparing values with tags of
    -     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
    -     *
    -     * @private
    -     * @param {Object} object The object to compare.
    -     * @param {Object} other The other object to compare.
    -     * @param {string} tag The `toStringTag` of the objects to compare.
    -     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
    -     * @param {Function} customizer The function to customize comparisons.
    -     * @param {Function} equalFunc The function to determine equivalents of values.
    -     * @param {Object} stack Tracks traversed `object` and `other` objects.
    -     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
    -     */
    -    function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
    -      switch (tag) {
    -        case dataViewTag:
    -          if (object.byteLength != other.byteLength || object.byteOffset != other.byteOffset) {
    -            return false;
    -          }
    -          object = object.buffer;
    -          other = other.buffer;
    -        case arrayBufferTag:
    -          if (object.byteLength != other.byteLength || !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
    -            return false;
    -          }
    -          return true;
    -        case boolTag:
    -        case dateTag:
    -        case numberTag:
    -          // Coerce booleans to `1` or `0` and dates to milliseconds.
    -          // Invalid dates are coerced to `NaN`.
    -          return eq(+object, +other);
    -        case errorTag:
    -          return object.name == other.name && object.message == other.message;
    -        case regexpTag:
    -        case stringTag:
    -          // Coerce regexes to strings and treat strings, primitives and objects,
    -          // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
    -          // for more details.
    -          return object == other + '';
    -        case mapTag:
    -          var convert = mapToArray;
    -        case setTag:
    -          var isPartial = bitmask & COMPARE_PARTIAL_FLAG;
    -          convert || (convert = setToArray);
    -          if (object.size != other.size && !isPartial) {
    -            return false;
    -          }
    -          // Assume cyclic values are equal.
    -          var stacked = stack.get(object);
    -          if (stacked) {
    -            return stacked == other;
    -          }
    -          bitmask |= COMPARE_UNORDERED_FLAG;
    -
    -          // Recursively compare objects (susceptible to call stack limits).
    -          stack.set(object, other);
    -          var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
    -          stack['delete'](object);
    -          return result;
    -        case symbolTag:
    -          if (symbolValueOf) {
    -            return symbolValueOf.call(object) == symbolValueOf.call(other);
    -          }
    -      }
    -      return false;
    -    }
    -
    -    /**
    -     * A specialized version of `baseIsEqualDeep` for objects with support for
    -     * partial deep comparisons.
    -     *
    -     * @private
    -     * @param {Object} object The object to compare.
    -     * @param {Object} other The other object to compare.
    -     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
    -     * @param {Function} customizer The function to customize comparisons.
    -     * @param {Function} equalFunc The function to determine equivalents of values.
    -     * @param {Object} stack Tracks traversed `object` and `other` objects.
    -     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
    -     */
    -    function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
    -      var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
    -        objProps = getAllKeys(object),
    -        objLength = objProps.length,
    -        othProps = getAllKeys(other),
    -        othLength = othProps.length;
    -      if (objLength != othLength && !isPartial) {
    -        return false;
    -      }
    -      var index = objLength;
    -      while (index--) {
    -        var key = objProps[index];
    -        if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {
    -          return false;
    -        }
    -      }
    -      // Check that cyclic values are equal.
    -      var objStacked = stack.get(object);
    -      var othStacked = stack.get(other);
    -      if (objStacked && othStacked) {
    -        return objStacked == other && othStacked == object;
    -      }
    -      var result = true;
    -      stack.set(object, other);
    -      stack.set(other, object);
    -      var skipCtor = isPartial;
    -      while (++index < objLength) {
    -        key = objProps[index];
    -        var objValue = object[key],
    -          othValue = other[key];
    -        if (customizer) {
    -          var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack);
    -        }
    -        // Recursively compare objects (susceptible to call stack limits).
    -        if (!(compared === undefined ? objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack) : compared)) {
    -          result = false;
    -          break;
    -        }
    -        skipCtor || (skipCtor = key == 'constructor');
    -      }
    -      if (result && !skipCtor) {
    -        var objCtor = object.constructor,
    -          othCtor = other.constructor;
    -
    -        // Non `Object` object instances with different constructors are not equal.
    -        if (objCtor != othCtor && 'constructor' in object && 'constructor' in other && !(typeof objCtor == 'function' && objCtor instanceof objCtor && typeof othCtor == 'function' && othCtor instanceof othCtor)) {
    -          result = false;
    -        }
    -      }
    -      stack['delete'](object);
    -      stack['delete'](other);
    -      return result;
    -    }
    -
    -    /**
    -     * A specialized version of `baseRest` which flattens the rest array.
    -     *
    -     * @private
    -     * @param {Function} func The function to apply a rest parameter to.
    -     * @returns {Function} Returns the new function.
    -     */
    -    function flatRest(func) {
    -      return setToString(overRest(func, undefined, flatten), func + '');
    -    }
    -
    -    /**
    -     * Creates an array of own enumerable property names and symbols of `object`.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @returns {Array} Returns the array of property names and symbols.
    -     */
    -    function getAllKeys(object) {
    -      return baseGetAllKeys(object, keys, getSymbols);
    -    }
    -
    -    /**
    -     * Creates an array of own and inherited enumerable property names and
    -     * symbols of `object`.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @returns {Array} Returns the array of property names and symbols.
    -     */
    -    function getAllKeysIn(object) {
    -      return baseGetAllKeys(object, keysIn, getSymbolsIn);
    -    }
    -
    -    /**
    -     * Gets metadata for `func`.
    -     *
    -     * @private
    -     * @param {Function} func The function to query.
    -     * @returns {*} Returns the metadata for `func`.
    -     */
    -    var getData = !metaMap ? noop : function (func) {
    -      return metaMap.get(func);
    -    };
    -
    -    /**
    -     * Gets the name of `func`.
    -     *
    -     * @private
    -     * @param {Function} func The function to query.
    -     * @returns {string} Returns the function name.
    -     */
    -    function getFuncName(func) {
    -      var result = func.name + '',
    -        array = realNames[result],
    -        length = hasOwnProperty.call(realNames, result) ? array.length : 0;
    -      while (length--) {
    -        var data = array[length],
    -          otherFunc = data.func;
    -        if (otherFunc == null || otherFunc == func) {
    -          return data.name;
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Gets the argument placeholder value for `func`.
    -     *
    -     * @private
    -     * @param {Function} func The function to inspect.
    -     * @returns {*} Returns the placeholder value.
    -     */
    -    function getHolder(func) {
    -      var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
    -      return object.placeholder;
    -    }
    -
    -    /**
    -     * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
    -     * this function returns the custom method, otherwise it returns `baseIteratee`.
    -     * If arguments are provided, the chosen function is invoked with them and
    -     * its result is returned.
    -     *
    -     * @private
    -     * @param {*} [value] The value to convert to an iteratee.
    -     * @param {number} [arity] The arity of the created iteratee.
    -     * @returns {Function} Returns the chosen function or its result.
    -     */
    -    function getIteratee() {
    -      var result = lodash.iteratee || iteratee;
    -      result = result === iteratee ? baseIteratee : result;
    -      return arguments.length ? result(arguments[0], arguments[1]) : result;
    -    }
    -
    -    /**
    -     * Gets the data for `map`.
    -     *
    -     * @private
    -     * @param {Object} map The map to query.
    -     * @param {string} key The reference key.
    -     * @returns {*} Returns the map data.
    -     */
    -    function getMapData(map, key) {
    -      var data = map.__data__;
    -      return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map;
    -    }
    -
    -    /**
    -     * Gets the property names, values, and compare flags of `object`.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @returns {Array} Returns the match data of `object`.
    -     */
    -    function getMatchData(object) {
    -      var result = keys(object),
    -        length = result.length;
    -      while (length--) {
    -        var key = result[length],
    -          value = object[key];
    -        result[length] = [key, value, isStrictComparable(value)];
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Gets the native function at `key` of `object`.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @param {string} key The key of the method to get.
    -     * @returns {*} Returns the function if it's native, else `undefined`.
    -     */
    -    function getNative(object, key) {
    -      var value = getValue(object, key);
    -      return baseIsNative(value) ? value : undefined;
    -    }
    -
    -    /**
    -     * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
    -     *
    -     * @private
    -     * @param {*} value The value to query.
    -     * @returns {string} Returns the raw `toStringTag`.
    -     */
    -    function getRawTag(value) {
    -      var isOwn = hasOwnProperty.call(value, symToStringTag),
    -        tag = value[symToStringTag];
    -      try {
    -        value[symToStringTag] = undefined;
    -        var unmasked = true;
    -      } catch (e) {}
    -      var result = nativeObjectToString.call(value);
    -      if (unmasked) {
    -        if (isOwn) {
    -          value[symToStringTag] = tag;
    -        } else {
    -          delete value[symToStringTag];
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Creates an array of the own enumerable symbols of `object`.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @returns {Array} Returns the array of symbols.
    -     */
    -    var getSymbols = !nativeGetSymbols ? stubArray : function (object) {
    -      if (object == null) {
    -        return [];
    -      }
    -      object = Object(object);
    -      return arrayFilter(nativeGetSymbols(object), function (symbol) {
    -        return propertyIsEnumerable.call(object, symbol);
    -      });
    -    };
    -
    -    /**
    -     * Creates an array of the own and inherited enumerable symbols of `object`.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @returns {Array} Returns the array of symbols.
    -     */
    -    var getSymbolsIn = !nativeGetSymbols ? stubArray : function (object) {
    -      var result = [];
    -      while (object) {
    -        arrayPush(result, getSymbols(object));
    -        object = getPrototype(object);
    -      }
    -      return result;
    -    };
    -
    -    /**
    -     * Gets the `toStringTag` of `value`.
    -     *
    -     * @private
    -     * @param {*} value The value to query.
    -     * @returns {string} Returns the `toStringTag`.
    -     */
    -    var getTag = baseGetTag;
    -
    -    // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
    -    if (DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag || Map && getTag(new Map()) != mapTag || Promise && getTag(Promise.resolve()) != promiseTag || Set && getTag(new Set()) != setTag || WeakMap && getTag(new WeakMap()) != weakMapTag) {
    -      getTag = function getTag(value) {
    -        var result = baseGetTag(value),
    -          Ctor = result == objectTag ? value.constructor : undefined,
    -          ctorString = Ctor ? toSource(Ctor) : '';
    -        if (ctorString) {
    -          switch (ctorString) {
    -            case dataViewCtorString:
    -              return dataViewTag;
    -            case mapCtorString:
    -              return mapTag;
    -            case promiseCtorString:
    -              return promiseTag;
    -            case setCtorString:
    -              return setTag;
    -            case weakMapCtorString:
    -              return weakMapTag;
    -          }
    -        }
    -        return result;
    -      };
    -    }
    -
    -    /**
    -     * Gets the view, applying any `transforms` to the `start` and `end` positions.
    -     *
    -     * @private
    -     * @param {number} start The start of the view.
    -     * @param {number} end The end of the view.
    -     * @param {Array} transforms The transformations to apply to the view.
    -     * @returns {Object} Returns an object containing the `start` and `end`
    -     *  positions of the view.
    -     */
    -    function getView(start, end, transforms) {
    -      var index = -1,
    -        length = transforms.length;
    -      while (++index < length) {
    -        var data = transforms[index],
    -          size = data.size;
    -        switch (data.type) {
    -          case 'drop':
    -            start += size;
    -            break;
    -          case 'dropRight':
    -            end -= size;
    -            break;
    -          case 'take':
    -            end = nativeMin(end, start + size);
    -            break;
    -          case 'takeRight':
    -            start = nativeMax(start, end - size);
    -            break;
    -        }
    -      }
    -      return {
    -        'start': start,
    -        'end': end
    -      };
    -    }
    -
    -    /**
    -     * Extracts wrapper details from the `source` body comment.
    -     *
    -     * @private
    -     * @param {string} source The source to inspect.
    -     * @returns {Array} Returns the wrapper details.
    -     */
    -    function getWrapDetails(source) {
    -      var match = source.match(reWrapDetails);
    -      return match ? match[1].split(reSplitDetails) : [];
    -    }
    -
    -    /**
    -     * Checks if `path` exists on `object`.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @param {Array|string} path The path to check.
    -     * @param {Function} hasFunc The function to check properties.
    -     * @returns {boolean} Returns `true` if `path` exists, else `false`.
    -     */
    -    function hasPath(object, path, hasFunc) {
    -      path = castPath(path, object);
    -      var index = -1,
    -        length = path.length,
    -        result = false;
    -      while (++index < length) {
    -        var key = toKey(path[index]);
    -        if (!(result = object != null && hasFunc(object, key))) {
    -          break;
    -        }
    -        object = object[key];
    -      }
    -      if (result || ++index != length) {
    -        return result;
    -      }
    -      length = object == null ? 0 : object.length;
    -      return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object));
    -    }
    -
    -    /**
    -     * Initializes an array clone.
    -     *
    -     * @private
    -     * @param {Array} array The array to clone.
    -     * @returns {Array} Returns the initialized clone.
    -     */
    -    function initCloneArray(array) {
    -      var length = array.length,
    -        result = new array.constructor(length);
    -
    -      // Add properties assigned by `RegExp#exec`.
    -      if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
    -        result.index = array.index;
    -        result.input = array.input;
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Initializes an object clone.
    -     *
    -     * @private
    -     * @param {Object} object The object to clone.
    -     * @returns {Object} Returns the initialized clone.
    -     */
    -    function initCloneObject(object) {
    -      return typeof object.constructor == 'function' && !isPrototype(object) ? baseCreate(getPrototype(object)) : {};
    -    }
    -
    -    /**
    -     * Initializes an object clone based on its `toStringTag`.
    -     *
    -     * **Note:** This function only supports cloning values with tags of
    -     * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
    -     *
    -     * @private
    -     * @param {Object} object The object to clone.
    -     * @param {string} tag The `toStringTag` of the object to clone.
    -     * @param {boolean} [isDeep] Specify a deep clone.
    -     * @returns {Object} Returns the initialized clone.
    -     */
    -    function initCloneByTag(object, tag, isDeep) {
    -      var Ctor = object.constructor;
    -      switch (tag) {
    -        case arrayBufferTag:
    -          return cloneArrayBuffer(object);
    -        case boolTag:
    -        case dateTag:
    -          return new Ctor(+object);
    -        case dataViewTag:
    -          return cloneDataView(object, isDeep);
    -        case float32Tag:
    -        case float64Tag:
    -        case int8Tag:
    -        case int16Tag:
    -        case int32Tag:
    -        case uint8Tag:
    -        case uint8ClampedTag:
    -        case uint16Tag:
    -        case uint32Tag:
    -          return cloneTypedArray(object, isDeep);
    -        case mapTag:
    -          return new Ctor();
    -        case numberTag:
    -        case stringTag:
    -          return new Ctor(object);
    -        case regexpTag:
    -          return cloneRegExp(object);
    -        case setTag:
    -          return new Ctor();
    -        case symbolTag:
    -          return cloneSymbol(object);
    -      }
    -    }
    -
    -    /**
    -     * Inserts wrapper `details` in a comment at the top of the `source` body.
    -     *
    -     * @private
    -     * @param {string} source The source to modify.
    -     * @returns {Array} details The details to insert.
    -     * @returns {string} Returns the modified source.
    -     */
    -    function insertWrapDetails(source, details) {
    -      var length = details.length;
    -      if (!length) {
    -        return source;
    -      }
    -      var lastIndex = length - 1;
    -      details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];
    -      details = details.join(length > 2 ? ', ' : ' ');
    -      return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n');
    -    }
    -
    -    /**
    -     * Checks if `value` is a flattenable `arguments` object or array.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
    -     */
    -    function isFlattenable(value) {
    -      return isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]);
    -    }
    -
    -    /**
    -     * Checks if `value` is a valid array-like index.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
    -     * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
    -     */
    -    function isIndex(value, length) {
    -      var type = typeof value;
    -      length = length == null ? MAX_SAFE_INTEGER : length;
    -      return !!length && (type == 'number' || type != 'symbol' && reIsUint.test(value)) && value > -1 && value % 1 == 0 && value < length;
    -    }
    -
    -    /**
    -     * Checks if the given arguments are from an iteratee call.
    -     *
    -     * @private
    -     * @param {*} value The potential iteratee value argument.
    -     * @param {*} index The potential iteratee index or key argument.
    -     * @param {*} object The potential iteratee object argument.
    -     * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
    -     *  else `false`.
    -     */
    -    function isIterateeCall(value, index, object) {
    -      if (!isObject(object)) {
    -        return false;
    -      }
    -      var type = typeof index;
    -      if (type == 'number' ? isArrayLike(object) && isIndex(index, object.length) : type == 'string' && index in object) {
    -        return eq(object[index], value);
    -      }
    -      return false;
    -    }
    -
    -    /**
    -     * Checks if `value` is a property name and not a property path.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @param {Object} [object] The object to query keys on.
    -     * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
    -     */
    -    function isKey(value, object) {
    -      if (isArray(value)) {
    -        return false;
    -      }
    -      var type = typeof value;
    -      if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol(value)) {
    -        return true;
    -      }
    -      return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object(object);
    -    }
    -
    -    /**
    -     * Checks if `value` is suitable for use as unique object key.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
    -     */
    -    function isKeyable(value) {
    -      var type = typeof value;
    -      return type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean' ? value !== '__proto__' : value === null;
    -    }
    -
    -    /**
    -     * Checks if `func` has a lazy counterpart.
    -     *
    -     * @private
    -     * @param {Function} func The function to check.
    -     * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
    -     *  else `false`.
    -     */
    -    function isLaziable(func) {
    -      var funcName = getFuncName(func),
    -        other = lodash[funcName];
    -      if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
    -        return false;
    -      }
    -      if (func === other) {
    -        return true;
    -      }
    -      var data = getData(other);
    -      return !!data && func === data[0];
    -    }
    -
    -    /**
    -     * Checks if `func` has its source masked.
    -     *
    -     * @private
    -     * @param {Function} func The function to check.
    -     * @returns {boolean} Returns `true` if `func` is masked, else `false`.
    -     */
    -    function isMasked(func) {
    -      return !!maskSrcKey && maskSrcKey in func;
    -    }
    -
    -    /**
    -     * Checks if `func` is capable of being masked.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `func` is maskable, else `false`.
    -     */
    -    var isMaskable = coreJsData ? isFunction : stubFalse;
    -
    -    /**
    -     * Checks if `value` is likely a prototype object.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
    -     */
    -    function isPrototype(value) {
    -      var Ctor = value && value.constructor,
    -        proto = typeof Ctor == 'function' && Ctor.prototype || objectProto;
    -      return value === proto;
    -    }
    -
    -    /**
    -     * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
    -     *
    -     * @private
    -     * @param {*} value The value to check.
    -     * @returns {boolean} Returns `true` if `value` if suitable for strict
    -     *  equality comparisons, else `false`.
    -     */
    -    function isStrictComparable(value) {
    -      return value === value && !isObject(value);
    -    }
    -
    -    /**
    -     * A specialized version of `matchesProperty` for source values suitable
    -     * for strict equality comparisons, i.e. `===`.
    -     *
    -     * @private
    -     * @param {string} key The key of the property to get.
    -     * @param {*} srcValue The value to match.
    -     * @returns {Function} Returns the new spec function.
    -     */
    -    function matchesStrictComparable(key, srcValue) {
    -      return function (object) {
    -        if (object == null) {
    -          return false;
    -        }
    -        return object[key] === srcValue && (srcValue !== undefined || key in Object(object));
    -      };
    -    }
    -
    -    /**
    -     * A specialized version of `_.memoize` which clears the memoized function's
    -     * cache when it exceeds `MAX_MEMOIZE_SIZE`.
    -     *
    -     * @private
    -     * @param {Function} func The function to have its output memoized.
    -     * @returns {Function} Returns the new memoized function.
    -     */
    -    function memoizeCapped(func) {
    -      var result = memoize(func, function (key) {
    -        if (cache.size === MAX_MEMOIZE_SIZE) {
    -          cache.clear();
    -        }
    -        return key;
    -      });
    -      var cache = result.cache;
    -      return result;
    -    }
    -
    -    /**
    -     * Merges the function metadata of `source` into `data`.
    -     *
    -     * Merging metadata reduces the number of wrappers used to invoke a function.
    -     * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
    -     * may be applied regardless of execution order. Methods like `_.ary` and
    -     * `_.rearg` modify function arguments, making the order in which they are
    -     * executed important, preventing the merging of metadata. However, we make
    -     * an exception for a safe combined case where curried functions have `_.ary`
    -     * and or `_.rearg` applied.
    -     *
    -     * @private
    -     * @param {Array} data The destination metadata.
    -     * @param {Array} source The source metadata.
    -     * @returns {Array} Returns `data`.
    -     */
    -    function mergeData(data, source) {
    -      var bitmask = data[1],
    -        srcBitmask = source[1],
    -        newBitmask = bitmask | srcBitmask,
    -        isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG);
    -      var isCombo = srcBitmask == WRAP_ARY_FLAG && bitmask == WRAP_CURRY_FLAG || srcBitmask == WRAP_ARY_FLAG && bitmask == WRAP_REARG_FLAG && data[7].length <= source[8] || srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG) && source[7].length <= source[8] && bitmask == WRAP_CURRY_FLAG;
    -
    -      // Exit early if metadata can't be merged.
    -      if (!(isCommon || isCombo)) {
    -        return data;
    -      }
    -      // Use source `thisArg` if available.
    -      if (srcBitmask & WRAP_BIND_FLAG) {
    -        data[2] = source[2];
    -        // Set when currying a bound function.
    -        newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG;
    -      }
    -      // Compose partial arguments.
    -      var value = source[3];
    -      if (value) {
    -        var partials = data[3];
    -        data[3] = partials ? composeArgs(partials, value, source[4]) : value;
    -        data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
    -      }
    -      // Compose partial right arguments.
    -      value = source[5];
    -      if (value) {
    -        partials = data[5];
    -        data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
    -        data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
    -      }
    -      // Use source `argPos` if available.
    -      value = source[7];
    -      if (value) {
    -        data[7] = value;
    -      }
    -      // Use source `ary` if it's smaller.
    -      if (srcBitmask & WRAP_ARY_FLAG) {
    -        data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
    -      }
    -      // Use source `arity` if one is not provided.
    -      if (data[9] == null) {
    -        data[9] = source[9];
    -      }
    -      // Use source `func` and merge bitmasks.
    -      data[0] = source[0];
    -      data[1] = newBitmask;
    -      return data;
    -    }
    -
    -    /**
    -     * This function is like
    -     * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
    -     * except that it includes inherited enumerable properties.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @returns {Array} Returns the array of property names.
    -     */
    -    function nativeKeysIn(object) {
    -      var result = [];
    -      if (object != null) {
    -        for (var key in Object(object)) {
    -          result.push(key);
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Converts `value` to a string using `Object.prototype.toString`.
    -     *
    -     * @private
    -     * @param {*} value The value to convert.
    -     * @returns {string} Returns the converted string.
    -     */
    -    function objectToString(value) {
    -      return nativeObjectToString.call(value);
    -    }
    -
    -    /**
    -     * A specialized version of `baseRest` which transforms the rest array.
    -     *
    -     * @private
    -     * @param {Function} func The function to apply a rest parameter to.
    -     * @param {number} [start=func.length-1] The start position of the rest parameter.
    -     * @param {Function} transform The rest array transform.
    -     * @returns {Function} Returns the new function.
    -     */
    -    function overRest(func, start, transform) {
    -      start = nativeMax(start === undefined ? func.length - 1 : start, 0);
    -      return function () {
    -        var args = arguments,
    -          index = -1,
    -          length = nativeMax(args.length - start, 0),
    -          array = Array(length);
    -        while (++index < length) {
    -          array[index] = args[start + index];
    -        }
    -        index = -1;
    -        var otherArgs = Array(start + 1);
    -        while (++index < start) {
    -          otherArgs[index] = args[index];
    -        }
    -        otherArgs[start] = transform(array);
    -        return apply(func, this, otherArgs);
    -      };
    -    }
    -
    -    /**
    -     * Gets the parent value at `path` of `object`.
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @param {Array} path The path to get the parent value of.
    -     * @returns {*} Returns the parent value.
    -     */
    -    function parent(object, path) {
    -      return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));
    -    }
    -
    -    /**
    -     * Reorder `array` according to the specified indexes where the element at
    -     * the first index is assigned as the first element, the element at
    -     * the second index is assigned as the second element, and so on.
    -     *
    -     * @private
    -     * @param {Array} array The array to reorder.
    -     * @param {Array} indexes The arranged array indexes.
    -     * @returns {Array} Returns `array`.
    -     */
    -    function reorder(array, indexes) {
    -      var arrLength = array.length,
    -        length = nativeMin(indexes.length, arrLength),
    -        oldArray = copyArray(array);
    -      while (length--) {
    -        var index = indexes[length];
    -        array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
    -      }
    -      return array;
    -    }
    -
    -    /**
    -     * Gets the value at `key`, unless `key` is "__proto__" or "constructor".
    -     *
    -     * @private
    -     * @param {Object} object The object to query.
    -     * @param {string} key The key of the property to get.
    -     * @returns {*} Returns the property value.
    -     */
    -    function safeGet(object, key) {
    -      if (key === 'constructor' && typeof object[key] === 'function') {
    -        return;
    -      }
    -      if (key == '__proto__') {
    -        return;
    -      }
    -      return object[key];
    -    }
    -
    -    /**
    -     * Sets metadata for `func`.
    -     *
    -     * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
    -     * period of time, it will trip its breaker and transition to an identity
    -     * function to avoid garbage collection pauses in V8. See
    -     * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
    -     * for more details.
    -     *
    -     * @private
    -     * @param {Function} func The function to associate metadata with.
    -     * @param {*} data The metadata.
    -     * @returns {Function} Returns `func`.
    -     */
    -    var setData = shortOut(baseSetData);
    -
    -    /**
    -     * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout).
    -     *
    -     * @private
    -     * @param {Function} func The function to delay.
    -     * @param {number} wait The number of milliseconds to delay invocation.
    -     * @returns {number|Object} Returns the timer id or timeout object.
    -     */
    -    var setTimeout = ctxSetTimeout || function (func, wait) {
    -      return root.setTimeout(func, wait);
    -    };
    -
    -    /**
    -     * Sets the `toString` method of `func` to return `string`.
    -     *
    -     * @private
    -     * @param {Function} func The function to modify.
    -     * @param {Function} string The `toString` result.
    -     * @returns {Function} Returns `func`.
    -     */
    -    var setToString = shortOut(baseSetToString);
    -
    -    /**
    -     * Sets the `toString` method of `wrapper` to mimic the source of `reference`
    -     * with wrapper details in a comment at the top of the source body.
    -     *
    -     * @private
    -     * @param {Function} wrapper The function to modify.
    -     * @param {Function} reference The reference function.
    -     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    -     * @returns {Function} Returns `wrapper`.
    -     */
    -    function setWrapToString(wrapper, reference, bitmask) {
    -      var source = reference + '';
    -      return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));
    -    }
    -
    -    /**
    -     * Creates a function that'll short out and invoke `identity` instead
    -     * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
    -     * milliseconds.
    -     *
    -     * @private
    -     * @param {Function} func The function to restrict.
    -     * @returns {Function} Returns the new shortable function.
    -     */
    -    function shortOut(func) {
    -      var count = 0,
    -        lastCalled = 0;
    -      return function () {
    -        var stamp = nativeNow(),
    -          remaining = HOT_SPAN - (stamp - lastCalled);
    -        lastCalled = stamp;
    -        if (remaining > 0) {
    -          if (++count >= HOT_COUNT) {
    -            return arguments[0];
    -          }
    -        } else {
    -          count = 0;
    -        }
    -        return func.apply(undefined, arguments);
    -      };
    -    }
    -
    -    /**
    -     * A specialized version of `_.shuffle` which mutates and sets the size of `array`.
    -     *
    -     * @private
    -     * @param {Array} array The array to shuffle.
    -     * @param {number} [size=array.length] The size of `array`.
    -     * @returns {Array} Returns `array`.
    -     */
    -    function shuffleSelf(array, size) {
    -      var index = -1,
    -        length = array.length,
    -        lastIndex = length - 1;
    -      size = size === undefined ? length : size;
    -      while (++index < size) {
    -        var rand = baseRandom(index, lastIndex),
    -          value = array[rand];
    -        array[rand] = array[index];
    -        array[index] = value;
    -      }
    -      array.length = size;
    -      return array;
    -    }
    -
    -    /**
    -     * Converts `string` to a property path array.
    -     *
    -     * @private
    -     * @param {string} string The string to convert.
    -     * @returns {Array} Returns the property path array.
    -     */
    -    var stringToPath = memoizeCapped(function (string) {
    -      var result = [];
    -      if (string.charCodeAt(0) === 46 /* . */) {
    -        result.push('');
    -      }
    -      string.replace(rePropName, function (match, number, quote, subString) {
    -        result.push(quote ? subString.replace(reEscapeChar, '$1') : number || match);
    -      });
    -      return result;
    -    });
    -
    -    /**
    -     * Converts `value` to a string key if it's not a string or symbol.
    -     *
    -     * @private
    -     * @param {*} value The value to inspect.
    -     * @returns {string|symbol} Returns the key.
    -     */
    -    function toKey(value) {
    -      if (typeof value == 'string' || isSymbol(value)) {
    -        return value;
    -      }
    -      var result = value + '';
    -      return result == '0' && 1 / value == -INFINITY ? '-0' : result;
    -    }
    -
    -    /**
    -     * Converts `func` to its source code.
    -     *
    -     * @private
    -     * @param {Function} func The function to convert.
    -     * @returns {string} Returns the source code.
    -     */
    -    function toSource(func) {
    -      if (func != null) {
    -        try {
    -          return funcToString.call(func);
    -        } catch (e) {}
    -        try {
    -          return func + '';
    -        } catch (e) {}
    -      }
    -      return '';
    -    }
    -
    -    /**
    -     * Updates wrapper `details` based on `bitmask` flags.
    -     *
    -     * @private
    -     * @returns {Array} details The details to modify.
    -     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
    -     * @returns {Array} Returns `details`.
    -     */
    -    function updateWrapDetails(details, bitmask) {
    -      arrayEach(wrapFlags, function (pair) {
    -        var value = '_.' + pair[0];
    -        if (bitmask & pair[1] && !arrayIncludes(details, value)) {
    -          details.push(value);
    -        }
    -      });
    -      return details.sort();
    -    }
    -
    -    /**
    -     * Creates a clone of `wrapper`.
    -     *
    -     * @private
    -     * @param {Object} wrapper The wrapper to clone.
    -     * @returns {Object} Returns the cloned wrapper.
    -     */
    -    function wrapperClone(wrapper) {
    -      if (wrapper instanceof LazyWrapper) {
    -        return wrapper.clone();
    -      }
    -      var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
    -      result.__actions__ = copyArray(wrapper.__actions__);
    -      result.__index__ = wrapper.__index__;
    -      result.__values__ = wrapper.__values__;
    -      return result;
    -    }
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates an array of elements split into groups the length of `size`.
    -     * If `array` can't be split evenly, the final chunk will be the remaining
    -     * elements.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to process.
    -     * @param {number} [size=1] The length of each chunk
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {Array} Returns the new array of chunks.
    -     * @example
    -     *
    -     * _.chunk(['a', 'b', 'c', 'd'], 2);
    -     * // => [['a', 'b'], ['c', 'd']]
    -     *
    -     * _.chunk(['a', 'b', 'c', 'd'], 3);
    -     * // => [['a', 'b', 'c'], ['d']]
    -     */
    -    function chunk(array, size, guard) {
    -      if (guard ? isIterateeCall(array, size, guard) : size === undefined) {
    -        size = 1;
    -      } else {
    -        size = nativeMax(toInteger(size), 0);
    -      }
    -      var length = array == null ? 0 : array.length;
    -      if (!length || size < 1) {
    -        return [];
    -      }
    -      var index = 0,
    -        resIndex = 0,
    -        result = Array(nativeCeil(length / size));
    -      while (index < length) {
    -        result[resIndex++] = baseSlice(array, index, index += size);
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Creates an array with all falsey values removed. The values `false`, `null`,
    -     * `0`, `""`, `undefined`, and `NaN` are falsey.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to compact.
    -     * @returns {Array} Returns the new array of filtered values.
    -     * @example
    -     *
    -     * _.compact([0, 1, false, 2, '', 3]);
    -     * // => [1, 2, 3]
    -     */
    -    function compact(array) {
    -      var index = -1,
    -        length = array == null ? 0 : array.length,
    -        resIndex = 0,
    -        result = [];
    -      while (++index < length) {
    -        var value = array[index];
    -        if (value) {
    -          result[resIndex++] = value;
    -        }
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Creates a new array concatenating `array` with any additional arrays
    -     * and/or values.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to concatenate.
    -     * @param {...*} [values] The values to concatenate.
    -     * @returns {Array} Returns the new concatenated array.
    -     * @example
    -     *
    -     * var array = [1];
    -     * var other = _.concat(array, 2, [3], [[4]]);
    -     *
    -     * console.log(other);
    -     * // => [1, 2, 3, [4]]
    -     *
    -     * console.log(array);
    -     * // => [1]
    -     */
    -    function concat() {
    -      var length = arguments.length;
    -      if (!length) {
    -        return [];
    -      }
    -      var args = Array(length - 1),
    -        array = arguments[0],
    -        index = length;
    -      while (index--) {
    -        args[index - 1] = arguments[index];
    -      }
    -      return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
    -    }
    -
    -    /**
    -     * Creates an array of `array` values not included in the other given arrays
    -     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    -     * for equality comparisons. The order and references of result values are
    -     * determined by the first array.
    -     *
    -     * **Note:** Unlike `_.pullAll`, this method returns a new array.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {...Array} [values] The values to exclude.
    -     * @returns {Array} Returns the new array of filtered values.
    -     * @see _.without, _.xor
    -     * @example
    -     *
    -     * _.difference([2, 1], [2, 3]);
    -     * // => [1]
    -     */
    -    var difference = baseRest(function (array, values) {
    -      return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : [];
    -    });
    -
    -    /**
    -     * This method is like `_.difference` except that it accepts `iteratee` which
    -     * is invoked for each element of `array` and `values` to generate the criterion
    -     * by which they're compared. The order and references of result values are
    -     * determined by the first array. The iteratee is invoked with one argument:
    -     * (value).
    -     *
    -     * **Note:** Unlike `_.pullAllBy`, this method returns a new array.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {...Array} [values] The values to exclude.
    -     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    -     * @returns {Array} Returns the new array of filtered values.
    -     * @example
    -     *
    -     * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);
    -     * // => [1.2]
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
    -     * // => [{ 'x': 2 }]
    -     */
    -    var differenceBy = baseRest(function (array, values) {
    -      var iteratee = last(values);
    -      if (isArrayLikeObject(iteratee)) {
    -        iteratee = undefined;
    -      }
    -      return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) : [];
    -    });
    -
    -    /**
    -     * This method is like `_.difference` except that it accepts `comparator`
    -     * which is invoked to compare elements of `array` to `values`. The order and
    -     * references of result values are determined by the first array. The comparator
    -     * is invoked with two arguments: (arrVal, othVal).
    -     *
    -     * **Note:** Unlike `_.pullAllWith`, this method returns a new array.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {...Array} [values] The values to exclude.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns the new array of filtered values.
    -     * @example
    -     *
    -     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    -     *
    -     * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
    -     * // => [{ 'x': 2, 'y': 1 }]
    -     */
    -    var differenceWith = baseRest(function (array, values) {
    -      var comparator = last(values);
    -      if (isArrayLikeObject(comparator)) {
    -        comparator = undefined;
    -      }
    -      return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) : [];
    -    });
    -
    -    /**
    -     * Creates a slice of `array` with `n` elements dropped from the beginning.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.5.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @param {number} [n=1] The number of elements to drop.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * _.drop([1, 2, 3]);
    -     * // => [2, 3]
    -     *
    -     * _.drop([1, 2, 3], 2);
    -     * // => [3]
    -     *
    -     * _.drop([1, 2, 3], 5);
    -     * // => []
    -     *
    -     * _.drop([1, 2, 3], 0);
    -     * // => [1, 2, 3]
    -     */
    -    function drop(array, n, guard) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return [];
    -      }
    -      n = guard || n === undefined ? 1 : toInteger(n);
    -      return baseSlice(array, n < 0 ? 0 : n, length);
    -    }
    -
    -    /**
    -     * Creates a slice of `array` with `n` elements dropped from the end.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @param {number} [n=1] The number of elements to drop.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * _.dropRight([1, 2, 3]);
    -     * // => [1, 2]
    -     *
    -     * _.dropRight([1, 2, 3], 2);
    -     * // => [1]
    -     *
    -     * _.dropRight([1, 2, 3], 5);
    -     * // => []
    -     *
    -     * _.dropRight([1, 2, 3], 0);
    -     * // => [1, 2, 3]
    -     */
    -    function dropRight(array, n, guard) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return [];
    -      }
    -      n = guard || n === undefined ? 1 : toInteger(n);
    -      n = length - n;
    -      return baseSlice(array, 0, n < 0 ? 0 : n);
    -    }
    -
    -    /**
    -     * Creates a slice of `array` excluding elements dropped from the end.
    -     * Elements are dropped until `predicate` returns falsey. The predicate is
    -     * invoked with three arguments: (value, index, array).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney',  'active': true },
    -     *   { 'user': 'fred',    'active': false },
    -     *   { 'user': 'pebbles', 'active': false }
    -     * ];
    -     *
    -     * _.dropRightWhile(users, function(o) { return !o.active; });
    -     * // => objects for ['barney']
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });
    -     * // => objects for ['barney', 'fred']
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.dropRightWhile(users, ['active', false]);
    -     * // => objects for ['barney']
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.dropRightWhile(users, 'active');
    -     * // => objects for ['barney', 'fred', 'pebbles']
    -     */
    -    function dropRightWhile(array, predicate) {
    -      return array && array.length ? baseWhile(array, getIteratee(predicate, 3), true, true) : [];
    -    }
    -
    -    /**
    -     * Creates a slice of `array` excluding elements dropped from the beginning.
    -     * Elements are dropped until `predicate` returns falsey. The predicate is
    -     * invoked with three arguments: (value, index, array).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney',  'active': false },
    -     *   { 'user': 'fred',    'active': false },
    -     *   { 'user': 'pebbles', 'active': true }
    -     * ];
    -     *
    -     * _.dropWhile(users, function(o) { return !o.active; });
    -     * // => objects for ['pebbles']
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.dropWhile(users, { 'user': 'barney', 'active': false });
    -     * // => objects for ['fred', 'pebbles']
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.dropWhile(users, ['active', false]);
    -     * // => objects for ['pebbles']
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.dropWhile(users, 'active');
    -     * // => objects for ['barney', 'fred', 'pebbles']
    -     */
    -    function dropWhile(array, predicate) {
    -      return array && array.length ? baseWhile(array, getIteratee(predicate, 3), true) : [];
    -    }
    -
    -    /**
    -     * Fills elements of `array` with `value` from `start` up to, but not
    -     * including, `end`.
    -     *
    -     * **Note:** This method mutates `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.2.0
    -     * @category Array
    -     * @param {Array} array The array to fill.
    -     * @param {*} value The value to fill `array` with.
    -     * @param {number} [start=0] The start position.
    -     * @param {number} [end=array.length] The end position.
    -     * @returns {Array} Returns `array`.
    -     * @example
    -     *
    -     * var array = [1, 2, 3];
    -     *
    -     * _.fill(array, 'a');
    -     * console.log(array);
    -     * // => ['a', 'a', 'a']
    -     *
    -     * _.fill(Array(3), 2);
    -     * // => [2, 2, 2]
    -     *
    -     * _.fill([4, 6, 8, 10], '*', 1, 3);
    -     * // => [4, '*', '*', 10]
    -     */
    -    function fill(array, value, start, end) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return [];
    -      }
    -      if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {
    -        start = 0;
    -        end = length;
    -      }
    -      return baseFill(array, value, start, end);
    -    }
    -
    -    /**
    -     * This method is like `_.find` except that it returns the index of the first
    -     * element `predicate` returns truthy for instead of the element itself.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 1.1.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @param {number} [fromIndex=0] The index to search from.
    -     * @returns {number} Returns the index of the found element, else `-1`.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney',  'active': false },
    -     *   { 'user': 'fred',    'active': false },
    -     *   { 'user': 'pebbles', 'active': true }
    -     * ];
    -     *
    -     * _.findIndex(users, function(o) { return o.user == 'barney'; });
    -     * // => 0
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.findIndex(users, { 'user': 'fred', 'active': false });
    -     * // => 1
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.findIndex(users, ['active', false]);
    -     * // => 0
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.findIndex(users, 'active');
    -     * // => 2
    -     */
    -    function findIndex(array, predicate, fromIndex) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return -1;
    -      }
    -      var index = fromIndex == null ? 0 : toInteger(fromIndex);
    -      if (index < 0) {
    -        index = nativeMax(length + index, 0);
    -      }
    -      return baseFindIndex(array, getIteratee(predicate, 3), index);
    -    }
    -
    -    /**
    -     * This method is like `_.findIndex` except that it iterates over elements
    -     * of `collection` from right to left.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 2.0.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @param {number} [fromIndex=array.length-1] The index to search from.
    -     * @returns {number} Returns the index of the found element, else `-1`.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney',  'active': true },
    -     *   { 'user': 'fred',    'active': false },
    -     *   { 'user': 'pebbles', 'active': false }
    -     * ];
    -     *
    -     * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
    -     * // => 2
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.findLastIndex(users, { 'user': 'barney', 'active': true });
    -     * // => 0
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.findLastIndex(users, ['active', false]);
    -     * // => 2
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.findLastIndex(users, 'active');
    -     * // => 0
    -     */
    -    function findLastIndex(array, predicate, fromIndex) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return -1;
    -      }
    -      var index = length - 1;
    -      if (fromIndex !== undefined) {
    -        index = toInteger(fromIndex);
    -        index = fromIndex < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
    -      }
    -      return baseFindIndex(array, getIteratee(predicate, 3), index, true);
    -    }
    -
    -    /**
    -     * Flattens `array` a single level deep.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to flatten.
    -     * @returns {Array} Returns the new flattened array.
    -     * @example
    -     *
    -     * _.flatten([1, [2, [3, [4]], 5]]);
    -     * // => [1, 2, [3, [4]], 5]
    -     */
    -    function flatten(array) {
    -      var length = array == null ? 0 : array.length;
    -      return length ? baseFlatten(array, 1) : [];
    -    }
    -
    -    /**
    -     * Recursively flattens `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to flatten.
    -     * @returns {Array} Returns the new flattened array.
    -     * @example
    -     *
    -     * _.flattenDeep([1, [2, [3, [4]], 5]]);
    -     * // => [1, 2, 3, 4, 5]
    -     */
    -    function flattenDeep(array) {
    -      var length = array == null ? 0 : array.length;
    -      return length ? baseFlatten(array, INFINITY) : [];
    -    }
    -
    -    /**
    -     * Recursively flatten `array` up to `depth` times.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.4.0
    -     * @category Array
    -     * @param {Array} array The array to flatten.
    -     * @param {number} [depth=1] The maximum recursion depth.
    -     * @returns {Array} Returns the new flattened array.
    -     * @example
    -     *
    -     * var array = [1, [2, [3, [4]], 5]];
    -     *
    -     * _.flattenDepth(array, 1);
    -     * // => [1, 2, [3, [4]], 5]
    -     *
    -     * _.flattenDepth(array, 2);
    -     * // => [1, 2, 3, [4], 5]
    -     */
    -    function flattenDepth(array, depth) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return [];
    -      }
    -      depth = depth === undefined ? 1 : toInteger(depth);
    -      return baseFlatten(array, depth);
    -    }
    -
    -    /**
    -     * The inverse of `_.toPairs`; this method returns an object composed
    -     * from key-value `pairs`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} pairs The key-value pairs.
    -     * @returns {Object} Returns the new object.
    -     * @example
    -     *
    -     * _.fromPairs([['a', 1], ['b', 2]]);
    -     * // => { 'a': 1, 'b': 2 }
    -     */
    -    function fromPairs(pairs) {
    -      var index = -1,
    -        length = pairs == null ? 0 : pairs.length,
    -        result = {};
    -      while (++index < length) {
    -        var pair = pairs[index];
    -        result[pair[0]] = pair[1];
    -      }
    -      return result;
    -    }
    -
    -    /**
    -     * Gets the first element of `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @alias first
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @returns {*} Returns the first element of `array`.
    -     * @example
    -     *
    -     * _.head([1, 2, 3]);
    -     * // => 1
    -     *
    -     * _.head([]);
    -     * // => undefined
    -     */
    -    function head(array) {
    -      return array && array.length ? array[0] : undefined;
    -    }
    -
    -    /**
    -     * Gets the index at which the first occurrence of `value` is found in `array`
    -     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    -     * for equality comparisons. If `fromIndex` is negative, it's used as the
    -     * offset from the end of `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {*} value The value to search for.
    -     * @param {number} [fromIndex=0] The index to search from.
    -     * @returns {number} Returns the index of the matched value, else `-1`.
    -     * @example
    -     *
    -     * _.indexOf([1, 2, 1, 2], 2);
    -     * // => 1
    -     *
    -     * // Search from the `fromIndex`.
    -     * _.indexOf([1, 2, 1, 2], 2, 2);
    -     * // => 3
    -     */
    -    function indexOf(array, value, fromIndex) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return -1;
    -      }
    -      var index = fromIndex == null ? 0 : toInteger(fromIndex);
    -      if (index < 0) {
    -        index = nativeMax(length + index, 0);
    -      }
    -      return baseIndexOf(array, value, index);
    -    }
    -
    -    /**
    -     * Gets all but the last element of `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * _.initial([1, 2, 3]);
    -     * // => [1, 2]
    -     */
    -    function initial(array) {
    -      var length = array == null ? 0 : array.length;
    -      return length ? baseSlice(array, 0, -1) : [];
    -    }
    -
    -    /**
    -     * Creates an array of unique values that are included in all given arrays
    -     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    -     * for equality comparisons. The order and references of result values are
    -     * determined by the first array.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to inspect.
    -     * @returns {Array} Returns the new array of intersecting values.
    -     * @example
    -     *
    -     * _.intersection([2, 1], [2, 3]);
    -     * // => [2]
    -     */
    -    var intersection = baseRest(function (arrays) {
    -      var mapped = arrayMap(arrays, castArrayLikeObject);
    -      return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped) : [];
    -    });
    -
    -    /**
    -     * This method is like `_.intersection` except that it accepts `iteratee`
    -     * which is invoked for each element of each `arrays` to generate the criterion
    -     * by which they're compared. The order and references of result values are
    -     * determined by the first array. The iteratee is invoked with one argument:
    -     * (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to inspect.
    -     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    -     * @returns {Array} Returns the new array of intersecting values.
    -     * @example
    -     *
    -     * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);
    -     * // => [2.1]
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
    -     * // => [{ 'x': 1 }]
    -     */
    -    var intersectionBy = baseRest(function (arrays) {
    -      var iteratee = last(arrays),
    -        mapped = arrayMap(arrays, castArrayLikeObject);
    -      if (iteratee === last(mapped)) {
    -        iteratee = undefined;
    -      } else {
    -        mapped.pop();
    -      }
    -      return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, getIteratee(iteratee, 2)) : [];
    -    });
    -
    -    /**
    -     * This method is like `_.intersection` except that it accepts `comparator`
    -     * which is invoked to compare elements of `arrays`. The order and references
    -     * of result values are determined by the first array. The comparator is
    -     * invoked with two arguments: (arrVal, othVal).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to inspect.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns the new array of intersecting values.
    -     * @example
    -     *
    -     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    -     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
    -     *
    -     * _.intersectionWith(objects, others, _.isEqual);
    -     * // => [{ 'x': 1, 'y': 2 }]
    -     */
    -    var intersectionWith = baseRest(function (arrays) {
    -      var comparator = last(arrays),
    -        mapped = arrayMap(arrays, castArrayLikeObject);
    -      comparator = typeof comparator == 'function' ? comparator : undefined;
    -      if (comparator) {
    -        mapped.pop();
    -      }
    -      return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, undefined, comparator) : [];
    -    });
    -
    -    /**
    -     * Converts all elements in `array` into a string separated by `separator`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to convert.
    -     * @param {string} [separator=','] The element separator.
    -     * @returns {string} Returns the joined string.
    -     * @example
    -     *
    -     * _.join(['a', 'b', 'c'], '~');
    -     * // => 'a~b~c'
    -     */
    -    function join(array, separator) {
    -      return array == null ? '' : nativeJoin.call(array, separator);
    -    }
    -
    -    /**
    -     * Gets the last element of `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @returns {*} Returns the last element of `array`.
    -     * @example
    -     *
    -     * _.last([1, 2, 3]);
    -     * // => 3
    -     */
    -    function last(array) {
    -      var length = array == null ? 0 : array.length;
    -      return length ? array[length - 1] : undefined;
    -    }
    -
    -    /**
    -     * This method is like `_.indexOf` except that it iterates over elements of
    -     * `array` from right to left.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {*} value The value to search for.
    -     * @param {number} [fromIndex=array.length-1] The index to search from.
    -     * @returns {number} Returns the index of the matched value, else `-1`.
    -     * @example
    -     *
    -     * _.lastIndexOf([1, 2, 1, 2], 2);
    -     * // => 3
    -     *
    -     * // Search from the `fromIndex`.
    -     * _.lastIndexOf([1, 2, 1, 2], 2, 2);
    -     * // => 1
    -     */
    -    function lastIndexOf(array, value, fromIndex) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return -1;
    -      }
    -      var index = length;
    -      if (fromIndex !== undefined) {
    -        index = toInteger(fromIndex);
    -        index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
    -      }
    -      return value === value ? strictLastIndexOf(array, value, index) : baseFindIndex(array, baseIsNaN, index, true);
    -    }
    -
    -    /**
    -     * Gets the element at index `n` of `array`. If `n` is negative, the nth
    -     * element from the end is returned.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.11.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @param {number} [n=0] The index of the element to return.
    -     * @returns {*} Returns the nth element of `array`.
    -     * @example
    -     *
    -     * var array = ['a', 'b', 'c', 'd'];
    -     *
    -     * _.nth(array, 1);
    -     * // => 'b'
    -     *
    -     * _.nth(array, -2);
    -     * // => 'c';
    -     */
    -    function nth(array, n) {
    -      return array && array.length ? baseNth(array, toInteger(n)) : undefined;
    -    }
    -
    -    /**
    -     * Removes all given values from `array` using
    -     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    -     * for equality comparisons.
    -     *
    -     * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`
    -     * to remove elements from an array by predicate.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 2.0.0
    -     * @category Array
    -     * @param {Array} array The array to modify.
    -     * @param {...*} [values] The values to remove.
    -     * @returns {Array} Returns `array`.
    -     * @example
    -     *
    -     * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
    -     *
    -     * _.pull(array, 'a', 'c');
    -     * console.log(array);
    -     * // => ['b', 'b']
    -     */
    -    var pull = baseRest(pullAll);
    -
    -    /**
    -     * This method is like `_.pull` except that it accepts an array of values to remove.
    -     *
    -     * **Note:** Unlike `_.difference`, this method mutates `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to modify.
    -     * @param {Array} values The values to remove.
    -     * @returns {Array} Returns `array`.
    -     * @example
    -     *
    -     * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
    -     *
    -     * _.pullAll(array, ['a', 'c']);
    -     * console.log(array);
    -     * // => ['b', 'b']
    -     */
    -    function pullAll(array, values) {
    -      return array && array.length && values && values.length ? basePullAll(array, values) : array;
    -    }
    -
    -    /**
    -     * This method is like `_.pullAll` except that it accepts `iteratee` which is
    -     * invoked for each element of `array` and `values` to generate the criterion
    -     * by which they're compared. The iteratee is invoked with one argument: (value).
    -     *
    -     * **Note:** Unlike `_.differenceBy`, this method mutates `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to modify.
    -     * @param {Array} values The values to remove.
    -     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    -     * @returns {Array} Returns `array`.
    -     * @example
    -     *
    -     * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
    -     *
    -     * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
    -     * console.log(array);
    -     * // => [{ 'x': 2 }]
    -     */
    -    function pullAllBy(array, values, iteratee) {
    -      return array && array.length && values && values.length ? basePullAll(array, values, getIteratee(iteratee, 2)) : array;
    -    }
    -
    -    /**
    -     * This method is like `_.pullAll` except that it accepts `comparator` which
    -     * is invoked to compare elements of `array` to `values`. The comparator is
    -     * invoked with two arguments: (arrVal, othVal).
    -     *
    -     * **Note:** Unlike `_.differenceWith`, this method mutates `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.6.0
    -     * @category Array
    -     * @param {Array} array The array to modify.
    -     * @param {Array} values The values to remove.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns `array`.
    -     * @example
    -     *
    -     * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];
    -     *
    -     * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);
    -     * console.log(array);
    -     * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
    -     */
    -    function pullAllWith(array, values, comparator) {
    -      return array && array.length && values && values.length ? basePullAll(array, values, undefined, comparator) : array;
    -    }
    -
    -    /**
    -     * Removes elements from `array` corresponding to `indexes` and returns an
    -     * array of removed elements.
    -     *
    -     * **Note:** Unlike `_.at`, this method mutates `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to modify.
    -     * @param {...(number|number[])} [indexes] The indexes of elements to remove.
    -     * @returns {Array} Returns the new array of removed elements.
    -     * @example
    -     *
    -     * var array = ['a', 'b', 'c', 'd'];
    -     * var pulled = _.pullAt(array, [1, 3]);
    -     *
    -     * console.log(array);
    -     * // => ['a', 'c']
    -     *
    -     * console.log(pulled);
    -     * // => ['b', 'd']
    -     */
    -    var pullAt = flatRest(function (array, indexes) {
    -      var length = array == null ? 0 : array.length,
    -        result = baseAt(array, indexes);
    -      basePullAt(array, arrayMap(indexes, function (index) {
    -        return isIndex(index, length) ? +index : index;
    -      }).sort(compareAscending));
    -      return result;
    -    });
    -
    -    /**
    -     * Removes all elements from `array` that `predicate` returns truthy for
    -     * and returns an array of the removed elements. The predicate is invoked
    -     * with three arguments: (value, index, array).
    -     *
    -     * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`
    -     * to pull elements from an array by value.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 2.0.0
    -     * @category Array
    -     * @param {Array} array The array to modify.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the new array of removed elements.
    -     * @example
    -     *
    -     * var array = [1, 2, 3, 4];
    -     * var evens = _.remove(array, function(n) {
    -     *   return n % 2 == 0;
    -     * });
    -     *
    -     * console.log(array);
    -     * // => [1, 3]
    -     *
    -     * console.log(evens);
    -     * // => [2, 4]
    -     */
    -    function remove(array, predicate) {
    -      var result = [];
    -      if (!(array && array.length)) {
    -        return result;
    -      }
    -      var index = -1,
    -        indexes = [],
    -        length = array.length;
    -      predicate = getIteratee(predicate, 3);
    -      while (++index < length) {
    -        var value = array[index];
    -        if (predicate(value, index, array)) {
    -          result.push(value);
    -          indexes.push(index);
    -        }
    -      }
    -      basePullAt(array, indexes);
    -      return result;
    -    }
    -
    -    /**
    -     * Reverses `array` so that the first element becomes the last, the second
    -     * element becomes the second to last, and so on.
    -     *
    -     * **Note:** This method mutates `array` and is based on
    -     * [`Array#reverse`](https://mdn.io/Array/reverse).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to modify.
    -     * @returns {Array} Returns `array`.
    -     * @example
    -     *
    -     * var array = [1, 2, 3];
    -     *
    -     * _.reverse(array);
    -     * // => [3, 2, 1]
    -     *
    -     * console.log(array);
    -     * // => [3, 2, 1]
    -     */
    -    function reverse(array) {
    -      return array == null ? array : nativeReverse.call(array);
    -    }
    -
    -    /**
    -     * Creates a slice of `array` from `start` up to, but not including, `end`.
    -     *
    -     * **Note:** This method is used instead of
    -     * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are
    -     * returned.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to slice.
    -     * @param {number} [start=0] The start position.
    -     * @param {number} [end=array.length] The end position.
    -     * @returns {Array} Returns the slice of `array`.
    -     */
    -    function slice(array, start, end) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return [];
    -      }
    -      if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
    -        start = 0;
    -        end = length;
    -      } else {
    -        start = start == null ? 0 : toInteger(start);
    -        end = end === undefined ? length : toInteger(end);
    -      }
    -      return baseSlice(array, start, end);
    -    }
    -
    -    /**
    -     * Uses a binary search to determine the lowest index at which `value`
    -     * should be inserted into `array` in order to maintain its sort order.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The sorted array to inspect.
    -     * @param {*} value The value to evaluate.
    -     * @returns {number} Returns the index at which `value` should be inserted
    -     *  into `array`.
    -     * @example
    -     *
    -     * _.sortedIndex([30, 50], 40);
    -     * // => 1
    -     */
    -    function sortedIndex(array, value) {
    -      return baseSortedIndex(array, value);
    -    }
    -
    -    /**
    -     * This method is like `_.sortedIndex` except that it accepts `iteratee`
    -     * which is invoked for `value` and each element of `array` to compute their
    -     * sort ranking. The iteratee is invoked with one argument: (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The sorted array to inspect.
    -     * @param {*} value The value to evaluate.
    -     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    -     * @returns {number} Returns the index at which `value` should be inserted
    -     *  into `array`.
    -     * @example
    -     *
    -     * var objects = [{ 'x': 4 }, { 'x': 5 }];
    -     *
    -     * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
    -     * // => 0
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.sortedIndexBy(objects, { 'x': 4 }, 'x');
    -     * // => 0
    -     */
    -    function sortedIndexBy(array, value, iteratee) {
    -      return baseSortedIndexBy(array, value, getIteratee(iteratee, 2));
    -    }
    -
    -    /**
    -     * This method is like `_.indexOf` except that it performs a binary
    -     * search on a sorted `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {*} value The value to search for.
    -     * @returns {number} Returns the index of the matched value, else `-1`.
    -     * @example
    -     *
    -     * _.sortedIndexOf([4, 5, 5, 5, 6], 5);
    -     * // => 1
    -     */
    -    function sortedIndexOf(array, value) {
    -      var length = array == null ? 0 : array.length;
    -      if (length) {
    -        var index = baseSortedIndex(array, value);
    -        if (index < length && eq(array[index], value)) {
    -          return index;
    -        }
    -      }
    -      return -1;
    -    }
    -
    -    /**
    -     * This method is like `_.sortedIndex` except that it returns the highest
    -     * index at which `value` should be inserted into `array` in order to
    -     * maintain its sort order.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The sorted array to inspect.
    -     * @param {*} value The value to evaluate.
    -     * @returns {number} Returns the index at which `value` should be inserted
    -     *  into `array`.
    -     * @example
    -     *
    -     * _.sortedLastIndex([4, 5, 5, 5, 6], 5);
    -     * // => 4
    -     */
    -    function sortedLastIndex(array, value) {
    -      return baseSortedIndex(array, value, true);
    -    }
    -
    -    /**
    -     * This method is like `_.sortedLastIndex` except that it accepts `iteratee`
    -     * which is invoked for `value` and each element of `array` to compute their
    -     * sort ranking. The iteratee is invoked with one argument: (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The sorted array to inspect.
    -     * @param {*} value The value to evaluate.
    -     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    -     * @returns {number} Returns the index at which `value` should be inserted
    -     *  into `array`.
    -     * @example
    -     *
    -     * var objects = [{ 'x': 4 }, { 'x': 5 }];
    -     *
    -     * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
    -     * // => 1
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');
    -     * // => 1
    -     */
    -    function sortedLastIndexBy(array, value, iteratee) {
    -      return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true);
    -    }
    -
    -    /**
    -     * This method is like `_.lastIndexOf` except that it performs a binary
    -     * search on a sorted `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {*} value The value to search for.
    -     * @returns {number} Returns the index of the matched value, else `-1`.
    -     * @example
    -     *
    -     * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);
    -     * // => 3
    -     */
    -    function sortedLastIndexOf(array, value) {
    -      var length = array == null ? 0 : array.length;
    -      if (length) {
    -        var index = baseSortedIndex(array, value, true) - 1;
    -        if (eq(array[index], value)) {
    -          return index;
    -        }
    -      }
    -      return -1;
    -    }
    -
    -    /**
    -     * This method is like `_.uniq` except that it's designed and optimized
    -     * for sorted arrays.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @returns {Array} Returns the new duplicate free array.
    -     * @example
    -     *
    -     * _.sortedUniq([1, 1, 2]);
    -     * // => [1, 2]
    -     */
    -    function sortedUniq(array) {
    -      return array && array.length ? baseSortedUniq(array) : [];
    -    }
    -
    -    /**
    -     * This method is like `_.uniqBy` except that it's designed and optimized
    -     * for sorted arrays.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {Function} [iteratee] The iteratee invoked per element.
    -     * @returns {Array} Returns the new duplicate free array.
    -     * @example
    -     *
    -     * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
    -     * // => [1.1, 2.3]
    -     */
    -    function sortedUniqBy(array, iteratee) {
    -      return array && array.length ? baseSortedUniq(array, getIteratee(iteratee, 2)) : [];
    -    }
    -
    -    /**
    -     * Gets all but the first element of `array`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * _.tail([1, 2, 3]);
    -     * // => [2, 3]
    -     */
    -    function tail(array) {
    -      var length = array == null ? 0 : array.length;
    -      return length ? baseSlice(array, 1, length) : [];
    -    }
    -
    -    /**
    -     * Creates a slice of `array` with `n` elements taken from the beginning.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @param {number} [n=1] The number of elements to take.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * _.take([1, 2, 3]);
    -     * // => [1]
    -     *
    -     * _.take([1, 2, 3], 2);
    -     * // => [1, 2]
    -     *
    -     * _.take([1, 2, 3], 5);
    -     * // => [1, 2, 3]
    -     *
    -     * _.take([1, 2, 3], 0);
    -     * // => []
    -     */
    -    function take(array, n, guard) {
    -      if (!(array && array.length)) {
    -        return [];
    -      }
    -      n = guard || n === undefined ? 1 : toInteger(n);
    -      return baseSlice(array, 0, n < 0 ? 0 : n);
    -    }
    -
    -    /**
    -     * Creates a slice of `array` with `n` elements taken from the end.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @param {number} [n=1] The number of elements to take.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * _.takeRight([1, 2, 3]);
    -     * // => [3]
    -     *
    -     * _.takeRight([1, 2, 3], 2);
    -     * // => [2, 3]
    -     *
    -     * _.takeRight([1, 2, 3], 5);
    -     * // => [1, 2, 3]
    -     *
    -     * _.takeRight([1, 2, 3], 0);
    -     * // => []
    -     */
    -    function takeRight(array, n, guard) {
    -      var length = array == null ? 0 : array.length;
    -      if (!length) {
    -        return [];
    -      }
    -      n = guard || n === undefined ? 1 : toInteger(n);
    -      n = length - n;
    -      return baseSlice(array, n < 0 ? 0 : n, length);
    -    }
    -
    -    /**
    -     * Creates a slice of `array` with elements taken from the end. Elements are
    -     * taken until `predicate` returns falsey. The predicate is invoked with
    -     * three arguments: (value, index, array).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney',  'active': true },
    -     *   { 'user': 'fred',    'active': false },
    -     *   { 'user': 'pebbles', 'active': false }
    -     * ];
    -     *
    -     * _.takeRightWhile(users, function(o) { return !o.active; });
    -     * // => objects for ['fred', 'pebbles']
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });
    -     * // => objects for ['pebbles']
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.takeRightWhile(users, ['active', false]);
    -     * // => objects for ['fred', 'pebbles']
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.takeRightWhile(users, 'active');
    -     * // => []
    -     */
    -    function takeRightWhile(array, predicate) {
    -      return array && array.length ? baseWhile(array, getIteratee(predicate, 3), false, true) : [];
    -    }
    -
    -    /**
    -     * Creates a slice of `array` with elements taken from the beginning. Elements
    -     * are taken until `predicate` returns falsey. The predicate is invoked with
    -     * three arguments: (value, index, array).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Array
    -     * @param {Array} array The array to query.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the slice of `array`.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney',  'active': false },
    -     *   { 'user': 'fred',    'active': false },
    -     *   { 'user': 'pebbles', 'active': true }
    -     * ];
    -     *
    -     * _.takeWhile(users, function(o) { return !o.active; });
    -     * // => objects for ['barney', 'fred']
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.takeWhile(users, { 'user': 'barney', 'active': false });
    -     * // => objects for ['barney']
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.takeWhile(users, ['active', false]);
    -     * // => objects for ['barney', 'fred']
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.takeWhile(users, 'active');
    -     * // => []
    -     */
    -    function takeWhile(array, predicate) {
    -      return array && array.length ? baseWhile(array, getIteratee(predicate, 3)) : [];
    -    }
    -
    -    /**
    -     * Creates an array of unique values, in order, from all given arrays using
    -     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    -     * for equality comparisons.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to inspect.
    -     * @returns {Array} Returns the new array of combined values.
    -     * @example
    -     *
    -     * _.union([2], [1, 2]);
    -     * // => [2, 1]
    -     */
    -    var union = baseRest(function (arrays) {
    -      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
    -    });
    -
    -    /**
    -     * This method is like `_.union` except that it accepts `iteratee` which is
    -     * invoked for each element of each `arrays` to generate the criterion by
    -     * which uniqueness is computed. Result values are chosen from the first
    -     * array in which the value occurs. The iteratee is invoked with one argument:
    -     * (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to inspect.
    -     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    -     * @returns {Array} Returns the new array of combined values.
    -     * @example
    -     *
    -     * _.unionBy([2.1], [1.2, 2.3], Math.floor);
    -     * // => [2.1, 1.2]
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
    -     * // => [{ 'x': 1 }, { 'x': 2 }]
    -     */
    -    var unionBy = baseRest(function (arrays) {
    -      var iteratee = last(arrays);
    -      if (isArrayLikeObject(iteratee)) {
    -        iteratee = undefined;
    -      }
    -      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2));
    -    });
    -
    -    /**
    -     * This method is like `_.union` except that it accepts `comparator` which
    -     * is invoked to compare elements of `arrays`. Result values are chosen from
    -     * the first array in which the value occurs. The comparator is invoked
    -     * with two arguments: (arrVal, othVal).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to inspect.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns the new array of combined values.
    -     * @example
    -     *
    -     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    -     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
    -     *
    -     * _.unionWith(objects, others, _.isEqual);
    -     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
    -     */
    -    var unionWith = baseRest(function (arrays) {
    -      var comparator = last(arrays);
    -      comparator = typeof comparator == 'function' ? comparator : undefined;
    -      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);
    -    });
    -
    -    /**
    -     * Creates a duplicate-free version of an array, using
    -     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    -     * for equality comparisons, in which only the first occurrence of each element
    -     * is kept. The order of result values is determined by the order they occur
    -     * in the array.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @returns {Array} Returns the new duplicate free array.
    -     * @example
    -     *
    -     * _.uniq([2, 1, 2]);
    -     * // => [2, 1]
    -     */
    -    function uniq(array) {
    -      return array && array.length ? baseUniq(array) : [];
    -    }
    -
    -    /**
    -     * This method is like `_.uniq` except that it accepts `iteratee` which is
    -     * invoked for each element in `array` to generate the criterion by which
    -     * uniqueness is computed. The order of result values is determined by the
    -     * order they occur in the array. The iteratee is invoked with one argument:
    -     * (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    -     * @returns {Array} Returns the new duplicate free array.
    -     * @example
    -     *
    -     * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
    -     * // => [2.1, 1.2]
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
    -     * // => [{ 'x': 1 }, { 'x': 2 }]
    -     */
    -    function uniqBy(array, iteratee) {
    -      return array && array.length ? baseUniq(array, getIteratee(iteratee, 2)) : [];
    -    }
    -
    -    /**
    -     * This method is like `_.uniq` except that it accepts `comparator` which
    -     * is invoked to compare elements of `array`. The order of result values is
    -     * determined by the order they occur in the array.The comparator is invoked
    -     * with two arguments: (arrVal, othVal).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns the new duplicate free array.
    -     * @example
    -     *
    -     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
    -     *
    -     * _.uniqWith(objects, _.isEqual);
    -     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
    -     */
    -    function uniqWith(array, comparator) {
    -      comparator = typeof comparator == 'function' ? comparator : undefined;
    -      return array && array.length ? baseUniq(array, undefined, comparator) : [];
    -    }
    -
    -    /**
    -     * This method is like `_.zip` except that it accepts an array of grouped
    -     * elements and creates an array regrouping the elements to their pre-zip
    -     * configuration.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 1.2.0
    -     * @category Array
    -     * @param {Array} array The array of grouped elements to process.
    -     * @returns {Array} Returns the new array of regrouped elements.
    -     * @example
    -     *
    -     * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);
    -     * // => [['a', 1, true], ['b', 2, false]]
    -     *
    -     * _.unzip(zipped);
    -     * // => [['a', 'b'], [1, 2], [true, false]]
    -     */
    -    function unzip(array) {
    -      if (!(array && array.length)) {
    -        return [];
    -      }
    -      var length = 0;
    -      array = arrayFilter(array, function (group) {
    -        if (isArrayLikeObject(group)) {
    -          length = nativeMax(group.length, length);
    -          return true;
    -        }
    -      });
    -      return baseTimes(length, function (index) {
    -        return arrayMap(array, baseProperty(index));
    -      });
    -    }
    -
    -    /**
    -     * This method is like `_.unzip` except that it accepts `iteratee` to specify
    -     * how regrouped values should be combined. The iteratee is invoked with the
    -     * elements of each group: (...group).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.8.0
    -     * @category Array
    -     * @param {Array} array The array of grouped elements to process.
    -     * @param {Function} [iteratee=_.identity] The function to combine
    -     *  regrouped values.
    -     * @returns {Array} Returns the new array of regrouped elements.
    -     * @example
    -     *
    -     * var zipped = _.zip([1, 2], [10, 20], [100, 200]);
    -     * // => [[1, 10, 100], [2, 20, 200]]
    -     *
    -     * _.unzipWith(zipped, _.add);
    -     * // => [3, 30, 300]
    -     */
    -    function unzipWith(array, iteratee) {
    -      if (!(array && array.length)) {
    -        return [];
    -      }
    -      var result = unzip(array);
    -      if (iteratee == null) {
    -        return result;
    -      }
    -      return arrayMap(result, function (group) {
    -        return apply(iteratee, undefined, group);
    -      });
    -    }
    -
    -    /**
    -     * Creates an array excluding all given values using
    -     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    -     * for equality comparisons.
    -     *
    -     * **Note:** Unlike `_.pull`, this method returns a new array.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {Array} array The array to inspect.
    -     * @param {...*} [values] The values to exclude.
    -     * @returns {Array} Returns the new array of filtered values.
    -     * @see _.difference, _.xor
    -     * @example
    -     *
    -     * _.without([2, 1, 2, 3], 1, 2);
    -     * // => [3]
    -     */
    -    var without = baseRest(function (array, values) {
    -      return isArrayLikeObject(array) ? baseDifference(array, values) : [];
    -    });
    -
    -    /**
    -     * Creates an array of unique values that is the
    -     * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)
    -     * of the given arrays. The order of result values is determined by the order
    -     * they occur in the arrays.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 2.4.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to inspect.
    -     * @returns {Array} Returns the new array of filtered values.
    -     * @see _.difference, _.without
    -     * @example
    -     *
    -     * _.xor([2, 1], [2, 3]);
    -     * // => [1, 3]
    -     */
    -    var xor = baseRest(function (arrays) {
    -      return baseXor(arrayFilter(arrays, isArrayLikeObject));
    -    });
    -
    -    /**
    -     * This method is like `_.xor` except that it accepts `iteratee` which is
    -     * invoked for each element of each `arrays` to generate the criterion by
    -     * which by which they're compared. The order of result values is determined
    -     * by the order they occur in the arrays. The iteratee is invoked with one
    -     * argument: (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to inspect.
    -     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
    -     * @returns {Array} Returns the new array of filtered values.
    -     * @example
    -     *
    -     * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
    -     * // => [1.2, 3.4]
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
    -     * // => [{ 'x': 2 }]
    -     */
    -    var xorBy = baseRest(function (arrays) {
    -      var iteratee = last(arrays);
    -      if (isArrayLikeObject(iteratee)) {
    -        iteratee = undefined;
    -      }
    -      return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2));
    -    });
    -
    -    /**
    -     * This method is like `_.xor` except that it accepts `comparator` which is
    -     * invoked to compare elements of `arrays`. The order of result values is
    -     * determined by the order they occur in the arrays. The comparator is invoked
    -     * with two arguments: (arrVal, othVal).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to inspect.
    -     * @param {Function} [comparator] The comparator invoked per element.
    -     * @returns {Array} Returns the new array of filtered values.
    -     * @example
    -     *
    -     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
    -     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
    -     *
    -     * _.xorWith(objects, others, _.isEqual);
    -     * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
    -     */
    -    var xorWith = baseRest(function (arrays) {
    -      var comparator = last(arrays);
    -      comparator = typeof comparator == 'function' ? comparator : undefined;
    -      return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);
    -    });
    -
    -    /**
    -     * Creates an array of grouped elements, the first of which contains the
    -     * first elements of the given arrays, the second of which contains the
    -     * second elements of the given arrays, and so on.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to process.
    -     * @returns {Array} Returns the new array of grouped elements.
    -     * @example
    -     *
    -     * _.zip(['a', 'b'], [1, 2], [true, false]);
    -     * // => [['a', 1, true], ['b', 2, false]]
    -     */
    -    var zip = baseRest(unzip);
    -
    -    /**
    -     * This method is like `_.fromPairs` except that it accepts two arrays,
    -     * one of property identifiers and one of corresponding values.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.4.0
    -     * @category Array
    -     * @param {Array} [props=[]] The property identifiers.
    -     * @param {Array} [values=[]] The property values.
    -     * @returns {Object} Returns the new object.
    -     * @example
    -     *
    -     * _.zipObject(['a', 'b'], [1, 2]);
    -     * // => { 'a': 1, 'b': 2 }
    -     */
    -    function zipObject(props, values) {
    -      return baseZipObject(props || [], values || [], assignValue);
    -    }
    -
    -    /**
    -     * This method is like `_.zipObject` except that it supports property paths.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.1.0
    -     * @category Array
    -     * @param {Array} [props=[]] The property identifiers.
    -     * @param {Array} [values=[]] The property values.
    -     * @returns {Object} Returns the new object.
    -     * @example
    -     *
    -     * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
    -     * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
    -     */
    -    function zipObjectDeep(props, values) {
    -      return baseZipObject(props || [], values || [], baseSet);
    -    }
    -
    -    /**
    -     * This method is like `_.zip` except that it accepts `iteratee` to specify
    -     * how grouped values should be combined. The iteratee is invoked with the
    -     * elements of each group: (...group).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.8.0
    -     * @category Array
    -     * @param {...Array} [arrays] The arrays to process.
    -     * @param {Function} [iteratee=_.identity] The function to combine
    -     *  grouped values.
    -     * @returns {Array} Returns the new array of grouped elements.
    -     * @example
    -     *
    -     * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {
    -     *   return a + b + c;
    -     * });
    -     * // => [111, 222]
    -     */
    -    var zipWith = baseRest(function (arrays) {
    -      var length = arrays.length,
    -        iteratee = length > 1 ? arrays[length - 1] : undefined;
    -      iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;
    -      return unzipWith(arrays, iteratee);
    -    });
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates a `lodash` wrapper instance that wraps `value` with explicit method
    -     * chain sequences enabled. The result of such sequences must be unwrapped
    -     * with `_#value`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 1.3.0
    -     * @category Seq
    -     * @param {*} value The value to wrap.
    -     * @returns {Object} Returns the new `lodash` wrapper instance.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney',  'age': 36 },
    -     *   { 'user': 'fred',    'age': 40 },
    -     *   { 'user': 'pebbles', 'age': 1 }
    -     * ];
    -     *
    -     * var youngest = _
    -     *   .chain(users)
    -     *   .sortBy('age')
    -     *   .map(function(o) {
    -     *     return o.user + ' is ' + o.age;
    -     *   })
    -     *   .head()
    -     *   .value();
    -     * // => 'pebbles is 1'
    -     */
    -    function chain(value) {
    -      var result = lodash(value);
    -      result.__chain__ = true;
    -      return result;
    -    }
    -
    -    /**
    -     * This method invokes `interceptor` and returns `value`. The interceptor
    -     * is invoked with one argument; (value). The purpose of this method is to
    -     * "tap into" a method chain sequence in order to modify intermediate results.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Seq
    -     * @param {*} value The value to provide to `interceptor`.
    -     * @param {Function} interceptor The function to invoke.
    -     * @returns {*} Returns `value`.
    -     * @example
    -     *
    -     * _([1, 2, 3])
    -     *  .tap(function(array) {
    -     *    // Mutate input array.
    -     *    array.pop();
    -     *  })
    -     *  .reverse()
    -     *  .value();
    -     * // => [2, 1]
    -     */
    -    function tap(value, interceptor) {
    -      interceptor(value);
    -      return value;
    -    }
    -
    -    /**
    -     * This method is like `_.tap` except that it returns the result of `interceptor`.
    -     * The purpose of this method is to "pass thru" values replacing intermediate
    -     * results in a method chain sequence.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Seq
    -     * @param {*} value The value to provide to `interceptor`.
    -     * @param {Function} interceptor The function to invoke.
    -     * @returns {*} Returns the result of `interceptor`.
    -     * @example
    -     *
    -     * _('  abc  ')
    -     *  .chain()
    -     *  .trim()
    -     *  .thru(function(value) {
    -     *    return [value];
    -     *  })
    -     *  .value();
    -     * // => ['abc']
    -     */
    -    function thru(value, interceptor) {
    -      return interceptor(value);
    -    }
    -
    -    /**
    -     * This method is the wrapper version of `_.at`.
    -     *
    -     * @name at
    -     * @memberOf _
    -     * @since 1.0.0
    -     * @category Seq
    -     * @param {...(string|string[])} [paths] The property paths to pick.
    -     * @returns {Object} Returns the new `lodash` wrapper instance.
    -     * @example
    -     *
    -     * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
    -     *
    -     * _(object).at(['a[0].b.c', 'a[1]']).value();
    -     * // => [3, 4]
    -     */
    -    var wrapperAt = flatRest(function (paths) {
    -      var length = paths.length,
    -        start = length ? paths[0] : 0,
    -        value = this.__wrapped__,
    -        interceptor = function interceptor(object) {
    -          return baseAt(object, paths);
    -        };
    -      if (length > 1 || this.__actions__.length || !(value instanceof LazyWrapper) || !isIndex(start)) {
    -        return this.thru(interceptor);
    -      }
    -      value = value.slice(start, +start + (length ? 1 : 0));
    -      value.__actions__.push({
    -        'func': thru,
    -        'args': [interceptor],
    -        'thisArg': undefined
    -      });
    -      return new LodashWrapper(value, this.__chain__).thru(function (array) {
    -        if (length && !array.length) {
    -          array.push(undefined);
    -        }
    -        return array;
    -      });
    -    });
    -
    -    /**
    -     * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
    -     *
    -     * @name chain
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Seq
    -     * @returns {Object} Returns the new `lodash` wrapper instance.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney', 'age': 36 },
    -     *   { 'user': 'fred',   'age': 40 }
    -     * ];
    -     *
    -     * // A sequence without explicit chaining.
    -     * _(users).head();
    -     * // => { 'user': 'barney', 'age': 36 }
    -     *
    -     * // A sequence with explicit chaining.
    -     * _(users)
    -     *   .chain()
    -     *   .head()
    -     *   .pick('user')
    -     *   .value();
    -     * // => { 'user': 'barney' }
    -     */
    -    function wrapperChain() {
    -      return chain(this);
    -    }
    -
    -    /**
    -     * Executes the chain sequence and returns the wrapped result.
    -     *
    -     * @name commit
    -     * @memberOf _
    -     * @since 3.2.0
    -     * @category Seq
    -     * @returns {Object} Returns the new `lodash` wrapper instance.
    -     * @example
    -     *
    -     * var array = [1, 2];
    -     * var wrapped = _(array).push(3);
    -     *
    -     * console.log(array);
    -     * // => [1, 2]
    -     *
    -     * wrapped = wrapped.commit();
    -     * console.log(array);
    -     * // => [1, 2, 3]
    -     *
    -     * wrapped.last();
    -     * // => 3
    -     *
    -     * console.log(array);
    -     * // => [1, 2, 3]
    -     */
    -    function wrapperCommit() {
    -      return new LodashWrapper(this.value(), this.__chain__);
    -    }
    -
    -    /**
    -     * Gets the next value on a wrapped object following the
    -     * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
    -     *
    -     * @name next
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Seq
    -     * @returns {Object} Returns the next iterator value.
    -     * @example
    -     *
    -     * var wrapped = _([1, 2]);
    -     *
    -     * wrapped.next();
    -     * // => { 'done': false, 'value': 1 }
    -     *
    -     * wrapped.next();
    -     * // => { 'done': false, 'value': 2 }
    -     *
    -     * wrapped.next();
    -     * // => { 'done': true, 'value': undefined }
    -     */
    -    function wrapperNext() {
    -      if (this.__values__ === undefined) {
    -        this.__values__ = toArray(this.value());
    -      }
    -      var done = this.__index__ >= this.__values__.length,
    -        value = done ? undefined : this.__values__[this.__index__++];
    -      return {
    -        'done': done,
    -        'value': value
    -      };
    -    }
    -
    -    /**
    -     * Enables the wrapper to be iterable.
    -     *
    -     * @name Symbol.iterator
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Seq
    -     * @returns {Object} Returns the wrapper object.
    -     * @example
    -     *
    -     * var wrapped = _([1, 2]);
    -     *
    -     * wrapped[Symbol.iterator]() === wrapped;
    -     * // => true
    -     *
    -     * Array.from(wrapped);
    -     * // => [1, 2]
    -     */
    -    function wrapperToIterator() {
    -      return this;
    -    }
    -
    -    /**
    -     * Creates a clone of the chain sequence planting `value` as the wrapped value.
    -     *
    -     * @name plant
    -     * @memberOf _
    -     * @since 3.2.0
    -     * @category Seq
    -     * @param {*} value The value to plant.
    -     * @returns {Object} Returns the new `lodash` wrapper instance.
    -     * @example
    -     *
    -     * function square(n) {
    -     *   return n * n;
    -     * }
    -     *
    -     * var wrapped = _([1, 2]).map(square);
    -     * var other = wrapped.plant([3, 4]);
    -     *
    -     * other.value();
    -     * // => [9, 16]
    -     *
    -     * wrapped.value();
    -     * // => [1, 4]
    -     */
    -    function wrapperPlant(value) {
    -      var result,
    -        parent = this;
    -      while (parent instanceof baseLodash) {
    -        var clone = wrapperClone(parent);
    -        clone.__index__ = 0;
    -        clone.__values__ = undefined;
    -        if (result) {
    -          previous.__wrapped__ = clone;
    -        } else {
    -          result = clone;
    -        }
    -        var previous = clone;
    -        parent = parent.__wrapped__;
    -      }
    -      previous.__wrapped__ = value;
    -      return result;
    -    }
    -
    -    /**
    -     * This method is the wrapper version of `_.reverse`.
    -     *
    -     * **Note:** This method mutates the wrapped array.
    -     *
    -     * @name reverse
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Seq
    -     * @returns {Object} Returns the new `lodash` wrapper instance.
    -     * @example
    -     *
    -     * var array = [1, 2, 3];
    -     *
    -     * _(array).reverse().value()
    -     * // => [3, 2, 1]
    -     *
    -     * console.log(array);
    -     * // => [3, 2, 1]
    -     */
    -    function wrapperReverse() {
    -      var value = this.__wrapped__;
    -      if (value instanceof LazyWrapper) {
    -        var wrapped = value;
    -        if (this.__actions__.length) {
    -          wrapped = new LazyWrapper(this);
    -        }
    -        wrapped = wrapped.reverse();
    -        wrapped.__actions__.push({
    -          'func': thru,
    -          'args': [reverse],
    -          'thisArg': undefined
    -        });
    -        return new LodashWrapper(wrapped, this.__chain__);
    -      }
    -      return this.thru(reverse);
    -    }
    -
    -    /**
    -     * Executes the chain sequence to resolve the unwrapped value.
    -     *
    -     * @name value
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @alias toJSON, valueOf
    -     * @category Seq
    -     * @returns {*} Returns the resolved unwrapped value.
    -     * @example
    -     *
    -     * _([1, 2, 3]).value();
    -     * // => [1, 2, 3]
    -     */
    -    function wrapperValue() {
    -      return baseWrapperValue(this.__wrapped__, this.__actions__);
    -    }
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Creates an object composed of keys generated from the results of running
    -     * each element of `collection` thru `iteratee`. The corresponding value of
    -     * each key is the number of times the key was returned by `iteratee`. The
    -     * iteratee is invoked with one argument: (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.5.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
    -     * @returns {Object} Returns the composed aggregate object.
    -     * @example
    -     *
    -     * _.countBy([6.1, 4.2, 6.3], Math.floor);
    -     * // => { '4': 1, '6': 2 }
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.countBy(['one', 'two', 'three'], 'length');
    -     * // => { '3': 2, '5': 1 }
    -     */
    -    var countBy = createAggregator(function (result, value, key) {
    -      if (hasOwnProperty.call(result, key)) {
    -        ++result[key];
    -      } else {
    -        baseAssignValue(result, key, 1);
    -      }
    -    });
    -
    -    /**
    -     * Checks if `predicate` returns truthy for **all** elements of `collection`.
    -     * Iteration is stopped once `predicate` returns falsey. The predicate is
    -     * invoked with three arguments: (value, index|key, collection).
    -     *
    -     * **Note:** This method returns `true` for
    -     * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because
    -     * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of
    -     * elements of empty collections.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {boolean} Returns `true` if all elements pass the predicate check,
    -     *  else `false`.
    -     * @example
    -     *
    -     * _.every([true, 1, null, 'yes'], Boolean);
    -     * // => false
    -     *
    -     * var users = [
    -     *   { 'user': 'barney', 'age': 36, 'active': false },
    -     *   { 'user': 'fred',   'age': 40, 'active': false }
    -     * ];
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.every(users, { 'user': 'barney', 'active': false });
    -     * // => false
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.every(users, ['active', false]);
    -     * // => true
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.every(users, 'active');
    -     * // => false
    -     */
    -    function every(collection, predicate, guard) {
    -      var func = isArray(collection) ? arrayEvery : baseEvery;
    -      if (guard && isIterateeCall(collection, predicate, guard)) {
    -        predicate = undefined;
    -      }
    -      return func(collection, getIteratee(predicate, 3));
    -    }
    -
    -    /**
    -     * Iterates over elements of `collection`, returning an array of all elements
    -     * `predicate` returns truthy for. The predicate is invoked with three
    -     * arguments: (value, index|key, collection).
    -     *
    -     * **Note:** Unlike `_.remove`, this method returns a new array.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the new filtered array.
    -     * @see _.reject
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney', 'age': 36, 'active': true },
    -     *   { 'user': 'fred',   'age': 40, 'active': false }
    -     * ];
    -     *
    -     * _.filter(users, function(o) { return !o.active; });
    -     * // => objects for ['fred']
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.filter(users, { 'age': 36, 'active': true });
    -     * // => objects for ['barney']
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.filter(users, ['active', false]);
    -     * // => objects for ['fred']
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.filter(users, 'active');
    -     * // => objects for ['barney']
    -     *
    -     * // Combining several predicates using `_.overEvery` or `_.overSome`.
    -     * _.filter(users, _.overSome([{ 'age': 36 }, ['age', 40]]));
    -     * // => objects for ['fred', 'barney']
    -     */
    -    function filter(collection, predicate) {
    -      var func = isArray(collection) ? arrayFilter : baseFilter;
    -      return func(collection, getIteratee(predicate, 3));
    -    }
    -
    -    /**
    -     * Iterates over elements of `collection`, returning the first element
    -     * `predicate` returns truthy for. The predicate is invoked with three
    -     * arguments: (value, index|key, collection).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to inspect.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @param {number} [fromIndex=0] The index to search from.
    -     * @returns {*} Returns the matched element, else `undefined`.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney',  'age': 36, 'active': true },
    -     *   { 'user': 'fred',    'age': 40, 'active': false },
    -     *   { 'user': 'pebbles', 'age': 1,  'active': true }
    -     * ];
    -     *
    -     * _.find(users, function(o) { return o.age < 40; });
    -     * // => object for 'barney'
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.find(users, { 'age': 1, 'active': true });
    -     * // => object for 'pebbles'
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.find(users, ['active', false]);
    -     * // => object for 'fred'
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.find(users, 'active');
    -     * // => object for 'barney'
    -     */
    -    var find = createFind(findIndex);
    -
    -    /**
    -     * This method is like `_.find` except that it iterates over elements of
    -     * `collection` from right to left.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 2.0.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to inspect.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @param {number} [fromIndex=collection.length-1] The index to search from.
    -     * @returns {*} Returns the matched element, else `undefined`.
    -     * @example
    -     *
    -     * _.findLast([1, 2, 3, 4], function(n) {
    -     *   return n % 2 == 1;
    -     * });
    -     * // => 3
    -     */
    -    var findLast = createFind(findLastIndex);
    -
    -    /**
    -     * Creates a flattened array of values by running each element in `collection`
    -     * thru `iteratee` and flattening the mapped results. The iteratee is invoked
    -     * with three arguments: (value, index|key, collection).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the new flattened array.
    -     * @example
    -     *
    -     * function duplicate(n) {
    -     *   return [n, n];
    -     * }
    -     *
    -     * _.flatMap([1, 2], duplicate);
    -     * // => [1, 1, 2, 2]
    -     */
    -    function flatMap(collection, iteratee) {
    -      return baseFlatten(map(collection, iteratee), 1);
    -    }
    -
    -    /**
    -     * This method is like `_.flatMap` except that it recursively flattens the
    -     * mapped results.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.7.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the new flattened array.
    -     * @example
    -     *
    -     * function duplicate(n) {
    -     *   return [[[n, n]]];
    -     * }
    -     *
    -     * _.flatMapDeep([1, 2], duplicate);
    -     * // => [1, 1, 2, 2]
    -     */
    -    function flatMapDeep(collection, iteratee) {
    -      return baseFlatten(map(collection, iteratee), INFINITY);
    -    }
    -
    -    /**
    -     * This method is like `_.flatMap` except that it recursively flattens the
    -     * mapped results up to `depth` times.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.7.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    -     * @param {number} [depth=1] The maximum recursion depth.
    -     * @returns {Array} Returns the new flattened array.
    -     * @example
    -     *
    -     * function duplicate(n) {
    -     *   return [[[n, n]]];
    -     * }
    -     *
    -     * _.flatMapDepth([1, 2], duplicate, 2);
    -     * // => [[1, 1], [2, 2]]
    -     */
    -    function flatMapDepth(collection, iteratee, depth) {
    -      depth = depth === undefined ? 1 : toInteger(depth);
    -      return baseFlatten(map(collection, iteratee), depth);
    -    }
    -
    -    /**
    -     * Iterates over elements of `collection` and invokes `iteratee` for each element.
    -     * The iteratee is invoked with three arguments: (value, index|key, collection).
    -     * Iteratee functions may exit iteration early by explicitly returning `false`.
    -     *
    -     * **Note:** As with other "Collections" methods, objects with a "length"
    -     * property are iterated like arrays. To avoid this behavior use `_.forIn`
    -     * or `_.forOwn` for object iteration.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @alias each
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    -     * @returns {Array|Object} Returns `collection`.
    -     * @see _.forEachRight
    -     * @example
    -     *
    -     * _.forEach([1, 2], function(value) {
    -     *   console.log(value);
    -     * });
    -     * // => Logs `1` then `2`.
    -     *
    -     * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
    -     *   console.log(key);
    -     * });
    -     * // => Logs 'a' then 'b' (iteration order is not guaranteed).
    -     */
    -    function forEach(collection, iteratee) {
    -      var func = isArray(collection) ? arrayEach : baseEach;
    -      return func(collection, getIteratee(iteratee, 3));
    -    }
    -
    -    /**
    -     * This method is like `_.forEach` except that it iterates over elements of
    -     * `collection` from right to left.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 2.0.0
    -     * @alias eachRight
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    -     * @returns {Array|Object} Returns `collection`.
    -     * @see _.forEach
    -     * @example
    -     *
    -     * _.forEachRight([1, 2], function(value) {
    -     *   console.log(value);
    -     * });
    -     * // => Logs `2` then `1`.
    -     */
    -    function forEachRight(collection, iteratee) {
    -      var func = isArray(collection) ? arrayEachRight : baseEachRight;
    -      return func(collection, getIteratee(iteratee, 3));
    -    }
    -
    -    /**
    -     * Creates an object composed of keys generated from the results of running
    -     * each element of `collection` thru `iteratee`. The order of grouped values
    -     * is determined by the order they occur in `collection`. The corresponding
    -     * value of each key is an array of elements responsible for generating the
    -     * key. The iteratee is invoked with one argument: (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
    -     * @returns {Object} Returns the composed aggregate object.
    -     * @example
    -     *
    -     * _.groupBy([6.1, 4.2, 6.3], Math.floor);
    -     * // => { '4': [4.2], '6': [6.1, 6.3] }
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.groupBy(['one', 'two', 'three'], 'length');
    -     * // => { '3': ['one', 'two'], '5': ['three'] }
    -     */
    -    var groupBy = createAggregator(function (result, value, key) {
    -      if (hasOwnProperty.call(result, key)) {
    -        result[key].push(value);
    -      } else {
    -        baseAssignValue(result, key, [value]);
    -      }
    -    });
    -
    -    /**
    -     * Checks if `value` is in `collection`. If `collection` is a string, it's
    -     * checked for a substring of `value`, otherwise
    -     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
    -     * is used for equality comparisons. If `fromIndex` is negative, it's used as
    -     * the offset from the end of `collection`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object|string} collection The collection to inspect.
    -     * @param {*} value The value to search for.
    -     * @param {number} [fromIndex=0] The index to search from.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
    -     * @returns {boolean} Returns `true` if `value` is found, else `false`.
    -     * @example
    -     *
    -     * _.includes([1, 2, 3], 1);
    -     * // => true
    -     *
    -     * _.includes([1, 2, 3], 1, 2);
    -     * // => false
    -     *
    -     * _.includes({ 'a': 1, 'b': 2 }, 1);
    -     * // => true
    -     *
    -     * _.includes('abcd', 'bc');
    -     * // => true
    -     */
    -    function includes(collection, value, fromIndex, guard) {
    -      collection = isArrayLike(collection) ? collection : values(collection);
    -      fromIndex = fromIndex && !guard ? toInteger(fromIndex) : 0;
    -      var length = collection.length;
    -      if (fromIndex < 0) {
    -        fromIndex = nativeMax(length + fromIndex, 0);
    -      }
    -      return isString(collection) ? fromIndex <= length && collection.indexOf(value, fromIndex) > -1 : !!length && baseIndexOf(collection, value, fromIndex) > -1;
    -    }
    -
    -    /**
    -     * Invokes the method at `path` of each element in `collection`, returning
    -     * an array of the results of each invoked method. Any additional arguments
    -     * are provided to each invoked method. If `path` is a function, it's invoked
    -     * for, and `this` bound to, each element in `collection`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Array|Function|string} path The path of the method to invoke or
    -     *  the function invoked per iteration.
    -     * @param {...*} [args] The arguments to invoke each method with.
    -     * @returns {Array} Returns the array of results.
    -     * @example
    -     *
    -     * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');
    -     * // => [[1, 5, 7], [1, 2, 3]]
    -     *
    -     * _.invokeMap([123, 456], String.prototype.split, '');
    -     * // => [['1', '2', '3'], ['4', '5', '6']]
    -     */
    -    var invokeMap = baseRest(function (collection, path, args) {
    -      var index = -1,
    -        isFunc = typeof path == 'function',
    -        result = isArrayLike(collection) ? Array(collection.length) : [];
    -      baseEach(collection, function (value) {
    -        result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args);
    -      });
    -      return result;
    -    });
    -
    -    /**
    -     * Creates an object composed of keys generated from the results of running
    -     * each element of `collection` thru `iteratee`. The corresponding value of
    -     * each key is the last element responsible for generating the key. The
    -     * iteratee is invoked with one argument: (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
    -     * @returns {Object} Returns the composed aggregate object.
    -     * @example
    -     *
    -     * var array = [
    -     *   { 'dir': 'left', 'code': 97 },
    -     *   { 'dir': 'right', 'code': 100 }
    -     * ];
    -     *
    -     * _.keyBy(array, function(o) {
    -     *   return String.fromCharCode(o.code);
    -     * });
    -     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
    -     *
    -     * _.keyBy(array, 'dir');
    -     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
    -     */
    -    var keyBy = createAggregator(function (result, value, key) {
    -      baseAssignValue(result, key, value);
    -    });
    -
    -    /**
    -     * Creates an array of values by running each element in `collection` thru
    -     * `iteratee`. The iteratee is invoked with three arguments:
    -     * (value, index|key, collection).
    -     *
    -     * Many lodash methods are guarded to work as iteratees for methods like
    -     * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
    -     *
    -     * The guarded methods are:
    -     * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
    -     * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
    -     * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
    -     * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the new mapped array.
    -     * @example
    -     *
    -     * function square(n) {
    -     *   return n * n;
    -     * }
    -     *
    -     * _.map([4, 8], square);
    -     * // => [16, 64]
    -     *
    -     * _.map({ 'a': 4, 'b': 8 }, square);
    -     * // => [16, 64] (iteration order is not guaranteed)
    -     *
    -     * var users = [
    -     *   { 'user': 'barney' },
    -     *   { 'user': 'fred' }
    -     * ];
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.map(users, 'user');
    -     * // => ['barney', 'fred']
    -     */
    -    function map(collection, iteratee) {
    -      var func = isArray(collection) ? arrayMap : baseMap;
    -      return func(collection, getIteratee(iteratee, 3));
    -    }
    -
    -    /**
    -     * This method is like `_.sortBy` except that it allows specifying the sort
    -     * orders of the iteratees to sort by. If `orders` is unspecified, all values
    -     * are sorted in ascending order. Otherwise, specify an order of "desc" for
    -     * descending or "asc" for ascending sort order of corresponding values.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]
    -     *  The iteratees to sort by.
    -     * @param {string[]} [orders] The sort orders of `iteratees`.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
    -     * @returns {Array} Returns the new sorted array.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'fred',   'age': 48 },
    -     *   { 'user': 'barney', 'age': 34 },
    -     *   { 'user': 'fred',   'age': 40 },
    -     *   { 'user': 'barney', 'age': 36 }
    -     * ];
    -     *
    -     * // Sort by `user` in ascending order and by `age` in descending order.
    -     * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);
    -     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
    -     */
    -    function orderBy(collection, iteratees, orders, guard) {
    -      if (collection == null) {
    -        return [];
    -      }
    -      if (!isArray(iteratees)) {
    -        iteratees = iteratees == null ? [] : [iteratees];
    -      }
    -      orders = guard ? undefined : orders;
    -      if (!isArray(orders)) {
    -        orders = orders == null ? [] : [orders];
    -      }
    -      return baseOrderBy(collection, iteratees, orders);
    -    }
    -
    -    /**
    -     * Creates an array of elements split into two groups, the first of which
    -     * contains elements `predicate` returns truthy for, the second of which
    -     * contains elements `predicate` returns falsey for. The predicate is
    -     * invoked with one argument: (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the array of grouped elements.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney',  'age': 36, 'active': false },
    -     *   { 'user': 'fred',    'age': 40, 'active': true },
    -     *   { 'user': 'pebbles', 'age': 1,  'active': false }
    -     * ];
    -     *
    -     * _.partition(users, function(o) { return o.active; });
    -     * // => objects for [['fred'], ['barney', 'pebbles']]
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.partition(users, { 'age': 1, 'active': false });
    -     * // => objects for [['pebbles'], ['barney', 'fred']]
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.partition(users, ['active', false]);
    -     * // => objects for [['barney', 'pebbles'], ['fred']]
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.partition(users, 'active');
    -     * // => objects for [['fred'], ['barney', 'pebbles']]
    -     */
    -    var partition = createAggregator(function (result, value, key) {
    -      result[key ? 0 : 1].push(value);
    -    }, function () {
    -      return [[], []];
    -    });
    -
    -    /**
    -     * Reduces `collection` to a value which is the accumulated result of running
    -     * each element in `collection` thru `iteratee`, where each successive
    -     * invocation is supplied the return value of the previous. If `accumulator`
    -     * is not given, the first element of `collection` is used as the initial
    -     * value. The iteratee is invoked with four arguments:
    -     * (accumulator, value, index|key, collection).
    -     *
    -     * Many lodash methods are guarded to work as iteratees for methods like
    -     * `_.reduce`, `_.reduceRight`, and `_.transform`.
    -     *
    -     * The guarded methods are:
    -     * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
    -     * and `sortBy`
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    -     * @param {*} [accumulator] The initial value.
    -     * @returns {*} Returns the accumulated value.
    -     * @see _.reduceRight
    -     * @example
    -     *
    -     * _.reduce([1, 2], function(sum, n) {
    -     *   return sum + n;
    -     * }, 0);
    -     * // => 3
    -     *
    -     * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
    -     *   (result[value] || (result[value] = [])).push(key);
    -     *   return result;
    -     * }, {});
    -     * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
    -     */
    -    function reduce(collection, iteratee, accumulator) {
    -      var func = isArray(collection) ? arrayReduce : baseReduce,
    -        initAccum = arguments.length < 3;
    -      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
    -    }
    -
    -    /**
    -     * This method is like `_.reduce` except that it iterates over elements of
    -     * `collection` from right to left.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
    -     * @param {*} [accumulator] The initial value.
    -     * @returns {*} Returns the accumulated value.
    -     * @see _.reduce
    -     * @example
    -     *
    -     * var array = [[0, 1], [2, 3], [4, 5]];
    -     *
    -     * _.reduceRight(array, function(flattened, other) {
    -     *   return flattened.concat(other);
    -     * }, []);
    -     * // => [4, 5, 2, 3, 0, 1]
    -     */
    -    function reduceRight(collection, iteratee, accumulator) {
    -      var func = isArray(collection) ? arrayReduceRight : baseReduce,
    -        initAccum = arguments.length < 3;
    -      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);
    -    }
    -
    -    /**
    -     * The opposite of `_.filter`; this method returns the elements of `collection`
    -     * that `predicate` does **not** return truthy for.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @returns {Array} Returns the new filtered array.
    -     * @see _.filter
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'barney', 'age': 36, 'active': false },
    -     *   { 'user': 'fred',   'age': 40, 'active': true }
    -     * ];
    -     *
    -     * _.reject(users, function(o) { return !o.active; });
    -     * // => objects for ['fred']
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.reject(users, { 'age': 40, 'active': true });
    -     * // => objects for ['barney']
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.reject(users, ['active', false]);
    -     * // => objects for ['fred']
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.reject(users, 'active');
    -     * // => objects for ['barney']
    -     */
    -    function reject(collection, predicate) {
    -      var func = isArray(collection) ? arrayFilter : baseFilter;
    -      return func(collection, negate(getIteratee(predicate, 3)));
    -    }
    -
    -    /**
    -     * Gets a random element from `collection`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 2.0.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to sample.
    -     * @returns {*} Returns the random element.
    -     * @example
    -     *
    -     * _.sample([1, 2, 3, 4]);
    -     * // => 2
    -     */
    -    function sample(collection) {
    -      var func = isArray(collection) ? arraySample : baseSample;
    -      return func(collection);
    -    }
    -
    -    /**
    -     * Gets `n` random elements at unique keys from `collection` up to the
    -     * size of `collection`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to sample.
    -     * @param {number} [n=1] The number of elements to sample.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {Array} Returns the random elements.
    -     * @example
    -     *
    -     * _.sampleSize([1, 2, 3], 2);
    -     * // => [3, 1]
    -     *
    -     * _.sampleSize([1, 2, 3], 4);
    -     * // => [2, 3, 1]
    -     */
    -    function sampleSize(collection, n, guard) {
    -      if (guard ? isIterateeCall(collection, n, guard) : n === undefined) {
    -        n = 1;
    -      } else {
    -        n = toInteger(n);
    -      }
    -      var func = isArray(collection) ? arraySampleSize : baseSampleSize;
    -      return func(collection, n);
    -    }
    -
    -    /**
    -     * Creates an array of shuffled values, using a version of the
    -     * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to shuffle.
    -     * @returns {Array} Returns the new shuffled array.
    -     * @example
    -     *
    -     * _.shuffle([1, 2, 3, 4]);
    -     * // => [4, 1, 3, 2]
    -     */
    -    function shuffle(collection) {
    -      var func = isArray(collection) ? arrayShuffle : baseShuffle;
    -      return func(collection);
    -    }
    -
    -    /**
    -     * Gets the size of `collection` by returning its length for array-like
    -     * values or the number of own enumerable string keyed properties for objects.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object|string} collection The collection to inspect.
    -     * @returns {number} Returns the collection size.
    -     * @example
    -     *
    -     * _.size([1, 2, 3]);
    -     * // => 3
    -     *
    -     * _.size({ 'a': 1, 'b': 2 });
    -     * // => 2
    -     *
    -     * _.size('pebbles');
    -     * // => 7
    -     */
    -    function size(collection) {
    -      if (collection == null) {
    -        return 0;
    -      }
    -      if (isArrayLike(collection)) {
    -        return isString(collection) ? stringSize(collection) : collection.length;
    -      }
    -      var tag = getTag(collection);
    -      if (tag == mapTag || tag == setTag) {
    -        return collection.size;
    -      }
    -      return baseKeys(collection).length;
    -    }
    -
    -    /**
    -     * Checks if `predicate` returns truthy for **any** element of `collection`.
    -     * Iteration is stopped once `predicate` returns truthy. The predicate is
    -     * invoked with three arguments: (value, index|key, collection).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {Function} [predicate=_.identity] The function invoked per iteration.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {boolean} Returns `true` if any element passes the predicate check,
    -     *  else `false`.
    -     * @example
    -     *
    -     * _.some([null, 0, 'yes', false], Boolean);
    -     * // => true
    -     *
    -     * var users = [
    -     *   { 'user': 'barney', 'active': true },
    -     *   { 'user': 'fred',   'active': false }
    -     * ];
    -     *
    -     * // The `_.matches` iteratee shorthand.
    -     * _.some(users, { 'user': 'barney', 'active': false });
    -     * // => false
    -     *
    -     * // The `_.matchesProperty` iteratee shorthand.
    -     * _.some(users, ['active', false]);
    -     * // => true
    -     *
    -     * // The `_.property` iteratee shorthand.
    -     * _.some(users, 'active');
    -     * // => true
    -     */
    -    function some(collection, predicate, guard) {
    -      var func = isArray(collection) ? arraySome : baseSome;
    -      if (guard && isIterateeCall(collection, predicate, guard)) {
    -        predicate = undefined;
    -      }
    -      return func(collection, getIteratee(predicate, 3));
    -    }
    -
    -    /**
    -     * Creates an array of elements, sorted in ascending order by the results of
    -     * running each element in a collection thru each iteratee. This method
    -     * performs a stable sort, that is, it preserves the original sort order of
    -     * equal elements. The iteratees are invoked with one argument: (value).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Collection
    -     * @param {Array|Object} collection The collection to iterate over.
    -     * @param {...(Function|Function[])} [iteratees=[_.identity]]
    -     *  The iteratees to sort by.
    -     * @returns {Array} Returns the new sorted array.
    -     * @example
    -     *
    -     * var users = [
    -     *   { 'user': 'fred',   'age': 48 },
    -     *   { 'user': 'barney', 'age': 36 },
    -     *   { 'user': 'fred',   'age': 30 },
    -     *   { 'user': 'barney', 'age': 34 }
    -     * ];
    -     *
    -     * _.sortBy(users, [function(o) { return o.user; }]);
    -     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 30]]
    -     *
    -     * _.sortBy(users, ['user', 'age']);
    -     * // => objects for [['barney', 34], ['barney', 36], ['fred', 30], ['fred', 48]]
    -     */
    -    var sortBy = baseRest(function (collection, iteratees) {
    -      if (collection == null) {
    -        return [];
    -      }
    -      var length = iteratees.length;
    -      if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
    -        iteratees = [];
    -      } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
    -        iteratees = [iteratees[0]];
    -      }
    -      return baseOrderBy(collection, baseFlatten(iteratees, 1), []);
    -    });
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * Gets the timestamp of the number of milliseconds that have elapsed since
    -     * the Unix epoch (1 January 1970 00:00:00 UTC).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 2.4.0
    -     * @category Date
    -     * @returns {number} Returns the timestamp.
    -     * @example
    -     *
    -     * _.defer(function(stamp) {
    -     *   console.log(_.now() - stamp);
    -     * }, _.now());
    -     * // => Logs the number of milliseconds it took for the deferred invocation.
    -     */
    -    var now = ctxNow || function () {
    -      return root.Date.now();
    -    };
    -
    -    /*------------------------------------------------------------------------*/
    -
    -    /**
    -     * The opposite of `_.before`; this method creates a function that invokes
    -     * `func` once it's called `n` or more times.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Function
    -     * @param {number} n The number of calls before `func` is invoked.
    -     * @param {Function} func The function to restrict.
    -     * @returns {Function} Returns the new restricted function.
    -     * @example
    -     *
    -     * var saves = ['profile', 'settings'];
    -     *
    -     * var done = _.after(saves.length, function() {
    -     *   console.log('done saving!');
    -     * });
    -     *
    -     * _.forEach(saves, function(type) {
    -     *   asyncSave({ 'type': type, 'complete': done });
    -     * });
    -     * // => Logs 'done saving!' after the two async saves have completed.
    -     */
    -    function after(n, func) {
    -      if (typeof func != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      n = toInteger(n);
    -      return function () {
    -        if (--n < 1) {
    -          return func.apply(this, arguments);
    -        }
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that invokes `func`, with up to `n` arguments,
    -     * ignoring any additional arguments.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Function
    -     * @param {Function} func The function to cap arguments for.
    -     * @param {number} [n=func.length] The arity cap.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {Function} Returns the new capped function.
    -     * @example
    -     *
    -     * _.map(['6', '8', '10'], _.ary(parseInt, 1));
    -     * // => [6, 8, 10]
    -     */
    -    function ary(func, n, guard) {
    -      n = guard ? undefined : n;
    -      n = func && n == null ? func.length : n;
    -      return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);
    -    }
    -
    -    /**
    -     * Creates a function that invokes `func`, with the `this` binding and arguments
    -     * of the created function, while it's called less than `n` times. Subsequent
    -     * calls to the created function return the result of the last `func` invocation.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Function
    -     * @param {number} n The number of calls at which `func` is no longer invoked.
    -     * @param {Function} func The function to restrict.
    -     * @returns {Function} Returns the new restricted function.
    -     * @example
    -     *
    -     * jQuery(element).on('click', _.before(5, addContactToList));
    -     * // => Allows adding up to 4 contacts to the list.
    -     */
    -    function before(n, func) {
    -      var result;
    -      if (typeof func != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      n = toInteger(n);
    -      return function () {
    -        if (--n > 0) {
    -          result = func.apply(this, arguments);
    -        }
    -        if (n <= 1) {
    -          func = undefined;
    -        }
    -        return result;
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that invokes `func` with the `this` binding of `thisArg`
    -     * and `partials` prepended to the arguments it receives.
    -     *
    -     * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
    -     * may be used as a placeholder for partially applied arguments.
    -     *
    -     * **Note:** Unlike native `Function#bind`, this method doesn't set the "length"
    -     * property of bound functions.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Function
    -     * @param {Function} func The function to bind.
    -     * @param {*} thisArg The `this` binding of `func`.
    -     * @param {...*} [partials] The arguments to be partially applied.
    -     * @returns {Function} Returns the new bound function.
    -     * @example
    -     *
    -     * function greet(greeting, punctuation) {
    -     *   return greeting + ' ' + this.user + punctuation;
    -     * }
    -     *
    -     * var object = { 'user': 'fred' };
    -     *
    -     * var bound = _.bind(greet, object, 'hi');
    -     * bound('!');
    -     * // => 'hi fred!'
    -     *
    -     * // Bound with placeholders.
    -     * var bound = _.bind(greet, object, _, '!');
    -     * bound('hi');
    -     * // => 'hi fred!'
    -     */
    -    var bind = baseRest(function (func, thisArg, partials) {
    -      var bitmask = WRAP_BIND_FLAG;
    -      if (partials.length) {
    -        var holders = replaceHolders(partials, getHolder(bind));
    -        bitmask |= WRAP_PARTIAL_FLAG;
    -      }
    -      return createWrap(func, bitmask, thisArg, partials, holders);
    -    });
    -
    -    /**
    -     * Creates a function that invokes the method at `object[key]` with `partials`
    -     * prepended to the arguments it receives.
    -     *
    -     * This method differs from `_.bind` by allowing bound functions to reference
    -     * methods that may be redefined or don't yet exist. See
    -     * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)
    -     * for more details.
    -     *
    -     * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
    -     * builds, may be used as a placeholder for partially applied arguments.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.10.0
    -     * @category Function
    -     * @param {Object} object The object to invoke the method on.
    -     * @param {string} key The key of the method.
    -     * @param {...*} [partials] The arguments to be partially applied.
    -     * @returns {Function} Returns the new bound function.
    -     * @example
    -     *
    -     * var object = {
    -     *   'user': 'fred',
    -     *   'greet': function(greeting, punctuation) {
    -     *     return greeting + ' ' + this.user + punctuation;
    -     *   }
    -     * };
    -     *
    -     * var bound = _.bindKey(object, 'greet', 'hi');
    -     * bound('!');
    -     * // => 'hi fred!'
    -     *
    -     * object.greet = function(greeting, punctuation) {
    -     *   return greeting + 'ya ' + this.user + punctuation;
    -     * };
    -     *
    -     * bound('!');
    -     * // => 'hiya fred!'
    -     *
    -     * // Bound with placeholders.
    -     * var bound = _.bindKey(object, 'greet', _, '!');
    -     * bound('hi');
    -     * // => 'hiya fred!'
    -     */
    -    var bindKey = baseRest(function (object, key, partials) {
    -      var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG;
    -      if (partials.length) {
    -        var holders = replaceHolders(partials, getHolder(bindKey));
    -        bitmask |= WRAP_PARTIAL_FLAG;
    -      }
    -      return createWrap(key, bitmask, object, partials, holders);
    -    });
    -
    -    /**
    -     * Creates a function that accepts arguments of `func` and either invokes
    -     * `func` returning its result, if at least `arity` number of arguments have
    -     * been provided, or returns a function that accepts the remaining `func`
    -     * arguments, and so on. The arity of `func` may be specified if `func.length`
    -     * is not sufficient.
    -     *
    -     * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
    -     * may be used as a placeholder for provided arguments.
    -     *
    -     * **Note:** This method doesn't set the "length" property of curried functions.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 2.0.0
    -     * @category Function
    -     * @param {Function} func The function to curry.
    -     * @param {number} [arity=func.length] The arity of `func`.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {Function} Returns the new curried function.
    -     * @example
    -     *
    -     * var abc = function(a, b, c) {
    -     *   return [a, b, c];
    -     * };
    -     *
    -     * var curried = _.curry(abc);
    -     *
    -     * curried(1)(2)(3);
    -     * // => [1, 2, 3]
    -     *
    -     * curried(1, 2)(3);
    -     * // => [1, 2, 3]
    -     *
    -     * curried(1, 2, 3);
    -     * // => [1, 2, 3]
    -     *
    -     * // Curried with placeholders.
    -     * curried(1)(_, 3)(2);
    -     * // => [1, 2, 3]
    -     */
    -    function curry(func, arity, guard) {
    -      arity = guard ? undefined : arity;
    -      var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
    -      result.placeholder = curry.placeholder;
    -      return result;
    -    }
    -
    -    /**
    -     * This method is like `_.curry` except that arguments are applied to `func`
    -     * in the manner of `_.partialRight` instead of `_.partial`.
    -     *
    -     * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
    -     * builds, may be used as a placeholder for provided arguments.
    -     *
    -     * **Note:** This method doesn't set the "length" property of curried functions.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Function
    -     * @param {Function} func The function to curry.
    -     * @param {number} [arity=func.length] The arity of `func`.
    -     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
    -     * @returns {Function} Returns the new curried function.
    -     * @example
    -     *
    -     * var abc = function(a, b, c) {
    -     *   return [a, b, c];
    -     * };
    -     *
    -     * var curried = _.curryRight(abc);
    -     *
    -     * curried(3)(2)(1);
    -     * // => [1, 2, 3]
    -     *
    -     * curried(2, 3)(1);
    -     * // => [1, 2, 3]
    -     *
    -     * curried(1, 2, 3);
    -     * // => [1, 2, 3]
    -     *
    -     * // Curried with placeholders.
    -     * curried(3)(1, _)(2);
    -     * // => [1, 2, 3]
    -     */
    -    function curryRight(func, arity, guard) {
    -      arity = guard ? undefined : arity;
    -      var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
    -      result.placeholder = curryRight.placeholder;
    -      return result;
    -    }
    -
    -    /**
    -     * Creates a debounced function that delays invoking `func` until after `wait`
    -     * milliseconds have elapsed since the last time the debounced function was
    -     * invoked. The debounced function comes with a `cancel` method to cancel
    -     * delayed `func` invocations and a `flush` method to immediately invoke them.
    -     * Provide `options` to indicate whether `func` should be invoked on the
    -     * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
    -     * with the last arguments provided to the debounced function. Subsequent
    -     * calls to the debounced function return the result of the last `func`
    -     * invocation.
    -     *
    -     * **Note:** If `leading` and `trailing` options are `true`, `func` is
    -     * invoked on the trailing edge of the timeout only if the debounced function
    -     * is invoked more than once during the `wait` timeout.
    -     *
    -     * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
    -     * until to the next tick, similar to `setTimeout` with a timeout of `0`.
    -     *
    -     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
    -     * for details over the differences between `_.debounce` and `_.throttle`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Function
    -     * @param {Function} func The function to debounce.
    -     * @param {number} [wait=0] The number of milliseconds to delay.
    -     * @param {Object} [options={}] The options object.
    -     * @param {boolean} [options.leading=false]
    -     *  Specify invoking on the leading edge of the timeout.
    -     * @param {number} [options.maxWait]
    -     *  The maximum time `func` is allowed to be delayed before it's invoked.
    -     * @param {boolean} [options.trailing=true]
    -     *  Specify invoking on the trailing edge of the timeout.
    -     * @returns {Function} Returns the new debounced function.
    -     * @example
    -     *
    -     * // Avoid costly calculations while the window size is in flux.
    -     * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
    -     *
    -     * // Invoke `sendMail` when clicked, debouncing subsequent calls.
    -     * jQuery(element).on('click', _.debounce(sendMail, 300, {
    -     *   'leading': true,
    -     *   'trailing': false
    -     * }));
    -     *
    -     * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
    -     * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
    -     * var source = new EventSource('/stream');
    -     * jQuery(source).on('message', debounced);
    -     *
    -     * // Cancel the trailing debounced invocation.
    -     * jQuery(window).on('popstate', debounced.cancel);
    -     */
    -    function debounce(func, wait, options) {
    -      var lastArgs,
    -        lastThis,
    -        maxWait,
    -        result,
    -        timerId,
    -        lastCallTime,
    -        lastInvokeTime = 0,
    -        leading = false,
    -        maxing = false,
    -        trailing = true;
    -      if (typeof func != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      wait = toNumber(wait) || 0;
    -      if (isObject(options)) {
    -        leading = !!options.leading;
    -        maxing = 'maxWait' in options;
    -        maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
    -        trailing = 'trailing' in options ? !!options.trailing : trailing;
    -      }
    -      function invokeFunc(time) {
    -        var args = lastArgs,
    -          thisArg = lastThis;
    -        lastArgs = lastThis = undefined;
    -        lastInvokeTime = time;
    -        result = func.apply(thisArg, args);
    -        return result;
    -      }
    -      function leadingEdge(time) {
    -        // Reset any `maxWait` timer.
    -        lastInvokeTime = time;
    -        // Start the timer for the trailing edge.
    -        timerId = setTimeout(timerExpired, wait);
    -        // Invoke the leading edge.
    -        return leading ? invokeFunc(time) : result;
    -      }
    -      function remainingWait(time) {
    -        var timeSinceLastCall = time - lastCallTime,
    -          timeSinceLastInvoke = time - lastInvokeTime,
    -          timeWaiting = wait - timeSinceLastCall;
    -        return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
    -      }
    -      function shouldInvoke(time) {
    -        var timeSinceLastCall = time - lastCallTime,
    -          timeSinceLastInvoke = time - lastInvokeTime;
    -
    -        // Either this is the first call, activity has stopped and we're at the
    -        // trailing edge, the system time has gone backwards and we're treating
    -        // it as the trailing edge, or we've hit the `maxWait` limit.
    -        return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
    -      }
    -      function timerExpired() {
    -        var time = now();
    -        if (shouldInvoke(time)) {
    -          return trailingEdge(time);
    -        }
    -        // Restart the timer.
    -        timerId = setTimeout(timerExpired, remainingWait(time));
    -      }
    -      function trailingEdge(time) {
    -        timerId = undefined;
    -
    -        // Only invoke if we have `lastArgs` which means `func` has been
    -        // debounced at least once.
    -        if (trailing && lastArgs) {
    -          return invokeFunc(time);
    -        }
    -        lastArgs = lastThis = undefined;
    -        return result;
    -      }
    -      function cancel() {
    -        if (timerId !== undefined) {
    -          clearTimeout(timerId);
    -        }
    -        lastInvokeTime = 0;
    -        lastArgs = lastCallTime = lastThis = timerId = undefined;
    -      }
    -      function flush() {
    -        return timerId === undefined ? result : trailingEdge(now());
    -      }
    -      function debounced() {
    -        var time = now(),
    -          isInvoking = shouldInvoke(time);
    -        lastArgs = arguments;
    -        lastThis = this;
    -        lastCallTime = time;
    -        if (isInvoking) {
    -          if (timerId === undefined) {
    -            return leadingEdge(lastCallTime);
    -          }
    -          if (maxing) {
    -            // Handle invocations in a tight loop.
    -            clearTimeout(timerId);
    -            timerId = setTimeout(timerExpired, wait);
    -            return invokeFunc(lastCallTime);
    -          }
    -        }
    -        if (timerId === undefined) {
    -          timerId = setTimeout(timerExpired, wait);
    -        }
    -        return result;
    -      }
    -      debounced.cancel = cancel;
    -      debounced.flush = flush;
    -      return debounced;
    -    }
    -
    -    /**
    -     * Defers invoking the `func` until the current call stack has cleared. Any
    -     * additional arguments are provided to `func` when it's invoked.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Function
    -     * @param {Function} func The function to defer.
    -     * @param {...*} [args] The arguments to invoke `func` with.
    -     * @returns {number} Returns the timer id.
    -     * @example
    -     *
    -     * _.defer(function(text) {
    -     *   console.log(text);
    -     * }, 'deferred');
    -     * // => Logs 'deferred' after one millisecond.
    -     */
    -    var defer = baseRest(function (func, args) {
    -      return baseDelay(func, 1, args);
    -    });
    -
    -    /**
    -     * Invokes `func` after `wait` milliseconds. Any additional arguments are
    -     * provided to `func` when it's invoked.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Function
    -     * @param {Function} func The function to delay.
    -     * @param {number} wait The number of milliseconds to delay invocation.
    -     * @param {...*} [args] The arguments to invoke `func` with.
    -     * @returns {number} Returns the timer id.
    -     * @example
    -     *
    -     * _.delay(function(text) {
    -     *   console.log(text);
    -     * }, 1000, 'later');
    -     * // => Logs 'later' after one second.
    -     */
    -    var delay = baseRest(function (func, wait, args) {
    -      return baseDelay(func, toNumber(wait) || 0, args);
    -    });
    -
    -    /**
    -     * Creates a function that invokes `func` with arguments reversed.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Function
    -     * @param {Function} func The function to flip arguments for.
    -     * @returns {Function} Returns the new flipped function.
    -     * @example
    -     *
    -     * var flipped = _.flip(function() {
    -     *   return _.toArray(arguments);
    -     * });
    -     *
    -     * flipped('a', 'b', 'c', 'd');
    -     * // => ['d', 'c', 'b', 'a']
    -     */
    -    function flip(func) {
    -      return createWrap(func, WRAP_FLIP_FLAG);
    -    }
    -
    -    /**
    -     * Creates a function that memoizes the result of `func`. If `resolver` is
    -     * provided, it determines the cache key for storing the result based on the
    -     * arguments provided to the memoized function. By default, the first argument
    -     * provided to the memoized function is used as the map cache key. The `func`
    -     * is invoked with the `this` binding of the memoized function.
    -     *
    -     * **Note:** The cache is exposed as the `cache` property on the memoized
    -     * function. Its creation may be customized by replacing the `_.memoize.Cache`
    -     * constructor with one whose instances implement the
    -     * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
    -     * method interface of `clear`, `delete`, `get`, `has`, and `set`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Function
    -     * @param {Function} func The function to have its output memoized.
    -     * @param {Function} [resolver] The function to resolve the cache key.
    -     * @returns {Function} Returns the new memoized function.
    -     * @example
    -     *
    -     * var object = { 'a': 1, 'b': 2 };
    -     * var other = { 'c': 3, 'd': 4 };
    -     *
    -     * var values = _.memoize(_.values);
    -     * values(object);
    -     * // => [1, 2]
    -     *
    -     * values(other);
    -     * // => [3, 4]
    -     *
    -     * object.a = 2;
    -     * values(object);
    -     * // => [1, 2]
    -     *
    -     * // Modify the result cache.
    -     * values.cache.set(object, ['a', 'b']);
    -     * values(object);
    -     * // => ['a', 'b']
    -     *
    -     * // Replace `_.memoize.Cache`.
    -     * _.memoize.Cache = WeakMap;
    -     */
    -    function memoize(func, resolver) {
    -      if (typeof func != 'function' || resolver != null && typeof resolver != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      var memoized = function memoized() {
    -        var args = arguments,
    -          key = resolver ? resolver.apply(this, args) : args[0],
    -          cache = memoized.cache;
    -        if (cache.has(key)) {
    -          return cache.get(key);
    -        }
    -        var result = func.apply(this, args);
    -        memoized.cache = cache.set(key, result) || cache;
    -        return result;
    -      };
    -      memoized.cache = new (memoize.Cache || MapCache)();
    -      return memoized;
    -    }
    -
    -    // Expose `MapCache`.
    -    memoize.Cache = MapCache;
    -
    -    /**
    -     * Creates a function that negates the result of the predicate `func`. The
    -     * `func` predicate is invoked with the `this` binding and arguments of the
    -     * created function.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Function
    -     * @param {Function} predicate The predicate to negate.
    -     * @returns {Function} Returns the new negated function.
    -     * @example
    -     *
    -     * function isEven(n) {
    -     *   return n % 2 == 0;
    -     * }
    -     *
    -     * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
    -     * // => [1, 3, 5]
    -     */
    -    function negate(predicate) {
    -      if (typeof predicate != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      return function () {
    -        var args = arguments;
    -        switch (args.length) {
    -          case 0:
    -            return !predicate.call(this);
    -          case 1:
    -            return !predicate.call(this, args[0]);
    -          case 2:
    -            return !predicate.call(this, args[0], args[1]);
    -          case 3:
    -            return !predicate.call(this, args[0], args[1], args[2]);
    -        }
    -        return !predicate.apply(this, args);
    -      };
    -    }
    -
    -    /**
    -     * Creates a function that is restricted to invoking `func` once. Repeat calls
    -     * to the function return the value of the first invocation. The `func` is
    -     * invoked with the `this` binding and arguments of the created function.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Function
    -     * @param {Function} func The function to restrict.
    -     * @returns {Function} Returns the new restricted function.
    -     * @example
    -     *
    -     * var initialize = _.once(createApplication);
    -     * initialize();
    -     * initialize();
    -     * // => `createApplication` is invoked once
    -     */
    -    function once(func) {
    -      return before(2, func);
    -    }
    -
    -    /**
    -     * Creates a function that invokes `func` with its arguments transformed.
    -     *
    -     * @static
    -     * @since 4.0.0
    -     * @memberOf _
    -     * @category Function
    -     * @param {Function} func The function to wrap.
    -     * @param {...(Function|Function[])} [transforms=[_.identity]]
    -     *  The argument transforms.
    -     * @returns {Function} Returns the new function.
    -     * @example
    -     *
    -     * function doubled(n) {
    -     *   return n * 2;
    -     * }
    -     *
    -     * function square(n) {
    -     *   return n * n;
    -     * }
    -     *
    -     * var func = _.overArgs(function(x, y) {
    -     *   return [x, y];
    -     * }, [square, doubled]);
    -     *
    -     * func(9, 3);
    -     * // => [81, 6]
    -     *
    -     * func(10, 5);
    -     * // => [100, 10]
    -     */
    -    var overArgs = castRest(function (func, transforms) {
    -      transforms = transforms.length == 1 && isArray(transforms[0]) ? arrayMap(transforms[0], baseUnary(getIteratee())) : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee()));
    -      var funcsLength = transforms.length;
    -      return baseRest(function (args) {
    -        var index = -1,
    -          length = nativeMin(args.length, funcsLength);
    -        while (++index < length) {
    -          args[index] = transforms[index].call(this, args[index]);
    -        }
    -        return apply(func, this, args);
    -      });
    -    });
    -
    -    /**
    -     * Creates a function that invokes `func` with `partials` prepended to the
    -     * arguments it receives. This method is like `_.bind` except it does **not**
    -     * alter the `this` binding.
    -     *
    -     * The `_.partial.placeholder` value, which defaults to `_` in monolithic
    -     * builds, may be used as a placeholder for partially applied arguments.
    -     *
    -     * **Note:** This method doesn't set the "length" property of partially
    -     * applied functions.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.2.0
    -     * @category Function
    -     * @param {Function} func The function to partially apply arguments to.
    -     * @param {...*} [partials] The arguments to be partially applied.
    -     * @returns {Function} Returns the new partially applied function.
    -     * @example
    -     *
    -     * function greet(greeting, name) {
    -     *   return greeting + ' ' + name;
    -     * }
    -     *
    -     * var sayHelloTo = _.partial(greet, 'hello');
    -     * sayHelloTo('fred');
    -     * // => 'hello fred'
    -     *
    -     * // Partially applied with placeholders.
    -     * var greetFred = _.partial(greet, _, 'fred');
    -     * greetFred('hi');
    -     * // => 'hi fred'
    -     */
    -    var partial = baseRest(function (func, partials) {
    -      var holders = replaceHolders(partials, getHolder(partial));
    -      return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders);
    -    });
    -
    -    /**
    -     * This method is like `_.partial` except that partially applied arguments
    -     * are appended to the arguments it receives.
    -     *
    -     * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
    -     * builds, may be used as a placeholder for partially applied arguments.
    -     *
    -     * **Note:** This method doesn't set the "length" property of partially
    -     * applied functions.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 1.0.0
    -     * @category Function
    -     * @param {Function} func The function to partially apply arguments to.
    -     * @param {...*} [partials] The arguments to be partially applied.
    -     * @returns {Function} Returns the new partially applied function.
    -     * @example
    -     *
    -     * function greet(greeting, name) {
    -     *   return greeting + ' ' + name;
    -     * }
    -     *
    -     * var greetFred = _.partialRight(greet, 'fred');
    -     * greetFred('hi');
    -     * // => 'hi fred'
    -     *
    -     * // Partially applied with placeholders.
    -     * var sayHelloTo = _.partialRight(greet, 'hello', _);
    -     * sayHelloTo('fred');
    -     * // => 'hello fred'
    -     */
    -    var partialRight = baseRest(function (func, partials) {
    -      var holders = replaceHolders(partials, getHolder(partialRight));
    -      return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders);
    -    });
    -
    -    /**
    -     * Creates a function that invokes `func` with arguments arranged according
    -     * to the specified `indexes` where the argument value at the first index is
    -     * provided as the first argument, the argument value at the second index is
    -     * provided as the second argument, and so on.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.0.0
    -     * @category Function
    -     * @param {Function} func The function to rearrange arguments for.
    -     * @param {...(number|number[])} indexes The arranged argument indexes.
    -     * @returns {Function} Returns the new function.
    -     * @example
    -     *
    -     * var rearged = _.rearg(function(a, b, c) {
    -     *   return [a, b, c];
    -     * }, [2, 0, 1]);
    -     *
    -     * rearged('b', 'c', 'a')
    -     * // => ['a', 'b', 'c']
    -     */
    -    var rearg = flatRest(function (func, indexes) {
    -      return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);
    -    });
    -
    -    /**
    -     * Creates a function that invokes `func` with the `this` binding of the
    -     * created function and arguments from `start` and beyond provided as
    -     * an array.
    -     *
    -     * **Note:** This method is based on the
    -     * [rest parameter](https://mdn.io/rest_parameters).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Function
    -     * @param {Function} func The function to apply a rest parameter to.
    -     * @param {number} [start=func.length-1] The start position of the rest parameter.
    -     * @returns {Function} Returns the new function.
    -     * @example
    -     *
    -     * var say = _.rest(function(what, names) {
    -     *   return what + ' ' + _.initial(names).join(', ') +
    -     *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
    -     * });
    -     *
    -     * say('hello', 'fred', 'barney', 'pebbles');
    -     * // => 'hello fred, barney, & pebbles'
    -     */
    -    function rest(func, start) {
    -      if (typeof func != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      start = start === undefined ? start : toInteger(start);
    -      return baseRest(func, start);
    -    }
    -
    -    /**
    -     * Creates a function that invokes `func` with the `this` binding of the
    -     * create function and an array of arguments much like
    -     * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply).
    -     *
    -     * **Note:** This method is based on the
    -     * [spread operator](https://mdn.io/spread_operator).
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 3.2.0
    -     * @category Function
    -     * @param {Function} func The function to spread arguments over.
    -     * @param {number} [start=0] The start position of the spread.
    -     * @returns {Function} Returns the new function.
    -     * @example
    -     *
    -     * var say = _.spread(function(who, what) {
    -     *   return who + ' says ' + what;
    -     * });
    -     *
    -     * say(['fred', 'hello']);
    -     * // => 'fred says hello'
    -     *
    -     * var numbers = Promise.all([
    -     *   Promise.resolve(40),
    -     *   Promise.resolve(36)
    -     * ]);
    -     *
    -     * numbers.then(_.spread(function(x, y) {
    -     *   return x + y;
    -     * }));
    -     * // => a Promise of 76
    -     */
    -    function spread(func, start) {
    -      if (typeof func != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      start = start == null ? 0 : nativeMax(toInteger(start), 0);
    -      return baseRest(function (args) {
    -        var array = args[start],
    -          otherArgs = castSlice(args, 0, start);
    -        if (array) {
    -          arrayPush(otherArgs, array);
    -        }
    -        return apply(func, this, otherArgs);
    -      });
    -    }
    -
    -    /**
    -     * Creates a throttled function that only invokes `func` at most once per
    -     * every `wait` milliseconds. The throttled function comes with a `cancel`
    -     * method to cancel delayed `func` invocations and a `flush` method to
    -     * immediately invoke them. Provide `options` to indicate whether `func`
    -     * should be invoked on the leading and/or trailing edge of the `wait`
    -     * timeout. The `func` is invoked with the last arguments provided to the
    -     * throttled function. Subsequent calls to the throttled function return the
    -     * result of the last `func` invocation.
    -     *
    -     * **Note:** If `leading` and `trailing` options are `true`, `func` is
    -     * invoked on the trailing edge of the timeout only if the throttled function
    -     * is invoked more than once during the `wait` timeout.
    -     *
    -     * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
    -     * until to the next tick, similar to `setTimeout` with a timeout of `0`.
    -     *
    -     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
    -     * for details over the differences between `_.throttle` and `_.debounce`.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Function
    -     * @param {Function} func The function to throttle.
    -     * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
    -     * @param {Object} [options={}] The options object.
    -     * @param {boolean} [options.leading=true]
    -     *  Specify invoking on the leading edge of the timeout.
    -     * @param {boolean} [options.trailing=true]
    -     *  Specify invoking on the trailing edge of the timeout.
    -     * @returns {Function} Returns the new throttled function.
    -     * @example
    -     *
    -     * // Avoid excessively updating the position while scrolling.
    -     * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
    -     *
    -     * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
    -     * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
    -     * jQuery(element).on('click', throttled);
    -     *
    -     * // Cancel the trailing throttled invocation.
    -     * jQuery(window).on('popstate', throttled.cancel);
    -     */
    -    function throttle(func, wait, options) {
    -      var leading = true,
    -        trailing = true;
    -      if (typeof func != 'function') {
    -        throw new TypeError(FUNC_ERROR_TEXT);
    -      }
    -      if (isObject(options)) {
    -        leading = 'leading' in options ? !!options.leading : leading;
    -        trailing = 'trailing' in options ? !!options.trailing : trailing;
    -      }
    -      return debounce(func, wait, {
    -        'leading': leading,
    -        'maxWait': wait,
    -        'trailing': trailing
    -      });
    -    }
    -
    -    /**
    -     * Creates a function that accepts up to one argument, ignoring any
    -     * additional arguments.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 4.0.0
    -     * @category Function
    -     * @param {Function} func The function to cap arguments for.
    -     * @returns {Function} Returns the new capped function.
    -     * @example
    -     *
    -     * _.map(['6', '8', '10'], _.unary(parseInt));
    -     * // => [6, 8, 10]
    -     */
    -    function unary(func) {
    -      return ary(func, 1);
    -    }
    -
    -    /**
    -     * Creates a function that provides `value` to `wrapper` as its first
    -     * argument. Any additional arguments provided to the function are appended
    -     * to those provided to the `wrapper`. The wrapper is invoked with the `this`
    -     * binding of the created function.
    -     *
    -     * @static
    -     * @memberOf _
    -     * @since 0.1.0
    -     * @category Function
    -     * @param {*} value The value to wrap.
    -     * @param {Function} [wrapper=identity] The wrapper function.
    -     * @returns {Function} Returns the new function.
    -     * @example
    -     *
    -     * var p = _.wrap(_.escape, function(func, text) {
    -     *   return '

    ' + func(text) + '

    '; - * }); - * - * p('fred, barney, & pebbles'); - * // => '

    fred, barney, & pebbles

    ' - */ - function wrap(value, wrapper) { - return partial(castFunction(wrapper), value); - } - - /*------------------------------------------------------------------------*/ - - /** - * Casts `value` as an array if it's not one. - * - * @static - * @memberOf _ - * @since 4.4.0 - * @category Lang - * @param {*} value The value to inspect. - * @returns {Array} Returns the cast array. - * @example - * - * _.castArray(1); - * // => [1] - * - * _.castArray({ 'a': 1 }); - * // => [{ 'a': 1 }] - * - * _.castArray('abc'); - * // => ['abc'] - * - * _.castArray(null); - * // => [null] - * - * _.castArray(undefined); - * // => [undefined] - * - * _.castArray(); - * // => [] - * - * var array = [1, 2, 3]; - * console.log(_.castArray(array) === array); - * // => true - */ - function castArray() { - if (!arguments.length) { - return []; - } - var value = arguments[0]; - return isArray(value) ? value : [value]; - } - - /** - * Creates a shallow clone of `value`. - * - * **Note:** This method is loosely based on the - * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) - * and supports cloning arrays, array buffers, booleans, date objects, maps, - * numbers, `Object` objects, regexes, sets, strings, symbols, and typed - * arrays. The own enumerable properties of `arguments` objects are cloned - * as plain objects. An empty object is returned for uncloneable values such - * as error objects, functions, DOM nodes, and WeakMaps. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to clone. - * @returns {*} Returns the cloned value. - * @see _.cloneDeep - * @example - * - * var objects = [{ 'a': 1 }, { 'b': 2 }]; - * - * var shallow = _.clone(objects); - * console.log(shallow[0] === objects[0]); - * // => true - */ - function clone(value) { - return baseClone(value, CLONE_SYMBOLS_FLAG); - } - - /** - * This method is like `_.clone` except that it accepts `customizer` which - * is invoked to produce the cloned value. If `customizer` returns `undefined`, - * cloning is handled by the method instead. The `customizer` is invoked with - * up to four arguments; (value [, index|key, object, stack]). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to clone. - * @param {Function} [customizer] The function to customize cloning. - * @returns {*} Returns the cloned value. - * @see _.cloneDeepWith - * @example - * - * function customizer(value) { - * if (_.isElement(value)) { - * return value.cloneNode(false); - * } - * } - * - * var el = _.cloneWith(document.body, customizer); - * - * console.log(el === document.body); - * // => false - * console.log(el.nodeName); - * // => 'BODY' - * console.log(el.childNodes.length); - * // => 0 - */ - function cloneWith(value, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); - } - - /** - * This method is like `_.clone` except that it recursively clones `value`. - * - * @static - * @memberOf _ - * @since 1.0.0 - * @category Lang - * @param {*} value The value to recursively clone. - * @returns {*} Returns the deep cloned value. - * @see _.clone - * @example - * - * var objects = [{ 'a': 1 }, { 'b': 2 }]; - * - * var deep = _.cloneDeep(objects); - * console.log(deep[0] === objects[0]); - * // => false - */ - function cloneDeep(value) { - return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); - } - - /** - * This method is like `_.cloneWith` except that it recursively clones `value`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to recursively clone. - * @param {Function} [customizer] The function to customize cloning. - * @returns {*} Returns the deep cloned value. - * @see _.cloneWith - * @example - * - * function customizer(value) { - * if (_.isElement(value)) { - * return value.cloneNode(true); - * } - * } - * - * var el = _.cloneDeepWith(document.body, customizer); - * - * console.log(el === document.body); - * // => false - * console.log(el.nodeName); - * // => 'BODY' - * console.log(el.childNodes.length); - * // => 20 - */ - function cloneDeepWith(value, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); - } - - /** - * Checks if `object` conforms to `source` by invoking the predicate - * properties of `source` with the corresponding property values of `object`. - * - * **Note:** This method is equivalent to `_.conforms` when `source` is - * partially applied. - * - * @static - * @memberOf _ - * @since 4.14.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property predicates to conform to. - * @returns {boolean} Returns `true` if `object` conforms, else `false`. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * - * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); - * // => true - * - * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); - * // => false - */ - function conformsTo(object, source) { - return source == null || baseConformsTo(object, source, keys(source)); - } - - /** - * Performs a - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * comparison between two values to determine if they are equivalent. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'a': 1 }; - * var other = { 'a': 1 }; - * - * _.eq(object, object); - * // => true - * - * _.eq(object, other); - * // => false - * - * _.eq('a', 'a'); - * // => true - * - * _.eq('a', Object('a')); - * // => false - * - * _.eq(NaN, NaN); - * // => true - */ - function eq(value, other) { - return value === other || value !== value && other !== other; - } - - /** - * Checks if `value` is greater than `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than `other`, - * else `false`. - * @see _.lt - * @example - * - * _.gt(3, 1); - * // => true - * - * _.gt(3, 3); - * // => false - * - * _.gt(1, 3); - * // => false - */ - var gt = createRelationalOperation(baseGt); - - /** - * Checks if `value` is greater than or equal to `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than or equal to - * `other`, else `false`. - * @see _.lte - * @example - * - * _.gte(3, 1); - * // => true - * - * _.gte(3, 3); - * // => true - * - * _.gte(1, 3); - * // => false - */ - var gte = createRelationalOperation(function (value, other) { - return value >= other; - }); - - /** - * Checks if `value` is likely an `arguments` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, - * else `false`. - * @example - * - * _.isArguments(function() { return arguments; }()); - * // => true - * - * _.isArguments([1, 2, 3]); - * // => false - */ - var isArguments = baseIsArguments(function () { - return arguments; - }()) ? baseIsArguments : function (value) { - return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); - }; - - /** - * Checks if `value` is classified as an `Array` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array, else `false`. - * @example - * - * _.isArray([1, 2, 3]); - * // => true - * - * _.isArray(document.body.children); - * // => false - * - * _.isArray('abc'); - * // => false - * - * _.isArray(_.noop); - * // => false - */ - var isArray = Array.isArray; - - /** - * Checks if `value` is classified as an `ArrayBuffer` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. - * @example - * - * _.isArrayBuffer(new ArrayBuffer(2)); - * // => true - * - * _.isArrayBuffer(new Array(2)); - * // => false - */ - var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; - - /** - * Checks if `value` is array-like. A value is considered array-like if it's - * not a function and has a `value.length` that's an integer greater than or - * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is array-like, else `false`. - * @example - * - * _.isArrayLike([1, 2, 3]); - * // => true - * - * _.isArrayLike(document.body.children); - * // => true - * - * _.isArrayLike('abc'); - * // => true - * - * _.isArrayLike(_.noop); - * // => false - */ - function isArrayLike(value) { - return value != null && isLength(value.length) && !isFunction(value); - } - - /** - * This method is like `_.isArrayLike` except that it also checks if `value` - * is an object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array-like object, - * else `false`. - * @example - * - * _.isArrayLikeObject([1, 2, 3]); - * // => true - * - * _.isArrayLikeObject(document.body.children); - * // => true - * - * _.isArrayLikeObject('abc'); - * // => false - * - * _.isArrayLikeObject(_.noop); - * // => false - */ - function isArrayLikeObject(value) { - return isObjectLike(value) && isArrayLike(value); - } - - /** - * Checks if `value` is classified as a boolean primitive or object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. - * @example - * - * _.isBoolean(false); - * // => true - * - * _.isBoolean(null); - * // => false - */ - function isBoolean(value) { - return value === true || value === false || isObjectLike(value) && baseGetTag(value) == boolTag; - } - - /** - * Checks if `value` is a buffer. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. - * @example - * - * _.isBuffer(new Buffer(2)); - * // => true - * - * _.isBuffer(new Uint8Array(2)); - * // => false - */ - var isBuffer = nativeIsBuffer || stubFalse; - - /** - * Checks if `value` is classified as a `Date` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a date object, else `false`. - * @example - * - * _.isDate(new Date); - * // => true - * - * _.isDate('Mon April 23 2012'); - * // => false - */ - var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; - - /** - * Checks if `value` is likely a DOM element. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. - * @example - * - * _.isElement(document.body); - * // => true - * - * _.isElement(''); - * // => false - */ - function isElement(value) { - return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); - } - - /** - * Checks if `value` is an empty object, collection, map, or set. - * - * Objects are considered empty if they have no own enumerable string keyed - * properties. - * - * Array-like values such as `arguments` objects, arrays, buffers, strings, or - * jQuery-like collections are considered empty if they have a `length` of `0`. - * Similarly, maps and sets are considered empty if they have a `size` of `0`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is empty, else `false`. - * @example - * - * _.isEmpty(null); - * // => true - * - * _.isEmpty(true); - * // => true - * - * _.isEmpty(1); - * // => true - * - * _.isEmpty([1, 2, 3]); - * // => false - * - * _.isEmpty({ 'a': 1 }); - * // => false - */ - function isEmpty(value) { - if (value == null) { - return true; - } - if (isArrayLike(value) && (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || isBuffer(value) || isTypedArray(value) || isArguments(value))) { - return !value.length; - } - var tag = getTag(value); - if (tag == mapTag || tag == setTag) { - return !value.size; - } - if (isPrototype(value)) { - return !baseKeys(value).length; - } - for (var key in value) { - if (hasOwnProperty.call(value, key)) { - return false; - } - } - return true; - } - - /** - * Performs a deep comparison between two values to determine if they are - * equivalent. - * - * **Note:** This method supports comparing arrays, array buffers, booleans, - * date objects, error objects, maps, numbers, `Object` objects, regexes, - * sets, strings, symbols, and typed arrays. `Object` objects are compared - * by their own, not inherited, enumerable properties. Functions and DOM - * nodes are compared by strict equality, i.e. `===`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'a': 1 }; - * var other = { 'a': 1 }; - * - * _.isEqual(object, other); - * // => true - * - * object === other; - * // => false - */ - function isEqual(value, other) { - return baseIsEqual(value, other); - } - - /** - * This method is like `_.isEqual` except that it accepts `customizer` which - * is invoked to compare values. If `customizer` returns `undefined`, comparisons - * are handled by the method instead. The `customizer` is invoked with up to - * six arguments: (objValue, othValue [, index|key, object, other, stack]). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * function isGreeting(value) { - * return /^h(?:i|ello)$/.test(value); - * } - * - * function customizer(objValue, othValue) { - * if (isGreeting(objValue) && isGreeting(othValue)) { - * return true; - * } - * } - * - * var array = ['hello', 'goodbye']; - * var other = ['hi', 'goodbye']; - * - * _.isEqualWith(array, other, customizer); - * // => true - */ - function isEqualWith(value, other, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - var result = customizer ? customizer(value, other) : undefined; - return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; - } - - /** - * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, - * `SyntaxError`, `TypeError`, or `URIError` object. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an error object, else `false`. - * @example - * - * _.isError(new Error); - * // => true - * - * _.isError(Error); - * // => false - */ - function isError(value) { - if (!isObjectLike(value)) { - return false; - } - var tag = baseGetTag(value); - return tag == errorTag || tag == domExcTag || typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value); - } - - /** - * Checks if `value` is a finite primitive number. - * - * **Note:** This method is based on - * [`Number.isFinite`](https://mdn.io/Number/isFinite). - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. - * @example - * - * _.isFinite(3); - * // => true - * - * _.isFinite(Number.MIN_VALUE); - * // => true - * - * _.isFinite(Infinity); - * // => false - * - * _.isFinite('3'); - * // => false - */ - function isFinite(value) { - return typeof value == 'number' && nativeIsFinite(value); - } - - /** - * Checks if `value` is classified as a `Function` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a function, else `false`. - * @example - * - * _.isFunction(_); - * // => true - * - * _.isFunction(/abc/); - * // => false - */ - function isFunction(value) { - if (!isObject(value)) { - return false; - } - // The use of `Object#toString` avoids issues with the `typeof` operator - // in Safari 9 which returns 'object' for typed arrays and other constructors. - var tag = baseGetTag(value); - return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; - } - - /** - * Checks if `value` is an integer. - * - * **Note:** This method is based on - * [`Number.isInteger`](https://mdn.io/Number/isInteger). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an integer, else `false`. - * @example - * - * _.isInteger(3); - * // => true - * - * _.isInteger(Number.MIN_VALUE); - * // => false - * - * _.isInteger(Infinity); - * // => false - * - * _.isInteger('3'); - * // => false - */ - function isInteger(value) { - return typeof value == 'number' && value == toInteger(value); - } - - /** - * Checks if `value` is a valid array-like length. - * - * **Note:** This method is loosely based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - * @example - * - * _.isLength(3); - * // => true - * - * _.isLength(Number.MIN_VALUE); - * // => false - * - * _.isLength(Infinity); - * // => false - * - * _.isLength('3'); - * // => false - */ - function isLength(value) { - return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; - } - - /** - * Checks if `value` is the - * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) - * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(_.noop); - * // => true - * - * _.isObject(null); - * // => false - */ - function isObject(value) { - var type = typeof value; - return value != null && (type == 'object' || type == 'function'); - } - - /** - * Checks if `value` is object-like. A value is object-like if it's not `null` - * and has a `typeof` result of "object". - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - * @example - * - * _.isObjectLike({}); - * // => true - * - * _.isObjectLike([1, 2, 3]); - * // => true - * - * _.isObjectLike(_.noop); - * // => false - * - * _.isObjectLike(null); - * // => false - */ - function isObjectLike(value) { - return value != null && typeof value == 'object'; - } - - /** - * Checks if `value` is classified as a `Map` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a map, else `false`. - * @example - * - * _.isMap(new Map); - * // => true - * - * _.isMap(new WeakMap); - * // => false - */ - var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; - - /** - * Performs a partial deep comparison between `object` and `source` to - * determine if `object` contains equivalent property values. - * - * **Note:** This method is equivalent to `_.matches` when `source` is - * partially applied. - * - * Partial comparisons will match empty array and empty object `source` - * values against any array or object value, respectively. See `_.isEqual` - * for a list of supported value comparisons. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * - * _.isMatch(object, { 'b': 2 }); - * // => true - * - * _.isMatch(object, { 'b': 1 }); - * // => false - */ - function isMatch(object, source) { - return object === source || baseIsMatch(object, source, getMatchData(source)); - } - - /** - * This method is like `_.isMatch` except that it accepts `customizer` which - * is invoked to compare values. If `customizer` returns `undefined`, comparisons - * are handled by the method instead. The `customizer` is invoked with five - * arguments: (objValue, srcValue, index|key, object, source). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * function isGreeting(value) { - * return /^h(?:i|ello)$/.test(value); - * } - * - * function customizer(objValue, srcValue) { - * if (isGreeting(objValue) && isGreeting(srcValue)) { - * return true; - * } - * } - * - * var object = { 'greeting': 'hello' }; - * var source = { 'greeting': 'hi' }; - * - * _.isMatchWith(object, source, customizer); - * // => true - */ - function isMatchWith(object, source, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return baseIsMatch(object, source, getMatchData(source), customizer); - } - - /** - * Checks if `value` is `NaN`. - * - * **Note:** This method is based on - * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as - * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for - * `undefined` and other non-number values. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. - * @example - * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true - * - * _.isNaN(undefined); - * // => false - */ - function isNaN(value) { - // An `NaN` primitive is the only value that is not equal to itself. - // Perform the `toStringTag` check first to avoid errors with some - // ActiveX objects in IE. - return isNumber(value) && value != +value; - } - - /** - * Checks if `value` is a pristine native function. - * - * **Note:** This method can't reliably detect native functions in the presence - * of the core-js package because core-js circumvents this kind of detection. - * Despite multiple requests, the core-js maintainer has made it clear: any - * attempt to fix the detection will be obstructed. As a result, we're left - * with little choice but to throw an error. Unfortunately, this also affects - * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), - * which rely on core-js. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, - * else `false`. - * @example - * - * _.isNative(Array.prototype.push); - * // => true - * - * _.isNative(_); - * // => false - */ - function isNative(value) { - if (isMaskable(value)) { - throw new Error(CORE_ERROR_TEXT); - } - return baseIsNative(value); - } - - /** - * Checks if `value` is `null`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `null`, else `false`. - * @example - * - * _.isNull(null); - * // => true - * - * _.isNull(void 0); - * // => false - */ - function isNull(value) { - return value === null; - } - - /** - * Checks if `value` is `null` or `undefined`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is nullish, else `false`. - * @example - * - * _.isNil(null); - * // => true - * - * _.isNil(void 0); - * // => true - * - * _.isNil(NaN); - * // => false - */ - function isNil(value) { - return value == null; - } - - /** - * Checks if `value` is classified as a `Number` primitive or object. - * - * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are - * classified as numbers, use the `_.isFinite` method. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a number, else `false`. - * @example - * - * _.isNumber(3); - * // => true - * - * _.isNumber(Number.MIN_VALUE); - * // => true - * - * _.isNumber(Infinity); - * // => true - * - * _.isNumber('3'); - * // => false - */ - function isNumber(value) { - return typeof value == 'number' || isObjectLike(value) && baseGetTag(value) == numberTag; - } - - /** - * Checks if `value` is a plain object, that is, an object created by the - * `Object` constructor or one with a `[[Prototype]]` of `null`. - * - * @static - * @memberOf _ - * @since 0.8.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * _.isPlainObject(new Foo); - * // => false - * - * _.isPlainObject([1, 2, 3]); - * // => false - * - * _.isPlainObject({ 'x': 0, 'y': 0 }); - * // => true - * - * _.isPlainObject(Object.create(null)); - * // => true - */ - function isPlainObject(value) { - if (!isObjectLike(value) || baseGetTag(value) != objectTag) { - return false; - } - var proto = getPrototype(value); - if (proto === null) { - return true; - } - var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; - return typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString; - } - - /** - * Checks if `value` is classified as a `RegExp` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. - * @example - * - * _.isRegExp(/abc/); - * // => true - * - * _.isRegExp('/abc/'); - * // => false - */ - var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; - - /** - * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 - * double precision number which isn't the result of a rounded unsafe integer. - * - * **Note:** This method is based on - * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. - * @example - * - * _.isSafeInteger(3); - * // => true - * - * _.isSafeInteger(Number.MIN_VALUE); - * // => false - * - * _.isSafeInteger(Infinity); - * // => false - * - * _.isSafeInteger('3'); - * // => false - */ - function isSafeInteger(value) { - return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER; - } - - /** - * Checks if `value` is classified as a `Set` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a set, else `false`. - * @example - * - * _.isSet(new Set); - * // => true - * - * _.isSet(new WeakSet); - * // => false - */ - var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; - - /** - * Checks if `value` is classified as a `String` primitive or object. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a string, else `false`. - * @example - * - * _.isString('abc'); - * // => true - * - * _.isString(1); - * // => false - */ - function isString(value) { - return typeof value == 'string' || !isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag; - } - - /** - * Checks if `value` is classified as a `Symbol` primitive or object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. - * @example - * - * _.isSymbol(Symbol.iterator); - * // => true - * - * _.isSymbol('abc'); - * // => false - */ - function isSymbol(value) { - return typeof value == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag; - } - - /** - * Checks if `value` is classified as a typed array. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - * @example - * - * _.isTypedArray(new Uint8Array); - * // => true - * - * _.isTypedArray([]); - * // => false - */ - var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; - - /** - * Checks if `value` is `undefined`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. - * @example - * - * _.isUndefined(void 0); - * // => true - * - * _.isUndefined(null); - * // => false - */ - function isUndefined(value) { - return value === undefined; - } - - /** - * Checks if `value` is classified as a `WeakMap` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. - * @example - * - * _.isWeakMap(new WeakMap); - * // => true - * - * _.isWeakMap(new Map); - * // => false - */ - function isWeakMap(value) { - return isObjectLike(value) && getTag(value) == weakMapTag; - } - - /** - * Checks if `value` is classified as a `WeakSet` object. - * - * @static - * @memberOf _ - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. - * @example - * - * _.isWeakSet(new WeakSet); - * // => true - * - * _.isWeakSet(new Set); - * // => false - */ - function isWeakSet(value) { - return isObjectLike(value) && baseGetTag(value) == weakSetTag; - } - - /** - * Checks if `value` is less than `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than `other`, - * else `false`. - * @see _.gt - * @example - * - * _.lt(1, 3); - * // => true - * - * _.lt(3, 3); - * // => false - * - * _.lt(3, 1); - * // => false - */ - var lt = createRelationalOperation(baseLt); - - /** - * Checks if `value` is less than or equal to `other`. - * - * @static - * @memberOf _ - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than or equal to - * `other`, else `false`. - * @see _.gte - * @example - * - * _.lte(1, 3); - * // => true - * - * _.lte(3, 3); - * // => true - * - * _.lte(3, 1); - * // => false - */ - var lte = createRelationalOperation(function (value, other) { - return value <= other; - }); - - /** - * Converts `value` to an array. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Lang - * @param {*} value The value to convert. - * @returns {Array} Returns the converted array. - * @example - * - * _.toArray({ 'a': 1, 'b': 2 }); - * // => [1, 2] - * - * _.toArray('abc'); - * // => ['a', 'b', 'c'] - * - * _.toArray(1); - * // => [] - * - * _.toArray(null); - * // => [] - */ - function toArray(value) { - if (!value) { - return []; - } - if (isArrayLike(value)) { - return isString(value) ? stringToArray(value) : copyArray(value); - } - if (symIterator && value[symIterator]) { - return iteratorToArray(value[symIterator]()); - } - var tag = getTag(value), - func = tag == mapTag ? mapToArray : tag == setTag ? setToArray : values; - return func(value); - } - - /** - * Converts `value` to a finite number. - * - * @static - * @memberOf _ - * @since 4.12.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted number. - * @example - * - * _.toFinite(3.2); - * // => 3.2 - * - * _.toFinite(Number.MIN_VALUE); - * // => 5e-324 - * - * _.toFinite(Infinity); - * // => 1.7976931348623157e+308 - * - * _.toFinite('3.2'); - * // => 3.2 - */ - function toFinite(value) { - if (!value) { - return value === 0 ? value : 0; - } - value = toNumber(value); - if (value === INFINITY || value === -INFINITY) { - var sign = value < 0 ? -1 : 1; - return sign * MAX_INTEGER; - } - return value === value ? value : 0; - } - - /** - * Converts `value` to an integer. - * - * **Note:** This method is loosely based on - * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * _.toInteger(3.2); - * // => 3 - * - * _.toInteger(Number.MIN_VALUE); - * // => 0 - * - * _.toInteger(Infinity); - * // => 1.7976931348623157e+308 - * - * _.toInteger('3.2'); - * // => 3 - */ - function toInteger(value) { - var result = toFinite(value), - remainder = result % 1; - return result === result ? remainder ? result - remainder : result : 0; - } - - /** - * Converts `value` to an integer suitable for use as the length of an - * array-like object. - * - * **Note:** This method is based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * _.toLength(3.2); - * // => 3 - * - * _.toLength(Number.MIN_VALUE); - * // => 0 - * - * _.toLength(Infinity); - * // => 4294967295 - * - * _.toLength('3.2'); - * // => 3 - */ - function toLength(value) { - return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0; - } - - /** - * Converts `value` to a number. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to process. - * @returns {number} Returns the number. - * @example - * - * _.toNumber(3.2); - * // => 3.2 - * - * _.toNumber(Number.MIN_VALUE); - * // => 5e-324 - * - * _.toNumber(Infinity); - * // => Infinity - * - * _.toNumber('3.2'); - * // => 3.2 - */ - function toNumber(value) { - if (typeof value == 'number') { - return value; - } - if (isSymbol(value)) { - return NAN; - } - if (isObject(value)) { - var other = typeof value.valueOf == 'function' ? value.valueOf() : value; - value = isObject(other) ? other + '' : other; - } - if (typeof value != 'string') { - return value === 0 ? value : +value; - } - value = baseTrim(value); - var isBinary = reIsBinary.test(value); - return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value; - } - - /** - * Converts `value` to a plain object flattening inherited enumerable string - * keyed properties of `value` to own properties of the plain object. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {Object} Returns the converted plain object. - * @example - * - * function Foo() { - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.assign({ 'a': 1 }, new Foo); - * // => { 'a': 1, 'b': 2 } - * - * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); - * // => { 'a': 1, 'b': 2, 'c': 3 } - */ - function toPlainObject(value) { - return copyObject(value, keysIn(value)); - } - - /** - * Converts `value` to a safe integer. A safe integer can be compared and - * represented correctly. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * _.toSafeInteger(3.2); - * // => 3 - * - * _.toSafeInteger(Number.MIN_VALUE); - * // => 0 - * - * _.toSafeInteger(Infinity); - * // => 9007199254740991 - * - * _.toSafeInteger('3.2'); - * // => 3 - */ - function toSafeInteger(value) { - return value ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) : value === 0 ? value : 0; - } - - /** - * Converts `value` to a string. An empty string is returned for `null` - * and `undefined` values. The sign of `-0` is preserved. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - * @example - * - * _.toString(null); - * // => '' - * - * _.toString(-0); - * // => '-0' - * - * _.toString([1, 2, 3]); - * // => '1,2,3' - */ - function toString(value) { - return value == null ? '' : baseToString(value); - } - - /*------------------------------------------------------------------------*/ - - /** - * Assigns own enumerable string keyed properties of source objects to the - * destination object. Source objects are applied from left to right. - * Subsequent sources overwrite property assignments of previous sources. - * - * **Note:** This method mutates `object` and is loosely based on - * [`Object.assign`](https://mdn.io/Object/assign). - * - * @static - * @memberOf _ - * @since 0.10.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.assignIn - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * function Bar() { - * this.c = 3; - * } - * - * Foo.prototype.b = 2; - * Bar.prototype.d = 4; - * - * _.assign({ 'a': 0 }, new Foo, new Bar); - * // => { 'a': 1, 'c': 3 } - */ - var assign = createAssigner(function (object, source) { - if (isPrototype(source) || isArrayLike(source)) { - copyObject(source, keys(source), object); - return; - } - for (var key in source) { - if (hasOwnProperty.call(source, key)) { - assignValue(object, key, source[key]); - } - } - }); - - /** - * This method is like `_.assign` except that it iterates over own and - * inherited source properties. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias extend - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.assign - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * function Bar() { - * this.c = 3; - * } - * - * Foo.prototype.b = 2; - * Bar.prototype.d = 4; - * - * _.assignIn({ 'a': 0 }, new Foo, new Bar); - * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } - */ - var assignIn = createAssigner(function (object, source) { - copyObject(source, keysIn(source), object); - }); - - /** - * This method is like `_.assignIn` except that it accepts `customizer` - * which is invoked to produce the assigned values. If `customizer` returns - * `undefined`, assignment is handled by the method instead. The `customizer` - * is invoked with five arguments: (objValue, srcValue, key, object, source). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias extendWith - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @see _.assignWith - * @example - * - * function customizer(objValue, srcValue) { - * return _.isUndefined(objValue) ? srcValue : objValue; - * } - * - * var defaults = _.partialRight(_.assignInWith, customizer); - * - * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); - * // => { 'a': 1, 'b': 2 } - */ - var assignInWith = createAssigner(function (object, source, srcIndex, customizer) { - copyObject(source, keysIn(source), object, customizer); - }); - - /** - * This method is like `_.assign` except that it accepts `customizer` - * which is invoked to produce the assigned values. If `customizer` returns - * `undefined`, assignment is handled by the method instead. The `customizer` - * is invoked with five arguments: (objValue, srcValue, key, object, source). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @see _.assignInWith - * @example - * - * function customizer(objValue, srcValue) { - * return _.isUndefined(objValue) ? srcValue : objValue; - * } - * - * var defaults = _.partialRight(_.assignWith, customizer); - * - * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); - * // => { 'a': 1, 'b': 2 } - */ - var assignWith = createAssigner(function (object, source, srcIndex, customizer) { - copyObject(source, keys(source), object, customizer); - }); - - /** - * Creates an array of values corresponding to `paths` of `object`. - * - * @static - * @memberOf _ - * @since 1.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {...(string|string[])} [paths] The property paths to pick. - * @returns {Array} Returns the picked values. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; - * - * _.at(object, ['a[0].b.c', 'a[1]']); - * // => [3, 4] - */ - var at = flatRest(baseAt); - - /** - * Creates an object that inherits from the `prototype` object. If a - * `properties` object is given, its own enumerable string keyed properties - * are assigned to the created object. - * - * @static - * @memberOf _ - * @since 2.3.0 - * @category Object - * @param {Object} prototype The object to inherit from. - * @param {Object} [properties] The properties to assign to the object. - * @returns {Object} Returns the new object. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * function Circle() { - * Shape.call(this); - * } - * - * Circle.prototype = _.create(Shape.prototype, { - * 'constructor': Circle - * }); - * - * var circle = new Circle; - * circle instanceof Circle; - * // => true - * - * circle instanceof Shape; - * // => true - */ - function create(prototype, properties) { - var result = baseCreate(prototype); - return properties == null ? result : baseAssign(result, properties); - } - - /** - * Assigns own and inherited enumerable string keyed properties of source - * objects to the destination object for all destination properties that - * resolve to `undefined`. Source objects are applied from left to right. - * Once a property is set, additional values of the same property are ignored. - * - * **Note:** This method mutates `object`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.defaultsDeep - * @example - * - * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); - * // => { 'a': 1, 'b': 2 } - */ - var defaults = baseRest(function (object, sources) { - object = Object(object); - var index = -1; - var length = sources.length; - var guard = length > 2 ? sources[2] : undefined; - if (guard && isIterateeCall(sources[0], sources[1], guard)) { - length = 1; - } - while (++index < length) { - var source = sources[index]; - var props = keysIn(source); - var propsIndex = -1; - var propsLength = props.length; - while (++propsIndex < propsLength) { - var key = props[propsIndex]; - var value = object[key]; - if (value === undefined || eq(value, objectProto[key]) && !hasOwnProperty.call(object, key)) { - object[key] = source[key]; - } - } - } - return object; - }); - - /** - * This method is like `_.defaults` except that it recursively assigns - * default properties. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 3.10.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see _.defaults - * @example - * - * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); - * // => { 'a': { 'b': 2, 'c': 3 } } - */ - var defaultsDeep = baseRest(function (args) { - args.push(undefined, customDefaultsMerge); - return apply(mergeWith, undefined, args); - }); - - /** - * This method is like `_.find` except that it returns the key of the first - * element `predicate` returns truthy for instead of the element itself. - * - * @static - * @memberOf _ - * @since 1.1.0 - * @category Object - * @param {Object} object The object to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {string|undefined} Returns the key of the matched element, - * else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findKey(users, function(o) { return o.age < 40; }); - * // => 'barney' (iteration order is not guaranteed) - * - * // The `_.matches` iteratee shorthand. - * _.findKey(users, { 'age': 1, 'active': true }); - * // => 'pebbles' - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findKey(users, ['active', false]); - * // => 'fred' - * - * // The `_.property` iteratee shorthand. - * _.findKey(users, 'active'); - * // => 'barney' - */ - function findKey(object, predicate) { - return baseFindKey(object, getIteratee(predicate, 3), baseForOwn); - } - - /** - * This method is like `_.findKey` except that it iterates over elements of - * a collection in the opposite order. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Object - * @param {Object} object The object to inspect. - * @param {Function} [predicate=_.identity] The function invoked per iteration. - * @returns {string|undefined} Returns the key of the matched element, - * else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findLastKey(users, function(o) { return o.age < 40; }); - * // => returns 'pebbles' assuming `_.findKey` returns 'barney' - * - * // The `_.matches` iteratee shorthand. - * _.findLastKey(users, { 'age': 36, 'active': true }); - * // => 'barney' - * - * // The `_.matchesProperty` iteratee shorthand. - * _.findLastKey(users, ['active', false]); - * // => 'fred' - * - * // The `_.property` iteratee shorthand. - * _.findLastKey(users, 'active'); - * // => 'pebbles' - */ - function findLastKey(object, predicate) { - return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight); - } - - /** - * Iterates over own and inherited enumerable string keyed properties of an - * object and invokes `iteratee` for each property. The iteratee is invoked - * with three arguments: (value, key, object). Iteratee functions may exit - * iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @since 0.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forInRight - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forIn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). - */ - function forIn(object, iteratee) { - return object == null ? object : baseFor(object, getIteratee(iteratee, 3), keysIn); - } - - /** - * This method is like `_.forIn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forIn - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forInRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. - */ - function forInRight(object, iteratee) { - return object == null ? object : baseForRight(object, getIteratee(iteratee, 3), keysIn); - } - - /** - * Iterates over own enumerable string keyed properties of an object and - * invokes `iteratee` for each property. The iteratee is invoked with three - * arguments: (value, key, object). Iteratee functions may exit iteration - * early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @since 0.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forOwnRight - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'a' then 'b' (iteration order is not guaranteed). - */ - function forOwn(object, iteratee) { - return object && baseForOwn(object, getIteratee(iteratee, 3)); - } - - /** - * This method is like `_.forOwn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @since 2.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see _.forOwn - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwnRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. - */ - function forOwnRight(object, iteratee) { - return object && baseForOwnRight(object, getIteratee(iteratee, 3)); - } - - /** - * Creates an array of function property names from own enumerable properties - * of `object`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to inspect. - * @returns {Array} Returns the function names. - * @see _.functionsIn - * @example - * - * function Foo() { - * this.a = _.constant('a'); - * this.b = _.constant('b'); - * } - * - * Foo.prototype.c = _.constant('c'); - * - * _.functions(new Foo); - * // => ['a', 'b'] - */ - function functions(object) { - return object == null ? [] : baseFunctions(object, keys(object)); - } - - /** - * Creates an array of function property names from own and inherited - * enumerable properties of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to inspect. - * @returns {Array} Returns the function names. - * @see _.functions - * @example - * - * function Foo() { - * this.a = _.constant('a'); - * this.b = _.constant('b'); - * } - * - * Foo.prototype.c = _.constant('c'); - * - * _.functionsIn(new Foo); - * // => ['a', 'b', 'c'] - */ - function functionsIn(object) { - return object == null ? [] : baseFunctions(object, keysIn(object)); - } - - /** - * Gets the value at `path` of `object`. If the resolved value is - * `undefined`, the `defaultValue` is returned in its place. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.get(object, 'a[0].b.c'); - * // => 3 - * - * _.get(object, ['a', '0', 'b', 'c']); - * // => 3 - * - * _.get(object, 'a.b.c', 'default'); - * // => 'default' - */ - function get(object, path, defaultValue) { - var result = object == null ? undefined : baseGet(object, path); - return result === undefined ? defaultValue : result; - } - - /** - * Checks if `path` is a direct property of `object`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @example - * - * var object = { 'a': { 'b': 2 } }; - * var other = _.create({ 'a': _.create({ 'b': 2 }) }); - * - * _.has(object, 'a'); - * // => true - * - * _.has(object, 'a.b'); - * // => true - * - * _.has(object, ['a', 'b']); - * // => true - * - * _.has(other, 'a'); - * // => false - */ - function has(object, path) { - return object != null && hasPath(object, path, baseHas); - } - - /** - * Checks if `path` is a direct or inherited property of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @example - * - * var object = _.create({ 'a': _.create({ 'b': 2 }) }); - * - * _.hasIn(object, 'a'); - * // => true - * - * _.hasIn(object, 'a.b'); - * // => true - * - * _.hasIn(object, ['a', 'b']); - * // => true - * - * _.hasIn(object, 'b'); - * // => false - */ - function hasIn(object, path) { - return object != null && hasPath(object, path, baseHasIn); - } - - /** - * Creates an object composed of the inverted keys and values of `object`. - * If `object` contains duplicate values, subsequent values overwrite - * property assignments of previous values. - * - * @static - * @memberOf _ - * @since 0.7.0 - * @category Object - * @param {Object} object The object to invert. - * @returns {Object} Returns the new inverted object. - * @example - * - * var object = { 'a': 1, 'b': 2, 'c': 1 }; - * - * _.invert(object); - * // => { '1': 'c', '2': 'b' } - */ - var invert = createInverter(function (result, value, key) { - if (value != null && typeof value.toString != 'function') { - value = nativeObjectToString.call(value); - } - result[value] = key; - }, constant(identity)); - - /** - * This method is like `_.invert` except that the inverted object is generated - * from the results of running each element of `object` thru `iteratee`. The - * corresponding inverted value of each inverted key is an array of keys - * responsible for generating the inverted value. The iteratee is invoked - * with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.1.0 - * @category Object - * @param {Object} object The object to invert. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {Object} Returns the new inverted object. - * @example - * - * var object = { 'a': 1, 'b': 2, 'c': 1 }; - * - * _.invertBy(object); - * // => { '1': ['a', 'c'], '2': ['b'] } - * - * _.invertBy(object, function(value) { - * return 'group' + value; - * }); - * // => { 'group1': ['a', 'c'], 'group2': ['b'] } - */ - var invertBy = createInverter(function (result, value, key) { - if (value != null && typeof value.toString != 'function') { - value = nativeObjectToString.call(value); - } - if (hasOwnProperty.call(result, value)) { - result[value].push(key); - } else { - result[value] = [key]; - } - }, getIteratee); - - /** - * Invokes the method at `path` of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the method to invoke. - * @param {...*} [args] The arguments to invoke the method with. - * @returns {*} Returns the result of the invoked method. - * @example - * - * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; - * - * _.invoke(object, 'a[0].b.c.slice', 1, 3); - * // => [2, 3] - */ - var invoke = baseRest(baseInvoke); - - /** - * Creates an array of the own enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. See the - * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) - * for more details. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keys(new Foo); - * // => ['a', 'b'] (iteration order is not guaranteed) - * - * _.keys('hi'); - * // => ['0', '1'] - */ - function keys(object) { - return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); - } - - /** - * Creates an array of the own and inherited enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keysIn(new Foo); - * // => ['a', 'b', 'c'] (iteration order is not guaranteed) - */ - function keysIn(object) { - return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); - } - - /** - * The opposite of `_.mapValues`; this method creates an object with the - * same values as `object` and keys generated by running each own enumerable - * string keyed property of `object` thru `iteratee`. The iteratee is invoked - * with three arguments: (value, key, object). - * - * @static - * @memberOf _ - * @since 3.8.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns the new mapped object. - * @see _.mapValues - * @example - * - * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { - * return key + value; - * }); - * // => { 'a1': 1, 'b2': 2 } - */ - function mapKeys(object, iteratee) { - var result = {}; - iteratee = getIteratee(iteratee, 3); - baseForOwn(object, function (value, key, object) { - baseAssignValue(result, iteratee(value, key, object), value); - }); - return result; - } - - /** - * Creates an object with the same keys as `object` and values generated - * by running each own enumerable string keyed property of `object` thru - * `iteratee`. The iteratee is invoked with three arguments: - * (value, key, object). - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @returns {Object} Returns the new mapped object. - * @see _.mapKeys - * @example - * - * var users = { - * 'fred': { 'user': 'fred', 'age': 40 }, - * 'pebbles': { 'user': 'pebbles', 'age': 1 } - * }; - * - * _.mapValues(users, function(o) { return o.age; }); - * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) - * - * // The `_.property` iteratee shorthand. - * _.mapValues(users, 'age'); - * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) - */ - function mapValues(object, iteratee) { - var result = {}; - iteratee = getIteratee(iteratee, 3); - baseForOwn(object, function (value, key, object) { - baseAssignValue(result, key, iteratee(value, key, object)); - }); - return result; - } - - /** - * This method is like `_.assign` except that it recursively merges own and - * inherited enumerable string keyed properties of source objects into the - * destination object. Source properties that resolve to `undefined` are - * skipped if a destination value exists. Array and plain object properties - * are merged recursively. Other objects and value types are overridden by - * assignment. Source objects are applied from left to right. Subsequent - * sources overwrite property assignments of previous sources. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 0.5.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @example - * - * var object = { - * 'a': [{ 'b': 2 }, { 'd': 4 }] - * }; - * - * var other = { - * 'a': [{ 'c': 3 }, { 'e': 5 }] - * }; - * - * _.merge(object, other); - * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } - */ - var merge = createAssigner(function (object, source, srcIndex) { - baseMerge(object, source, srcIndex); - }); - - /** - * This method is like `_.merge` except that it accepts `customizer` which - * is invoked to produce the merged values of the destination and source - * properties. If `customizer` returns `undefined`, merging is handled by the - * method instead. The `customizer` is invoked with six arguments: - * (objValue, srcValue, key, object, source, stack). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} customizer The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * function customizer(objValue, srcValue) { - * if (_.isArray(objValue)) { - * return objValue.concat(srcValue); - * } - * } - * - * var object = { 'a': [1], 'b': [2] }; - * var other = { 'a': [3], 'b': [4] }; - * - * _.mergeWith(object, other, customizer); - * // => { 'a': [1, 3], 'b': [2, 4] } - */ - var mergeWith = createAssigner(function (object, source, srcIndex, customizer) { - baseMerge(object, source, srcIndex, customizer); - }); - - /** - * The opposite of `_.pick`; this method creates an object composed of the - * own and inherited enumerable property paths of `object` that are not omitted. - * - * **Note:** This method is considerably slower than `_.pick`. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {...(string|string[])} [paths] The property paths to omit. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.omit(object, ['a', 'c']); - * // => { 'b': '2' } - */ - var omit = flatRest(function (object, paths) { - var result = {}; - if (object == null) { - return result; - } - var isDeep = false; - paths = arrayMap(paths, function (path) { - path = castPath(path, object); - isDeep || (isDeep = path.length > 1); - return path; - }); - copyObject(object, getAllKeysIn(object), result); - if (isDeep) { - result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone); - } - var length = paths.length; - while (length--) { - baseUnset(result, paths[length]); - } - return result; - }); - - /** - * The opposite of `_.pickBy`; this method creates an object composed of - * the own and inherited enumerable string keyed properties of `object` that - * `predicate` doesn't return truthy for. The predicate is invoked with two - * arguments: (value, key). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The source object. - * @param {Function} [predicate=_.identity] The function invoked per property. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.omitBy(object, _.isNumber); - * // => { 'b': '2' } - */ - function omitBy(object, predicate) { - return pickBy(object, negate(getIteratee(predicate))); - } - - /** - * Creates an object composed of the picked `object` properties. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {...(string|string[])} [paths] The property paths to pick. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.pick(object, ['a', 'c']); - * // => { 'a': 1, 'c': 3 } - */ - var pick = flatRest(function (object, paths) { - return object == null ? {} : basePick(object, paths); - }); - - /** - * Creates an object composed of the `object` properties `predicate` returns - * truthy for. The predicate is invoked with two arguments: (value, key). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The source object. - * @param {Function} [predicate=_.identity] The function invoked per property. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'a': 1, 'b': '2', 'c': 3 }; - * - * _.pickBy(object, _.isNumber); - * // => { 'a': 1, 'c': 3 } - */ - function pickBy(object, predicate) { - if (object == null) { - return {}; - } - var props = arrayMap(getAllKeysIn(object), function (prop) { - return [prop]; - }); - predicate = getIteratee(predicate); - return basePickBy(object, props, function (value, path) { - return predicate(value, path[0]); - }); - } - - /** - * This method is like `_.get` except that if the resolved value is a - * function it's invoked with the `this` binding of its parent object and - * its result is returned. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to resolve. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; - * - * _.result(object, 'a[0].b.c1'); - * // => 3 - * - * _.result(object, 'a[0].b.c2'); - * // => 4 - * - * _.result(object, 'a[0].b.c3', 'default'); - * // => 'default' - * - * _.result(object, 'a[0].b.c3', _.constant('default')); - * // => 'default' - */ - function result(object, path, defaultValue) { - path = castPath(path, object); - var index = -1, - length = path.length; - - // Ensure the loop is entered when path is empty. - if (!length) { - length = 1; - object = undefined; - } - while (++index < length) { - var value = object == null ? undefined : object[toKey(path[index])]; - if (value === undefined) { - index = length; - value = defaultValue; - } - object = isFunction(value) ? value.call(object) : value; - } - return object; - } - - /** - * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, - * it's created. Arrays are created for missing index properties while objects - * are created for all other missing properties. Use `_.setWith` to customize - * `path` creation. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @returns {Object} Returns `object`. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.set(object, 'a[0].b.c', 4); - * console.log(object.a[0].b.c); - * // => 4 - * - * _.set(object, ['x', '0', 'y', 'z'], 5); - * console.log(object.x[0].y.z); - * // => 5 - */ - function set(object, path, value) { - return object == null ? object : baseSet(object, path, value); - } - - /** - * This method is like `_.set` except that it accepts `customizer` which is - * invoked to produce the objects of `path`. If `customizer` returns `undefined` - * path creation is handled by the method instead. The `customizer` is invoked - * with three arguments: (nsValue, key, nsObject). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * var object = {}; - * - * _.setWith(object, '[0][1]', 'a', Object); - * // => { '0': { '1': 'a' } } - */ - function setWith(object, path, value, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return object == null ? object : baseSet(object, path, value, customizer); - } - - /** - * Creates an array of own enumerable string keyed-value pairs for `object` - * which can be consumed by `_.fromPairs`. If `object` is a map or set, its - * entries are returned. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias entries - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the key-value pairs. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.toPairs(new Foo); - * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) - */ - var toPairs = createToPairs(keys); - - /** - * Creates an array of own and inherited enumerable string keyed-value pairs - * for `object` which can be consumed by `_.fromPairs`. If `object` is a map - * or set, its entries are returned. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias entriesIn - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the key-value pairs. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.toPairsIn(new Foo); - * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) - */ - var toPairsIn = createToPairs(keysIn); - - /** - * An alternative to `_.reduce`; this method transforms `object` to a new - * `accumulator` object which is the result of running each of its own - * enumerable string keyed properties thru `iteratee`, with each invocation - * potentially mutating the `accumulator` object. If `accumulator` is not - * provided, a new object with the same `[[Prototype]]` will be used. The - * iteratee is invoked with four arguments: (accumulator, value, key, object). - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @since 1.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The custom accumulator value. - * @returns {*} Returns the accumulated value. - * @example - * - * _.transform([2, 3, 4], function(result, n) { - * result.push(n *= n); - * return n % 2 == 0; - * }, []); - * // => [4, 9] - * - * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { - * (result[value] || (result[value] = [])).push(key); - * }, {}); - * // => { '1': ['a', 'c'], '2': ['b'] } - */ - function transform(object, iteratee, accumulator) { - var isArr = isArray(object), - isArrLike = isArr || isBuffer(object) || isTypedArray(object); - iteratee = getIteratee(iteratee, 4); - if (accumulator == null) { - var Ctor = object && object.constructor; - if (isArrLike) { - accumulator = isArr ? new Ctor() : []; - } else if (isObject(object)) { - accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {}; - } else { - accumulator = {}; - } - } - (isArrLike ? arrayEach : baseForOwn)(object, function (value, index, object) { - return iteratee(accumulator, value, index, object); - }); - return accumulator; - } - - /** - * Removes the property at `path` of `object`. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to unset. - * @returns {boolean} Returns `true` if the property is deleted, else `false`. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 7 } }] }; - * _.unset(object, 'a[0].b.c'); - * // => true - * - * console.log(object); - * // => { 'a': [{ 'b': {} }] }; - * - * _.unset(object, ['a', '0', 'b', 'c']); - * // => true - * - * console.log(object); - * // => { 'a': [{ 'b': {} }] }; - */ - function unset(object, path) { - return object == null ? true : baseUnset(object, path); - } - - /** - * This method is like `_.set` except that accepts `updater` to produce the - * value to set. Use `_.updateWith` to customize `path` creation. The `updater` - * is invoked with one argument: (value). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.6.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {Function} updater The function to produce the updated value. - * @returns {Object} Returns `object`. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.update(object, 'a[0].b.c', function(n) { return n * n; }); - * console.log(object.a[0].b.c); - * // => 9 - * - * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; }); - * console.log(object.x[0].y.z); - * // => 0 - */ - function update(object, path, updater) { - return object == null ? object : baseUpdate(object, path, castFunction(updater)); - } - - /** - * This method is like `_.update` except that it accepts `customizer` which is - * invoked to produce the objects of `path`. If `customizer` returns `undefined` - * path creation is handled by the method instead. The `customizer` is invoked - * with three arguments: (nsValue, key, nsObject). - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @since 4.6.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {Function} updater The function to produce the updated value. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * var object = {}; - * - * _.updateWith(object, '[0][1]', _.constant('a'), Object); - * // => { '0': { '1': 'a' } } - */ - function updateWith(object, path, updater, customizer) { - customizer = typeof customizer == 'function' ? customizer : undefined; - return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer); - } - - /** - * Creates an array of the own enumerable string keyed property values of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.values(new Foo); - * // => [1, 2] (iteration order is not guaranteed) - * - * _.values('hi'); - * // => ['h', 'i'] - */ - function values(object) { - return object == null ? [] : baseValues(object, keys(object)); - } - - /** - * Creates an array of the own and inherited enumerable string keyed property - * values of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.valuesIn(new Foo); - * // => [1, 2, 3] (iteration order is not guaranteed) - */ - function valuesIn(object) { - return object == null ? [] : baseValues(object, keysIn(object)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Clamps `number` within the inclusive `lower` and `upper` bounds. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Number - * @param {number} number The number to clamp. - * @param {number} [lower] The lower bound. - * @param {number} upper The upper bound. - * @returns {number} Returns the clamped number. - * @example - * - * _.clamp(-10, -5, 5); - * // => -5 - * - * _.clamp(10, -5, 5); - * // => 5 - */ - function clamp(number, lower, upper) { - if (upper === undefined) { - upper = lower; - lower = undefined; - } - if (upper !== undefined) { - upper = toNumber(upper); - upper = upper === upper ? upper : 0; - } - if (lower !== undefined) { - lower = toNumber(lower); - lower = lower === lower ? lower : 0; - } - return baseClamp(toNumber(number), lower, upper); - } - - /** - * Checks if `n` is between `start` and up to, but not including, `end`. If - * `end` is not specified, it's set to `start` with `start` then set to `0`. - * If `start` is greater than `end` the params are swapped to support - * negative ranges. - * - * @static - * @memberOf _ - * @since 3.3.0 - * @category Number - * @param {number} number The number to check. - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @returns {boolean} Returns `true` if `number` is in the range, else `false`. - * @see _.range, _.rangeRight - * @example - * - * _.inRange(3, 2, 4); - * // => true - * - * _.inRange(4, 8); - * // => true - * - * _.inRange(4, 2); - * // => false - * - * _.inRange(2, 2); - * // => false - * - * _.inRange(1.2, 2); - * // => true - * - * _.inRange(5.2, 4); - * // => false - * - * _.inRange(-3, -2, -6); - * // => true - */ - function inRange(number, start, end) { - start = toFinite(start); - if (end === undefined) { - end = start; - start = 0; - } else { - end = toFinite(end); - } - number = toNumber(number); - return baseInRange(number, start, end); - } - - /** - * Produces a random number between the inclusive `lower` and `upper` bounds. - * If only one argument is provided a number between `0` and the given number - * is returned. If `floating` is `true`, or either `lower` or `upper` are - * floats, a floating-point number is returned instead of an integer. - * - * **Note:** JavaScript follows the IEEE-754 standard for resolving - * floating-point values which can produce unexpected results. - * - * @static - * @memberOf _ - * @since 0.7.0 - * @category Number - * @param {number} [lower=0] The lower bound. - * @param {number} [upper=1] The upper bound. - * @param {boolean} [floating] Specify returning a floating-point number. - * @returns {number} Returns the random number. - * @example - * - * _.random(0, 5); - * // => an integer between 0 and 5 - * - * _.random(5); - * // => also an integer between 0 and 5 - * - * _.random(5, true); - * // => a floating-point number between 0 and 5 - * - * _.random(1.2, 5.2); - * // => a floating-point number between 1.2 and 5.2 - */ - function random(lower, upper, floating) { - if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { - upper = floating = undefined; - } - if (floating === undefined) { - if (typeof upper == 'boolean') { - floating = upper; - upper = undefined; - } else if (typeof lower == 'boolean') { - floating = lower; - lower = undefined; - } - } - if (lower === undefined && upper === undefined) { - lower = 0; - upper = 1; - } else { - lower = toFinite(lower); - if (upper === undefined) { - upper = lower; - lower = 0; - } else { - upper = toFinite(upper); - } - } - if (lower > upper) { - var temp = lower; - lower = upper; - upper = temp; - } - if (floating || lower % 1 || upper % 1) { - var rand = nativeRandom(); - return nativeMin(lower + rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1))), upper); - } - return baseRandom(lower, upper); - } - - /*------------------------------------------------------------------------*/ - - /** - * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the camel cased string. - * @example - * - * _.camelCase('Foo Bar'); - * // => 'fooBar' - * - * _.camelCase('--foo-bar--'); - * // => 'fooBar' - * - * _.camelCase('__FOO_BAR__'); - * // => 'fooBar' - */ - var camelCase = createCompounder(function (result, word, index) { - word = word.toLowerCase(); - return result + (index ? capitalize(word) : word); - }); - - /** - * Converts the first character of `string` to upper case and the remaining - * to lower case. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to capitalize. - * @returns {string} Returns the capitalized string. - * @example - * - * _.capitalize('FRED'); - * // => 'Fred' - */ - function capitalize(string) { - return upperFirst(toString(string).toLowerCase()); - } - - /** - * Deburrs `string` by converting - * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) - * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) - * letters to basic Latin letters and removing - * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to deburr. - * @returns {string} Returns the deburred string. - * @example - * - * _.deburr('déjà vu'); - * // => 'deja vu' - */ - function deburr(string) { - string = toString(string); - return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); - } - - /** - * Checks if `string` ends with the given target string. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {string} [target] The string to search for. - * @param {number} [position=string.length] The position to search up to. - * @returns {boolean} Returns `true` if `string` ends with `target`, - * else `false`. - * @example - * - * _.endsWith('abc', 'c'); - * // => true - * - * _.endsWith('abc', 'b'); - * // => false - * - * _.endsWith('abc', 'b', 2); - * // => true - */ - function endsWith(string, target, position) { - string = toString(string); - target = baseToString(target); - var length = string.length; - position = position === undefined ? length : baseClamp(toInteger(position), 0, length); - var end = position; - position -= target.length; - return position >= 0 && string.slice(position, end) == target; - } - - /** - * Converts the characters "&", "<", ">", '"', and "'" in `string` to their - * corresponding HTML entities. - * - * **Note:** No other characters are escaped. To escape additional - * characters use a third-party library like [_he_](https://mths.be/he). - * - * Though the ">" character is escaped for symmetry, characters like - * ">" and "/" don't need escaping in HTML and have no special meaning - * unless they're part of a tag or unquoted attribute value. See - * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) - * (under "semi-related fun fact") for more details. - * - * When working with HTML you should always - * [quote attribute values](http://wonko.com/post/html-escaping) to reduce - * XSS vectors. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escape('fred, barney, & pebbles'); - * // => 'fred, barney, & pebbles' - */ - function escape(string) { - string = toString(string); - return string && reHasUnescapedHtml.test(string) ? string.replace(reUnescapedHtml, escapeHtmlChar) : string; - } - - /** - * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", - * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escapeRegExp('[lodash](https://lodash.com/)'); - * // => '\[lodash\]\(https://lodash\.com/\)' - */ - function escapeRegExp(string) { - string = toString(string); - return string && reHasRegExpChar.test(string) ? string.replace(reRegExpChar, '\\$&') : string; - } - - /** - * Converts `string` to - * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the kebab cased string. - * @example - * - * _.kebabCase('Foo Bar'); - * // => 'foo-bar' - * - * _.kebabCase('fooBar'); - * // => 'foo-bar' - * - * _.kebabCase('__FOO_BAR__'); - * // => 'foo-bar' - */ - var kebabCase = createCompounder(function (result, word, index) { - return result + (index ? '-' : '') + word.toLowerCase(); - }); - - /** - * Converts `string`, as space separated words, to lower case. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the lower cased string. - * @example - * - * _.lowerCase('--Foo-Bar--'); - * // => 'foo bar' - * - * _.lowerCase('fooBar'); - * // => 'foo bar' - * - * _.lowerCase('__FOO_BAR__'); - * // => 'foo bar' - */ - var lowerCase = createCompounder(function (result, word, index) { - return result + (index ? ' ' : '') + word.toLowerCase(); - }); - - /** - * Converts the first character of `string` to lower case. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the converted string. - * @example - * - * _.lowerFirst('Fred'); - * // => 'fred' - * - * _.lowerFirst('FRED'); - * // => 'fRED' - */ - var lowerFirst = createCaseFirst('toLowerCase'); - - /** - * Pads `string` on the left and right sides if it's shorter than `length`. - * Padding characters are truncated if they can't be evenly divided by `length`. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.pad('abc', 8); - * // => ' abc ' - * - * _.pad('abc', 8, '_-'); - * // => '_-abc_-_' - * - * _.pad('abc', 3); - * // => 'abc' - */ - function pad(string, length, chars) { - string = toString(string); - length = toInteger(length); - var strLength = length ? stringSize(string) : 0; - if (!length || strLength >= length) { - return string; - } - var mid = (length - strLength) / 2; - return createPadding(nativeFloor(mid), chars) + string + createPadding(nativeCeil(mid), chars); - } - - /** - * Pads `string` on the right side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padEnd('abc', 6); - * // => 'abc ' - * - * _.padEnd('abc', 6, '_-'); - * // => 'abc_-_' - * - * _.padEnd('abc', 3); - * // => 'abc' - */ - function padEnd(string, length, chars) { - string = toString(string); - length = toInteger(length); - var strLength = length ? stringSize(string) : 0; - return length && strLength < length ? string + createPadding(length - strLength, chars) : string; - } - - /** - * Pads `string` on the left side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padStart('abc', 6); - * // => ' abc' - * - * _.padStart('abc', 6, '_-'); - * // => '_-_abc' - * - * _.padStart('abc', 3); - * // => 'abc' - */ - function padStart(string, length, chars) { - string = toString(string); - length = toInteger(length); - var strLength = length ? stringSize(string) : 0; - return length && strLength < length ? createPadding(length - strLength, chars) + string : string; - } - - /** - * Converts `string` to an integer of the specified radix. If `radix` is - * `undefined` or `0`, a `radix` of `10` is used unless `value` is a - * hexadecimal, in which case a `radix` of `16` is used. - * - * **Note:** This method aligns with the - * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. - * - * @static - * @memberOf _ - * @since 1.1.0 - * @category String - * @param {string} string The string to convert. - * @param {number} [radix=10] The radix to interpret `value` by. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {number} Returns the converted integer. - * @example - * - * _.parseInt('08'); - * // => 8 - * - * _.map(['6', '08', '10'], _.parseInt); - * // => [6, 8, 10] - */ - function parseInt(string, radix, guard) { - if (guard || radix == null) { - radix = 0; - } else if (radix) { - radix = +radix; - } - return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); - } - - /** - * Repeats the given string `n` times. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to repeat. - * @param {number} [n=1] The number of times to repeat the string. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {string} Returns the repeated string. - * @example - * - * _.repeat('*', 3); - * // => '***' - * - * _.repeat('abc', 2); - * // => 'abcabc' - * - * _.repeat('abc', 0); - * // => '' - */ - function repeat(string, n, guard) { - if (guard ? isIterateeCall(string, n, guard) : n === undefined) { - n = 1; - } else { - n = toInteger(n); - } - return baseRepeat(toString(string), n); - } - - /** - * Replaces matches for `pattern` in `string` with `replacement`. - * - * **Note:** This method is based on - * [`String#replace`](https://mdn.io/String/replace). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to modify. - * @param {RegExp|string} pattern The pattern to replace. - * @param {Function|string} replacement The match replacement. - * @returns {string} Returns the modified string. - * @example - * - * _.replace('Hi Fred', 'Fred', 'Barney'); - * // => 'Hi Barney' - */ - function replace() { - var args = arguments, - string = toString(args[0]); - return args.length < 3 ? string : string.replace(args[1], args[2]); - } - - /** - * Converts `string` to - * [snake case](https://en.wikipedia.org/wiki/Snake_case). - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the snake cased string. - * @example - * - * _.snakeCase('Foo Bar'); - * // => 'foo_bar' - * - * _.snakeCase('fooBar'); - * // => 'foo_bar' - * - * _.snakeCase('--FOO-BAR--'); - * // => 'foo_bar' - */ - var snakeCase = createCompounder(function (result, word, index) { - return result + (index ? '_' : '') + word.toLowerCase(); - }); - - /** - * Splits `string` by `separator`. - * - * **Note:** This method is based on - * [`String#split`](https://mdn.io/String/split). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to split. - * @param {RegExp|string} separator The separator pattern to split by. - * @param {number} [limit] The length to truncate results to. - * @returns {Array} Returns the string segments. - * @example - * - * _.split('a-b-c', '-', 2); - * // => ['a', 'b'] - */ - function split(string, separator, limit) { - if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { - separator = limit = undefined; - } - limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0; - if (!limit) { - return []; - } - string = toString(string); - if (string && (typeof separator == 'string' || separator != null && !isRegExp(separator))) { - separator = baseToString(separator); - if (!separator && hasUnicode(string)) { - return castSlice(stringToArray(string), 0, limit); - } - } - return string.split(separator, limit); - } - - /** - * Converts `string` to - * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). - * - * @static - * @memberOf _ - * @since 3.1.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the start cased string. - * @example - * - * _.startCase('--foo-bar--'); - * // => 'Foo Bar' - * - * _.startCase('fooBar'); - * // => 'Foo Bar' - * - * _.startCase('__FOO_BAR__'); - * // => 'FOO BAR' - */ - var startCase = createCompounder(function (result, word, index) { - return result + (index ? ' ' : '') + upperFirst(word); - }); - - /** - * Checks if `string` starts with the given target string. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {string} [target] The string to search for. - * @param {number} [position=0] The position to search from. - * @returns {boolean} Returns `true` if `string` starts with `target`, - * else `false`. - * @example - * - * _.startsWith('abc', 'a'); - * // => true - * - * _.startsWith('abc', 'b'); - * // => false - * - * _.startsWith('abc', 'b', 1); - * // => true - */ - function startsWith(string, target, position) { - string = toString(string); - position = position == null ? 0 : baseClamp(toInteger(position), 0, string.length); - target = baseToString(target); - return string.slice(position, position + target.length) == target; - } - - /** - * Creates a compiled template function that can interpolate data properties - * in "interpolate" delimiters, HTML-escape interpolated data properties in - * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data - * properties may be accessed as free variables in the template. If a setting - * object is given, it takes precedence over `_.templateSettings` values. - * - * **Note:** In the development build `_.template` utilizes - * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) - * for easier debugging. - * - * For more information on precompiling templates see - * [lodash's custom builds documentation](https://lodash.com/custom-builds). - * - * For more information on Chrome extension sandboxes see - * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category String - * @param {string} [string=''] The template string. - * @param {Object} [options={}] The options object. - * @param {RegExp} [options.escape=_.templateSettings.escape] - * The HTML "escape" delimiter. - * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] - * The "evaluate" delimiter. - * @param {Object} [options.imports=_.templateSettings.imports] - * An object to import into the template as free variables. - * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] - * The "interpolate" delimiter. - * @param {string} [options.sourceURL='lodash.templateSources[n]'] - * The sourceURL of the compiled template. - * @param {string} [options.variable='obj'] - * The data object variable name. - * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. - * @returns {Function} Returns the compiled template function. - * @example - * - * // Use the "interpolate" delimiter to create a compiled template. - * var compiled = _.template('hello <%= user %>!'); - * compiled({ 'user': 'fred' }); - * // => 'hello fred!' - * - * // Use the HTML "escape" delimiter to escape data property values. - * var compiled = _.template('<%- value %>'); - * compiled({ 'value': '") + Markup('<script>alert(document.cookie);</script>') + + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup("Hello") + Markup('hello') + + >>> escape(Markup("Hello")) + Markup('hello') + + >>> # Markup is a str subclass + >>> # methods and operators escape their arguments + >>> template = Markup("Hello {name}") + >>> template.format(name='"World"') + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Chat: https://discord.gg/pallets diff --git a/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/RECORD b/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/RECORD new file mode 100644 index 000000000..3ef30fccc --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/RECORD @@ -0,0 +1,15 @@ +MarkupSafe-2.1.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.1.5.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-2.1.5.dist-info/METADATA,sha256=2dRDPam6OZLfpX0wg1JN5P3u9arqACxVSfdGmsJU7o8,3003 +MarkupSafe-2.1.5.dist-info/RECORD,, +MarkupSafe-2.1.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +MarkupSafe-2.1.5.dist-info/WHEEL,sha256=2rnFQKN1y4ODxAzmbuA5Z6aEng-PoJTYpqGpnJDGdxw,111 +MarkupSafe-2.1.5.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=r7VOTjUq7EMQ4v3p4R1LoVOGJg6ysfYRncLr34laRBs,10958 +markupsafe/__pycache__/__init__.cpython-311.pyc,, +markupsafe/__pycache__/_native.cpython-311.pyc,, +markupsafe/_native.py,sha256=GR86Qvo_GcgKmKreA1WmYN9ud17OFwkww8E-fiW-57s,1713 +markupsafe/_speedups.c,sha256=X2XvQVtIdcK4Usz70BvkzoOfjTCmQlDkkjYSn-swE0g,7083 +markupsafe/_speedups.cpython-311-darwin.so,sha256=dLtLNhRcybjKGMYMx94fHSTTAPgtYoW9L06TV79sZMs,35272 +markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/REQUESTED b/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/REQUESTED new file mode 100644 index 000000000..e69de29bb diff --git a/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/WHEEL b/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/WHEEL new file mode 100644 index 000000000..a88b33b1b --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: false +Tag: cp311-cp311-macosx_10_9_x86_64 + diff --git a/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/top_level.txt b/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/top_level.txt new file mode 100644 index 000000000..75bf72925 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/MarkupSafe-2.1.5.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/INSTALLER b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/LICENSE b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/LICENSE new file mode 100644 index 000000000..2f1b8e15e --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2017-2021 Ingy döt Net +Copyright (c) 2006-2016 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/METADATA b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/METADATA new file mode 100644 index 000000000..c8905983e --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/METADATA @@ -0,0 +1,46 @@ +Metadata-Version: 2.1 +Name: PyYAML +Version: 6.0.1 +Summary: YAML parser and emitter for Python +Home-page: https://pyyaml.org/ +Download-URL: https://pypi.org/project/PyYAML/ +Author: Kirill Simonov +Author-email: xi@resolvent.net +License: MIT +Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues +Project-URL: CI, https://github.com/yaml/pyyaml/actions +Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation +Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core +Project-URL: Source Code, https://github.com/yaml/pyyaml +Platform: Any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup +Requires-Python: >=3.6 +License-File: LICENSE + +YAML is a data serialization format designed for human readability +and interaction with scripting languages. PyYAML is a YAML parser +and emitter for Python. + +PyYAML features a complete YAML 1.1 parser, Unicode support, pickle +support, capable extension API, and sensible error messages. PyYAML +supports standard YAML tags and provides Python-specific tags that +allow to represent an arbitrary Python object. + +PyYAML is applicable for a broad range of tasks from complex +configuration files to object serialization and persistence. diff --git a/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/RECORD b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/RECORD new file mode 100644 index 000000000..88258ef4b --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/RECORD @@ -0,0 +1,44 @@ +PyYAML-6.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +PyYAML-6.0.1.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101 +PyYAML-6.0.1.dist-info/METADATA,sha256=UNNF8-SzzwOKXVo-kV5lXUGH2_wDWMBmGxqISpp5HQk,2058 +PyYAML-6.0.1.dist-info/RECORD,, +PyYAML-6.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +PyYAML-6.0.1.dist-info/WHEEL,sha256=zATDCBMA0NrnUw5OGrN7taAN4ImJV7kn5PkKRuCgN5Q,111 +PyYAML-6.0.1.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11 +_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402 +_yaml/__pycache__/__init__.cpython-311.pyc,, +yaml/__init__.py,sha256=bhl05qSeO-1ZxlSRjGrvl2m9nrXb1n9-GQatTN0Mrqc,12311 +yaml/__pycache__/__init__.cpython-311.pyc,, +yaml/__pycache__/composer.cpython-311.pyc,, +yaml/__pycache__/constructor.cpython-311.pyc,, +yaml/__pycache__/cyaml.cpython-311.pyc,, +yaml/__pycache__/dumper.cpython-311.pyc,, +yaml/__pycache__/emitter.cpython-311.pyc,, +yaml/__pycache__/error.cpython-311.pyc,, +yaml/__pycache__/events.cpython-311.pyc,, +yaml/__pycache__/loader.cpython-311.pyc,, +yaml/__pycache__/nodes.cpython-311.pyc,, +yaml/__pycache__/parser.cpython-311.pyc,, +yaml/__pycache__/reader.cpython-311.pyc,, +yaml/__pycache__/representer.cpython-311.pyc,, +yaml/__pycache__/resolver.cpython-311.pyc,, +yaml/__pycache__/scanner.cpython-311.pyc,, +yaml/__pycache__/serializer.cpython-311.pyc,, +yaml/__pycache__/tokens.cpython-311.pyc,, +yaml/_yaml.cpython-311-darwin.so,sha256=9rqdpMeZVNxWDZ56UEFXdbczGQdHw8YHXxJkbZSz9_M,429464 +yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883 +yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639 +yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851 +yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837 +yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006 +yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533 +yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445 +yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061 +yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440 +yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495 +yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794 +yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190 +yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004 +yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279 +yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165 +yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573 diff --git a/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/REQUESTED b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/REQUESTED new file mode 100644 index 000000000..e69de29bb diff --git a/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/WHEEL b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/WHEEL new file mode 100644 index 000000000..b77285b61 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.40.0) +Root-Is-Purelib: false +Tag: cp311-cp311-macosx_10_9_x86_64 + diff --git a/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/top_level.txt b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/top_level.txt new file mode 100644 index 000000000..e6475e911 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/PyYAML-6.0.1.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_yaml +yaml diff --git a/lib/go-jinja2/internal/data/darwin-amd64/_yaml/__init__.py b/lib/go-jinja2/internal/data/darwin-amd64/_yaml/__init__.py new file mode 100644 index 000000000..7baa8c4b6 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/_yaml/__init__.py @@ -0,0 +1,33 @@ +# This is a stub package designed to roughly emulate the _yaml +# extension module, which previously existed as a standalone module +# and has been moved into the `yaml` package namespace. +# It does not perfectly mimic its old counterpart, but should get +# close enough for anyone who's relying on it even when they shouldn't. +import yaml + +# in some circumstances, the yaml module we imoprted may be from a different version, so we need +# to tread carefully when poking at it here (it may not have the attributes we expect) +if not getattr(yaml, '__with_libyaml__', False): + from sys import version_info + + exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/lib/go-jinja2/internal/data/darwin-amd64/bin/jsonpath_ng b/lib/go-jinja2/internal/data/darwin-amd64/bin/jsonpath_ng new file mode 100644 index 000000000..28a424903 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/bin/jsonpath_ng @@ -0,0 +1,8 @@ +#!/tmp/python-pip-darwin-amd64/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from jsonpath_ng.bin.jsonpath import entry_point +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(entry_point()) diff --git a/lib/go-jinja2/internal/data/darwin-amd64/bin/slugify b/lib/go-jinja2/internal/data/darwin-amd64/bin/slugify new file mode 100644 index 000000000..bc16b756e --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/bin/slugify @@ -0,0 +1,8 @@ +#!/tmp/python-pip-darwin-amd64/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from slugify.__main__ import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/INSTALLER b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/LICENSE.rst b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/LICENSE.rst new file mode 100644 index 000000000..d12a84918 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +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/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/METADATA b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/METADATA new file mode 100644 index 000000000..7a6bbb24b --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/METADATA @@ -0,0 +1,103 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.1.7 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/click/ +Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: colorama ; platform_system == "Windows" +Requires-Dist: importlib-metadata ; python_version < "3.8" + +\$ click\_ +========== + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U click + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + import click + + @click.command() + @click.option("--count", default=1, help="Number of greetings.") + @click.option("--name", prompt="Your name", help="The person to greet.") + def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + + if __name__ == '__main__': + hello() + +.. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + +Donate +------ + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://click.palletsprojects.com/ +- Changes: https://click.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/click/ +- Source Code: https://github.com/pallets/click +- Issue Tracker: https://github.com/pallets/click/issues +- Chat: https://discord.gg/pallets diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/RECORD b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/RECORD new file mode 100644 index 000000000..dcffbf004 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/RECORD @@ -0,0 +1,40 @@ +click-8.1.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.1.7.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.1.7.dist-info/METADATA,sha256=qIMevCxGA9yEmJOM_4WHuUJCwWpsIEVbCPOhs45YPN4,3014 +click-8.1.7.dist-info/RECORD,, +click-8.1.7.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click-8.1.7.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92 +click-8.1.7.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=YDDbjm406dTOA0V8bTtdGnhN7zj5j-_dFRewZF_pLvw,3138 +click/__pycache__/__init__.cpython-311.pyc,, +click/__pycache__/_compat.cpython-311.pyc,, +click/__pycache__/_termui_impl.cpython-311.pyc,, +click/__pycache__/_textwrap.cpython-311.pyc,, +click/__pycache__/_winconsole.cpython-311.pyc,, +click/__pycache__/core.cpython-311.pyc,, +click/__pycache__/decorators.cpython-311.pyc,, +click/__pycache__/exceptions.cpython-311.pyc,, +click/__pycache__/formatting.cpython-311.pyc,, +click/__pycache__/globals.cpython-311.pyc,, +click/__pycache__/parser.cpython-311.pyc,, +click/__pycache__/shell_completion.cpython-311.pyc,, +click/__pycache__/termui.cpython-311.pyc,, +click/__pycache__/testing.cpython-311.pyc,, +click/__pycache__/types.cpython-311.pyc,, +click/__pycache__/utils.cpython-311.pyc,, +click/_compat.py,sha256=5318agQpbt4kroKsbqDOYpTSWzL_YCZVUQiTT04yXmc,18744 +click/_termui_impl.py,sha256=3dFYv4445Nw-rFvZOTBMBPYwB1bxnmNk9Du6Dm_oBSU,24069 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=j6oEWtGgGna8JarD6WxhXmNnxLnfRjwXglbBc-8jr7U,114086 +click/decorators.py,sha256=-ZlbGYgV-oI8jr_oH4RpuL1PFS-5QmeuEAsLDAYgxtw,18719 +click/exceptions.py,sha256=fyROO-47HWFDjt2qupo7A3J32VlpM-ovJnfowu92K3s,9273 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=TP-qM88STzc7f127h35TD_v920FgfOD2EwzqA0oE8XU,1961 +click/parser.py,sha256=LKyYQE9ZLj5KgIDXkrcTHQRXIggfoivX14_UVIn56YA,19067 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=Ty3VM_ts0sQhj6u7eFTiLwHPoTgcXTGEAUg2OpLqYKw,18460 +click/termui.py,sha256=H7Q8FpmPelhJ2ovOhfCRhjMtCpNyjFXryAMLZODqsdc,28324 +click/testing.py,sha256=1Qd4kS5bucn1hsNIRryd0WtTMuCpkA93grkWxT8POsU,16084 +click/types.py,sha256=TZvz3hKvBztf-Hpa2enOmP4eznSPLzijjig5b_0XMxE,36391 +click/utils.py,sha256=1476UduUNY6UePGU4m18uzVHLt1sKM2PP3yWsQhbItM,20298 diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/REQUESTED b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/REQUESTED new file mode 100644 index 000000000..e69de29bb diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/WHEEL b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/WHEEL new file mode 100644 index 000000000..2c08da084 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/top_level.txt b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/top_level.txt new file mode 100644 index 000000000..dca9a9096 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click-8.1.7.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/__init__.py b/lib/go-jinja2/internal/data/darwin-amd64/click/__init__.py new file mode 100644 index 000000000..9a1dab048 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/__init__.py @@ -0,0 +1,73 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" +from .core import Argument as Argument +from .core import BaseCommand as BaseCommand +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import MultiCommand as MultiCommand +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .parser import OptionParser as OptionParser +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.1.7" diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/_compat.py b/lib/go-jinja2/internal/data/darwin-amd64/click/_compat.py new file mode 100644 index 000000000..23f886659 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/_compat.py @@ -0,0 +1,623 @@ +import codecs +import io +import os +import re +import sys +import typing as t +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +WIN = sys.platform.startswith("win") +auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO[t.Any]) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write("") # type: ignore + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.Optional[t.BinaryIO]], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: t.Union[str, "os.PathLike[str]", int], + mode: str, + encoding: t.Optional[str], + errors: t.Optional[str], +) -> t.IO[t.Any]: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: "t.Union[str, os.PathLike[str]]", + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, +) -> t.Tuple[t.IO[t.Any], bool]: + binary = "b" in mode + filename = os.fspath(filename) + + # Standard streams first. These are simple because they ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(filename) == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: t.Optional[int] = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO[t.Any], af), True + + +class _AtomicFile: + def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> "_AtomicFile": + return self + + def __exit__(self, exc_type: t.Optional[t.Type[BaseException]], *_: t.Any) -> None: + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi( # noqa: F811 + stream: t.TextIO, color: t.Optional[bool] = None + ) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s): + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] + ) -> t.Optional[t.TextIO]: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.Optional[t.TextIO]], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.Optional[t.TextIO]]: + cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.Optional[t.TextIO]: + stream = src_func() + + if stream is None: + return None + + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: t.Mapping[ + str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] +] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/_termui_impl.py b/lib/go-jinja2/internal/data/darwin-amd64/click/_termui_impl.py new file mode 100644 index 000000000..f74465775 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/_termui_impl.py @@ -0,0 +1,739 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" +import contextlib +import math +import os +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +from types import TracebackType + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: t.Optional[t.Iterable[V]], + length: t.Optional[int] = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + label: t.Optional[str] = None, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: str = label or "" + + if file is None: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + file = StringIO() + + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width: int = width + self.autowidth: bool = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast(t.Iterable[V], range(length)) + self.iter: t.Iterable[V] = iter(iterable) + self.length = length + self.pos = 0 + self.avg: t.List[float] = [] + self.last_eta: float + self.start: float + self.start = self.last_eta = time.time() + self.eta_known: bool = False + self.finished: bool = False + self.max_width: t.Optional[int] = None + self.entered: bool = False + self.current_item: t.Optional[V] = None + self.is_hidden: bool = not isatty(self.file) + self._last_line: t.Optional[str] = None + + def __enter__(self) -> "ProgressBar[V]": + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.render_finish() + + def __iter__(self) -> t.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.is_hidden: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.is_hidden: + # Only output the label as it changes if the output is not a + # TTY. Use file=stderr if you expect to be piping stdout. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) # type: ignore + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> t.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if self.is_hidden: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if stdout is None: + stdout = StringIO() + + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + pager_cmd = (os.environ.get("PAGER", None) or "").strip() + if pager_cmd: + if WIN: + return _tempfilepager(generator, pager_cmd, color) + return _pipepager(generator, pager_cmd, color) + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if WIN or sys.platform.startswith("os2"): + return _tempfilepager(generator, "more <", color) + if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: + return _pipepager(generator, "less", color) + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if hasattr(os, "system") and os.system(f'more "{filename}"') == 0: + return _pipepager(generator, "more", color) + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> None: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + """ + import subprocess + + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) + stdin = t.cast(t.BinaryIO, c.stdin) + encoding = get_best_encoding(stdin) + try: + for text in generator: + if not color: + text = strip_ansi(text) + + stdin.write(text.encode(encoding, "replace")) + except (OSError, KeyboardInterrupt): + pass + else: + stdin.close() + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + +def _tempfilepager( + generator: t.Iterable[str], cmd: str, color: t.Optional[bool] +) -> None: + """Page through text by invoking a program on a temporary file.""" + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + os.system(f'{cmd} "{filename}"') + finally: + os.close(fd) + os.unlink(filename) + + +def _nullpager( + stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if os.system(f"which {editor} >/dev/null 2>&1") == 0: + return editor + return "vi" + + def edit_file(self, filename: str) -> None: + import subprocess + + editor = self.get_editor() + environ: t.Optional[t.Dict[str, str]] = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: + import tempfile + + if not text: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_file(name) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url.replace('"', "")) + args = f'explorer /select,"{url}"' + else: + url = url.replace('"', "") + wait_str = "/WAIT" if wait else "" + args = f'start {wait_str} "" "{url}"' + return os.system(args) + elif CYGWIN: + if locate: + url = os.path.dirname(_unquote_file(url).replace('"', "")) + args = f'cygstart "{url}"' + else: + url = url.replace('"', "") + wait_str = "-w" if wait else "" + args = f'cygstart {wait_str} "{url}"' + return os.system(args) + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + func: t.Callable[[], str] + + if echo: + func = msvcrt.getwche # type: ignore + else: + func = msvcrt.getwch # type: ignore + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + +else: + import tty + import termios + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + f: t.Optional[t.TextIO] + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/_textwrap.py b/lib/go-jinja2/internal/data/darwin-amd64/click/_textwrap.py new file mode 100644 index 000000000..b47dcbd42 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/_textwrap.py @@ -0,0 +1,49 @@ +import textwrap +import typing as t +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: t.List[str], + cur_line: t.List[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> t.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/_winconsole.py b/lib/go-jinja2/internal/data/darwin-amd64/click/_winconsole.py new file mode 100644 index 000000000..6b20df315 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/_winconsole.py @@ -0,0 +1,279 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +import io +import sys +import time +import typing as t +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): + self.handle = handle + + def isatty(self): + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): + return True + + def readinto(self, b): + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self): + return True + + @staticmethod + def _get_error_message(errno): + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b): + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self): + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> t.Optional[t.TextIO]: + if ( + get_buffer is not None + and encoding in {"utf-16-le", None} + and errors in {"strict", None} + and _is_console(f) + ): + func = _stream_factories.get(f.fileno()) + if func is not None: + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/core.py b/lib/go-jinja2/internal/data/darwin-amd64/click/core.py new file mode 100644 index 000000000..cc65e896b --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/core.py @@ -0,0 +1,3042 @@ +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: "Context", incomplete: str +) -> t.Iterator[t.Tuple[str, "Command"]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(MultiCommand, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Given a sequence of parameters in the order as should be considered + for processing and an iterable of parameters that exist, this returns + a list in the correct order as they should be processed. + """ + + def sort_key(item: "Parameter") -> t.Tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.MutableMapping[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.List[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args: t.List[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: t.Optional[t.MutableMapping[str, t.Any]] = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: t.Optional[str] = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: t.Optional[int] = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: t.Optional[int] = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: t.List[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Optional[ + t.Callable[[str], str] + ] = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: t.Optional[bool] = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> "Context": + self._depth += 1 + push_context(self) + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> t.Dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: t.ContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> "Context": + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional["Context"] = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: t.Type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[False]" = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> "te.NoReturn": + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> "te.NoReturn": + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> "te.NoReturn": + """Exits the application with a given exit code.""" + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: "Command") -> "Context": + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + @t.overload + def invoke( + __self, # noqa: B902 + __callback: "t.Callable[..., V]", + *args: t.Any, + **kwargs: t.Any, + ) -> V: + ... + + @t.overload + def invoke( + __self, # noqa: B902 + __callback: "Command", + *args: t.Any, + **kwargs: t.Any, + ) -> t.Any: + ... + + def invoke( + __self, # noqa: B902 + __callback: t.Union["Command", "t.Callable[..., V]"], + *args: t.Any, + **kwargs: t.Any, + ) -> t.Union[t.Any, V]: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + """ + if isinstance(__callback, Command): + other_cmd = __callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + __callback = t.cast("t.Callable[..., V]", other_cmd.callback) + + ctx = __self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = __self + + with augment_usage_errors(__self): + with ctx: + return __callback(*args, **kwargs) + + def forward( + __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902 + ) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(__cmd, Command): + raise TypeError("Callback is not a command.") + + for param in __self.params: + if param not in kwargs: + kwargs[param] = __self.params[param] + + return __self.invoke(__cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class BaseCommand: + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: t.Type[Context] = Context + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: t.MutableMapping[str, t.Any] = context_settings + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire structure + below this command. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + :param ctx: A :class:`Context` representing this command. + + .. versionadded:: 8.0 + """ + return {"name": self.name} + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get usage") + + def get_help(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get help") + + def make_context( + self, + info_name: t.Optional[str], + args: t.List[str], + parent: t.Optional[Context] = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class( + self, info_name=info_name, parent=parent, **extra # type: ignore + ) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError("Base commands do not know how to parse arguments.") + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError("Base commands are not invocable by default") + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. Other + command classes will return more completions. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, MultiCommand) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx.protected_args + ) + + return results + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: "te.Literal[True]" = True, + **extra: t.Any, + ) -> "te.NoReturn": + ... + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: + ... + + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) as e: + echo(file=sys.stderr) + raise Abort() from e + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: t.Optional[str] = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + + .. versionchanged:: 8.2.0 + Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). + """ + if complete_var is None: + complete_name = prog_name.replace("-", "_").replace(".", "_") + complete_var = f"_{complete_name}_COMPLETE".upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class Command(BaseCommand): + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: t.List["Parameter"] = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + params=[param.to_info_dict() for param in self.get_params(ctx)], + help=self.help, + epilog=self.epilog, + short_help=self.short_help, + hidden=self.hidden, + deprecated=self.deprecated, + ) + return info_dict + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> t.List[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> t.Optional["Option"]: + """Returns the help option object.""" + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + def show_help(ctx: Context, param: "Parameter", value: str) -> None: + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + return Option( + help_options, + is_flag=True, + is_eager=True, + expose_value=False, + callback=show_help, + help=_("Show this message and exit."), + ) + + def make_parser(self, ctx: Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + :param attrs: Other command arguments described in :class:`Command`. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(__value, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv + + return decorator + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx.protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx.protected_args, *ctx.args] + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + """ + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: t.Optional[t.Type[Command]] = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[ + t.Union[t.MutableMapping[str, Command], t.Sequence[Command]] + ] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: t.MutableMapping[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: + ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: + ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Optional[t.Callable[..., t.Any]] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'command(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> "Group": + ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: + ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Optional[t.Callable[..., t.Any]] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'group(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> "Group": + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + + See :class:`MultiCommand` and :class:`Command` for the description of + ``name`` and ``attrs``. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The latter is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + ) -> None: + self.name: t.Optional[str] + self.opts: t.List[str] + self.secondary_opts: t.List[str] + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type: types.ParamType = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, t.Any] + ) -> t.Tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> t.Iterator[t.Any]: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + if self.nargs == 1 or self.type.is_composite: + + def convert(value: t.Any) -> t.Any: + return self.type(value, param=self, ctx=ctx) + + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] + ) -> t.Tuple[t.Any, t.List[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + pass + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast(t.List["CompletionItem"], results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page. Normally, environment variables are not + shown. + :param prompt: If set to ``True`` or a non empty string then the + user will be prompted for input. If set to ``True`` the prompt + will be the option name capitalized. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: If this is ``True`` then the input on the prompt + will be hidden from the user. This is useful for password input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.1.0 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1.0 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1.0 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + show_default: t.Union[bool, str, None] = None, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + default_is_missing = "default" not in attrs + super().__init__(param_decls, type=type, multiple=multiple, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + self.default: t.Union[t.Any, t.Callable[[], t.Any]] + + if is_flag and default_is_missing and not self.required: + if multiple: + self.default = () + else: + self.default = False + + if flag_value is None: + flag_value = not self.default + + self.type: types.ParamType + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError("Could not determine name for option") + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: t.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar()}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + extra = [] + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + var_str = ( + envvar + if isinstance(envvar, str) + else ", ".join(str(d) for d in envvar) + ) + extra.append(_("env var: {var}").format(var=var_str)) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or (show_default and (default_value is not None)): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif inspect.isfunction(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = split_opt( + (self.opts if self.default else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + else: + default_string = str(default_value) + + if default_string: + extra.append(_("default: {default}").format(default=default_string)) + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra.append(range_str) + + if self.required: + extra.append(_("required")) + + if extra: + extra_str = "; ".join(extra) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return t.cast(Option, param).flag_value + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + ) + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, "Parameter"] + ) -> t.Tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the constructor of :class:`Parameter`. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: t.Sequence[str], + required: t.Optional[bool] = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() # type: ignore + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Could not determine name for argument") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [self.make_metavar()] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar()}'" + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/decorators.py b/lib/go-jinja2/internal/data/darwin-amd64/click/decorators.py new file mode 100644 index 000000000..d9bba9502 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/decorators.py @@ -0,0 +1,561 @@ +import inspect +import types +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") +T = t.TypeVar("T") +_AnyCallable = t.Callable[..., t.Any] +FC = t.TypeVar("FC", bound=t.Union[_AnyCallable, Command]) + + +def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]": + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + + +def pass_obj(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + +def make_pass_decorator( + object_type: t.Type[T], ensure: bool = False +) -> t.Callable[["t.Callable[te.Concatenate[T, P], R]"], "t.Callable[P, R]"]: + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: "t.Callable[te.Concatenate[T, P], R]") -> "t.Callable[P, R]": + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + ctx = get_current_context() + + obj: t.Optional[T] + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + return decorator # type: ignore[return-value] + + +def pass_meta_key( + key: str, *, doc_description: t.Optional[str] = None +) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, P], R]], t.Callable[P, R]]": + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R: + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator # type: ignore[return-value] + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +# variant: no call, directly as decorator for a function. +@t.overload +def command(name: _AnyCallable) -> Command: + ... + + +# variant: with positional name and with positional or keyword cls argument: +# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) +@t.overload +def command( + name: t.Optional[str], + cls: t.Type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: + ... + + +# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) +@t.overload +def command( + name: None = None, + *, + cls: t.Type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: + ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: + ... + + +def command( + name: t.Union[t.Optional[str], _AnyCallable] = None, + cls: t.Optional[t.Type[CmdType]] = None, + **attrs: t.Any, +) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast(t.Type[CmdType], Command) + + def decorator(f: _AnyCallable) -> CmdType: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + if t.TYPE_CHECKING: + assert cls is not None + assert not callable(name) + + cmd = cls( + name=name or f.__name__.lower().replace("_", "-"), + callback=f, + params=params, + **attrs, + ) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +GrpType = t.TypeVar("GrpType", bound=Group) + + +# variant: no call, directly as decorator for a function. +@t.overload +def group(name: _AnyCallable) -> Group: + ... + + +# variant: with positional name and with positional or keyword cls argument: +# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) +@t.overload +def group( + name: t.Optional[str], + cls: t.Type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: + ... + + +# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) +@t.overload +def group( + name: None = None, + *, + cls: t.Type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: + ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: + ... + + +def group( + name: t.Union[str, _AnyCallable, None] = None, + cls: t.Optional[t.Type[GrpType]] = None, + **attrs: t.Any, +) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if cls is None: + cls = t.cast(t.Type[GrpType], Group) + + if callable(name): + return command(cls=cls, **attrs)(name) + + return command(name, cls, **attrs) + + +def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument( + *param_decls: str, cls: t.Optional[t.Type[Argument]] = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default argument class, refer to :class:`Argument` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Argument + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def option( + *param_decls: str, cls: t.Optional[t.Type[Option]] = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default option class, refer to :class:`Option` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Option + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + metadata: t.Optional[types.ModuleType] + + try: + from importlib import metadata # type: ignore + except ImportError: + # Python < 3.8 + import importlib_metadata as metadata # type: ignore + + try: + version = metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: # type: ignore + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + message % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--help`` option which immediately prints the help page + and exits the program. + + This is usually unnecessary, as the ``--help`` option is added to + each command automatically unless ``add_help_option=False`` is + passed. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/exceptions.py b/lib/go-jinja2/internal/data/darwin-amd64/click/exceptions.py new file mode 100644 index 000000000..fe68a3613 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/exceptions.py @@ -0,0 +1,288 @@ +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .utils import echo +from .utils import format_filename + +if t.TYPE_CHECKING: + from .core import Command + from .core import Context + from .core import Parameter + + +def _join_param_hints( + param_hint: t.Optional[t.Union[t.Sequence[str], str]] +) -> t.Optional[str]: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + + echo(_("Error: {message}").format(message=self.format_message()), file=file) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd: t.Optional["Command"] = self.ctx.command if self.ctx else None + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: t.Optional[str] = None, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + param_type: t.Optional[str] = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: t.Optional[str] = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message(self.param) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: t.Optional[str] = None, + possibilities: t.Optional[t.Sequence[str]] = None, + ctx: t.Optional["Context"] = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: t.Optional["Context"] = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename: str = format_filename(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code: int = code diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/formatting.py b/lib/go-jinja2/internal/data/darwin-amd64/click/formatting.py new file mode 100644 index 000000000..ddd2a2f82 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/formatting.py @@ -0,0 +1,301 @@ +import typing as t +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: t.Optional[int] = None + + +def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: + widths: t.Dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: t.Iterable[t.Tuple[str, str]], col_count: int +) -> t.Iterator[t.Tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: t.List[t.Tuple[int, bool, str]] = [] + buf: t.List[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: t.Optional[int] = None, + max_width: t.Optional[int] = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer: t.List[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage( + self, prog: str, args: str = "", prefix: t.Optional[str] = None + ) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: t.Sequence[t.Tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> t.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> t.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/globals.py b/lib/go-jinja2/internal/data/darwin-amd64/click/globals.py new file mode 100644 index 000000000..480058f10 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/globals.py @@ -0,0 +1,68 @@ +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": + ... + + +@t.overload +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: + ... + + +def get_current_context(silent: bool = False) -> t.Optional["Context"]: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: "Context") -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/parser.py b/lib/go-jinja2/internal/data/darwin-amd64/click/parser.py new file mode 100644 index 000000000..5fa7adfac --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/parser.py @@ -0,0 +1,529 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: t.Sequence[str], nargs_spec: t.Sequence[int] +) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] + spos: t.Optional[int] = None + + def _fetch(c: "te.Deque[V]") -> t.Optional[V]: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def split_opt(opt: str) -> t.Tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +def split_arg_string(string: str) -> t.List[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +class Option: + def __init__( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: t.Set[str] = set() + + for opt in opts: + prefix, value = split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: t.Any, state: "ParsingState") -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class Argument: + def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], + state: "ParsingState", + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class ParsingState: + def __init__(self, rargs: t.List[str]) -> None: + self.opts: t.Dict[str, t.Any] = {} + self.largs: t.List[str] = [] + self.rargs = rargs + self.order: t.List["CoreParameter"] = [] + + +class OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + """ + + def __init__(self, ctx: t.Optional["Context"] = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args: bool = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options: bool = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: t.Dict[str, Option] = {} + self._long_opt: t.Dict[str, Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: t.List[Argument] = [] + + def add_option( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [normalize_opt(opt, self.ctx) for opt in opts] + option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument( + self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 + ) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: t.List[str] + ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: t.Optional[str], state: ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we recombine the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: Option, state: ParsingState + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/py.typed b/lib/go-jinja2/internal/data/darwin-amd64/click/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/shell_completion.py b/lib/go-jinja2/internal/data/darwin-amd64/click/shell_completion.py new file mode 100644 index 000000000..dc9e00b9b --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/shell_completion.py @@ -0,0 +1,596 @@ +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import BaseCommand +from .core import Context +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .parser import split_arg_string +from .utils import echo + + +def shell_complete( + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: + self.value: t.Any = value + self.type: str = type + self.help: t.Optional[str] = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + %(complete_func)s "$@" +else + # eval/source/. command, register function for later + compdef %(complete_func)s %(prog_name)s +fi +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> t.Dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions( + self, args: t.List[str], incomplete: str + ) -> t.List[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + @staticmethod + def _check_version() -> None: + import subprocess + + output = subprocess.run( + ["bash", "-c", 'echo "${BASH_VERSION}"'], stdout=subprocess.PIPE + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + echo( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ), + err=True, + ) + else: + echo( + _("Couldn't detect Bash version, shell completion is not supported."), + err=True, + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete]) + + +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: ShellCompleteType, name: t.Optional[str] = None +) -> ShellCompleteType: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + return cls + + +def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + # Will be None if expose_value is False. + value = ctx.params.get(param.name) + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + args: t.List[str], +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx.protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/termui.py b/lib/go-jinja2/internal/data/darwin-amd64/click/termui.py new file mode 100644 index 000000000..db7a4b286 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/termui.py @@ -0,0 +1,784 @@ +import inspect +import io +import itertools +import sys +import typing as t +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Optional[t.Any] = None, + show_choices: bool = True, + type: t.Optional[ParamType] = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name + + return default + + +def prompt( + text: str, + default: t.Optional[t.Any] = None, + hide_input: bool = False, + confirmation_prompt: t.Union[bool, str] = False, + type: t.Optional[t.Union[ParamType, t.Any]] = None, + value_proc: t.Optional[t.Callable[[str], t.Any]] = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending an interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) # noqa: B306 + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: t.Optional[bool] = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def echo_via_pager( + text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.Iterable[str], text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, +) -> "ProgressBar[V]": + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + + # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor + echo("\033[2J\033[1;1H", nl=False) + + +def _interpret_color( + color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.AnyStr]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +def edit( + text: t.Optional[t.AnyStr] = None, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + filename: t.Optional[str] = None, +) -> t.Optional[t.AnyStr]: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + ed.edit_file(filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Optional[t.Callable[[bool], str]] = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> t.ContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: t.Optional[str] = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/testing.py b/lib/go-jinja2/internal/data/darwin-amd64/click/testing.py new file mode 100644 index 000000000..e0df0d2a6 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/testing.py @@ -0,0 +1,479 @@ +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from .core import BaseCommand + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> t.List[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> t.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast(t.IO[t.Any], input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(input) + + +class Result: + """Holds the captured result of an invoked CLI script.""" + + def __init__( + self, + runner: "CliRunner", + stdout_bytes: bytes, + stderr_bytes: t.Optional[bytes], + return_value: t.Any, + exit_code: int, + exception: t.Optional[BaseException], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = None, + ): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or None if not available + self.stderr_bytes = stderr_bytes + #: The value returned from the invoked command. + #: + #: .. versionadded:: 8.0 + self.return_value = return_value + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self) -> str: + """The (standard) output as unicode string.""" + return self.stdout + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string.""" + if self.stderr_bytes is None: + raise ValueError("stderr not separately captured") + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__( + self, + charset: str = "utf-8", + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + echo_stdin: bool = False, + mix_stderr: bool = True, + ) -> None: + self.charset = charset + self.env: t.Mapping[str, t.Optional[str]] = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli: "BaseCommand") -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None + ) -> t.Mapping[str, t.Optional[str]]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + color: bool = False, + ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into sys.stdin. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + ``stderr`` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + bytes_output = io.BytesIO() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, bytes_output) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + bytes_output, encoding=self.charset, name="", mode="w" + ) + + bytes_error = None + if self.mix_stderr: + sys.stderr = sys.stdout + else: + bytes_error = io.BytesIO() + sys.stderr = _NamedTextIOWrapper( + bytes_error, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(prompt or "") + val = text_input.readline().rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return text_input.readline().rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (bytes_output, bytes_error) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: "BaseCommand", + args: t.Optional[t.Union[str, t.Sequence[str]]] = None, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + catch_exceptions: bool = True, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: t.Optional[BaseException] = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + stdout = outstreams[0].getvalue() + if self.mix_stderr: + stderr = None + else: + stderr = outstreams[1].getvalue() # type: ignore + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None + ) -> t.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + dt = tempfile.mkdtemp(dir=temp_dir) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: # noqa: B014 + pass diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/types.py b/lib/go-jinja2/internal/data/darwin-amd64/click/types.py new file mode 100644 index 000000000..2b1d1797f --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/types.py @@ -0,0 +1,1089 @@ +import os +import stat +import sys +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import format_filename +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[t.Optional[str]] = None + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + def __call__( + self, + value: t.Any, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> t.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> "t.NoReturn": + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name: str = func.__name__ + self.func = func + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = sys.getfilesystemencoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: "Parameter") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: t.Optional[t.Sequence[str]] = None): + self.formats: t.Sequence[str] = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[t.Type[t.Any]] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: "te.Literal[1, -1]", open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Starting with Click 2.0, files can also be opened atomically in which + case all writes go into a separate file in the same folder and upon + completion the file will be moved over to the original location. This + is useful if a file regularly read by other users is modified. + + See :ref:`file-args` for more information. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: "t.Union[str, os.PathLike[str]]") -> bool: + if self.lazy is not None: + return self.lazy + if os.fspath(value) == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, + value: t.Union[str, "os.PathLike[str]", t.IO[t.Any]], + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + value = t.cast("t.Union[str, os.PathLike[str]]", value) + + try: + lazy = self.resolve_lazy_flag(value) + + if lazy: + lf = LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + if ctx is not None: + ctx.call_on_close(lf.close_intelligently) + + return t.cast(t.IO[t.Any], lf) + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: # noqa: B014 + self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +def _is_file_like(value: t.Any) -> "te.TypeGuard[t.IO[t.Any]]": + return hasattr(value, "read") or hasattr(value, "write") + + +class Path(ParamType): + """The ``Path`` type is similar to the :class:`File` type, but + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + + .. versionchanged:: 8.0 + Allow passing ``path_type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: t.Optional[t.Type[t.Any]] = None, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name: str = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result( + self, value: "t.Union[str, os.PathLike[str]]" + ) -> "t.Union[str, bytes, os.PathLike[str]]": + if self.type is not None and not isinstance(value, self.type): + if self.type is str: + return os.fsdecode(value) + elif self.type is bytes: + return os.fsencode(value) + else: + return t.cast("os.PathLike[str]", self.type(value)) + + return value + + def convert( + self, + value: "t.Union[str, os.PathLike[str]]", + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> "t.Union[str, bytes, os.PathLike[str]]": + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + # os.path.realpath doesn't resolve symlinks on Windows + # until Python 3.8. Use pathlib for now. + import pathlib + + rv = os.fsdecode(pathlib.Path(rv).resolve()) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} '{filename}' is a directory.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: + self.types: t.Sequence[ParamType] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) + + +def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() diff --git a/lib/go-jinja2/internal/data/darwin-amd64/click/utils.py b/lib/go-jinja2/internal/data/darwin-amd64/click/utils.py new file mode 100644 index 000000000..d536434f0 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/click/utils.py @@ -0,0 +1,624 @@ +import os +import re +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType +from types import TracebackType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: "t.Callable[P, R]") -> "t.Callable[P, t.Optional[R]]": + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> t.Optional[R]: + try: + return func(*args, **kwargs) + except Exception: + pass + return None + + return update_wrapper(wrapper, func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(sys.getfilesystemencoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: t.Union[str, "os.PathLike[str]"], + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, + ): + self.name: str = os.fspath(filename) + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.Optional[t.IO[t.Any]] + self.should_close: bool + + if self.name == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO[t.Any]: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: # noqa: E402 + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> "LazyFile": + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.close_intelligently() + + def __iter__(self) -> t.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO[t.Any]) -> None: + self._file: t.IO[t.Any] = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> "KeepOpenFile": + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> t.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.Any]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + return + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: t.Optional[t.Union[str, bytes]] = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: "te.Literal['stdin', 'stdout', 'stderr']", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[t.Any]: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :param filename: The name of the file to open, or ``'-'`` for + ``stdin``/``stdout``. + :param mode: The mode in which to open the file. + :param encoding: The encoding to decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + if lazy: + return t.cast( + t.IO[t.Any], LazyFile(filename, mode, encoding, errors, atomic=atomic) + ) + + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + + if not should_close: + f = t.cast(t.IO[t.Any], KeepOpenFile(f)) + + return f + + +def format_filename( + filename: "t.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]", + shorten: bool = False, +) -> str: + """Format a filename as a string for display. Ensures the filename can be + displayed by replacing any invalid bytes or surrogate escapes in the name + with the replacement character ``�``. + + Invalid bytes or surrogate escapes will raise an error when written to a + stream with ``errors="strict". This will typically happen with ``stdout`` + when the locale is something like ``en_GB.UTF-8``. + + Many scenarios *are* safe to write surrogates though, due to PEP 538 and + PEP 540, including: + + - Writing to ``stderr``, which uses ``errors="backslashreplace"``. + - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens + stdout and stderr with ``errors="surrogateescape"``. + - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. + - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. + Python opens stdout and stderr with ``errors="surrogateescape"``. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + else: + filename = os.fspath(filename) + + if isinstance(filename, bytes): + filename = filename.decode(sys.getfilesystemencoding(), "replace") + else: + filename = filename.encode("utf-8", "surrogateescape").decode( + "utf-8", "replace" + ) + + return filename + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no effect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO[t.Any]) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: t.Optional[str] = None, _main: t.Optional[ModuleType] = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # It is set to "" inside a Shiv or PEX zipapp. + if getattr(_main, "__package__", None) in {None, ""} or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.List[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/lib/go-jinja2/internal/data/darwin-amd64/files.json b/lib/go-jinja2/internal/data/darwin-amd64/files.json new file mode 100644 index 000000000..0b2e283b2 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/files.json @@ -0,0 +1,888 @@ +{ + "contentHash": "d6fc8f26c0da5462509c87d32316bba4e8766aec5635673b2cd5cb73f0be68a2", + "files": [ + { + "name": "MarkupSafe-2.1.5.dist-info", + "size": 0, + "perm": 2147484141 + }, + { + "name": "MarkupSafe-2.1.5.dist-info/INSTALLER", + "size": 4, + "perm": 420 + }, + { + "name": "MarkupSafe-2.1.5.dist-info/LICENSE.rst", + "size": 1475, + "perm": 420 + }, + { + "name": "MarkupSafe-2.1.5.dist-info/METADATA", + "size": 3003, + "perm": 420 + }, + { + "name": "MarkupSafe-2.1.5.dist-info/RECORD", + "size": 1190, + "perm": 420 + }, + { + "name": "MarkupSafe-2.1.5.dist-info/REQUESTED", + "size": 0, + "perm": 420 + }, + { + "name": "MarkupSafe-2.1.5.dist-info/WHEEL", + "size": 111, + "perm": 420 + }, + { + "name": "MarkupSafe-2.1.5.dist-info/top_level.txt", + "size": 11, + "perm": 420 + }, + { + "name": "PyYAML-6.0.1.dist-info", + "size": 0, + "perm": 2147484141 + }, + { + "name": "PyYAML-6.0.1.dist-info/INSTALLER", + "size": 4, + "perm": 420 + }, + { + "name": "PyYAML-6.0.1.dist-info/LICENSE", + "size": 1101, + "perm": 420 + }, + { + "name": "PyYAML-6.0.1.dist-info/METADATA", + "size": 2058, + "perm": 420 + }, + { + "name": "PyYAML-6.0.1.dist-info/RECORD", + "size": 2773, + "perm": 420 + }, + { + "name": "PyYAML-6.0.1.dist-info/REQUESTED", + "size": 0, + "perm": 420 + }, + { + "name": "PyYAML-6.0.1.dist-info/WHEEL", + "size": 111, + "perm": 420 + }, + { + "name": "PyYAML-6.0.1.dist-info/top_level.txt", + "size": 11, + "perm": 420 + }, + { + "name": "_yaml", + "size": 0, + "perm": 2147484141 + }, + { + "name": "_yaml/__init__.py", + "size": 1402, + "perm": 420 + }, + { + "name": "bin", + "size": 0, + "perm": 2147484141 + }, + { + "name": "bin/jsonpath_ng", + "size": 261, + "perm": 493 + }, + { + "name": "bin/slugify", + "size": 239, + "perm": 493 + }, + { + "name": "click", + "size": 0, + "perm": 2147484141 + }, + { + "name": "click-8.1.7.dist-info", + "size": 0, + "perm": 2147484141 + }, + { + "name": "click-8.1.7.dist-info/INSTALLER", + "size": 4, + "perm": 420 + }, + { + "name": "click-8.1.7.dist-info/LICENSE.rst", + "size": 1475, + "perm": 420 + }, + { + "name": "click-8.1.7.dist-info/METADATA", + "size": 3014, + "perm": 420 + }, + { + "name": "click-8.1.7.dist-info/RECORD", + "size": 2582, + "perm": 420 + }, + { + "name": "click-8.1.7.dist-info/REQUESTED", + "size": 0, + "perm": 420 + }, + { + "name": "click-8.1.7.dist-info/WHEEL", + "size": 92, + "perm": 420 + }, + { + "name": "click-8.1.7.dist-info/top_level.txt", + "size": 6, + "perm": 420 + }, + { + "name": "click/__init__.py", + "size": 3138, + "perm": 420 + }, + { + "name": "click/_compat.py", + "size": 18744, + "perm": 420 + }, + { + "name": "click/_termui_impl.py", + "size": 24069, + "perm": 420 + }, + { + "name": "click/_textwrap.py", + "size": 1353, + "perm": 420 + }, + { + "name": "click/_winconsole.py", + "size": 7860, + "perm": 420 + }, + { + "name": "click/core.py", + "size": 114086, + "perm": 420 + }, + { + "name": "click/decorators.py", + "size": 18719, + "perm": 420 + }, + { + "name": "click/exceptions.py", + "size": 9273, + "perm": 420 + }, + { + "name": "click/formatting.py", + "size": 9706, + "perm": 420 + }, + { + "name": "click/globals.py", + "size": 1961, + "perm": 420 + }, + { + "name": "click/parser.py", + "size": 19067, + "perm": 420 + }, + { + "name": "click/py.typed", + "size": 0, + "perm": 420 + }, + { + "name": "click/shell_completion.py", + "size": 18460, + "perm": 420 + }, + { + "name": "click/termui.py", + "size": 28324, + "perm": 420 + }, + { + "name": "click/testing.py", + "size": 16084, + "perm": 420 + }, + { + "name": "click/types.py", + "size": 36391, + "perm": 420 + }, + { + "name": "click/utils.py", + "size": 20298, + "perm": 420 + }, + { + "name": "jinja2", + "size": 0, + "perm": 2147484141 + }, + { + "name": "jinja2-3.1.4.dist-info", + "size": 0, + "perm": 2147484141 + }, + { + "name": "jinja2-3.1.4.dist-info/INSTALLER", + "size": 4, + "perm": 420 + }, + { + "name": "jinja2-3.1.4.dist-info/LICENSE.txt", + "size": 1475, + "perm": 420 + }, + { + "name": "jinja2-3.1.4.dist-info/METADATA", + "size": 2640, + "perm": 420 + }, + { + "name": "jinja2-3.1.4.dist-info/RECORD", + "size": 3697, + "perm": 420 + }, + { + "name": "jinja2-3.1.4.dist-info/REQUESTED", + "size": 0, + "perm": 420 + }, + { + "name": "jinja2-3.1.4.dist-info/WHEEL", + "size": 81, + "perm": 420 + }, + { + "name": "jinja2-3.1.4.dist-info/entry_points.txt", + "size": 58, + "perm": 420 + }, + { + "name": "jinja2/__init__.py", + "size": 1928, + "perm": 420 + }, + { + "name": "jinja2/_identifier.py", + "size": 1958, + "perm": 420 + }, + { + "name": "jinja2/async_utils.py", + "size": 2477, + "perm": 420 + }, + { + "name": "jinja2/bccache.py", + "size": 14061, + "perm": 420 + }, + { + "name": "jinja2/compiler.py", + "size": 72271, + "perm": 420 + }, + { + "name": "jinja2/constants.py", + "size": 1433, + "perm": 420 + }, + { + "name": "jinja2/debug.py", + "size": 6299, + "perm": 420 + }, + { + "name": "jinja2/defaults.py", + "size": 1267, + "perm": 420 + }, + { + "name": "jinja2/environment.py", + "size": 61538, + "perm": 420 + }, + { + "name": "jinja2/exceptions.py", + "size": 5071, + "perm": 420 + }, + { + "name": "jinja2/ext.py", + "size": 31877, + "perm": 420 + }, + { + "name": "jinja2/filters.py", + "size": 54611, + "perm": 420 + }, + { + "name": "jinja2/idtracking.py", + "size": 10704, + "perm": 420 + }, + { + "name": "jinja2/lexer.py", + "size": 29754, + "perm": 420 + }, + { + "name": "jinja2/loaders.py", + "size": 23167, + "perm": 420 + }, + { + "name": "jinja2/meta.py", + "size": 4397, + "perm": 420 + }, + { + "name": "jinja2/nativetypes.py", + "size": 4210, + "perm": 420 + }, + { + "name": "jinja2/nodes.py", + "size": 34579, + "perm": 420 + }, + { + "name": "jinja2/optimizer.py", + "size": 1651, + "perm": 420 + }, + { + "name": "jinja2/parser.py", + "size": 39890, + "perm": 420 + }, + { + "name": "jinja2/py.typed", + "size": 0, + "perm": 420 + }, + { + "name": "jinja2/runtime.py", + "size": 33435, + "perm": 420 + }, + { + "name": "jinja2/sandbox.py", + "size": 14616, + "perm": 420 + }, + { + "name": "jinja2/tests.py", + "size": 5926, + "perm": 420 + }, + { + "name": "jinja2/utils.py", + "size": 23952, + "perm": 420 + }, + { + "name": "jinja2/visitor.py", + "size": 3557, + "perm": 420 + }, + { + "name": "jsonpath_ng", + "size": 0, + "perm": 2147484141 + }, + { + "name": "jsonpath_ng-1.6.1.dist-info", + "size": 0, + "perm": 2147484141 + }, + { + "name": "jsonpath_ng-1.6.1.dist-info/INSTALLER", + "size": 4, + "perm": 420 + }, + { + "name": "jsonpath_ng-1.6.1.dist-info/LICENSE", + "size": 11358, + "perm": 420 + }, + { + "name": "jsonpath_ng-1.6.1.dist-info/METADATA", + "size": 18072, + "perm": 420 + }, + { + "name": "jsonpath_ng-1.6.1.dist-info/RECORD", + "size": 2549, + "perm": 420 + }, + { + "name": "jsonpath_ng-1.6.1.dist-info/REQUESTED", + "size": 0, + "perm": 420 + }, + { + "name": "jsonpath_ng-1.6.1.dist-info/WHEEL", + "size": 92, + "perm": 420 + }, + { + "name": "jsonpath_ng-1.6.1.dist-info/entry_points.txt", + "size": 69, + "perm": 420 + }, + { + "name": "jsonpath_ng-1.6.1.dist-info/top_level.txt", + "size": 12, + "perm": 420 + }, + { + "name": "jsonpath_ng/__init__.py", + "size": 116, + "perm": 420 + }, + { + "name": "jsonpath_ng/bin", + "size": 0, + "perm": 2147484141 + }, + { + "name": "jsonpath_ng/bin/__init__.py", + "size": 0, + "perm": 420 + }, + { + "name": "jsonpath_ng/bin/jsonpath.py", + "size": 2057, + "perm": 420 + }, + { + "name": "jsonpath_ng/exceptions.py", + "size": 146, + "perm": 420 + }, + { + "name": "jsonpath_ng/ext", + "size": 0, + "perm": 2147484141 + }, + { + "name": "jsonpath_ng/ext/__init__.py", + "size": 605, + "perm": 420 + }, + { + "name": "jsonpath_ng/ext/arithmetic.py", + "size": 2381, + "perm": 420 + }, + { + "name": "jsonpath_ng/ext/filter.py", + "size": 4312, + "perm": 420 + }, + { + "name": "jsonpath_ng/ext/iterable.py", + "size": 3680, + "perm": 420 + }, + { + "name": "jsonpath_ng/ext/parser.py", + "size": 5351, + "perm": 420 + }, + { + "name": "jsonpath_ng/ext/string.py", + "size": 3261, + "perm": 420 + }, + { + "name": "jsonpath_ng/jsonpath.py", + "size": 26402, + "perm": 420 + }, + { + "name": "jsonpath_ng/lexer.py", + "size": 5231, + "perm": 420 + }, + { + "name": "jsonpath_ng/parser.py", + "size": 5883, + "perm": 420 + }, + { + "name": "markupsafe", + "size": 0, + "perm": 2147484141 + }, + { + "name": "markupsafe/__init__.py", + "size": 10958, + "perm": 420 + }, + { + "name": "markupsafe/_native.py", + "size": 1713, + "perm": 420 + }, + { + "name": "markupsafe/_speedups.c", + "size": 7083, + "perm": 420 + }, + { + "name": "markupsafe/_speedups.cpython-311-darwin.so", + "size": 35272, + "perm": 493, + "compressed": true + }, + { + "name": "markupsafe/_speedups.pyi", + "size": 229, + "perm": 420 + }, + { + "name": "markupsafe/py.typed", + "size": 0, + "perm": 420 + }, + { + "name": "ply", + "size": 0, + "perm": 2147484141 + }, + { + "name": "ply-3.11.dist-info", + "size": 0, + "perm": 2147484141 + }, + { + "name": "ply-3.11.dist-info/DESCRIPTION.rst", + "size": 519, + "perm": 420 + }, + { + "name": "ply-3.11.dist-info/INSTALLER", + "size": 4, + "perm": 420 + }, + { + "name": "ply-3.11.dist-info/METADATA", + "size": 844, + "perm": 420 + }, + { + "name": "ply-3.11.dist-info/RECORD", + "size": 1211, + "perm": 420 + }, + { + "name": "ply-3.11.dist-info/WHEEL", + "size": 110, + "perm": 420 + }, + { + "name": "ply-3.11.dist-info/metadata.json", + "size": 515, + "perm": 420 + }, + { + "name": "ply-3.11.dist-info/top_level.txt", + "size": 4, + "perm": 420 + }, + { + "name": "ply/__init__.py", + "size": 103, + "perm": 420 + }, + { + "name": "ply/cpp.py", + "size": 33639, + "perm": 420 + }, + { + "name": "ply/ctokens.py", + "size": 3155, + "perm": 420 + }, + { + "name": "ply/lex.py", + "size": 42905, + "perm": 420 + }, + { + "name": "ply/yacc.py", + "size": 137736, + "perm": 420 + }, + { + "name": "ply/ygen.py", + "size": 2246, + "perm": 420 + }, + { + "name": "python_slugify-8.0.4.dist-info", + "size": 0, + "perm": 2147484141 + }, + { + "name": "python_slugify-8.0.4.dist-info/INSTALLER", + "size": 4, + "perm": 420 + }, + { + "name": "python_slugify-8.0.4.dist-info/LICENSE", + "size": 1103, + "perm": 420 + }, + { + "name": "python_slugify-8.0.4.dist-info/METADATA", + "size": 8469, + "perm": 420 + }, + { + "name": "python_slugify-8.0.4.dist-info/RECORD", + "size": 1489, + "perm": 420 + }, + { + "name": "python_slugify-8.0.4.dist-info/REQUESTED", + "size": 0, + "perm": 420 + }, + { + "name": "python_slugify-8.0.4.dist-info/WHEEL", + "size": 110, + "perm": 420 + }, + { + "name": "python_slugify-8.0.4.dist-info/entry_points.txt", + "size": 50, + "perm": 420 + }, + { + "name": "python_slugify-8.0.4.dist-info/top_level.txt", + "size": 8, + "perm": 420 + }, + { + "name": "slugify", + "size": 0, + "perm": 2147484141 + }, + { + "name": "slugify/__init__.py", + "size": 346, + "perm": 420 + }, + { + "name": "slugify/__main__.py", + "size": 3961, + "perm": 420 + }, + { + "name": "slugify/__version__.py", + "size": 325, + "perm": 420 + }, + { + "name": "slugify/py.typed", + "size": 0, + "perm": 420 + }, + { + "name": "slugify/slugify.py", + "size": 6180, + "perm": 420 + }, + { + "name": "slugify/special.py", + "size": 1222, + "perm": 420 + }, + { + "name": "text_unidecode", + "size": 0, + "perm": 2147484141 + }, + { + "name": "text_unidecode-1.3.dist-info", + "size": 0, + "perm": 2147484141 + }, + { + "name": "text_unidecode-1.3.dist-info/DESCRIPTION.rst", + "size": 1199, + "perm": 420 + }, + { + "name": "text_unidecode-1.3.dist-info/INSTALLER", + "size": 4, + "perm": 420 + }, + { + "name": "text_unidecode-1.3.dist-info/LICENSE.txt", + "size": 6535, + "perm": 420 + }, + { + "name": "text_unidecode-1.3.dist-info/METADATA", + "size": 2422, + "perm": 420 + }, + { + "name": "text_unidecode-1.3.dist-info/RECORD", + "size": 937, + "perm": 420 + }, + { + "name": "text_unidecode-1.3.dist-info/WHEEL", + "size": 110, + "perm": 420 + }, + { + "name": "text_unidecode-1.3.dist-info/metadata.json", + "size": 1299, + "perm": 420 + }, + { + "name": "text_unidecode-1.3.dist-info/top_level.txt", + "size": 15, + "perm": 420 + }, + { + "name": "text_unidecode/__init__.py", + "size": 484, + "perm": 420 + }, + { + "name": "text_unidecode/data.bin", + "size": 311077, + "perm": 420, + "compressed": true + }, + { + "name": "yaml", + "size": 0, + "perm": 2147484141 + }, + { + "name": "yaml/__init__.py", + "size": 12311, + "perm": 420 + }, + { + "name": "yaml/_yaml.cpython-311-darwin.so", + "size": 429464, + "perm": 493, + "compressed": true + }, + { + "name": "yaml/composer.py", + "size": 4883, + "perm": 420 + }, + { + "name": "yaml/constructor.py", + "size": 28639, + "perm": 420 + }, + { + "name": "yaml/cyaml.py", + "size": 3851, + "perm": 420 + }, + { + "name": "yaml/dumper.py", + "size": 2837, + "perm": 420 + }, + { + "name": "yaml/emitter.py", + "size": 43006, + "perm": 420 + }, + { + "name": "yaml/error.py", + "size": 2533, + "perm": 420 + }, + { + "name": "yaml/events.py", + "size": 2445, + "perm": 420 + }, + { + "name": "yaml/loader.py", + "size": 2061, + "perm": 420 + }, + { + "name": "yaml/nodes.py", + "size": 1440, + "perm": 420 + }, + { + "name": "yaml/parser.py", + "size": 25495, + "perm": 420 + }, + { + "name": "yaml/reader.py", + "size": 6794, + "perm": 420 + }, + { + "name": "yaml/representer.py", + "size": 14190, + "perm": 420 + }, + { + "name": "yaml/resolver.py", + "size": 9004, + "perm": 420 + }, + { + "name": "yaml/scanner.py", + "size": 51279, + "perm": 420 + }, + { + "name": "yaml/serializer.py", + "size": 4165, + "perm": 420 + }, + { + "name": "yaml/tokens.py", + "size": 2573, + "perm": 420 + } + ] +} \ No newline at end of file diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/INSTALLER b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/LICENSE.txt b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/LICENSE.txt new file mode 100644 index 000000000..c37cae49e --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +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/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/METADATA b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/METADATA new file mode 100644 index 000000000..265cc32e1 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/METADATA @@ -0,0 +1,76 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.1.4 +Summary: A very fast and expressive template engine. +Maintainer-email: Pallets +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Typing :: Typed +Requires-Dist: MarkupSafe>=2.0 +Requires-Dist: Babel>=2.7 ; extra == "i18n" +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/jinja/ +Provides-Extra: i18n + +# Jinja + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +## In A Nutshell + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} + + {% endblock %} + + +## Donate + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/RECORD b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/RECORD new file mode 100644 index 000000000..e9bdca35c --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/RECORD @@ -0,0 +1,58 @@ +jinja2-3.1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jinja2-3.1.4.dist-info/LICENSE.txt,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +jinja2-3.1.4.dist-info/METADATA,sha256=R_brzpPQVBvpGcsm-WbrtgotO7suQ1D0F-qkhTzeEfY,2640 +jinja2-3.1.4.dist-info/RECORD,, +jinja2-3.1.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2-3.1.4.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +jinja2-3.1.4.dist-info/entry_points.txt,sha256=OL85gYU1eD8cuPlikifFngXpeBjaxl6rIJ8KkC_3r-I,58 +jinja2/__init__.py,sha256=wIl45IM20KGw-kfr7jJhaBxxX5g4-kihlBYjxopX7Pw,1928 +jinja2/__pycache__/__init__.cpython-311.pyc,, +jinja2/__pycache__/_identifier.cpython-311.pyc,, +jinja2/__pycache__/async_utils.cpython-311.pyc,, +jinja2/__pycache__/bccache.cpython-311.pyc,, +jinja2/__pycache__/compiler.cpython-311.pyc,, +jinja2/__pycache__/constants.cpython-311.pyc,, +jinja2/__pycache__/debug.cpython-311.pyc,, +jinja2/__pycache__/defaults.cpython-311.pyc,, +jinja2/__pycache__/environment.cpython-311.pyc,, +jinja2/__pycache__/exceptions.cpython-311.pyc,, +jinja2/__pycache__/ext.cpython-311.pyc,, +jinja2/__pycache__/filters.cpython-311.pyc,, +jinja2/__pycache__/idtracking.cpython-311.pyc,, +jinja2/__pycache__/lexer.cpython-311.pyc,, +jinja2/__pycache__/loaders.cpython-311.pyc,, +jinja2/__pycache__/meta.cpython-311.pyc,, +jinja2/__pycache__/nativetypes.cpython-311.pyc,, +jinja2/__pycache__/nodes.cpython-311.pyc,, +jinja2/__pycache__/optimizer.cpython-311.pyc,, +jinja2/__pycache__/parser.cpython-311.pyc,, +jinja2/__pycache__/runtime.cpython-311.pyc,, +jinja2/__pycache__/sandbox.cpython-311.pyc,, +jinja2/__pycache__/tests.cpython-311.pyc,, +jinja2/__pycache__/utils.cpython-311.pyc,, +jinja2/__pycache__/visitor.cpython-311.pyc,, +jinja2/_identifier.py,sha256=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=JXKWCAXmTx0iZB4-hAsF50vgjxw_RJTjiLOlGGTBso0,2477 +jinja2/bccache.py,sha256=gh0qs9rulnXo0PhX5jTJy2UHzI8wFnQ63o_vw7nhzRg,14061 +jinja2/compiler.py,sha256=dpV-n6_iQUP4uSwlXwGUavJmwjvXdyxKzJ-AonFjPBk,72271 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=iWJ432RadxJNnaMOPrjIDInz50UEgni3_HKuFXi2vuQ,6299 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=xhFkmxO0CESA76Ki5tz4XWq9yzGu-t0p93JCCVBVNps,61538 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=igsBH7c6C0byHaOtMbE-ugpt4GjLGgR-ywskyXtKgq8,31877 +jinja2/filters.py,sha256=bKeqjFjjz88TkHVLSyyMIEB75CzAN6b3Airgx0phJDg,54611 +jinja2/idtracking.py,sha256=GfNmadir4oDALVxzn3DL9YInhJDr69ebXeA2ygfuCGA,10704 +jinja2/lexer.py,sha256=xnWWXhPndHFsoqzpc5VTjheDE9JuKk9MUo9DZkrM8Os,29754 +jinja2/loaders.py,sha256=ru0GIWHo5KiHJi7_MoI_LvGDoBBvP6rd0hiC1ReaTwk,23167 +jinja2/meta.py,sha256=OTDPkaFvU2Hgvx-6akz7154F8BIWaRmvJcBFvwopHww,4397 +jinja2/nativetypes.py,sha256=7GIGALVJgdyL80oZJdQUaUfwSt5q2lSSZbXt0dNf_M4,4210 +jinja2/nodes.py,sha256=m1Duzcr6qhZI8JQ6VyJgUNinjAf5bQzijSmDnMsvUx8,34579 +jinja2/optimizer.py,sha256=rJnCRlQ7pZsEEmMhsQDgC_pKyDHxP5TPS6zVPGsgcu8,1651 +jinja2/parser.py,sha256=DV1iF1FR2Rsaj_5zl8rmx7j6Bj4S8iLHoYsvJ0bfEis,39890 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=POXT3tKNKJRENx2CymwUsOOXH2JwGPjW702njB5__cQ,33435 +jinja2/sandbox.py,sha256=TJjBNS9qRJ2ZgBMWdAgRBpyDLOHea2kT-2mk4PrjYx0,14616 +jinja2/tests.py,sha256=VLsBhVFnWg-PxSBz1MhRnNWgP1ovXk3neO1FLQMeC9Q,5926 +jinja2/utils.py,sha256=nV7IpWLvRCMyHW1irBAK8CIPAnOFfkb2ukggDBjbBEY,23952 +jinja2/visitor.py,sha256=EcnL1PIwf_4RVCOMxsRNuR8AXHbS1qfAdMOE2ngKJz4,3557 diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/REQUESTED b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/REQUESTED new file mode 100644 index 000000000..e69de29bb diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/WHEEL b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/WHEEL new file mode 100644 index 000000000..3b5e64b5e --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/entry_points.txt b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/entry_points.txt new file mode 100644 index 000000000..abc3eae3b --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2-3.1.4.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2=jinja2.ext:babel_extract[i18n] + diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2/__init__.py b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/__init__.py new file mode 100644 index 000000000..2f0b5b286 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/__init__.py @@ -0,0 +1,38 @@ +"""Jinja is a template engine written in pure Python. It provides a +non-XML syntax that supports inline expressions and an optional +sandboxed environment. +""" + +from .bccache import BytecodeCache as BytecodeCache +from .bccache import FileSystemBytecodeCache as FileSystemBytecodeCache +from .bccache import MemcachedBytecodeCache as MemcachedBytecodeCache +from .environment import Environment as Environment +from .environment import Template as Template +from .exceptions import TemplateAssertionError as TemplateAssertionError +from .exceptions import TemplateError as TemplateError +from .exceptions import TemplateNotFound as TemplateNotFound +from .exceptions import TemplateRuntimeError as TemplateRuntimeError +from .exceptions import TemplatesNotFound as TemplatesNotFound +from .exceptions import TemplateSyntaxError as TemplateSyntaxError +from .exceptions import UndefinedError as UndefinedError +from .loaders import BaseLoader as BaseLoader +from .loaders import ChoiceLoader as ChoiceLoader +from .loaders import DictLoader as DictLoader +from .loaders import FileSystemLoader as FileSystemLoader +from .loaders import FunctionLoader as FunctionLoader +from .loaders import ModuleLoader as ModuleLoader +from .loaders import PackageLoader as PackageLoader +from .loaders import PrefixLoader as PrefixLoader +from .runtime import ChainableUndefined as ChainableUndefined +from .runtime import DebugUndefined as DebugUndefined +from .runtime import make_logging_undefined as make_logging_undefined +from .runtime import StrictUndefined as StrictUndefined +from .runtime import Undefined as Undefined +from .utils import clear_caches as clear_caches +from .utils import is_undefined as is_undefined +from .utils import pass_context as pass_context +from .utils import pass_environment as pass_environment +from .utils import pass_eval_context as pass_eval_context +from .utils import select_autoescape as select_autoescape + +__version__ = "3.1.4" diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2/_identifier.py b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/_identifier.py new file mode 100644 index 000000000..928c1503c --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/_identifier.py @@ -0,0 +1,6 @@ +import re + +# generated by scripts/generate_identifier_pattern.py +pattern = re.compile( + r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𐴤-𐽆𐴧-𐽐𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑄴𑅅𑅆𑅳𑆀-𑆂𑆳-𑇀𑇉-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌻𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑑞𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑠬-𑠺𑨁-𑨊𑨳-𑨹𑨻-𑨾𑩇𑩑-𑩛𑪊-𑪙𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𑴱-𑴶𑴺𑴼𑴽𑴿-𑵅𑵇𑶊-𑶎𑶐𑶑𑶓-𑶗𑻳-𑻶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950 +) diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2/async_utils.py b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/async_utils.py new file mode 100644 index 000000000..e65219e49 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/async_utils.py @@ -0,0 +1,84 @@ +import inspect +import typing as t +from functools import WRAPPER_ASSIGNMENTS +from functools import wraps + +from .utils import _PassArg +from .utils import pass_eval_context + +V = t.TypeVar("V") + + +def async_variant(normal_func): # type: ignore + def decorator(async_func): # type: ignore + pass_arg = _PassArg.from_obj(normal_func) + need_eval_context = pass_arg is None + + if pass_arg is _PassArg.environment: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].is_async) + + else: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].environment.is_async) + + # Take the doc and annotations from the sync function, but the + # name from the async function. Pallets-Sphinx-Themes + # build_function_directive expects __wrapped__ to point to the + # sync function. + async_func_attrs = ("__module__", "__name__", "__qualname__") + normal_func_attrs = tuple(set(WRAPPER_ASSIGNMENTS).difference(async_func_attrs)) + + @wraps(normal_func, assigned=normal_func_attrs) + @wraps(async_func, assigned=async_func_attrs, updated=()) + def wrapper(*args, **kwargs): # type: ignore + b = is_async(args) + + if need_eval_context: + args = args[1:] + + if b: + return async_func(*args, **kwargs) + + return normal_func(*args, **kwargs) + + if need_eval_context: + wrapper = pass_eval_context(wrapper) + + wrapper.jinja_async_variant = True # type: ignore[attr-defined] + return wrapper + + return decorator + + +_common_primitives = {int, float, bool, str, list, dict, tuple, type(None)} + + +async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V": + # Avoid a costly call to isawaitable + if type(value) in _common_primitives: + return t.cast("V", value) + + if inspect.isawaitable(value): + return await t.cast("t.Awaitable[V]", value) + + return t.cast("V", value) + + +async def auto_aiter( + iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> "t.AsyncIterator[V]": + if hasattr(iterable, "__aiter__"): + async for item in t.cast("t.AsyncIterable[V]", iterable): + yield item + else: + for item in iterable: + yield item + + +async def auto_to_list( + value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> t.List["V"]: + return [x async for x in auto_aiter(value)] diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2/bccache.py b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/bccache.py new file mode 100644 index 000000000..ada8b099f --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/bccache.py @@ -0,0 +1,408 @@ +"""The optional bytecode cache system. This is useful if you have very +complex template situations and the compilation of all those templates +slows down your application too much. + +Situations where this is useful are often forking web applications that +are initialized on the first request. +""" + +import errno +import fnmatch +import marshal +import os +import pickle +import stat +import sys +import tempfile +import typing as t +from hashlib import sha1 +from io import BytesIO +from types import CodeType + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + + class _MemcachedClient(te.Protocol): + def get(self, key: str) -> bytes: ... + + def set( + self, key: str, value: bytes, timeout: t.Optional[int] = None + ) -> None: ... + + +bc_version = 5 +# Magic bytes to identify Jinja bytecode cache files. Contains the +# Python major and minor version to avoid loading incompatible bytecode +# if a project upgrades its Python version. +bc_magic = ( + b"j2" + + pickle.dumps(bc_version, 2) + + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1], 2) +) + + +class Bucket: + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + + def __init__(self, environment: "Environment", key: str, checksum: str) -> None: + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + + def reset(self) -> None: + """Resets the bucket (unloads the bytecode).""" + self.code: t.Optional[CodeType] = None + + def load_bytecode(self, f: t.BinaryIO) -> None: + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + # if marshal_load fails then we need to reload + try: + self.code = marshal.load(f) + except (EOFError, ValueError, TypeError): + self.reset() + return + + def write_bytecode(self, f: t.IO[bytes]) -> None: + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: + raise TypeError("can't write empty bucket") + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) + marshal.dump(self.code, f) + + def bytecode_from_string(self, string: bytes) -> None: + """Load bytecode from bytes.""" + self.load_bytecode(BytesIO(string)) + + def bytecode_to_string(self) -> bytes: + """Return the bytecode as bytes.""" + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + +class BytecodeCache: + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of + Jinja. + """ + + def load_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + + def dump_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + + def clear(self) -> None: + """Clears the cache. This method is not used by Jinja but should be + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + + def get_cache_key( + self, name: str, filename: t.Optional[t.Union[str]] = None + ) -> str: + """Returns the unique hash key for this template name.""" + hash = sha1(name.encode("utf-8")) + + if filename is not None: + hash.update(f"|{filename}".encode()) + + return hash.hexdigest() + + def get_source_checksum(self, source: str) -> str: + """Returns a checksum for the source.""" + return sha1(source.encode("utf-8")).hexdigest() + + def get_bucket( + self, + environment: "Environment", + name: str, + filename: t.Optional[str], + source: str, + ) -> Bucket: + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + + def set_bucket(self, bucket: Bucket) -> None: + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified a default cache directory is selected. On + Windows the user's temp directory is used, on UNIX systems a directory + is created for the user in the system temp directory. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + + def __init__( + self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache" + ) -> None: + if directory is None: + directory = self._get_default_cache_dir() + self.directory = directory + self.pattern = pattern + + def _get_default_cache_dir(self) -> str: + def _unsafe_dir() -> "te.NoReturn": + raise RuntimeError( + "Cannot determine safe temp directory. You " + "need to explicitly provide one." + ) + + tmpdir = tempfile.gettempdir() + + # On windows the temporary directory is used specific unless + # explicitly forced otherwise. We can just use that. + if os.name == "nt": + return tmpdir + if not hasattr(os, "getuid"): + _unsafe_dir() + + dirname = f"_jinja2-cache-{os.getuid()}" + actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + except OSError as e: + if e.errno != errno.EEXIST: + raise + + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + + return actual_dir + + def _get_cache_filename(self, bucket: Bucket) -> str: + return os.path.join(self.directory, self.pattern % (bucket.key,)) + + def load_bytecode(self, bucket: Bucket) -> None: + filename = self._get_cache_filename(bucket) + + # Don't test for existence before opening the file, since the + # file could disappear after the test before the open. + try: + f = open(filename, "rb") + except (FileNotFoundError, IsADirectoryError, PermissionError): + # PermissionError can occur on Windows when an operation is + # in progress, such as calling clear(). + return + + with f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket: Bucket) -> None: + # Write to a temporary file, then rename to the real name after + # writing. This avoids another process reading the file before + # it is fully written. + name = self._get_cache_filename(bucket) + f = tempfile.NamedTemporaryFile( + mode="wb", + dir=os.path.dirname(name), + prefix=os.path.basename(name), + suffix=".tmp", + delete=False, + ) + + def remove_silent() -> None: + try: + os.remove(f.name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + pass + + try: + with f: + bucket.write_bytecode(f) + except BaseException: + remove_silent() + raise + + try: + os.replace(f.name, name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + remove_silent() + except BaseException: + remove_silent() + raise + + def clear(self) -> None: + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove + + files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",)) + for filename in files: + try: + remove(os.path.join(self.directory, filename)) + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + + - `cachelib `_ + - `python-memcached `_ + + (Unfortunately the django cache interface is not compatible because it + does not support storing binary data, only text. You can however pass + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + + def __init__( + self, + client: "_MemcachedClient", + prefix: str = "jinja2/bytecode/", + timeout: t.Optional[int] = None, + ignore_memcache_errors: bool = True, + ): + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + + def load_bytecode(self, bucket: Bucket) -> None: + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise + else: + bucket.bytecode_from_string(code) + + def dump_bytecode(self, bucket: Bucket) -> None: + key = self.prefix + bucket.key + value = bucket.bytecode_to_string() + + try: + if self.timeout is not None: + self.client.set(key, value, self.timeout) + else: + self.client.set(key, value) + except Exception: + if not self.ignore_memcache_errors: + raise diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2/compiler.py b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/compiler.py new file mode 100644 index 000000000..274071750 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/compiler.py @@ -0,0 +1,1960 @@ +"""Compiles nodes from the parser into Python code.""" + +import typing as t +from contextlib import contextmanager +from functools import update_wrapper +from io import StringIO +from itertools import chain +from keyword import iskeyword as is_python_keyword + +from markupsafe import escape +from markupsafe import Markup + +from . import nodes +from .exceptions import TemplateAssertionError +from .idtracking import Symbols +from .idtracking import VAR_LOAD_ALIAS +from .idtracking import VAR_LOAD_PARAMETER +from .idtracking import VAR_LOAD_RESOLVE +from .idtracking import VAR_LOAD_UNDEFINED +from .nodes import EvalContext +from .optimizer import Optimizer +from .utils import _PassArg +from .utils import concat +from .visitor import NodeVisitor + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +operators = { + "eq": "==", + "ne": "!=", + "gt": ">", + "gteq": ">=", + "lt": "<", + "lteq": "<=", + "in": "in", + "notin": "not in", +} + + +def optimizeconst(f: F) -> F: + def new_func( + self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any + ) -> t.Any: + # Only optimize if the frame is not volatile + if self.optimizer is not None and not frame.eval_ctx.volatile: + new_node = self.optimizer.visit(node, frame.eval_ctx) + + if new_node != node: + return self.visit(new_node, frame) + + return f(self, node, frame, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_binops # type: ignore + ): + self.write(f"environment.call_binop(context, {op!r}, ") + self.visit(node.left, frame) + self.write(", ") + self.visit(node.right, frame) + else: + self.write("(") + self.visit(node.left, frame) + self.write(f" {op} ") + self.visit(node.right, frame) + + self.write(")") + + return visitor + + +def _make_unop( + op: str, +) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_unops # type: ignore + ): + self.write(f"environment.call_unop(context, {op!r}, ") + self.visit(node.node, frame) + else: + self.write("(" + op) + self.visit(node.node, frame) + + self.write(")") + + return visitor + + +def generate( + node: nodes.Template, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, +) -> t.Optional[str]: + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError("Can't compile non template nodes") + + generator = environment.code_generator_class( + environment, name, filename, stream, defer_init, optimized + ) + generator.visit(node) + + if stream is None: + return generator.stream.getvalue() # type: ignore + + return None + + +def has_safe_repr(value: t.Any) -> bool: + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + + if type(value) in {bool, int, float, complex, range, str, Markup}: + return True + + if type(value) in {tuple, list, set, frozenset}: + return all(has_safe_repr(v) for v in value) + + if type(value) is dict: # noqa E721 + return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items()) + + return False + + +def find_undeclared( + nodes: t.Iterable[nodes.Node], names: t.Iterable[str] +) -> t.Set[str]: + """Check if the names passed are accessed undeclared. The return value + is a set of all the undeclared names from the sequence of names found. + """ + visitor = UndeclaredNameVisitor(names) + try: + for node in nodes: + visitor.visit(node) + except VisitorExit: + pass + return visitor.undeclared + + +class MacroRef: + def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None: + self.node = node + self.accesses_caller = False + self.accesses_kwargs = False + self.accesses_varargs = False + + +class Frame: + """Holds compile time information for us.""" + + def __init__( + self, + eval_ctx: EvalContext, + parent: t.Optional["Frame"] = None, + level: t.Optional[int] = None, + ) -> None: + self.eval_ctx = eval_ctx + + # the parent of this frame + self.parent = parent + + if parent is None: + self.symbols = Symbols(level=level) + + # in some dynamic inheritance situations the compiler needs to add + # write tests around output statements. + self.require_output_check = False + + # inside some tags we are using a buffer rather than yield statements. + # this for example affects {% filter %} or {% macro %}. If a frame + # is buffered this variable points to the name of the list used as + # buffer. + self.buffer: t.Optional[str] = None + + # the name of the block we're in, otherwise None. + self.block: t.Optional[str] = None + + else: + self.symbols = Symbols(parent.symbols, level=level) + self.require_output_check = parent.require_output_check + self.buffer = parent.buffer + self.block = parent.block + + # a toplevel frame is the root + soft frames such as if conditions. + self.toplevel = False + + # the root frame is basically just the outermost frame, so no if + # conditions. This information is used to optimize inheritance + # situations. + self.rootlevel = False + + # variables set inside of loops and blocks should not affect outer frames, + # but they still needs to be kept track of as part of the active context. + self.loop_frame = False + self.block_frame = False + + # track whether the frame is being used in an if-statement or conditional + # expression as it determines which errors should be raised during runtime + # or compile time. + self.soft_frame = False + + def copy(self) -> "Frame": + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.symbols = self.symbols.copy() + return rv + + def inner(self, isolated: bool = False) -> "Frame": + """Return an inner frame.""" + if isolated: + return Frame(self.eval_ctx, level=self.symbols.level + 1) + return Frame(self.eval_ctx, self) + + def soft(self) -> "Frame": + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + + This is only used to implement if-statements and conditional + expressions. + """ + rv = self.copy() + rv.rootlevel = False + rv.soft_frame = True + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self) -> None: + self.filters: t.Set[str] = set() + self.tests: t.Set[str] = set() + + def visit_Filter(self, node: nodes.Filter) -> None: + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node: nodes.Test) -> None: + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names: t.Iterable[str]) -> None: + self.names = set(names) + self.undeclared: t.Set[str] = set() + + def visit_Name(self, node: nodes.Name) -> None: + if node.ctx == "load" and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting a blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + def __init__( + self, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, + ) -> None: + if stream is None: + stream = StringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + self.optimizer: t.Optional[Optimizer] = None + + if optimized: + self.optimizer = Optimizer(environment) + + # aliases for imports + self.import_aliases: t.Dict[str, str] = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks: t.Dict[str, nodes.Block] = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests: t.Dict[str, str] = {} + self.filters: t.Dict[str, str] = {} + + # the debug information + self.debug_info: t.List[t.Tuple[int, int]] = [] + self._write_debug_info: t.Optional[int] = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # Tracks toplevel assignments + self._assign_stack: t.List[t.Set[str]] = [] + + # Tracks parameter definition blocks + self._param_def_block: t.List[t.Set[str]] = [] + + # Tracks the current context. + self._context_reference_stack = ["context"] + + @property + def optimized(self) -> bool: + return self.optimizer is not None + + # -- Various compilation helpers + + def fail(self, msg: str, lineno: int) -> "te.NoReturn": + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self) -> str: + """Get a new unique identifier.""" + self._last_identifier += 1 + return f"t_{self._last_identifier}" + + def buffer(self, frame: Frame) -> None: + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline(f"{frame.buffer} = []") + + def return_buffer_contents( + self, frame: Frame, force_unescaped: bool = False + ) -> None: + """Return the buffer contents of the frame.""" + if not force_unescaped: + if frame.eval_ctx.volatile: + self.writeline("if context.eval_ctx.autoescape:") + self.indent() + self.writeline(f"return Markup(concat({frame.buffer}))") + self.outdent() + self.writeline("else:") + self.indent() + self.writeline(f"return concat({frame.buffer})") + self.outdent() + return + elif frame.eval_ctx.autoescape: + self.writeline(f"return Markup(concat({frame.buffer}))") + return + self.writeline(f"return concat({frame.buffer})") + + def indent(self) -> None: + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step: int = 1) -> None: + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None: + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline("yield ", node) + else: + self.writeline(f"{frame.buffer}.append(", node) + + def end_write(self, frame: Frame) -> None: + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(")") + + def simple_write( + self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None + ) -> None: + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None: + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically. + """ + try: + self.writeline("pass") + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x: str) -> None: + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write("\n" * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(" " * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline( + self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0 + ) -> None: + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None: + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature( + self, + node: t.Union[nodes.Call, nodes.Filter, nodes.Test], + frame: Frame, + extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> None: + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occur. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = any( + is_python_keyword(t.cast(str, k)) + for k in chain((x.key for x in node.kwargs), extra_kwargs or ()) + ) + + for arg in node.args: + self.write(", ") + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(", ") + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f", {key}={value}") + if node.dyn_args: + self.write(", *") + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(", **dict({") + else: + self.write(", **{") + for kwarg in node.kwargs: + self.write(f"{kwarg.key!r}: ") + self.visit(kwarg.value, frame) + self.write(", ") + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f"{key!r}: {value}, ") + if node.dyn_kwargs is not None: + self.write("}, **") + self.visit(node.dyn_kwargs, frame) + self.write(")") + else: + self.write("}") + + elif node.dyn_kwargs is not None: + self.write(", **") + self.visit(node.dyn_kwargs, frame) + + def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None: + """Find all filter and test names used in the template and + assign them to variables in the compiled namespace. Checking + that the names are registered with the environment is done when + compiling the Filter and Test nodes. If the node is in an If or + CondExpr node, the check is done at runtime instead. + + .. versionchanged:: 3.0 + Filters and tests in If and CondExpr nodes are checked at + runtime instead of compile time. + """ + visitor = DependencyFinderVisitor() + + for node in nodes: + visitor.visit(node) + + for id_map, names, dependency in ( + (self.filters, visitor.filters, "filters"), + ( + self.tests, + visitor.tests, + "tests", + ), + ): + for name in sorted(names): + if name not in id_map: + id_map[name] = self.temporary_identifier() + + # add check during runtime that dependencies used inside of executed + # blocks are defined, as this step may be skipped during compile time + self.writeline("try:") + self.indent() + self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]") + self.outdent() + self.writeline("except KeyError:") + self.indent() + self.writeline("@internalcode") + self.writeline(f"def {id_map[name]}(*unused):") + self.indent() + self.writeline( + f'raise TemplateRuntimeError("No {dependency[:-1]}' + f' named {name!r} found.")' + ) + self.outdent() + self.outdent() + + def enter_frame(self, frame: Frame) -> None: + undefs = [] + for target, (action, param) in frame.symbols.loads.items(): + if action == VAR_LOAD_PARAMETER: + pass + elif action == VAR_LOAD_RESOLVE: + self.writeline(f"{target} = {self.get_resolve_func()}({param!r})") + elif action == VAR_LOAD_ALIAS: + self.writeline(f"{target} = {param}") + elif action == VAR_LOAD_UNDEFINED: + undefs.append(target) + else: + raise NotImplementedError("unknown load instruction") + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None: + if not with_python_scope: + undefs = [] + for target in frame.symbols.loads: + undefs.append(target) + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str: + return async_value if self.environment.is_async else sync_value + + def func(self, name: str) -> str: + return f"{self.choose_async()}def {name}" + + def macro_body( + self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame + ) -> t.Tuple[Frame, MacroRef]: + """Dump the function def of a macro or call block.""" + frame = frame.inner() + frame.symbols.analyze_node(node) + macro_ref = MacroRef(node) + + explicit_caller = None + skip_special_params = set() + args = [] + + for idx, arg in enumerate(node.args): + if arg.name == "caller": + explicit_caller = idx + if arg.name in ("kwargs", "varargs"): + skip_special_params.add(arg.name) + args.append(frame.symbols.ref(arg.name)) + + undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs")) + + if "caller" in undeclared: + # In older Jinja versions there was a bug that allowed caller + # to retain the special behavior even if it was mentioned in + # the argument list. However thankfully this was only really + # working if it was the last argument. So we are explicitly + # checking this now and error out if it is anywhere else in + # the argument list. + if explicit_caller is not None: + try: + node.defaults[explicit_caller - len(node.args)] + except IndexError: + self.fail( + "When defining macros or call blocks the " + 'special "caller" argument must be omitted ' + "or be given a default.", + node.lineno, + ) + else: + args.append(frame.symbols.declare_parameter("caller")) + macro_ref.accesses_caller = True + if "kwargs" in undeclared and "kwargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("kwargs")) + macro_ref.accesses_kwargs = True + if "varargs" in undeclared and "varargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("varargs")) + macro_ref.accesses_varargs = True + + # macros are delayed, they never require output checks + frame.require_output_check = False + frame.symbols.analyze_node(node) + self.writeline(f"{self.func('macro')}({', '.join(args)}):", node) + self.indent() + + self.buffer(frame) + self.enter_frame(frame) + + self.push_parameter_definitions(frame) + for idx, arg in enumerate(node.args): + ref = frame.symbols.ref(arg.name) + self.writeline(f"if {ref} is missing:") + self.indent() + try: + default = node.defaults[idx - len(node.args)] + except IndexError: + self.writeline( + f'{ref} = undefined("parameter {arg.name!r} was not provided",' + f" name={arg.name!r})" + ) + else: + self.writeline(f"{ref} = ") + self.visit(default, frame) + self.mark_parameter_stored(ref) + self.outdent() + self.pop_parameter_definitions() + + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame, force_unescaped=True) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + return frame, macro_ref + + def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None: + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args) + name = getattr(macro_ref.node, "name", None) + if len(macro_ref.node.args) == 1: + arg_tuple += "," + self.write( + f"Macro(environment, macro, {name!r}, ({arg_tuple})," + f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r}," + f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)" + ) + + def position(self, node: nodes.Node) -> str: + """Return a human readable position for the node.""" + rv = f"line {node.lineno}" + if self.name is not None: + rv = f"{rv} in {self.name!r}" + return rv + + def dump_local_context(self, frame: Frame) -> str: + items_kv = ", ".join( + f"{name!r}: {target}" + for name, target in frame.symbols.dump_stores().items() + ) + return f"{{{items_kv}}}" + + def write_commons(self) -> None: + """Writes a common preamble that is used by root and block functions. + Primarily this sets up common local helpers and enforces a generator + through a dead branch. + """ + self.writeline("resolve = context.resolve_or_missing") + self.writeline("undefined = environment.undefined") + self.writeline("concat = environment.concat") + # always use the standard Undefined class for the implicit else of + # conditional expressions + self.writeline("cond_expr_undefined = Undefined") + self.writeline("if 0: yield None") + + def push_parameter_definitions(self, frame: Frame) -> None: + """Pushes all parameter targets from the given frame into a local + stack that permits tracking of yet to be assigned parameters. In + particular this enables the optimization from `visit_Name` to skip + undefined expressions for parameters in macros as macros can reference + otherwise unbound parameters. + """ + self._param_def_block.append(frame.symbols.dump_param_targets()) + + def pop_parameter_definitions(self) -> None: + """Pops the current parameter definitions set.""" + self._param_def_block.pop() + + def mark_parameter_stored(self, target: str) -> None: + """Marks a parameter in the current parameter definitions as stored. + This will skip the enforced undefined checks. + """ + if self._param_def_block: + self._param_def_block[-1].discard(target) + + def push_context_reference(self, target: str) -> None: + self._context_reference_stack.append(target) + + def pop_context_reference(self) -> None: + self._context_reference_stack.pop() + + def get_context_ref(self) -> str: + return self._context_reference_stack[-1] + + def get_resolve_func(self) -> str: + target = self._context_reference_stack[-1] + if target == "context": + return "resolve" + return f"{target}.resolve" + + def derive_context(self, frame: Frame) -> str: + return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})" + + def parameter_is_undeclared(self, target: str) -> bool: + """Checks if a given target is an undeclared parameter.""" + if not self._param_def_block: + return False + return target in self._param_def_block[-1] + + def push_assign_tracking(self) -> None: + """Pushes a new layer for assignment tracking.""" + self._assign_stack.append(set()) + + def pop_assign_tracking(self, frame: Frame) -> None: + """Pops the topmost level for assignment tracking and updates the + context variables if necessary. + """ + vars = self._assign_stack.pop() + if ( + not frame.block_frame + and not frame.loop_frame + and not frame.toplevel + or not vars + ): + return + public_names = [x for x in vars if x[:1] != "_"] + if len(vars) == 1: + name = next(iter(vars)) + ref = frame.symbols.ref(name) + if frame.loop_frame: + self.writeline(f"_loop_vars[{name!r}] = {ref}") + return + if frame.block_frame: + self.writeline(f"_block_vars[{name!r}] = {ref}") + return + self.writeline(f"context.vars[{name!r}] = {ref}") + else: + if frame.loop_frame: + self.writeline("_loop_vars.update({") + elif frame.block_frame: + self.writeline("_block_vars.update({") + else: + self.writeline("context.vars.update({") + for idx, name in enumerate(vars): + if idx: + self.write(", ") + ref = frame.symbols.ref(name) + self.write(f"{name!r}: {ref}") + self.write("})") + if not frame.block_frame and not frame.loop_frame and public_names: + if len(public_names) == 1: + self.writeline(f"context.exported_vars.add({public_names[0]!r})") + else: + names_str = ", ".join(map(repr, public_names)) + self.writeline(f"context.exported_vars.update(({names_str}))") + + # -- Statement Visitors + + def visit_Template( + self, node: nodes.Template, frame: t.Optional[Frame] = None + ) -> None: + assert frame is None, "no root frame allowed" + eval_ctx = EvalContext(self.environment, self.name) + + from .runtime import async_exported + from .runtime import exported + + if self.environment.is_async: + exported_names = sorted(exported + async_exported) + else: + exported_names = sorted(exported) + + self.writeline("from jinja2.runtime import " + ", ".join(exported_names)) + + # if we want a deferred initialization we cannot move the + # environment into a local name + envenv = "" if self.defer_init else ", environment=environment" + + # do we have an extends tag at all? If not, we can save some + # overhead by just not processing any inheritance code. + have_extends = node.find(nodes.Extends) is not None + + # find all blocks + for block in node.find_all(nodes.Block): + if block.name in self.blocks: + self.fail(f"block {block.name!r} defined twice", block.lineno) + self.blocks[block.name] = block + + # find all imports and import them + for import_ in node.find_all(nodes.ImportedName): + if import_.importname not in self.import_aliases: + imp = import_.importname + self.import_aliases[imp] = alias = self.temporary_identifier() + if "." in imp: + module, obj = imp.rsplit(".", 1) + self.writeline(f"from {module} import {obj} as {alias}") + else: + self.writeline(f"import {imp} as {alias}") + + # add the load name + self.writeline(f"name = {self.name!r}") + + # generate the root render function. + self.writeline( + f"{self.func('root')}(context, missing=missing{envenv}):", extra=1 + ) + self.indent() + self.write_commons() + + # process the root + frame = Frame(eval_ctx) + if "self" in find_undeclared(node.body, ("self",)): + ref = frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + frame.symbols.analyze_node(node) + frame.toplevel = frame.rootlevel = True + frame.require_output_check = have_extends and not self.has_known_extends + if have_extends: + self.writeline("parent_template = None") + self.enter_frame(frame) + self.pull_dependencies(node.body) + self.blockvisit(node.body, frame) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + # make sure that the parent root is called. + if have_extends: + if not self.has_known_extends: + self.indent() + self.writeline("if parent_template is not None:") + self.indent() + if not self.environment.is_async: + self.writeline("yield from parent_template.root_render_func(context)") + else: + self.writeline( + "async for event in parent_template.root_render_func(context):" + ) + self.indent() + self.writeline("yield event") + self.outdent() + self.outdent(1 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in self.blocks.items(): + self.writeline( + f"{self.func('block_' + name)}(context, missing=missing{envenv}):", + block, + 1, + ) + self.indent() + self.write_commons() + # It's important that we do not make this frame a child of the + # toplevel template. This would cause a variety of + # interesting issues with identifier tracking. + block_frame = Frame(eval_ctx) + block_frame.block_frame = True + undeclared = find_undeclared(block.body, ("self", "super")) + if "self" in undeclared: + ref = block_frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + if "super" in undeclared: + ref = block_frame.symbols.declare_parameter("super") + self.writeline(f"{ref} = context.super({name!r}, block_{name})") + block_frame.symbols.analyze_node(block) + block_frame.block = name + self.writeline("_block_vars = {}") + self.enter_frame(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.leave_frame(block_frame, with_python_scope=True) + self.outdent() + + blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks) + self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1) + debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info) + self.writeline(f"debug_info = {debug_kv_str!r}") + + def visit_Block(self, node: nodes.Block, frame: Frame) -> None: + """Call a block and register it for the template.""" + level = 0 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline("if parent_template is None:") + self.indent() + level += 1 + + if node.scoped: + context = self.derive_context(frame) + else: + context = self.get_context_ref() + + if node.required: + self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node) + self.indent() + self.writeline( + f'raise TemplateRuntimeError("Required block {node.name!r} not found")', + node, + ) + self.outdent() + + if not self.environment.is_async and frame.buffer is None: + self.writeline( + f"yield from context.blocks[{node.name!r}][0]({context})", node + ) + else: + self.writeline( + f"{self.choose_async()}for event in" + f" context.blocks[{node.name!r}][0]({context}):", + node, + ) + self.indent() + self.simple_write("event", frame) + self.outdent() + + self.outdent(level) + + def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None: + """Calls the extender.""" + if not frame.toplevel: + self.fail("cannot use extend from a non top-level scope", node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline("if parent_template is not None:") + self.indent() + self.writeline('raise TemplateRuntimeError("extended multiple times")') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline("parent_template = environment.get_template(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + self.writeline("for name, parent_block in parent_template.blocks.items():") + self.indent() + self.writeline("context.blocks.setdefault(name, []).append(parent_block)") + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node: nodes.Include, frame: Frame) -> None: + """Handles includes.""" + if node.ignore_missing: + self.writeline("try:") + self.indent() + + func_name = "get_or_select_template" + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, str): + func_name = "get_template" + elif isinstance(node.template.value, (tuple, list)): + func_name = "select_template" + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = "select_template" + + self.writeline(f"template = environment.{func_name}(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + if node.ignore_missing: + self.outdent() + self.writeline("except TemplateNotFound:") + self.indent() + self.writeline("pass") + self.outdent() + self.writeline("else:") + self.indent() + + skip_event_yield = False + if node.with_context: + self.writeline( + f"{self.choose_async()}for event in template.root_render_func(" + "template.new_context(context.get_all(), True," + f" {self.dump_local_context(frame)})):" + ) + elif self.environment.is_async: + self.writeline( + "for event in (await template._get_default_module_async())" + "._body_stream:" + ) + else: + self.writeline("yield from template._get_default_module()._body_stream") + skip_event_yield = True + + if not skip_event_yield: + self.indent() + self.simple_write("event", frame) + self.outdent() + + if node.ignore_missing: + self.outdent() + + def _import_common( + self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame + ) -> None: + self.write(f"{self.choose_async('await ')}environment.get_template(") + self.visit(node.template, frame) + self.write(f", {self.name!r}).") + + if node.with_context: + f_name = f"make_module{self.choose_async('_async')}" + self.write( + f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})" + ) + else: + self.write(f"_get_default_module{self.choose_async('_async')}(context)") + + def visit_Import(self, node: nodes.Import, frame: Frame) -> None: + """Visit regular imports.""" + self.writeline(f"{frame.symbols.ref(node.target)} = ", node) + if frame.toplevel: + self.write(f"context.vars[{node.target!r}] = ") + + self._import_common(node, frame) + + if frame.toplevel and not node.target.startswith("_"): + self.writeline(f"context.exported_vars.discard({node.target!r})") + + def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None: + """Visit named imports.""" + self.newline(node) + self.write("included_template = ") + self._import_common(node, frame) + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline( + f"{frame.symbols.ref(alias)} =" + f" getattr(included_template, {name!r}, missing)" + ) + self.writeline(f"if {frame.symbols.ref(alias)} is missing:") + self.indent() + message = ( + "the template {included_template.__name__!r}" + f" (imported on {self.position(node)})" + f" does not export the requested name {name!r}" + ) + self.writeline( + f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})" + ) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith("_"): + discarded_names.append(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}") + else: + names_kv = ", ".join( + f"{name!r}: {frame.symbols.ref(name)}" for name in var_names + ) + self.writeline(f"context.vars.update({{{names_kv}}})") + if discarded_names: + if len(discarded_names) == 1: + self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})") + else: + names_str = ", ".join(map(repr, discarded_names)) + self.writeline( + f"context.exported_vars.difference_update(({names_str}))" + ) + + def visit_For(self, node: nodes.For, frame: Frame) -> None: + loop_frame = frame.inner() + loop_frame.loop_frame = True + test_frame = frame.inner() + else_frame = frame.inner() + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body if the body is a scoped block. + extended_loop = ( + node.recursive + or "loop" + in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",)) + or any(block.scoped for block in node.find_all(nodes.Block)) + ) + + loop_ref = None + if extended_loop: + loop_ref = loop_frame.symbols.declare_parameter("loop") + + loop_frame.symbols.analyze_node(node, for_branch="body") + if node.else_: + else_frame.symbols.analyze_node(node, for_branch="else") + + if node.test: + loop_filter_func = self.temporary_identifier() + test_frame.symbols.analyze_node(node, for_branch="test") + self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test) + self.indent() + self.enter_frame(test_frame) + self.writeline(self.choose_async("async for ", "for ")) + self.visit(node.target, loop_frame) + self.write(" in ") + self.write(self.choose_async("auto_aiter(fiter)", "fiter")) + self.write(":") + self.indent() + self.writeline("if ", node.test) + self.visit(node.test, test_frame) + self.write(":") + self.indent() + self.writeline("yield ") + self.visit(node.target, loop_frame) + self.outdent(3) + self.leave_frame(test_frame, with_python_scope=True) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if node.recursive: + self.writeline( + f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node + ) + self.indent() + self.buffer(loop_frame) + + # Use the same buffer for the else frame + else_frame.buffer = loop_frame.buffer + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline(f"{loop_ref} = missing") + + for name in node.find_all(nodes.Name): + if name.ctx == "store" and name.name == "loop": + self.fail( + "Can't assign to special loop variable in for-loop target", + name.lineno, + ) + + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline(f"{iteration_indicator} = 1") + + self.writeline(self.choose_async("async for ", "for "), node) + self.visit(node.target, loop_frame) + if extended_loop: + self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(") + else: + self.write(" in ") + + if node.test: + self.write(f"{loop_filter_func}(") + if node.recursive: + self.write("reciter") + else: + if self.environment.is_async and not extended_loop: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async and not extended_loop: + self.write(")") + if node.test: + self.write(")") + + if node.recursive: + self.write(", undefined, loop_render_func, depth):") + else: + self.write(", undefined):" if extended_loop else ":") + + self.indent() + self.enter_frame(loop_frame) + + self.writeline("_loop_vars = {}") + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline(f"{iteration_indicator} = 0") + self.outdent() + self.leave_frame( + loop_frame, with_python_scope=node.recursive and not node.else_ + ) + + if node.else_: + self.writeline(f"if {iteration_indicator}:") + self.indent() + self.enter_frame(else_frame) + self.blockvisit(node.else_, else_frame) + self.leave_frame(else_frame) + self.outdent() + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + self.write(f"{self.choose_async('await ')}loop(") + if self.environment.is_async: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async: + self.write(")") + self.write(", loop)") + self.end_write(frame) + + # at the end of the iteration, clear any assignments made in the + # loop from the top level + if self._assign_stack: + self._assign_stack[-1].difference_update(loop_frame.symbols.stores) + + def visit_If(self, node: nodes.If, frame: Frame) -> None: + if_frame = frame.soft() + self.writeline("if ", node) + self.visit(node.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + for elif_ in node.elif_: + self.writeline("elif ", elif_) + self.visit(elif_.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(elif_.body, if_frame) + self.outdent() + if node.else_: + self.writeline("else:") + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None: + macro_frame, macro_ref = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith("_"): + self.write(f"context.exported_vars.add({node.name!r})") + self.writeline(f"context.vars[{node.name!r}] = ") + self.write(f"{frame.symbols.ref(node.name)} = ") + self.macro_def(macro_ref, macro_frame) + + def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None: + call_frame, macro_ref = self.macro_body(node, frame) + self.writeline("caller = ") + self.macro_def(macro_ref, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None: + filter_frame = frame.inner() + filter_frame.symbols.analyze_node(node) + self.enter_frame(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.leave_frame(filter_frame) + + def visit_With(self, node: nodes.With, frame: Frame) -> None: + with_frame = frame.inner() + with_frame.symbols.analyze_node(node) + self.enter_frame(with_frame) + for target, expr in zip(node.targets, node.values): + self.newline() + self.visit(target, with_frame) + self.write(" = ") + self.visit(expr, frame) + self.blockvisit(node.body, with_frame) + self.leave_frame(with_frame) + + def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None: + self.newline(node) + self.visit(node.node, frame) + + class _FinalizeInfo(t.NamedTuple): + const: t.Optional[t.Callable[..., str]] + src: t.Optional[str] + + @staticmethod + def _default_finalize(value: t.Any) -> t.Any: + """The default finalize function if the environment isn't + configured with one. Or, if the environment has one, this is + called on that function's output for constants. + """ + return str(value) + + _finalize: t.Optional[_FinalizeInfo] = None + + def _make_finalize(self) -> _FinalizeInfo: + """Build the finalize function to be used on constants and at + runtime. Cached so it's only created once for all output nodes. + + Returns a ``namedtuple`` with the following attributes: + + ``const`` + A function to finalize constant data at compile time. + + ``src`` + Source code to output around nodes to be evaluated at + runtime. + """ + if self._finalize is not None: + return self._finalize + + finalize: t.Optional[t.Callable[..., t.Any]] + finalize = default = self._default_finalize + src = None + + if self.environment.finalize: + src = "environment.finalize(" + env_finalize = self.environment.finalize + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(env_finalize) # type: ignore + ) + finalize = None + + if pass_arg is None: + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(value)) + + else: + src = f"{src}{pass_arg}, " + + if pass_arg == "environment": + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(self.environment, value)) + + self._finalize = self._FinalizeInfo(finalize, src) + return self._finalize + + def _output_const_repr(self, group: t.Iterable[t.Any]) -> str: + """Given a group of constant values converted from ``Output`` + child nodes, produce a string to write to the template module + source. + """ + return repr(concat(group)) + + def _output_child_to_const( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> str: + """Try to optimize a child of an ``Output`` node by trying to + convert it to constant, finalized data at compile time. + + If :exc:`Impossible` is raised, the node is not constant and + will be evaluated at runtime. Any other exception will also be + evaluated at runtime for easier debugging. + """ + const = node.as_const(frame.eval_ctx) + + if frame.eval_ctx.autoescape: + const = escape(const) + + # Template data doesn't go through finalize. + if isinstance(node, nodes.TemplateData): + return str(const) + + return finalize.const(const) # type: ignore + + def _output_child_pre( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code before visiting a child of an + ``Output`` node. + """ + if frame.eval_ctx.volatile: + self.write("(escape if context.eval_ctx.autoescape else str)(") + elif frame.eval_ctx.autoescape: + self.write("escape(") + else: + self.write("str(") + + if finalize.src is not None: + self.write(finalize.src) + + def _output_child_post( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code after visiting a child of an + ``Output`` node. + """ + self.write(")") + + if finalize.src is not None: + self.write(")") + + def visit_Output(self, node: nodes.Output, frame: Frame) -> None: + # If an extends is active, don't render outside a block. + if frame.require_output_check: + # A top-level extends is known to exist at compile time. + if self.has_known_extends: + return + + self.writeline("if parent_template is None:") + self.indent() + + finalize = self._make_finalize() + body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = [] + + # Evaluate constants at compile time if possible. Each item in + # body will be either a list of static data or a node to be + # evaluated at runtime. + for child in node.nodes: + try: + if not ( + # If the finalize function requires runtime context, + # constants can't be evaluated at compile time. + finalize.const + # Unless it's basic template data that won't be + # finalized anyway. + or isinstance(child, nodes.TemplateData) + ): + raise nodes.Impossible() + + const = self._output_child_to_const(child, frame, finalize) + except (nodes.Impossible, Exception): + # The node was not constant and needs to be evaluated at + # runtime. Or another error was raised, which is easier + # to debug at runtime. + body.append(child) + continue + + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + if frame.buffer is not None: + if len(body) == 1: + self.writeline(f"{frame.buffer}.append(") + else: + self.writeline(f"{frame.buffer}.extend((") + + self.indent() + + for item in body: + if isinstance(item, list): + # A group of constant data to join and output. + val = self._output_const_repr(item) + + if frame.buffer is None: + self.writeline("yield " + val) + else: + self.writeline(val + ",") + else: + if frame.buffer is None: + self.writeline("yield ", item) + else: + self.newline(item) + + # A node to be evaluated at runtime. + self._output_child_pre(item, frame, finalize) + self.visit(item, frame) + self._output_child_post(item, frame, finalize) + + if frame.buffer is not None: + self.write(",") + + if frame.buffer is not None: + self.outdent() + self.writeline(")" if len(body) == 1 else "))") + + if frame.require_output_check: + self.outdent() + + def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None: + self.push_assign_tracking() + self.newline(node) + self.visit(node.target, frame) + self.write(" = ") + self.visit(node.node, frame) + self.pop_assign_tracking(frame) + + def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None: + self.push_assign_tracking() + block_frame = frame.inner() + # This is a special case. Since a set block always captures we + # will disable output checks. This way one can use set blocks + # toplevel even in extended templates. + block_frame.require_output_check = False + block_frame.symbols.analyze_node(node) + self.enter_frame(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.newline(node) + self.visit(node.target, frame) + self.write(" = (Markup if context.eval_ctx.autoescape else identity)(") + if node.filter is not None: + self.visit_Filter(node.filter, block_frame) + else: + self.write(f"concat({block_frame.buffer})") + self.write(")") + self.pop_assign_tracking(frame) + self.leave_frame(block_frame) + + # -- Expression Visitors + + def visit_Name(self, node: nodes.Name, frame: Frame) -> None: + if node.ctx == "store" and ( + frame.toplevel or frame.loop_frame or frame.block_frame + ): + if self._assign_stack: + self._assign_stack[-1].add(node.name) + ref = frame.symbols.ref(node.name) + + # If we are looking up a variable we might have to deal with the + # case where it's undefined. We can skip that case if the load + # instruction indicates a parameter which are always defined. + if node.ctx == "load": + load = frame.symbols.find_load(ref) + if not ( + load is not None + and load[0] == VAR_LOAD_PARAMETER + and not self.parameter_is_undeclared(ref) + ): + self.write( + f"(undefined(name={node.name!r}) if {ref} is missing else {ref})" + ) + return + + self.write(ref) + + def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None: + # NSRefs can only be used to store values; since they use the normal + # `foo.bar` notation they will be parsed as a normal attribute access + # when used anywhere but in a `set` context + ref = frame.symbols.ref(node.name) + self.writeline(f"if not isinstance({ref}, Namespace):") + self.indent() + self.writeline( + "raise TemplateRuntimeError" + '("cannot assign attribute on non-namespace object")' + ) + self.outdent() + self.writeline(f"{ref}[{node.attr!r}]") + + def visit_Const(self, node: nodes.Const, frame: Frame) -> None: + val = node.as_const(frame.eval_ctx) + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None: + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write( + f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})" + ) + + def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None: + self.write("(") + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write(",)" if idx == 0 else ")") + + def visit_List(self, node: nodes.List, frame: Frame) -> None: + self.write("[") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write("]") + + def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None: + self.write("{") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item.key, frame) + self.write(": ") + self.visit(item.value, frame) + self.write("}") + + visit_Add = _make_binop("+") + visit_Sub = _make_binop("-") + visit_Mul = _make_binop("*") + visit_Div = _make_binop("/") + visit_FloorDiv = _make_binop("//") + visit_Pow = _make_binop("**") + visit_Mod = _make_binop("%") + visit_And = _make_binop("and") + visit_Or = _make_binop("or") + visit_Pos = _make_unop("+") + visit_Neg = _make_unop("-") + visit_Not = _make_unop("not ") + + @optimizeconst + def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None: + if frame.eval_ctx.volatile: + func_name = "(markup_join if context.eval_ctx.volatile else str_join)" + elif frame.eval_ctx.autoescape: + func_name = "markup_join" + else: + func_name = "str_join" + self.write(f"{func_name}((") + for arg in node.nodes: + self.visit(arg, frame) + self.write(", ") + self.write("))") + + @optimizeconst + def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None: + self.write("(") + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + self.write(")") + + def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None: + self.write(f" {operators[node.op]} ") + self.visit(node.expr, frame) + + @optimizeconst + def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getattr(") + self.visit(node.node, frame) + self.write(f", {node.attr!r})") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None: + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write("[") + self.visit(node.arg, frame) + self.write("]") + else: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getitem(") + self.visit(node.node, frame) + self.write(", ") + self.visit(node.arg, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None: + if node.start is not None: + self.visit(node.start, frame) + self.write(":") + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(":") + self.visit(node.step, frame) + + @contextmanager + def _filter_test_common( + self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool + ) -> t.Iterator[None]: + if self.environment.is_async: + self.write("(await auto_await(") + + if is_filter: + self.write(f"{self.filters[node.name]}(") + func = self.environment.filters.get(node.name) + else: + self.write(f"{self.tests[node.name]}(") + func = self.environment.tests.get(node.name) + + # When inside an If or CondExpr frame, allow the filter to be + # undefined at compile time and only raise an error if it's + # actually called at runtime. See pull_dependencies. + if func is None and not frame.soft_frame: + type_name = "filter" if is_filter else "test" + self.fail(f"No {type_name} named {node.name!r}.", node.lineno) + + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(func) # type: ignore + ) + + if pass_arg is not None: + self.write(f"{pass_arg}, ") + + # Back to the visitor function to handle visiting the target of + # the filter or test. + yield + + self.signature(node, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None: + with self._filter_test_common(node, frame, True): + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write( + f"(Markup(concat({frame.buffer}))" + f" if context.eval_ctx.autoescape else concat({frame.buffer}))" + ) + elif frame.eval_ctx.autoescape: + self.write(f"Markup(concat({frame.buffer}))") + else: + self.write(f"concat({frame.buffer})") + + @optimizeconst + def visit_Test(self, node: nodes.Test, frame: Frame) -> None: + with self._filter_test_common(node, frame, False): + self.visit(node.node, frame) + + @optimizeconst + def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None: + frame = frame.soft() + + def write_expr2() -> None: + if node.expr2 is not None: + self.visit(node.expr2, frame) + return + + self.write( + f'cond_expr_undefined("the inline if-expression on' + f" {self.position(node)} evaluated to false and no else" + f' section was defined.")' + ) + + self.write("(") + self.visit(node.expr1, frame) + self.write(" if ") + self.visit(node.test, frame) + self.write(" else ") + write_expr2() + self.write(")") + + @optimizeconst + def visit_Call( + self, node: nodes.Call, frame: Frame, forward_caller: bool = False + ) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + if self.environment.sandboxed: + self.write("environment.call(context, ") + else: + self.write("context.call(") + self.visit(node.node, frame) + extra_kwargs = {"caller": "caller"} if forward_caller else None + loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {} + block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {} + if extra_kwargs: + extra_kwargs.update(loop_kwargs, **block_kwargs) + elif loop_kwargs or block_kwargs: + extra_kwargs = dict(loop_kwargs, **block_kwargs) + self.signature(node, frame, extra_kwargs) + self.write(")") + if self.environment.is_async: + self.write("))") + + def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None: + self.write(node.key + "=") + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None: + self.write("Markup(") + self.visit(node.expr, frame) + self.write(")") + + def visit_MarkSafeIfAutoescape( + self, node: nodes.MarkSafeIfAutoescape, frame: Frame + ) -> None: + self.write("(Markup if context.eval_ctx.autoescape else identity)(") + self.visit(node.expr, frame) + self.write(")") + + def visit_EnvironmentAttribute( + self, node: nodes.EnvironmentAttribute, frame: Frame + ) -> None: + self.write("environment." + node.name) + + def visit_ExtensionAttribute( + self, node: nodes.ExtensionAttribute, frame: Frame + ) -> None: + self.write(f"environment.extensions[{node.identifier!r}].{node.name}") + + def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None: + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None: + self.write(node.name) + + def visit_ContextReference( + self, node: nodes.ContextReference, frame: Frame + ) -> None: + self.write("context") + + def visit_DerivedContextReference( + self, node: nodes.DerivedContextReference, frame: Frame + ) -> None: + self.write(self.derive_context(frame)) + + def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None: + self.writeline("continue", node) + + def visit_Break(self, node: nodes.Break, frame: Frame) -> None: + self.writeline("break", node) + + def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None: + scope_frame = frame.inner() + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + + def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None: + ctx = self.temporary_identifier() + self.writeline(f"{ctx} = {self.derive_context(frame)}") + self.writeline(f"{ctx}.vars = ") + self.visit(node.context, frame) + self.push_context_reference(ctx) + + scope_frame = frame.inner(isolated=True) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + self.pop_context_reference() + + def visit_EvalContextModifier( + self, node: nodes.EvalContextModifier, frame: Frame + ) -> None: + for keyword in node.options: + self.writeline(f"context.eval_ctx.{keyword.key} = ") + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier( + self, node: nodes.ScopedEvalContextModifier, frame: Frame + ) -> None: + old_ctx_name = self.temporary_identifier() + saved_ctx = frame.eval_ctx.save() + self.writeline(f"{old_ctx_name} = context.eval_ctx.save()") + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(saved_ctx) + self.writeline(f"context.eval_ctx.revert({old_ctx_name})") diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2/constants.py b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/constants.py new file mode 100644 index 000000000..41a1c23b0 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/constants.py @@ -0,0 +1,20 @@ +#: list of lorem ipsum words used by the lipsum() helper function +LOREM_IPSUM_WORDS = """\ +a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at +auctor augue bibendum blandit class commodo condimentum congue consectetuer +consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus +diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend +elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames +faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac +hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum +justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem +luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie +mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non +nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque +penatibus per pharetra phasellus placerat platea porta porttitor posuere +potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus +ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit +sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor +tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices +ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus +viverra volutpat vulputate""" diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2/debug.py b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/debug.py new file mode 100644 index 000000000..7ed7e9297 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/debug.py @@ -0,0 +1,191 @@ +import sys +import typing as t +from types import CodeType +from types import TracebackType + +from .exceptions import TemplateSyntaxError +from .utils import internal_code +from .utils import missing + +if t.TYPE_CHECKING: + from .runtime import Context + + +def rewrite_traceback_stack(source: t.Optional[str] = None) -> BaseException: + """Rewrite the current exception to replace any tracebacks from + within compiled template code with tracebacks that look like they + came from the template source. + + This must be called within an ``except`` block. + + :param source: For ``TemplateSyntaxError``, the original source if + known. + :return: The original exception with the rewritten traceback. + """ + _, exc_value, tb = sys.exc_info() + exc_value = t.cast(BaseException, exc_value) + tb = t.cast(TracebackType, tb) + + if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated: + exc_value.translated = True + exc_value.source = source + # Remove the old traceback, otherwise the frames from the + # compiler still show up. + exc_value.with_traceback(None) + # Outside of runtime, so the frame isn't executing template + # code, but it still needs to point at the template. + tb = fake_traceback( + exc_value, None, exc_value.filename or "", exc_value.lineno + ) + else: + # Skip the frame for the render function. + tb = tb.tb_next + + stack = [] + + # Build the stack of traceback object, replacing any in template + # code with the source file and line information. + while tb is not None: + # Skip frames decorated with @internalcode. These are internal + # calls that aren't useful in template debugging output. + if tb.tb_frame.f_code in internal_code: + tb = tb.tb_next + continue + + template = tb.tb_frame.f_globals.get("__jinja_template__") + + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + fake_tb = fake_traceback(exc_value, tb, template.filename, lineno) + stack.append(fake_tb) + else: + stack.append(tb) + + tb = tb.tb_next + + tb_next = None + + # Assign tb_next in reverse to avoid circular references. + for tb in reversed(stack): + tb.tb_next = tb_next + tb_next = tb + + return exc_value.with_traceback(tb_next) + + +def fake_traceback( # type: ignore + exc_value: BaseException, tb: t.Optional[TracebackType], filename: str, lineno: int +) -> TracebackType: + """Produce a new traceback object that looks like it came from the + template source instead of the compiled code. The filename, line + number, and location name will point to the template, and the local + variables will be the current template context. + + :param exc_value: The original exception to be re-raised to create + the new traceback. + :param tb: The original traceback to get the local variables and + code info from. + :param filename: The template filename. + :param lineno: The line number in the template source. + """ + if tb is not None: + # Replace the real locals with the context that would be + # available at that point in the template. + locals = get_template_locals(tb.tb_frame.f_locals) + locals.pop("__jinja_exception__", None) + else: + locals = {} + + globals = { + "__name__": filename, + "__file__": filename, + "__jinja_exception__": exc_value, + } + # Raise an exception at the correct line number. + code: CodeType = compile( + "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec" + ) + + # Build a new code object that points to the template file and + # replaces the location with a block name. + location = "template" + + if tb is not None: + function = tb.tb_frame.f_code.co_name + + if function == "root": + location = "top-level template code" + elif function.startswith("block_"): + location = f"block {function[6:]!r}" + + if sys.version_info >= (3, 8): + code = code.replace(co_name=location) + else: + code = CodeType( + code.co_argcount, + code.co_kwonlyargcount, + code.co_nlocals, + code.co_stacksize, + code.co_flags, + code.co_code, + code.co_consts, + code.co_names, + code.co_varnames, + code.co_filename, + location, + code.co_firstlineno, + code.co_lnotab, + code.co_freevars, + code.co_cellvars, + ) + + # Execute the new code, which is guaranteed to raise, and return + # the new traceback without this frame. + try: + exec(code, globals, locals) + except BaseException: + return sys.exc_info()[2].tb_next # type: ignore + + +def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]: + """Based on the runtime locals, get the context that would be + available at that point in the template. + """ + # Start with the current template context. + ctx: "t.Optional[Context]" = real_locals.get("context") + + if ctx is not None: + data: t.Dict[str, t.Any] = ctx.get_all().copy() + else: + data = {} + + # Might be in a derived context that only sets local variables + # rather than pushing a context. Local variables follow the scheme + # l_depth_name. Find the highest-depth local that has a value for + # each name. + local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {} + + for name, value in real_locals.items(): + if not name.startswith("l_") or value is missing: + # Not a template variable, or no longer relevant. + continue + + try: + _, depth_str, name = name.split("_", 2) + depth = int(depth_str) + except ValueError: + continue + + cur_depth = local_overrides.get(name, (-1,))[0] + + if cur_depth < depth: + local_overrides[name] = (depth, value) + + # Modify the context with any derived context. + for name, (_, value) in local_overrides.items(): + if value is missing: + data.pop(name, None) + else: + data[name] = value + + return data diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2/defaults.py b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/defaults.py new file mode 100644 index 000000000..638cad3d2 --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/defaults.py @@ -0,0 +1,48 @@ +import typing as t + +from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401 +from .tests import TESTS as DEFAULT_TESTS # noqa: F401 +from .utils import Cycler +from .utils import generate_lorem_ipsum +from .utils import Joiner +from .utils import Namespace + +if t.TYPE_CHECKING: + import typing_extensions as te + +# defaults for the parser / lexer +BLOCK_START_STRING = "{%" +BLOCK_END_STRING = "%}" +VARIABLE_START_STRING = "{{" +VARIABLE_END_STRING = "}}" +COMMENT_START_STRING = "{#" +COMMENT_END_STRING = "#}" +LINE_STATEMENT_PREFIX: t.Optional[str] = None +LINE_COMMENT_PREFIX: t.Optional[str] = None +TRIM_BLOCKS = False +LSTRIP_BLOCKS = False +NEWLINE_SEQUENCE: "te.Literal['\\n', '\\r\\n', '\\r']" = "\n" +KEEP_TRAILING_NEWLINE = False + +# default filters, tests and namespace + +DEFAULT_NAMESPACE = { + "range": range, + "dict": dict, + "lipsum": generate_lorem_ipsum, + "cycler": Cycler, + "joiner": Joiner, + "namespace": Namespace, +} + +# default policies +DEFAULT_POLICIES: t.Dict[str, t.Any] = { + "compiler.ascii_str": True, + "urlize.rel": "noopener", + "urlize.target": None, + "urlize.extra_schemes": None, + "truncate.leeway": 5, + "json.dumps_function": None, + "json.dumps_kwargs": {"sort_keys": True}, + "ext.i18n.trimmed": False, +} diff --git a/lib/go-jinja2/internal/data/darwin-amd64/jinja2/environment.py b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/environment.py new file mode 100644 index 000000000..1d3be0bed --- /dev/null +++ b/lib/go-jinja2/internal/data/darwin-amd64/jinja2/environment.py @@ -0,0 +1,1675 @@ +"""Classes for managing templates and their runtime and compile time +options. +""" + +import os +import typing +import typing as t +import weakref +from collections import ChainMap +from functools import lru_cache +from functools import partial +from functools import reduce +from types import CodeType + +from markupsafe import Markup + +from . import nodes +from .compiler import CodeGenerator +from .compiler import generate +from .defaults import BLOCK_END_STRING +from .defaults import BLOCK_START_STRING +from .defaults import COMMENT_END_STRING +from .defaults import COMMENT_START_STRING +from .defaults import DEFAULT_FILTERS # type: ignore[attr-defined] +from .defaults import DEFAULT_NAMESPACE +from .defaults import DEFAULT_POLICIES +from .defaults import DEFAULT_TESTS # type: ignore[attr-defined] +from .defaults import KEEP_TRAILING_NEWLINE +from .defaults import LINE_COMMENT_PREFIX +from .defaults import LINE_STATEMENT_PREFIX +from .defaults import LSTRIP_BLOCKS +from .defaults import NEWLINE_SEQUENCE +from .defaults import TRIM_BLOCKS +from .defaults import VARIABLE_END_STRING +from .defaults import VARIABLE_START_STRING +from .exceptions import TemplateNotFound +from .exceptions import TemplateRuntimeError +from .exceptions import TemplatesNotFound +from .exceptions import TemplateSyntaxError +from .exceptions import UndefinedError +from .lexer import get_lexer +from .lexer import Lexer +from .lexer import TokenStream +from .nodes import EvalContext +from .parser import Parser +from .runtime import Context +from .runtime import new_context +from .runtime import Undefined +from .utils import _PassArg +from .utils import concat +from .utils import consume +from .utils import import_string +from .utils import internalcode +from .utils import LRUCache +from .utils import missing + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .bccache import BytecodeCache + from .ext import Extension + from .loaders import BaseLoader + +_env_bound = t.TypeVar("_env_bound", bound="Environment") + + +# for direct template usage we have up to ten living environments +@lru_cache(maxsize=10) +def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound: + """Return a new spontaneous environment. A spontaneous environment + is used for templates created directly rather than through an + existing environment. + + :param cls: Environment class to create. + :param args: Positional arguments passed to environment. + """ + env = cls(*args) + env.shared = True + return env + + +def create_cache( + size: int, +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Return the cache class for the given size.""" + if size == 0: + return None + + if size < 0: + return {} + + return LRUCache(size) # type: ignore + + +def copy_cache( + cache: t.Optional[t.MutableMapping[t.Any, t.Any]], +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Create an empty copy of the given cache.""" + if cache is None: + return None + + if type(cache) is dict: # noqa E721 + return {} + + return LRUCache(cache.capacity) # type: ignore + + +def load_extensions( + environment: "Environment", + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]], +) -> t.Dict[str, "Extension"]: + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated extensions. + """ + result = {} + + for extension in extensions: + if isinstance(extension, str): + extension = t.cast(t.Type["Extension"], import_string(extension)) + + result[extension.identifier] = extension(environment) + + return result + + +def _environment_config_check(environment: "Environment") -> "Environment": + """Perform a sanity check on the environment.""" + assert issubclass( + environment.undefined, Undefined + ), "'undefined' must be a subclass of 'jinja2.Undefined'." + assert ( + environment.block_start_string + != environment.variable_start_string + != environment.comment_start_string + ), "block, variable and comment start strings must be different." + assert environment.newline_sequence in { + "\r", + "\r\n", + "\n", + }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'." + return environment + + +class Environment: + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation `. + + `optimized` + should the optimizer be enabled? Default is ``True``. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + ``None`` implicitly into an empty string here. + + `autoescape` + If set to ``True`` the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see + :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return ``True`` or ``False`` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + ``auto_reload`` is set to ``True`` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + + `enable_async` + If set to true this enables async template execution which + allows using async functions and generators. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to: t.Optional["Environment"] = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class: t.Type["CodeGenerator"] = CodeGenerator + + concat = "".join + + #: the context class that is used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class: t.Type[Context] = Context + + template_class: t.Type["Template"] + + def __init__( + self, + block_start_string: str = BLOCK_START_STRING, + block_end_string: str = BLOCK_END_STRING, + variable_start_string: str = VARIABLE_START_STRING, + variable_end_string: str = VARIABLE_END_STRING, + comment_start_string: str = COMMENT_START_STRING, + comment_end_string: str = COMMENT_END_STRING, + line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX, + line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX, + trim_blocks: bool = TRIM_BLOCKS, + lstrip_blocks: bool = LSTRIP_BLOCKS, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE, + keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (), + optimized: bool = True, + undefined: t.Type[Undefined] = Undefined, + finalize: t.Optional[t.Callable[..., t.Any]] = None, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False, + loader: t.Optional["BaseLoader"] = None, + cache_size: int = 400, + auto_reload: bool = True, + bytecode_cache: t.Optional["BytecodeCache"] = None, + enable_async: bool = False, + ): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined: t.Type[Undefined] = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # configurable policies + self.policies = DEFAULT_POLICIES.copy() + + # load extensions + self.extensions = load_extensions(self, extensions) + + self.is_async = enable_async + _environment_config_check(self) + + def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None: + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes: t.Any) -> None: + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions ` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in attributes.items(): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay( + self, + block_start_string: str = missing, + block_end_string: str = missing, + variable_start_string: str = missing, + variable_end_string: str = missing, + comment_start_string: str = missing, + comment_end_string: str = missing, + line_statement_prefix: t.Optional[str] = missing, + line_comment_prefix: t.Optional[str] = missing, + trim_blocks: bool = missing, + lstrip_blocks: bool = missing, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing, + keep_trailing_newline: bool = missing, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing, + optimized: bool = missing, + undefined: t.Type[Undefined] = missing, + finalize: t.Optional[t.Callable[..., t.Any]] = missing, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing, + loader: t.Optional["BaseLoader"] = missing, + cache_size: int = missing, + auto_reload: bool = missing, + bytecode_cache: t.Optional["BytecodeCache"] = missing, + enable_async: bool = False, + ) -> "Environment": + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + + .. versionchanged:: 3.1.2 + Added the ``newline_sequence``,, ``keep_trailing_newline``, + and ``enable_async`` parameters to match ``__init__``. + """ + args = dict(locals()) + del args["self"], args["cache_size"], args["extensions"], args["enable_async"] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in args.items(): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in self.extensions.items(): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + if enable_async is not missing: + rv.is_async = enable_async + + return _environment_config_check(rv) + + @property + def lexer(self) -> Lexer: + """The lexer for this environment.""" + return get_lexer(self) + + def iter_extensions(self) -> t.Iterator["Extension"]: + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), key=lambda x: x.priority)) + + def getitem( + self, obj: t.Any, argument: t.Union[str, t.Any] + ) -> t.Union[t.Any, Undefined]: + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (AttributeError, TypeError, LookupError): + if isinstance(argument, str): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj: t.Any, attribute: str) -> t.Any: + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a string. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def _filter_test_common( + self, + name: t.Union[str, Undefined], + value: t.Any, + args: t.Optional[t.Sequence[t.Any]], + kwargs: t.Optional[t.Mapping[str, t.Any]], + context: t.Optional[Context], + eval_ctx: t.Optional[EvalContext], + is_filter: bool, + ) -> t.Any: + if is_filter: + env_map = self.filters + type_name = "filter" + else: + env_map = self.tests + type_name = "test" + + func = env_map.get(name) # type: ignore + + if func is None: + msg = f"No {type_name} named {name!r}." + + if isinstance(name, Undefined): + try: + name._fail_with_undefined_error() + except Exception as e: + msg = f"{msg} ({e}; did you forget to quote the callable name?)" + + raise TemplateRuntimeError(msg) + + args = [value, *(args if args is not None else ())] + kwargs = kwargs if kwargs is not None else {} + pass_arg = _PassArg.from_obj(func) + + if pass_arg is _PassArg.context: + if context is None: + raise TemplateRuntimeError( + f"Attempted to invoke a context {type_name} without context." + ) + + args.insert(0, context) + elif pass_arg is _PassArg.eval_context: + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + + args.insert(0, eval_ctx) + elif pass_arg is _PassArg.environment: + args.insert(0, self) + + return func(*args, **kwargs) + + def call_filter( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a filter on a value the same way the compiler does. + + This might return a coroutine if the filter is running from an + environment in async mode and the filter supports async + execution. It's your responsibility to await this if needed. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, True + ) + + def call_test( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a test on a value the same way the compiler does. + + This might return a coroutine if the test is running from an + environment in async mode and the test supports async execution. + It's your responsibility to await this if needed. + + .. versionchanged:: 3.0 + Tests support ``@pass_context``, etc. decorators. Added + the ``context`` and ``eval_ctx`` parameters. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, False + ) + + @internalcode + def parse( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> nodes.Template: + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja extensions ` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def _parse( + self, source: str, name: t.Optional[str], filename: t.Optional[str] + ) -> nodes.Template: + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, filename).parse() + + def lex( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> t.Iterator[t.Tuple[int, str, str]]: + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development ` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = str(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def preprocess( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> str: + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce( + lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), + str(source), + ) + + def _tokenize( + self, + source: str, + name: t.Optional[str], + filename: t.Optional[str] = None, + state: t.Optional[str] = None, + ) -> TokenStream: + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) # type: ignore + + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + + return stream + + def _generate( + self, + source: nodes.Template, + name: t.Optional[str], + filename: t.Optional[str], + defer_init: bool = False, + ) -> str: + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ + return generate( # type: ignore + source, + self, + name, + filename, + defer_init=defer_init, + optimized=self.optimized, + ) + + def _compile(self, source: str, filename: str) -> CodeType: + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ + return compile(source, filename, "exec") + + @typing.overload + def compile( # type: ignore + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[False]" = False, + defer_init: bool = False, + ) -> CodeType: ... + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[True]" = ..., + defer_init: bool = False, + ) -> str: ... + + @internalcode + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: bool = False, + defer_init: bool = False, + ) -> t.Union[str, CodeType]: + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, str): + source_hint = source + source = self._parse(source, name, filename) + source = self._generate(source, name, filename, defer_init=defer_init) + if raw: + return source + if filename is None: + filename = "